From 6c1bff576886238ee65b5ba293d6f47d5a7b0940 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Tue, 25 Jun 2024 21:32:29 -0400 Subject: [PATCH] render: get sub render graphs working and create a simple test of them --- lyra-game/src/render/graph/mod.rs | 145 +++++++++++++----- .../render/graph/passes/light_cull_compute.rs | 71 +++++---- lyra-game/src/render/graph/passes/meshes.rs | 93 +++++++++-- lyra-game/src/render/graph/passes/mod.rs | 5 +- lyra-game/src/render/graph/render_target.rs | 25 ++- lyra-game/src/render/renderer.rs | 98 ++++++++++-- 6 files changed, 338 insertions(+), 99 deletions(-) diff --git a/lyra-game/src/render/graph/mod.rs b/lyra-game/src/render/graph/mod.rs index 9504bec..35fca1b 100644 --- a/lyra-game/src/render/graph/mod.rs +++ b/lyra-game/src/render/graph/mod.rs @@ -20,7 +20,7 @@ pub use render_target::*; use rustc_hash::FxHashMap; use tracing::{debug_span, instrument, trace, warn}; -use wgpu::ComputePass; +use wgpu::{CommandEncoder, ComputePass}; use super::resource::{ComputePipeline, Pipeline, RenderPipeline}; @@ -90,6 +90,7 @@ struct PassEntry { pipeline: Rc>>, } +#[derive(Clone)] pub struct BindGroupEntry { pub label: RenderGraphLabelValue, /// BindGroup @@ -99,6 +100,7 @@ pub struct BindGroupEntry { } #[allow(dead_code)] +#[derive(Clone)] struct ResourcedSlot { label: RenderGraphLabelValue, ty: SlotType, @@ -255,6 +257,10 @@ impl RenderGraph { *pipeline = Some(res); } } + + for sub in self.sub_graphs.values_mut() { + sub.setup(device); + } } #[instrument(skip(self, world))] @@ -306,6 +312,12 @@ impl RenderGraph { } } + fn create_encoder(&self) -> CommandEncoder { + self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("graph encoder"), + }) + } + #[instrument(skip(self))] pub fn render(&mut self) { let mut sorted: VecDeque = petgraph::algo::toposort(&self.node_graph, None) @@ -313,9 +325,14 @@ impl RenderGraph { .iter() .map(|i| self.node_graph[i.clone()].clone()) .collect(); - //debug!("Render graph execution order: {:?}", sorted); - let mut encoders = Vec::with_capacity(self.nodes.len() / 2); + // A bit of 'encoder hot potato' is played using this. + // Although the encoder is an option, its only an option so ownership of it can be given + // to the context for the time of the node execution. + // After the node is executed, the encoder is taken back. If the node is a presenter node, + // the encoder will be submitted and a new one will be made. + let mut encoder = Some(self.create_encoder()); + while let Some(pass_label) = sorted.pop_front() { let pass = self.nodes.get(&pass_label).unwrap(); let pass_inn = pass.inner.clone(); @@ -323,48 +340,37 @@ impl RenderGraph { let pass_desc = pass.desc.clone(); let pass_desc = (*pass_desc).borrow(); - let label = format!("{:?} Encoder", pass_label.0); - - // encoders are not needed for presenter nodes. - let encoder = if pass_desc.ty.should_have_pipeline() { - Some( - self.device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some(&label), - }), - ) - } else { - None - }; - // clone of the Rc's is required to appease the borrow checker let device = self.device.clone(); let queue = self.queue.clone(); - let mut context = RenderGraphContext::new(device, queue, encoder, pass_label.clone()); + + // create a new encoder since the last one was presented + if encoder.is_none() { + encoder = Some(self.create_encoder()); + } // all encoders need to be submitted before a presenter node is executed. if pass_desc.ty == NodeType::Presenter { - trace!("Submitting {} encoderd before presenting", encoders.len()); - self.queue.submit(encoders.drain(..)); + trace!("Submitting encoder before presenting"); + + let finished = encoder.take().unwrap().finish(); + self.queue.submit(std::iter::once(finished)); + + // Do not create a new encoder yet since this node may be the last node. + // A new encoder can be made on the next iteration of this loop. } + let mut context = RenderGraphContext::new(device, queue, encoder.take(), pass_label.clone()); + trace!("Executing {:?}", pass_label.0); let mut inner = pass_inn.borrow_mut(); inner.execute(self, &pass_desc, &mut context); - if let Some(encoder) = context.encoder { - encoders.push(encoder.finish()); - } + encoder = context.encoder; } - if !encoders.is_empty() { - warn!( - "{} encoders were not submitted in the same render cycle they were created. \ - Make sure there is a presenting pass at the end. You may still see something, \ - however it will be delayed a render cycle.", - encoders.len() - ); - self.queue.submit(encoders.into_iter()); + if let Some(encoder) = encoder { + self.queue.submit(std::iter::once(encoder.finish())); } } @@ -410,8 +416,9 @@ impl RenderGraph { #[inline(always)] pub fn bind_group_layout>(&self, label: L) -> &Rc { - self.try_bind_group_layout(label) - .expect("Unknown id for bind group layout") + let l = label.into(); + self.try_bind_group_layout(l.clone()) + .unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone())) } pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel) @@ -478,9 +485,67 @@ impl RenderGraph { pub fn sub_graph_mut>(&mut self, label: L) -> Option<&mut RenderGraph> { self.sub_graphs.get_mut(&label.into()) } + + pub fn add_sub_graph>(&mut self, label: L, sub: RenderGraph) { + self.sub_graphs.insert(label.into(), sub); + } + + /// Clone rendering resources (slots, bind groups, etc.) to a sub graph. + fn clone_resources_to_sub(&mut self, sub_graph: RenderGraphLabelValue, slots: Vec) { + // instead of inserting the slots to the sub graph as they are extracted from the parent graph, + // they are done separately to make the borrow checker happy. If this is not done, + // the borrow checker complains about multiple mutable borrows (or an inmutable borrow + // while mutable borrowing) to self; caused by borrowing the sub graph from self, and + // self.slots. + let mut collected_slots = VecDeque::new(); + let mut collected_bind_groups = VecDeque::new(); + + for slot in slots.iter() { + let mut found_res = false; + + // Since slots and bind groups may go by the same label, + // there must be a way to collect both of them. A flag variable is used to detect + // if neither was found. + + if let Some(slot_res) = self.slots.get(slot) { + collected_slots.push_back(slot_res.clone()); + found_res = true; + } + + if let Some(bg_res) = self.bind_groups.get(slot) { + collected_bind_groups.push_back(bg_res.clone()); + found_res = true; + } + + if !found_res { + panic!("sub graph is missing {:?} input slot or bind group", slot); + } + } + + let sg = self.sub_graph_mut(sub_graph.clone()).unwrap(); + while let Some(res) = collected_slots.pop_front() { + sg.slots.insert(res.label.clone(), res); + } + + while let Some(bg) = collected_bind_groups.pop_front() { + sg.bind_groups.insert(bg.label.clone(), bg); + } + } } -pub struct SubGraphNode(RenderGraphLabelValue); +pub struct SubGraphNode { + subg: RenderGraphLabelValue, + slots: Vec, +} + +impl SubGraphNode { + pub fn new>(sub_label: L, slot_labels: Vec) -> Self { + Self { + subg: sub_label.into(), + slots: slot_labels, + } + } +} impl Node for SubGraphNode { fn desc<'a, 'b>(&'a mut self, _: &'b mut RenderGraph) -> NodeDesc { @@ -488,8 +553,10 @@ impl Node for SubGraphNode { } fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, _: &mut RenderGraphContext) { - let sg = graph.sub_graph_mut(self.0.clone()) - .unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0)); + graph.clone_resources_to_sub(self.subg.clone(), self.slots.clone()); + + let sg = graph.sub_graph_mut(self.subg.clone()) + .unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.subg)); sg.prepare(world); } @@ -499,8 +566,10 @@ impl Node for SubGraphNode { _: &NodeDesc, _: &mut RenderGraphContext, ) { - let sg = graph.sub_graph_mut(self.0.clone()) - .unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0)); + graph.clone_resources_to_sub(self.subg.clone(), self.slots.clone()); + + let sg = graph.sub_graph_mut(self.subg.clone()) + .unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.subg)); sg.render(); } } 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 3eef02f..846ba38 100644 --- a/lyra-game/src/render/graph/passes/light_cull_compute.rs +++ b/lyra-game/src/render/graph/passes/light_cull_compute.rs @@ -8,7 +8,7 @@ use wgpu::util::DeviceExt; use crate::render::{ graph::{ Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue - }, renderer::ScreenSize, resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader} + }, renderer::ScreenSize, resource::{ComputePipeline, ComputePipelineDescriptor, Shader} }; use super::{BasePassSlots, LightBasePassSlots}; @@ -26,12 +26,14 @@ pub enum LightCullComputePassSlots { pub struct LightCullComputePass { workgroup_size: glam::UVec2, + pipeline: Option, } impl LightCullComputePass { pub fn new(screen_size: winit::dpi::PhysicalSize) -> Self { Self { workgroup_size: glam::UVec2::new(screen_size.width, screen_size.height), + pipeline: None, } } } @@ -41,18 +43,6 @@ impl Node for LightCullComputePass { &mut self, graph: &mut crate::render::graph::RenderGraph, ) -> crate::render::graph::NodeDesc { - let shader = Rc::new(Shader { - label: Some("light_cull_comp_shader".into()), - source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(), - }); - - // get the size of the work group for the grid - let main_rt = graph - .slot_value(BasePassSlots::MainRenderTarget) - .and_then(|s| s.as_render_target()) - .expect("missing main render target"); - self.workgroup_size = main_rt.size(); - // initialize some buffers with empty data let mut contents = Vec::::new(); let contents_len = @@ -165,16 +155,16 @@ impl Node for LightCullComputePass { label: Some("light_indices_grid_bind_group"), })); - drop(main_rt); + //drop(main_rt); - let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture); + /* let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture); let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); - let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize); + let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize); */ let mut desc = NodeDesc::new( NodeType::Compute, - Some(PipelineDescriptor::Compute(ComputePipelineDescriptor { + /* Some(PipelineDescriptor::Compute(ComputePipelineDescriptor { label: Some("light_cull_pipeline".into()), push_constant_ranges: vec![], layouts: vec![ @@ -186,7 +176,8 @@ impl Node for LightCullComputePass { ], shader, shader_entry_point: "cs_main".into(), - })), + })), */ + None, vec![( &LightCullComputePassSlots::LightIndicesGridGroup, light_indices_bg, @@ -194,11 +185,6 @@ impl Node for LightCullComputePass { )], ); - desc.add_texture_view_slot( - BasePassSlots::WindowTextureView, - SlotAttribute::Input, - None, - ); desc.add_buffer_slot( BasePassSlots::ScreenSize, SlotAttribute::Input, @@ -214,7 +200,7 @@ impl Node for LightCullComputePass { desc } - fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut World, context: &mut RenderGraphContext) { + fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, context: &mut RenderGraphContext) { context.queue_buffer_write_with(LightCullComputePassSlots::IndexCounterBuffer, 0, 0); let screen_size = world.get_resource::(); @@ -222,6 +208,37 @@ impl Node for LightCullComputePass { self.workgroup_size = screen_size.xy(); todo!("Resize buffers and other resources"); } + + if self.pipeline.is_none() { + let device = graph.device(); + + let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture); + let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); + let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); + let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize); + let light_indices_bg_layout = graph.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup); + + let shader = Rc::new(Shader { + label: Some("light_cull_comp_shader".into()), + source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(), + }); + + let pipeline = ComputePipeline::create(device, &ComputePipelineDescriptor { + label: Some("light_cull_pipeline".into()), + push_constant_ranges: vec![], + layouts: vec![ + depth_tex_bgl.clone(), + camera_bgl.clone(), + lights_bgl.clone(), + light_indices_bg_layout.clone(), + screen_size_bgl.clone(), + ], + shader, + shader_entry_point: "cs_main".into(), + }); + + self.pipeline = Some(pipeline); + } } fn execute( @@ -230,11 +247,7 @@ impl Node for LightCullComputePass { _: &crate::render::graph::NodeDesc, 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 pipeline = self.pipeline.as_mut().unwrap(); let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor { label: Some("light_cull_pass"), diff --git a/lyra-game/src/render/graph/passes/meshes.rs b/lyra-game/src/render/graph/passes/meshes.rs index e1f17e7..1891a39 100644 --- a/lyra-game/src/render/graph/passes/meshes.rs +++ b/lyra-game/src/render/graph/passes/meshes.rs @@ -16,7 +16,7 @@ use crate::{ render::{ desc_buf_lay::DescVertexBufferLayout, graph::{ Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext - }, 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, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex }, DeltaTime, }; @@ -61,6 +61,9 @@ pub struct MeshPass { entity_meshes: FxHashMap, default_texture: Option, + + pipeline: Option, + material_bgl: Option>, } impl MeshPass { @@ -209,7 +212,7 @@ impl Node for MeshPass { let device = graph.device(); let transforms = TransformBuffers::new(device); - let transform_bgl = transforms.bindgroup_layout.clone(); + //let transform_bgl = transforms.bindgroup_layout.clone(); self.transforms = Some(transforms); let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(&device)); @@ -222,6 +225,7 @@ impl Node for MeshPass { .contents(&[MaterialUniform::default()]) .finish_parts(device); let material_bgl = Rc::new(material_bgl); + self.material_bgl = Some(material_bgl.clone()); let material_bg = Rc::new(material_bg); self.material_buffer = Some(material_buf); @@ -231,13 +235,13 @@ impl Node 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_value(BasePassSlots::MainRenderTarget) + /* let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget) .and_then(|s| s.as_render_target()) .expect("missing main render target"); let surface_config_format = main_rt.format(); - drop(main_rt); + drop(main_rt); */ - let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); + /* let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); let light_grid_bgl = graph .bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup); @@ -245,11 +249,12 @@ impl Node for MeshPass { let shader = Rc::new(Shader { label: Some("base_shader".into()), source: include_str!("../../shaders/base.wgsl").to_string(), - }); + }); */ let desc = NodeDesc::new( NodeType::Render, - Some(PipelineDescriptor::Render(RenderPipelineDescriptor { + None, + /* Some(PipelineDescriptor::Render(RenderPipelineDescriptor { label: Some("meshes".into()), layouts: vec![ texture_bind_group_layout.clone(), @@ -287,7 +292,7 @@ impl Node for MeshPass { primitive: wgpu::PrimitiveState::default(), multisample: wgpu::MultisampleState::default(), multiview: None, - })), + })), */ vec![ (&MeshesPassSlots::Material, material_bg, Some(material_bgl)), ], @@ -296,8 +301,8 @@ impl Node for MeshPass { desc } - #[instrument(skip(self, _graph, world, context))] - fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { + #[instrument(skip(self, graph, world, context))] + fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { let device = &context.device; let queue = &context.queue; let render_limits = device.limits(); @@ -432,6 +437,67 @@ impl Node for MeshPass { let transforms = self.transforms.as_mut().unwrap(); transforms.send_to_gpu(&queue); + + if self.pipeline.is_none() { + let device = graph.device(); + + let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget) + .and_then(|s| s.as_render_target()) + .expect("missing main render target"); + let surface_config_format = main_rt.format(); + drop(main_rt); + + let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); + let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); + let light_grid_bgl = graph + .bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup); + + let shader = Rc::new(Shader { + label: Some("base_shader".into()), + source: include_str!("../../shaders/base.wgsl").to_string(), + }); + + self.pipeline = Some(RenderPipeline::create(device, &RenderPipelineDescriptor { + label: Some("meshes".into()), + layouts: vec![ + self.texture_bind_group_layout.as_ref().unwrap().clone(), + //transform_bgl + self.transforms.as_ref().unwrap().bindgroup_layout.clone(), + camera_bgl.clone(), + lights_bgl.clone(), + self.material_bgl.as_ref().unwrap().clone(), + self.texture_bind_group_layout.as_ref().unwrap().clone(), + light_grid_bgl.clone(), + ], + push_constant_ranges: vec![], + vertex: VertexState { + module: shader.clone(), + entry_point: "vs_main".into(), + buffers: vec![ + Vertex::desc().into(), + ], + }, + 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: Some(wgpu::DepthStencilState { + format: RenderTexture::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), // TODO: stencil buffer + bias: wgpu::DepthBiasState::default(), + }), + primitive: wgpu::PrimitiveState::default(), + multisample: wgpu::MultisampleState::default(), + multiview: None, + })); + } } fn execute( @@ -466,8 +532,9 @@ impl Node for MeshPass { let material_bg = graph .bind_group(MeshesPassSlots::Material); - let pipeline = graph.pipeline(context.label.clone()) - .expect("Failed to find pipeline for MeshPass"); + /* let pipeline = graph.pipeline(context.label.clone()) + .expect("Failed to find pipeline for MeshPass"); */ + let pipeline = self.pipeline.as_ref().unwrap(); let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), @@ -495,7 +562,7 @@ impl Node for MeshPass { }), }); - pass.set_pipeline(&pipeline.as_render()); + pass.set_pipeline(&pipeline); //let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup(); let default_texture = self.default_texture.as_ref().unwrap(); diff --git a/lyra-game/src/render/graph/passes/mod.rs b/lyra-game/src/render/graph/passes/mod.rs index c7950d2..ccd17ab 100644 --- a/lyra-game/src/render/graph/passes/mod.rs +++ b/lyra-game/src/render/graph/passes/mod.rs @@ -14,4 +14,7 @@ mod present_pass; pub use present_pass::*; mod init; -pub use init::*; \ No newline at end of file +pub use init::*; + +mod tint; +pub use tint::*; \ No newline at end of file diff --git a/lyra-game/src/render/graph/render_target.rs b/lyra-game/src/render/graph/render_target.rs index 84461c9..2e7eb5d 100644 --- a/lyra-game/src/render/graph/render_target.rs +++ b/lyra-game/src/render/graph/render_target.rs @@ -27,6 +27,25 @@ impl RenderTarget { Self(RenderTargetInner::Surface { surface, config }) } + pub fn new_texture(device: &wgpu::Device, format: wgpu::TextureFormat, size: math::UVec2) -> Self { + let tex = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: size.x, + height: size.y, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + Self(RenderTargetInner::Texture { texture: Arc::new(tex) }) + } + pub fn format(&self) -> wgpu::TextureFormat { match &self.0 { RenderTargetInner::Surface { config, .. } => config.format, @@ -63,8 +82,10 @@ impl RenderTarget { surface.configure(device, config); }, RenderTargetInner::Texture { texture } => { - let _ = texture; - todo!() + let format = texture.format(); + let size = self.size(); + + *self = Self::new_texture(device, format, size); }, } } diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index d492fd3..a1d301d 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -3,10 +3,11 @@ use std::ops::{Deref, DerefMut}; use std::sync::Arc; use lyra_ecs::World; +use lyra_game_derive::RenderGraphLabel; use tracing::{debug, instrument, warn}; use winit::window::Window; -use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderTarget}; +use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, TintPass, TintPassLabel, TintPassSlots}; use super::graph::RenderGraph; use super::{resource::RenderPipeline, render_job::RenderJob}; @@ -30,6 +31,9 @@ impl DerefMut for ScreenSize { } } +#[derive(Debug, Clone, Copy, Hash, RenderGraphLabel)] +struct TestSubGraphLabel; + pub trait Renderer { fn prepare(&mut self, main_world: &mut World); fn render(&mut self) -> Result<(), wgpu::SurfaceError>; @@ -123,37 +127,99 @@ impl BasicRenderer { let device = Arc::new(device); let queue = Arc::new(queue); - let mut g = RenderGraph::new(device.clone(), queue.clone()); - + let surface_size = wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1 + }; let surface_target = RenderTarget::from_surface(surface, config); + let headless_texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("headless_texture"), + size: surface_size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: surface_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT // we'll be rendering to it + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + + let headless_target = RenderTarget::from(headless_texture); + + + let mut main_graph = RenderGraph::new(device.clone(), queue.clone()); + debug!("Adding base pass"); + main_graph.add_node(BasePassLabel, BasePass::new(surface_target)); + + { + let mut forward_plus_graph = RenderGraph::new(device.clone(), queue.clone()); + + debug!("Adding light base pass"); + forward_plus_graph.add_node(LightBasePassLabel, LightBasePass::new()); + + debug!("Adding light cull compute pass"); + forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size)); + + debug!("Adding mesh pass"); + forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new()); + + forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel); + + main_graph.add_sub_graph(TestSubGraphLabel, forward_plus_graph); + main_graph.add_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel, + vec![ + RenderGraphLabelValue::from(BasePassSlots::WindowTextureView), + RenderGraphLabelValue::from(BasePassSlots::MainRenderTarget), + RenderGraphLabelValue::from(BasePassSlots::DepthTexture), + RenderGraphLabelValue::from(BasePassSlots::DepthTextureView), + RenderGraphLabelValue::from(BasePassSlots::Camera), + RenderGraphLabelValue::from(BasePassSlots::ScreenSize), + ] + )); + } + + let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame);//TintPassSlots::Frame); + let p = PresentPass::from_node_label(present_pass_label.clone()); + main_graph.add_node(p.label.clone(), p); + + main_graph.add_edge(BasePassLabel, TestSubGraphLabel); + main_graph.add_edge(TestSubGraphLabel, present_pass_label); + + /* debug!("Adding base pass"); g.add_node(BasePassLabel, BasePass::new(surface_target)); - debug!("Adding light base pass"); - g.add_node(LightBasePassLabel, LightBasePass::new()); - debug!("Adding light cull compute pass"); - g.add_node(LightCullComputePassLabel, LightCullComputePass::new(size)); + //debug!("Adding triangle pass"); //g.add_node(TrianglePass::new()); - debug!("Adding mesh pass"); - g.add_node(MeshesPassLabel, MeshPass::new()); + debug!("Adding present pass"); - let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame); + let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame);//TintPassSlots::Frame); let p = PresentPass::from_node_label(present_pass_label.clone()); - g.add_node(p.label.clone(), p); + g.add_node(p.label.clone(), p); */ - g.add_edge(BasePassLabel, LightBasePassLabel); + /* debug!("adding tint pass"); + g.add_node(TintPassLabel, TintPass::new(surface_target)); + + g.add_edge(BasePassLabel, TintPassLabel); + g.add_edge(LightCullComputePassLabel, TintPassLabel); + g.add_edge(MeshesPassLabel, TintPassLabel); + + g.add_edge(TintPassLabel, present_pass_label.clone()); + */ + + /* g.add_edge(BasePassLabel, LightBasePassLabel); g.add_edge(LightBasePassLabel, LightCullComputePassLabel); g.add_edge(BasePassLabel, MeshesPassLabel); - // make sure that present runs last g.add_edge(BasePassLabel, present_pass_label.clone()); g.add_edge(LightCullComputePassLabel, present_pass_label.clone()); - g.add_edge(MeshesPassLabel, present_pass_label.clone()); + g.add_edge(MeshesPassLabel, present_pass_label.clone()); */ - g.setup(&device); + main_graph.setup(&device); Self { window, @@ -169,7 +235,7 @@ impl BasicRenderer { render_pipelines: Default::default(), render_jobs: Default::default(), - graph: g, + graph: main_graph, } } }