From c846d52b0d6cff8df94c8b15433e927586b424c6 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 31 May 2024 20:11:35 -0400 Subject: [PATCH] render: finally get meshes and entities rendering again with the render graph! --- examples/simple_scene/src/main.rs | 2 +- lyra-game/src/render/graph/mod.rs | 12 +- lyra-game/src/render/graph/passes/base.rs | 4 +- lyra-game/src/render/graph/passes/meshes.rs | 560 ++++++++++++++++++ lyra-game/src/render/graph/passes/mod.rs | 3 + lyra-game/src/render/renderer.rs | 111 +--- lyra-game/src/render/resource/shader.rs | 10 + lyra-game/src/render/texture.rs | 5 +- .../src/render/transform_buffer_storage.rs | 7 +- 9 files changed, 606 insertions(+), 108 deletions(-) create mode 100644 lyra-game/src/render/graph/passes/meshes.rs diff --git a/examples/simple_scene/src/main.rs b/examples/simple_scene/src/main.rs index 5faefb0..64eca82 100644 --- a/examples/simple_scene/src/main.rs +++ b/examples/simple_scene/src/main.rs @@ -125,7 +125,7 @@ fn setup_scene_plugin(game: &mut Game) { world.spawn(( cube_mesh.clone(), WorldTransform::default(), - Transform::from_xyz(0.0, -5.0, -2.0), + Transform::from_xyz(0.0, 0.0, -2.0), )); { diff --git a/lyra-game/src/render/graph/mod.rs b/lyra-game/src/render/graph/mod.rs index b04f8b2..ba50510 100644 --- a/lyra-game/src/render/graph/mod.rs +++ b/lyra-game/src/render/graph/mod.rs @@ -205,7 +205,7 @@ impl RenderGraph { #[instrument(skip(self, world))] pub fn prepare(&mut self, world: &mut World) { // prepare all passes - let mut context = RenderGraphContext::new(&self.queue, None); + let mut context = RenderGraphContext::new(&self.device, &self.queue, None); for (_, pass) in &mut self.passes { let mut inner = pass.inner.borrow_mut(); inner.prepare(world, &mut context); @@ -266,8 +266,10 @@ impl RenderGraph { None }; - let queue = self.queue.clone(); // clone is required to appease the borrow checker - let mut context = RenderGraphContext::new(&queue, encoder); + // 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); // all encoders need to be submitted before a presenter node is executed. if pass_desc.pass_type == RenderPassType::Presenter { @@ -407,15 +409,17 @@ pub(crate) struct GraphBufferWrite { pub struct RenderGraphContext<'a> { /// Becomes None when the encoder is submitted pub(crate) encoder: Option, + pub(crate) device: &'a wgpu::Device, pub(crate) queue: &'a wgpu::Queue, pub(crate) buffer_writes: VecDeque, renderpass_desc: Vec>, } impl<'a> RenderGraphContext<'a> { - pub fn new(queue: &'a wgpu::Queue, encoder: Option) -> Self { + pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option) -> Self { Self { encoder, + device, queue, buffer_writes: Default::default(), renderpass_desc: vec![], diff --git a/lyra-game/src/render/graph/passes/base.rs b/lyra-game/src/render/graph/passes/base.rs index 9700f0a..59a610e 100644 --- a/lyra-game/src/render/graph/passes/base.rs +++ b/lyra-game/src/render/graph/passes/base.rs @@ -90,7 +90,7 @@ impl RenderGraphPass for BasePass { let mut desc = RenderGraphPassDesc::new( graph.next_id(), "base", - RenderPassType::Render, + RenderPassType::Node, None, vec![ ("depth_texture", depth_texture_bg, Some(depth_texture_bgl)), @@ -117,7 +117,7 @@ impl RenderGraphPass for BasePass { Some(SlotValue::Lazy), ); desc.add_texture_view_slot( - self.window_tv_id, + graph.next_id(), "depth_texture_view", SlotAttribute::Output, Some(SlotValue::TextureView(depth_texture_view)), diff --git a/lyra-game/src/render/graph/passes/meshes.rs b/lyra-game/src/render/graph/passes/meshes.rs new file mode 100644 index 0000000..1b07d68 --- /dev/null +++ b/lyra-game/src/render/graph/passes/meshes.rs @@ -0,0 +1,560 @@ +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_math::Transform; +use lyra_resource::{gltf::Mesh, ResHandle}; +use lyra_scene::{SceneGraph, WorldTransform}; +use rustc_hash::FxHashMap; +use tracing::{debug, instrument, warn}; +use uuid::Uuid; +use wgpu::util::DeviceExt; + +use crate::{ + render::{ + desc_buf_lay::DescVertexBufferLayout, graph::{ + RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, + RenderPassType, + }, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex + }, + DeltaTime, +}; + +type MeshHandle = ResHandle; +type SceneHandle = ResHandle; + +struct MeshBufferStorage { + buffer_vertex: BufferStorage, + buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>, + + //#[allow(dead_code)] + //render_texture: Option, + material: Option>, + + // The index of the transform for this entity. + // The tuple is structured like this: (transform index, index of transform inside the buffer) + //transform_index: TransformBufferIndices, +} + +#[derive(Clone, Debug, Component)] +struct InterpTransform { + last_transform: Transform, + alpha: f32, +} + +#[derive(Default)] +pub struct MeshPass { + transforms: Option, + mesh_buffers: FxHashMap, + render_jobs: VecDeque, + + texture_bind_group_layout: Option>, + material_buffer: Option, + material_buffers: FxHashMap>, + entity_meshes: FxHashMap, + + default_texture: Option, +} + +impl MeshPass { + pub fn new() -> Self { + Self::default() + } + + /// Checks if the mesh buffers in the GPU need to be updated. + #[instrument(skip(self, device, queue, mesh_han))] + fn check_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh_han: &ResHandle) { + let mesh_uuid = mesh_han.uuid(); + + if let (Some(mesh), Some(buffers)) = (mesh_han.data_ref(), self.mesh_buffers.get_mut(&mesh_uuid)) { + // check if the buffer sizes dont match. If they dont, completely remake the buffers + let vertices = mesh.position().unwrap(); + if buffers.buffer_vertex.count() != vertices.len() { + debug!("Recreating buffers for mesh {}", mesh_uuid.to_string()); + let (vert, idx) = self.create_vertex_index_buffers(device, &mesh); + + // have to re-get buffers because of borrow checker + let buffers = self.mesh_buffers.get_mut(&mesh_uuid).unwrap(); + buffers.buffer_indices = idx; + buffers.buffer_vertex = vert; + + return; + } + + // update vertices + let vertex_buffer = buffers.buffer_vertex.buffer(); + let vertices = vertices.as_slice(); + // align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT) + let (_, vertices, _) = bytemuck::pod_align_to::(vertices); + queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(vertices)); + + // update the indices if they're given + if let Some(index_buffer) = buffers.buffer_indices.as_ref() { + let aligned_indices = match mesh.indices.as_ref().unwrap() { + // U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size. + lyra_resource::gltf::MeshIndices::U16(v) => bytemuck::pod_align_to::(v).1, + lyra_resource::gltf::MeshIndices::U32(v) => bytemuck::pod_align_to::(v).1, + }; + + let index_buffer = index_buffer.1.buffer(); + queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(aligned_indices)); + } + } + } + + #[instrument(skip(self, device, mesh))] + fn create_vertex_index_buffers(&mut self, device: &wgpu::Device, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) { + let positions = mesh.position().unwrap(); + let tex_coords: Vec = mesh.tex_coords().cloned() + .unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]); + let normals = mesh.normals().unwrap(); + + assert!(positions.len() == tex_coords.len() && positions.len() == normals.len()); + + let mut vertex_inputs = vec![]; + for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) { + vertex_inputs.push(Vertex::new(*v, *t, *n)); + } + + let vertex_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(vertex_inputs.as_slice()),//vertex_combined.as_slice(), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages:: COPY_DST, + } + ); + let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len()); + + let indices = match mesh.indices.as_ref() { + Some(indices) => { + let (idx_type, len, contents) = match indices { + lyra_resource::gltf::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v)), + lyra_resource::gltf::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v)), + }; + + let index_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents, + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages:: COPY_DST, + } + ); + + let buffer_indices = BufferStorage::new(index_buffer, 0, len); + + Some((idx_type, buffer_indices)) + }, + None => { + None + } + }; + + ( vertex_buffer, indices ) + } + + #[instrument(skip(self, device, queue, mesh))] + fn create_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh: &Mesh) -> MeshBufferStorage { + let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(device, mesh); + + let material = mesh.material.as_ref() + .expect("Material resource not loaded yet"); + let material_ref = material.data_ref() + .unwrap(); + + let material = self.material_buffers.entry(material.uuid()) + .or_insert_with(|| { + debug!(uuid=material.uuid().to_string(), "Sending material to gpu"); + Rc::new(Material::from_resource(&device, &queue, self.texture_bind_group_layout.clone().unwrap(), &material_ref)) + }); + + // TODO: support material uniforms from multiple uniforms + let uni = MaterialUniform::from(&**material); + queue.write_buffer(&self.material_buffer.as_ref().unwrap(), 0, bytemuck::bytes_of(&uni)); + + MeshBufferStorage { + buffer_vertex: vertex_buffer, + buffer_indices, + material: Some(material.clone()), + } + } + + /// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed. + #[instrument(skip(self, device, queue, transform, mesh, entity))] + fn process_mesh(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool { + let _ = transform; + /* if self.transform_buffers.should_expand() { + self.transform_buffers.expand_buffers(&self.device); + } + + self.transform_buffers.update_or_insert(&self.queue, &self.render_limits, + entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) )); */ + + #[allow(clippy::map_entry)] + if !self.mesh_buffers.contains_key(&mesh_uuid) { + // create the mesh's buffers + let buffers = self.create_mesh_buffers(device, queue, mesh); + self.mesh_buffers.insert(mesh_uuid, buffers); + self.entity_meshes.insert(entity, mesh_uuid); + + true + } else { false } + } +} + +impl RenderGraphPass for MeshPass { + fn desc( + &mut self, + graph: &mut crate::render::graph::RenderGraph, + ) -> crate::render::graph::RenderGraphPassDesc { + + let device = graph.device(); + + let transforms = TransformBuffers::new(device); + let transform_bgl = transforms.bindgroup_layout.clone(); + self.transforms = Some(transforms); + + let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(&device)); + self.texture_bind_group_layout = Some(texture_bind_group_layout.clone()); + + let (material_bgl, material_bg, material_buf, _) = BufferWrapper::builder() + .label_prefix("material") + .visibility(wgpu::ShaderStages::FRAGMENT) + .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) + .contents(&[MaterialUniform::default()]) + .finish_parts(device); + let material_bgl = Rc::new(material_bgl); + let material_bg = Rc::new(material_bg); + + self.material_buffer = Some(material_buf); + + // load the default texture + let bytes = include_bytes!("../../default_texture.png"); + 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") + .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); + + // 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 light_grid_bgl = graph + .bind_group_layout(graph.bind_group_id("light_indices_grid") + .expect("Missing light grid bind group")); + + let shader = Rc::new(Shader { + label: Some("base_shader".into()), + source: include_str!("../../shaders/base.wgsl").to_string(), + }); + + let desc = RenderGraphPassDesc::new( + pass_id, + "meshes", + RenderPassType::Render, + Some(PipelineDescriptor::Render(RenderPipelineDescriptor { + label: Some("meshes".into()), + layouts: vec![ + texture_bind_group_layout.clone(), + transform_bgl, + camera_bgl.clone(), + lights_bgl.clone(), + material_bgl.clone(), + texture_bind_group_layout, + 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, + })), + vec![ + ("material", material_bg, Some(material_bgl)), + ], + ); + + desc + } + + #[instrument(skip(self, world, context))] + fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { + let device = context.device; + let queue = context.queue; + let render_limits = device.limits(); + + let last_epoch = world.current_tick(); + let mut alive_entities = HashSet::new(); + + let view = world.view_iter::<( + Entities, + &Transform, + TickOf, + Or< + (&MeshHandle, TickOf), + (&SceneHandle, TickOf) + >, + Option<&mut InterpTransform>, + Res, + )>(); + + // used to store InterpTransform components to add to entities later + let mut component_queue: Vec<(Entity, InterpTransform)> = vec![]; + + for ( + entity, + transform, + _transform_epoch, + ( + mesh_pair, + scene_pair + ), + interp_tran, + delta_time, + ) in view + { + alive_entities.insert(entity); + + let interp_transform = match interp_tran { + Some(mut interp_transform) => { + // found in https://youtu.be/YJB1QnEmlTs?t=472 + interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time); + + interp_transform.last_transform = interp_transform.last_transform.lerp(*transform, interp_transform.alpha); + interp_transform.last_transform + }, + None => { + let interp = InterpTransform { + last_transform: *transform, + alpha: 0.5, + }; + component_queue.push((entity, interp)); + + *transform + } + }; + + if let Some((mesh_han, mesh_epoch)) = mesh_pair { + if let Some(mesh) = mesh_han.data_ref() { + // if process mesh did not just create a new mesh, and the epoch + // shows that the scene has changed, verify that the mesh buffers + // dont need to be resent to the gpu. + if !self.process_mesh(device, queue, entity, interp_transform, &*mesh, mesh_han.uuid()) + && mesh_epoch == last_epoch { + self.check_mesh_buffers(device, queue, &mesh_han); + } + + let transforms = self.transforms.as_mut().unwrap(); + if transforms.needs_expand() { + debug!("Expanding transform buffers"); + transforms.expand_buffers(device); + } + + let group = TransformGroup::EntityRes(entity, mesh_han.uuid()); + let transform_id = transforms.update_or_push(device, queue, &render_limits, + group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation)); + + let material = mesh.material.as_ref().unwrap() + .data_ref().unwrap(); + let shader = material.shader_uuid.unwrap_or(0); + let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id); + self.render_jobs.push_back(job); + } + } + + if let Some((scene_han, scene_epoch)) = scene_pair { + if let Some(scene) = scene_han.data_ref() { + if scene_epoch == last_epoch { + let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not>>)>(); + lyra_scene::system_update_world_transforms(scene.world(), view).unwrap(); + } + + for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() { + if let Some(mesh) = mesh_han.data_ref() { + let mesh_interpo = interp_transform + **pos; + + // if process mesh did not just create a new mesh, and the epoch + // shows that the scene has changed, verify that the mesh buffers + // dont need to be resent to the gpu. + if !self.process_mesh(device, queue, entity, mesh_interpo, &*mesh, mesh_han.uuid()) + && scene_epoch == last_epoch { + self.check_mesh_buffers(device, queue, &mesh_han); + } + + let transforms = self.transforms.as_mut().unwrap(); + if transforms.needs_expand() { + debug!("Expanding transform buffers"); + transforms.expand_buffers(device); + } + + let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid()); + let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into()); + let transform_id = transforms.update_or_push(device, queue, &render_limits, + group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) ); + + let material = mesh.material.as_ref().unwrap() + .data_ref().unwrap(); + let shader = material.shader_uuid.unwrap_or(0); + let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id); + self.render_jobs.push_back(job); + } + } + } + } + } + + for (en, interp) in component_queue { + world.insert(en, interp); + } + + let transforms = self.transforms.as_mut().unwrap(); + transforms.send_to_gpu(queue); + } + + fn execute( + &mut self, + graph: &mut crate::render::graph::RenderGraph, + desc: &crate::render::graph::RenderGraphPassDesc, + context: &mut crate::render::graph::RenderGraphContext, + ) { + let encoder = context.encoder.as_mut().unwrap(); + + let view = graph + .slot_value(graph.slot_id("window_texture_view").unwrap()) + .unwrap() + .as_texture_view(); + + let depth_view = graph + .slot_value(graph.slot_id("depth_texture_view").unwrap()) + .unwrap() + .as_texture_view(); + + let camera_bg = graph + .bind_group(graph.bind_group_id("camera") + .expect("Missing camera bind group")); + + let lights_bg = graph + .bind_group(graph.bind_group_id("light_buffers") + .expect("Missing lights bind group")); + + let light_grid_bg = graph + .bind_group(graph.bind_group_id("light_indices_grid") + .expect("Missing light grid bind group")); + + let material_bg = graph + .bind_group(graph.bind_group_id("material") + .expect("Missing material bind group")); + + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[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, + }, + })], + // enable depth buffer + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + }); + + let pipeline = graph.pipeline(desc.id); + pass.set_pipeline(&pipeline.as_render()); + + //let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup(); + let default_texture = self.default_texture.as_ref().unwrap(); + let transforms = self.transforms.as_mut().unwrap(); + + while let Some(job) = self.render_jobs.pop_front() { + // get the mesh (containing vertices) and the buffers from storage + let buffers = self.mesh_buffers.get(&job.mesh_uuid); + if buffers.is_none() { + warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid); + continue; + } + let buffers = buffers.unwrap(); + + // Bind the optional texture + if let Some(tex) = buffers.material.as_ref() + .and_then(|m| m.diffuse_texture.as_ref()) { + pass.set_bind_group(0, tex.bind_group(), &[]); + } else { + pass.set_bind_group(0, default_texture.bind_group(), &[]); + } + + if let Some(tex) = buffers.material.as_ref() + .and_then(|m| m.specular.as_ref()) + .and_then(|s| s.texture.as_ref().or(s.color_texture.as_ref())) { + pass.set_bind_group(5, tex.bind_group(), &[]); + } else { + pass.set_bind_group(5, default_texture.bind_group(), &[]); + } + + // Get the bindgroup for job's transform and bind to it using an offset. + let bindgroup = transforms.bind_group(job.transform_id); + let offset = transforms.buffer_offset(job.transform_id); + pass.set_bind_group(1, bindgroup, &[ offset, ]); + + pass.set_bind_group(2, &camera_bg, &[]); + pass.set_bind_group(3, &lights_bg, &[]); + pass.set_bind_group(4, &material_bg, &[]); + + pass.set_bind_group(6, &light_grid_bg, &[]); + + // if this mesh uses indices, use them to draw the mesh + if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { + let indices_len = indices.count() as u32; + + pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); + pass.set_index_buffer(indices.buffer().slice(..), *idx_type); + pass.draw_indexed(0..indices_len, 0, 0..1); + } else { + let vertex_count = buffers.buffer_vertex.count(); + + pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); + pass.draw(0..vertex_count as u32, 0..1); + } + } + } +} diff --git a/lyra-game/src/render/graph/passes/mod.rs b/lyra-game/src/render/graph/passes/mod.rs index bd590fc..3aff4e2 100644 --- a/lyra-game/src/render/graph/passes/mod.rs +++ b/lyra-game/src/render/graph/passes/mod.rs @@ -10,6 +10,9 @@ pub use simple_phong::*; */ mod base; pub use base::*; +mod meshes; +pub use meshes::*; + mod light_base; pub use light_base::*; diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index 56fbc9e..c1f585f 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -1,35 +1,16 @@ -use std::cell::RefCell; use std::collections::VecDeque; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::sync::Arc; -use std::borrow::Cow; -use lyra_ecs::Component; use lyra_ecs::World; -use lyra_scene::SceneGraph; use tracing::{debug, instrument, warn}; -use wgpu::Limits; use winit::window::Window; -use crate::math::Transform; -use crate::render::graph::{BasePass, LightBasePass, LightCullComputePass, PresentPass, TrianglePass}; -use crate::render::material::MaterialUniform; -use crate::render::render_buffer::BufferWrapperBuilder; +use crate::render::graph::{BasePass, LightBasePass, LightCullComputePass, MeshPass, PresentPass}; -use super::camera::CameraUniform; use super::graph::RenderGraph; -use super::light::LightUniformBuffers; -use super::material::Material; -use super::render_buffer::BufferWrapper; -use super::texture::RenderTexture; -use super::transform_buffer_storage::TransformBuffers; -use super::{resource::RenderPipeline, render_buffer::BufferStorage, render_job::RenderJob}; - -use lyra_resource::{gltf::Mesh, ResHandle}; - -type MeshHandle = ResHandle; -type SceneHandle = ResHandle; +use super::{resource::RenderPipeline, render_job::RenderJob}; #[derive(Clone, Copy, Debug)] pub struct ScreenSize(glam::UVec2); @@ -63,25 +44,6 @@ pub trait RenderPass { fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize); } -struct MeshBufferStorage { - buffer_vertex: BufferStorage, - buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>, - - //#[allow(dead_code)] - //render_texture: Option, - material: Option>, - - // The index of the transform for this entity. - // The tuple is structured like this: (transform index, index of transform inside the buffer) - //transform_index: TransformBufferIndices, -} - -#[derive(Clone, Debug, Component)] -pub struct InterpTransform { - last_transform: Transform, - alpha: f32, -} - pub struct BasicRenderer { pub device: Rc, // device does not need to be mutable, no need for refcell pub queue: Rc, @@ -93,24 +55,6 @@ pub struct BasicRenderer { pub render_pipelines: rustc_hash::FxHashMap>, pub render_jobs: VecDeque, - //mesh_buffers: rustc_hash::FxHashMap, // TODO: clean up left over buffers from deleted entities/components - //material_buffers: rustc_hash::FxHashMap>, - //entity_meshes: rustc_hash::FxHashMap, - - //transform_buffers: TransformBuffers, - - render_limits: Limits, - - //inuse_camera: RenderCamera, - //camera_buffer: BufferWrapper, - - //bgl_texture: Rc, - //default_texture: RenderTexture, - //depth_buffer_texture: RenderTexture, - //material_buffer: BufferWrapper, - //light_buffers: LightUniformBuffers, - //light_cull_compute: LightCullCompute, - graph: RenderGraph, } @@ -156,11 +100,8 @@ impl BasicRenderer { None, ).await.unwrap(); - let render_limits = device.limits(); let surface_caps = surface.get_capabilities(&adapter); - let present_mode = surface_caps.present_modes[0]; - debug!("present mode: {:?}", present_mode); let surface_format = surface_caps.formats.iter() @@ -172,43 +113,12 @@ impl BasicRenderer { format: surface_format, width: size.width, height: size.height, - present_mode: wgpu::PresentMode::Immediate, + present_mode: wgpu::PresentMode::default(), //wgpu::PresentMode::Mailbox, // "Fast Vsync" alpha_mode: surface_caps.alpha_modes[0], view_formats: vec![], }; surface.configure(&device, &config); - let bgl_texture = Rc::new(RenderTexture::create_layout(&device)); - - let shader_src = include_str!("shaders/base.wgsl"); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)), - }); - - let transform_buffers = TransformBuffers::new(&device); - let camera_buffer = BufferWrapper::builder() - .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) - .contents(&[CameraUniform::default()]) - .label_prefix("Camera") - .visibility(wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE) - .buffer_dynamic_offset(false) - .finish(&device); - - let mut depth_texture = RenderTexture::create_depth_texture(&device, &config, "Tex_Depth"); - - // load the default texture - let bytes = include_bytes!("default_texture.png"); - let default_texture = RenderTexture::from_bytes(&device, &queue, bgl_texture.clone(), bytes, "default_texture").unwrap(); - - let light_uniform_buffers = LightUniformBuffers::new(&device); - - let mat_buffer = BufferWrapperBuilder::new() - .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) - .visibility(wgpu::ShaderStages::FRAGMENT) - .contents(&[MaterialUniform::default()]) - .finish(&device); - let device = Rc::new(device); let queue = Rc::new(queue); @@ -220,15 +130,23 @@ impl BasicRenderer { g.add_pass(LightBasePass::new()); debug!("Adding light cull compute pass"); g.add_pass(LightCullComputePass::new(size)); - debug!("Adding triangle pass"); - g.add_pass(TrianglePass::new()); + //debug!("Adding triangle pass"); + //g.add_pass(TrianglePass::new()); + + debug!("Adding mesh pass"); + g.add_pass(MeshPass::new()); + debug!("Adding present pass"); g.add_pass(PresentPass::new("main_render_target")); g.add_edge("base", "light_base"); g.add_edge("light_base", "light_cull_compute"); - g.add_edge("base", "triangle"); + g.add_edge("base", "meshes"); + + // 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.setup(&device); @@ -246,7 +164,6 @@ impl BasicRenderer { render_pipelines: Default::default(), render_jobs: Default::default(), - render_limits, graph: g, } } diff --git a/lyra-game/src/render/resource/shader.rs b/lyra-game/src/render/resource/shader.rs index fa1c9b4..71c9669 100644 --- a/lyra-game/src/render/resource/shader.rs +++ b/lyra-game/src/render/resource/shader.rs @@ -7,6 +7,16 @@ pub struct VertexBufferLayout { pub attributes: Vec, } +impl<'a> From> for VertexBufferLayout { + fn from(value: wgpu::VertexBufferLayout) -> Self { + Self { + array_stride: value.array_stride, + step_mode: value.step_mode, + attributes: value.attributes.to_vec(), + } + } +} + /// Describes the vertex stage in a render pipeline. #[derive(Debug, Clone)] pub struct VertexState { diff --git a/lyra-game/src/render/texture.rs b/lyra-game/src/render/texture.rs index 64e2b59..c3927dd 100755 --- a/lyra-game/src/render/texture.rs +++ b/lyra-game/src/render/texture.rs @@ -273,7 +273,10 @@ impl RenderTexture { }; let texture = device.create_texture(&desc); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let view = texture.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Depth32Float), + ..Default::default() + }); let sampler = device.create_sampler( &wgpu::SamplerDescriptor { // 4. address_mode_u: wgpu::AddressMode::ClampToEdge, diff --git a/lyra-game/src/render/transform_buffer_storage.rs b/lyra-game/src/render/transform_buffer_storage.rs index 9fae477..34796c4 100644 --- a/lyra-game/src/render/transform_buffer_storage.rs +++ b/lyra-game/src/render/transform_buffer_storage.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64}; +use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, rc::Rc}; use lyra_ecs::Entity; use tracing::instrument; @@ -162,7 +162,7 @@ impl CachedValMap, //groups: CachedValMap, //groups: SlotMap, entries: Vec, @@ -192,7 +192,7 @@ impl TransformBuffers { }); let mut s = Self { - bindgroup_layout, + bindgroup_layout: Rc::new(bindgroup_layout), entries: Default::default(), max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::()), limits, @@ -209,6 +209,7 @@ impl TransformBuffers { /// /// This uses [`wgpu::Queue::write_buffer`], so the write is not immediately submitted, /// and instead enqueued internally to happen at the start of the next submit() call. + #[instrument(skip(self, queue))] pub fn send_to_gpu(&mut self, queue: &wgpu::Queue) { self.next_index = 0;