Refactor MeshCommands

This commit is contained in:
marceline-cramer 2022-01-31 11:46:06 -07:00
parent 55264bfceb
commit 0c75860efd
2 changed files with 75 additions and 53 deletions

55
src/commands.rs Normal file
View File

@ -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<u32>)>,
}
impl MeshCommands {
pub fn build(instances: &Vec<MeshInstance>) -> Self {
let mut sorted_meshes = HashMap::<usize, Vec<MeshInstance>>::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());
}
}
}

View File

@ -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<MeshInstance>,
) -> 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::<usize, Vec<MeshInstance>>::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<u32>)>::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<MeshGroup>,
}
@ -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<MeshInstance>;
fn load_model() -> MeshData {
use tobj::*;