MaterialManager: first iteration

This commit is contained in:
Florian RICHER 2025-05-31 21:53:20 +02:00
parent 9d2a4410f0
commit 5971c8cd5f
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
2 changed files with 219 additions and 0 deletions

View file

@ -0,0 +1,218 @@
use std::{
any::TypeId,
collections::HashMap,
error::Error,
sync::{Arc, RwLock},
};
use vulkano::{
device::Device, format::Format, memory::allocator::StandardMemoryAllocator,
pipeline::Pipeline as VulkanoPipeline,
};
pub trait Pipeline {
fn load(
&mut self,
device: &Device,
memory_allocator: &StandardMemoryAllocator,
swapchain_format: Format,
depth_format: Format,
) -> Result<(), Box<dyn Error>>;
}
pub trait Material {
fn pipeline_type_id() -> TypeId
where
Self: Sized;
fn load(
&mut self,
device: &Device,
memory_allocator: &StandardMemoryAllocator,
) -> Result<(), Box<dyn Error>>;
}
pub struct MaterialManager {
device: Arc<Device>,
memory_allocator: Arc<StandardMemoryAllocator>,
swapchain_format: Format,
depth_format: Format,
// cached pipelines by Pipeline ID
loading_pipelines: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Pipeline>>>>>,
loaded_pipelines: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Pipeline>>>>>,
// cached materials by Material ID
loading_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>,
loaded_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>,
material_pipeline_map: Arc<RwLock<HashMap<TypeId, TypeId>>>,
// Store all materials marked as renderable when pipeline and material are loaded
renderable_materials: Arc<RwLock<HashMap<TypeId, Arc<RwLock<dyn Material>>>>>,
}
impl MaterialManager {
pub fn new(
device: Arc<Device>,
memory_allocator: Arc<StandardMemoryAllocator>,
swapchain_format: Format,
depth_format: Format,
) -> Self {
Self {
device,
memory_allocator,
swapchain_format,
depth_format,
loading_pipelines: Arc::new(RwLock::new(HashMap::new())),
loaded_pipelines: Arc::new(RwLock::new(HashMap::new())),
loading_materials: Arc::new(RwLock::new(HashMap::new())),
loaded_materials: Arc::new(RwLock::new(HashMap::new())),
material_pipeline_map: Arc::new(RwLock::new(HashMap::new())),
renderable_materials: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn add_pipeline<P: Pipeline + Default + 'static>(&self) {
let type_id = TypeId::of::<P>();
self.loading_pipelines
.write()
.expect("Failed to get write lock to loading_pipelines")
.insert(type_id, Arc::new(RwLock::new(P::default())));
}
pub fn add_material<M: Material + Default + 'static>(&self) {
let type_id = TypeId::of::<M>();
let pipeline_id = M::pipeline_type_id();
let material = Arc::new(RwLock::new(M::default()));
self.loading_materials
.write()
.expect("Failed to get write lock to loading_materials")
.insert(type_id, material.clone());
self.material_pipeline_map
.write()
.expect("Failed to get write lock to material_pipeline_map")
.insert(type_id, pipeline_id);
}
pub fn update_swapchain_format(&mut self, swapchain_format: Format) {
if self.swapchain_format == swapchain_format {
return;
}
self.swapchain_format = swapchain_format;
self.mark_all_pipelines_as_loading();
}
fn load_pipelines(&self) {
let mut loaded_pipelines = HashMap::<TypeId, Arc<RwLock<dyn Pipeline>>>::new();
{
let loading_pipelines = self.loading_pipelines.read().unwrap();
for (type_id, pipeline) in loading_pipelines.iter() {
let result = {
let mut pipeline = pipeline.write().unwrap();
pipeline.load(
&self.device,
&self.memory_allocator,
self.swapchain_format,
self.depth_format,
)
};
match result {
Ok(_) => {
loaded_pipelines.insert(type_id.clone(), pipeline.clone());
}
Err(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) {
let mut loaded_materials = HashMap::<TypeId, Arc<RwLock<dyn Material>>>::new();
{
let loading_materials = self.loading_materials.read().unwrap();
for (type_id, material) in loading_materials.iter() {
let result = {
let mut material = material.write().unwrap();
material.load(&self.device, &self.memory_allocator)
};
match result {
Ok(_) => {
loaded_materials.insert(type_id.clone(), material.clone());
}
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) {
let mut loading_pipelines = self.loading_pipelines.write().unwrap();
let mut loaded_pipelines = self.loaded_pipelines.write().unwrap();
let mut renderable_materials = self.renderable_materials.write().unwrap();
for (type_id, pipeline) in loaded_pipelines.iter() {
loading_pipelines.insert(type_id.clone(), pipeline.clone());
}
loaded_pipelines.clear();
renderable_materials.clear(); // Mark all materials as loading
}
}

View file

@ -1,3 +1,4 @@
pub mod material_manager;
pub mod primitives;
pub mod render_pass_manager;
pub mod texture;