From 8d1961bb08d2306364ac01943ff642b60ada451a Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 8 Mar 2024 00:41:27 -0500 Subject: [PATCH] render: fix the TransformBuffers that could only store a single Transform for an entity This caused Scenes to be rendered poorly since all meshes would use the Transform of the last processed mesh --- lyra-game/src/render/render_job.rs | 8 +-- lyra-game/src/render/renderer.rs | 25 +++---- .../src/render/transform_buffer_storage.rs | 65 ++++++++++++------- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/lyra-game/src/render/render_job.rs b/lyra-game/src/render/render_job.rs index 9c7ec1c..945149b 100755 --- a/lyra-game/src/render/render_job.rs +++ b/lyra-game/src/render/render_job.rs @@ -5,17 +5,15 @@ use crate::math::Transform; pub struct RenderJob { pub entity: Entity, pub shader_id: u64, - pub mesh_buffer_id: uuid::Uuid, - pub transform: Transform, + pub mesh_uuid: uuid::Uuid, } impl RenderJob { - pub fn new(entity: Entity, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform) -> Self { + pub fn new(entity: Entity, shader_id: u64, mesh_buffer_id: uuid::Uuid) -> Self { Self { entity, shader_id, - mesh_buffer_id, - transform, + mesh_uuid: mesh_buffer_id, } } } \ No newline at end of file diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index e8cba97..fd6f9bb 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -386,7 +386,7 @@ impl BasicRenderer { } self.transform_buffers.update_or_insert(&self.queue, &self.render_limits, - entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) )); + mesh_uuid, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) )); #[allow(clippy::map_entry)] if !self.mesh_buffers.contains_key(&mesh_uuid) { @@ -461,7 +461,7 @@ impl Renderer for BasicRenderer { 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(), interop_pos); + let job = RenderJob::new(entity, shader, mesh_han.uuid()); self.render_jobs.push_back(job); } } @@ -470,35 +470,26 @@ impl Renderer for BasicRenderer { if let Some((scene_han, scene_epoch)) = scene_view.get() { // TODO: Rendering scenes - let scene_scope = debug_span!("scene", uuid = scene_han.uuid().to_string()); - let _enter = scene_scope.enter(); - if let Some(scene) = scene_han.data_ref() { - let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); + let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); let meshes = scene.collect_world_meshes(); for (mesh_han, mesh_pos) in meshes.into_iter() { if let Some(mesh) = mesh_han.data_ref() { - let mesh_interp = interop_pos + mesh_pos; - - let mesh_scope = debug_span!("mesh", uuid = mesh_han.uuid().to_string()); - let _enter = mesh_scope.enter(); - - debug!("mesh at {:?}", mesh_interp.translation); + let mesh_interpo = interpo_pos + mesh_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(entity, mesh_interp, &*mesh, mesh_han.uuid()) + if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid()) && scene_epoch == last_epoch { self.check_mesh_buffers(entity, &mesh_han); - debug!("updating mesh buffers"); } 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(), interop_pos); + let job = RenderJob::new(entity, shader, mesh_han.uuid()); self.render_jobs.push_back(job); } } @@ -573,7 +564,7 @@ impl Renderer for BasicRenderer { render_pass.set_pipeline(pipeline.get_wgpu_pipeline()); // get the mesh (containing vertices) and the buffers from storage - let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap(); + let buffers = self.mesh_buffers.get(&job.mesh_uuid).unwrap(); // Bind the optional texture if let Some(tex) = buffers.material.as_ref() @@ -592,7 +583,7 @@ impl Renderer for BasicRenderer { } // Get the bindgroup for job's transform and bind to it using an offset. - let transform_indices = *self.transform_buffers.entity_indices(job.entity).unwrap(); + let transform_indices = *self.transform_buffers.transform_indices(job.mesh_uuid).unwrap(); let bindgroup = self.transform_buffers.bind_group(transform_indices).unwrap(); let offset = TransformBuffers::index_offset(&self.render_limits, transform_indices) as u32; render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]); diff --git a/lyra-game/src/render/transform_buffer_storage.rs b/lyra-game/src/render/transform_buffer_storage.rs index 36e9a1d..eadc547 100644 --- a/lyra-game/src/render/transform_buffer_storage.rs +++ b/lyra-game/src/render/transform_buffer_storage.rs @@ -1,6 +1,7 @@ use std::{collections::{VecDeque, HashMap}, num::NonZeroU64}; use lyra_ecs::Entity; +use uuid::Uuid; use wgpu::Limits; use std::mem; @@ -19,11 +20,20 @@ pub(crate) struct TransformBufferEntry { pub bindgroup: wgpu::BindGroup, } +/// A helper struct for managing the Transform buffers for meshes. +/// +/// This struct manages a "chain" of uniform buffers that store Transform for meshes. When +/// first created it only has a single "chain-link" with a buffer that is the maximum length +/// the GPU supports. When the first buffer fills up, a new one should be created which will also +/// be the maximum length the GPU supports. When the new buffer fills up, a new one will be +/// created once again, and so on. +/// +/// `Uuid`'s are used to represent entries (usually Meshes) in the buffer. The Uuid's can be used +/// to insert, update, and retrieve the transforms. pub(crate) struct TransformBuffers { pub bindgroup_layout: wgpu::BindGroupLayout, - /// A vector storing the EntityId and - pub just_updated: HashMap, - pub not_updated: HashMap, + pub just_updated: HashMap, + pub not_updated: HashMap, pub dead_indices: VecDeque, pub next_indices: TransformBufferIndices, /// (transform count, buffer, bindgroup) @@ -78,12 +88,15 @@ impl TransformBuffers { s } - /// Update an entity's buffer with the new transform. Will panic if the entity isn't stored - pub fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices { - let indices = self.not_updated.remove(&entity) - .or_else(|| self.just_updated.remove(&entity)) + /// Update an transform in the buffer. + /// + /// # Panics + /// Panics if the entity isn't stored, you can check if it is before with [`TransformBuffers:contains`]. + pub fn update_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, uuid: Uuid, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices { + let indices = self.not_updated.remove(&uuid) + .or_else(|| self.just_updated.remove(&uuid)) .expect("Use 'insert_entity' for new entities"); - self.just_updated.insert(entity, indices); + self.just_updated.insert(uuid, indices); let normal_matrix = glam::Mat4::from_mat3(normal_matrix); @@ -94,8 +107,8 @@ impl TransformBuffers { indices } - /// Insert a new entity into the buffer, returns where it was stored. - pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices { + /// Insert a new transform into the buffer, returns where in the buffer it was stored. + pub fn insert_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, uuid: Uuid, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices { let indices = match self.dead_indices.pop_front() { Some(indices) => indices, None => { @@ -113,28 +126,28 @@ impl TransformBuffers { } }; - self.just_updated.insert(entity, indices); - self.update_entity(queue, limits, entity, transform, normal_matrix) + self.just_updated.insert(uuid, indices); + self.update_transform(queue, limits, uuid, transform, normal_matrix) } - /// Update or insert an entities transform - pub fn update_or_insert(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform_fn: TFn) -> TransformBufferIndices + /// Update or insert a transform + pub fn update_or_insert(&mut self, queue: &wgpu::Queue, limits: &Limits, uuid: Uuid, transform_fn: TFn) -> TransformBufferIndices where TFn: Fn() -> (glam::Mat4, glam::Mat3) { let (tran, norm) = transform_fn(); - if self.contains(entity) { - self.update_entity(queue, limits, entity, tran, norm) + if self.contains(uuid) { + self.update_transform(queue, limits, uuid, tran, norm) } else { - self.insert_entity(queue, limits, entity, tran, norm) + self.insert_transform(queue, limits, uuid, tran, norm) } } - /// Returns true if the entity's transform is stored (does not mean its up-to-date). - pub fn contains(&self, entity: Entity) -> bool { - self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity) + /// Returns true if the transform related to the `uuid` is stored (does not mean its up-to-date). + pub fn contains(&self, uuid: Uuid) -> bool { + self.not_updated.contains_key(&uuid) || self.just_updated.contains_key(&uuid) } - /// Collect the dead entities, mark entities and not updated for next updates. + /// Collect the dead transforms and prepare self to check next time. pub fn tick(&mut self) { // take the dead entities, these were ones that were not updated this tick let dead: VecDeque = self.not_updated.values().copied().collect(); @@ -164,7 +177,10 @@ impl TransformBuffers { .map(|entry| &entry.bindgroup) } - /// Expand the Transform buffers by adding another uniform buffer binding + /// Expand the Transform buffers by adding another uniform buffer binding. + /// + /// This object has a chain of uniform buffers, when the buffers are expanded, a new + /// "chain-link" is created. pub fn expand_buffers(&mut self, device: &wgpu::Device) { let limits = device.limits(); let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2; @@ -228,7 +244,8 @@ impl TransformBuffers { self.buffer_bindgroups.push(entry); } - pub fn entity_indices(&self, entity: Entity) -> Option<&TransformBufferIndices> { - self.just_updated.get(&entity).or_else(|| self.not_updated.get(&entity)) + /// Returns the indices of the Transform + pub fn transform_indices(&self, uuid: Uuid) -> Option<&TransformBufferIndices> { + self.just_updated.get(&uuid).or_else(|| self.not_updated.get(&uuid)) } } \ No newline at end of file