From 747f628f04e4527da013bb0ea928edc6655df2c6 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Fri, 6 Oct 2023 15:46:02 -0700 Subject: [PATCH] deduplicate models --- src/main.rs | 174 +++++++++++++++++++++++++++++++++++++++++- src/model_graphics.rs | 22 +++++- 2 files changed, 191 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 761b8f2e..f0a98de9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -230,7 +230,7 @@ impl GlobalState{ Some(ModelGraphicsInstance{ transform: instance.transform.into(), normal_transform: Into::::into(instance.transform.matrix3).inverse().transpose(), - color: instance.color, + color:model_graphics::ModelGraphicsColor4::from(instance.color), }) } }).collect(); @@ -262,9 +262,174 @@ impl GlobalState{ }); } } + //check every model to see if it's using the same (texture,color) but has few instances, if it is combine it into one model + //1. collect unique instances of texture and color, note model id + //2. for each model id, check if removing it from the pool decreases both the model count and instance count by more than one + //3. transpose all models that stay in the set + + //best plan: benchmark set_bind_group, set_vertex_buffer, set_index_buffer and draw_indexed + //check if the estimated render performance is better by transposing multiple model instances into one model instance + + //for now: just deduplicate single models... + let mut deduplicated_models=Vec::with_capacity(indexed_models_len);//use indexed_models_len because the list will likely get smaller instead of bigger + let mut unique_texture_color=std::collections::HashMap::new();//texture->color->vec![(model_id,instance_id)] + for (model_id,model) in unique_texture_models.iter().enumerate(){ + //for now: filter out models with more than one instance + if 1=model.unique_pos.iter().map(|untransformed_pos|{ + let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array(); + let h=pos.map(|v|bytemuck::cast::(v)); + (if let Some(&pos_id)=pos_id_from.get(&h){ + pos_id + }else{ + let pos_id=unique_pos.len(); + unique_pos.push(pos.clone()); + pos_id_from.insert(h,pos_id); + pos_id + }) as u32 + }).collect(); + let map_tex_id:Vec=model.unique_tex.iter().map(|tex|{ + let h=tex.map(|v|bytemuck::cast::(v)); + (if let Some(&tex_id)=tex_id_from.get(&h){ + tex_id + }else{ + let tex_id=unique_tex.len(); + unique_tex.push(tex.clone()); + tex_id_from.insert(h,tex_id); + tex_id + }) as u32 + }).collect(); + let map_normal_id:Vec=model.unique_normal.iter().map(|untransformed_normal|{ + let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array(); + let h=normal.map(|v|bytemuck::cast::(v)); + (if let Some(&normal_id)=normal_id_from.get(&h){ + normal_id + }else{ + let normal_id=unique_normal.len(); + unique_normal.push(normal.clone()); + normal_id_from.insert(h,normal_id); + normal_id + }) as u32 + }).collect(); + let map_color_id:Vec=model.unique_color.iter().map(|color|{ + let h=color.map(|v|bytemuck::cast::(v)); + (if let Some(&color_id)=color_id_from.get(&h){ + color_id + }else{ + let color_id=unique_color.len(); + unique_color.push(color.clone()); + color_id_from.insert(h,color_id); + color_id + }) as u32 + }).collect(); + //map the indexed vertices onto new indices + //creating the vertex map is slightly different because the vertices are directly hashable + let map_vertex_id:Vec=model.unique_vertices.iter().map(|unmapped_vertex|{ + let vertex=model::IndexedVertex{ + pos:map_pos_id[unmapped_vertex.pos as usize] as u32, + tex:map_tex_id[unmapped_vertex.tex as usize] as u32, + normal:map_normal_id[unmapped_vertex.normal as usize] as u32, + color:map_color_id[unmapped_vertex.color as usize] as u32, + }; + (if let Some(&vertex_id)=vertex_id_from.get(&vertex){ + vertex_id + }else{ + let vertex_id=unique_vertices.len(); + unique_vertices.push(vertex.clone()); + vertex_id_from.insert(vertex,vertex_id); + vertex_id + }) as u32 + }).collect(); + for group in &model.groups{ + for poly in &group.polys{ + polys.push(model::IndexedPolygon{vertices:poly.vertices.iter().map(|&vertex_id|map_vertex_id[vertex_id as usize]).collect()}); + } + } + } + //push model into dedup + deduplicated_models.push(model_graphics::IndexedModelGraphicsSingleTexture{ + unique_pos, + unique_tex, + unique_normal, + unique_color, + unique_vertices, + texture, + groups:vec![model_graphics::IndexedGroupFixedTexture{ + polys + }], + instances:vec![model_graphics::ModelGraphicsInstance{ + transform:glam::Mat4::IDENTITY, + normal_transform:glam::Mat3::IDENTITY, + color + }], + }); + } + } + } + //fill untouched models + for (model_id,model) in unique_texture_models.into_iter().enumerate(){ + if !selected_model_instances.contains(&model_id){ + deduplicated_models.push(model); + } + } + //de-index models - let mut models=Vec::with_capacity(unique_texture_models.len()); - for model in unique_texture_models.into_iter(){ + let deduplicated_models_len=deduplicated_models.len(); + let mut models=Vec::with_capacity(deduplicated_models_len); + for model in deduplicated_models.into_iter(){ let mut vertices = Vec::new(); let mut index_from_vertex = std::collections::HashMap::new();//:: let mut entities = Vec::new(); @@ -372,6 +537,7 @@ impl GlobalState{ println!("Texture References={}",num_textures); println!("Textures Loaded={}",texture_views.len()); println!("Indexed Models={}",indexed_models_len); + println!("Deduplicated Models={}",deduplicated_models_len); println!("Graphics Objects: {}",self.graphics.models.len()); println!("Graphics Instances: {}",instance_count); } @@ -393,7 +559,7 @@ fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec { raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis)); raw.extend_from_slice(&[0.0]); //color - raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color)); + raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get())); raw.append(&mut v); } raw diff --git a/src/model_graphics.rs b/src/model_graphics.rs index 57bbdd80..f1a5acb3 100644 --- a/src/model_graphics.rs +++ b/src/model_graphics.rs @@ -27,9 +27,29 @@ pub struct ModelGraphicsSingleTexture{ pub entities: Vec>, pub texture: Option, } +#[derive(Clone,PartialEq)] +pub struct ModelGraphicsColor4(glam::Vec4); +impl ModelGraphicsColor4{ + pub const fn get(&self)->glam::Vec4{ + self.0 + } +} +impl From for ModelGraphicsColor4{ + fn from(value:glam::Vec4)->Self{ + Self(value) + } +} +impl std::hash::Hash for ModelGraphicsColor4{ + fn hash(&self,state:&mut H) { + for &f in self.0.as_ref(){ + bytemuck::cast::(f).hash(state); + } + } +} +impl Eq for ModelGraphicsColor4{} #[derive(Clone)] pub struct ModelGraphicsInstance{ pub transform:glam::Mat4, pub normal_transform:glam::Mat3, - pub color:glam::Vec4, + pub color:ModelGraphicsColor4, } \ No newline at end of file