From cee6e44d6122dd68f9a0b0bfe100fecd94e79282 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Tue, 14 May 2024 18:46:35 -0400 Subject: [PATCH] render: support creating bindgroups for passes and updating buffers every frame --- lyra-game/src/render/graph/mod.rs | 175 +++++------------- lyra-game/src/render/graph/pass.rs | 62 +++---- lyra-game/src/render/graph/passes/triangle.rs | 68 +++++-- lyra-game/src/render/graph/slot_desc.rs | 18 +- lyra-game/src/render/render_buffer.rs | 28 ++- lyra-game/src/render/renderer.rs | 4 +- .../src/render/resource/render_pipeline.rs | 4 +- .../src/render/shaders/simple_phong.wgsl | 87 +++++++++ lyra-game/src/render/shaders/triangle.wgsl | 5 +- 9 files changed, 266 insertions(+), 185 deletions(-) diff --git a/lyra-game/src/render/graph/mod.rs b/lyra-game/src/render/graph/mod.rs index d975370..379e2a9 100644 --- a/lyra-game/src/render/graph/mod.rs +++ b/lyra-game/src/render/graph/mod.rs @@ -3,9 +3,11 @@ use std::{ cell::RefCell, collections::{HashMap, VecDeque}, ptr::NonNull, + rc::Rc, sync::Arc, }; +use lyra_ecs::World; pub use pass::*; mod passes; @@ -17,13 +19,14 @@ pub use slot_desc::*; mod execution_path; use rustc_hash::{FxHashMap, FxHashSet}; -use tracing::{debug, debug_span, instrument}; +use tracing::{debug, debug_span, instrument, trace}; use wgpu::{util::DeviceExt, RenderPass}; use self::execution_path::GraphExecutionPath; use super::{ - renderer::{BasicRenderer, Renderer}, resource::{Pipeline, RenderPipeline}, + renderer::{BasicRenderer, Renderer}, + resource::{Pipeline, RenderPipeline}, }; //#[derive(Clone)] @@ -37,11 +40,6 @@ struct ResourcedSlot { //slot: RenderPassSlot, ty: SlotType, value: SlotValue, - // will when the bind group is created - /// The id of the bind group for this slot. Becomes `Some` when the render graph creates the - /// bind group - bind_group_id: Option, - create_desc: Option, } /// Stores the pipeline and other resources it uses. @@ -54,6 +52,8 @@ pub struct PipelineResource { } pub struct RenderGraph { + device: Rc, + queue: Rc, slots: FxHashMap, slot_names: HashMap, passes: FxHashMap, @@ -70,7 +70,7 @@ pub struct RenderGraph { } impl RenderGraph { - pub fn new(surface_config: wgpu::SurfaceConfiguration) -> Self { + pub fn new(device: Rc, queue: Rc, surface_config: wgpu::SurfaceConfiguration) -> Self { let mut slots = FxHashMap::default(); let mut slot_names = HashMap::default(); @@ -79,14 +79,15 @@ impl RenderGraph { ResourcedSlot { name: "window_texture_view".to_string(), ty: SlotType::TextureView, + // this will get set in prepare stage. value: SlotValue::None, - bind_group_id: None, - create_desc: None, }, ); slot_names.insert("window_texture_view".to_string(), 0u64); Self { + device, + queue, slots, slot_names, passes: Default::default(), @@ -98,6 +99,10 @@ impl RenderGraph { } } + pub fn device(&self) -> &wgpu::Device { + &*self.device + } + pub fn next_id(&mut self) -> u64 { self.current_id += 1; self.current_id @@ -115,7 +120,7 @@ impl RenderGraph { self.passes.get(&id).map(|s| &*s.desc) } - pub fn add_pass(&mut self, pass: P) { + pub fn add_pass(&mut self, mut pass: P) { let mut desc = pass.desc(self); for slot in &mut desc.slots { @@ -124,18 +129,19 @@ impl RenderGraph { .get(&slot.name) .and_then(|id| self.slots.get_mut(id).map(|s| (id, s))) { + // if there is a slot of the same name slot.id = *id; - if slot.desc.is_some() && other.create_desc.is_none() { - other.create_desc = slot.desc.clone(); - } + debug_assert_eq!( + slot.ty, other.ty, + "slot {} in pass {} does not match existing slot of same name", + slot.name, desc.name + ); } else { let res_slot = ResourcedSlot { name: slot.name.clone(), ty: slot.ty, value: SlotValue::None, - bind_group_id: None, - create_desc: slot.desc.clone(), }; self.slots.insert(slot.id, res_slot); @@ -155,8 +161,6 @@ impl RenderGraph { /// Creates all buffers required for the passes, also creates an internal execution path. #[instrument(skip(self, device))] pub fn setup(&mut self, device: &wgpu::Device) { - let mut later_slots = VecDeque::new(); - // For all passes, create their pipelines for pass in self.passes.values() { if let Some(pipei) = &pass.desc.pipeline_desc { @@ -174,101 +178,27 @@ impl RenderGraph { self.pipelines.insert(pass.desc.id, res); } } - - for (slot_id, slot) in &mut self.slots { - if slot.bind_group_id.is_none() && slot.create_desc.is_some() { - let s = debug_span!("res_creation", slot = slot.name); - let _e = s.enter(); - - debug!("Creating bind group for slot"); - - match &slot.create_desc { - Some(SlotDescriptor::BufferInit(bi)) => { - let label = format!("B_{}", slot.name); - let wb = bi.as_wgpu(Some(&label)); - - let buf = device.create_buffer_init(&wb); - slot.value = SlotValue::Buffer(buf); - - debug!("Created and initialized buffer for slot"); - } - Some(SlotDescriptor::Buffer(b)) => { - let label = format!("B_{}", slot.name); - let wb = b.as_wgpu(Some(&label)); - - let buf = device.create_buffer(&wb); - slot.value = SlotValue::Buffer(buf); - - debug!("Created buffer"); - } - Some(SlotDescriptor::Texture(t)) => { - let label = format!("Tex_{}", slot.name); - let wt = t.as_wgpu(Some(&label)); - - let tex = device.create_texture(&wt); - slot.value = SlotValue::Texture(tex); - - debug!("Created texture"); - } - Some(SlotDescriptor::TextureView(tv)) => { - // texture views cannot be done in this step. Not only would we run into a - // borrow checker error when trying to get the texture for the view, we - // can also not guarantee that the texture was actually created. - let tex_slot = self - .slot_names - .get(&tv.texture_label) - .expect("Failed to find texture for texture view slot"); - later_slots.push_back((*slot_id, *tex_slot)); - - debug!("Queuing slot resources for later creation"); - } - - //Some(SlotDescriptor::Sampler(b)) => {}, - //Some(SlotDescriptor::Texture(b)) => {}, - Some(SlotDescriptor::None) => {} - None => {} - _ => todo!(), - } - } - } - - // create buffers for the queued slots - while let Some((lower_id, upper_id)) = later_slots.pop_front() { - let many = self.slots.get_many_mut([&lower_id, &upper_id]).unwrap(); - let (lower_slot, upper_slot) = match many { - [a, b] => (a, b), - }; - - let s = debug_span!("deferred_res_creation", slot = lower_slot.name); - let _e = s.enter(); - - match &lower_slot.create_desc { - Some(SlotDescriptor::TextureView(tv)) => { - let label = format!("TexView_{}", lower_slot.name); - - let wt = tv.as_wgpu(Some(&label)); - let tex = upper_slot.value.as_texture(); - - let texview = tex.create_view(&wt); - lower_slot.value = SlotValue::TextureView(texview); - - debug!(slot = lower_slot.name, "Created texture view"); - } - Some(SlotDescriptor::None) => {} - None => {} - _ => unreachable!("this slot should not have been put into the do later list"), - } - } } - pub fn prepare(&mut self) { + #[instrument(skip(self, world))] + pub fn prepare(&mut self, world: &mut World) { + // prepare all passes + let mut context = RenderGraphContext::new(&self.queue, None); + for (_, pass) in &mut self.passes { + let mut inner = pass.inner.borrow_mut(); + inner.prepare(world, &mut context); + } + + // create the execution path for the graph. This will be executed in `RenderGraph::render` let builtin = { let mut h = FxHashSet::default(); h.insert(0u64); h }; let descs = self.passes.values().map(|p| &*p.desc).collect(); - self.exec_path = Some(GraphExecutionPath::new(builtin, descs)); + let path = GraphExecutionPath::new(builtin, descs); + trace!("Found {} steps in the rendergraph to execute", path.queue.len()); + self.exec_path = Some(path); } pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, surface: &wgpu::Surface) { @@ -298,12 +228,12 @@ impl RenderGraph { let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some(&label), }); - let mut context = RenderGraphContext::new(encoder, queue); + let mut context = RenderGraphContext::new(queue, Some(encoder)); let mut inner = pass_inn.borrow_mut(); inner.execute(self, &*pass_desc, &mut context); - encoders.push(context.encoder.finish()); + encoders.push(context.encoder.unwrap().finish()); } queue.submit(encoders.into_iter()); @@ -327,23 +257,12 @@ impl RenderGraph { #[inline(always)] pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> { - self.slots.get(&id).and_then(|s| { - self.bind_groups.get( - &s.bind_group_id - .expect("Slot bind group has not been created yet"), - ) - }) + todo!() } #[inline(always)] pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup { - let bg_id = self - .slots - .get(&id) - .expect("unknown slot id") - .bind_group_id - .expect("Slot bind group has not been created yet"); - self.bind_group(id) + todo!() } } @@ -355,14 +274,14 @@ pub(crate) struct BufferWrite { pub struct RenderGraphContext<'a> { /// Becomes None when the encoder is submitted - pub(crate) encoder: wgpu::CommandEncoder, + pub(crate) encoder: Option, pub(crate) queue: &'a wgpu::Queue, pub(crate) buffer_writes: Vec, renderpass_desc: Vec>, } impl<'a> RenderGraphContext<'a> { - pub fn new(encoder: wgpu::CommandEncoder, queue: &'a wgpu::Queue) -> Self { + pub fn new(queue: &'a wgpu::Queue, encoder: Option) -> Self { Self { encoder, queue, @@ -375,11 +294,19 @@ impl<'a> RenderGraphContext<'a> { &'a mut self, desc: wgpu::RenderPassDescriptor<'a, 'a>, ) -> wgpu::RenderPass { - self.encoder.begin_render_pass(&desc) + self.encoder + .as_mut() + .expect("RenderGraphContext is missing a command encoder. This is likely \ + because you are trying to run render commands in the prepare stage.") + .begin_render_pass(&desc) } pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass { - self.encoder.begin_compute_pass(desc) + self.encoder + .as_mut() + .expect("RenderGraphContext is missing a command encoder. This is likely \ + because you are trying to run render commands in the prepare stage.") + .begin_compute_pass(desc) } pub fn write_buffer(&mut self, target_slot: &str, bytes: &[u8]) { diff --git a/lyra-game/src/render/graph/pass.rs b/lyra-game/src/render/graph/pass.rs index 0ae51b1..5aa7e83 100644 --- a/lyra-game/src/render/graph/pass.rs +++ b/lyra-game/src/render/graph/pass.rs @@ -30,7 +30,7 @@ pub enum SlotValue { TextureView(wgpu::TextureView), Sampler(wgpu::Sampler), Texture(wgpu::Texture), - Buffer(wgpu::Buffer), + Buffer(Rc), } impl SlotValue { @@ -49,19 +49,6 @@ impl SlotValue { } } } - -#[derive(Clone, Debug)] -pub enum SlotDescriptor { - /// Most likely this slot is an input, so it doesn't need to specify the descriptor. - None, - - TextureView(TextureViewDescriptor), - Sampler(SamplerDescriptor), - Texture(TextureDescriptor), - Buffer(BufferDescriptor), - BufferInit(BufferInitDescriptor), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum SlotAttribute { Input, @@ -76,7 +63,7 @@ pub struct RenderPassSlot { pub name: String, /// The descriptor of the slot value. /// This will be `None` if this slot is an input. - pub desc: Option, + pub value: Option>, } #[derive(Clone)] @@ -171,14 +158,14 @@ impl RenderGraphPassDesc { id: u64, name: &str, attribute: SlotAttribute, - desc: Option, + value: Option, ) { debug_assert!( matches!( - desc, - None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_)) + value, + None | Some(SlotValue::Buffer(_)) ), - "slot descriptor does not match the type of slot" + "slot value is not a buffer" ); let slot = RenderPassSlot { @@ -186,7 +173,7 @@ impl RenderGraphPassDesc { name: name.to_string(), ty: SlotType::Buffer, attribute, - desc, + value: value.map(|v| Rc::new(v)), }; self.add_slot(slot); } @@ -197,11 +184,14 @@ impl RenderGraphPassDesc { id: u64, name: &str, attribute: SlotAttribute, - desc: Option, + value: Option, ) { debug_assert!( - matches!(desc, None | Some(SlotDescriptor::Texture(_))), - "slot descriptor does not match the type of slot" + matches!( + value, + None | Some(SlotValue::Texture(_)) + ), + "slot value is not a texture" ); let slot = RenderPassSlot { @@ -209,7 +199,7 @@ impl RenderGraphPassDesc { name: name.to_string(), ty: SlotType::Texture, attribute, - desc, + value: value.map(|v| Rc::new(v)), }; self.add_slot(slot); } @@ -220,11 +210,14 @@ impl RenderGraphPassDesc { id: u64, name: &str, attribute: SlotAttribute, - desc: Option, + value: Option, ) { debug_assert!( - matches!(desc, None | Some(SlotDescriptor::TextureView(_))), - "slot descriptor does not match the type of slot" + matches!( + value, + None | Some(SlotValue::TextureView(_)) + ), + "slot value is not a texture view" ); let slot = RenderPassSlot { @@ -232,7 +225,7 @@ impl RenderGraphPassDesc { name: name.to_string(), ty: SlotType::TextureView, attribute, - desc, + value: value.map(|v| Rc::new(v)), }; self.add_slot(slot); } @@ -243,11 +236,14 @@ impl RenderGraphPassDesc { id: u64, name: &str, attribute: SlotAttribute, - desc: Option, + value: Option, ) { debug_assert!( - matches!(desc, None | Some(SlotDescriptor::Sampler(_))), - "slot descriptor does not match the type of slot" + matches!( + value, + None | Some(SlotValue::Sampler(_)) + ), + "slot value is not a sampler" ); let slot = RenderPassSlot { @@ -255,7 +251,7 @@ impl RenderGraphPassDesc { name: name.to_string(), ty: SlotType::Sampler, attribute, - desc, + value: value.map(|v| Rc::new(v)), }; self.add_slot(slot); } @@ -279,7 +275,7 @@ pub trait RenderGraphPass: 'static { /// Create a render pass describer. /// /// The `id` argument is passed as mutable so you can increment it as you use it for new slots. - fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc; + fn desc<'a, 'b>(&'a mut self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc; fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext); fn execute( diff --git a/lyra-game/src/render/graph/passes/triangle.rs b/lyra-game/src/render/graph/passes/triangle.rs index 1ab2a62..6958536 100644 --- a/lyra-game/src/render/graph/passes/triangle.rs +++ b/lyra-game/src/render/graph/passes/triangle.rs @@ -8,21 +8,26 @@ use crate::{ render::{ camera::{CameraUniform, RenderCamera}, graph::{ - BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, RenderGraphPass, - RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, SlotAttribute, - SlotDescriptor, + BufferDescriptor, BufferInitDescriptor, PipelineShaderDesc, RenderGraphContext, + RenderGraphPass, RenderGraphPassDesc, RenderGraphPipelineInfo, RenderPassType, + SlotAttribute, SlotValue, }, + render_buffer::BufferWrapper, renderer::ScreenSize, resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState}, }, - scene::CameraComponent, + scene::CameraComponent, DeltaTime, }; /// Supplies some basic things other passes needs. /// /// screen size buffer, camera buffer, #[derive(Default)] -pub struct TrianglePass; +pub struct TrianglePass { + color_bg: Option, + color_buf: Option>, + acc: f32, +} impl TrianglePass { pub fn new() -> Self { @@ -32,7 +37,7 @@ impl TrianglePass { impl RenderGraphPass for TrianglePass { fn desc( - &self, + &mut self, graph: &mut crate::render::graph::RenderGraph, ) -> crate::render::graph::RenderGraphPassDesc { let shader = Rc::new(Shader { @@ -40,13 +45,25 @@ impl RenderGraphPass for TrianglePass { 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_buf = Rc::new(color_buf); + self.color_bg = Some(color_bg); + self.color_buf = Some(color_buf.clone()); + let mut desc = RenderGraphPassDesc::new( graph.next_id(), "TrianglePass", RenderPassType::Render, Some(RenderPipelineDescriptor { label: Some("triangle_pipeline".into()), - layouts: vec![], + layouts: vec![color_bgl], push_constant_ranges: vec![], vertex: VertexState { module: shader.clone(), @@ -76,21 +93,28 @@ impl RenderGraphPass for TrianglePass { None, ); - /* desc.add_buffer_slot(graph.next_id(), "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { - label: Some("B_ScreenSize".to_string()), - contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }))); - desc.add_buffer_slot(graph.next_id(), "camera_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor { - label: Some("B_Camera".to_string()), - contents: bytemuck::bytes_of(&CameraUniform::default()).to_vec(), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }))); */ + desc.add_buffer_slot( + graph.next_id(), + "color_buffer", + SlotAttribute::Output, + Some(SlotValue::Buffer(color_buf)), + ); desc } - fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {} + 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.write_buffer(self.color_buf.as_ref().unwrap(), 0, bytemuck::bytes_of(&color)); + } fn execute( &mut self, @@ -102,7 +126,12 @@ impl RenderGraphPass for TrianglePass { .slot_value(graph.slot_id("window_texture_view").unwrap()) .unwrap() .as_texture_view(); - let encoder = &mut context.encoder; + let encoder = context.encoder.as_mut().unwrap(); + + //context.queue.write_buffer(buffer, offset, data) + + //let color_bg = graph.bind_group(graph.slot_id("color_buffer").unwrap()); + let color_bg = self.color_bg.as_ref().unwrap(); let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("TrianglePass"), @@ -127,6 +156,7 @@ impl RenderGraphPass for TrianglePass { 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/graph/slot_desc.rs b/lyra-game/src/render/graph/slot_desc.rs index 51ac703..5c56eab 100644 --- a/lyra-game/src/render/graph/slot_desc.rs +++ b/lyra-game/src/render/graph/slot_desc.rs @@ -1,4 +1,4 @@ -use std::num::{NonZeroU32, NonZeroU8}; +use std::{mem, num::{NonZeroU32, NonZeroU8}}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct TextureViewDescriptor { @@ -162,6 +162,14 @@ pub struct BufferDescriptor { } impl BufferDescriptor { + pub fn new(usage: wgpu::BufferUsages, mapped_at_creation: bool) -> Self { + Self { + size: mem::size_of::() as _, + usage, + mapped_at_creation, + } + } + pub fn as_wgpu<'a>(&self, label: Option<&'a str>) -> wgpu::BufferDescriptor<'a> { wgpu::BufferDescriptor { label, @@ -184,6 +192,14 @@ pub struct BufferInitDescriptor { } impl BufferInitDescriptor { + pub fn new(label: Option<&str>, data: &T, usage: wgpu::BufferUsages) -> Self { + Self { + label: label.map(|s| s.to_string()), + contents: bytemuck::bytes_of(data).to_vec(), + usage, + } + } + pub fn as_wgpu<'a>(&'a self, label: Option<&'a str>) -> wgpu::util::BufferInitDescriptor<'a> { wgpu::util::BufferInitDescriptor { label, diff --git a/lyra-game/src/render/render_buffer.rs b/lyra-game/src/render/render_buffer.rs index 794e09e..ed40980 100755 --- a/lyra-game/src/render/render_buffer.rs +++ b/lyra-game/src/render/render_buffer.rs @@ -134,6 +134,15 @@ impl BufferWrapper { "BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!", ).bindgroup } + + /// Take the bind group layout, the bind group, and the buffer out of the wrapper. + pub fn parts(self) -> (Option>, Option, wgpu::Buffer) { + if let Some(pair) = self.bindgroup_pair { + (Some(pair.layout), Some(pair.bindgroup), self.inner_buf) + } else { + (None, None, self.inner_buf) + } + } } /// Struct used for building a BufferWrapper @@ -221,7 +230,7 @@ impl BufferWrapperBuilder { /// * `contents` - The contents to initialize the buffer with. /// /// If a field is missing, a panic will occur. - pub fn finish(self, device: &wgpu::Device) -> BufferWrapper { + pub fn finish_parts(self, device: &wgpu::Device) -> (wgpu::BindGroupLayout, wgpu::BindGroup, wgpu::Buffer, usize) { let buf_usage = self.buffer_usage.expect("Buffer usage was not set"); let buffer = if let Some(contents) = self.contents.as_ref() { device.create_buffer_init( @@ -293,10 +302,25 @@ impl BufferWrapperBuilder { } }; - BufferWrapper { + /* BufferWrapper { bindgroup_pair: Some(bg_pair), inner_buf: buffer, len: Some(self.count.unwrap_or_default() as usize), + } */ + + (Rc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize) + } + + pub fn finish(self, device: &wgpu::Device) -> BufferWrapper { + let (bgl, bg, buff, len) = self.finish_parts(device); + + BufferWrapper { + bindgroup_pair: Some(BindGroupPair { + layout: Rc::new(bgl), + bindgroup: bg + }), + inner_buf: buff, + len: Some(len), } } } diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index 4567bfe..290bd42 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -214,7 +214,7 @@ impl BasicRenderer { let queue = Rc::new(queue); //let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture); - let mut g = RenderGraph::new(config.clone()); + let mut g = RenderGraph::new(device.clone(), queue.clone(), config.clone()); /* debug!("Adding base pass"); g.add_pass(TrianglePass::new()); debug!("Adding depth pre-pass"); @@ -265,7 +265,7 @@ impl BasicRenderer { impl Renderer for BasicRenderer { #[instrument(skip(self, main_world))] fn prepare(&mut self, main_world: &mut World) { - self.graph.prepare(); + self.graph.prepare(main_world); } #[instrument(skip(self))] diff --git a/lyra-game/src/render/resource/render_pipeline.rs b/lyra-game/src/render/resource/render_pipeline.rs index 20629b3..e3ab1d5 100755 --- a/lyra-game/src/render/resource/render_pipeline.rs +++ b/lyra-game/src/render/resource/render_pipeline.rs @@ -63,9 +63,7 @@ impl RenderPipelineDescriptor { label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)), bind_group_layouts: &bgs, push_constant_ranges: &self.push_constant_ranges, - }); - - todo!() + }) } /* fn as_wgpu<'a>(&'a self, device: &wgpu::Device, layout: Option<&'a wgpu::PipelineLayout>) -> wgpu::RenderPipelineDescriptor<'a> { diff --git a/lyra-game/src/render/shaders/simple_phong.wgsl b/lyra-game/src/render/shaders/simple_phong.wgsl index e69de29..aa9f8a9 100644 --- a/lyra-game/src/render/shaders/simple_phong.wgsl +++ b/lyra-game/src/render/shaders/simple_phong.wgsl @@ -0,0 +1,87 @@ +// Vertex shader + +const max_light_count: u32 = 16u; + +const LIGHT_TY_DIRECTIONAL = 0u; +const LIGHT_TY_POINT = 1u; +const LIGHT_TY_SPOT = 2u; + +const ALPHA_CUTOFF = 0.1; + +struct VertexInput { + @location(0) position: vec3, + @location(1) tex_coords: vec2, + @location(2) normal: vec3, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, + @location(1) world_position: vec3, + @location(2) world_normal: vec3, +} + +struct TransformData { + transform: mat4x4, + normal_matrix: mat4x4, +} + +struct CameraUniform { + view: mat4x4, + inverse_projection: mat4x4, + view_projection: mat4x4, + projection: mat4x4, + position: vec3, + tile_debug: u32, +}; + +@group(1) @binding(0) +var u_model_transform_data: TransformData; + +@group(2) @binding(0) +var u_camera: CameraUniform; + +@vertex +fn vs_main( + model: VertexInput, +) -> VertexOutput { + var out: VertexOutput; + + out.tex_coords = model.tex_coords; + out.clip_position = u_camera.view_projection * u_model_transform_data.transform * vec4(model.position, 1.0); + + // the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36 + let normal_mat4 = u_model_transform_data.normal_matrix; + let normal_mat = mat3x3(normal_mat4[0].xyz, normal_mat4[1].xyz, normal_mat4[2].xyz); + out.world_normal = normalize(normal_mat * model.normal, ); + + var world_position: vec4 = u_model_transform_data.transform * vec4(model.position, 1.0); + out.world_position = world_position.xyz; + + return out; +} + +// Fragment shader + +struct Material { + ambient: vec4, + diffuse: vec4, + specular: vec4, + shininess: f32, +} + +@group(0) @binding(0) +var t_diffuse: texture_2d; +@group(0) @binding(1) +var s_diffuse: sampler; + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); + + if (object_color.a < ALPHA_CUTOFF) { + discard; + } + + return object_color; +} \ No newline at end of file diff --git a/lyra-game/src/render/shaders/triangle.wgsl b/lyra-game/src/render/shaders/triangle.wgsl index f5a0eae..171ddc0 100644 --- a/lyra-game/src/render/shaders/triangle.wgsl +++ b/lyra-game/src/render/shaders/triangle.wgsl @@ -2,6 +2,9 @@ struct VertexOutput { @builtin(position) clip_position: vec4, }; +@group(0) @binding(0) +var u_triangle_color: vec4; + @vertex fn vs_main( @builtin(vertex_index) in_vertex_index: u32, @@ -15,5 +18,5 @@ fn vs_main( @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return vec4(0.3, 0.2, 0.1, 1.0); + return vec4(u_triangle_color.xyz, 1.0); } \ No newline at end of file