mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
✨ Improve shapes pool performance
This commit is contained in:
committed by
alonso.torres
parent
ed4df73e42
commit
c66a8f5dc5
@@ -24,7 +24,7 @@ use std::collections::HashMap;
|
||||
use utils::uuid_from_u32_quartet;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(crate) static mut STATE: Option<Box<State>> = None;
|
||||
pub(crate) static mut STATE: Option<Box<State<'static>>> = None;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! with_state_mut {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::Matrix;
|
||||
use crate::render::{RenderState, SurfaceId};
|
||||
use crate::shapes::{BoolType, Path, Segment, Shape, StructureEntry, ToPath, Type};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
use bezier_rs::{Bezier, BezierHandles, ProjectionOptions, TValue};
|
||||
use glam::DVec2;
|
||||
@@ -387,7 +387,7 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec<Segment> {
|
||||
pub fn bool_from_shapes(
|
||||
bool_type: BoolType,
|
||||
children_ids: &IndexSet<Uuid>,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Path {
|
||||
@@ -424,7 +424,7 @@ pub fn bool_from_shapes(
|
||||
|
||||
pub fn update_bool_to_path(
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Shape {
|
||||
@@ -449,7 +449,7 @@ pub fn update_bool_to_path(
|
||||
pub fn debug_render_bool_paths(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::shapes::{
|
||||
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke,
|
||||
StructureEntry, Type,
|
||||
};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::{ShapesPoolMutRef, ShapesPoolRef};
|
||||
use crate::tiles::{self, PendingTiles, TileRect};
|
||||
use crate::uuid::Uuid;
|
||||
use crate::view::Viewbox;
|
||||
@@ -277,7 +277,7 @@ pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
||||
|
||||
fn is_modified_child(
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) -> bool {
|
||||
if modifiers.is_empty() {
|
||||
@@ -476,7 +476,7 @@ impl RenderState {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_shape(
|
||||
&mut self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
shape: &Shape,
|
||||
@@ -839,7 +839,7 @@ impl RenderState {
|
||||
|
||||
pub fn render_from_cache(
|
||||
&mut self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
@@ -884,7 +884,7 @@ impl RenderState {
|
||||
|
||||
pub fn start_render_loop(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
@@ -944,7 +944,7 @@ impl RenderState {
|
||||
|
||||
pub fn process_animation_frame(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
@@ -1031,7 +1031,7 @@ impl RenderState {
|
||||
#[inline]
|
||||
pub fn render_shape_exit(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
element: &Shape,
|
||||
@@ -1141,7 +1141,7 @@ impl RenderState {
|
||||
self.get_rect_bounds(rect)
|
||||
}
|
||||
|
||||
pub fn get_shape_extrect_bounds(&mut self, shape: &Shape, tree: &ShapesPool) -> Rect {
|
||||
pub fn get_shape_extrect_bounds(&mut self, shape: &Shape, tree: ShapesPoolRef) -> Rect {
|
||||
let rect = shape.extrect(tree);
|
||||
self.get_rect_bounds(rect)
|
||||
}
|
||||
@@ -1179,7 +1179,7 @@ impl RenderState {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_drop_black_shadow(
|
||||
&mut self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
shape: &Shape,
|
||||
@@ -1253,7 +1253,7 @@ impl RenderState {
|
||||
|
||||
pub fn render_shape_tree_partial_uncached(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
@@ -1537,7 +1537,7 @@ impl RenderState {
|
||||
|
||||
pub fn render_shape_tree_partial(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
scale_content: &HashMap<Uuid, f32>,
|
||||
@@ -1655,13 +1655,13 @@ impl RenderState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_tiles_for_shape(&mut self, shape: &Shape, tree: &ShapesPool) -> TileRect {
|
||||
pub fn get_tiles_for_shape(&mut self, shape: &Shape, tree: ShapesPoolRef) -> TileRect {
|
||||
let extrect = shape.extrect(tree);
|
||||
let tile_size = tiles::get_tile_size(self.get_scale());
|
||||
tiles::get_tiles_for_rect(extrect, tile_size)
|
||||
}
|
||||
|
||||
pub fn update_tile_for(&mut self, shape: &Shape, tree: &ShapesPool) {
|
||||
pub fn update_tile_for(&mut self, shape: &Shape, tree: ShapesPoolRef) {
|
||||
let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape, tree);
|
||||
let old_tiles: HashSet<tiles::Tile> = self
|
||||
.tiles
|
||||
@@ -1691,7 +1691,7 @@ impl RenderState {
|
||||
|
||||
pub fn rebuild_tiles_shallow(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
@@ -1721,7 +1721,7 @@ impl RenderState {
|
||||
|
||||
pub fn rebuild_tiles(
|
||||
&mut self,
|
||||
tree: &ShapesPool,
|
||||
tree: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
@@ -1759,7 +1759,7 @@ impl RenderState {
|
||||
pub fn invalidate_and_update_tiles(
|
||||
&mut self,
|
||||
shape_ids: &IndexSet<Uuid>,
|
||||
tree: &mut ShapesPool,
|
||||
tree: ShapesPoolMutRef<'_>,
|
||||
) {
|
||||
for shape_id in shape_ids {
|
||||
if let Some(shape) = tree.get(shape_id) {
|
||||
@@ -1776,7 +1776,7 @@ impl RenderState {
|
||||
/// Additionally, it processes all ancestors of modified shapes to ensure their
|
||||
/// extended rectangles are properly recalculated and their tiles are updated.
|
||||
/// This is crucial for frames and groups that contain transformed children.
|
||||
pub fn rebuild_modifier_tiles(&mut self, tree: &mut ShapesPool, ids: Vec<Uuid>) {
|
||||
pub fn rebuild_modifier_tiles(&mut self, tree: ShapesPoolMutRef<'_>, ids: Vec<Uuid>) {
|
||||
let ancestors = all_with_ancestors(&ids, tree, false);
|
||||
self.invalidate_and_update_tiles(&ancestors, tree);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ use std::collections::HashMap;
|
||||
use crate::math::{Matrix, Rect};
|
||||
use crate::shapes::modifiers::grid_layout::grid_cell_data;
|
||||
use crate::shapes::{Shape, StructureEntry};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
pub fn render_overlay(
|
||||
zoom: f32,
|
||||
canvas: &skia::Canvas,
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use skia_safe::{self as skia, Color4f};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{RenderState, ShapesPool, SurfaceId};
|
||||
use super::{RenderState, ShapesPoolRef, SurfaceId};
|
||||
use crate::math::Matrix;
|
||||
use crate::render::grid_layout;
|
||||
use crate::shapes::StructureEntry;
|
||||
@@ -9,7 +9,7 @@ use crate::uuid::Uuid;
|
||||
|
||||
pub fn render(
|
||||
render_state: &mut RenderState,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) {
|
||||
|
||||
@@ -50,7 +50,7 @@ pub use transform::*;
|
||||
use crate::math::{self, Bounds, Matrix, Point};
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
|
||||
const MIN_VISIBLE_SIZE: f32 = 2.0;
|
||||
const ANTIALIAS_THRESHOLD: f32 = 15.0;
|
||||
@@ -197,7 +197,7 @@ pub struct Shape {
|
||||
// A set of ancestor UUIDs in traversal order (closest ancestor first)
|
||||
pub fn all_with_ancestors(
|
||||
shapes: &[Uuid],
|
||||
shapes_pool: &ShapesPool,
|
||||
shapes_pool: ShapesPoolRef,
|
||||
include_hidden: bool,
|
||||
) -> IndexSet<Uuid> {
|
||||
let mut pending = Vec::from_iter(shapes.iter());
|
||||
@@ -677,7 +677,7 @@ impl Shape {
|
||||
self.selrect.width()
|
||||
}
|
||||
|
||||
pub fn visually_insignificant(&self, scale: f32, shapes_pool: &ShapesPool) -> bool {
|
||||
pub fn visually_insignificant(&self, scale: f32, shapes_pool: ShapesPoolRef) -> bool {
|
||||
let extrect = self.extrect(shapes_pool);
|
||||
extrect.width() * scale < MIN_VISIBLE_SIZE && extrect.height() * scale < MIN_VISIBLE_SIZE
|
||||
}
|
||||
@@ -721,7 +721,7 @@ impl Shape {
|
||||
self.selrect
|
||||
}
|
||||
|
||||
pub fn extrect(&self, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
pub fn extrect(&self, shapes_pool: ShapesPoolRef) -> math::Rect {
|
||||
*self
|
||||
.extrect
|
||||
.get_or_init(|| self.calculate_extrect(shapes_pool))
|
||||
@@ -834,7 +834,11 @@ impl Shape {
|
||||
rect
|
||||
}
|
||||
|
||||
fn apply_children_bounds(&self, mut rect: math::Rect, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
fn apply_children_bounds(
|
||||
&self,
|
||||
mut rect: math::Rect,
|
||||
shapes_pool: ShapesPoolRef,
|
||||
) -> math::Rect {
|
||||
let include_children = match self.shape_type {
|
||||
Type::Group(_) => true,
|
||||
Type::Frame(_) => !self.clip_content,
|
||||
@@ -852,7 +856,7 @@ impl Shape {
|
||||
rect
|
||||
}
|
||||
|
||||
pub fn calculate_extrect(&self, shapes_pool: &ShapesPool) -> math::Rect {
|
||||
pub fn calculate_extrect(&self, shapes_pool: ShapesPoolRef) -> math::Rect {
|
||||
let shape = self;
|
||||
let max_stroke = Stroke::max_bounds_width(shape.strokes.iter(), shape.is_open());
|
||||
|
||||
@@ -940,7 +944,7 @@ impl Shape {
|
||||
|
||||
pub fn all_children(
|
||||
&self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
include_hidden: bool,
|
||||
include_self: bool,
|
||||
) -> IndexSet<Uuid> {
|
||||
@@ -968,7 +972,7 @@ impl Shape {
|
||||
matrix
|
||||
}
|
||||
|
||||
pub fn get_concatenated_matrix(&self, shapes: &ShapesPool) -> Matrix {
|
||||
pub fn get_concatenated_matrix(&self, shapes: ShapesPoolRef) -> Matrix {
|
||||
let mut matrix = Matrix::new_identity();
|
||||
let mut current_id = self.id;
|
||||
while let Some(parent_id) = shapes.get(¤t_id).and_then(|s| s.parent_id) {
|
||||
@@ -1179,8 +1183,8 @@ impl Shape {
|
||||
pub fn transformed(
|
||||
&self,
|
||||
transform: Option<&Matrix>,
|
||||
structure: Option<&Vec<StructureEntry>>) -> Self
|
||||
{
|
||||
structure: Option<&Vec<StructureEntry>>,
|
||||
) -> Self {
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(self);
|
||||
if let Some(transform) = transform {
|
||||
shape.to_mut().apply_transform(transform);
|
||||
|
||||
@@ -13,13 +13,13 @@ use crate::shapes::{
|
||||
ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, StructureEntry,
|
||||
TransformEntry, Type,
|
||||
};
|
||||
use crate::state::{ShapesPool, State};
|
||||
use crate::state::{ShapesPoolRef, State};
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn propagate_children(
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
parent_bounds_before: &Bounds,
|
||||
parent_bounds_after: &Bounds,
|
||||
transform: Matrix,
|
||||
@@ -90,7 +90,7 @@ fn propagate_children(
|
||||
|
||||
fn calculate_group_bounds(
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Option<Bounds> {
|
||||
@@ -110,7 +110,7 @@ fn calculate_group_bounds(
|
||||
|
||||
fn calculate_bool_bounds(
|
||||
shape: &Shape,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
let parent_id = Uuid::new_v4();
|
||||
|
||||
let shapes = {
|
||||
let mut shapes = ShapesPool::new();
|
||||
let mut shapes = ShapesPoolRef::new();
|
||||
shapes.initialize(10);
|
||||
|
||||
let child_id = Uuid::new_v4();
|
||||
@@ -507,7 +507,7 @@ mod tests {
|
||||
fn test_group_bounds() {
|
||||
let parent_id = Uuid::new_v4();
|
||||
let shapes = {
|
||||
let mut shapes = ShapesPool::new();
|
||||
let mut shapes = ShapesPoolRef::new();
|
||||
shapes.initialize(10);
|
||||
|
||||
let child1_id = Uuid::new_v4();
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::shapes::{
|
||||
AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem,
|
||||
Modifier, Shape, StructureEntry,
|
||||
};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
@@ -179,7 +179,7 @@ fn initialize_tracks(
|
||||
layout_bounds: &Bounds,
|
||||
layout_axis: &LayoutAxis,
|
||||
flex_data: &FlexData,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Vec<TrackData> {
|
||||
@@ -433,7 +433,7 @@ fn calculate_track_data(
|
||||
layout_data: &LayoutData,
|
||||
flex_data: &FlexData,
|
||||
layout_bounds: &Bounds,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Vec<TrackData> {
|
||||
@@ -574,7 +574,7 @@ pub fn reflow_flex_layout(
|
||||
shape: &Shape,
|
||||
layout_data: &LayoutData,
|
||||
flex_data: &FlexData,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> VecDeque<Modifier> {
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::shapes::{
|
||||
JustifyContent, JustifyItems, JustifySelf, Layout, LayoutData, LayoutItem, Modifier, Shape,
|
||||
StructureEntry, Type,
|
||||
};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
use indexmap::IndexSet;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
@@ -45,7 +45,7 @@ pub fn calculate_tracks(
|
||||
grid_data: &GridData,
|
||||
layout_bounds: &Bounds,
|
||||
cells: &Vec<GridCell>,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
) -> Vec<TrackData> {
|
||||
let layout_size = if is_column {
|
||||
@@ -122,7 +122,7 @@ fn set_auto_base_size(
|
||||
column: bool,
|
||||
tracks: &mut [TrackData],
|
||||
cells: &Vec<GridCell>,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
) {
|
||||
for cell in cells {
|
||||
@@ -173,7 +173,7 @@ fn set_auto_multi_span(
|
||||
column: bool,
|
||||
tracks: &mut [TrackData],
|
||||
cells: &[GridCell],
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
) {
|
||||
// Remove groups with flex (will be set in flex_multi_span)
|
||||
@@ -248,7 +248,7 @@ fn set_flex_multi_span(
|
||||
layout_data: &LayoutData,
|
||||
tracks: &mut [TrackData],
|
||||
cells: &[GridCell],
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
) {
|
||||
// Remove groups without flex
|
||||
@@ -539,7 +539,7 @@ fn cell_bounds(
|
||||
pub fn create_cell_data<'a>(
|
||||
layout_bounds: &Bounds,
|
||||
children: &IndexSet<Uuid>,
|
||||
shapes: &'a ShapesPool,
|
||||
shapes: ShapesPoolRef<'a>,
|
||||
cells: &Vec<GridCell>,
|
||||
column_tracks: &[TrackData],
|
||||
row_tracks: &[TrackData],
|
||||
@@ -602,7 +602,7 @@ pub fn create_cell_data<'a>(
|
||||
|
||||
pub fn grid_cell_data<'a>(
|
||||
shape: &Shape,
|
||||
shapes: &'a ShapesPool,
|
||||
shapes: ShapesPoolRef<'a>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
allow_empty: bool,
|
||||
@@ -723,7 +723,7 @@ pub fn reflow_grid_layout(
|
||||
shape: &Shape,
|
||||
layout_data: &LayoutData,
|
||||
grid_data: &GridData,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> VecDeque<Modifier> {
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::{Corners, Path, Segment, Shape, StructureEntry, Type};
|
||||
use crate::math;
|
||||
|
||||
use crate::shapes::text_paths::TextPaths;
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -13,7 +13,7 @@ const BEZIER_CIRCLE_C: f32 = 0.551_915_05;
|
||||
pub trait ToPath {
|
||||
fn to_path(
|
||||
&self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Path;
|
||||
@@ -182,7 +182,7 @@ fn transform_segments(segments: Vec<Segment>, shape: &Shape) -> Vec<Segment> {
|
||||
impl ToPath for Shape {
|
||||
fn to_path(
|
||||
&self,
|
||||
shapes: &ShapesPool,
|
||||
shapes: ShapesPoolRef,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
structure: &HashMap<Uuid, Vec<StructureEntry>>,
|
||||
) -> Path {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
mod shapes_pool;
|
||||
mod text_editor;
|
||||
pub use shapes_pool::*;
|
||||
pub use shapes_pool::{ShapesPool, ShapesPoolMutRef, ShapesPoolRef};
|
||||
pub use text_editor::*;
|
||||
|
||||
use crate::render::RenderState;
|
||||
@@ -19,17 +19,17 @@ use crate::shapes::modifiers::grid_layout::grid_cell_data;
|
||||
/// It is created by [init] and passed to the other exported functions.
|
||||
/// Note that rust-skia data structures are not thread safe, so a state
|
||||
/// must not be shared between different Web Workers.
|
||||
pub(crate) struct State {
|
||||
pub(crate) struct State<'a> {
|
||||
pub render_state: RenderState,
|
||||
pub text_editor_state: TextEditorState,
|
||||
pub current_id: Option<Uuid>,
|
||||
pub shapes: ShapesPool,
|
||||
pub shapes: ShapesPool<'a>,
|
||||
pub modifiers: HashMap<Uuid, skia::Matrix>,
|
||||
pub scale_content: HashMap<Uuid, f32>,
|
||||
pub structure: HashMap<Uuid, Vec<StructureEntry>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
impl<'a> State<'a> {
|
||||
pub fn new(width: i32, height: i32) -> Self {
|
||||
State {
|
||||
render_state: RenderState::new(width, height),
|
||||
@@ -183,8 +183,16 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) {
|
||||
self.render_state
|
||||
.rebuild_modifier_tiles(&mut self.shapes, ids);
|
||||
// SAFETY: We're extending the lifetime of the mutable borrow to 'a.
|
||||
// This is safe because:
|
||||
// 1. shapes has lifetime 'a in the struct
|
||||
// 2. The reference won't outlive the struct
|
||||
// 3. No other references to shapes exist during this call
|
||||
unsafe {
|
||||
let shapes_ptr = &mut self.shapes as *mut ShapesPool<'a>;
|
||||
self.render_state
|
||||
.rebuild_modifier_tiles(&mut *shapes_ptr, ids);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn font_collection(&self) -> &FontCollection {
|
||||
|
||||
@@ -14,7 +14,7 @@ const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3;
|
||||
|
||||
/// A pool allocator for `Shape` objects that attempts to minimize memory reallocations.
|
||||
///
|
||||
/// `ShapesPool` pre-allocates a contiguous vector of `Shape` instances,
|
||||
/// `ShapesPoolImpl` pre-allocates a contiguous vector of `Shape` instances,
|
||||
/// which can be reused and indexed efficiently. This design helps avoid
|
||||
/// memory reallocation overhead by reserving enough space in advance.
|
||||
///
|
||||
@@ -23,20 +23,25 @@ const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3;
|
||||
/// Shapes are stored in a `Vec<Shape>`, which keeps the `Shape` instances
|
||||
/// in a contiguous memory block.
|
||||
///
|
||||
pub struct ShapesPool {
|
||||
pub struct ShapesPoolImpl<'a> {
|
||||
shapes: Vec<Shape>,
|
||||
counter: usize,
|
||||
|
||||
shapes_uuid_to_idx: HashMap<Uuid, usize>,
|
||||
shapes_uuid_to_idx: HashMap<&'a Uuid, usize>,
|
||||
|
||||
modified_shape_cache: HashMap<Uuid, OnceCell<Shape>>,
|
||||
modifiers: HashMap<Uuid, skia::Matrix>,
|
||||
structure: HashMap<Uuid, Vec<StructureEntry>>,
|
||||
modified_shape_cache: HashMap<&'a Uuid, OnceCell<Shape>>,
|
||||
modifiers: HashMap<&'a Uuid, skia::Matrix>,
|
||||
structure: HashMap<&'a Uuid, Vec<StructureEntry>>,
|
||||
}
|
||||
|
||||
impl ShapesPool {
|
||||
// Type aliases to avoid writing lifetimes everywhere
|
||||
pub type ShapesPool<'a> = ShapesPoolImpl<'a>;
|
||||
pub type ShapesPoolRef<'a> = &'a ShapesPoolImpl<'a>;
|
||||
pub type ShapesPoolMutRef<'a> = &'a mut ShapesPoolImpl<'a>;
|
||||
|
||||
impl<'a> ShapesPoolImpl<'a> {
|
||||
pub fn new() -> Self {
|
||||
ShapesPool {
|
||||
ShapesPoolImpl {
|
||||
shapes: vec![],
|
||||
counter: 0,
|
||||
shapes_uuid_to_idx: HashMap::default(),
|
||||
@@ -68,11 +73,19 @@ impl ShapesPool {
|
||||
self.shapes
|
||||
.extend(iter::repeat_with(|| Shape::new(Uuid::nil())).take(additional));
|
||||
}
|
||||
let new_shape = &mut self.shapes[self.counter];
|
||||
let idx = self.counter;
|
||||
let new_shape = &mut self.shapes[idx];
|
||||
new_shape.id = id;
|
||||
self.shapes_uuid_to_idx.insert(id, self.counter);
|
||||
|
||||
// Get a reference to the id field in the shape
|
||||
// SAFETY: We need to get a reference with lifetime 'a from the shape's id.
|
||||
// This is safe because the shapes Vec is stable and won't be reallocated
|
||||
// (we pre-allocate), and the id field won't move within the Shape.
|
||||
let id_ref: &'a Uuid = unsafe { &*(&self.shapes[idx].id as *const Uuid) };
|
||||
|
||||
self.shapes_uuid_to_idx.insert(id_ref, idx);
|
||||
self.counter += 1;
|
||||
new_shape
|
||||
&mut self.shapes[idx]
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
@@ -80,31 +93,47 @@ impl ShapesPool {
|
||||
}
|
||||
|
||||
pub fn has(&self, id: &Uuid) -> bool {
|
||||
self.shapes_uuid_to_idx.contains_key(id)
|
||||
self.shapes_uuid_to_idx.contains_key(&id)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: &Uuid) -> Option<&mut Shape> {
|
||||
let idx = *self.shapes_uuid_to_idx.get(id)?;
|
||||
let idx = *self.shapes_uuid_to_idx.get(&id)?;
|
||||
Some(&mut self.shapes[idx])
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &Uuid) -> Option<&Shape> {
|
||||
let idx = *self.shapes_uuid_to_idx.get(id)?;
|
||||
if self.modifiers.contains_key(id) || self.structure.contains_key(id) {
|
||||
if let Some(cell) = self.modified_shape_cache.get(id) {
|
||||
Some(cell.get_or_init(|| {
|
||||
let shape = &self.shapes[idx];
|
||||
shape.transformed(
|
||||
self.modifiers.get(id),
|
||||
self.structure.get(id)
|
||||
)
|
||||
}))
|
||||
pub fn get(&self, id: &Uuid) -> Option<&'a Shape> {
|
||||
let idx = *self.shapes_uuid_to_idx.get(&id)?;
|
||||
|
||||
// SAFETY: We're extending the lifetimes to 'a.
|
||||
// This is safe because:
|
||||
// 1. All internal HashMaps and the shapes Vec have fields with lifetime 'a
|
||||
// 2. The shape at idx won't be moved or reallocated (pre-allocated Vec)
|
||||
// 3. The id is stored in shapes[idx].id which has lifetime 'a
|
||||
// 4. The references won't outlive the ShapesPoolImpl
|
||||
unsafe {
|
||||
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<StructureEntry>>;
|
||||
let cache_ptr = &self.modified_shape_cache as *const HashMap<&'a Uuid, OnceCell<Shape>>;
|
||||
|
||||
// Extend the lifetime of id to 'a - safe because it's the same Uuid stored in shapes[idx].id
|
||||
let id_ref: &'a Uuid = &*(id as *const Uuid);
|
||||
|
||||
if (*modifiers_ptr).contains_key(&id_ref) || (*structure_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(
|
||||
(*modifiers_ptr).get(&id_ref),
|
||||
(*structure_ptr).get(&id_ref),
|
||||
)
|
||||
}))
|
||||
} else {
|
||||
Some(&*shape_ptr)
|
||||
}
|
||||
} else {
|
||||
let shape = &self.shapes[idx];
|
||||
Some(shape)
|
||||
Some(&*shape_ptr)
|
||||
}
|
||||
} else {
|
||||
Some(&self.shapes[idx])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,23 +155,30 @@ impl ShapesPool {
|
||||
pub fn set_modifiers(&mut self, modifiers: HashMap<Uuid, skia::Matrix>) {
|
||||
// self.clean_shape_cache();
|
||||
|
||||
// Initialize the cache cells because
|
||||
// later we don't want to have the mutable pointer
|
||||
for key in modifiers.keys() {
|
||||
self.modified_shape_cache.insert(*key, OnceCell::new());
|
||||
// Convert HashMap<Uuid, V> 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 modifiers_with_refs = HashMap::with_capacity(modifiers.len());
|
||||
for (uuid, matrix) in modifiers {
|
||||
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
|
||||
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
|
||||
modifiers_with_refs.insert(uuid_ref, matrix);
|
||||
}
|
||||
}
|
||||
self.modifiers = modifiers;
|
||||
self.modifiers = modifiers_with_refs;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_structure(&mut self, structure: HashMap<Uuid, Vec<StructureEntry>>) {
|
||||
// self.clean_shape_cache();
|
||||
// Initialize the cache cells because
|
||||
// later we don't want to have the mutable pointer
|
||||
for key in structure.keys() {
|
||||
self.modified_shape_cache.insert(*key, OnceCell::new());
|
||||
// Convert HashMap<Uuid, V> 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 structure_with_refs = HashMap::with_capacity(structure.len());
|
||||
for (uuid, entries) in structure {
|
||||
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
|
||||
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
|
||||
structure_with_refs.insert(uuid_ref, entries);
|
||||
}
|
||||
}
|
||||
self.structure = structure;
|
||||
self.structure = structure_with_refs;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -156,4 +192,13 @@ impl ShapesPool {
|
||||
self.clean_shape_cache();
|
||||
self.structure = HashMap::default();
|
||||
}
|
||||
|
||||
/// Get a reference to the Uuid stored in a shape, if it exists
|
||||
pub fn get_uuid_ref(&self, id: &Uuid) -> Option<&'a Uuid> {
|
||||
let idx = *self.shapes_uuid_to_idx.get(&id)?;
|
||||
// SAFETY: We're returning a reference with lifetime 'a to a Uuid stored
|
||||
// in the shapes Vec. This is safe because the Vec is stable (pre-allocated)
|
||||
// and won't be reallocated.
|
||||
unsafe { Some(&*(&self.shapes[idx].id as *const Uuid)) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user