From 49fddea1ef00a0e55b97a7624134bf21a5485359 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 17 May 2026 14:56:20 +0300 Subject: [PATCH] fix(scene): include camera_generation in hatch/wipeout cache keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Phase 2.1 step-4 change made `synced_hatch_models` and `wipeout_models` view-cull through `entity_index().query_rect`, but left both caches keyed on `geometry_epoch` alone. Cache hit would return a list culled to a previous camera's view, dropping hatches as the user panned. Now keyed by `(geometry_epoch, camera_generation)` — same pattern as `wire_cache`. `image_cache` stays geometry-only: images self-cull per frame via `vp_scissor` in the GPU pipeline; the cached list is camera-agnostic. Co-Authored-By: Claude Opus 4.7 --- src/scene/mod.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 03dfbae76..2e0a5ca6c 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -408,11 +408,17 @@ pub struct Scene { /// Maps block_handle → (entity_handle.value() → sort_handle.value()). /// Replaces the O(objects) linear scan inside `wires_for_block()` with an O(1) lookup. sort_cache: RefCell>)>>, - /// Cached hatch fill models, keyed by geometry_epoch. - hatch_cache: RefCell>)>>, - /// Cached wipeout fill models, keyed by geometry_epoch. - wipeout_cache: RefCell>)>>, - /// Cached image models, keyed by geometry_epoch. + /// Cached hatch fill models, keyed by (geometry_epoch, camera_generation). + /// The camera term is needed because `synced_hatch_models` now view-culls + /// via the quadtree — a key that ignored camera would return stale culled + /// lists across pan/zoom. + hatch_cache: RefCell>)>>, + /// Cached wipeout fill models, keyed by (geometry_epoch, camera_generation). + /// Same reasoning as `hatch_cache`. + wipeout_cache: RefCell>)>>, + /// Cached image models, keyed by geometry_epoch. Images do their own + /// per-frame culling in the GPU pipeline (vp_scissor); no camera key + /// needed here. image_cache: RefCell>)>>, /// Cached mesh models, keyed by geometry_epoch. mesh_cache: RefCell>)>>, @@ -1055,30 +1061,32 @@ impl Scene { } pub(super) fn hatch_models_arc(&self) -> Arc> { + let key = (self.geometry_epoch, self.camera_generation); { let cache = self.hatch_cache.borrow(); - if let Some((cached_epoch, ref arc)) = *cache { - if cached_epoch == self.geometry_epoch { + if let Some((cached_key, ref arc)) = *cache { + if cached_key == key { return Arc::clone(arc); } } } let arc = Arc::new(self.synced_hatch_models()); - *self.hatch_cache.borrow_mut() = Some((self.geometry_epoch, Arc::clone(&arc))); + *self.hatch_cache.borrow_mut() = Some((key, Arc::clone(&arc))); arc } pub(super) fn wipeout_models_arc(&self) -> Arc> { + let key = (self.geometry_epoch, self.camera_generation); { let cache = self.wipeout_cache.borrow(); - if let Some((cached_epoch, ref arc)) = *cache { - if cached_epoch == self.geometry_epoch { + if let Some((cached_key, ref arc)) = *cache { + if cached_key == key { return Arc::clone(arc); } } } let arc = Arc::new(self.wipeout_models()); - *self.wipeout_cache.borrow_mut() = Some((self.geometry_epoch, Arc::clone(&arc))); + *self.wipeout_cache.borrow_mut() = Some((key, Arc::clone(&arc))); arc }