diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index ae634630a9..36a00a67fb 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -982,10 +982,6 @@ [] (h/call wasm/internal-module "_clean_modifiers")) -(defn clean-geometry-modifiers - [] - (h/call wasm/internal-module "_clean_geometry_modifiers")) - (defn set-modifiers [modifiers] diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 694c0c9901..9ecb93d6d6 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -541,6 +541,7 @@ pub extern "C" fn set_structure_modifiers() { with_state_mut!(state, { let mut structure = HashMap::new(); + let mut scale_content = HashMap::new(); for entry in entries { match entry.entry_type { StructureEntryType::ScaleContent => { @@ -548,7 +549,7 @@ pub extern "C" fn set_structure_modifiers() { continue; }; for id in shape.all_children(&state.shapes, true, true) { - state.scale_content.insert(id, entry.value); + scale_content.insert(id, entry.value); } } _ => { @@ -560,6 +561,9 @@ pub extern "C" fn set_structure_modifiers() { } } } + if !scale_content.is_empty() { + state.shapes.set_scale_content(scale_content); + } if !structure.is_empty() { state.shapes.set_structure(structure); } @@ -571,16 +575,7 @@ pub extern "C" fn set_structure_modifiers() { #[no_mangle] pub extern "C" fn clean_modifiers() { with_state_mut!(state, { - state.scale_content.clear(); - state.shapes.clean_modifiers(); - state.shapes.clean_structure(); - }); -} - -#[no_mangle] -pub extern "C" fn clean_geometry_modifiers() { - with_state_mut!(state, { - state.shapes.clean_modifiers(); + state.shapes.clean_all(); }); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 78e032caaf..02dc4768fa 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -14,7 +14,7 @@ mod ui; use skia_safe::{self as skia, Matrix, RRect, Rect}; use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use gpu_state::GpuState; use options::RenderOptions; @@ -439,7 +439,6 @@ impl RenderState { pub fn render_shape( &mut self, shape: &Shape, - scale_content: Option<&f32>, clip_bounds: Option<(Rect, Option, Matrix)>, fills_surface_id: SurfaceId, strokes_surface_id: SurfaceId, @@ -449,12 +448,6 @@ impl RenderState { offset: Option<(f32, f32)>, parent_shadows: Option>, ) { - let shape = if let Some(scale_content) = scale_content { - &shape.scale_content(*scale_content) - } else { - shape - }; - let surface_ids = fills_surface_id as u32 | strokes_surface_id as u32 | innershadows_surface_id as u32 @@ -819,12 +812,7 @@ impl RenderState { } } - pub fn start_render_loop( - &mut self, - tree: ShapesPoolRef, - scale_content: &HashMap, - timestamp: i32, - ) -> Result<(), String> { + pub fn start_render_loop(&mut self, tree: ShapesPoolRef, timestamp: i32) -> Result<(), String> { let scale = self.get_scale(); self.tile_viewbox.update(self.viewbox, scale); @@ -872,7 +860,7 @@ impl RenderState { self.current_tile = None; self.render_in_progress = true; self.apply_drawing_to_render_canvas(None); - self.process_animation_frame(tree, scale_content, timestamp)?; + self.process_animation_frame(tree, timestamp)?; performance::end_measure!("start_render_loop"); Ok(()) } @@ -880,13 +868,12 @@ impl RenderState { pub fn process_animation_frame( &mut self, tree: ShapesPoolRef, - scale_content: &HashMap, timestamp: i32, ) -> Result<(), String> { performance::begin_measure!("process_animation_frame"); if self.render_in_progress { if tree.len() != 0 { - self.render_shape_tree_partial(tree, scale_content, timestamp)?; + self.render_shape_tree_partial(tree, timestamp)?; } else { println!("Empty tree"); } @@ -956,12 +943,7 @@ impl RenderState { } #[inline] - pub fn render_shape_exit( - &mut self, - element: &Shape, - visited_mask: bool, - scale_content: Option<&f32>, - ) { + pub fn render_shape_exit(&mut self, element: &Shape, visited_mask: bool) { if visited_mask { // Because masked groups needs two rendering passes (first drawing // the content and then drawing the mask), we need to do an @@ -1016,7 +998,6 @@ impl RenderState { element_strokes.to_mut().clip_content = false; self.render_shape( &element_strokes, - scale_content, None, SurfaceId::Fills, SurfaceId::Strokes, @@ -1102,7 +1083,6 @@ impl RenderState { &mut self, shape: &Shape, shadow: &Shadow, - scale_content: Option<&f32>, clip_bounds: Option<(Rect, Option, Matrix)>, scale: f32, translation: (f32, f32), @@ -1152,7 +1132,6 @@ impl RenderState { self.render_shape( &plain_shape, - scale_content, clip_bounds, SurfaceId::DropShadows, SurfaceId::DropShadows, @@ -1169,7 +1148,6 @@ impl RenderState { pub fn render_shape_tree_partial_uncached( &mut self, tree: ShapesPoolRef, - scale_content: &HashMap, timestamp: i32, ) -> Result<(bool, bool), String> { let mut iteration = 0; @@ -1198,7 +1176,7 @@ impl RenderState { } if visited_children { - self.render_shape_exit(element, visited_mask, scale_content.get(&element.id)); + self.render_shape_exit(element, visited_mask); continue; } @@ -1260,7 +1238,6 @@ impl RenderState { self.render_drop_black_shadow( element, shadow, - scale_content.get(&element.id), clip_bounds, scale, translation, @@ -1280,7 +1257,6 @@ impl RenderState { self.render_drop_black_shadow( shadow_shape, shadow, - scale_content.get(&element.id), clip_bounds, scale, translation, @@ -1314,7 +1290,6 @@ impl RenderState { self.render_shape( shadow_shape, - scale_content.get(&element.id), clip_bounds, SurfaceId::DropShadows, SurfaceId::DropShadows, @@ -1352,7 +1327,6 @@ impl RenderState { self.render_shape( element, - scale_content.get(&element.id), clip_bounds, SurfaceId::Fills, SurfaceId::Strokes, @@ -1426,7 +1400,6 @@ impl RenderState { pub fn render_shape_tree_partial( &mut self, tree: ShapesPoolRef, - scale_content: &HashMap, timestamp: i32, ) -> Result<(), String> { let mut should_stop = false; @@ -1453,7 +1426,7 @@ impl RenderState { } else { performance::begin_measure!("render_shape_tree::uncached"); let (is_empty, early_return) = - self.render_shape_tree_partial_uncached(tree, scale_content, timestamp)?; + self.render_shape_tree_partial_uncached(tree, timestamp)?; if early_return { return Ok(()); } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 6a9d0bddb5..adc8235b83 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -183,6 +183,7 @@ pub struct Shape { pub extrect: OnceCell, pub bounds: OnceCell, pub svg_transform: Option, + pub ignore_constraints: bool, } // Returns all ancestor shapes of this shape, traversing up the parent hierarchy @@ -265,30 +266,24 @@ impl Shape { extrect: OnceCell::new(), bounds: OnceCell::new(), svg_transform: None, + ignore_constraints: false, } } - pub fn scale_content(&self, value: f32) -> Self { - let mut result = self.clone(); - result.shape_type.scale_content(value); - result - .strokes - .iter_mut() - .for_each(|s| s.scale_content(value)); - result - .shadows - .iter_mut() - .for_each(|s| s.scale_content(value)); + pub fn scale_content(&mut self, value: f32) { + self.ignore_constraints = true; + self.shape_type.scale_content(value); + self.strokes.iter_mut().for_each(|s| s.scale_content(value)); - if let Some(blur) = result.blur.as_mut() { + self.shadows.iter_mut().for_each(|s| s.scale_content(value)); + + if let Some(blur) = self.blur.as_mut() { blur.scale_content(value); } - result - .layout_item + self.layout_item .iter_mut() .for_each(|i| i.scale_content(value)); - result } pub fn invalidate_extrect(&mut self) { diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index 5bfd4781ca..f6c456ae59 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -23,7 +23,6 @@ fn propagate_children( parent_bounds_after: &Bounds, transform: Matrix, bounds: &HashMap, - scale_content: &HashMap, ) -> VecDeque { let children_ids = shape.children_ids(true); @@ -38,8 +37,6 @@ fn propagate_children( continue; }; - let ignore_constraints = scale_content.contains_key(child_id); - let child_bounds = bounds.find(child); let constraint_h = match &shape.shape_type { @@ -77,7 +74,7 @@ fn propagate_children( constraint_h, constraint_v, transform, - ignore_constraints, + child.ignore_constraints, ); result.push_back(Modifier::transform(*child_id, transform)); @@ -229,7 +226,6 @@ fn propagate_transform( &shape_bounds_after, transform, bounds, - &state.scale_content, ); entries.append(&mut children); } @@ -343,12 +339,6 @@ fn reflow_shape( let shapes = &state.shapes; - let shape = if let Some(scale_content) = state.scale_content.get(id) { - &shape.scale_content(*scale_content) - } else { - shape - }; - let Type::Frame(frame_data) = &shape.shape_type else { return; }; @@ -468,7 +458,6 @@ mod tests { &bounds_after, transform, &HashMap::new(), - &HashMap::new(), ); assert_eq!(result.len(), 1); @@ -499,8 +488,7 @@ mod tests { let parent = shapes.get(&parent_id).unwrap(); - let bounds = - calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap(); + let bounds = calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap(); assert_eq!(bounds.width(), 3.0); assert_eq!(bounds.height(), 3.0); diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 54059ba70d..7cf42d3c74 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -23,7 +23,6 @@ pub(crate) struct State<'a> { pub text_editor_state: TextEditorState, pub current_id: Option, pub shapes: ShapesPool<'a>, - pub scale_content: HashMap, } impl<'a> State<'a> { @@ -33,7 +32,6 @@ impl<'a> State<'a> { text_editor_state: TextEditorState::new(), current_id: None, shapes: ShapesPool::new(), - scale_content: HashMap::new(), } } @@ -65,13 +63,13 @@ impl<'a> State<'a> { pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> { self.render_state - .start_render_loop(&self.shapes, &self.scale_content, timestamp)?; + .start_render_loop(&self.shapes, timestamp)?; Ok(()) } pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> { self.render_state - .process_animation_frame(&self.shapes, &self.scale_content, timestamp)?; + .process_animation_frame(&self.shapes, timestamp)?; Ok(()) } diff --git a/render-wasm/src/state/shapes_pool.rs b/render-wasm/src/state/shapes_pool.rs index 9c0b8ff450..ca91b833d3 100644 --- a/render-wasm/src/state/shapes_pool.rs +++ b/render-wasm/src/state/shapes_pool.rs @@ -33,6 +33,7 @@ pub struct ShapesPoolImpl<'a> { modified_shape_cache: HashMap<&'a Uuid, OnceCell>, modifiers: HashMap<&'a Uuid, skia::Matrix>, structure: HashMap<&'a Uuid, Vec>, + scale_content: HashMap<&'a Uuid, f32>, } // Type aliases to avoid writing lifetimes everywhere @@ -50,6 +51,7 @@ impl<'a> ShapesPoolImpl<'a> { modified_shape_cache: HashMap::default(), modifiers: HashMap::default(), structure: HashMap::default(), + scale_content: HashMap::default(), } } @@ -162,6 +164,20 @@ impl<'a> ShapesPoolImpl<'a> { } } + // Rebuild scale_content with fresh references + if !self.scale_content.is_empty() { + let old_scale_content: Vec<(Uuid, f32)> = self + .scale_content + .drain() + .map(|(uuid_ref, scale)| (*uuid_ref, scale)) + .collect(); + + for (uuid, scale) in old_scale_content { + if let Some(uuid_ref) = self.get_uuid_ref(&uuid) { + self.scale_content.insert(uuid_ref, scale); + } + } + } // Rebuild modified_shape_cache with fresh references if !self.modified_shape_cache.is_empty() { let old_cache: Vec<(Uuid, OnceCell)> = self @@ -204,6 +220,7 @@ impl<'a> ShapesPoolImpl<'a> { let shape_ptr = &self.shapes[idx] as *const Shape; let modifiers_ptr = &self.modifiers as *const HashMap<&'a Uuid, skia::Matrix>; let structure_ptr = &self.structure as *const HashMap<&'a Uuid, Vec>; + let scale_content_ptr = &self.scale_content as *const HashMap<&'a Uuid, f32>; let cache_ptr = &self.modified_shape_cache as *const HashMap<&'a Uuid, OnceCell>; // Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id @@ -212,15 +229,19 @@ impl<'a> ShapesPoolImpl<'a> { if self.to_update_bool(&*shape_ptr) || (*modifiers_ptr).contains_key(&id_ref) || (*structure_ptr).contains_key(&id_ref) + || (*scale_content_ptr).contains_key(&id_ref) { if let Some(cell) = (*cache_ptr).get(&id_ref) { Some(cell.get_or_init(|| { - let shape = &*shape_ptr; - shape.transformed( + let mut shape = (*shape_ptr).transformed( self, (*modifiers_ptr).get(&id_ref), (*structure_ptr).get(&id_ref), - ) + ); + if let Some(scale) = (*scale_content_ptr).get(&id_ref) { + shape.scale_content(*scale); + } + shape })) } else { Some(&*shape_ptr) @@ -240,12 +261,10 @@ impl<'a> ShapesPoolImpl<'a> { self.shapes.iter_mut() } - #[allow(dead_code)] fn clean_shape_cache(&mut self) { self.modified_shape_cache.clear() } - #[allow(dead_code)] pub fn set_modifiers(&mut self, modifiers: HashMap) { // self.clean_shape_cache(); @@ -272,7 +291,6 @@ impl<'a> ShapesPoolImpl<'a> { } } - #[allow(dead_code)] pub fn set_structure(&mut self, structure: HashMap>) { // Convert HashMap to HashMap<&'a Uuid, V> using references from shapes and // Initialize the cache cells because later we don't want to have the mutable pointer @@ -295,16 +313,33 @@ impl<'a> ShapesPoolImpl<'a> { } } - #[allow(dead_code)] - pub fn clean_modifiers(&mut self) { - self.clean_shape_cache(); - self.modifiers = HashMap::default(); + pub fn set_scale_content(&mut self, scale_content: HashMap) { + // Convert HashMap to HashMap<&'a Uuid, V> using references from shapes and + // Initialize the cache cells because later we don't want to have the mutable pointer + let mut scale_content_with_refs = HashMap::with_capacity(scale_content.len()); + let mut ids = Vec::::new(); + + for (uuid, value) in scale_content { + if let Some(uuid_ref) = self.get_uuid_ref(&uuid) { + scale_content_with_refs.insert(uuid_ref, value); + ids.push(*uuid_ref); + } + } + self.scale_content = scale_content_with_refs; + + let all_ids = shapes::all_with_ancestors(&ids, self, true); + for uuid in all_ids { + if let Some(uuid_ref) = self.get_uuid_ref(&uuid) { + self.modified_shape_cache.insert(uuid_ref, OnceCell::new()); + } + } } - #[allow(dead_code)] - pub fn clean_structure(&mut self) { + pub fn clean_all(&mut self) { self.clean_shape_cache(); + self.modifiers = HashMap::default(); self.structure = HashMap::default(); + self.scale_content = HashMap::default(); } /// Get a reference to the Uuid stored in a shape, if it exists @@ -346,6 +381,7 @@ impl<'a> ShapesPoolImpl<'a> { modified_shape_cache: HashMap::default(), modifiers: HashMap::default(), structure: HashMap::default(), + scale_content: HashMap::default(), }; result.rebuild_references();