Improve ancestors modifiers performance

This commit is contained in:
Alejandro Alonso
2025-10-08 10:07:52 +02:00
committed by Alonso Torres
parent 544bedf7c2
commit 551a25661f

View File

@@ -32,6 +32,7 @@ use crate::wapi;
use crate::math; use crate::math;
use crate::math::bools; use crate::math::bools;
use indexmap::IndexSet;
pub use fonts::*; pub use fonts::*;
pub use images::*; pub use images::*;
@@ -1716,28 +1717,47 @@ impl RenderState {
performance::end_measure!("rebuild_tiles"); performance::end_measure!("rebuild_tiles");
} }
/// Invalidates extended rectangles and updates tiles for a set of shapes
///
/// This function takes a set of shape IDs and for each one:
/// 1. Invalidates the extrect cache
/// 2. Updates the tiles to ensure proper rendering
///
/// This is useful when you have a pre-computed set of shape IDs that need to be refreshed,
/// regardless of their relationship to other shapes (e.g., ancestors, descendants, or any other collection).
pub fn invalidate_and_update_tiles(
&mut self,
shape_ids: &IndexSet<Uuid>,
tree: &mut ShapesPool,
modifiers: &HashMap<Uuid, Matrix>,
) {
for shape_id in shape_ids {
if let Some(shape) = tree.get_mut(shape_id) {
shape.invalidate_extrect();
}
if let Some(shape) = tree.get(shape_id) {
if !shape.id.is_nil() {
self.update_tile_for(shape, tree, modifiers);
}
}
}
}
/// Processes all ancestors of a shape, invalidating their extended rectangles and updating their tiles /// Processes all ancestors of a shape, invalidating their extended rectangles and updating their tiles
/// ///
/// When a shape changes, all its ancestors need to have their extended rectangles recalculated /// When a shape changes, all its ancestors need to have their extended rectangles recalculated
/// because they may contain the changed shape. This function: /// because they may contain the changed shape. This function:
/// 1. Invalidates the extrect cache for each ancestor /// 1. Computes all ancestors of the shape
/// 2. Updates the tiles for each ancestor to ensure proper rendering /// 2. Invalidates the extrect cache for each ancestor
/// 3. Updates the tiles for each ancestor to ensure proper rendering
pub fn process_shape_ancestors( pub fn process_shape_ancestors(
&mut self, &mut self,
shape: &Shape, shape: &Shape,
tree: &mut ShapesPool, tree: &mut ShapesPool,
modifiers: &HashMap<Uuid, Matrix>, modifiers: &HashMap<Uuid, Matrix>,
) { ) {
for ancestor in shape.all_ancestors(tree, false) { let ancestors = shape.all_ancestors(tree, false);
if let Some(ancestor) = tree.get_mut(&ancestor) { self.invalidate_and_update_tiles(&ancestors, tree, modifiers);
ancestor.invalidate_extrect();
}
if let Some(ancestor) = tree.get(&ancestor) {
if !ancestor.id.is_nil() {
self.update_tile_for(ancestor, tree, modifiers);
}
}
}
} }
/// Rebuilds tiles for shapes with modifiers and processes their ancestors /// Rebuilds tiles for shapes with modifiers and processes their ancestors
@@ -1751,18 +1771,21 @@ impl RenderState {
tree: &mut ShapesPool, tree: &mut ShapesPool,
modifiers: &HashMap<Uuid, Matrix>, modifiers: &HashMap<Uuid, Matrix>,
) { ) {
let mut ancestors = IndexSet::new();
for (uuid, matrix) in modifiers { for (uuid, matrix) in modifiers {
let mut shape = { let mut shape = {
let Some(shape) = tree.get(uuid) else { let Some(shape) = tree.get(uuid) else {
panic!("Invalid current shape") panic!("Invalid current shape")
}; };
shape.clone() let shape: Cow<Shape> = Cow::Borrowed(shape);
shape
}; };
shape.apply_transform(matrix); shape.to_mut().apply_transform(matrix);
self.update_tile_for(&shape, tree, modifiers); ancestors.insert(*uuid);
self.process_shape_ancestors(&shape, tree, modifiers); ancestors.extend(shape.all_ancestors(tree, false));
} }
self.invalidate_and_update_tiles(&ancestors, tree, modifiers);
} }
pub fn get_scale(&self) -> f32 { pub fn get_scale(&self) -> f32 {