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
This commit is contained in:
parent
fba925512b
commit
e36307eef7
|
@ -5,17 +5,15 @@ use crate::math::Transform;
|
||||||
pub struct RenderJob {
|
pub struct RenderJob {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub shader_id: u64,
|
pub shader_id: u64,
|
||||||
pub mesh_buffer_id: uuid::Uuid,
|
pub mesh_uuid: uuid::Uuid,
|
||||||
pub transform: Transform,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderJob {
|
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 {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
shader_id,
|
shader_id,
|
||||||
mesh_buffer_id,
|
mesh_uuid: mesh_buffer_id,
|
||||||
transform,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -386,7 +386,7 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
|
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)]
|
#[allow(clippy::map_entry)]
|
||||||
if !self.mesh_buffers.contains_key(&mesh_uuid) {
|
if !self.mesh_buffers.contains_key(&mesh_uuid) {
|
||||||
|
@ -461,7 +461,7 @@ impl Renderer for BasicRenderer {
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
let shader = material.shader_uuid.unwrap_or(0);
|
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);
|
self.render_jobs.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,35 +470,26 @@ impl Renderer for BasicRenderer {
|
||||||
if let Some((scene_han, scene_epoch)) = scene_view.get() {
|
if let Some((scene_han, scene_epoch)) = scene_view.get() {
|
||||||
// TODO: Rendering scenes
|
// 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() {
|
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();
|
let meshes = scene.collect_world_meshes();
|
||||||
|
|
||||||
for (mesh_han, mesh_pos) in meshes.into_iter() {
|
for (mesh_han, mesh_pos) in meshes.into_iter() {
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
let mesh_interp = interop_pos + mesh_pos;
|
let mesh_interpo = interpo_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);
|
|
||||||
|
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
// dont need to be resent to the gpu.
|
// 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 {
|
&& scene_epoch == last_epoch {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
debug!("updating mesh buffers");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
let shader = material.shader_uuid.unwrap_or(0);
|
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);
|
self.render_jobs.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,7 +564,7 @@ impl Renderer for BasicRenderer {
|
||||||
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
|
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
|
||||||
|
|
||||||
// get the mesh (containing vertices) and the buffers from storage
|
// 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
|
// Bind the optional texture
|
||||||
if let Some(tex) = buffers.material.as_ref()
|
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.
|
// 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 bindgroup = self.transform_buffers.bind_group(transform_indices).unwrap();
|
||||||
let offset = TransformBuffers::index_offset(&self.render_limits, transform_indices) as u32;
|
let offset = TransformBuffers::index_offset(&self.render_limits, transform_indices) as u32;
|
||||||
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
||||||
|
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::Entity;
|
||||||
|
use uuid::Uuid;
|
||||||
use wgpu::Limits;
|
use wgpu::Limits;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -19,11 +20,20 @@ pub(crate) struct TransformBufferEntry {
|
||||||
pub bindgroup: wgpu::BindGroup,
|
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(crate) struct TransformBuffers {
|
||||||
pub bindgroup_layout: wgpu::BindGroupLayout,
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
/// A vector storing the EntityId and
|
pub just_updated: HashMap<Uuid, TransformBufferIndices>,
|
||||||
pub just_updated: HashMap<Entity, TransformBufferIndices>,
|
pub not_updated: HashMap<Uuid, TransformBufferIndices>,
|
||||||
pub not_updated: HashMap<Entity, TransformBufferIndices>,
|
|
||||||
pub dead_indices: VecDeque<TransformBufferIndices>,
|
pub dead_indices: VecDeque<TransformBufferIndices>,
|
||||||
pub next_indices: TransformBufferIndices,
|
pub next_indices: TransformBufferIndices,
|
||||||
/// (transform count, buffer, bindgroup)
|
/// (transform count, buffer, bindgroup)
|
||||||
|
@ -78,12 +88,15 @@ impl TransformBuffers {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
|
/// Update an transform in the buffer.
|
||||||
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)
|
/// # Panics
|
||||||
.or_else(|| self.just_updated.remove(&entity))
|
/// 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");
|
.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);
|
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
||||||
|
|
||||||
|
@ -94,8 +107,8 @@ impl TransformBuffers {
|
||||||
indices
|
indices
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new entity into the buffer, returns where it was stored.
|
/// Insert a new transform into the buffer, returns where in the buffer it was stored.
|
||||||
pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
|
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() {
|
let indices = match self.dead_indices.pop_front() {
|
||||||
Some(indices) => indices,
|
Some(indices) => indices,
|
||||||
None => {
|
None => {
|
||||||
|
@ -113,28 +126,28 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.just_updated.insert(entity, indices);
|
self.just_updated.insert(uuid, indices);
|
||||||
self.update_entity(queue, limits, entity, transform, normal_matrix)
|
self.update_transform(queue, limits, uuid, transform, normal_matrix)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update or insert an entities transform
|
/// Update or insert a transform
|
||||||
pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform_fn: TFn) -> TransformBufferIndices
|
pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, uuid: Uuid, transform_fn: TFn) -> TransformBufferIndices
|
||||||
where TFn: Fn() -> (glam::Mat4, glam::Mat3)
|
where TFn: Fn() -> (glam::Mat4, glam::Mat3)
|
||||||
{
|
{
|
||||||
let (tran, norm) = transform_fn();
|
let (tran, norm) = transform_fn();
|
||||||
if self.contains(entity) {
|
if self.contains(uuid) {
|
||||||
self.update_entity(queue, limits, entity, tran, norm)
|
self.update_transform(queue, limits, uuid, tran, norm)
|
||||||
} else {
|
} 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).
|
/// Returns true if the transform related to the `uuid` is stored (does not mean its up-to-date).
|
||||||
pub fn contains(&self, entity: Entity) -> bool {
|
pub fn contains(&self, uuid: Uuid) -> bool {
|
||||||
self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity)
|
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) {
|
pub fn tick(&mut self) {
|
||||||
// take the dead entities, these were ones that were not updated this tick
|
// take the dead entities, these were ones that were not updated this tick
|
||||||
let dead: VecDeque<TransformBufferIndices> = self.not_updated.values().copied().collect();
|
let dead: VecDeque<TransformBufferIndices> = self.not_updated.values().copied().collect();
|
||||||
|
@ -164,7 +177,10 @@ impl TransformBuffers {
|
||||||
.map(|entry| &entry.bindgroup)
|
.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) {
|
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
|
||||||
let limits = device.limits();
|
let limits = device.limits();
|
||||||
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
||||||
|
@ -228,7 +244,8 @@ impl TransformBuffers {
|
||||||
self.buffer_bindgroups.push(entry);
|
self.buffer_bindgroups.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity_indices(&self, entity: Entity) -> Option<&TransformBufferIndices> {
|
/// Returns the indices of the Transform
|
||||||
self.just_updated.get(&entity).or_else(|| self.not_updated.get(&entity))
|
pub fn transform_indices(&self, uuid: Uuid) -> Option<&TransformBufferIndices> {
|
||||||
|
self.just_updated.get(&uuid).or_else(|| self.not_updated.get(&uuid))
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue