🔧 Optimize layout sorting and modifier propagation

This commit is contained in:
Elena Torro
2026-03-09 17:23:47 +01:00
parent 04207aebe3
commit c372b1f668
2 changed files with 42 additions and 22 deletions

View File

@@ -1260,8 +1260,6 @@ impl RenderState {
let current_fast_mode = self.options.is_fast_mode();
self.options.set_fast_mode(true);
// Skip tile rebuilding during preview - we'll do it at the end
// Just rebuild tiles for touched shapes and render synchronously
self.rebuild_touched_tiles(tree);
// Use the sync render path
@@ -2195,26 +2193,41 @@ impl RenderState {
element.children_ids_iter(false).copied().collect()
};
// Z-index ordering
// For reverse flex layouts with custom z-indexes, we reverse the base order
// so that visual stacking matches visual position
// Z-index ordering for layout children.
// Only sort when at least one child has a non-default z-index
// or absolute positioning, to avoid unnecessary work.
let children_ids = if element.has_layout() {
let mut ids = children_ids;
let has_z_index = ids
.iter()
.any(|id| tree.get(id).map(|s| s.has_z_index()).unwrap_or(false));
let mut has_z_index = false;
let mut has_absolute = false;
for id in ids.iter() {
if let Some(s) = tree.get(id) {
if s.has_z_index() {
has_z_index = true;
}
if s.is_absolute() {
has_absolute = true;
}
if has_z_index && has_absolute {
break;
}
}
}
if element.is_flex_reverse() && has_z_index {
ids.reverse();
}
// Sort by z_index descending (higher z renders on top).
// When z_index is equal, absolute children go above
// non-absolute children
ids.sort_by_key(|id| {
let s = tree.get(id);
let z = s.map(|s| s.z_index()).unwrap_or(0);
let abs = s.map(|s| s.is_absolute()).unwrap_or(false);
(std::cmp::Reverse(z), !abs)
});
// Only sort if there's something to sort by
if has_z_index || has_absolute {
ids.sort_by_key(|id| {
let s = tree.get(id);
let z = s.map(|s| s.z_index()).unwrap_or(0);
let abs = s.map(|s| s.is_absolute()).unwrap_or(false);
(std::cmp::Reverse(z), !abs)
});
}
ids
} else {
children_ids
@@ -2383,7 +2396,8 @@ impl RenderState {
pub fn get_tiles_for_shape(&mut self, shape: &Shape, tree: ShapesPoolRef) -> TileRect {
let scale = self.get_scale();
let tile_size = tiles::get_tile_size(scale);
let interest_rect = &self.tile_viewbox.interest_rect;
// Copy interest_rect to avoid holding an immutable borrow on self
let interest_rect = self.tile_viewbox.interest_rect;
// Fast path: check selrect against interest area before computing full extrect.
// For clipping shapes, selrect IS the bounds (children can't extend beyond),

View File

@@ -428,16 +428,22 @@ pub fn propagate_modifiers(
db.cmp(&da)
});
// This temporary bounds is necesary so the layouts can be calculated
// Filter out already-reflowed shapes before cloning bounds
let reflow_ids: Vec<Uuid> = layout_reflows_vec
.iter()
.filter(|id| !reflown.contains(id))
.copied()
.collect();
// This temporary bounds is necessary so the layouts can be calculated
// correctly but will be discarded before the next iteration for the
// bounds to be calculated properly with the modifiers.
let mut bounds_temp = bounds.clone();
for id in &layout_reflows_vec {
if reflown.contains(id) {
continue;
// Only clone when there are actually shapes to reflow.
if !reflow_ids.is_empty() {
let mut bounds_temp = bounds.clone();
for id in &reflow_ids {
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp);
}
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp);
}
layout_reflows = HashSet::new();
}