fix: material batching/wrong texture issue

* fix: incorrect material batching

Signed-off-by: Schmarni <marnistromer@gmail.com>

* fix: prevent memory leak with batched materials

Signed-off-by: Schmarni <marnistromer@gmail.com>

---------

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2025-01-29 22:27:02 +01:00
committed by Nova
parent a44f36641e
commit 9f49ba729d

View File

@@ -22,11 +22,18 @@ use stereokit_rust::sk::MainThreadToken;
use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128}; use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128};
pub struct MaterialWrapper(pub Material); pub struct MaterialWrapper(pub Material);
impl Drop for MaterialWrapper {
fn drop(&mut self) {
MATERIAL_REGISTRY.remove(self);
}
}
impl Hash for MaterialWrapper { impl Hash for MaterialWrapper {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.get_shader().0.as_ptr().hash(state); self.0.get_shader().0.as_ptr().hash(state);
for param in self.0.get_all_param_info() { for param in self.0.get_all_param_info() {
param.to_string().hash(state) param.name.hash(state);
param.to_string().hash(state);
} }
self.0.get_chain().map(MaterialWrapper).hash(state) self.0.get_chain().map(MaterialWrapper).hash(state)
} }
@@ -59,10 +66,9 @@ unsafe impl Send for MaterialWrapper {}
unsafe impl Sync for MaterialWrapper {} unsafe impl Sync for MaterialWrapper {}
#[derive(Default)] #[derive(Default)]
struct MaterialRegistry(Mutex<FxHashMap<u64, String>>); struct MaterialRegistry(Mutex<FxHashMap<u64, Weak<MaterialWrapper>>>);
impl MaterialRegistry { impl MaterialRegistry {
fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> { fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> {
let mut lock = self.0.lock();
let hash = { let hash = {
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new(); let mut hasher = std::collections::hash_map::DefaultHasher::new();
@@ -70,15 +76,26 @@ impl MaterialRegistry {
hasher.finish() hasher.finish()
}; };
if let Some(id) = lock.get(&hash) { let mut lock = self.0.lock();
if let Ok(existing) = Material::find(id) { if let Some(mat) = lock.get(&hash) {
return Arc::new(MaterialWrapper(existing)); if let Some(mat) = mat.upgrade() {
return mat;
} }
} }
lock.insert(hash, material.0.get_id().to_string()); lock.insert(hash, Arc::downgrade(&material));
material material
} }
fn remove(&self, material: &MaterialWrapper) {
let hash = {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
material.hash(&mut hasher);
hasher.finish()
};
let mut lock = self.0.lock();
lock.remove(&hash);
}
} }
static MATERIAL_REGISTRY: Lazy<MaterialRegistry> = Lazy::new(MaterialRegistry::default); static MATERIAL_REGISTRY: Lazy<MaterialRegistry> = Lazy::new(MaterialRegistry::default);
@@ -132,6 +149,7 @@ pub struct ModelPart {
path: String, path: String,
space: Arc<Spatial>, space: Arc<Spatial>,
model: Weak<Model>, model: Weak<Model>,
material: Mutex<Option<Arc<MaterialWrapper>>>,
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>, pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>, pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>,
aliases: AliasList, aliases: AliasList,
@@ -205,6 +223,7 @@ impl ModelPart {
pending_material_parameters: Mutex::new(FxHashMap::default()), pending_material_parameters: Mutex::new(FxHashMap::default()),
pending_material_replacement: Mutex::new(None), pending_material_replacement: Mutex::new(None),
aliases: AliasList::default(), aliases: AliasList::default(),
material: Mutex::new(part.get_material().map(MaterialWrapper).map(Arc::new)),
}); });
node.add_aspect_raw(model_part.clone()); node.add_aspect_raw(model_part.clone());
parts.push(model_part.clone()); parts.push(model_part.clone());
@@ -231,7 +250,10 @@ impl ModelPart {
}; };
let shared_material = let shared_material =
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(replacement.copy()))); MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(replacement.copy())));
let mut lock = self.material.lock();
part.material(&shared_material.0); part.material(&shared_material.0);
lock.replace(shared_material);
} }
fn update(&self) { fn update(&self) {
@@ -258,7 +280,9 @@ impl ModelPart {
}; };
if let Some(material_replacement) = self.pending_material_replacement.lock().take() { if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
let mut lock = self.material.lock();
part.material(&material_replacement.0); part.material(&material_replacement.0);
lock.replace(material_replacement);
} }
'mat_params: { 'mat_params: {
@@ -274,7 +298,9 @@ impl ModelPart {
let shared_material = let shared_material =
MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(new_material))); MATERIAL_REGISTRY.add_or_get(Arc::new(MaterialWrapper(new_material)));
let mut lock = self.material.lock();
part.material(&shared_material.0); part.material(&shared_material.0);
lock.replace(shared_material);
} }
} }
} }