From f21bd4589317bae4d11cb77230bb5fbe33a20aa4 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 22 Jul 2026 15:47:43 +0200 Subject: [PATCH] :bug: Fix paths and layout performance and rendering on boolean exclusions (#10778) * :bug: Skip identity transforms in layout reflow propagation Layout reflow emitted identity transforms for unchanged children, which fanned out through the whole subtree on every drag frame and froze large files in the WASM renderer. * :bug: Fix exclude boolean rendering in render WASM --- render-wasm/src/shapes.rs | 9 +++++++-- render-wasm/src/shapes/modifiers/flex_layout.rs | 9 ++++++--- render-wasm/src/shapes/modifiers/grid_layout.rs | 9 ++++++--- render-wasm/src/shapes/paths.rs | 3 +++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 92d49c7c30..fa4a71a46d 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -705,16 +705,21 @@ impl Shape { } pub fn set_path_segments(&mut self, segments: Vec) { - let path = Path::new(segments); match &mut self.shape_type { Type::Bool(Bool { bool_type, .. }) => { + let path = match bool_type { + // Exclusion booleans are computed with even-odd semantics but + // PathData uploads do not carry the fill rule. + BoolType::Exclusion => Path::new(segments).with_even_odd(true), + _ => Path::new(segments), + }; self.shape_type = Type::Bool(Bool { bool_type: *bool_type, path, }); } Type::Path(_) => { - self.shape_type = Type::Path(path); + self.shape_type = Type::Path(Path::new(segments)); } _ => {} }; diff --git a/render-wasm/src/shapes/modifiers/flex_layout.rs b/render-wasm/src/shapes/modifiers/flex_layout.rs index 7c7b911c1f..5bdd64e488 100644 --- a/render-wasm/src/shapes/modifiers/flex_layout.rs +++ b/render-wasm/src/shapes/modifiers/flex_layout.rs @@ -667,9 +667,12 @@ pub fn reflow_flex_layout( transform.post_concat(&Matrix::translate(delta_v)); } - result.push_back(Modifier::transform_propagate(child.id, transform)); - if child.has_layout() { - result.push_back(Modifier::reflow(child.id, force_reflow)); + // Skip identity: propagating it fans out through the whole subtree. + if !math::identitish(&transform) { + result.push_back(Modifier::transform_propagate(child.id, transform)); + if child.has_layout() { + result.push_back(Modifier::reflow(child.id, force_reflow)); + } } shape_anchor = next_anchor( diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index 7ddc877d91..a9749b9d8f 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -884,9 +884,12 @@ pub fn reflow_grid_layout( transform.post_concat(&Matrix::translate(delta_v)); } - result.push_back(Modifier::transform_propagate(child.id, transform)); - if child.has_layout() { - result.push_back(Modifier::reflow(child.id, force_reflow)); + // Skip identity: propagating it fans out through the whole subtree. + if !math::identitish(&transform) { + result.push_back(Modifier::transform_propagate(child.id, transform)); + if child.has_layout() { + result.push_back(Modifier::reflow(child.id, force_reflow)); + } } } diff --git a/render-wasm/src/shapes/paths.rs b/render-wasm/src/shapes/paths.rs index 33666bb0de..eac01fbde9 100644 --- a/render-wasm/src/shapes/paths.rs +++ b/render-wasm/src/shapes/paths.rs @@ -251,6 +251,9 @@ impl Path { pub fn to_skia_path(&self, svg_attrs: Option<&SvgAttrs>) -> skia::Path { let mut path = self.skia_path.snapshot(); + if self.is_even_odd() { + path.set_fill_type(skia::PathFillType::EvenOdd); + } if let Some(attrs) = svg_attrs { if attrs.fill_rule == FillRule::Evenodd { path.set_fill_type(skia::PathFillType::EvenOdd);