render: get sub render graphs working and create a simple test of them

This commit is contained in:
SeanOMik 2024-06-25 21:32:29 -04:00
parent 5f1a61ef52
commit 6c1bff5768
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
6 changed files with 338 additions and 99 deletions

View File

@ -20,7 +20,7 @@ pub use render_target::*;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use tracing::{debug_span, instrument, trace, warn}; use tracing::{debug_span, instrument, trace, warn};
use wgpu::ComputePass; use wgpu::{CommandEncoder, ComputePass};
use super::resource::{ComputePipeline, Pipeline, RenderPipeline}; use super::resource::{ComputePipeline, Pipeline, RenderPipeline};
@ -90,6 +90,7 @@ struct PassEntry {
pipeline: Rc<RefCell<Option<PipelineResource>>>, pipeline: Rc<RefCell<Option<PipelineResource>>>,
} }
#[derive(Clone)]
pub struct BindGroupEntry { pub struct BindGroupEntry {
pub label: RenderGraphLabelValue, pub label: RenderGraphLabelValue,
/// BindGroup /// BindGroup
@ -99,6 +100,7 @@ pub struct BindGroupEntry {
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone)]
struct ResourcedSlot { struct ResourcedSlot {
label: RenderGraphLabelValue, label: RenderGraphLabelValue,
ty: SlotType, ty: SlotType,
@ -255,6 +257,10 @@ impl RenderGraph {
*pipeline = Some(res); *pipeline = Some(res);
} }
} }
for sub in self.sub_graphs.values_mut() {
sub.setup(device);
}
} }
#[instrument(skip(self, world))] #[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))] #[instrument(skip(self))]
pub fn render(&mut self) { pub fn render(&mut self) {
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.node_graph, None) let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.node_graph, None)
@ -313,9 +325,14 @@ impl RenderGraph {
.iter() .iter()
.map(|i| self.node_graph[i.clone()].clone()) .map(|i| self.node_graph[i.clone()].clone())
.collect(); .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() { while let Some(pass_label) = sorted.pop_front() {
let pass = self.nodes.get(&pass_label).unwrap(); let pass = self.nodes.get(&pass_label).unwrap();
let pass_inn = pass.inner.clone(); let pass_inn = pass.inner.clone();
@ -323,48 +340,37 @@ impl RenderGraph {
let pass_desc = pass.desc.clone(); let pass_desc = pass.desc.clone();
let pass_desc = (*pass_desc).borrow(); 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 // 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());
// 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. // all encoders need to be submitted before a presenter node is executed.
if pass_desc.ty == NodeType::Presenter { if pass_desc.ty == NodeType::Presenter {
trace!("Submitting {} encoderd before presenting", encoders.len()); trace!("Submitting encoder before presenting");
self.queue.submit(encoders.drain(..));
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); trace!("Executing {:?}", pass_label.0);
let mut inner = pass_inn.borrow_mut(); let mut inner = pass_inn.borrow_mut();
inner.execute(self, &pass_desc, &mut context); inner.execute(self, &pass_desc, &mut context);
if let Some(encoder) = context.encoder { encoder = context.encoder;
encoders.push(encoder.finish());
}
} }
if !encoders.is_empty() { if let Some(encoder) = encoder {
warn!( self.queue.submit(std::iter::once(encoder.finish()));
"{} 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());
} }
} }
@ -410,8 +416,9 @@ impl RenderGraph {
#[inline(always)] #[inline(always)]
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> { pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> {
self.try_bind_group_layout(label) let l = label.into();
.expect("Unknown id for bind group layout") 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) pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel)
@ -478,9 +485,67 @@ impl RenderGraph {
pub fn sub_graph_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut RenderGraph> { pub fn sub_graph_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut RenderGraph> {
self.sub_graphs.get_mut(&label.into()) self.sub_graphs.get_mut(&label.into())
} }
pub fn add_sub_graph<L: Into<RenderGraphLabelValue>>(&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<RenderGraphLabelValue>) {
// 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<RenderGraphLabelValue>,
}
impl SubGraphNode {
pub fn new<L: Into<RenderGraphLabelValue>>(sub_label: L, slot_labels: Vec<RenderGraphLabelValue>) -> Self {
Self {
subg: sub_label.into(),
slots: slot_labels,
}
}
}
impl Node for SubGraphNode { impl Node for SubGraphNode {
fn desc<'a, 'b>(&'a mut self, _: &'b mut RenderGraph) -> NodeDesc { 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) { fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, _: &mut RenderGraphContext) {
let sg = graph.sub_graph_mut(self.0.clone()) graph.clone_resources_to_sub(self.subg.clone(), self.slots.clone());
.unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0));
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); sg.prepare(world);
} }
@ -499,8 +566,10 @@ impl Node for SubGraphNode {
_: &NodeDesc, _: &NodeDesc,
_: &mut RenderGraphContext, _: &mut RenderGraphContext,
) { ) {
let sg = graph.sub_graph_mut(self.0.clone()) graph.clone_resources_to_sub(self.subg.clone(), self.slots.clone());
.unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0));
let sg = graph.sub_graph_mut(self.subg.clone())
.unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.subg));
sg.render(); sg.render();
} }
} }

