Compare commits

..

No commits in common. "6d7932f6a5f01b877ed141383b177885ba4b88aa" and "ef68b2a4c5bb6d95a2ca5bd35e1023a74c47c8f5" have entirely different histories.

11 changed files with 403 additions and 335 deletions

7
Cargo.lock generated
View File

@ -384,12 +384,6 @@ 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"
@ -1865,7 +1859,6 @@ dependencies = [
"anyhow", "anyhow",
"async-std", "async-std",
"async-trait", "async-trait",
"bind_match",
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"gilrs-core", "gilrs-core",

View File

@ -37,7 +37,6 @@ 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"]

View File

@ -1,10 +1,11 @@
mod node; mod pass;
use std::{ use std::{
cell::{Ref, RefCell}, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc cell::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 node::*; pub use pass::*;
mod passes; mod passes;
pub use passes::*; pub use passes::*;
@ -33,8 +34,6 @@ 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>);
@ -77,11 +76,10 @@ impl PartialEq for RenderGraphLabelValue {
impl Eq for RenderGraphLabelValue {} impl Eq for RenderGraphLabelValue {}
struct PassEntry { struct PassEntry {
inner: Arc<RefCell<dyn Node>>, inner: Arc<RefCell<dyn RenderGraphPass>>,
desc: Rc<RefCell<NodeDesc>>, desc: Arc<RenderGraphPassDesc>,
/// 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 {
@ -118,19 +116,20 @@ 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<RenderGraphLabelValue, ResourcedSlot>, slots: FxHashMap<u64, 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<RenderGraphLabelValue, PassEntry>, passes: FxHashMap<u64, PassEntry>,
// TODO: Use a SlotMap // TODO: Use a SlotMap
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>, bind_groups: FxHashMap<u64, 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<RenderGraphLabelValue, (), Option<()>, usize>, execution_graph: petgraph::matrix_graph::DiMatrix<u64, (), Option<()>, usize>,
} }
impl RenderGraph { impl RenderGraph {
@ -139,8 +138,12 @@ 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(),
} }
} }
@ -149,46 +152,48 @@ impl RenderGraph {
&*self.device &*self.device
} }
/// Add a [`Node`] to the RenderGraph. pub fn next_id(&mut self) -> u64 {
/// self.current_id += 1;
/// When the node is added, its [`Node::desc`] method will be executed. self.current_id
/// }
/// Additionally, all [`Slot`](node::NodeSlot)s of the node will be iterated,
/// 1. Ensuring that there are no two slots of the same name, with different value types pub(crate) fn slot_id_rc(&self, label: &RenderGraphLabelValue) -> Option<u64> {
/// 2. Changing the id of insert slots to match the id of the output slot of the same name. self.slot_label_lookup.get(&label.clone().into()).cloned()
/// * 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.
/// 3. Ensuring that no two slots share the same ID when the names do not match. pub fn slot_id(&self, label: &dyn RenderGraphLabel) -> Option<u64> {
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: Node>(&mut self, label: impl RenderGraphLabel, mut pass: P) { pub fn add_pass<P: RenderGraphPass>(&mut self, 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(other) = self if let Some((id, other)) = self
.slots .slot_label_lookup
.get_mut(&slot.label) .get(&slot.label)
//.map(|s| (id, s)) .and_then(|id| self.slots.get_mut(id).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, label slot.label, desc.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.label), debug_assert!(!self.slots.contains_key(&slot.id),
"Reuse of id detected in render graph! Pass: {:?}, slot: {:?}", "Reuse of id detected in render graph! Pass: {:?}, slot: {:?}",
label, slot.label, desc.label, slot.label,
); );
let res_slot = ResourcedSlot { let res_slot = ResourcedSlot {
@ -197,69 +202,66 @@ impl RenderGraph {
value: slot.value.clone().unwrap_or(SlotValue::None), value: slot.value.clone().unwrap_or(SlotValue::None),
}; };
self.slots.insert(slot.label.clone(), res_slot); self.slots.insert(slot.id, 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 {
self.bind_groups.insert(label.clone(), BindGroupEntry { let bg_id = self.next_id();
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 label: RenderGraphLabelValue = label.into(); let index = self.execution_graph.add_node(desc.id);
let index = self.execution_graph.add_node(label.clone());
self.passes.insert( self.passes.insert(
label, desc.id,
PassEntry { PassEntry {
inner: Arc::new(RefCell::new(pass)), inner: Arc::new(RefCell::new(pass)),
desc: Rc::new(RefCell::new(desc)), desc: Arc::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_mut() { for pass in self.passes.values() {
let desc = (*pass.desc).borrow(); if let Some(pipeline_desc) = &pass.desc.pipeline_desc {
if let Some(pipeline_desc) = &desc.pipeline_desc { let pipeline = match pass.desc.pass_type {
let pipeline = match desc.ty { RenderPassType::Render => Pipeline::Render(RenderPipeline::create(
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"),
)), )),
NodeType::Compute => Pipeline::Compute(ComputePipeline::create( RenderPassType::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"),
)), )),
NodeType::Presenter | NodeType::Node => { RenderPassType::Presenter | RenderPassType::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);
} }
} }
} }
@ -267,16 +269,10 @@ 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 buffer_writes = VecDeque::<GraphBufferWrite>::new(); let mut context = RenderGraphContext::new(&self.device, &self.queue, None);
// reserve some buffer writes. not all nodes write so half the amount of them is probably for (_, pass) in &mut self.passes {
// 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);
} }
{ {
@ -284,10 +280,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) = buffer_writes.pop_front() { while let Some(bufwr) = context.buffer_writes.pop_front() {
let slot = self let slot = self
.slots .slots
.get(&bufwr.target_slot) .get(&self.slot_id_rc(&bufwr.target_slot).unwrap())
.expect(&format!( .expect(&format!(
"Failed to find slot '{:?}' for buffer write", "Failed to find slot '{:?}' for buffer write",
bufwr.target_slot bufwr.target_slot
@ -304,25 +300,26 @@ impl RenderGraph {
#[instrument(skip(self))] #[instrument(skip(self))]
pub fn render(&mut self) { pub fn render(&mut self) {
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.execution_graph, None) let mut sorted: VecDeque<u64> = petgraph::algo::toposort(&self.execution_graph, None)
.expect("RenderGraph had cycled!") .expect("RenderGraph had cycled!")
.iter() .iter()
.map(|i| self.execution_graph[i.clone()].clone()) .map(|i| self.execution_graph[i.clone()])
.collect(); .collect();
//debug!("Render graph execution order: {:?}", sorted); let path_names = 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_label) = sorted.pop_front() { while let Some(pass_id) = sorted.pop_front() {
let pass = self.passes.get(&pass_label).unwrap(); let pass = self.passes.get(&pass_id).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 pass_desc = (*pass_desc).borrow(); let label = format!("{:?} Encoder", pass_desc.label);
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.ty.should_have_pipeline() { let encoder = if pass_desc.pass_type.should_have_pipeline() {
Some( Some(
self.device self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { .create_command_encoder(&wgpu::CommandEncoderDescriptor {
@ -336,17 +333,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, pass_label.clone()); let mut context = RenderGraphContext::new(&device, &queue, encoder);
// 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.ty == NodeType::Presenter { if pass_desc.pass_type == RenderPassType::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_label.0); trace!("Executing {:?}", pass_desc.label);
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());
@ -364,52 +361,49 @@ impl RenderGraph {
} }
} }
pub fn slot_value<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&SlotValue> { pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {
self.slots.get(&label.into()).map(|s| &s.value) self.slots.get(&id).map(|s| &s.value)
} }
pub fn slot_value_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut SlotValue> { pub fn slot_value_mut(&mut self, id: u64) -> Option<&mut SlotValue> {
self.slots.get_mut(&label.into()).map(|s| &mut s.value) self.slots.get_mut(&id).map(|s| &mut s.value)
} }
pub fn node_desc<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<NodeDesc>> { pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
self.passes.get(&label.into()).map(|s| (*s.desc).borrow()) self.passes.get(&id).map(|s| &*s.desc)
} }
#[inline(always)] #[inline(always)]
pub fn pipeline<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<Pipeline>> { pub fn pipeline(&self, id: u64) -> &Pipeline {
self.passes.get(&label.into()) &self.pipelines.get(&id).unwrap().pipeline
.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<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> { pub fn try_bind_group(&self, id: u64) -> Option<&Rc<wgpu::BindGroup>> {
self.bind_groups.get(&label.into()).map(|e| &e.bg) self.bind_groups.get(&id).map(|e| &e.bg)
} }
#[inline(always)] #[inline(always)]
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> { pub fn bind_group(&self, id: u64) -> &Rc<wgpu::BindGroup> {
self.try_bind_group(label).expect("Unknown id for bind group") self.try_bind_group(id).expect("Unknown id for bind group")
} }
#[inline(always)] #[inline(always)]
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> { pub fn try_bind_group_layout(&self, id: u64) -> Option<&Rc<wgpu::BindGroupLayout>> {
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref()) self.bind_groups.get(&id).and_then(|e| e.layout.as_ref())
} }
#[inline(always)] #[inline(always)]
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> { pub fn bind_group_layout(&self, id: u64) -> &Rc<wgpu::BindGroupLayout> {
self.try_bind_group_layout(label) self.try_bind_group_layout(id)
.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);
@ -418,13 +412,13 @@ impl RenderGraph {
let from_idx = self let from_idx = self
.passes .passes
.iter() .iter()
.find(|p| *p.0 == from) .find(|p| p.1.desc.label == 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.0 == to) .find(|p| p.1.desc.label == 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");
@ -444,13 +438,13 @@ impl RenderGraph {
/// graph.set_bind_groups( /// graph.set_bind_groups(
/// &mut pass, /// &mut pass,
/// &[ /// &[
/// // retrieves the `BasePassSlots::DepthTexture` bind group and sets the index 0 in the /// // retrieves the "depth_texture" bind group and sets the index 0 in the
/// // pass to it. /// // pass to it.
/// (&BasePassSlots::DepthTexture, 0), /// ("depth_texture", 0),
/// (&BasePassSlots::Camera, 1), /// ("camera", 1),
/// (&LightBasePassSlots::Lights, 2), /// ("light_buffers", 2),
/// (&LightCullComputePassSlots::LightIndicesGridGroup, 3), /// ("light_indices_grid", 3),
/// (&BasePassSlots::ScreenSize, 4), /// ("screen_size", 4),
/// ], /// ],
/// ); /// );
/// ``` /// ```
@ -464,8 +458,9 @@ impl RenderGraph {
) { ) {
for (label, index) in bind_groups { for (label, index) in bind_groups {
let bg = self let bg = self
.bind_group(label.rc_clone()); .bind_group_id(*label)
//.expect(&format!("Could not find bind group '{:?}'", label)); .map(|bgi| self.bind_group(bgi))
.expect(&format!("Could not find bind group '{:?}'", label));
pass.set_bind_group(*index, bg, &[]); pass.set_bind_group(*index, bg, &[]);
} }
@ -482,28 +477,22 @@ pub(crate) struct GraphBufferWrite {
#[allow(dead_code)] #[allow(dead_code)]
pub struct RenderGraphContext<'a> { pub struct RenderGraphContext<'a> {
/// The [`wgpu::CommandEncoder`] used to encode GPU operations. /// Becomes None when the encoder is submitted
/// pub(crate) encoder: Option<wgpu::CommandEncoder>,
/// This is `None` during the `prepare` stage. pub(crate) device: &'a wgpu::Device,
pub encoder: Option<wgpu::CommandEncoder>, pub(crate) queue: &'a wgpu::Queue,
/// 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>, label: RenderGraphLabelValue) -> Self { pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>) -> Self {
Self { Self {
encoder, encoder,
device, device,
queue, queue,
buffer_writes: Default::default(), buffer_writes: Default::default(),
renderpass_desc: vec![], renderpass_desc: vec![],
label,
} }
} }

View File

@ -1,6 +1,5 @@
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc}; use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, 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;
@ -8,31 +7,26 @@ 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 NodeType { pub enum RenderPassType {
/// 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.
#[default]
Node, Node,
/// A Compute pass node type.
Compute, Compute,
/// A render pass node type. #[default]
Render, Render,
/// A node that presents render results to a render target.
Presenter, Presenter,
} }
impl NodeType { impl RenderPassType {
/// 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 {
NodeType::Node => false, RenderPassType::Node => false,
NodeType::Compute => true, RenderPassType::Compute => true,
NodeType::Render => true, RenderPassType::Render => true,
NodeType::Presenter => false, RenderPassType::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,
@ -42,13 +36,10 @@ 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. To see the type of value /// The value will be set during a later phase of the render graph.
/// 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>),
@ -58,49 +49,56 @@ pub enum SlotValue {
} }
impl SlotValue { impl SlotValue {
pub fn as_texture_view(&self) -> Option<&Rc<wgpu::TextureView>> { /// Gets `self` as a texture, panics if self is not a instance of [`SlotValue::Texture`].
bind_match!(self, Self::TextureView(v) => v) pub fn as_texture(&self) -> &wgpu::Texture {
match self {
Self::Texture(v) => v,
_ => panic!("self is not an instance of SlotValue::Texture"),
}
} }
pub fn as_sampler(&self) -> Option<&Rc<wgpu::Sampler>> { pub fn as_texture_view(&self) -> &wgpu::TextureView {
bind_match!(self, Self::Sampler(v) => v) match self {
Self::TextureView(v) => v,
_ => panic!("self is not an instance of SlotValue::TextureView"),
}
} }
pub fn as_texture(&self) -> Option<&Rc<wgpu::Texture>> { pub fn as_buffer(&self) -> Option<&wgpu::Buffer> {
bind_match!(self, Self::Texture(v) => v) match self {
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>> {
bind_match!(self, Self::RenderTarget(v) => v.borrow()) match self {
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>> {
bind_match!(self, Self::RenderTarget(v) => v.borrow_mut()) match self {
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 NodeSlot { pub struct RenderPassSlot {
/// 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,
/// The identifying label of this slot. pub id: u64,
pub label: RenderGraphLabelValue, pub label: RenderGraphLabelValue,
/// The value of the slot. /// The descriptor of the slot value.
/// This is `None` if the slot is a `SlotAttribute::Input` type. /// This will be `None` if this slot is an input.
pub value: Option<SlotValue>, pub value: Option<SlotValue>,
} }
@ -159,20 +157,13 @@ impl RenderGraphPipelineInfo {
} }
} }
/// Descriptor of a Node in a [`RenderGraph`]. pub struct RenderGraphPassDesc {
pub struct NodeDesc { pub id: u64,
/// The [`NodeType`] of the node. pub label: RenderGraphLabelValue,
pub ty: NodeType, pub pass_type: RenderPassType,
/// The slots that the Node uses. pub slots: Vec<RenderPassSlot>,
/// This defines the resources that the node uses and creates in the graph. slot_label_lookup: HashMap<RenderGraphLabelValue, u64>,
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>,
@ -180,16 +171,20 @@ pub struct NodeDesc {
)>, )>,
} }
impl NodeDesc { impl RenderGraphPassDesc {
/// Create a new node descriptor. pub fn new<L: RenderGraphLabel>(
pub fn new( id: u64,
pass_type: NodeType, label: L,
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 {
ty: pass_type, id,
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()
@ -198,27 +193,20 @@ impl NodeDesc {
} }
} }
/// Add a slot to the descriptor. pub fn add_slot(&mut self, slot: RenderPassSlot) {
///
/// 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>,
@ -228,7 +216,8 @@ impl NodeDesc {
"slot value is not a buffer" "slot value is not a buffer"
); );
let slot = NodeSlot { let slot = RenderPassSlot {
id,
label: label.into(), label: label.into(),
ty: SlotType::Buffer, ty: SlotType::Buffer,
attribute, attribute,
@ -237,14 +226,10 @@ impl NodeDesc {
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>,
@ -254,7 +239,8 @@ impl NodeDesc {
"slot value is not a texture" "slot value is not a texture"
); );
let slot = NodeSlot { let slot = RenderPassSlot {
id,
label: label.into(), label: label.into(),
ty: SlotType::Texture, ty: SlotType::Texture,
attribute, attribute,
@ -263,14 +249,10 @@ impl NodeDesc {
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>,
@ -280,7 +262,8 @@ impl NodeDesc {
"slot value is not a texture view" "slot value is not a texture view"
); );
let slot = NodeSlot { let slot = RenderPassSlot {
id,
label: label.into(), label: label.into(),
ty: SlotType::TextureView, ty: SlotType::TextureView,
attribute, attribute,
@ -289,14 +272,10 @@ impl NodeDesc {
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>,
@ -306,7 +285,8 @@ impl NodeDesc {
"slot value is not a sampler" "slot value is not a sampler"
); );
let slot = NodeSlot { let slot = RenderPassSlot {
id,
label: label.into(), label: label.into(),
ty: SlotType::Sampler, ty: SlotType::Sampler,
attribute, attribute,
@ -315,16 +295,14 @@ impl NodeDesc {
self.add_slot(slot); self.add_slot(slot);
} }
/// Returns all input slots that the descriptor defines. pub fn input_slots(&self) -> Vec<&RenderPassSlot> {
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()
} }
/// Returns all output slots that the descriptor defines. pub fn output_slots(&self) -> Vec<&RenderPassSlot> {
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)
@ -332,34 +310,17 @@ impl NodeDesc {
} }
} }
/// A node that can be executed and scheduled in a [`RenderGraph`]. pub trait RenderGraphPass: 'static {
/// /// Create a render pass describer.
/// A node can be used for rendering, computing data on the GPU, collecting data from the main
/// 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 /// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
/// and write to GPU buffers. fn desc<'a, 'b>(&'a mut self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
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: &NodeDesc, desc: &RenderGraphPassDesc,
context: &mut RenderGraphContext, context: &mut RenderGraphContext,
); );
} }

View File

@ -9,8 +9,8 @@ use crate::{
render::{ render::{
camera::{CameraUniform, RenderCamera}, camera::{CameraUniform, RenderCamera},
graph::{ graph::{
RenderGraphContext, Node, NodeDesc, NodeSlot, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot,
NodeType, RenderTarget, SlotAttribute, SlotType, SlotValue, RenderPassType, RenderTarget, SlotAttribute, SlotType, SlotValue,
}, },
render_buffer::BufferWrapper, texture::RenderTexture, render_buffer::BufferWrapper, texture::RenderTexture,
}, },
@ -40,6 +40,8 @@ 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,
} }
@ -59,11 +61,11 @@ impl BasePass {
} }
} }
impl Node for BasePass { impl RenderGraphPass 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::NodeDesc { ) -> crate::render::graph::RenderGraphPassDesc {
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,
@ -99,46 +101,49 @@ impl Node 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 = NodeDesc::new( let mut desc = RenderGraphPassDesc::new(
NodeType::Node, graph.next_id(),
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)),
], ],
); );
desc.add_slot(NodeSlot { self.main_rt_id = graph.next_id();
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))),
@ -162,11 +167,11 @@ impl Node 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::NodeDesc, _desc: &crate::render::graph::RenderGraphPassDesc,
context: &mut crate::render::graph::RenderGraphContext, context: &mut crate::render::graph::RenderGraphContext,
) { ) {
let tv_slot = graph let tv_slot = graph
.slot_value_mut(BasePassSlots::MainRenderTarget) .slot_value_mut(self.main_rt_id)
.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!(
@ -192,7 +197,7 @@ impl Node 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(BasePassSlots::WindowTextureView) .slot_value_mut(self.window_tv_id)
.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));
} }

View File

@ -0,0 +1,94 @@
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!()
}
}

View File

@ -2,7 +2,7 @@ use lyra_game_derive::RenderGraphLabel;
use crate::render::{ use crate::render::{
graph::{ graph::{
RenderGraphContext, Node, NodeDesc, NodeType, SlotAttribute, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
SlotValue, SlotValue,
}, },
light::LightUniformBuffers, light::LightUniformBuffers,
@ -30,17 +30,19 @@ impl LightBasePass {
} }
} }
impl Node for LightBasePass { impl RenderGraphPass 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::NodeDesc { ) -> crate::render::graph::RenderGraphPassDesc {
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 = NodeDesc::new( let mut desc = RenderGraphPassDesc::new(
NodeType::Node, graph.next_id(),
LightBasePassLabel,
RenderPassType::Node,
None, None,
vec![( vec![(
&LightBasePassSlots::Lights, &LightBasePassSlots::Lights,
@ -50,6 +52,7 @@ impl Node 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())),
@ -67,7 +70,7 @@ impl Node 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::NodeDesc, _desc: &crate::render::graph::RenderGraphPassDesc,
_context: &mut crate::render::graph::RenderGraphContext, _context: &mut crate::render::graph::RenderGraphContext,
) { ) {
} }

View File

@ -6,7 +6,7 @@ use wgpu::util::DeviceExt;
use crate::render::{ use crate::render::{
graph::{ graph::{
RenderGraphContext, Node, NodeDesc, NodeType, SlotAttribute, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
SlotValue, SlotValue,
}, },
resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader}, resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader},
@ -37,11 +37,11 @@ impl LightCullComputePass {
} }
} }
impl Node for LightCullComputePass { impl RenderGraphPass 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::NodeDesc { ) -> crate::render::graph::RenderGraphPassDesc {
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,7 +49,8 @@ impl Node 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_value(BasePassSlots::MainRenderTarget) .slot_id(&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 =
@ -168,14 +169,17 @@ impl Node for LightCullComputePass {
})); }));
drop(main_rt); drop(main_rt);
let pass_id = graph.next_id();
let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture); let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::DepthTexture).unwrap());
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); let camera_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::Camera).unwrap());
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); let lights_bgl = graph.bind_group_layout(graph.bind_group_id(&LightBasePassSlots::Lights).unwrap());
let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize); let screen_size_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::ScreenSize).unwrap());
let mut desc = NodeDesc::new( let mut desc = RenderGraphPassDesc::new(
NodeType::Compute, pass_id,
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![],
@ -197,17 +201,20 @@ impl Node 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(BasePassSlots::Camera, SlotAttribute::Input, None); desc.add_buffer_slot(graph.next_id(), 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))),
@ -221,20 +228,27 @@ impl Node for LightCullComputePass {
fn execute( fn execute(
&mut self, &mut self,
graph: &mut crate::render::graph::RenderGraph, graph: &mut crate::render::graph::RenderGraph,
_: &crate::render::graph::NodeDesc, desc: &crate::render::graph::RenderGraphPassDesc,
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"),
}); });
pass.set_pipeline(pipeline); let pipeline = graph.pipeline(desc.id);
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,

