diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..e9f563c --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,55 @@ +use super::{MeshInstance, MeshPool}; +use std::collections::HashMap; + +pub struct MeshCommands { + transforms: Vec<[f32; 16]>, + transform_ranges: Vec<(usize, std::ops::Range)>, +} + +impl MeshCommands { + pub fn build(instances: &Vec) -> Self { + let mut sorted_meshes = HashMap::>::new(); + for mesh in instances.iter() { + let group_id = mesh.handle.group_id; + if let Some(by_group) = sorted_meshes.get_mut(&group_id) { + by_group.push(*mesh); + } else { + sorted_meshes.insert(group_id, vec![*mesh]); + } + } + + let mut transforms = Vec::new(); + let mut transform_ranges = 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, 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; + transform_ranges.push((*group_id, start_idx..end_idx)); + } + + Self { + transforms, + transform_ranges, + } + } + + pub fn get_storage(&self) -> &[u8] { + bytemuck::cast_slice(&self.transforms) + } + + pub fn dispatch<'a>(&self, rp: &mut wgpu::RenderPass<'a>, mesh_pool: &'a MeshPool) { + // TODO one group per mesh, still... + // TODO this could be implemented without accessing private members + for (group_id, meshes_range) in self.transform_ranges.iter() { + let group = mesh_pool.groups.get(*group_id).unwrap(); + rp.set_vertex_buffer(0, group.vertices.slice(..)); + rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); + let indices = 0..(group.index_capacity as u32); + rp.draw_indexed(indices, 0, meshes_range.to_owned()); + } + } +} diff --git a/src/main.rs b/src/main.rs index d4d4982..703e02e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use wgpu::util::DeviceExt; use winit::{ @@ -8,6 +7,7 @@ use winit::{ }; mod camera; +mod commands; mod mesh; use camera::*; @@ -102,8 +102,7 @@ impl Renderer { let meshes_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Meshes Buffer"), size: 65536, // TODO resizable meshes buffer/gpu vectors - usage: wgpu::BufferUsages::STORAGE - | wgpu::BufferUsages::COPY_DST, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -203,7 +202,7 @@ impl Renderer { pub fn render( &mut self, camera: &impl Camera, - meshes: &MeshCommands, + meshes: &Vec, ) -> Result<(), wgpu::SurfaceError> { self.camera_uniform.update(camera); self.queue.write_buffer( @@ -212,36 +211,11 @@ impl Renderer { bytemuck::cast_slice(&[self.camera_uniform]), ); - let mut sorted_meshes = HashMap::>::new(); - for mesh in meshes.iter() { - let group_id = mesh.handle.group_id; - if let Some(by_group) = sorted_meshes.get_mut(&group_id) { - by_group.push(*mesh); - } else { - let new_list = vec![*mesh]; - sorted_meshes.insert(group_id, new_list); - } - } - - let mut mesh_transforms = Vec::<[f32; 16]>::new(); - let mut transform_ranges = Vec::<(usize, std::ops::Range)>::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, instances) in sorted_meshes.iter() { - let start_idx = mesh_transforms.len() as u32; - let transforms = instances.iter().map(|i| i.transform.to_cols_array()); - mesh_transforms.extend(transforms); - let end_idx = mesh_transforms.len() as u32; - transform_ranges.push((*group_id, start_idx..end_idx)); - } + let mesh_commands = commands::MeshCommands::build(meshes); // TODO persistent staging buffer (write_buffer creates a new one per call) - self.queue.write_buffer( - &self.meshes_buffer, - 0, - bytemuck::cast_slice(&mesh_transforms), - ); + self.queue + .write_buffer(&self.meshes_buffer, 0, mesh_commands.get_storage()); let output = self.surface.get_current_texture()?; let view = output @@ -254,7 +228,7 @@ impl Renderer { }); { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut rp = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[wgpu::RenderPassColorAttachment { view: &view, @@ -272,19 +246,10 @@ impl Renderer { depth_stencil_attachment: None, }); - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(0, &self.camera_bind_group, &[]); - render_pass.set_bind_group(1, &self.meshes_bind_group, &[]); - - // TODO one group per mesh, still... - // TODO this could be implemented without accessing private members - for (group_id, meshes_range) in transform_ranges.iter() { - let group = self.mesh_pool.groups.get(*group_id).unwrap(); - render_pass.set_vertex_buffer(0, group.vertices.slice(..)); - render_pass.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); - let indices = 0..(group.index_capacity as u32); - render_pass.draw_indexed(indices, 0, meshes_range.to_owned()); - } + 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.queue.submit(std::iter::once(encoder.finish())); @@ -294,7 +259,7 @@ impl Renderer { } } -struct MeshGroup { +pub struct MeshGroup { vertices: wgpu::Buffer, vertex_capacity: usize, indices: wgpu::Buffer, @@ -302,7 +267,7 @@ struct MeshGroup { } impl MeshGroup { - pub fn new(device: &wgpu::Device, data: &MeshData) -> Self { + fn new(device: &wgpu::Device, data: &MeshData) -> Self { let vertex_capacity = data.vertices.len(); let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), @@ -327,7 +292,7 @@ impl MeshGroup { } #[derive(Default)] -struct MeshPool { +pub struct MeshPool { groups: slab::Slab, } @@ -338,6 +303,10 @@ impl MeshPool { let sub_id = 0; MeshHandle { group_id, sub_id } } + + pub fn get_group(&self, handle: &MeshHandle) -> Option<&MeshGroup> { + self.groups.get(handle.group_id) + } } #[repr(C)] @@ -360,20 +329,18 @@ impl CameraUniform { #[repr(C)] #[derive(Copy, Clone, Eq, Hash, PartialEq)] -struct MeshHandle { +pub struct MeshHandle { group_id: usize, // unused for now, since each group contains only one mesh sub_id: usize, } #[derive(Copy, Clone, PartialEq)] -struct MeshInstance { +pub struct MeshInstance { pub handle: MeshHandle, pub transform: glam::Mat4, } -type MeshCommands = Vec; - fn load_model() -> MeshData { use tobj::*;