View File

@ -8,7 +8,7 @@ use wgpu::util::DeviceExt;
use crate::render::{ use crate::render::{
graph::{ graph::{
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
}, renderer::ScreenSize, resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader} }, renderer::ScreenSize, resource::{ComputePipeline, ComputePipelineDescriptor, Shader}
}; };
use super::{BasePassSlots, LightBasePassSlots}; use super::{BasePassSlots, LightBasePassSlots};
@ -26,12 +26,14 @@ pub enum LightCullComputePassSlots {
pub struct LightCullComputePass { pub struct LightCullComputePass {
workgroup_size: glam::UVec2, workgroup_size: glam::UVec2,
pipeline: Option<ComputePipeline>,
} }
impl LightCullComputePass { impl LightCullComputePass {
pub fn new(screen_size: winit::dpi::PhysicalSize<u32>) -> Self { pub fn new(screen_size: winit::dpi::PhysicalSize<u32>) -> Self {
Self { Self {
workgroup_size: glam::UVec2::new(screen_size.width, screen_size.height), workgroup_size: glam::UVec2::new(screen_size.width, screen_size.height),
pipeline: None,
} }
} }
} }
@ -41,18 +43,6 @@ impl Node for LightCullComputePass {
&mut self, &mut self,
graph: &mut crate::render::graph::RenderGraph, graph: &mut crate::render::graph::RenderGraph,
) -> crate::render::graph::NodeDesc { ) -> 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 // initialize some buffers with empty data
let mut contents = Vec::<u8>::new(); let mut contents = Vec::<u8>::new();
let contents_len = let contents_len =
@ -165,16 +155,16 @@ impl Node for LightCullComputePass {
label: Some("light_indices_grid_bind_group"), 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 camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); 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( let mut desc = NodeDesc::new(
NodeType::Compute, NodeType::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![],
layouts: vec![ layouts: vec![
@ -186,7 +176,8 @@ impl Node for LightCullComputePass {
], ],
shader, shader,
shader_entry_point: "cs_main".into(), shader_entry_point: "cs_main".into(),
})), })), */
None,
vec![( vec![(
&LightCullComputePassSlots::LightIndicesGridGroup, &LightCullComputePassSlots::LightIndicesGridGroup,
light_indices_bg, 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( desc.add_buffer_slot(
BasePassSlots::ScreenSize, BasePassSlots::ScreenSize,
SlotAttribute::Input, SlotAttribute::Input,
@ -214,7 +200,7 @@ impl Node for LightCullComputePass {
desc 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); context.queue_buffer_write_with(LightCullComputePassSlots::IndexCounterBuffer, 0, 0);
let screen_size = world.get_resource::<ScreenSize>(); let screen_size = world.get_resource::<ScreenSize>();
@ -222,6 +208,37 @@ impl Node for LightCullComputePass {
self.workgroup_size = screen_size.xy(); self.workgroup_size = screen_size.xy();
todo!("Resize buffers and other resources"); 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( fn execute(
@ -230,11 +247,7 @@ impl Node for LightCullComputePass {
_: &crate::render::graph::NodeDesc, _: &crate::render::graph::NodeDesc,
context: &mut RenderGraphContext, context: &mut RenderGraphContext,
) { ) {
let label = context.label.clone(); let pipeline = self.pipeline.as_mut().unwrap();
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"),

View File

@ -16,7 +16,7 @@ use crate::{
render::{ render::{
desc_buf_lay::DescVertexBufferLayout, graph::{ desc_buf_lay::DescVertexBufferLayout, graph::{
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext 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, DeltaTime,
}; };
@ -61,6 +61,9 @@ pub struct MeshPass {
entity_meshes: FxHashMap<Entity, uuid::Uuid>, entity_meshes: FxHashMap<Entity, uuid::Uuid>,
default_texture: Option<RenderTexture>, default_texture: Option<RenderTexture>,
pipeline: Option<RenderPipeline>,
material_bgl: Option<Rc<wgpu::BindGroupLayout>>,
} }
impl MeshPass { impl MeshPass {
@ -209,7 +212,7 @@ impl Node for MeshPass {
let device = graph.device(); let device = graph.device();
let transforms = TransformBuffers::new(device); let transforms = TransformBuffers::new(device);
let transform_bgl = transforms.bindgroup_layout.clone(); //let transform_bgl = transforms.bindgroup_layout.clone();
self.transforms = Some(transforms); self.transforms = Some(transforms);
let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(&device)); let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(&device));
@ -222,6 +225,7 @@ impl Node for MeshPass {
.contents(&[MaterialUniform::default()]) .contents(&[MaterialUniform::default()])
.finish_parts(device); .finish_parts(device);
let material_bgl = Rc::new(material_bgl); let material_bgl = Rc::new(material_bgl);
self.material_bgl = Some(material_bgl.clone());
let material_bg = Rc::new(material_bg); let material_bg = Rc::new(material_bg);
self.material_buffer = Some(material_buf); 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()); 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_value(BasePassSlots::MainRenderTarget)
.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.format(); 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 lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
let light_grid_bgl = graph let light_grid_bgl = graph
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup); .bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
@ -245,11 +249,12 @@ impl Node for MeshPass {
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 = NodeDesc::new(
NodeType::Render, NodeType::Render,
Some(PipelineDescriptor::Render(RenderPipelineDescriptor { None,
/* Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
label: Some("meshes".into()), label: Some("meshes".into()),
layouts: vec![ layouts: vec![
texture_bind_group_layout.clone(), texture_bind_group_layout.clone(),
@ -287,7 +292,7 @@ impl Node for MeshPass {
primitive: wgpu::PrimitiveState::default(), primitive: wgpu::PrimitiveState::default(),
multisample: wgpu::MultisampleState::default(), multisample: wgpu::MultisampleState::default(),
multiview: None, multiview: None,
})), })), */
vec![ vec![
(&MeshesPassSlots::Material, material_bg, Some(material_bgl)), (&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
], ],
@ -296,8 +301,8 @@ impl Node for MeshPass {
desc desc
} }
#[instrument(skip(self, _graph, world, context))] #[instrument(skip(self, graph, world, context))]
fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
let device = &context.device; let device = &context.device;
let queue = &context.queue; let queue = &context.queue;
let render_limits = device.limits(); let render_limits = device.limits();
@ -432,6 +437,67 @@ impl Node for MeshPass {
let transforms = self.transforms.as_mut().unwrap(); let transforms = self.transforms.as_mut().unwrap();
transforms.send_to_gpu(&queue); 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( fn execute(
@ -466,8 +532,9 @@ impl Node for MeshPass {
let material_bg = graph let material_bg = graph
.bind_group(MeshesPassSlots::Material); .bind_group(MeshesPassSlots::Material);
let pipeline = graph.pipeline(context.label.clone()) /* let pipeline = graph.pipeline(context.label.clone())
.expect("Failed to find pipeline for MeshPass"); .expect("Failed to find pipeline for MeshPass"); */
let pipeline = self.pipeline.as_ref().unwrap();
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"),
@ -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 material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();
let default_texture = self.default_texture.as_ref().unwrap(); let default_texture = self.default_texture.as_ref().unwrap();

View File

@ -15,3 +15,6 @@ pub use present_pass::*;
mod init; mod init;
pub use init::*; pub use init::*;
mod tint;
pub use tint::*;

View File

@ -27,6 +27,25 @@ impl RenderTarget {
Self(RenderTargetInner::Surface { surface, config }) 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 { pub fn format(&self) -> wgpu::TextureFormat {
match &self.0 { match &self.0 {
RenderTargetInner::Surface { config, .. } => config.format, RenderTargetInner::Surface { config, .. } => config.format,
@ -63,8 +82,10 @@ impl RenderTarget {
surface.configure(device, config); surface.configure(device, config);
}, },
RenderTargetInner::Texture { texture } => { RenderTargetInner::Texture { texture } => {
let _ = texture; let format = texture.format();
todo!() let size = self.size();
*self = Self::new_texture(device, format, size);
}, },
} }
} }

View File

@ -3,10 +3,11 @@ use std::ops::{Deref, DerefMut};
use std::sync::Arc; use std::sync::Arc;
use lyra_ecs::World; use lyra_ecs::World;
use lyra_game_derive::RenderGraphLabel;
use tracing::{debug, instrument, warn}; use tracing::{debug, instrument, warn};
use winit::window::Window; 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::graph::RenderGraph;
use super::{resource::RenderPipeline, render_job::RenderJob}; 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 { pub trait Renderer {
fn prepare(&mut self, main_world: &mut World); fn prepare(&mut self, main_world: &mut World);
fn render(&mut self) -> Result<(), wgpu::SurfaceError>; fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
@ -123,37 +127,99 @@ impl BasicRenderer {
let device = Arc::new(device); let device = Arc::new(device);
let queue = Arc::new(queue); 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 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"); debug!("Adding base pass");
g.add_node(BasePassLabel, BasePass::new(surface_target)); 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"); debug!("Adding light base pass");
g.add_node(LightBasePassLabel, LightBasePass::new()); forward_plus_graph.add_node(LightBasePassLabel, LightBasePass::new());
debug!("Adding light cull compute pass"); debug!("Adding light cull compute pass");
g.add_node(LightCullComputePassLabel, LightCullComputePass::new(size)); 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 triangle pass"); //debug!("Adding triangle pass");
//g.add_node(TrianglePass::new()); //g.add_node(TrianglePass::new());
debug!("Adding mesh pass");
g.add_node(MeshesPassLabel, MeshPass::new());
debug!("Adding present pass"); 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()); 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(LightBasePassLabel, LightCullComputePassLabel);
g.add_edge(BasePassLabel, MeshesPassLabel); g.add_edge(BasePassLabel, MeshesPassLabel);
// make sure that present runs last
g.add_edge(BasePassLabel, present_pass_label.clone()); g.add_edge(BasePassLabel, present_pass_label.clone());
g.add_edge(LightCullComputePassLabel, 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 { Self {
window, window,
@ -169,7 +235,7 @@ impl BasicRenderer {
render_pipelines: Default::default(), render_pipelines: Default::default(),
render_jobs: Default::default(), render_jobs: Default::default(),
graph: g, graph: main_graph,
} }
} }
} }