fixed incorrect usage of dynamic surface cache

- generate a bundle per material and a surface per vertex buffer layout
- add any number of instances to the bundle, but leave the number of surfaces the same
- fixes uncontrollable growth of dynamic surface cache (especially when moving camera)
This commit is contained in:
Dmitry Stepanov
2026-07-07 23:53:42 +03:00
parent c9f454509f
commit 6a858abda9
3 changed files with 25 additions and 34 deletions
+22 -12
View File
@@ -1181,27 +1181,24 @@ impl RenderDataBundleStorageTrait for RenderDataBundleStorage {
node_handle: Handle<Node>,
func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
) {
let mut layout_hasher = FxHasher::default();
layout.hash(&mut layout_hasher);
let layout_id = layout_hasher.finish();
let mut hasher = FxHasher::default();
hasher.write_u64(material.key());
layout.hash(&mut hasher);
hasher.write_u64(sort_index);
hasher.write_u32(render_path as u32);
let key = hasher.finish();
let bundle_id = hasher.finish();
let bundle = if let Some(&bundle_index) = self.bundle_map.get(&key) {
let bundle = if let Some(&bundle_index) = self.bundle_map.get(&bundle_id) {
self.bundles.get_mut(bundle_index).unwrap()
} else {
self.bundle_map.insert(key, self.bundles.len());
self.bundle_map.insert(bundle_id, self.bundles.len());
self.bundles.push(RenderDataBundle {
data: dynamic_surface_cache.get_or_create(key, layout),
data: dynamic_surface_cache.get_or_create(layout_id, layout),
sort_index,
instances: vec![
// Each bundle must have at least one instance to be rendered.
SurfaceInstanceData {
node_handle,
..Default::default()
},
],
instances: Vec::default(),
material: material.clone(),
render_path,
time_to_live: Default::default(),
@@ -1215,7 +1212,20 @@ impl RenderDataBundleStorageTrait for RenderDataBundleStorage {
let vertex_buffer = data.vertex_buffer.modify();
let triangle_buffer = data.geometry_buffer.modify();
let offset = triangle_buffer.len();
func(vertex_buffer, triangle_buffer);
let instance = SurfaceInstanceData {
node_handle,
element_range: ElementRange::Specific {
offset,
count: data.geometry_buffer.len() - offset,
},
..Default::default()
};
bundle.instances.push(instance);
}
/// Adds a new surface instance to the storage. The method will automatically put the instance in the appropriate
+3 -21
View File
@@ -29,7 +29,6 @@ use crate::{
},
};
use fxhash::FxHashMap;
use fyrox_resource::entry::TimedEntry;
use fyrox_resource::untyped::ResourceKind;
use std::{
ops::{Deref, DerefMut},
@@ -224,12 +223,10 @@ impl<T> TemporaryCache<T> {
/// as 2D sprites, tile maps, etc.
#[derive(Default)]
pub struct DynamicSurfaceCache {
cache: FxHashMap<u64, TimedEntry<SurfaceResource>>,
cache: FxHashMap<u64, SurfaceResource>,
}
impl DynamicSurfaceCache {
const TTL: f32 = 1.0;
/// Creates a new empty cache.
pub fn new() -> Self {
Self::default()
@@ -243,8 +240,7 @@ impl DynamicSurfaceCache {
layout: &[VertexAttributeDescriptor],
) -> SurfaceResource {
if let Some(surface) = self.cache.get_mut(&unique_id) {
surface.time_to_live = Self::TTL;
surface.value.clone()
surface.clone()
} else {
let default_capacity = 4096;
@@ -265,13 +261,7 @@ impl DynamicSurfaceCache {
SurfaceData::new(vertex_buffer, triangle_buffer),
);
self.cache.insert(
unique_id,
TimedEntry {
value: surface.clone(),
time_to_live: Self::TTL,
},
);
self.cache.insert(unique_id, surface.clone());
surface
}
@@ -289,12 +279,4 @@ impl DynamicSurfaceCache {
surface_data.geometry_buffer.modify().clear();
}
}
/// Updates the cache entries and removes ones that wasn't used for some time.
pub fn update(&mut self, dt: f32) {
self.cache.retain(|_, entry| {
entry.time_to_live -= dt;
entry.time_to_live > 0.0
})
}
}
-1
View File
@@ -924,7 +924,6 @@ impl Renderer {
self.update_texture_cache(resource_manager, dt);
self.update_shader_cache(dt);
self.geometry_cache.update(dt);
self.dynamic_surface_cache.update(dt);
}
fn render_scene_observer(