From ef68b2a4c5bb6d95a2ca5bd35e1023a74c47c8f5 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 2 Jun 2024 21:35:59 -0400 Subject: [PATCH] render: create a RenderGraphLabel trait for graph labels instead of strings --- Cargo.lock | 10 ++ lyra-game/Cargo.toml | 1 + lyra-game/lyra-game-derive/Cargo.toml | 14 ++ lyra-game/lyra-game-derive/src/lib.rs | 35 ++++ lyra-game/src/render/graph/mod.rs | 151 ++++++++++++----- lyra-game/src/render/graph/pass.rs | 48 +++--- lyra-game/src/render/graph/passes/base.rs | 36 +++-- .../src/render/graph/passes/light_base.rs | 16 +- .../render/graph/passes/light_cull_compute.rs | 46 ++++-- lyra-game/src/render/graph/passes/meshes.rs | 35 ++-- lyra-game/src/render/graph/passes/mod.rs | 11 +- .../src/render/graph/passes/present_pass.rs | 32 +++- lyra-game/src/render/graph/passes/triangle.rs | 153 ------------------ lyra-game/src/render/renderer.rs | 18 +-- 14 files changed, 317 insertions(+), 289 deletions(-) create mode 100644 lyra-game/lyra-game-derive/Cargo.toml create mode 100644 lyra-game/lyra-game-derive/src/lib.rs delete mode 100644 lyra-game/src/render/graph/passes/triangle.rs diff --git a/Cargo.lock b/Cargo.lock index ebb7297..a2362f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1867,6 +1867,7 @@ dependencies = [ "instant", "itertools 0.11.0", "lyra-ecs", + "lyra-game-derive", "lyra-math", "lyra-reflect", "lyra-resource", @@ -1887,6 +1888,15 @@ dependencies = [ "winit", ] +[[package]] +name = "lyra-game-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + [[package]] name = "lyra-math" version = "0.1.0" diff --git a/lyra-game/Cargo.toml b/lyra-game/Cargo.toml index 197f2f8..efba477 100644 --- a/lyra-game/Cargo.toml +++ b/lyra-game/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +lyra-game-derive = { path = "./lyra-game-derive" } lyra-resource = { path = "../lyra-resource" } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } diff --git a/lyra-game/lyra-game-derive/Cargo.toml b/lyra-game/lyra-game-derive/Cargo.toml new file mode 100644 index 0000000..a176391 --- /dev/null +++ b/lyra-game/lyra-game-derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lyra-game-derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.70" +quote = "1.0.33" +syn = "2.0.41" \ No newline at end of file diff --git a/lyra-game/lyra-game-derive/src/lib.rs b/lyra-game/lyra-game-derive/src/lib.rs new file mode 100644 index 0000000..1de240a --- /dev/null +++ b/lyra-game/lyra-game-derive/src/lib.rs @@ -0,0 +1,35 @@ +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +#[proc_macro_derive(RenderGraphLabel)] +pub fn derive_render_graph_label(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let type_ident = &input.ident; + + proc_macro::TokenStream::from(quote! { + impl #impl_generics crate::render::graph::RenderGraphLabel for #type_ident #ty_generics #where_clause { + fn rc_clone(&self) -> std::rc::Rc { + std::rc::Rc::new(self.clone()) + } + + /* fn as_dyn(&self) -> &dyn crate::render::graph::RenderGraphLabel { + &self + } + + fn as_partial_eq(&self) -> &dyn PartialEq { + self + } */ + + fn as_label_hash(&self) -> u64 { + let tyid = ::std::any::TypeId::of::(); + + let mut s = ::std::hash::DefaultHasher::new(); + ::std::hash::Hash::hash(&tyid, &mut s); + ::std::hash::Hash::hash(self, &mut s); + ::std::hash::Hasher::finish(&s) + } + } + }) +} \ No newline at end of file diff --git a/lyra-game/src/render/graph/mod.rs b/lyra-game/src/render/graph/mod.rs index 21fe746..27b71c4 100644 --- a/lyra-game/src/render/graph/mod.rs +++ b/lyra-game/src/render/graph/mod.rs @@ -1,9 +1,6 @@ mod pass; use std::{ - cell::RefCell, - collections::{HashMap, VecDeque}, - rc::Rc, - sync::Arc, + cell::RefCell, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc }; use itertools::Itertools; @@ -22,6 +19,62 @@ use wgpu::ComputePass; use super::resource::{ComputePipeline, Pipeline, RenderPipeline}; +pub trait RenderGraphLabel: Debug + 'static { + fn rc_clone(&self) -> Rc; + //fn as_dyn(&self) -> &dyn RenderGraphLabel; + //fn as_partial_eq(&self) -> &dyn PartialEq; + fn as_label_hash(&self) -> u64; + + fn label_eq_rc(&self, other: &Rc) -> bool { + self.as_label_hash() == other.as_label_hash() + } + + fn label_eq(&self, other: &dyn RenderGraphLabel) -> bool { + self.as_label_hash() == other.as_label_hash() + } +} + +#[derive(Clone)] +pub struct RenderGraphLabelValue(Rc); + +impl Debug for RenderGraphLabelValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl From for RenderGraphLabelValue { + fn from(value: L) -> Self { + Self(Rc::new(value)) + } +} + +impl From> for RenderGraphLabelValue { + fn from(value: Rc) -> Self { + Self(value) + } +} + +impl From<&Rc> for RenderGraphLabelValue { + fn from(value: &Rc) -> Self { + Self(value.clone()) + } +} + +impl Hash for RenderGraphLabelValue { + fn hash(&self, state: &mut H) { + state.write_u64(self.0.as_label_hash()); + } +} + +impl PartialEq for RenderGraphLabelValue { + fn eq(&self, other: &Self) -> bool { + self.0.label_eq_rc(&other.0) + } +} + +impl Eq for RenderGraphLabelValue {} + struct PassEntry { inner: Arc>, desc: Arc, @@ -30,7 +83,7 @@ struct PassEntry { } pub struct BindGroupEntry { - pub name: String, + pub label: RenderGraphLabelValue, /// BindGroup pub bg: Rc, /// BindGroupLayout @@ -39,7 +92,7 @@ pub struct BindGroupEntry { #[allow(dead_code)] struct ResourcedSlot { - name: String, + label: RenderGraphLabelValue, ty: SlotType, value: SlotValue, } @@ -64,11 +117,13 @@ pub struct RenderGraph { device: Rc, queue: Rc, slots: FxHashMap, - slot_names: HashMap, + /// HashMap used to lookup the slot id using the label's hash + slot_label_lookup: FxHashMap, passes: FxHashMap, // TODO: Use a SlotMap bind_groups: FxHashMap, - bind_group_names: HashMap, + /// HashMap used to lookup the bind group id using the label's hash + bind_group_names: FxHashMap, // TODO: make pipelines a `type` parameter in RenderPasses, // then the pipelines can be retrieved via TypeId to the pass. pipelines: FxHashMap, @@ -83,7 +138,7 @@ impl RenderGraph { device, queue, slots: Default::default(), - slot_names: Default::default(), + slot_label_lookup: Default::default(), passes: Default::default(), bind_groups: Default::default(), bind_group_names: Default::default(), @@ -102,8 +157,12 @@ impl RenderGraph { self.current_id } - pub fn slot_id(&self, name: &str) -> Option { - self.slot_names.get(name).cloned() + pub(crate) fn slot_id_rc(&self, label: &RenderGraphLabelValue) -> Option { + self.slot_label_lookup.get(&label.clone().into()).cloned() + } + + pub fn slot_id(&self, label: &dyn RenderGraphLabel) -> Option { + self.slot_label_lookup.get(&label.rc_clone().into()).cloned() } #[instrument(skip(self, pass), level = "debug")] @@ -113,19 +172,19 @@ impl RenderGraph { // collect all the slots of the pass for slot in &mut desc.slots { if let Some((id, other)) = self - .slot_names - .get(&slot.name) + .slot_label_lookup + .get(&slot.label) .and_then(|id| self.slots.get_mut(id).map(|s| (id, s))) { debug_assert_eq!( slot.ty, other.ty, - "slot {} in pass {} does not match existing slot of same name", - slot.name, desc.name + "slot {:?} in pass {:?} does not match existing slot of same name", + slot.label, desc.label ); trace!( - "Found existing slot for {}, changing id to {}", - slot.name, + "Found existing slot for {:?}, changing id to {}", + slot.label, id ); @@ -133,33 +192,33 @@ impl RenderGraph { slot.id = *id; } else { debug_assert!(!self.slots.contains_key(&slot.id), - "Reuse of id detected in render graph! Pass: {}, slot: {}", - desc.name, slot.name, + "Reuse of id detected in render graph! Pass: {:?}, slot: {:?}", + desc.label, slot.label, ); let res_slot = ResourcedSlot { - name: slot.name.clone(), + label: slot.label.clone(), ty: slot.ty, value: slot.value.clone().unwrap_or(SlotValue::None), }; self.slots.insert(slot.id, res_slot); - self.slot_names.insert(slot.name.clone(), slot.id); + self.slot_label_lookup.insert(slot.label.clone(), slot.id); } } // get clones of the bind groups and layouts - for (name, bg, bgl) in &desc.bind_groups { + for (label, bg, bgl) in &desc.bind_groups { let bg_id = self.next_id(); self.bind_groups.insert( bg_id, BindGroupEntry { - name: name.clone(), + label: label.clone(), bg: bg.clone(), layout: bgl.clone(), }, ); - self.bind_group_names.insert(name.clone(), bg_id); + self.bind_group_names.insert(label.clone().into(), bg_id); } let index = self.execution_graph.add_node(desc.id); @@ -224,15 +283,15 @@ impl RenderGraph { while let Some(bufwr) = context.buffer_writes.pop_front() { let slot = self .slots - .get(&self.slot_id(&bufwr.target_slot).unwrap()) + .get(&self.slot_id_rc(&bufwr.target_slot).unwrap()) .expect(&format!( - "Failed to find slot '{}' for buffer write", + "Failed to find slot '{:?}' for buffer write", bufwr.target_slot )); let buf = slot .value .as_buffer() - .expect(&format!("Slot '{}' is not a buffer", bufwr.target_slot)); + .expect(&format!("Slot '{:?}' is not a buffer", bufwr.target_slot)); self.queue.write_buffer(buf, bufwr.offset, &bufwr.bytes); } @@ -248,7 +307,7 @@ impl RenderGraph { .collect(); let path_names = sorted .iter() - .map(|i| self.pass(*i).unwrap().name.clone()) + .map(|i| self.pass(*i).unwrap().label.clone()) .collect_vec(); trace!("Render graph execution order: {:?}", path_names); @@ -257,7 +316,7 @@ impl RenderGraph { let pass = self.passes.get(&pass_id).unwrap(); let pass_inn = pass.inner.clone(); let pass_desc = pass.desc.clone(); - let label = format!("{} Encoder", pass_desc.name); + let label = format!("{:?} Encoder", pass_desc.label); // encoders are not needed for presenter nodes. let encoder = if pass_desc.pass_type.should_have_pipeline() { @@ -282,7 +341,7 @@ impl RenderGraph { self.queue.submit(encoders.drain(..)); } - trace!("Executing {}", pass_desc.name); + trace!("Executing {:?}", pass_desc.label); let mut inner = pass_inn.borrow_mut(); inner.execute(self, &*pass_desc, &mut context); @@ -341,24 +400,30 @@ impl RenderGraph { } #[inline(always)] - pub fn bind_group_id(&self, name: &str) -> Option { - self.bind_group_names.get(name).copied() + pub fn bind_group_id(&self, label: &dyn RenderGraphLabel) -> Option { + self.bind_group_names.get(&label.rc_clone().into()).copied() } - pub fn add_edge(&mut self, from: &str, to: &str) { + pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel) + { + let from = RenderGraphLabelValue::from(from); + let to = RenderGraphLabelValue::from(to); + let from_idx = self .passes .iter() - .find(|p| p.1.desc.name == from) + .find(|p| p.1.desc.label == from) .map(|p| p.1.graph_index) .expect("Failed to find from pass"); let to_idx = self .passes .iter() - .find(|p| p.1.desc.name == to) + .find(|p| p.1.desc.label == to) .map(|p| p.1.graph_index) .expect("Failed to find to pass"); + debug_assert_ne!(from_idx, to_idx, "cannot add edges between the same node"); + self.execution_graph.add_edge(from_idx, to_idx, ()); } @@ -389,13 +454,13 @@ impl RenderGraph { pub fn set_bind_groups<'a>( &'a self, pass: &mut ComputePass<'a>, - bind_groups: &[(&str, u32)], + bind_groups: &[(&dyn RenderGraphLabel, u32)], ) { - for (name, index) in bind_groups { + for (label, index) in bind_groups { let bg = self - .bind_group_id(name) + .bind_group_id(*label) .map(|bgi| self.bind_group(bgi)) - .expect(&format!("Could not find bind group '{}'", name)); + .expect(&format!("Could not find bind group '{:?}'", label)); pass.set_bind_group(*index, bg, &[]); } @@ -405,7 +470,7 @@ impl RenderGraph { /// A queued write to a GPU buffer targeting a graph slot. pub(crate) struct GraphBufferWrite { /// The name of the slot that has the resource that will be written - target_slot: String, + target_slot: RenderGraphLabelValue, offset: u64, bytes: Vec, } @@ -460,9 +525,9 @@ impl<'a> RenderGraphContext<'a> { /// data will be submitted to the GPU queue right after the prepare stage for all passes /// is ran. #[instrument(skip(self, bytes), level="trace", fields(size = bytes.len()))] - pub fn queue_buffer_write(&mut self, target_slot: &str, offset: u64, bytes: &[u8]) { + pub fn queue_buffer_write(&mut self, target_slot: impl RenderGraphLabel, offset: u64, bytes: &[u8]) { self.buffer_writes.push_back(GraphBufferWrite { - target_slot: target_slot.to_string(), + target_slot: target_slot.into(), offset, bytes: bytes.to_vec(), }) @@ -472,7 +537,7 @@ impl<'a> RenderGraphContext<'a> { #[instrument(skip(self, bytes), level="trace", fields(size = std::mem::size_of::()))] pub fn queue_buffer_write_with( &mut self, - target_slot: &str, + target_slot: impl RenderGraphLabel, offset: u64, bytes: T, ) { diff --git a/lyra-game/src/render/graph/pass.rs b/lyra-game/src/render/graph/pass.rs index c995290..6c0f7bb 100644 --- a/lyra-game/src/render/graph/pass.rs +++ b/lyra-game/src/render/graph/pass.rs @@ -4,7 +4,7 @@ use lyra_ecs::World; use crate::render::resource::PipelineDescriptor; -use super::{RenderGraph, RenderGraphContext, RenderTarget}; +use super::{RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderTarget}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum RenderPassType { @@ -96,7 +96,7 @@ pub struct RenderPassSlot { pub ty: SlotType, pub attribute: SlotAttribute, pub id: u64, - pub name: String, + pub label: RenderGraphLabelValue, /// The descriptor of the slot value. /// This will be `None` if this slot is an input. pub value: Option, @@ -159,36 +159,36 @@ impl RenderGraphPipelineInfo { pub struct RenderGraphPassDesc { pub id: u64, - pub name: String, + pub label: RenderGraphLabelValue, pub pass_type: RenderPassType, pub slots: Vec, - slot_names: HashMap, + slot_label_lookup: HashMap, pub pipeline_desc: Option, pub bind_groups: Vec<( - String, + RenderGraphLabelValue, Rc, Option>, )>, } impl RenderGraphPassDesc { - pub fn new( + pub fn new( id: u64, - name: &str, + label: L, pass_type: RenderPassType, pipeline_desc: Option, - bind_groups: Vec<(&str, Rc, Option>)>, + bind_groups: Vec<(&dyn RenderGraphLabel, Rc, Option>)>, ) -> Self { Self { id, - name: name.to_string(), + label: label.into(), pass_type, slots: vec![], - slot_names: HashMap::default(), + slot_label_lookup: HashMap::default(), pipeline_desc, bind_groups: bind_groups .into_iter() - .map(|bg| (bg.0.to_string(), bg.1, bg.2)) + .map(|bg| (bg.0.rc_clone().into(), bg.1, bg.2)) .collect(), } } @@ -199,15 +199,15 @@ impl RenderGraphPassDesc { "input slots should not have values" ); - self.slot_names.insert(slot.name.clone(), slot.id); + self.slot_label_lookup.insert(slot.label.clone().into(), slot.id); self.slots.push(slot); } #[inline(always)] - pub fn add_buffer_slot( + pub fn add_buffer_slot( &mut self, id: u64, - name: &str, + label: L, attribute: SlotAttribute, value: Option, ) { @@ -218,7 +218,7 @@ impl RenderGraphPassDesc { let slot = RenderPassSlot { id, - name: name.to_string(), + label: label.into(), ty: SlotType::Buffer, attribute, value, @@ -227,10 +227,10 @@ impl RenderGraphPassDesc { } #[inline(always)] - pub fn add_texture_slot( + pub fn add_texture_slot( &mut self, id: u64, - name: &str, + label: L, attribute: SlotAttribute, value: Option, ) { @@ -241,7 +241,7 @@ impl RenderGraphPassDesc { let slot = RenderPassSlot { id, - name: name.to_string(), + label: label.into(), ty: SlotType::Texture, attribute, value, @@ -250,10 +250,10 @@ impl RenderGraphPassDesc { } #[inline(always)] - pub fn add_texture_view_slot( + pub fn add_texture_view_slot( &mut self, id: u64, - name: &str, + label: L, attribute: SlotAttribute, value: Option, ) { @@ -264,7 +264,7 @@ impl RenderGraphPassDesc { let slot = RenderPassSlot { id, - name: name.to_string(), + label: label.into(), ty: SlotType::TextureView, attribute, value, @@ -273,10 +273,10 @@ impl RenderGraphPassDesc { } #[inline(always)] - pub fn add_sampler_slot( + pub fn add_sampler_slot( &mut self, id: u64, - name: &str, + label: L, attribute: SlotAttribute, value: Option, ) { @@ -287,7 +287,7 @@ impl RenderGraphPassDesc { let slot = RenderPassSlot { id, - name: name.to_string(), + label: label.into(), ty: SlotType::Sampler, attribute, value, diff --git a/lyra-game/src/render/graph/passes/base.rs b/lyra-game/src/render/graph/passes/base.rs index 59a610e..c89dcc4 100644 --- a/lyra-game/src/render/graph/passes/base.rs +++ b/lyra-game/src/render/graph/passes/base.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, rc::Rc}; use glam::UVec2; +use lyra_game_derive::RenderGraphLabel; use tracing::warn; use winit::dpi::PhysicalSize; @@ -16,6 +17,19 @@ use crate::{ scene::CameraComponent, }; +#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)] +pub struct BasePassLabel; + +#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)] +pub enum BasePassSlots { + DepthTexture, + ScreenSize, + Camera, + MainRenderTarget, + WindowTextureView, + DepthTextureView, +} + /// Supplies some basic things other passes needs. /// /// screen size buffer, camera buffer, @@ -89,13 +103,13 @@ impl RenderGraphPass for BasePass { let mut desc = RenderGraphPassDesc::new( graph.next_id(), - "base", + BasePassLabel, RenderPassType::Node, None, vec![ - ("depth_texture", depth_texture_bg, Some(depth_texture_bgl)), - ("screen_size", screen_size_bg, Some(screen_size_bgl)), - ("camera", camera_bg, Some(camera_bgl)), + (&BasePassSlots::DepthTexture, depth_texture_bg, Some(depth_texture_bgl)), + (&BasePassSlots::ScreenSize, screen_size_bg, Some(screen_size_bgl)), + (&BasePassSlots::Camera, camera_bg, Some(camera_bgl)), ], ); @@ -104,7 +118,7 @@ impl RenderGraphPass for BasePass { ty: SlotType::RenderTarget, attribute: SlotAttribute::Output, id: self.main_rt_id, - name: "main_render_target".into(), + label: BasePassSlots::MainRenderTarget.into(), value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new( render_target, )))), @@ -112,25 +126,25 @@ impl RenderGraphPass for BasePass { self.window_tv_id = graph.next_id(); desc.add_texture_view_slot( self.window_tv_id, - "window_texture_view", + BasePassSlots::WindowTextureView, SlotAttribute::Output, Some(SlotValue::Lazy), ); desc.add_texture_view_slot( graph.next_id(), - "depth_texture_view", + BasePassSlots::DepthTextureView, SlotAttribute::Output, Some(SlotValue::TextureView(depth_texture_view)), ); desc.add_buffer_slot( graph.next_id(), - "screen_size_buffer", + BasePassSlots::ScreenSize, SlotAttribute::Output, Some(SlotValue::Buffer(Rc::new(screen_size_buf))), ); desc.add_buffer_slot( graph.next_id(), - "camera_buffer", + BasePassSlots::Camera, SlotAttribute::Output, Some(SlotValue::Buffer(Rc::new(camera_buf))), ); @@ -144,7 +158,7 @@ impl RenderGraphPass for BasePass { RenderCamera::new(PhysicalSize::new(self.screen_size.x, self.screen_size.y)); let uniform = render_cam.calc_view_projection(&camera); - context.queue_buffer_write_with("camera_buffer", 0, uniform) + context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform) } else { warn!("Missing camera!"); } @@ -170,7 +184,7 @@ impl RenderGraphPass for BasePass { || rt.surface_config.height != self.screen_size.y { self.screen_size = UVec2::new(rt.surface_config.width, rt.surface_config.height); - context.queue_buffer_write_with("screen_size_buffer", 0, self.screen_size) + context.queue_buffer_write_with(BasePassSlots::ScreenSize, 0, self.screen_size) } let surface_tex = rt.surface.get_current_texture().unwrap(); diff --git a/lyra-game/src/render/graph/passes/light_base.rs b/lyra-game/src/render/graph/passes/light_base.rs index e26c30d..b5176a4 100644 --- a/lyra-game/src/render/graph/passes/light_base.rs +++ b/lyra-game/src/render/graph/passes/light_base.rs @@ -1,3 +1,5 @@ +use lyra_game_derive::RenderGraphLabel; + use crate::render::{ graph::{ RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute, @@ -6,6 +8,14 @@ use crate::render::{ light::LightUniformBuffers, }; +#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)] +pub struct LightBasePassLabel; + +#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)] +pub enum LightBasePassSlots { + Lights +} + /// Supplies some basic things other passes needs. /// /// screen size buffer, camera buffer, @@ -31,11 +41,11 @@ impl RenderGraphPass for LightBasePass { let mut desc = RenderGraphPassDesc::new( graph.next_id(), - "light_base", + LightBasePassLabel, RenderPassType::Node, None, vec![( - "light_buffers", + &LightBasePassSlots::Lights, light_buffers.bind_group.clone(), Some(light_buffers.bind_group_layout.clone()), )], @@ -43,7 +53,7 @@ impl RenderGraphPass for LightBasePass { desc.add_buffer_slot( graph.next_id(), - "light_buffers", + LightBasePassSlots::Lights, SlotAttribute::Output, Some(SlotValue::Buffer(light_buffers.buffer.clone())), ); diff --git a/lyra-game/src/render/graph/passes/light_cull_compute.rs b/lyra-game/src/render/graph/passes/light_cull_compute.rs index 01a21ef..41ca789 100644 --- a/lyra-game/src/render/graph/passes/light_cull_compute.rs +++ b/lyra-game/src/render/graph/passes/light_cull_compute.rs @@ -1,6 +1,7 @@ use std::{mem, rc::Rc}; use lyra_ecs::World; +use lyra_game_derive::RenderGraphLabel; use wgpu::util::DeviceExt; use crate::render::{ @@ -11,6 +12,19 @@ use crate::render::{ resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader}, }; +use super::{BasePassSlots, LightBasePassSlots}; + +#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)] +pub struct LightCullComputePassLabel; + +#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)] +pub enum LightCullComputePassSlots { + LightGridTexture, + LightGridTextureView, + IndexCounterBuffer, + LightIndicesGridGroup, +} + pub struct LightCullComputePass { workgroup_size: glam::UVec2, } @@ -35,7 +49,7 @@ impl RenderGraphPass for LightCullComputePass { // get the size of the work group for the grid let main_rt = graph - .slot_id("main_render_target") + .slot_id(&BasePassSlots::MainRenderTarget) .and_then(|s| graph.slot_value(s)) .and_then(|s| s.as_render_target()) .expect("missing main render target"); @@ -157,14 +171,14 @@ impl RenderGraphPass for LightCullComputePass { drop(main_rt); let pass_id = graph.next_id(); - let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id("depth_texture").unwrap()); - let camera_bgl = graph.bind_group_layout(graph.bind_group_id("camera").unwrap()); - let lights_bgl = graph.bind_group_layout(graph.bind_group_id("light_buffers").unwrap()); - let screen_size_bgl = graph.bind_group_layout(graph.bind_group_id("screen_size").unwrap()); + let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::DepthTexture).unwrap()); + 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 screen_size_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::ScreenSize).unwrap()); let mut desc = RenderGraphPassDesc::new( pass_id, - "light_cull_compute", + LightCullComputePassLabel, RenderPassType::Compute, Some(PipelineDescriptor::Compute(ComputePipelineDescriptor { label: Some("light_cull_pipeline".into()), @@ -180,7 +194,7 @@ impl RenderGraphPass for LightCullComputePass { shader_entry_point: "cs_main".into(), })), vec![( - "light_indices_grid", + &LightCullComputePassSlots::LightIndicesGridGroup, light_indices_bg, Some(light_indices_bg_layout), )], @@ -188,20 +202,20 @@ impl RenderGraphPass for LightCullComputePass { desc.add_texture_view_slot( graph.next_id(), - "window_texture_view", + BasePassSlots::WindowTextureView, SlotAttribute::Input, None, ); desc.add_buffer_slot( graph.next_id(), - "screen_size_buffer", + BasePassSlots::ScreenSize, SlotAttribute::Input, None, ); - desc.add_buffer_slot(graph.next_id(), "camera_buffer", SlotAttribute::Input, None); + desc.add_buffer_slot(graph.next_id(), BasePassSlots::Camera, SlotAttribute::Input, None); desc.add_buffer_slot( graph.next_id(), - "index_counter_buffer", + LightCullComputePassSlots::IndexCounterBuffer, SlotAttribute::Output, Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))), ); @@ -239,11 +253,11 @@ impl RenderGraphPass for LightCullComputePass { graph.set_bind_groups( &mut pass, &[ - ("depth_texture", 0), - ("camera", 1), - ("light_buffers", 2), - ("light_indices_grid", 3), - ("screen_size", 4), + (&BasePassSlots::DepthTexture, 0), + (&BasePassSlots::Camera, 1), + (&LightBasePassSlots::Lights, 2), + (&LightCullComputePassSlots::LightIndicesGridGroup, 3), + (&BasePassSlots::ScreenSize, 4), ], ); diff --git a/lyra-game/src/render/graph/passes/meshes.rs b/lyra-game/src/render/graph/passes/meshes.rs index 0e366f1..ac85c61 100644 --- a/lyra-game/src/render/graph/passes/meshes.rs +++ b/lyra-game/src/render/graph/passes/meshes.rs @@ -3,6 +3,7 @@ use std::{collections::{HashSet, VecDeque}, rc::Rc}; use glam::Vec3; use itertools::izip; use lyra_ecs::{query::{filter::{Has, Not, Or}, Entities, Res, TickOf}, relation::{ChildOf, RelationOriginComponent}, Component, Entity}; +use lyra_game_derive::RenderGraphLabel; use lyra_math::Transform; use lyra_resource::{gltf::Mesh, ResHandle}; use lyra_scene::{SceneGraph, WorldTransform}; @@ -21,9 +22,19 @@ use crate::{ DeltaTime, }; +use super::{BasePassSlots, LightBasePassSlots, LightCullComputePassSlots}; + type MeshHandle = ResHandle; type SceneHandle = ResHandle; +#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)] +pub struct MeshesPassLabel; + +#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)] +pub enum MeshesPassSlots { + Material +} + struct MeshBufferStorage { buffer_vertex: BufferStorage, buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>, @@ -221,7 +232,7 @@ impl RenderGraphPass for MeshPass { self.default_texture = Some(RenderTexture::from_bytes(&device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap()); // get surface config format - let main_rt = graph.slot_id("main_render_target") + let main_rt = graph.slot_id(&BasePassSlots::MainRenderTarget) .and_then(|s| graph.slot_value(s)) .and_then(|s| s.as_render_target()) .expect("missing main render target"); @@ -231,10 +242,10 @@ impl RenderGraphPass for MeshPass { // get the id here to make borrow checker happy let pass_id = graph.next_id(); - let camera_bgl = graph.bind_group_layout(graph.bind_group_id("camera").unwrap()); - let lights_bgl = graph.bind_group_layout(graph.bind_group_id("light_buffers").unwrap()); + 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 - .bind_group_layout(graph.bind_group_id("light_indices_grid") + .bind_group_layout(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup) .expect("Missing light grid bind group")); let shader = Rc::new(Shader { @@ -244,7 +255,7 @@ impl RenderGraphPass for MeshPass { let desc = RenderGraphPassDesc::new( pass_id, - "meshes", + MeshesPassLabel, RenderPassType::Render, Some(PipelineDescriptor::Render(RenderPipelineDescriptor { label: Some("meshes".into()), @@ -286,7 +297,7 @@ impl RenderGraphPass for MeshPass { multiview: None, })), vec![ - ("material", material_bg, Some(material_bgl)), + (&MeshesPassSlots::Material, material_bg, Some(material_bgl)), ], ); @@ -440,29 +451,29 @@ impl RenderGraphPass for MeshPass { let encoder = context.encoder.as_mut().unwrap(); let view = graph - .slot_value(graph.slot_id("window_texture_view").unwrap()) + .slot_value(graph.slot_id(&BasePassSlots::WindowTextureView).unwrap()) .unwrap() .as_texture_view(); let depth_view = graph - .slot_value(graph.slot_id("depth_texture_view").unwrap()) + .slot_value(graph.slot_id(&BasePassSlots::DepthTextureView).unwrap()) .unwrap() .as_texture_view(); let camera_bg = graph - .bind_group(graph.bind_group_id("camera") + .bind_group(graph.bind_group_id(&BasePassSlots::Camera) .expect("Missing camera bind group")); let lights_bg = graph - .bind_group(graph.bind_group_id("light_buffers") + .bind_group(graph.bind_group_id(&LightBasePassSlots::Lights) .expect("Missing lights bind group")); let light_grid_bg = graph - .bind_group(graph.bind_group_id("light_indices_grid") + .bind_group(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup) .expect("Missing light grid bind group")); let material_bg = graph - .bind_group(graph.bind_group_id("material") + .bind_group(graph.bind_group_id(&MeshesPassSlots::Material) .expect("Missing material bind group")); let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { diff --git a/lyra-game/src/render/graph/passes/mod.rs b/lyra-game/src/render/graph/passes/mod.rs index 3aff4e2..967d440 100644 --- a/lyra-game/src/render/graph/passes/mod.rs +++ b/lyra-game/src/render/graph/passes/mod.rs @@ -1,12 +1,6 @@ mod light_cull_compute; pub use light_cull_compute::*; -/*mod depth_prepass; -pub use depth_prepass::*; */ - -/* mod simple_phong; -pub use simple_phong::*; */ - mod base; pub use base::*; @@ -17,7 +11,4 @@ mod light_base; pub use light_base::*; mod present_pass; -pub use present_pass::*; - -mod triangle; -pub use triangle::*; \ No newline at end of file +pub use present_pass::*; \ No newline at end of file diff --git a/lyra-game/src/render/graph/passes/present_pass.rs b/lyra-game/src/render/graph/passes/present_pass.rs index 221837a..e496595 100644 --- a/lyra-game/src/render/graph/passes/present_pass.rs +++ b/lyra-game/src/render/graph/passes/present_pass.rs @@ -1,16 +1,32 @@ -use crate::render::graph::{RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, SlotAttribute, SlotType}; +use std::hash::Hash; + +use lyra_game_derive::RenderGraphLabel; + +use crate::render::graph::{RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, SlotAttribute, SlotType}; + +#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] +pub struct PresentPassLabel(RenderGraphLabelValue); + +impl PresentPassLabel { + pub fn new(label: impl RenderGraphLabel) -> Self { + Self(label.into()) + } +} /// Supplies some basic things other passes needs. /// /// screen size buffer, camera buffer, pub struct PresentPass { - render_target_slot: String, + /// Label of this pass + label: PresentPassLabel, + //render_target_slot: Rc, } impl PresentPass { - pub fn new(render_target_slot: &str) -> Self { + pub fn new(render_target_slot: impl RenderGraphLabel) -> Self { Self { - render_target_slot: render_target_slot.into(), + //render_target_slot: render_target_slot.rc_clone(), + label: PresentPassLabel::new(render_target_slot), } } } @@ -19,7 +35,7 @@ impl RenderGraphPass for PresentPass { fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc { let mut desc = RenderGraphPassDesc::new( graph.next_id(), - &format!("present_{}", self.render_target_slot), + self.label.clone(), RenderPassType::Presenter, None, vec![], @@ -30,7 +46,7 @@ impl RenderGraphPass for PresentPass { ty: SlotType::RenderTarget, attribute: SlotAttribute::Input, id: graph.next_id(), - name: self.render_target_slot.clone(), + label: self.label.0.clone(), value: None, } ); @@ -43,8 +59,8 @@ 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) { - let id = graph.slot_id(&self.render_target_slot) - .expect(&format!("render target slot '{}' for PresentPass is missing", self.render_target_slot)); + let id = graph.slot_id_rc(&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(); let surf_tex = slot.current_texture.take().unwrap(); surf_tex.present(); diff --git a/lyra-game/src/render/graph/passes/triangle.rs b/lyra-game/src/render/graph/passes/triangle.rs deleted file mode 100644 index b6417b0..0000000 --- a/lyra-game/src/render/graph/passes/triangle.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::rc::Rc; - -use crate::{ - render::{ - graph::{ - RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, - SlotAttribute, SlotValue, - }, - render_buffer::BufferWrapper, - resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, - }, - DeltaTime, -}; - -/// A demo pass that renders a triangle that changes colors every frame. -#[derive(Default)] -pub struct TrianglePass { - acc: f32, -} - -impl TrianglePass { - pub fn new() -> Self { - Self::default() - } -} - -impl RenderGraphPass for TrianglePass { - fn desc( - &mut self, - graph: &mut crate::render::graph::RenderGraph, - ) -> crate::render::graph::RenderGraphPassDesc { - let shader = Rc::new(Shader { - label: Some("triangle_shader".into()), - source: include_str!("../../shaders/triangle.wgsl").to_string(), - }); - - let device = graph.device(); - let (color_bgl, color_bg, color_buf, _) = BufferWrapper::builder() - .label_prefix("color") - .visibility(wgpu::ShaderStages::FRAGMENT) - .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) - .contents(&[glam::Vec4::new(0.902, 0.639, 0.451, 1.0)]) - .finish_parts(device); - let color_bgl = Rc::new(color_bgl); - let color_bg = Rc::new(color_bg); - - let main_rt = graph.slot_id("main_render_target") - .and_then(|s| graph.slot_value(s)) - .and_then(|s| s.as_render_target()) - .expect("missing main render target"); - let surface_config_format = main_rt.surface_config.format; - drop(main_rt); - - let mut desc = RenderGraphPassDesc::new( - graph.next_id(), - "triangle", - RenderPassType::Render, - Some(PipelineDescriptor::Render(RenderPipelineDescriptor { - label: Some("triangle_pipeline".into()), - layouts: vec![color_bgl.clone()], - push_constant_ranges: vec![], - vertex: VertexState { - module: shader.clone(), - entry_point: "vs_main".into(), - buffers: vec![], - }, - fragment: Some(FragmentState { - module: shader, - entry_point: "fs_main".into(), - targets: vec![Some(wgpu::ColorTargetState { - format: surface_config_format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - depth_stencil: None, - primitive: wgpu::PrimitiveState::default(), - multisample: wgpu::MultisampleState::default(), - multiview: None, - })), - vec![("color_bg", color_bg, Some(color_bgl))], - ); - - desc.add_texture_view_slot( - graph.next_id(), - "window_texture_view", - SlotAttribute::Input, - None, - ); - - desc.add_buffer_slot( - graph.next_id(), - "color_buffer", - SlotAttribute::Output, - Some(SlotValue::Buffer(Rc::new(color_buf))), - ); - - desc - } - - fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { - const SPEED: f32 = 1.5; - - let dt = **world.get_resource::(); - self.acc += dt; - let x = (self.acc * SPEED).sin(); - let y = ((self.acc + 2.15) * SPEED).sin(); - let z = ((self.acc + 5.35) * SPEED).sin(); - - let color = glam::Vec4::new(x, y, z, 1.0); - context.queue_buffer_write_with("color_buffer", 0, color); - } - - fn execute( - &mut self, - graph: &mut crate::render::graph::RenderGraph, - desc: &crate::render::graph::RenderGraphPassDesc, - context: &mut crate::render::graph::RenderGraphContext, - ) { - let view = graph - .slot_value(graph.slot_id("window_texture_view").unwrap()) - .unwrap() - .as_texture_view(); - let color_bg = graph.bind_group(graph.bind_group_id("color_bg").unwrap()); - - let encoder = context.encoder.as_mut().unwrap(); - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("triangle_pass"), - color_attachments: &[ - // This is what @location(0) in the fragment shader targets - Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, - }, - }), - ], - depth_stencil_attachment: None, - }); - - let pipeline = graph.pipeline(desc.id); - pass.set_pipeline(&pipeline.as_render()); - pass.set_bind_group(0, color_bg, &[]); - pass.draw(0..3, 0..1); - } -} diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index c1f585f..353f45d 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -7,7 +7,7 @@ use lyra_ecs::World; use tracing::{debug, instrument, warn}; use winit::window::Window; -use crate::render::graph::{BasePass, LightBasePass, LightCullComputePass, MeshPass, PresentPass}; +use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel}; use super::graph::RenderGraph; use super::{resource::RenderPipeline, render_job::RenderJob}; @@ -137,16 +137,16 @@ impl BasicRenderer { g.add_pass(MeshPass::new()); debug!("Adding present pass"); - g.add_pass(PresentPass::new("main_render_target")); + g.add_pass(PresentPass::new(BasePassSlots::MainRenderTarget)); - g.add_edge("base", "light_base"); - g.add_edge("light_base", "light_cull_compute"); - g.add_edge("base", "meshes"); + g.add_edge(BasePassLabel, LightBasePassLabel); + g.add_edge(LightBasePassLabel, LightCullComputePassLabel); + g.add_edge(BasePassLabel, MeshesPassLabel); // make sure that present runs last - g.add_edge("base", "present_main_render_target"); - g.add_edge("light_cull_compute", "present_main_render_target"); - g.add_edge("meshes", "present_main_render_target"); + g.add_edge(BasePassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget)); + g.add_edge(LightCullComputePassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget)); + g.add_edge(MeshesPassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget)); g.setup(&device); @@ -188,7 +188,7 @@ impl Renderer for BasicRenderer { self.size = new_size; // update surface config and the surface - let mut rt = self.graph.slot_value_mut(self.graph.slot_id("main_render_target").unwrap()) + let mut rt = self.graph.slot_value_mut(self.graph.slot_id(&BasePassSlots::MainRenderTarget).unwrap()) .unwrap().as_render_target_mut().unwrap(); rt.surface_config.width = new_size.width; rt.surface_config.height = new_size.height;