View File

@ -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, Node, NodeDesc, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc,
NodeType, RenderPassType,
}, 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 Node for MeshPass { impl RenderGraphPass 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::NodeDesc { ) -> crate::render::graph::RenderGraphPassDesc {
let device = graph.device(); let device = graph.device();
@ -232,24 +232,31 @@ impl Node 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_value(BasePassSlots::MainRenderTarget) let main_rt = graph.slot_id(&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);
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); // get the id here to make borrow checker happy
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); let pass_id = graph.next_id();
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(LightCullComputePassSlots::LightIndicesGridGroup); .bind_group_layout(graph.bind_group_id(&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 = NodeDesc::new( let desc = RenderGraphPassDesc::new(
NodeType::Render, pass_id,
MeshesPassLabel,
RenderPassType::Render,
Some(PipelineDescriptor::Render(RenderPipelineDescriptor { Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
label: Some("meshes".into()), label: Some("meshes".into()),
layouts: vec![ layouts: vec![
@ -438,37 +445,36 @@ impl Node for MeshPass {
fn execute( fn execute(
&mut self, &mut self,
graph: &mut crate::render::graph::RenderGraph, graph: &mut crate::render::graph::RenderGraph,
_: &crate::render::graph::NodeDesc, desc: &crate::render::graph::RenderGraphPassDesc,
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(BasePassSlots::WindowTextureView) .slot_value(graph.slot_id(&BasePassSlots::WindowTextureView).unwrap())
.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(BasePassSlots::DepthTextureView) .slot_value(graph.slot_id(&BasePassSlots::DepthTextureView).unwrap())
.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(BasePassSlots::Camera); .bind_group(graph.bind_group_id(&BasePassSlots::Camera)
.expect("Missing camera bind group"));
let lights_bg = graph let lights_bg = graph
.bind_group(LightBasePassSlots::Lights); .bind_group(graph.bind_group_id(&LightBasePassSlots::Lights)
.expect("Missing lights bind group"));
let light_grid_bg = graph let light_grid_bg = graph
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup); .bind_group(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup)
.expect("Missing light grid bind group"));
let material_bg = graph let material_bg = graph
.bind_group(MeshesPassSlots::Material); .bind_group(graph.bind_group_id(&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"),
@ -496,6 +502,7 @@ impl Node 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();

View File

@ -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, Node, NodeDesc, NodeSlot, NodeType, SlotAttribute, SlotType}; use crate::render::graph::{RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, SlotAttribute, SlotType};
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
pub struct PresentPassLabel(RenderGraphLabelValue); pub struct PresentPassLabel(RenderGraphLabelValue);
@ -18,7 +18,8 @@ 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
pub label: PresentPassLabel, label: PresentPassLabel,
//render_target_slot: Rc<dyn RenderGraphLabel>,
} }
impl PresentPass { impl PresentPass {
@ -30,18 +31,21 @@ impl PresentPass {
} }
} }
impl Node for PresentPass { impl RenderGraphPass for PresentPass {
fn desc(&mut self, _graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc { fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc {
let mut desc = NodeDesc::new( let mut desc = RenderGraphPassDesc::new(
NodeType::Presenter, graph.next_id(),
self.label.clone(),
RenderPassType::Presenter,
None, None,
vec![], vec![],
); );
desc.add_slot( desc.add_slot(
NodeSlot { RenderPassSlot {
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,
} }
@ -54,10 +58,10 @@ impl Node for PresentPass {
} }
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::NodeDesc, _context: &mut crate::render::graph::RenderGraphContext) { fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::RenderGraphPassDesc, _context: &mut crate::render::graph::RenderGraphContext) {
let mut slot = graph.slot_value_mut(self.label.0.clone()) let id = graph.slot_id_rc(&self.label.0)
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0)) .expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0));
.as_render_target_mut().unwrap(); let mut slot = graph.slot_value_mut(id).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();
} }

View File

@ -125,20 +125,19 @@ 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(BasePassLabel, BasePass::new(surface, config)); g.add_pass(BasePass::new(surface, config));
debug!("Adding light base pass"); debug!("Adding light base pass");
g.add_pass(LightBasePassLabel, LightBasePass::new()); g.add_pass(LightBasePass::new());
debug!("Adding light cull compute pass"); debug!("Adding light cull compute pass");
g.add_pass(LightCullComputePassLabel, LightCullComputePass::new(size)); g.add_pass(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(MeshesPassLabel, MeshPass::new()); g.add_pass(MeshPass::new());
debug!("Adding present pass"); debug!("Adding present pass");
let p = PresentPass::new(BasePassSlots::MainRenderTarget); g.add_pass(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);
@ -189,7 +188,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(BasePassSlots::MainRenderTarget) let mut rt = self.graph.slot_value_mut(self.graph.slot_id(&BasePassSlots::MainRenderTarget).unwrap())
.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;