mirror of
https://github.com/penpot/penpot.git
synced 2025-06-02 05:51:45 +02:00
🎉 Improve performance reducing unnecessary calls to set-objects
🎉 Improve performance reducing unnecessary calls to set-objects
This commit is contained in:
commit
9800331505
20 changed files with 360 additions and 180 deletions
|
@ -8,13 +8,15 @@ mod render;
|
|||
mod shapes;
|
||||
mod state;
|
||||
mod utils;
|
||||
mod uuid;
|
||||
mod view;
|
||||
mod wasm;
|
||||
|
||||
use crate::mem::SerializableResult;
|
||||
use crate::shapes::{BoolType, ConstraintH, ConstraintV, TransformEntry, Type};
|
||||
|
||||
use crate::utils::uuid_from_u32_quartet;
|
||||
use crate::uuid::Uuid;
|
||||
use indexmap::IndexSet;
|
||||
use state::State;
|
||||
|
||||
pub(crate) static mut STATE: Option<Box<State>> = None;
|
||||
|
@ -201,6 +203,28 @@ pub extern "C" fn add_shape_child(a: u32, b: u32, c: u32, d: u32) {
|
|||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_children() {
|
||||
let bytes = mem::bytes();
|
||||
let entries: IndexSet<Uuid> = bytes
|
||||
.chunks(size_of::<<Uuid as SerializableResult>::BytesType>())
|
||||
.map(|data| Uuid::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
let mut deleted = IndexSet::new();
|
||||
|
||||
with_current_shape!(state, |shape: &mut Shape| {
|
||||
(_, deleted) = shape.compute_children_differences(&entries);
|
||||
shape.children = entries.clone();
|
||||
});
|
||||
|
||||
with_state!(state, {
|
||||
for id in deleted {
|
||||
state.delete_shape(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn clear_shape_children() {
|
||||
with_current_shape!(state, |shape: &mut Shape| {
|
||||
|
@ -621,6 +645,13 @@ pub extern "C" fn clear_shape_shadows() {
|
|||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn update_shape_tiles() {
|
||||
with_state!(state, {
|
||||
state.update_tile_for_current_shape();
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_flex_layout_data(
|
||||
dir: u8,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use skia_safe::{self as skia, Matrix, RRect, Rect};
|
||||
|
||||
use crate::uuid::Uuid;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::view::Viewbox;
|
||||
use crate::{run_script, run_script_int};
|
||||
|
@ -477,6 +477,7 @@ impl RenderState {
|
|||
self.surfaces.cache_visit(tile);
|
||||
}
|
||||
}
|
||||
self.pending_nodes = vec![];
|
||||
self.current_tile = None;
|
||||
self.render_in_progress = true;
|
||||
self.apply_drawing_to_render_canvas(None);
|
||||
|
@ -755,21 +756,33 @@ impl RenderState {
|
|||
self.update_render_context(next_tile);
|
||||
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
||||
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
|
||||
for id in ids {
|
||||
let element = tree.get_mut(&id).ok_or(
|
||||
"Error: Element with root_id {id} not found in the tree."
|
||||
.to_string(),
|
||||
)?;
|
||||
if element.parent_id == Some(Uuid::nil()) {
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: *id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
});
|
||||
}
|
||||
// We only need first level shapes
|
||||
let mut valid_ids: Vec<Uuid> = ids
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
tree.get(id)
|
||||
.filter(|element| element.parent_id == Some(Uuid::nil()))
|
||||
.map(|_| *id)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// These shapes for the tile should be ordered as they are in the parent node
|
||||
if let Some(root) = tree.get(&Uuid::nil()) {
|
||||
let root_ids = &root.children_ids();
|
||||
valid_ids.sort_by_key(|id| {
|
||||
root_ids.iter().rev().position(|root_id| root_id == id)
|
||||
});
|
||||
}
|
||||
|
||||
self.pending_nodes.extend(valid_ids.into_iter().map(|id| {
|
||||
NodeRenderState {
|
||||
id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -787,9 +800,13 @@ impl RenderState {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_tile_for(&mut self, shape: &Shape) {
|
||||
pub fn get_tiles_for_rect(&mut self, shape: &Shape) -> (i32, i32, i32, i32) {
|
||||
let tile_size = tiles::get_tile_size(self.viewbox);
|
||||
let (rsx, rsy, rex, rey) = tiles::get_tiles_for_rect(shape.extrect(), tile_size);
|
||||
tiles::get_tiles_for_rect(shape.extrect(), tile_size)
|
||||
}
|
||||
|
||||
pub fn update_tile_for(&mut self, shape: &Shape) {
|
||||
let (rsx, rsy, rex, rey) = self.get_tiles_for_rect(shape);
|
||||
|
||||
// Update tiles where the shape was
|
||||
if let Some(tiles) = self.tiles.get_tiles_of(shape.id) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::uuid::Uuid;
|
||||
use skia_safe as skia;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type Image = skia::Image;
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use skia_safe as skia;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::uuid::Uuid;
|
||||
use crate::view::Viewbox;
|
||||
use indexmap::IndexSet;
|
||||
use skia_safe as skia;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub type Tile = (i32, i32);
|
||||
|
||||
|
@ -64,6 +63,12 @@ impl TileHashMap {
|
|||
return self.grid.get(&tile);
|
||||
}
|
||||
|
||||
pub fn remove_shape_at(&mut self, tile: Tile, id: Uuid) {
|
||||
if let Some(shapes) = self.grid.get_mut(&tile) {
|
||||
shapes.shift_remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tiles_of(&mut self, shape_id: Uuid) -> Option<&HashSet<Tile>> {
|
||||
self.index.get(&shape_id)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use skia_safe::{self as skia};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::render::BlendMode;
|
||||
use crate::uuid::Uuid;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod blurs;
|
||||
mod bools;
|
||||
|
@ -41,6 +40,7 @@ pub use transform::*;
|
|||
|
||||
use crate::math;
|
||||
use crate::math::{Bounds, Matrix, Point};
|
||||
use indexmap::IndexSet;
|
||||
|
||||
const MIN_VISIBLE_SIZE: f32 = 2.0;
|
||||
const ANTIALIAS_THRESHOLD: f32 = 15.0;
|
||||
|
@ -160,7 +160,7 @@ pub struct Shape {
|
|||
pub id: Uuid,
|
||||
pub parent_id: Option<Uuid>,
|
||||
pub shape_type: Type,
|
||||
pub children: Vec<Uuid>,
|
||||
pub children: IndexSet<Uuid>,
|
||||
pub selrect: math::Rect,
|
||||
pub transform: Matrix,
|
||||
pub rotation: f32,
|
||||
|
@ -185,7 +185,7 @@ impl Shape {
|
|||
id,
|
||||
parent_id: None,
|
||||
shape_type: Type::Rect(Rect::default()),
|
||||
children: Vec::<Uuid>::new(),
|
||||
children: IndexSet::<Uuid>::new(),
|
||||
selrect: math::Rect::new_empty(),
|
||||
transform: Matrix::default(),
|
||||
rotation: 0.,
|
||||
|
@ -432,7 +432,16 @@ impl Shape {
|
|||
}
|
||||
|
||||
pub fn add_child(&mut self, id: Uuid) {
|
||||
self.children.push(id);
|
||||
self.children.insert(id);
|
||||
}
|
||||
|
||||
pub fn compute_children_differences(
|
||||
&mut self,
|
||||
children: &IndexSet<Uuid>,
|
||||
) -> (IndexSet<Uuid>, IndexSet<Uuid>) {
|
||||
let added = children.difference(&self.children).cloned().collect();
|
||||
let removed = self.children.difference(children).cloned().collect();
|
||||
(added, removed)
|
||||
}
|
||||
|
||||
pub fn clear_children(&mut self) {
|
||||
|
@ -659,12 +668,12 @@ impl Shape {
|
|||
vec![]
|
||||
} else if let Type::Group(group) = self.shape_type {
|
||||
if group.masked {
|
||||
self.children[1..self.children.len()].to_vec()
|
||||
self.children.iter().skip(1).cloned().collect()
|
||||
} else {
|
||||
self.children.clone()
|
||||
self.children.clone().into_iter().collect()
|
||||
}
|
||||
} else {
|
||||
self.children.clone()
|
||||
self.children.clone().into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use skia_safe::{self as skia, Rect};
|
||||
|
||||
use super::Color;
|
||||
use uuid::Uuid;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
use uuid::Uuid;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum FontStyle {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::uuid_from_u32_quartet;
|
||||
use uuid::Uuid;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -4,8 +4,6 @@ mod constraints;
|
|||
mod flex_layout;
|
||||
mod grid_layout;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use common::GetBounds;
|
||||
|
||||
use crate::math::{identitish, Bounds, Matrix, Point};
|
||||
|
@ -13,6 +11,7 @@ use crate::shapes::{
|
|||
ConstraintH, ConstraintV, Frame, Group, Layout, Modifier, Shape, TransformEntry, Type,
|
||||
};
|
||||
use crate::state::State;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
fn propagate_children(
|
||||
shape: &Shape,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::math::Bounds;
|
||||
use crate::shapes::Shape;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
pub trait GetBounds {
|
||||
fn find(&self, shape: &Shape) -> Bounds;
|
||||
|
|
|
@ -4,8 +4,9 @@ use crate::shapes::{
|
|||
AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem,
|
||||
Modifier, Shape,
|
||||
};
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::common::GetBounds;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
use crate::math::{Bounds, Matrix, Point, Vector, VectorExt};
|
||||
use crate::shapes::{GridData, LayoutData, Modifier, Shape};
|
||||
use crate::uuid::Uuid;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::common::GetBounds;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::mem::SerializableResult;
|
||||
use crate::utils::{uuid_from_u32_quartet, uuid_to_u32_quartet};
|
||||
use crate::uuid::Uuid;
|
||||
use skia::Matrix;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
@ -101,7 +101,7 @@ impl SerializableResult for TransformEntry {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use uuid::uuid;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::render::RenderState;
|
||||
use crate::shapes::Shape;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
/// This struct holds the state of the Rust application between JS calls.
|
||||
///
|
||||
|
@ -59,6 +59,20 @@ impl<'a> State<'a> {
|
|||
self.current_shape = self.shapes.get_mut(&id);
|
||||
}
|
||||
|
||||
pub fn delete_shape(&mut self, id: Uuid) {
|
||||
// We don't really do a self.shapes.remove so that redo/undo keep working
|
||||
if let Some(shape) = self.shapes.get(&id) {
|
||||
let (rsx, rsy, rex, rey) = self.render_state.get_tiles_for_rect(&shape);
|
||||
for x in rsx..=rex {
|
||||
for y in rsy..=rey {
|
||||
let tile = (x, y);
|
||||
self.render_state.surfaces.remove_cached_tile_surface(tile);
|
||||
self.render_state.tiles.remove_shape_at(tile, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_shape(&mut self) -> Option<&mut Shape> {
|
||||
self.current_shape.as_deref_mut()
|
||||
}
|
||||
|
@ -80,6 +94,19 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_tile_for_current_shape(&mut self) {
|
||||
match self.current_shape.as_mut() {
|
||||
Some(shape) => {
|
||||
// We don't need to update the tile for the root shape.
|
||||
// We can also have deleted the selected shape
|
||||
if !shape.id.is_nil() && self.shapes.contains_key(&shape.id) {
|
||||
self.render_state.update_tile_for(&shape);
|
||||
}
|
||||
}
|
||||
None => panic!("Invalid current shape"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rebuild_tiles(&mut self) {
|
||||
self.render_state
|
||||
.rebuild_tiles(&mut self.shapes, &self.modifiers);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use uuid::Uuid;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
pub fn uuid_from_u32_quartet(a: u32, b: u32, c: u32, d: u32) -> Uuid {
|
||||
let hi: u64 = ((a as u64) << 32) | b as u64;
|
||||
|
|
69
render-wasm/src/uuid.rs
Normal file
69
render-wasm/src/uuid.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use crate::mem::SerializableResult;
|
||||
use crate::utils::uuid_from_u32_quartet;
|
||||
use crate::utils::uuid_to_u32_quartet;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use uuid::Uuid as ExternalUuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Uuid(ExternalUuid);
|
||||
|
||||
impl Deref for Uuid {
|
||||
type Target = ExternalUuid;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Uuid {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Uuid {
|
||||
pub fn nil() -> Self {
|
||||
Self(ExternalUuid::nil())
|
||||
}
|
||||
|
||||
pub fn from_u64_pair(high: u64, low: u64) -> Self {
|
||||
Self(ExternalUuid::from_u64_pair(high, low))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Uuid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializableResult for Uuid {
|
||||
type BytesType = [u8; 16];
|
||||
|
||||
fn from_bytes(bytes: Self::BytesType) -> Self {
|
||||
Self(*uuid_from_u32_quartet(
|
||||
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
|
||||
u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
|
||||
u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
|
||||
u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
|
||||
))
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> Self::BytesType {
|
||||
let mut result: Self::BytesType = [0; 16];
|
||||
let (a, b, c, d) = uuid_to_u32_quartet(&self);
|
||||
result[0..4].clone_from_slice(&a.to_le_bytes());
|
||||
result[4..8].clone_from_slice(&b.to_le_bytes());
|
||||
result[8..12].clone_from_slice(&c.to_le_bytes());
|
||||
result[12..16].clone_from_slice(&d.to_le_bytes());
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// The generic trait doesn't know the size of the array. This is why the
|
||||
// clone needs to be here even if it could be generic.
|
||||
fn clone_to_slice(&self, slice: &mut [u8]) {
|
||||
slice.clone_from_slice(&self.as_bytes());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue