Compare commits
3 Commits
ef68b2a4c5
...
6d7932f6a5
Author | SHA1 | Date |
---|---|---|
SeanOMik | 6d7932f6a5 | |
SeanOMik | 28b9604189 | |
SeanOMik | a0a2acfec0 |
|
@ -384,6 +384,12 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bind_match"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "171f0236f66c7be99f32060539c2bade94033ded356ecf4c9dc9b1e6198326cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -1859,6 +1865,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bind_match",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"gilrs-core",
|
"gilrs-core",
|
||||||
|
|
|
@ -37,6 +37,7 @@ thiserror = "1.0.56"
|
||||||
unique = "0.9.1"
|
unique = "0.9.1"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
petgraph = { version = "0.6.5", features = ["matrix_graph"] }
|
petgraph = { version = "0.6.5", features = ["matrix_graph"] }
|
||||||
|
bind_match = "0.1.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracy = ["dep:tracing-tracy"]
|
tracy = ["dep:tracing-tracy"]
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
mod pass;
|
mod node;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc
|
cell::{Ref, RefCell}, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
pub use pass::*;
|
pub use node::*;
|
||||||
|
|
||||||
mod passes;
|
mod passes;
|
||||||
pub use passes::*;
|
pub use passes::*;
|
||||||
|
@ -34,6 +33,8 @@ pub trait RenderGraphLabel: Debug + 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RenderGraphHash(u64);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderGraphLabelValue(Rc<dyn RenderGraphLabel>);
|
pub struct RenderGraphLabelValue(Rc<dyn RenderGraphLabel>);
|
||||||
|
|
||||||
|
@ -76,10 +77,11 @@ impl PartialEq for RenderGraphLabelValue {
|
||||||
impl Eq for RenderGraphLabelValue {}
|
impl Eq for RenderGraphLabelValue {}
|
||||||
|
|
||||||
struct PassEntry {
|
struct PassEntry {
|
||||||
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
inner: Arc<RefCell<dyn Node>>,
|
||||||
desc: Arc<RenderGraphPassDesc>,
|
desc: Rc<RefCell<NodeDesc>>,
|
||||||
/// The index of the pass in the execution graph
|
/// The index of the pass in the execution graph
|
||||||
graph_index: petgraph::matrix_graph::NodeIndex<usize>,
|
graph_index: petgraph::matrix_graph::NodeIndex<usize>,
|
||||||
|
pipeline: Rc<RefCell<Option<PipelineResource>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BindGroupEntry {
|
pub struct BindGroupEntry {
|
||||||
|
@ -116,20 +118,19 @@ pub struct RenderTarget {
|
||||||
pub struct RenderGraph {
|
pub struct RenderGraph {
|
||||||
device: Rc<wgpu::Device>,
|
device: Rc<wgpu::Device>,
|
||||||
queue: Rc<wgpu::Queue>,
|
queue: Rc<wgpu::Queue>,
|
||||||
slots: FxHashMap<u64, ResourcedSlot>,
|
slots: FxHashMap<RenderGraphLabelValue, ResourcedSlot>,
|
||||||
/// HashMap used to lookup the slot id using the label's hash
|
/// HashMap used to lookup the slot id using the label's hash
|
||||||
slot_label_lookup: FxHashMap<RenderGraphLabelValue, u64>,
|
//slot_label_lookup: FxHashMap<RenderGraphLabelValue, u64>,
|
||||||
passes: FxHashMap<u64, PassEntry>,
|
passes: FxHashMap<RenderGraphLabelValue, PassEntry>,
|
||||||
// TODO: Use a SlotMap
|
// TODO: Use a SlotMap
|
||||||
bind_groups: FxHashMap<u64, BindGroupEntry>,
|
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>,
|
||||||
/// HashMap used to lookup the bind group id using the label's hash
|
/// HashMap used to lookup the bind group id using the label's hash
|
||||||
bind_group_names: FxHashMap<RenderGraphLabelValue, u64>,
|
//bind_group_names: FxHashMap<RenderGraphLabelValue, u64>,
|
||||||
// TODO: make pipelines a `type` parameter in RenderPasses,
|
// TODO: make pipelines a `type` parameter in RenderPasses,
|
||||||
// then the pipelines can be retrieved via TypeId to the pass.
|
// then the pipelines can be retrieved via TypeId to the pass.
|
||||||
pipelines: FxHashMap<u64, PipelineResource>,
|
//pipelines: FxHashMap<u64, PipelineResource>,
|
||||||
current_id: u64,
|
|
||||||
/// A directed graph describing the execution path of the RenderGraph
|
/// A directed graph describing the execution path of the RenderGraph
|
||||||
execution_graph: petgraph::matrix_graph::DiMatrix<u64, (), Option<()>, usize>,
|
execution_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
|
@ -138,12 +139,8 @@ impl RenderGraph {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
slots: Default::default(),
|
slots: Default::default(),
|
||||||
slot_label_lookup: Default::default(),
|
|
||||||
passes: Default::default(),
|
passes: Default::default(),
|
||||||
bind_groups: Default::default(),
|
bind_groups: Default::default(),
|
||||||
bind_group_names: Default::default(),
|
|
||||||
pipelines: Default::default(),
|
|
||||||
current_id: 1,
|
|
||||||
execution_graph: Default::default(),
|
execution_graph: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,48 +149,46 @@ impl RenderGraph {
|
||||||
&*self.device
|
&*self.device
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_id(&mut self) -> u64 {
|
/// Add a [`Node`] to the RenderGraph.
|
||||||
self.current_id += 1;
|
///
|
||||||
self.current_id
|
/// When the node is added, its [`Node::desc`] method will be executed.
|
||||||
}
|
///
|
||||||
|
/// Additionally, all [`Slot`](node::NodeSlot)s of the node will be iterated,
|
||||||
pub(crate) fn slot_id_rc(&self, label: &RenderGraphLabelValue) -> Option<u64> {
|
/// 1. Ensuring that there are no two slots of the same name, with different value types
|
||||||
self.slot_label_lookup.get(&label.clone().into()).cloned()
|
/// 2. Changing the id of insert slots to match the id of the output slot of the same name.
|
||||||
}
|
/// * This means that the id of insert slots **ARE NOT STABLE**. **DO NOT** rely on them to
|
||||||
|
/// not change. The IDs of output slots do stay the same.
|
||||||
pub fn slot_id(&self, label: &dyn RenderGraphLabel) -> Option<u64> {
|
/// 3. Ensuring that no two slots share the same ID when the names do not match.
|
||||||
self.slot_label_lookup.get(&label.rc_clone().into()).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(self, pass), level = "debug")]
|
#[instrument(skip(self, pass), level = "debug")]
|
||||||
pub fn add_pass<P: RenderGraphPass>(&mut self, mut pass: P) {
|
pub fn add_pass<P: Node>(&mut self, label: impl RenderGraphLabel, mut pass: P) {
|
||||||
let mut desc = pass.desc(self);
|
let mut desc = pass.desc(self);
|
||||||
|
|
||||||
// collect all the slots of the pass
|
// collect all the slots of the pass
|
||||||
for slot in &mut desc.slots {
|
for slot in &mut desc.slots {
|
||||||
if let Some((id, other)) = self
|
if let Some(other) = self
|
||||||
.slot_label_lookup
|
.slots
|
||||||
.get(&slot.label)
|
.get_mut(&slot.label)
|
||||||
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
|
//.map(|s| (id, s))
|
||||||
|
//.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
|
||||||
{
|
{
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
slot.ty, other.ty,
|
slot.ty, other.ty,
|
||||||
"slot {:?} in pass {:?} does not match existing slot of same name",
|
"slot {:?} in pass {:?} does not match existing slot of same name",
|
||||||
slot.label, desc.label
|
slot.label, label
|
||||||
);
|
);
|
||||||
|
|
||||||
trace!(
|
/* trace!(
|
||||||
"Found existing slot for {:?}, changing id to {}",
|
"Found existing slot for {:?}, changing id to {}",
|
||||||
slot.label,
|
slot.label,
|
||||||
id
|
id
|
||||||
);
|
); */
|
||||||
|
|
||||||
// if there is a slot of the same name
|
// if there is a slot of the same name
|
||||||
slot.id = *id;
|
//slot.id = *id;
|
||||||
} else {
|
} else {
|
||||||
debug_assert!(!self.slots.contains_key(&slot.id),
|
debug_assert!(!self.slots.contains_key(&slot.label),
|
||||||
"Reuse of id detected in render graph! Pass: {:?}, slot: {:?}",
|
"Reuse of id detected in render graph! Pass: {:?}, slot: {:?}",
|
||||||
desc.label, slot.label,
|
label, slot.label,
|
||||||
);
|
);
|
||||||
|
|
||||||
let res_slot = ResourcedSlot {
|
let res_slot = ResourcedSlot {
|
||||||
|
@ -202,66 +197,69 @@ impl RenderGraph {
|
||||||
value: slot.value.clone().unwrap_or(SlotValue::None),
|
value: slot.value.clone().unwrap_or(SlotValue::None),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.slots.insert(slot.id, res_slot);
|
self.slots.insert(slot.label.clone(), res_slot);
|
||||||
self.slot_label_lookup.insert(slot.label.clone(), slot.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get clones of the bind groups and layouts
|
// get clones of the bind groups and layouts
|
||||||
for (label, bg, bgl) in &desc.bind_groups {
|
for (label, bg, bgl) in &desc.bind_groups {
|
||||||
let bg_id = self.next_id();
|
self.bind_groups.insert(label.clone(), BindGroupEntry {
|
||||||
self.bind_groups.insert(
|
|
||||||
bg_id,
|
|
||||||
BindGroupEntry {
|
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
bg: bg.clone(),
|
bg: bg.clone(),
|
||||||
layout: bgl.clone(),
|
layout: bgl.clone(),
|
||||||
},
|
});
|
||||||
);
|
|
||||||
self.bind_group_names.insert(label.clone().into(), bg_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = self.execution_graph.add_node(desc.id);
|
let label: RenderGraphLabelValue = label.into();
|
||||||
|
let index = self.execution_graph.add_node(label.clone());
|
||||||
|
|
||||||
self.passes.insert(
|
self.passes.insert(
|
||||||
desc.id,
|
label,
|
||||||
PassEntry {
|
PassEntry {
|
||||||
inner: Arc::new(RefCell::new(pass)),
|
inner: Arc::new(RefCell::new(pass)),
|
||||||
desc: Arc::new(desc),
|
desc: Rc::new(RefCell::new(desc)),
|
||||||
graph_index: index,
|
graph_index: index,
|
||||||
|
pipeline: Rc::new(RefCell::new(None)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates all buffers required for the passes, also creates an internal execution path.
|
/// Creates all buffers required for the passes, also creates an internal execution path.
|
||||||
|
///
|
||||||
|
/// This only needs to be ran when the [`Node`]s in the graph change, or they are removed or
|
||||||
|
/// added.
|
||||||
#[instrument(skip(self, device))]
|
#[instrument(skip(self, device))]
|
||||||
pub fn setup(&mut self, device: &wgpu::Device) {
|
pub fn setup(&mut self, device: &wgpu::Device) {
|
||||||
// For all passes, create their pipelines
|
// For all passes, create their pipelines
|
||||||
for pass in self.passes.values() {
|
for pass in self.passes.values_mut() {
|
||||||
if let Some(pipeline_desc) = &pass.desc.pipeline_desc {
|
let desc = (*pass.desc).borrow();
|
||||||
let pipeline = match pass.desc.pass_type {
|
if let Some(pipeline_desc) = &desc.pipeline_desc {
|
||||||
RenderPassType::Render => Pipeline::Render(RenderPipeline::create(
|
let pipeline = match desc.ty {
|
||||||
|
NodeType::Render => Pipeline::Render(RenderPipeline::create(
|
||||||
device,
|
device,
|
||||||
pipeline_desc
|
pipeline_desc
|
||||||
.as_render_pipeline_descriptor()
|
.as_render_pipeline_descriptor()
|
||||||
.expect("got compute pipeline descriptor in a render pass"),
|
.expect("got compute pipeline descriptor in a render pass"),
|
||||||
)),
|
)),
|
||||||
RenderPassType::Compute => Pipeline::Compute(ComputePipeline::create(
|
NodeType::Compute => Pipeline::Compute(ComputePipeline::create(
|
||||||
device,
|
device,
|
||||||
pipeline_desc
|
pipeline_desc
|
||||||
.as_compute_pipeline_descriptor()
|
.as_compute_pipeline_descriptor()
|
||||||
.expect("got render pipeline descriptor in a compute pass"),
|
.expect("got render pipeline descriptor in a compute pass"),
|
||||||
)),
|
)),
|
||||||
RenderPassType::Presenter | RenderPassType::Node => {
|
NodeType::Presenter | NodeType::Node => {
|
||||||
panic!("Present or Node RenderGraph passes should not have a pipeline descriptor!");
|
panic!("Present or Node RenderGraph passes should not have a pipeline descriptor!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
drop(desc);
|
||||||
let res = PipelineResource {
|
let res = PipelineResource {
|
||||||
pipeline,
|
pipeline,
|
||||||
bg_layout_name_lookup: Default::default(),
|
bg_layout_name_lookup: Default::default(),
|
||||||
};
|
};
|
||||||
self.pipelines.insert(pass.desc.id, res);
|
|
||||||
|
let mut pipeline = pass.pipeline.borrow_mut();
|
||||||
|
*pipeline = Some(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,10 +267,16 @@ impl RenderGraph {
|
||||||
#[instrument(skip(self, world))]
|
#[instrument(skip(self, world))]
|
||||||
pub fn prepare(&mut self, world: &mut World) {
|
pub fn prepare(&mut self, world: &mut World) {
|
||||||
// prepare all passes
|
// prepare all passes
|
||||||
let mut context = RenderGraphContext::new(&self.device, &self.queue, None);
|
let mut buffer_writes = VecDeque::<GraphBufferWrite>::new();
|
||||||
for (_, pass) in &mut self.passes {
|
// reserve some buffer writes. not all nodes write so half the amount of them is probably
|
||||||
|
// fine.
|
||||||
|
buffer_writes.reserve(self.passes.len() / 2);
|
||||||
|
|
||||||
|
for (label, pass) in &mut self.passes {
|
||||||
|
let mut context = RenderGraphContext::new(&self.device, &self.queue, None, label.clone());
|
||||||
let mut inner = pass.inner.borrow_mut();
|
let mut inner = pass.inner.borrow_mut();
|
||||||
inner.prepare(world, &mut context);
|
inner.prepare(world, &mut context);
|
||||||
|
buffer_writes.append(&mut context.buffer_writes);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -280,10 +284,10 @@ impl RenderGraph {
|
||||||
let s = debug_span!("queue_buffer_writes");
|
let s = debug_span!("queue_buffer_writes");
|
||||||
let _e = s.enter();
|
let _e = s.enter();
|
||||||
|
|
||||||
while let Some(bufwr) = context.buffer_writes.pop_front() {
|
while let Some(bufwr) = buffer_writes.pop_front() {
|
||||||
let slot = self
|
let slot = self
|
||||||
.slots
|
.slots
|
||||||
.get(&self.slot_id_rc(&bufwr.target_slot).unwrap())
|
.get(&bufwr.target_slot)
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to find slot '{:?}' for buffer write",
|
"Failed to find slot '{:?}' for buffer write",
|
||||||
bufwr.target_slot
|
bufwr.target_slot
|
||||||
|
@ -300,26 +304,25 @@ impl RenderGraph {
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
let mut sorted: VecDeque<u64> = petgraph::algo::toposort(&self.execution_graph, None)
|
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.execution_graph, None)
|
||||||
.expect("RenderGraph had cycled!")
|
.expect("RenderGraph had cycled!")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| self.execution_graph[i.clone()])
|
.map(|i| self.execution_graph[i.clone()].clone())
|
||||||
.collect();
|
.collect();
|
||||||
let path_names = sorted
|
//debug!("Render graph execution order: {:?}", sorted);
|
||||||
.iter()
|
|
||||||
.map(|i| self.pass(*i).unwrap().label.clone())
|
|
||||||
.collect_vec();
|
|
||||||
trace!("Render graph execution order: {:?}", path_names);
|
|
||||||
|
|
||||||
let mut encoders = Vec::with_capacity(self.passes.len() / 2);
|
let mut encoders = Vec::with_capacity(self.passes.len() / 2);
|
||||||
while let Some(pass_id) = sorted.pop_front() {
|
while let Some(pass_label) = sorted.pop_front() {
|
||||||
let pass = self.passes.get(&pass_id).unwrap();
|
let pass = self.passes.get(&pass_label).unwrap();
|
||||||
let pass_inn = pass.inner.clone();
|
let pass_inn = pass.inner.clone();
|
||||||
|
|
||||||
let pass_desc = pass.desc.clone();
|
let pass_desc = pass.desc.clone();
|
||||||
let label = format!("{:?} Encoder", pass_desc.label);
|
let pass_desc = (*pass_desc).borrow();
|
||||||
|
|
||||||
|
let label = format!("{:?} Encoder", pass_label.0);
|
||||||
|
|
||||||
// encoders are not needed for presenter nodes.
|
// encoders are not needed for presenter nodes.
|
||||||
let encoder = if pass_desc.pass_type.should_have_pipeline() {
|
let encoder = if pass_desc.ty.should_have_pipeline() {
|
||||||
Some(
|
Some(
|
||||||
self.device
|
self.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
@ -333,17 +336,17 @@ impl RenderGraph {
|
||||||
// clone of the Rc's is required to appease the borrow checker
|
// clone of the Rc's is required to appease the borrow checker
|
||||||
let device = self.device.clone();
|
let device = self.device.clone();
|
||||||
let queue = self.queue.clone();
|
let queue = self.queue.clone();
|
||||||
let mut context = RenderGraphContext::new(&device, &queue, encoder);
|
let mut context = RenderGraphContext::new(&device, &queue, encoder, pass_label.clone());
|
||||||
|
|
||||||
// all encoders need to be submitted before a presenter node is executed.
|
// all encoders need to be submitted before a presenter node is executed.
|
||||||
if pass_desc.pass_type == RenderPassType::Presenter {
|
if pass_desc.ty == NodeType::Presenter {
|
||||||
trace!("Submitting {} encoderd before presenting", encoders.len());
|
trace!("Submitting {} encoderd before presenting", encoders.len());
|
||||||
self.queue.submit(encoders.drain(..));
|
self.queue.submit(encoders.drain(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("Executing {:?}", pass_desc.label);
|
trace!("Executing {:?}", pass_label.0);
|
||||||
let mut inner = pass_inn.borrow_mut();
|
let mut inner = pass_inn.borrow_mut();
|
||||||
inner.execute(self, &*pass_desc, &mut context);
|
inner.execute(self, &pass_desc, &mut context);
|
||||||
|
|
||||||
if let Some(encoder) = context.encoder {
|
if let Some(encoder) = context.encoder {
|
||||||
encoders.push(encoder.finish());
|
encoders.push(encoder.finish());
|
||||||
|
@ -361,49 +364,52 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {
|
pub fn slot_value<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&SlotValue> {
|
||||||
self.slots.get(&id).map(|s| &s.value)
|
self.slots.get(&label.into()).map(|s| &s.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slot_value_mut(&mut self, id: u64) -> Option<&mut SlotValue> {
|
pub fn slot_value_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut SlotValue> {
|
||||||
self.slots.get_mut(&id).map(|s| &mut s.value)
|
self.slots.get_mut(&label.into()).map(|s| &mut s.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
|
pub fn node_desc<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<NodeDesc>> {
|
||||||
self.passes.get(&id).map(|s| &*s.desc)
|
self.passes.get(&label.into()).map(|s| (*s.desc).borrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pipeline(&self, id: u64) -> &Pipeline {
|
pub fn pipeline<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<Pipeline>> {
|
||||||
&self.pipelines.get(&id).unwrap().pipeline
|
self.passes.get(&label.into())
|
||||||
|
.and_then(|p| {
|
||||||
|
let v = p.pipeline.borrow();
|
||||||
|
|
||||||
|
match &*v {
|
||||||
|
Some(_) => Some(Ref::map(v, |p| &p.as_ref().unwrap().pipeline)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_bind_group(&self, id: u64) -> Option<&Rc<wgpu::BindGroup>> {
|
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> {
|
||||||
self.bind_groups.get(&id).map(|e| &e.bg)
|
self.bind_groups.get(&label.into()).map(|e| &e.bg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group(&self, id: u64) -> &Rc<wgpu::BindGroup> {
|
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> {
|
||||||
self.try_bind_group(id).expect("Unknown id for bind group")
|
self.try_bind_group(label).expect("Unknown id for bind group")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_bind_group_layout(&self, id: u64) -> Option<&Rc<wgpu::BindGroupLayout>> {
|
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> {
|
||||||
self.bind_groups.get(&id).and_then(|e| e.layout.as_ref())
|
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group_layout(&self, id: u64) -> &Rc<wgpu::BindGroupLayout> {
|
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> {
|
||||||
self.try_bind_group_layout(id)
|
self.try_bind_group_layout(label)
|
||||||
.expect("Unknown id for bind group layout")
|
.expect("Unknown id for bind group layout")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn bind_group_id(&self, label: &dyn RenderGraphLabel) -> Option<u64> {
|
|
||||||
self.bind_group_names.get(&label.rc_clone().into()).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel)
|
pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel)
|
||||||
{
|
{
|
||||||
let from = RenderGraphLabelValue::from(from);
|
let from = RenderGraphLabelValue::from(from);
|
||||||
|
@ -412,13 +418,13 @@ impl RenderGraph {
|
||||||
let from_idx = self
|
let from_idx = self
|
||||||
.passes
|
.passes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p.1.desc.label == from)
|
.find(|p| *p.0 == from)
|
||||||
.map(|p| p.1.graph_index)
|
.map(|p| p.1.graph_index)
|
||||||
.expect("Failed to find from pass");
|
.expect("Failed to find from pass");
|
||||||
let to_idx = self
|
let to_idx = self
|
||||||
.passes
|
.passes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p.1.desc.label == to)
|
.find(|p| *p.0 == to)
|
||||||
.map(|p| p.1.graph_index)
|
.map(|p| p.1.graph_index)
|
||||||
.expect("Failed to find to pass");
|
.expect("Failed to find to pass");
|
||||||
|
|
||||||
|
@ -438,13 +444,13 @@ impl RenderGraph {
|
||||||
/// graph.set_bind_groups(
|
/// graph.set_bind_groups(
|
||||||
/// &mut pass,
|
/// &mut pass,
|
||||||
/// &[
|
/// &[
|
||||||
/// // retrieves the "depth_texture" bind group and sets the index 0 in the
|
/// // retrieves the `BasePassSlots::DepthTexture` bind group and sets the index 0 in the
|
||||||
/// // pass to it.
|
/// // pass to it.
|
||||||
/// ("depth_texture", 0),
|
/// (&BasePassSlots::DepthTexture, 0),
|
||||||
/// ("camera", 1),
|
/// (&BasePassSlots::Camera, 1),
|
||||||
/// ("light_buffers", 2),
|
/// (&LightBasePassSlots::Lights, 2),
|
||||||
/// ("light_indices_grid", 3),
|
/// (&LightCullComputePassSlots::LightIndicesGridGroup, 3),
|
||||||
/// ("screen_size", 4),
|
/// (&BasePassSlots::ScreenSize, 4),
|
||||||
/// ],
|
/// ],
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -458,9 +464,8 @@ impl RenderGraph {
|
||||||
) {
|
) {
|
||||||
for (label, index) in bind_groups {
|
for (label, index) in bind_groups {
|
||||||
let bg = self
|
let bg = self
|
||||||
.bind_group_id(*label)
|
.bind_group(label.rc_clone());
|
||||||
.map(|bgi| self.bind_group(bgi))
|
//.expect(&format!("Could not find bind group '{:?}'", label));
|
||||||
.expect(&format!("Could not find bind group '{:?}'", label));
|
|
||||||
|
|
||||||
pass.set_bind_group(*index, bg, &[]);
|
pass.set_bind_group(*index, bg, &[]);
|
||||||
}
|
}
|
||||||
|
@ -477,22 +482,28 @@ pub(crate) struct GraphBufferWrite {
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct RenderGraphContext<'a> {
|
pub struct RenderGraphContext<'a> {
|
||||||
/// Becomes None when the encoder is submitted
|
/// The [`wgpu::CommandEncoder`] used to encode GPU operations.
|
||||||
pub(crate) encoder: Option<wgpu::CommandEncoder>,
|
///
|
||||||
pub(crate) device: &'a wgpu::Device,
|
/// This is `None` during the `prepare` stage.
|
||||||
pub(crate) queue: &'a wgpu::Queue,
|
pub encoder: Option<wgpu::CommandEncoder>,
|
||||||
|
/// The gpu device that is being used.
|
||||||
|
pub device: &'a wgpu::Device,
|
||||||
|
pub queue: &'a wgpu::Queue,
|
||||||
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
|
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
|
||||||
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
||||||
|
/// The label of this Node.
|
||||||
|
pub label: RenderGraphLabelValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderGraphContext<'a> {
|
impl<'a> RenderGraphContext<'a> {
|
||||||
pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>) -> Self {
|
pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>, label: RenderGraphLabelValue) -> Self {
|
||||||
Self {
|
Self {
|
||||||
encoder,
|
encoder,
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
buffer_writes: Default::default(),
|
buffer_writes: Default::default(),
|
||||||
renderpass_desc: vec![],
|
renderpass_desc: vec![],
|
||||||
|
label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, num::NonZeroU32, rc::Rc};
|
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc};
|
||||||
|
|
||||||
|
use bind_match::bind_match;
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
|
|
||||||
use crate::render::resource::PipelineDescriptor;
|
use crate::render::resource::PipelineDescriptor;
|
||||||
|
@ -7,26 +8,31 @@ use crate::render::resource::PipelineDescriptor;
|
||||||
use super::{RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderTarget};
|
use super::{RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderTarget};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub enum RenderPassType {
|
pub enum NodeType {
|
||||||
/// A node doesn't render, compute, or present anything. This likely means it injects data into the graph.
|
/// A node doesn't render, compute, or present anything. This likely means it injects data into the graph.
|
||||||
Node,
|
|
||||||
Compute,
|
|
||||||
#[default]
|
#[default]
|
||||||
|
Node,
|
||||||
|
/// A Compute pass node type.
|
||||||
|
Compute,
|
||||||
|
/// A render pass node type.
|
||||||
Render,
|
Render,
|
||||||
|
/// A node that presents render results to a render target.
|
||||||
Presenter,
|
Presenter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderPassType {
|
impl NodeType {
|
||||||
|
/// Returns a boolean indicating if the node should have a [`Pipeline`](crate::render::resource::Pipeline).
|
||||||
pub fn should_have_pipeline(&self) -> bool {
|
pub fn should_have_pipeline(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RenderPassType::Node => false,
|
NodeType::Node => false,
|
||||||
RenderPassType::Compute => true,
|
NodeType::Compute => true,
|
||||||
RenderPassType::Render => true,
|
NodeType::Render => true,
|
||||||
RenderPassType::Presenter => false,
|
NodeType::Presenter => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of data that is stored in a [`Node`] slot.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum SlotType {
|
pub enum SlotType {
|
||||||
TextureView,
|
TextureView,
|
||||||
|
@ -36,10 +42,13 @@ pub enum SlotType {
|
||||||
RenderTarget,
|
RenderTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The value of a slot in a [`Node`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SlotValue {
|
pub enum SlotValue {
|
||||||
|
/// This slot doesn't have any value
|
||||||
None,
|
None,
|
||||||
/// The value will be set during a later phase of the render graph.
|
/// The value will be set during a later phase of the render graph. To see the type of value
|
||||||
|
/// this will be set to, see the slots type.
|
||||||
Lazy,
|
Lazy,
|
||||||
TextureView(Rc<wgpu::TextureView>),
|
TextureView(Rc<wgpu::TextureView>),
|
||||||
Sampler(Rc<wgpu::Sampler>),
|
Sampler(Rc<wgpu::Sampler>),
|
||||||
|
@ -49,56 +58,49 @@ pub enum SlotValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotValue {
|
impl SlotValue {
|
||||||
/// Gets `self` as a texture, panics if self is not a instance of [`SlotValue::Texture`].
|
pub fn as_texture_view(&self) -> Option<&Rc<wgpu::TextureView>> {
|
||||||
pub fn as_texture(&self) -> &wgpu::Texture {
|
bind_match!(self, Self::TextureView(v) => v)
|
||||||
match self {
|
|
||||||
Self::Texture(v) => v,
|
|
||||||
_ => panic!("self is not an instance of SlotValue::Texture"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_texture_view(&self) -> &wgpu::TextureView {
|
pub fn as_sampler(&self) -> Option<&Rc<wgpu::Sampler>> {
|
||||||
match self {
|
bind_match!(self, Self::Sampler(v) => v)
|
||||||
Self::TextureView(v) => v,
|
|
||||||
_ => panic!("self is not an instance of SlotValue::TextureView"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_buffer(&self) -> Option<&wgpu::Buffer> {
|
pub fn as_texture(&self) -> Option<&Rc<wgpu::Texture>> {
|
||||||
match self {
|
bind_match!(self, Self::Texture(v) => v)
|
||||||
Self::Buffer(v) => Some(v),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_buffer(&self) -> Option<&Rc<wgpu::Buffer>> {
|
||||||
|
bind_match!(self, Self::Buffer(v) => v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_render_target(&self) -> Option<Ref<RenderTarget>> {
|
pub fn as_render_target(&self) -> Option<Ref<RenderTarget>> {
|
||||||
match self {
|
bind_match!(self, Self::RenderTarget(v) => v.borrow())
|
||||||
Self::RenderTarget(v) => Some(v.borrow()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_render_target_mut(&mut self) -> Option<RefMut<RenderTarget>> {
|
pub fn as_render_target_mut(&mut self) -> Option<RefMut<RenderTarget>> {
|
||||||
match self {
|
bind_match!(self, Self::RenderTarget(v) => v.borrow_mut())
|
||||||
Self::RenderTarget(v) => Some(v.borrow_mut()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum SlotAttribute {
|
pub enum SlotAttribute {
|
||||||
|
/// This slot inputs a value into the node, expecting another node to `Output` it.
|
||||||
Input,
|
Input,
|
||||||
|
/// This slot outputs a value from the node, providing the value to other nodes that
|
||||||
|
/// `Input`it.
|
||||||
Output,
|
Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderPassSlot {
|
pub struct NodeSlot {
|
||||||
|
/// The type of the value that this slot inputs/outputs.
|
||||||
pub ty: SlotType,
|
pub ty: SlotType,
|
||||||
|
/// The way this slot uses the value. Defines if this slot is an output or input.
|
||||||
pub attribute: SlotAttribute,
|
pub attribute: SlotAttribute,
|
||||||
pub id: u64,
|
/// The identifying label of this slot.
|
||||||
pub label: RenderGraphLabelValue,
|
pub label: RenderGraphLabelValue,
|
||||||
/// The descriptor of the slot value.
|
/// The value of the slot.
|
||||||
/// This will be `None` if this slot is an input.
|
/// This is `None` if the slot is a `SlotAttribute::Input` type.
|
||||||
pub value: Option<SlotValue>,
|
pub value: Option<SlotValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,13 +159,20 @@ impl RenderGraphPipelineInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderGraphPassDesc {
|
/// Descriptor of a Node in a [`RenderGraph`].
|
||||||
pub id: u64,
|
pub struct NodeDesc {
|
||||||
pub label: RenderGraphLabelValue,
|
/// The [`NodeType`] of the node.
|
||||||
pub pass_type: RenderPassType,
|
pub ty: NodeType,
|
||||||
pub slots: Vec<RenderPassSlot>,
|
/// The slots that the Node uses.
|
||||||
slot_label_lookup: HashMap<RenderGraphLabelValue, u64>,
|
/// This defines the resources that the node uses and creates in the graph.
|
||||||
|
pub slots: Vec<NodeSlot>,
|
||||||
|
//slot_label_lookup: HashMap<RenderGraphLabelValue, u64>,
|
||||||
|
/// An optional pipeline descriptor for the Node.
|
||||||
|
/// This is `None` if the Node type is not a node that requires a pipeline
|
||||||
|
/// (see [`NodeType::should_have_pipeline`]).
|
||||||
pub pipeline_desc: Option<PipelineDescriptor>,
|
pub pipeline_desc: Option<PipelineDescriptor>,
|
||||||
|
/// The bind groups that this Node creates.
|
||||||
|
/// This makes the bind groups accessible to other Nodes.
|
||||||
pub bind_groups: Vec<(
|
pub bind_groups: Vec<(
|
||||||
RenderGraphLabelValue,
|
RenderGraphLabelValue,
|
||||||
Rc<wgpu::BindGroup>,
|
Rc<wgpu::BindGroup>,
|
||||||
|
@ -171,20 +180,16 @@ pub struct RenderGraphPassDesc {
|
||||||
)>,
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPassDesc {
|
impl NodeDesc {
|
||||||
pub fn new<L: RenderGraphLabel>(
|
/// Create a new node descriptor.
|
||||||
id: u64,
|
pub fn new(
|
||||||
label: L,
|
pass_type: NodeType,
|
||||||
pass_type: RenderPassType,
|
|
||||||
pipeline_desc: Option<PipelineDescriptor>,
|
pipeline_desc: Option<PipelineDescriptor>,
|
||||||
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
ty: pass_type,
|
||||||
label: label.into(),
|
|
||||||
pass_type,
|
|
||||||
slots: vec![],
|
slots: vec![],
|
||||||
slot_label_lookup: HashMap::default(),
|
|
||||||
pipeline_desc,
|
pipeline_desc,
|
||||||
bind_groups: bind_groups
|
bind_groups: bind_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -193,20 +198,27 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_slot(&mut self, slot: RenderPassSlot) {
|
/// Add a slot to the descriptor.
|
||||||
|
///
|
||||||
|
/// In debug builds, there is an assert that triggers if the slot is an input slot and has
|
||||||
|
/// a value set.
|
||||||
|
pub fn add_slot(&mut self, slot: NodeSlot) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!(slot.attribute == SlotAttribute::Input && slot.value.is_some()),
|
!(slot.attribute == SlotAttribute::Input && slot.value.is_some()),
|
||||||
"input slots should not have values"
|
"input slots should not have values"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.slot_label_lookup.insert(slot.label.clone().into(), slot.id);
|
|
||||||
self.slots.push(slot);
|
self.slots.push(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a buffer slot to the descriptor.
|
||||||
|
///
|
||||||
|
/// In debug builds, there is an assert that triggers if the slot is an input slot and has
|
||||||
|
/// a value set. There is also an assert that is triggered if this slot value is not `None`,
|
||||||
|
/// `SlotValue::Lazy` or a `Buffer`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_buffer_slot<L: RenderGraphLabel>(
|
pub fn add_buffer_slot<L: RenderGraphLabel>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: u64,
|
|
||||||
label: L,
|
label: L,
|
||||||
attribute: SlotAttribute,
|
attribute: SlotAttribute,
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
|
@ -216,8 +228,7 @@ impl RenderGraphPassDesc {
|
||||||
"slot value is not a buffer"
|
"slot value is not a buffer"
|
||||||
);
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = NodeSlot {
|
||||||
id,
|
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
ty: SlotType::Buffer,
|
ty: SlotType::Buffer,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -226,10 +237,14 @@ impl RenderGraphPassDesc {
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a slot that stores a [`wgpu::Texture`] to the descriptor.
|
||||||
|
///
|
||||||
|
/// In debug builds, there is an assert that triggers if the slot is an input slot and has
|
||||||
|
/// a value set. There is also an assert that is triggered if this slot value is not `None`,
|
||||||
|
/// `SlotValue::Lazy` or a `SlotValue::Texture`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_slot<L: RenderGraphLabel>(
|
pub fn add_texture_slot<L: RenderGraphLabel>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: u64,
|
|
||||||
label: L,
|
label: L,
|
||||||
attribute: SlotAttribute,
|
attribute: SlotAttribute,
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
|
@ -239,8 +254,7 @@ impl RenderGraphPassDesc {
|
||||||
"slot value is not a texture"
|
"slot value is not a texture"
|
||||||
);
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = NodeSlot {
|
||||||
id,
|
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
ty: SlotType::Texture,
|
ty: SlotType::Texture,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -249,10 +263,14 @@ impl RenderGraphPassDesc {
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a slot that stores a [`wgpu::TextureView`] to the descriptor.
|
||||||
|
///
|
||||||
|
/// In debug builds, there is an assert that triggers if the slot is an input slot and has
|
||||||
|
/// a value set. There is also an assert that is triggered if this slot value is not `None`,
|
||||||
|
/// `SlotValue::Lazy` or a `SlotValue::TextureView`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_view_slot<L: RenderGraphLabel>(
|
pub fn add_texture_view_slot<L: RenderGraphLabel>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: u64,
|
|
||||||
label: L,
|
label: L,
|
||||||
attribute: SlotAttribute,
|
attribute: SlotAttribute,
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
|
@ -262,8 +280,7 @@ impl RenderGraphPassDesc {
|
||||||
"slot value is not a texture view"
|
"slot value is not a texture view"
|
||||||
);
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = NodeSlot {
|
||||||
id,
|
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
ty: SlotType::TextureView,
|
ty: SlotType::TextureView,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -272,10 +289,14 @@ impl RenderGraphPassDesc {
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a slot that stores a [`wgpu::Sampler`] to the descriptor.
|
||||||
|
///
|
||||||
|
/// In debug builds, there is an assert that triggers if the slot is an input slot and has
|
||||||
|
/// a value set. There is also an assert that is triggered if this slot value is not `None`,
|
||||||
|
/// `SlotValue::Lazy` or a `SlotValue::Sampler`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_sampler_slot<L: RenderGraphLabel>(
|
pub fn add_sampler_slot<L: RenderGraphLabel>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: u64,
|
|
||||||
label: L,
|
label: L,
|
||||||
attribute: SlotAttribute,
|
attribute: SlotAttribute,
|
||||||
value: Option<SlotValue>,
|
value: Option<SlotValue>,
|
||||||
|
@ -285,8 +306,7 @@ impl RenderGraphPassDesc {
|
||||||
"slot value is not a sampler"
|
"slot value is not a sampler"
|
||||||
);
|
);
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = NodeSlot {
|
||||||
id,
|
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
ty: SlotType::Sampler,
|
ty: SlotType::Sampler,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -295,14 +315,16 @@ impl RenderGraphPassDesc {
|
||||||
self.add_slot(slot);
|
self.add_slot(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_slots(&self) -> Vec<&RenderPassSlot> {
|
/// Returns all input slots that the descriptor defines.
|
||||||
|
pub fn input_slots(&self) -> Vec<&NodeSlot> {
|
||||||
self.slots
|
self.slots
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| s.attribute == SlotAttribute::Input)
|
.filter(|s| s.attribute == SlotAttribute::Input)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_slots(&self) -> Vec<&RenderPassSlot> {
|
/// Returns all output slots that the descriptor defines.
|
||||||
|
pub fn output_slots(&self) -> Vec<&NodeSlot> {
|
||||||
self.slots
|
self.slots
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| s.attribute == SlotAttribute::Output)
|
.filter(|s| s.attribute == SlotAttribute::Output)
|
||||||
|
@ -310,17 +332,34 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RenderGraphPass: 'static {
|
/// A node that can be executed and scheduled in a [`RenderGraph`].
|
||||||
/// Create a render pass describer.
|
|
||||||
///
|
///
|
||||||
/// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
|
/// A node can be used for rendering, computing data on the GPU, collecting data from the main
|
||||||
fn desc<'a, 'b>(&'a mut self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
|
/// world and writing it to GPU buffers, or presenting renders to a surface.
|
||||||
|
///
|
||||||
|
/// The [`RenderGraph`] is ran in phases. The first phase is `prepare`, then `execute`. When a node
|
||||||
|
/// is first added to a RenderGraph, its [`Node::desc`] function will be ran. The descriptor
|
||||||
|
/// describes all resources the node requires for execution during the `execute` phase.
|
||||||
|
pub trait Node: 'static {
|
||||||
|
/// Retrieve a descriptor of the Node.
|
||||||
|
fn desc<'a, 'b>(&'a mut self, graph: &'b mut RenderGraph) -> NodeDesc;
|
||||||
|
|
||||||
|
/// Prepare the node for rendering.
|
||||||
|
///
|
||||||
|
/// This phase runs before `execute` and is meant to be used to collect data from the World
|
||||||
|
/// and write to GPU buffers.
|
||||||
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext);
|
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext);
|
||||||
|
|
||||||
|
/// Execute the node.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// * `graph` - The RenderGraph that this node is a part of. Can be used to get bind groups and bind to them.
|
||||||
|
/// * `desc` - The descriptor of this node.
|
||||||
|
/// * `context` - The rendering graph context.
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut RenderGraph,
|
graph: &mut RenderGraph,
|
||||||
desc: &RenderGraphPassDesc,
|
desc: &NodeDesc,
|
||||||
context: &mut RenderGraphContext,
|
context: &mut RenderGraphContext,
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -9,8 +9,8 @@ use crate::{
|
||||||
render::{
|
render::{
|
||||||
camera::{CameraUniform, RenderCamera},
|
camera::{CameraUniform, RenderCamera},
|
||||||
graph::{
|
graph::{
|
||||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot,
|
RenderGraphContext, Node, NodeDesc, NodeSlot,
|
||||||
RenderPassType, RenderTarget, SlotAttribute, SlotType, SlotValue,
|
NodeType, RenderTarget, SlotAttribute, SlotType, SlotValue,
|
||||||
},
|
},
|
||||||
render_buffer::BufferWrapper, texture::RenderTexture,
|
render_buffer::BufferWrapper, texture::RenderTexture,
|
||||||
},
|
},
|
||||||
|
@ -40,8 +40,6 @@ pub struct BasePass {
|
||||||
/// This should be Some when the pass is first created then after its added to
|
/// This should be Some when the pass is first created then after its added to
|
||||||
/// the render graph it will be None and stay None.
|
/// the render graph it will be None and stay None.
|
||||||
temp_render_target: Option<RenderTarget>,
|
temp_render_target: Option<RenderTarget>,
|
||||||
main_rt_id: u64,
|
|
||||||
window_tv_id: u64,
|
|
||||||
screen_size: glam::UVec2,
|
screen_size: glam::UVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +59,11 @@ impl BasePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for BasePass {
|
impl Node for BasePass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
let render_target = self.temp_render_target.take().unwrap();
|
let render_target = self.temp_render_target.take().unwrap();
|
||||||
self.screen_size = UVec2::new(
|
self.screen_size = UVec2::new(
|
||||||
render_target.surface_config.width,
|
render_target.surface_config.width,
|
||||||
|
@ -101,49 +99,46 @@ impl RenderGraphPass for BasePass {
|
||||||
let depth_texture_bgl = dt_bg_pair.layout;
|
let depth_texture_bgl = dt_bg_pair.layout;
|
||||||
let depth_texture_view = Rc::new(depth_texture.view);
|
let depth_texture_view = Rc::new(depth_texture.view);
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = NodeDesc::new(
|
||||||
graph.next_id(),
|
NodeType::Node,
|
||||||
BasePassLabel,
|
|
||||||
RenderPassType::Node,
|
|
||||||
None,
|
None,
|
||||||
vec![
|
vec![
|
||||||
|
// TODO: Make this a trait maybe?
|
||||||
|
// Could impl it for (RenderGraphLabel, wgpu::BindGroup) and also
|
||||||
|
// (RenderGraphLabel, wgpu::BindGroup, wgpu::BindGroupLabel) AND
|
||||||
|
// (RenderGraphLabel, wgpu::BindGroup, Option<wgpu::BindGroupLabel>)
|
||||||
|
//
|
||||||
|
// This could make it slightly easier to create this
|
||||||
(&BasePassSlots::DepthTexture, depth_texture_bg, Some(depth_texture_bgl)),
|
(&BasePassSlots::DepthTexture, depth_texture_bg, Some(depth_texture_bgl)),
|
||||||
(&BasePassSlots::ScreenSize, screen_size_bg, Some(screen_size_bgl)),
|
(&BasePassSlots::ScreenSize, screen_size_bg, Some(screen_size_bgl)),
|
||||||
(&BasePassSlots::Camera, camera_bg, Some(camera_bgl)),
|
(&BasePassSlots::Camera, camera_bg, Some(camera_bgl)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
self.main_rt_id = graph.next_id();
|
desc.add_slot(NodeSlot {
|
||||||
desc.add_slot(RenderPassSlot {
|
|
||||||
ty: SlotType::RenderTarget,
|
ty: SlotType::RenderTarget,
|
||||||
attribute: SlotAttribute::Output,
|
attribute: SlotAttribute::Output,
|
||||||
id: self.main_rt_id,
|
|
||||||
label: BasePassSlots::MainRenderTarget.into(),
|
label: BasePassSlots::MainRenderTarget.into(),
|
||||||
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
|
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
|
||||||
render_target,
|
render_target,
|
||||||
)))),
|
)))),
|
||||||
});
|
});
|
||||||
self.window_tv_id = graph.next_id();
|
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
self.window_tv_id,
|
|
||||||
BasePassSlots::WindowTextureView,
|
BasePassSlots::WindowTextureView,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Lazy),
|
Some(SlotValue::Lazy),
|
||||||
);
|
);
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
graph.next_id(),
|
|
||||||
BasePassSlots::DepthTextureView,
|
BasePassSlots::DepthTextureView,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::TextureView(depth_texture_view)),
|
Some(SlotValue::TextureView(depth_texture_view)),
|
||||||
);
|
);
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
graph.next_id(),
|
|
||||||
BasePassSlots::ScreenSize,
|
BasePassSlots::ScreenSize,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
|
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
|
||||||
);
|
);
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
graph.next_id(),
|
|
||||||
BasePassSlots::Camera,
|
BasePassSlots::Camera,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(camera_buf))),
|
Some(SlotValue::Buffer(Rc::new(camera_buf))),
|
||||||
|
@ -167,11 +162,11 @@ impl RenderGraphPass for BasePass {
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
_desc: &crate::render::graph::RenderGraphPassDesc,
|
_desc: &crate::render::graph::NodeDesc,
|
||||||
context: &mut crate::render::graph::RenderGraphContext,
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
let tv_slot = graph
|
let tv_slot = graph
|
||||||
.slot_value_mut(self.main_rt_id)
|
.slot_value_mut(BasePassSlots::MainRenderTarget)
|
||||||
.expect("somehow the main render target slot is missing");
|
.expect("somehow the main render target slot is missing");
|
||||||
let mut rt = tv_slot.as_render_target_mut().unwrap();
|
let mut rt = tv_slot.as_render_target_mut().unwrap();
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -197,7 +192,7 @@ impl RenderGraphPass for BasePass {
|
||||||
|
|
||||||
// store the surface texture to the slot
|
// store the surface texture to the slot
|
||||||
let tv_slot = graph
|
let tv_slot = graph
|
||||||
.slot_value_mut(self.window_tv_id)
|
.slot_value_mut(BasePassSlots::WindowTextureView)
|
||||||
.expect("somehow the window texture view slot is missing");
|
.expect("somehow the window texture view slot is missing");
|
||||||
*tv_slot = SlotValue::TextureView(Rc::new(view));
|
*tv_slot = SlotValue::TextureView(Rc::new(view));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
use glam::UVec2;
|
|
||||||
|
|
||||||
use crate::render::{
|
|
||||||
camera::CameraUniform,
|
|
||||||
graph::{
|
|
||||||
BufferInitDescriptor, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SamplerDescriptor, SlotAttribute, SlotDescriptor, TextureDescriptor, TextureViewDescriptor
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Supplies some basic things other passes needs.
|
|
||||||
///
|
|
||||||
/// screen size buffer, camera buffer,
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct DepthPrePass;
|
|
||||||
|
|
||||||
impl DepthPrePass {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderGraphPass for DepthPrePass {
|
|
||||||
fn desc(
|
|
||||||
&self,
|
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
|
||||||
id: &mut u64,
|
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
|
||||||
let mut desc = RenderGraphPassDesc::new(*id, "DepthPrePass", RenderPassType::Compute);
|
|
||||||
*id += 1;
|
|
||||||
|
|
||||||
let size = wgpu::Extent3d {
|
|
||||||
width: graph.surface_config.width,
|
|
||||||
height: graph.surface_config.height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
desc.add_texture_slot(
|
|
||||||
*id,
|
|
||||||
"depth_texture",
|
|
||||||
SlotAttribute::Output,
|
|
||||||
Some(SlotDescriptor::Texture(TextureDescriptor {
|
|
||||||
size,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Depth32Float,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
|
||||||
| wgpu::TextureUsages::TEXTURE_BINDING,
|
|
||||||
view_formats: vec![],
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
*id += 1;
|
|
||||||
|
|
||||||
desc.add_texture_view_slot(
|
|
||||||
*id,
|
|
||||||
"depth_texture_view",
|
|
||||||
SlotAttribute::Output,
|
|
||||||
Some(SlotDescriptor::TextureView(TextureViewDescriptor::default_view("depth_texture"))),
|
|
||||||
);
|
|
||||||
*id += 1;
|
|
||||||
|
|
||||||
desc.add_texture_view_slot(
|
|
||||||
*id,
|
|
||||||
"depth_texture_sampler",
|
|
||||||
SlotAttribute::Output,
|
|
||||||
Some(SlotDescriptor::Sampler(SamplerDescriptor {
|
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
|
||||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: 100.0,
|
|
||||||
..SamplerDescriptor::default_sampler("depth_texture")
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
*id += 1;
|
|
||||||
|
|
||||||
desc
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare(&mut self, world: &mut lyra_ecs::World) {
|
|
||||||
let _ = world;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute(
|
|
||||||
&mut self,
|
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
|
||||||
desc: &crate::render::graph::RenderGraphPassDesc,
|
|
||||||
context: &mut crate::render::graph::RenderGraphContext,
|
|
||||||
) {
|
|
||||||
let _ = (graph, desc, context);
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
use crate::render::{
|
use crate::render::{
|
||||||
graph::{
|
graph::{
|
||||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
|
RenderGraphContext, Node, NodeDesc, NodeType, SlotAttribute,
|
||||||
SlotValue,
|
SlotValue,
|
||||||
},
|
},
|
||||||
light::LightUniformBuffers,
|
light::LightUniformBuffers,
|
||||||
|
@ -30,19 +30,17 @@ impl LightBasePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for LightBasePass {
|
impl Node for LightBasePass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
let device = &graph.device;
|
let device = &graph.device;
|
||||||
self.light_buffers = Some(LightUniformBuffers::new(device));
|
self.light_buffers = Some(LightUniformBuffers::new(device));
|
||||||
let light_buffers = self.light_buffers.as_ref().unwrap();
|
let light_buffers = self.light_buffers.as_ref().unwrap();
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = NodeDesc::new(
|
||||||
graph.next_id(),
|
NodeType::Node,
|
||||||
LightBasePassLabel,
|
|
||||||
RenderPassType::Node,
|
|
||||||
None,
|
None,
|
||||||
vec![(
|
vec![(
|
||||||
&LightBasePassSlots::Lights,
|
&LightBasePassSlots::Lights,
|
||||||
|
@ -52,7 +50,6 @@ impl RenderGraphPass for LightBasePass {
|
||||||
);
|
);
|
||||||
|
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
graph.next_id(),
|
|
||||||
LightBasePassSlots::Lights,
|
LightBasePassSlots::Lights,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(light_buffers.buffer.clone())),
|
Some(SlotValue::Buffer(light_buffers.buffer.clone())),
|
||||||
|
@ -70,7 +67,7 @@ impl RenderGraphPass for LightBasePass {
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
_graph: &mut crate::render::graph::RenderGraph,
|
_graph: &mut crate::render::graph::RenderGraph,
|
||||||
_desc: &crate::render::graph::RenderGraphPassDesc,
|
_desc: &crate::render::graph::NodeDesc,
|
||||||
_context: &mut crate::render::graph::RenderGraphContext,
|
_context: &mut crate::render::graph::RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::render::{
|
use crate::render::{
|
||||||
graph::{
|
graph::{
|
||||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
|
RenderGraphContext, Node, NodeDesc, NodeType, SlotAttribute,
|
||||||
SlotValue,
|
SlotValue,
|
||||||
},
|
},
|
||||||
resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader},
|
resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader},
|
||||||
|
@ -37,11 +37,11 @@ impl LightCullComputePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for LightCullComputePass {
|
impl Node for LightCullComputePass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
let shader = Rc::new(Shader {
|
let shader = Rc::new(Shader {
|
||||||
label: Some("light_cull_comp_shader".into()),
|
label: Some("light_cull_comp_shader".into()),
|
||||||
source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(),
|
source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(),
|
||||||
|
@ -49,8 +49,7 @@ impl RenderGraphPass for LightCullComputePass {
|
||||||
|
|
||||||
// get the size of the work group for the grid
|
// get the size of the work group for the grid
|
||||||
let main_rt = graph
|
let main_rt = graph
|
||||||
.slot_id(&BasePassSlots::MainRenderTarget)
|
.slot_value(BasePassSlots::MainRenderTarget)
|
||||||
.and_then(|s| graph.slot_value(s))
|
|
||||||
.and_then(|s| s.as_render_target())
|
.and_then(|s| s.as_render_target())
|
||||||
.expect("missing main render target");
|
.expect("missing main render target");
|
||||||
self.workgroup_size =
|
self.workgroup_size =
|
||||||
|
@ -169,17 +168,14 @@ impl RenderGraphPass for LightCullComputePass {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
drop(main_rt);
|
drop(main_rt);
|
||||||
let pass_id = graph.next_id();
|
|
||||||
|
|
||||||
let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::DepthTexture).unwrap());
|
let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture);
|
||||||
let camera_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::Camera).unwrap());
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
||||||
let lights_bgl = graph.bind_group_layout(graph.bind_group_id(&LightBasePassSlots::Lights).unwrap());
|
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
|
||||||
let screen_size_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::ScreenSize).unwrap());
|
let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize);
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = NodeDesc::new(
|
||||||
pass_id,
|
NodeType::Compute,
|
||||||
LightCullComputePassLabel,
|
|
||||||
RenderPassType::Compute,
|
|
||||||
Some(PipelineDescriptor::Compute(ComputePipelineDescriptor {
|
Some(PipelineDescriptor::Compute(ComputePipelineDescriptor {
|
||||||
label: Some("light_cull_pipeline".into()),
|
label: Some("light_cull_pipeline".into()),
|
||||||
push_constant_ranges: vec![],
|
push_constant_ranges: vec![],
|
||||||
|
@ -201,20 +197,17 @@ impl RenderGraphPass for LightCullComputePass {
|
||||||
);
|
);
|
||||||
|
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
graph.next_id(),
|
|
||||||
BasePassSlots::WindowTextureView,
|
BasePassSlots::WindowTextureView,
|
||||||
SlotAttribute::Input,
|
SlotAttribute::Input,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
graph.next_id(),
|
|
||||||
BasePassSlots::ScreenSize,
|
BasePassSlots::ScreenSize,
|
||||||
SlotAttribute::Input,
|
SlotAttribute::Input,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
desc.add_buffer_slot(graph.next_id(), BasePassSlots::Camera, SlotAttribute::Input, None);
|
desc.add_buffer_slot(BasePassSlots::Camera, SlotAttribute::Input, None);
|
||||||
desc.add_buffer_slot(
|
desc.add_buffer_slot(
|
||||||
graph.next_id(),
|
|
||||||
LightCullComputePassSlots::IndexCounterBuffer,
|
LightCullComputePassSlots::IndexCounterBuffer,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
|
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
|
||||||
|
@ -228,27 +221,20 @@ impl RenderGraphPass for LightCullComputePass {
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
desc: &crate::render::graph::RenderGraphPassDesc,
|
_: &crate::render::graph::NodeDesc,
|
||||||
context: &mut RenderGraphContext,
|
context: &mut RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
|
let label = context.label.clone();
|
||||||
|
|
||||||
|
let pipeline = graph.pipeline(label)
|
||||||
|
.expect("Failed to find Pipeline for LightCullComputePass");
|
||||||
|
let pipeline = pipeline.as_compute();
|
||||||
|
|
||||||
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||||
label: Some("light_cull_pass"),
|
label: Some("light_cull_pass"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let pipeline = graph.pipeline(desc.id);
|
pass.set_pipeline(pipeline);
|
||||||
pass.set_pipeline(pipeline.as_compute());
|
|
||||||
|
|
||||||
/* let depth_tex_bg = graph.bind_group(graph.bind_group_id("depth_texture").unwrap());
|
|
||||||
let camera_bg = graph.bind_group(graph.bind_group_id("camera").unwrap());
|
|
||||||
let lights_bg = graph.bind_group(graph.bind_group_id("light_buffers").unwrap());
|
|
||||||
let grid_bg = graph.bind_group(graph.bind_group_id("light_indices_grid").unwrap());
|
|
||||||
let screen_size_bg = graph.bind_group(graph.bind_group_id("screen_size").unwrap());
|
|
||||||
|
|
||||||
pass.set_bind_group(0, depth_tex_bg, &[]);
|
|
||||||
pass.set_bind_group(1, camera_bg, &[]);
|
|
||||||
pass.set_bind_group(2, lights_bg, &[]);
|
|
||||||
pass.set_bind_group(3, grid_bg, &[]);
|
|
||||||
pass.set_bind_group(4, screen_size_bg, &[]); */
|
|
||||||
|
|
||||||
graph.set_bind_groups(
|
graph.set_bind_groups(
|
||||||
&mut pass,
|
&mut pass,
|
||||||
|
|
|
@ -15,8 +15,8 @@ use wgpu::util::DeviceExt;
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{
|
render::{
|
||||||
desc_buf_lay::DescVertexBufferLayout, graph::{
|
desc_buf_lay::DescVertexBufferLayout, graph::{
|
||||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc,
|
RenderGraphContext, Node, NodeDesc,
|
||||||
RenderPassType,
|
NodeType,
|
||||||
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex
|
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex
|
||||||
},
|
},
|
||||||
DeltaTime,
|
DeltaTime,
|
||||||
|
@ -201,11 +201,11 @@ impl MeshPass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for MeshPass {
|
impl Node for MeshPass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
|
|
||||||
let device = graph.device();
|
let device = graph.device();
|
||||||
|
|
||||||
|
@ -232,31 +232,24 @@ impl RenderGraphPass for MeshPass {
|
||||||
self.default_texture = Some(RenderTexture::from_bytes(&device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
|
self.default_texture = Some(RenderTexture::from_bytes(&device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
|
||||||
|
|
||||||
// get surface config format
|
// get surface config format
|
||||||
let main_rt = graph.slot_id(&BasePassSlots::MainRenderTarget)
|
let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
|
||||||
.and_then(|s| graph.slot_value(s))
|
|
||||||
.and_then(|s| s.as_render_target())
|
.and_then(|s| s.as_render_target())
|
||||||
.expect("missing main render target");
|
.expect("missing main render target");
|
||||||
let surface_config_format = main_rt.surface_config.format;
|
let surface_config_format = main_rt.surface_config.format;
|
||||||
drop(main_rt);
|
drop(main_rt);
|
||||||
|
|
||||||
// get the id here to make borrow checker happy
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
||||||
let pass_id = graph.next_id();
|
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
|
||||||
|
|
||||||
let camera_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::Camera).unwrap());
|
|
||||||
let lights_bgl = graph.bind_group_layout(graph.bind_group_id(&LightBasePassSlots::Lights).unwrap());
|
|
||||||
let light_grid_bgl = graph
|
let light_grid_bgl = graph
|
||||||
.bind_group_layout(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup)
|
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||||
.expect("Missing light grid bind group"));
|
|
||||||
|
|
||||||
let shader = Rc::new(Shader {
|
let shader = Rc::new(Shader {
|
||||||
label: Some("base_shader".into()),
|
label: Some("base_shader".into()),
|
||||||
source: include_str!("../../shaders/base.wgsl").to_string(),
|
source: include_str!("../../shaders/base.wgsl").to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let desc = RenderGraphPassDesc::new(
|
let desc = NodeDesc::new(
|
||||||
pass_id,
|
NodeType::Render,
|
||||||
MeshesPassLabel,
|
|
||||||
RenderPassType::Render,
|
|
||||||
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
|
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
|
||||||
label: Some("meshes".into()),
|
label: Some("meshes".into()),
|
||||||
layouts: vec![
|
layouts: vec![
|
||||||
|
@ -445,36 +438,37 @@ impl RenderGraphPass for MeshPass {
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
desc: &crate::render::graph::RenderGraphPassDesc,
|
_: &crate::render::graph::NodeDesc,
|
||||||
context: &mut crate::render::graph::RenderGraphContext,
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
let encoder = context.encoder.as_mut().unwrap();
|
let encoder = context.encoder.as_mut().unwrap();
|
||||||
|
|
||||||
let view = graph
|
let view = graph
|
||||||
.slot_value(graph.slot_id(&BasePassSlots::WindowTextureView).unwrap())
|
.slot_value(BasePassSlots::WindowTextureView)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_texture_view();
|
.as_texture_view()
|
||||||
|
.expect("BasePassSlots::WindowTextureView was not a TextureView slot");
|
||||||
|
|
||||||
let depth_view = graph
|
let depth_view = graph
|
||||||
.slot_value(graph.slot_id(&BasePassSlots::DepthTextureView).unwrap())
|
.slot_value(BasePassSlots::DepthTextureView)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_texture_view();
|
.as_texture_view()
|
||||||
|
.expect("BasePassSlots::DepthTextureView was not a TextureView slot");
|
||||||
|
|
||||||
let camera_bg = graph
|
let camera_bg = graph
|
||||||
.bind_group(graph.bind_group_id(&BasePassSlots::Camera)
|
.bind_group(BasePassSlots::Camera);
|
||||||
.expect("Missing camera bind group"));
|
|
||||||
|
|
||||||
let lights_bg = graph
|
let lights_bg = graph
|
||||||
.bind_group(graph.bind_group_id(&LightBasePassSlots::Lights)
|
.bind_group(LightBasePassSlots::Lights);
|
||||||
.expect("Missing lights bind group"));
|
|
||||||
|
|
||||||
let light_grid_bg = graph
|
let light_grid_bg = graph
|
||||||
.bind_group(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup)
|
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||||
.expect("Missing light grid bind group"));
|
|
||||||
|
|
||||||
let material_bg = graph
|
let material_bg = graph
|
||||||
.bind_group(graph.bind_group_id(&MeshesPassSlots::Material)
|
.bind_group(MeshesPassSlots::Material);
|
||||||
.expect("Missing material bind group"));
|
|
||||||
|
let pipeline = graph.pipeline(context.label.clone())
|
||||||
|
.expect("Failed to find pipeline for MeshPass");
|
||||||
|
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("Render Pass"),
|
label: Some("Render Pass"),
|
||||||
|
@ -502,7 +496,6 @@ impl RenderGraphPass for MeshPass {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
let pipeline = graph.pipeline(desc.id);
|
|
||||||
pass.set_pipeline(&pipeline.as_render());
|
pass.set_pipeline(&pipeline.as_render());
|
||||||
|
|
||||||
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();
|
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
use crate::render::graph::{RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, SlotAttribute, SlotType};
|
use crate::render::graph::{RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, Node, NodeDesc, NodeSlot, NodeType, SlotAttribute, SlotType};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
|
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
|
||||||
pub struct PresentPassLabel(RenderGraphLabelValue);
|
pub struct PresentPassLabel(RenderGraphLabelValue);
|
||||||
|
@ -18,8 +18,7 @@ impl PresentPassLabel {
|
||||||
/// screen size buffer, camera buffer,
|
/// screen size buffer, camera buffer,
|
||||||
pub struct PresentPass {
|
pub struct PresentPass {
|
||||||
/// Label of this pass
|
/// Label of this pass
|
||||||
label: PresentPassLabel,
|
pub label: PresentPassLabel,
|
||||||
//render_target_slot: Rc<dyn RenderGraphLabel>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PresentPass {
|
impl PresentPass {
|
||||||
|
@ -31,21 +30,18 @@ impl PresentPass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for PresentPass {
|
impl Node for PresentPass {
|
||||||
fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc {
|
fn desc(&mut self, _graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc {
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = NodeDesc::new(
|
||||||
graph.next_id(),
|
NodeType::Presenter,
|
||||||
self.label.clone(),
|
|
||||||
RenderPassType::Presenter,
|
|
||||||
None,
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
|
|
||||||
desc.add_slot(
|
desc.add_slot(
|
||||||
RenderPassSlot {
|
NodeSlot {
|
||||||
ty: SlotType::RenderTarget,
|
ty: SlotType::RenderTarget,
|
||||||
attribute: SlotAttribute::Input,
|
attribute: SlotAttribute::Input,
|
||||||
id: graph.next_id(),
|
|
||||||
label: self.label.0.clone(),
|
label: self.label.0.clone(),
|
||||||
value: None,
|
value: None,
|
||||||
}
|
}
|
||||||
|
@ -58,10 +54,10 @@ impl RenderGraphPass for PresentPass {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::RenderGraphPassDesc, _context: &mut crate::render::graph::RenderGraphContext) {
|
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::NodeDesc, _context: &mut crate::render::graph::RenderGraphContext) {
|
||||||
let id = graph.slot_id_rc(&self.label.0)
|
let mut slot = graph.slot_value_mut(self.label.0.clone())
|
||||||
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0));
|
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0))
|
||||||
let mut slot = graph.slot_value_mut(id).unwrap().as_render_target_mut().unwrap();
|
.as_render_target_mut().unwrap();
|
||||||
let surf_tex = slot.current_texture.take().unwrap();
|
let surf_tex = slot.current_texture.take().unwrap();
|
||||||
surf_tex.present();
|
surf_tex.present();
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,19 +125,20 @@ impl BasicRenderer {
|
||||||
let mut g = RenderGraph::new(device.clone(), queue.clone());
|
let mut g = RenderGraph::new(device.clone(), queue.clone());
|
||||||
|
|
||||||
debug!("Adding base pass");
|
debug!("Adding base pass");
|
||||||
g.add_pass(BasePass::new(surface, config));
|
g.add_pass(BasePassLabel, BasePass::new(surface, config));
|
||||||
debug!("Adding light base pass");
|
debug!("Adding light base pass");
|
||||||
g.add_pass(LightBasePass::new());
|
g.add_pass(LightBasePassLabel, LightBasePass::new());
|
||||||
debug!("Adding light cull compute pass");
|
debug!("Adding light cull compute pass");
|
||||||
g.add_pass(LightCullComputePass::new(size));
|
g.add_pass(LightCullComputePassLabel, LightCullComputePass::new(size));
|
||||||
//debug!("Adding triangle pass");
|
//debug!("Adding triangle pass");
|
||||||
//g.add_pass(TrianglePass::new());
|
//g.add_pass(TrianglePass::new());
|
||||||
|
|
||||||
debug!("Adding mesh pass");
|
debug!("Adding mesh pass");
|
||||||
g.add_pass(MeshPass::new());
|
g.add_pass(MeshesPassLabel, MeshPass::new());
|
||||||
|
|
||||||
debug!("Adding present pass");
|
debug!("Adding present pass");
|
||||||
g.add_pass(PresentPass::new(BasePassSlots::MainRenderTarget));
|
let p = PresentPass::new(BasePassSlots::MainRenderTarget);
|
||||||
|
g.add_pass(p.label.clone(), p);
|
||||||
|
|
||||||
g.add_edge(BasePassLabel, LightBasePassLabel);
|
g.add_edge(BasePassLabel, LightBasePassLabel);
|
||||||
g.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
g.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
||||||
|
@ -188,7 +189,7 @@ impl Renderer for BasicRenderer {
|
||||||
self.size = new_size;
|
self.size = new_size;
|
||||||
|
|
||||||
// update surface config and the surface
|
// update surface config and the surface
|
||||||
let mut rt = self.graph.slot_value_mut(self.graph.slot_id(&BasePassSlots::MainRenderTarget).unwrap())
|
let mut rt = self.graph.slot_value_mut(BasePassSlots::MainRenderTarget)
|
||||||
.unwrap().as_render_target_mut().unwrap();
|
.unwrap().as_render_target_mut().unwrap();
|
||||||
rt.surface_config.width = new_size.width;
|
rt.surface_config.width = new_size.width;
|
||||||
rt.surface_config.height = new_size.height;
|
rt.surface_config.height = new_size.height;
|
||||||
|
|
Loading…
Reference in New Issue