From 75d9004a8c7b05efc093055853033294fdd310ff Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 12 Jan 2026 10:23:47 +0100 Subject: [PATCH] Root ids refactor --- frontend/resources/wasm-playground/rects.html | 4 +- render-wasm/src/render.rs | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/frontend/resources/wasm-playground/rects.html b/frontend/resources/wasm-playground/rects.html index cee9351b91..e37718422a 100644 --- a/frontend/resources/wasm-playground/rects.html +++ b/frontend/resources/wasm-playground/rects.html @@ -71,10 +71,10 @@ performance.mark('render:begin'); Module._set_view(1, 0, 0); - Module._render(Date.now()); + Module._render_sync(); performance.mark('render:end'); const { duration } = performance.measure('render', 'render:begin', 'render:end'); - // alert(`render time: ${duration.toFixed(2)}ms`); + alert(`render time: ${duration.toFixed(2)}ms`); }); diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 321f14a981..877839971b 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -377,6 +377,10 @@ pub(crate) struct RenderState { /// where we must render shapes without inheriting ancestor layer blurs. Toggle it through /// `with_nested_blurs_suppressed` to ensure it's always restored. pub ignore_nested_blurs: bool, + /// Cached root_ids and root_ids_map to avoid recalculating them every frame + /// These are invalidated when the tree structure changes + cached_root_ids: Option>, + cached_root_ids_map: Option>, } pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize { @@ -449,6 +453,8 @@ impl RenderState { focus_mode: FocusMode::new(), touched_ids: HashSet::default(), ignore_nested_blurs: false, + cached_root_ids: None, + cached_root_ids_map: None, } } @@ -2050,14 +2056,37 @@ impl RenderState { .canvas(SurfaceId::Current) .clear(self.background_color); - let root_ids = { - if let Some(shape_id) = base_object { + // Get or compute root_ids and root_ids_map (cached to avoid recalculation every frame) + let (root_ids, root_ids_map) = { + let root_ids = if let Some(shape_id) = base_object { vec![*shape_id] } else { let Some(root) = tree.get(&Uuid::nil()) else { return Err(String::from("Root shape not found")); }; root.children_ids(false) + }; + + // Check if cache is valid (same root_ids) + let cache_valid = self.cached_root_ids.as_ref() + .map(|cached| cached.len() == root_ids.len() && cached.iter().zip(root_ids.iter()).all(|(a, b)| a == b)) + .unwrap_or(false); + + if cache_valid { + // Use cached map + (root_ids, self.cached_root_ids_map.as_ref().unwrap().clone()) + } else { + // Recompute and cache + let root_ids_map: std::collections::HashMap = root_ids + .iter() + .enumerate() + .map(|(i, id)| (*id, i)) + .collect(); + + self.cached_root_ids = Some(root_ids.clone()); + self.cached_root_ids_map = Some(root_ids_map.clone()); + + (root_ids, root_ids_map) } }; @@ -2068,12 +2097,6 @@ impl RenderState { if !self.surfaces.has_cached_tile_surface(next_tile) { if let Some(ids) = self.tiles.get_shapes_at(next_tile) { - let root_ids_map: std::collections::HashMap = root_ids - .iter() - .enumerate() - .map(|(i, id)| (*id, i)) - .collect(); - // We only need first level shapes let mut valid_ids: Vec = ids .iter()