MaterialManager: with O(n) complexity

This commit is contained in:
Florian RICHER 2025-05-31 22:38:38 +02:00
parent 5971c8cd5f
commit 1a071e44a9
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77

View file

@ -1,14 +1,10 @@
use std::{ use std::{
any::TypeId, any::TypeId,
collections::HashMap,
error::Error, error::Error,
sync::{Arc, RwLock}, sync::{Arc, RwLock, RwLockReadGuard},
}; };
use vulkano::{ use vulkano::{device::Device, format::Format, memory::allocator::StandardMemoryAllocator};
device::Device, format::Format, memory::allocator::StandardMemoryAllocator,
pipeline::Pipeline as VulkanoPipeline,
};
pub trait Pipeline { pub trait Pipeline {
fn load( fn load(
@ -32,23 +28,31 @@ pub trait Material {
) -> Result<(), Box<dyn Error>>; ) -> Result<(), Box<dyn Error>>;
} }
pub enum MaterialState {
Loading,
Loaded,
}
pub enum MaterialError {
PipelineNotFound,
}
pub struct MaterialManager { pub struct MaterialManager {
device: Arc<Device>, device: Arc<Device>,
memory_allocator: Arc<StandardMemoryAllocator>, memory_allocator: Arc<StandardMemoryAllocator>,
swapchain_format: Format, swapchain_format: Format,
depth_format: Format, depth_format: Format,
// cached pipelines by Pipeline ID pipelines_id: Arc<RwLock<Vec<TypeId>>>,
loading_pipelines: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Pipeline>>>>>, pipelines_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
loaded_pipelines: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Pipeline>>>>>, pipelines: Arc<RwLock<Vec<Arc<RwLock<dyn Pipeline>>>>>,
// cached materials by Material ID materials_id: Arc<RwLock<Vec<TypeId>>>,
loading_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>, materials_pipeline_id: Arc<RwLock<Vec<TypeId>>>,
loaded_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>, materials_pipeline: Arc<RwLock<Vec<Arc<RwLock<dyn Pipeline>>>>>,
material_pipeline_map: Arc<RwLock<HashMap<TypeId, TypeId>>>, materials_pipeline_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
materials_state: Arc<RwLock<Vec<Arc<RwLock<MaterialState>>>>>,
// Store all materials marked as renderable when pipeline and material are loaded materials: Arc<RwLock<Vec<Arc<RwLock<dyn Material>>>>>,
renderable_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>,
} }
impl MaterialManager { impl MaterialManager {
@ -63,37 +67,75 @@ impl MaterialManager {
memory_allocator, memory_allocator,
swapchain_format, swapchain_format,
depth_format, depth_format,
loading_pipelines: Arc::new(RwLock::new(HashMap::new())), pipelines_id: Arc::new(RwLock::new(Vec::new())),
loaded_pipelines: Arc::new(RwLock::new(HashMap::new())), pipelines_state: Arc::new(RwLock::new(Vec::new())),
loading_materials: Arc::new(RwLock::new(HashMap::new())), pipelines: Arc::new(RwLock::new(Vec::new())),
loaded_materials: Arc::new(RwLock::new(HashMap::new())), materials_id: Arc::new(RwLock::new(Vec::new())),
material_pipeline_map: Arc::new(RwLock::new(HashMap::new())), materials_pipeline_id: Arc::new(RwLock::new(Vec::new())),
renderable_materials: Arc::new(RwLock::new(HashMap::new())), materials_pipeline: Arc::new(RwLock::new(Vec::new())),
materials_pipeline_state: Arc::new(RwLock::new(Vec::new())),
materials_state: Arc::new(RwLock::new(Vec::new())),
materials: Arc::new(RwLock::new(Vec::new())),
} }
} }
pub fn add_pipeline<P: Pipeline + Default + 'static>(&self) { pub fn add_pipeline<P: Pipeline + Default + 'static>(&self) {
let type_id = TypeId::of::<P>(); let type_id = TypeId::of::<P>();
self.loading_pipelines let pipeline = Arc::new(RwLock::new(P::default()));
.write()
.expect("Failed to get write lock to loading_pipelines") let mut pipelines_id = self.pipelines_id.write().unwrap();
.insert(type_id, Arc::new(RwLock::new(P::default()))); let mut pipelines_state = self.pipelines_state.write().unwrap();
let mut pipelines = self.pipelines.write().unwrap();
pipelines_id.push(type_id);
pipelines_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
pipelines.push(pipeline.clone());
} }
pub fn add_material<M: Material + Default + 'static>(&self) { pub fn add_material<M: Material + Default + 'static>(&self) -> Result<(), MaterialError> {
let type_id = TypeId::of::<M>();
let pipeline_id = M::pipeline_type_id(); let pipeline_id = M::pipeline_type_id();
let pipeline_result = {
let pipelines_id = self.pipelines_id.read().unwrap();
let pipelines_state = self.pipelines_state.read().unwrap();
let pipelines = self.pipelines.read().unwrap();
pipelines_id
.iter()
.zip(pipelines.iter())
.zip(pipelines_state.iter())
.find(|((id, _), _)| *id == &pipeline_id)
.map(|((_, pipeline), state)| (pipeline.clone(), state.clone()))
};
let (pipeline, pipeline_state) = match pipeline_result {
Some(pipeline) => pipeline,
None => {
tracing::error!(
"Pipeline with id {pipeline_id:?} not found, please add it before adding a material"
);
return Err(MaterialError::PipelineNotFound);
}
};
let type_id = TypeId::of::<M>();
let material = Arc::new(RwLock::new(M::default())); let material = Arc::new(RwLock::new(M::default()));
self.loading_materials let mut materials_id = self.materials_id.write().unwrap();
.write() let mut materials_pipeline_id = self.materials_pipeline_id.write().unwrap();
.expect("Failed to get write lock to loading_materials") let mut materials_pipeline = self.materials_pipeline.write().unwrap();
.insert(type_id, material.clone()); let mut materials_pipeline_state = self.materials_pipeline_state.write().unwrap();
let mut materials_state = self.materials_state.write().unwrap();
let mut materials = self.materials.write().unwrap();
self.material_pipeline_map materials_id.push(type_id);
.write() materials_pipeline_id.push(pipeline_id);
.expect("Failed to get write lock to material_pipeline_map") materials_pipeline.push(pipeline.clone());
.insert(type_id, pipeline_id); materials_pipeline_state.push(pipeline_state.clone());
materials_state.push(Arc::new(RwLock::new(MaterialState::Loading)));
materials.push(material.clone());
Ok(())
} }
pub fn update_swapchain_format(&mut self, swapchain_format: Format) { pub fn update_swapchain_format(&mut self, swapchain_format: Format) {
@ -105,114 +147,101 @@ impl MaterialManager {
self.mark_all_pipelines_as_loading(); self.mark_all_pipelines_as_loading();
} }
fn mark_all_pipelines_as_loading(&self) {
let pipelines_state = self.pipelines_state.write().unwrap();
for state in pipelines_state.iter() {
let mut state = state.write().unwrap();
*state = MaterialState::Loading;
}
}
fn load_pipelines(&self) { fn load_pipelines(&self) {
let mut loaded_pipelines = HashMap::<TypeId, Arc<RwLock<dyn Pipeline>>>::new(); let pipelines_state = self.pipelines_state.read().unwrap();
let pipelines = self.pipelines.read().unwrap();
{ let iter = pipelines_state
let loading_pipelines = self.loading_pipelines.read().unwrap(); .iter()
.zip(pipelines.iter())
.filter(|(state, _)| {
let state = state.read().unwrap();
matches!(*state, MaterialState::Loading)
});
for (type_id, pipeline) in loading_pipelines.iter() { for (state, pipeline) in iter {
let result = { let mut pipeline = pipeline.write().unwrap();
let mut pipeline = pipeline.write().unwrap(); let result = pipeline.load(
pipeline.load( &self.device,
&self.device, &self.memory_allocator,
&self.memory_allocator, self.swapchain_format,
self.swapchain_format, self.depth_format,
self.depth_format, );
)
}; match result {
match result { Ok(_) => {
Ok(_) => { let mut state = state.write().unwrap();
loaded_pipelines.insert(type_id.clone(), pipeline.clone()); *state = MaterialState::Loaded;
} }
Err(e) => { Err(e) => {
tracing::error!("Failed to load pipeline: {e}"); tracing::error!("Failed to load pipeline: {e}");
}
} }
} }
} }
let loaded_pipeline_keys = loaded_pipelines.keys().collect::<Vec<_>>();
{
let mut loading_pipelines = self.loading_pipelines.write().unwrap();
let loaded_materials = self.loaded_materials.read().unwrap();
let mut renderable_materials = self.renderable_materials.write().unwrap();
for type_id in loaded_pipeline_keys {
loading_pipelines.remove(type_id);
if loaded_materials.contains_key(type_id) {
renderable_materials.insert(
type_id.clone(),
loaded_materials.get(type_id).unwrap().clone(),
);
}
}
}
self.loaded_pipelines
.write()
.expect("Failed to get write lock to loaded_pipelines")
.extend(loaded_pipelines);
} }
fn load_materials(&self) { fn load_materials(&self) {
let mut loaded_materials = HashMap::<TypeId, Arc<RwLock<dyn Material>>>::new(); let materials_state = self.materials_state.read().unwrap();
let materials = self.materials.read().unwrap();
{ let iter = materials_state
let loading_materials = self.loading_materials.read().unwrap(); .iter()
.zip(materials.iter())
.filter(|(state, _)| {
let state = state.read().unwrap();
matches!(*state, MaterialState::Loading)
});
for (type_id, material) in loading_materials.iter() { for (state, material) in iter {
let result = { let mut material = material.write().unwrap();
let mut material = material.write().unwrap(); let result = material.load(&self.device, &self.memory_allocator);
material.load(&self.device, &self.memory_allocator)
};
match result { match result {
Ok(_) => { Ok(_) => {
loaded_materials.insert(type_id.clone(), material.clone()); let mut state = state.write().unwrap();
} *state = MaterialState::Loaded;
Err(e) => { }
tracing::error!("Failed to load material: {e}"); Err(e) => {
} tracing::error!("Failed to load material: {e}");
} }
} }
} }
{
let mut loading_materials = self.loading_materials.write().unwrap();
let loaded_pipelines = self.loaded_pipelines.read().unwrap();
let material_pipeline_map = self.material_pipeline_map.read().unwrap();
let mut renderable_materials = self.renderable_materials.write().unwrap();
for (type_id, material) in loaded_materials.iter() {
loading_materials.remove(type_id);
let pipeline_id = material_pipeline_map.get(type_id).unwrap().clone();
if loaded_pipelines.contains_key(&pipeline_id) {
renderable_materials.insert(type_id.clone(), material.clone());
}
}
}
self.loaded_materials
.write()
.expect("Failed to get write lock to loaded_materials")
.extend(loaded_materials);
} }
fn mark_all_pipelines_as_loading(&self) { fn render_materials<F>(&self, f: F)
let mut loading_pipelines = self.loading_pipelines.write().unwrap(); where
let mut loaded_pipelines = self.loaded_pipelines.write().unwrap(); F: Fn(RwLockReadGuard<'_, dyn Material>, RwLockReadGuard<'_, dyn Pipeline>),
let mut renderable_materials = self.renderable_materials.write().unwrap(); {
let materials = self.materials.read().unwrap();
let materials_state = self.materials_state.read().unwrap();
let materials_pipeline = self.materials_pipeline.read().unwrap();
let materials_pipeline_state = self.materials_pipeline_state.read().unwrap();
for (type_id, pipeline) in loaded_pipelines.iter() { materials
loading_pipelines.insert(type_id.clone(), pipeline.clone()); .iter()
} .zip(materials_state.iter())
.zip(materials_pipeline.iter())
.zip(materials_pipeline_state.iter())
.filter(|(((_, material_state), _), pipeline_state)| {
let material_state = material_state.read().unwrap();
let pipeline_state = pipeline_state.read().unwrap();
matches!(*material_state, MaterialState::Loaded)
&& matches!(*pipeline_state, MaterialState::Loaded)
})
.for_each(|(((material, _), pipeline), _)| {
let material = material.read().unwrap();
let pipeline = pipeline.read().unwrap();
loaded_pipelines.clear(); f(material, pipeline);
renderable_materials.clear(); // Mark all materials as loading });
} }
} }