Fix #6: Rendering Shared 3D Models #10
|
@ -4,14 +4,16 @@ pub struct RenderJob {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub shader_id: u64,
|
pub shader_id: u64,
|
||||||
pub mesh_uuid: uuid::Uuid,
|
pub mesh_uuid: uuid::Uuid,
|
||||||
|
pub transform_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderJob {
|
impl RenderJob {
|
||||||
pub fn new(entity: Entity, shader_id: u64, mesh_buffer_id: uuid::Uuid) -> Self {
|
pub fn new(entity: Entity, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform_id: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
shader_id,
|
shader_id,
|
||||||
mesh_uuid: mesh_buffer_id,
|
mesh_uuid: mesh_buffer_id,
|
||||||
|
transform_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -374,12 +374,13 @@ impl BasicRenderer {
|
||||||
|
|
||||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||||
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
||||||
if self.transform_buffers.should_expand() {
|
let _ = transform;
|
||||||
|
/* if self.transform_buffers.should_expand() {
|
||||||
self.transform_buffers.expand_buffers(&self.device);
|
self.transform_buffers.expand_buffers(&self.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
|
self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
|
||||||
mesh_uuid, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) ));
|
entity, || ( 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) {
|
||||||
|
@ -451,10 +452,12 @@ impl Renderer for BasicRenderer {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let transform_id = self.transform_buffers.push_transform(&self.queue, &self.render_limits, interop_pos.calculate_mat4(), glam::Mat3::from_quat(interop_pos.rotation));
|
||||||
|
|
||||||
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());
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
||||||
self.render_jobs.push_back(job);
|
self.render_jobs.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,10 +482,12 @@ impl Renderer for BasicRenderer {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let transform_id = self.transform_buffers.push_transform(&self.queue, &self.render_limits, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation));
|
||||||
|
|
||||||
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());
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
|
||||||
self.render_jobs.push_back(job);
|
self.render_jobs.push_back(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,9 +581,11 @@ 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.transform_indices(job.mesh_uuid).unwrap();
|
/* let transform_indices = *self.transform_buffers.transform_indices(job.entity).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 bindgroup = self.transform_buffers.bind_group(job.transform_id);
|
||||||
|
let offset = self.transform_buffers.buffer_offset(job.transform_id);//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, ]);
|
||||||
|
|
||||||
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
use uuid::Uuid;
|
|
||||||
use wgpu::Limits;
|
use wgpu::Limits;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -14,9 +13,9 @@ pub(crate) struct TransformBufferIndices {
|
||||||
/// A struct representing a single transform buffer. There can be multiple of these
|
/// A struct representing a single transform buffer. There can be multiple of these
|
||||||
pub(crate) struct TransformBufferEntry {
|
pub(crate) struct TransformBufferEntry {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub transform_buf: wgpu::Buffer,
|
|
||||||
pub normal_mat_buf: wgpu::Buffer,
|
|
||||||
pub bindgroup: wgpu::BindGroup,
|
pub bindgroup: wgpu::BindGroup,
|
||||||
|
pub transform_buffer: wgpu::Buffer,
|
||||||
|
pub normal_buffer: wgpu::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper struct for managing the Transform buffers for meshes.
|
/// A helper struct for managing the Transform buffers for meshes.
|
||||||
|
@ -31,14 +30,8 @@ pub(crate) struct TransformBufferEntry {
|
||||||
/// to insert, update, and retrieve the transforms.
|
/// 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,
|
||||||
pub just_updated: HashMap<Uuid, TransformBufferIndices>,
|
pub entries: Vec<TransformBufferEntry>,
|
||||||
pub not_updated: HashMap<Uuid, TransformBufferIndices>,
|
limits: wgpu::Limits,
|
||||||
pub dead_indices: VecDeque<TransformBufferIndices>,
|
|
||||||
pub next_indices: TransformBufferIndices,
|
|
||||||
/// (transform count, buffer, bindgroup)
|
|
||||||
pub buffer_bindgroups: Vec<TransformBufferEntry>,
|
|
||||||
/// The max amount of transforms in a buffer
|
|
||||||
pub max_transform_count: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBuffers {
|
impl TransformBuffers {
|
||||||
|
@ -72,13 +65,9 @@ impl TransformBuffers {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
max_transform_count: limits.max_uniform_buffer_binding_size as usize / (mem::size_of::<glam::Mat4>() * 2),
|
|
||||||
buffer_bindgroups: Vec::new(),
|
|
||||||
bindgroup_layout,
|
bindgroup_layout,
|
||||||
just_updated: HashMap::new(),
|
entries: vec![],
|
||||||
not_updated: HashMap::new(),
|
limits,
|
||||||
dead_indices: VecDeque::new(),
|
|
||||||
next_indices: TransformBufferIndices::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the first uniform buffer
|
// create the first uniform buffer
|
||||||
|
@ -87,95 +76,29 @@ impl TransformBuffers {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an transform in the buffer.
|
pub fn push_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, transform: glam::Mat4, normal_matrix: glam::Mat3) -> u32 {
|
||||||
///
|
let entry = self.entries.iter_mut().next().unwrap(); // TODO: use other buffers than just the first
|
||||||
/// # 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(uuid, indices);
|
|
||||||
|
|
||||||
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
||||||
|
|
||||||
let buffer = self.buffer_bindgroups.get(indices.buffer_index).unwrap();
|
// write the transform and normal to the end of the transform
|
||||||
let offset = Self::index_offset(limits, indices);
|
//let offset = (mem::size_of::<glam::Mat4>() * entry.len) as u64;
|
||||||
queue.write_buffer(&buffer.transform_buf, offset, bytemuck::bytes_of(&transform));
|
let offset = Self::get_buffer_offset(limits, entry.len as u32) as _;
|
||||||
queue.write_buffer(&buffer.normal_mat_buf, offset, bytemuck::bytes_of(&normal_matrix));
|
queue.write_buffer(&entry.transform_buffer, offset, bytemuck::bytes_of(&transform));
|
||||||
indices
|
queue.write_buffer(&entry.normal_buffer, offset, bytemuck::bytes_of(&normal_matrix));
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a new transform into the buffer, returns where in the buffer it was stored.
|
let index = entry.len;
|
||||||
pub fn insert_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, uuid: Uuid, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
|
entry.len += 1;
|
||||||
let indices = match self.dead_indices.pop_front() {
|
index as _
|
||||||
Some(indices) => indices,
|
|
||||||
None => {
|
|
||||||
let indices = &mut self.next_indices;
|
|
||||||
let this_idx = *indices;
|
|
||||||
let entry = self.buffer_bindgroups.get_mut(indices.buffer_index).unwrap();
|
|
||||||
|
|
||||||
if entry.len >= self.max_transform_count {
|
|
||||||
panic!("Transform buffer is filled and 'next_indices' was not incremented! Was a new buffer created?");
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.len += 1;
|
|
||||||
indices.transform_index += 1;
|
|
||||||
this_idx
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.just_updated.insert(uuid, indices);
|
|
||||||
self.update_transform(queue, limits, uuid, transform, normal_matrix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update or insert a transform
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
let (tran, norm) = transform_fn();
|
|
||||||
if self.contains(uuid) {
|
|
||||||
self.update_transform(queue, limits, uuid, tran, norm)
|
|
||||||
} else {
|
|
||||||
self.insert_transform(queue, limits, uuid, tran, norm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 transforms and prepare self to check next time.
|
/// 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
|
for entry in self.entries.iter_mut() {
|
||||||
let dead: VecDeque<TransformBufferIndices> = self.not_updated.values().copied().collect();
|
entry.len = 0;
|
||||||
self.dead_indices = dead;
|
|
||||||
|
|
||||||
self.not_updated = self.just_updated.clone();
|
|
||||||
self.just_updated.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the offset for the transform index in the buffer
|
|
||||||
pub fn index_offset(limits: &Limits, indices: TransformBufferIndices) -> u64 {
|
|
||||||
indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether or not the transform buffers should be expanded
|
|
||||||
pub fn should_expand(&self) -> bool {
|
|
||||||
if let Some(entry) = self.buffer_bindgroups.last() {
|
|
||||||
entry.len >= self.max_transform_count
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bind group for the index
|
|
||||||
pub fn bind_group(&self, index: TransformBufferIndices) -> Option<&wgpu::BindGroup> {
|
|
||||||
self.buffer_bindgroups.get(index.buffer_index)
|
|
||||||
.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
|
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
||||||
|
@ -186,7 +109,7 @@ impl TransformBuffers {
|
||||||
|
|
||||||
let transform_buffer = device.create_buffer(
|
let transform_buffer = device.create_buffer(
|
||||||
&wgpu::BufferDescriptor {
|
&wgpu::BufferDescriptor {
|
||||||
label: Some(&format!("B_Transform_{}", self.buffer_bindgroups.len())),
|
label: Some(&format!("B_Transform_{}", self.entries.len())),
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
size: max_buffer_sizes,
|
size: max_buffer_sizes,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
|
@ -195,7 +118,7 @@ impl TransformBuffers {
|
||||||
|
|
||||||
let normal_mat_buffer = device.create_buffer(
|
let normal_mat_buffer = device.create_buffer(
|
||||||
&wgpu::BufferDescriptor {
|
&wgpu::BufferDescriptor {
|
||||||
label: Some(&format!("B_NormalMatrix_{}", self.buffer_bindgroups.len())),
|
label: Some(&format!("B_NormalMatrix_{}", self.entries.len())),
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
size: max_buffer_sizes,
|
size: max_buffer_sizes,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
|
@ -236,15 +159,37 @@ impl TransformBuffers {
|
||||||
|
|
||||||
let entry = TransformBufferEntry {
|
let entry = TransformBufferEntry {
|
||||||
bindgroup: transform_bind_group,
|
bindgroup: transform_bind_group,
|
||||||
transform_buf: transform_buffer,
|
transform_buffer,
|
||||||
normal_mat_buf: normal_mat_buffer,
|
normal_buffer: normal_mat_buffer,
|
||||||
len: 0,
|
len: 0,
|
||||||
};
|
};
|
||||||
self.buffer_bindgroups.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the indices of the Transform
|
pub fn bind_group(&self, transform_id: u32) -> &wgpu::BindGroup {
|
||||||
pub fn transform_indices(&self, uuid: Uuid) -> Option<&TransformBufferIndices> {
|
let _ = transform_id;
|
||||||
self.just_updated.get(&uuid).or_else(|| self.not_updated.get(&uuid))
|
|
||||||
|
let entry = self.entries.iter().next().unwrap(); // TODO: use other buffers than just the first
|
||||||
|
&entry.bindgroup
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the buffer offset for a transform using wgpu limits.
|
||||||
|
///
|
||||||
|
/// If its possible to borrow immutably, use [`TransformBuffers::buffer_offset`].
|
||||||
|
fn get_buffer_offset(limits: &wgpu::Limits, transform_id: u32) -> u32 {
|
||||||
|
//let entry = self.entries.iter().next().unwrap(); // TODO: use other buffers than just the first
|
||||||
|
|
||||||
|
transform_id * limits.min_uniform_buffer_offset_alignment as u32//mem::size_of::<glam::Mat4>() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer_offset(&self, transform_id: u32) -> u32 {
|
||||||
|
Self::get_buffer_offset(&self.limits, transform_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct TransformNormalMatPair {
|
||||||
|
transform: glam::Mat4,
|
||||||
|
normal_mat: glam::Mat4,
|
||||||
|
}
|
Loading…
Reference in New Issue