From 3ca8e2f4c63e111834dedb352125be2d0e7c55cc Mon Sep 17 00:00:00 2001 From: marceline-cramer Date: Tue, 1 Feb 2022 20:02:17 -0700 Subject: [PATCH] Refactor mesh commands (again) --- src/commands.rs | 132 ++++++++++++++++++++++++++++++++++++------------ src/main.rs | 27 +++++++++- 2 files changed, 125 insertions(+), 34 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index c378805..ed79cee 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,6 @@ -use super::{MeshHandle, MeshPool, TextureHandle, TexturePool}; +use super::{MeshHandle, TextureHandle}; use std::collections::HashMap; +use std::ops::Range; #[derive(Copy, Clone, PartialEq)] pub struct MeshInstance { @@ -8,20 +9,86 @@ pub struct MeshInstance { pub transform: glam::Mat4, } -pub struct MeshCommands { - transforms: Vec<[f32; 16]>, - draws: Vec<(usize, usize, std::ops::Range)>, +#[derive(Clone)] +struct DrawState { + group_id: usize, + texture_id: usize, + sub_id: usize, + instance_range: Range, } -impl MeshCommands { +pub enum Command { + BindMeshGroup { + group_id: usize, + }, + BindTexture { + texture_id: usize, + }, + Draw { + sub_id: usize, + instance_range: Range, + }, +} + +pub struct CommandIterator<'a> { + command_set: &'a CommandSet, + command_idx: usize, // TODO use iterator instead lol + last_group_id: usize, + last_texture_id: usize, +} + +impl<'a> CommandIterator<'a> { + pub fn new(command_set: &'a CommandSet) -> Self { + Self { + command_set, + command_idx: 0, + last_group_id: usize::MAX, + last_texture_id: usize::MAX, + } + } +} + +impl<'a> Iterator for CommandIterator<'a> { + type Item = Command; + + fn next(&mut self) -> Option { + let DrawState { + group_id, + sub_id, + texture_id, + instance_range, + } = self.command_set.draws.get(self.command_idx)?.clone(); + + if group_id != self.last_group_id { + self.last_group_id = group_id; + Some(Command::BindMeshGroup { group_id }) + } else if texture_id != self.last_texture_id { + self.last_texture_id = texture_id; + Some(Command::BindTexture { texture_id }) + } else { + self.command_idx += 1; + Some(Command::Draw { + sub_id, + instance_range, + }) + } + } +} + +pub struct CommandSet { + transforms: Vec<[f32; 16]>, + draws: Vec, +} + +impl CommandSet { pub fn build(instances: &Vec) -> Self { let mut sorted_meshes = HashMap::<(usize, usize), Vec>::new(); for instance in instances.iter() { let group_id = instance.mesh.group_id; let texture_id = instance.albedo.id; let key = (group_id, texture_id); - if let Some(by_group) = sorted_meshes.get_mut(&key) { - by_group.push(*instance); + if let Some(by_state) = sorted_meshes.get_mut(&key) { + by_state.push(*instance); } else { sorted_meshes.insert(key, vec![*instance]); } @@ -30,14 +97,30 @@ impl MeshCommands { let mut transforms = Vec::new(); let mut draws = Vec::new(); - // this code assumes MeshHandle only uses group_id (which it does for now) - // TODO bucket by sub_id too before MeshHandle supports it - for ((group_id, texture_id), meshes) in sorted_meshes.iter() { - let start_idx = transforms.len() as u32; - let as_arrays = meshes.iter().map(|i| i.transform.to_cols_array()); - transforms.extend(as_arrays); - let end_idx = transforms.len() as u32; - draws.push((*group_id, *texture_id, start_idx..end_idx)); + for ((group_id, texture_id), mut meshes) in sorted_meshes.drain() { + let mut sorted_subs = HashMap::>::new(); + for mesh in meshes.drain(..) { + let sub_id = mesh.mesh.sub_id; + if let Some(by_sub) = sorted_subs.get_mut(&sub_id) { + by_sub.push(mesh); + } else { + sorted_subs.insert(sub_id, vec![mesh]); + } + } + + for (sub_id, meshes) in sorted_subs.drain() { + let start_idx = transforms.len() as u32; + let as_arrays = meshes.iter().map(|i| i.transform.to_cols_array()); + transforms.extend(as_arrays); + let end_idx = transforms.len() as u32; + let instance_range = start_idx..end_idx; + draws.push(DrawState { + group_id, + texture_id, + sub_id, + instance_range, + }); + } } Self { transforms, draws } @@ -47,22 +130,7 @@ impl MeshCommands { bytemuck::cast_slice(&self.transforms) } - pub fn dispatch<'a>( - &self, - rp: &mut wgpu::RenderPass<'a>, - mesh_pool: &'a MeshPool, - texture_pool: &'a TexturePool, - ) { - // TODO one group per mesh, still... - // TODO this could be implemented without accessing private members - for (group_id, texture_id, meshes_range) in self.draws.iter() { - let group = mesh_pool.groups.get(*group_id).unwrap(); - let texture = texture_pool.textures.get(*texture_id).unwrap(); - rp.set_vertex_buffer(0, group.vertices.slice(..)); - rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); - rp.set_bind_group(2, &texture.bind_group, &[]); - let indices = 0..(group.index_capacity as u32); - rp.draw_indexed(indices, 0, meshes_range.to_owned()); - } + pub fn iter(&self) -> CommandIterator { + CommandIterator::new(self) } } diff --git a/src/main.rs b/src/main.rs index 932bb17..e6bee61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -262,7 +262,7 @@ impl Renderer { bytemuck::cast_slice(&[self.camera_uniform]), ); - let mesh_commands = commands::MeshCommands::build(meshes); + let mesh_commands = CommandSet::build(meshes); // TODO persistent staging buffer (write_buffer creates a new one per call) self.queue @@ -307,7 +307,30 @@ impl Renderer { rp.set_pipeline(&self.render_pipeline); rp.set_bind_group(0, &self.camera_bind_group, &[]); rp.set_bind_group(1, &self.meshes_bind_group, &[]); - mesh_commands.dispatch(&mut rp, &self.mesh_pool, &self.texture_pool); + + let mut group: Option<&MeshGroup> = None; + for cmd in mesh_commands.iter() { + match cmd { + Command::BindMeshGroup { group_id } => { + group = self.mesh_pool.groups.get(group_id); + let group = group.unwrap(); + rp.set_vertex_buffer(0, group.vertices.slice(..)); + rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); + } + Command::BindTexture { texture_id } => { + let texture = self.texture_pool.textures.get(texture_id).unwrap(); + rp.set_bind_group(2, &texture.bind_group, &[]); + } + Command::Draw { + sub_id: _, + instance_range, + } => { + // TODO use sub_id in mesh draw + let indices = 0..(group.unwrap().index_capacity as u32); + rp.draw_indexed(indices, 0, instance_range); + } + } + } } self.queue.submit(std::iter::once(encoder.finish()));