Refactor mesh commands (again)

This commit is contained in:
marceline-cramer 2022-02-01 20:02:17 -07:00
parent 2d6f408230
commit 3ca8e2f4c6
2 changed files with 125 additions and 34 deletions

View File

@ -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<u32>)>,
#[derive(Clone)]
struct DrawState {
group_id: usize,
texture_id: usize,
sub_id: usize,
instance_range: Range<u32>,
}
impl MeshCommands {
pub enum Command {
BindMeshGroup {
group_id: usize,
},
BindTexture {
texture_id: usize,
},
Draw {
sub_id: usize,
instance_range: Range<u32>,
},
}
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<Self::Item> {
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<DrawState>,
}
impl CommandSet {
pub fn build(instances: &Vec<MeshInstance>) -> Self {
let mut sorted_meshes = HashMap::<(usize, usize), Vec<MeshInstance>>::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::<usize, Vec<MeshInstance>>::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)
}
}

View File

@ -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()));