From 1d454f379099e27130640b57a24ec375af85131b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 20 Apr 2026 08:50:40 +0200 Subject: [PATCH] :bug: Fix stale tile cache when flex reflow changes modifier set between frames --- render-wasm/src/main.rs | 7 ++++++- render-wasm/src/render.rs | 2 -- render-wasm/src/state/shapes_pool.rs | 26 +++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 740fae8104..ee3a7815f3 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -866,7 +866,12 @@ pub extern "C" fn set_structure_modifiers() -> Result<()> { #[wasm_error] pub extern "C" fn clean_modifiers() -> Result<()> { with_state_mut!(state, { - state.shapes.clean_all(); + let prev_modifier_ids = state.shapes.clean_all(); + if !prev_modifier_ids.is_empty() { + state + .render_state + .update_tiles_shapes(&prev_modifier_ids, &mut state.shapes)?; + } }); Ok(()) } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 93fd9ce591..5df0326ace 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1775,8 +1775,6 @@ impl RenderState { self.render_shape_tree_partial(base_object, tree, timestamp, false)?; } self.flush_and_submit(); - wapi::notify_tiles_render_complete!(); - Ok(()) } diff --git a/render-wasm/src/state/shapes_pool.rs b/render-wasm/src/state/shapes_pool.rs index 436d57f2ea..7e03befa01 100644 --- a/render-wasm/src/state/shapes_pool.rs +++ b/render-wasm/src/state/shapes_pool.rs @@ -278,11 +278,35 @@ impl ShapesPoolImpl { } } - pub fn clean_all(&mut self) { + /// Clears transient per-frame state (modifiers, structure, scale_content) + /// and returns the list of UUIDs that had a `modifier` applied at the + /// moment of cleaning. The caller can use that list to re-sync the tile + /// index / tile cache for those shapes: after cleaning their modifier is + /// gone, but if we don't touch their tiles they keep pointing at the + /// previous modified position and the tile texture cache may serve stale + /// pixels. + pub fn clean_all(&mut self) -> Vec { self.clean_shape_cache(); + + let modified_uuids: Vec = if self.modifiers.is_empty() { + Vec::new() + } else { + let mut idx_to_uuid: HashMap = + HashMap::with_capacity(self.uuid_to_idx.len()); + for (uuid, idx) in self.uuid_to_idx.iter() { + idx_to_uuid.insert(*idx, *uuid); + } + self.modifiers + .keys() + .filter_map(|idx| idx_to_uuid.get(idx).copied()) + .collect() + }; + self.modifiers = HashMap::default(); self.structure = HashMap::default(); self.scale_content = HashMap::default(); + + modified_uuids } pub fn subtree(&self, id: &Uuid) -> ShapesPoolImpl {