♻️ Refactor surface iteration

This commit is contained in:
Aitor Moreno 2025-06-05 15:24:11 +02:00
parent 8922e7454f
commit fcd3e5c34c
6 changed files with 95 additions and 113 deletions

View file

@ -778,13 +778,7 @@
(defn set-objects (defn set-objects
[objects] [objects]
(perf/begin-measure "set-objects") (perf/begin-measure "set-objects")
#_(do (let [shapes (into [] (vals objects))
(api-js/setObjects objects set-object)
(clear-drawing-cache)
(request-render "set-objects")
(perf/end-measure "set-objects"))
(let [get-memory-measure (perf/memory-measure)
shapes (into [] (vals objects))
total-shapes (count shapes) total-shapes (count shapes)
pending pending
(loop [index 0 pending []] (loop [index 0 pending []]
@ -792,8 +786,7 @@
(let [shape (nth shapes index) (let [shape (nth shapes index)
pending' (set-object objects shape)] pending' (set-object objects shape)]
(recur (inc index) (into pending pending'))) (recur (inc index) (into pending pending')))
pending)) pending))]
_ (js/console.log (clj->js (get-memory-measure)))]
(perf/end-measure "set-objects") (perf/end-measure "set-objects")
(clear-drawing-cache) (clear-drawing-cache)
(request-render "set-objects") (request-render "set-objects")

View file

@ -1,7 +0,0 @@
export function setObject(object) {
console.log(object.id)
}
export function setObjects(objects, fn) {
objects.forEach((object) => fn(objects, object))
}

View file

@ -7,7 +7,7 @@ license-file = "../LICENSE"
description = "Wasm-based canvas renderer for Penpot" description = "Wasm-based canvas renderer for Penpot"
[features] [features]
default = ["profile"] default = []
profile = ["profile-macros", "profile-raf"] profile = ["profile-macros", "profile-raf"]
profile-macros = [] profile-macros = []
profile-raf = [] profile-raf = []

View file

@ -279,18 +279,14 @@ impl RenderState {
Some(&skia::Paint::default()), Some(&skia::Paint::default()),
); );
} }
let surface_ids = SurfaceId::Strokes as u32
| SurfaceId::Fills as u32
| SurfaceId::DropShadows as u32
| SurfaceId::InnerShadows as u32;
self.surfaces.apply_mut( self.surfaces.apply_mut(surface_ids, |s| {
&[ s.canvas().clear(skia::Color::TRANSPARENT);
SurfaceId::DropShadows, });
SurfaceId::InnerShadows,
SurfaceId::Fills,
SurfaceId::Strokes,
],
|s| {
s.canvas().clear(skia::Color::TRANSPARENT);
},
);
} }
pub fn render_shape( pub fn render_shape(
@ -306,12 +302,10 @@ impl RenderState {
shape shape
}; };
let surface_ids = &[ let surface_ids = SurfaceId::Strokes as u32
SurfaceId::Fills, | SurfaceId::Fills as u32
SurfaceId::Strokes, | SurfaceId::DropShadows as u32
SurfaceId::DropShadows, | SurfaceId::InnerShadows as u32;
SurfaceId::InnerShadows,
];
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().save(); s.canvas().save();
}); });
@ -391,17 +385,13 @@ impl RenderState {
} }
Type::Text(text_content) => { Type::Text(text_content) => {
self.surfaces.apply_mut( let surface_ids = SurfaceId::Strokes as u32
&[ | SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::DropShadows as u32
SurfaceId::Strokes, | SurfaceId::InnerShadows as u32;
SurfaceId::DropShadows, self.surfaces.apply_mut(surface_ids, |s| {
SurfaceId::InnerShadows, s.canvas().concat(&matrix);
], });
|s| {
s.canvas().concat(&matrix);
},
);
let text_content = text_content.new_bounds(shape.selrect()); let text_content = text_content.new_bounds(shape.selrect());
let paragraphs = text_content.get_skia_paragraphs(self.fonts.font_collection()); let paragraphs = text_content.get_skia_paragraphs(self.fonts.font_collection());
@ -437,17 +427,13 @@ impl RenderState {
shadows::render_text_inner_shadows(self, &shape, &paragraphs, antialias); shadows::render_text_inner_shadows(self, &shape, &paragraphs, antialias);
} }
_ => { _ => {
self.surfaces.apply_mut( let surface_ids = SurfaceId::Strokes as u32
&[ | SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::DropShadows as u32
SurfaceId::Strokes, | SurfaceId::InnerShadows as u32;
SurfaceId::DropShadows, self.surfaces.apply_mut(surface_ids, |s| {
SurfaceId::InnerShadows, s.canvas().concat(&matrix);
], });
|s| {
s.canvas().concat(&matrix);
},
);
for fill in shape.fills().rev() { for fill in shape.fills().rev() {
fills::render(self, &shape, fill, antialias); fills::render(self, &shape, fill, antialias);
@ -465,17 +451,13 @@ impl RenderState {
}; };
self.apply_drawing_to_render_canvas(Some(&shape)); self.apply_drawing_to_render_canvas(Some(&shape));
self.surfaces.apply_mut( let surface_ids = SurfaceId::Strokes as u32
&[ | SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::DropShadows as u32
SurfaceId::Strokes, | SurfaceId::InnerShadows as u32;
SurfaceId::DropShadows, self.surfaces.apply_mut(surface_ids, |s| {
SurfaceId::InnerShadows, s.canvas().restore();
], });
|s| {
s.canvas().restore();
},
);
} }
pub fn update_render_context(&mut self, tile: tiles::Tile) { pub fn update_render_context(&mut self, tile: tiles::Tile) {
@ -543,17 +525,13 @@ impl RenderState {
performance::begin_measure!("start_render_loop"); performance::begin_measure!("start_render_loop");
self.reset_canvas(); self.reset_canvas();
self.surfaces.apply_mut( let surface_ids = SurfaceId::Strokes as u32
&[ | SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::DropShadows as u32
SurfaceId::Strokes, | SurfaceId::InnerShadows as u32;
SurfaceId::DropShadows, self.surfaces.apply_mut(surface_ids, |s| {
SurfaceId::InnerShadows, s.canvas().scale((scale, scale));
], });
|s| {
s.canvas().scale((scale, scale));
},
);
let viewbox_cache_size = get_cache_size(self.viewbox, scale); let viewbox_cache_size = get_cache_size(self.viewbox, scale);
let cached_viewbox_cache_size = get_cache_size(self.cached_viewbox, scale); let cached_viewbox_cache_size = get_cache_size(self.cached_viewbox, scale);
@ -573,7 +551,8 @@ impl RenderState {
self.pending_nodes.clear(); self.pending_nodes.clear();
if self.pending_nodes.capacity() < tree.len() { if self.pending_nodes.capacity() < tree.len() {
self.pending_nodes.reserve(tree.len() - self.pending_nodes.capacity()); self.pending_nodes
.reserve(tree.len() - self.pending_nodes.capacity());
} }
// reorder by distance to the center. // reorder by distance to the center.
self.current_tile = None; self.current_tile = None;
@ -887,7 +866,7 @@ impl RenderState {
if !is_empty { if !is_empty {
self.apply_render_to_final_canvas(tile_rect); self.apply_render_to_final_canvas(tile_rect);
} else { } else {
self.surfaces.apply_mut(&[SurfaceId::Target], |s| { self.surfaces.apply_mut(SurfaceId::Target as u32, |s| {
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
paint.set_color(self.background_color); paint.set_color(self.background_color);
s.canvas().draw_rect(tile_rect, &paint); s.canvas().draw_rect(tile_rect, &paint);

View file

@ -1,4 +1,6 @@
use crate::performance;
use crate::shapes::Shape; use crate::shapes::Shape;
use skia_safe::{self as skia, IRect, Paint, RRect}; use skia_safe::{self as skia, IRect, Paint, RRect};
use super::{gpu_state::GpuState, tiles::Tile, tiles::TileViewbox, tiles::TILE_SIZE}; use super::{gpu_state::GpuState, tiles::Tile, tiles::TileViewbox, tiles::TILE_SIZE};
@ -12,16 +14,17 @@ const TEXTURES_BATCH_DELETE: usize = 32;
// If it's too big it could affect performance. // If it's too big it could affect performance.
const TILE_SIZE_MULTIPLIER: i32 = 2; const TILE_SIZE_MULTIPLIER: i32 = 2;
#[repr(u32)]
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum SurfaceId { pub enum SurfaceId {
Target, Target = 0b0000_0001,
Cache, Cache = 0b0000_0010,
Current, Current = 0b0000_0100,
Fills, Fills = 0b0000_1000,
Strokes, Strokes = 0b0001_0000,
DropShadows, DropShadows = 0b0010_0000,
InnerShadows, InnerShadows = 0b0100_0000,
Debug, Debug = 0b1000_0000,
} }
pub struct Surfaces { pub struct Surfaces {
@ -137,11 +140,33 @@ impl Surfaces {
.draw(self.canvas(to), (0.0, 0.0), sampling_options, paint); .draw(self.canvas(to), (0.0, 0.0), sampling_options, paint);
} }
pub fn apply_mut(&mut self, ids: &[SurfaceId], mut f: impl FnMut(&mut skia::Surface)) { pub fn apply_mut(&mut self, ids: u32, mut f: impl FnMut(&mut skia::Surface)) {
for id in ids { performance::begin_measure!("apply_mut::flags");
let surface = self.get_mut(*id); if ids & SurfaceId::Target as u32 != 0 {
f(surface); f(self.get_mut(SurfaceId::Target));
} }
if ids & SurfaceId::Current as u32 != 0 {
f(self.get_mut(SurfaceId::Current));
}
if ids & SurfaceId::Cache as u32 != 0 {
f(self.get_mut(SurfaceId::Cache));
}
if ids & SurfaceId::Fills as u32 != 0 {
f(self.get_mut(SurfaceId::Fills));
}
if ids & SurfaceId::Strokes as u32 != 0 {
f(self.get_mut(SurfaceId::Strokes));
}
if ids & SurfaceId::InnerShadows as u32 != 0 {
f(self.get_mut(SurfaceId::InnerShadows));
}
if ids & SurfaceId::DropShadows as u32 != 0 {
f(self.get_mut(SurfaceId::DropShadows));
}
if ids & SurfaceId::Debug as u32 != 0 {
f(self.get_mut(SurfaceId::Debug));
}
performance::begin_measure!("apply_mut::flags");
} }
pub fn update_render_context(&mut self, render_area: skia::Rect, scale: f32) { pub fn update_render_context(&mut self, render_area: skia::Rect, scale: f32) {
@ -150,12 +175,10 @@ impl Surfaces {
-render_area.top() + self.margins.height as f32 / scale, -render_area.top() + self.margins.height as f32 / scale,
); );
self.apply_mut( self.apply_mut(
&[ SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::Strokes as u32
SurfaceId::Strokes, | SurfaceId::DropShadows as u32
SurfaceId::DropShadows, | SurfaceId::InnerShadows as u32,
SurfaceId::InnerShadows,
],
|s| { |s| {
s.canvas().restore(); s.canvas().restore();
s.canvas().save(); s.canvas().save();
@ -164,6 +187,7 @@ impl Surfaces {
); );
} }
#[inline]
fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface { fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface {
match id { match id {
SurfaceId::Target => &mut self.target, SurfaceId::Target => &mut self.target,
@ -224,13 +248,11 @@ impl Surfaces {
self.canvas(SurfaceId::Strokes).restore_to_count(1); self.canvas(SurfaceId::Strokes).restore_to_count(1);
self.canvas(SurfaceId::Current).restore_to_count(1); self.canvas(SurfaceId::Current).restore_to_count(1);
self.apply_mut( self.apply_mut(
&[ SurfaceId::Fills as u32
SurfaceId::Fills, | SurfaceId::Strokes as u32
SurfaceId::Strokes, | SurfaceId::Current as u32
SurfaceId::Current, | SurfaceId::DropShadows as u32
SurfaceId::DropShadows, | SurfaceId::InnerShadows as u32,
SurfaceId::InnerShadows,
],
|s| { |s| {
s.canvas().clear(color).reset_matrix(); s.canvas().clear(color).reset_matrix();
}, },

View file

@ -52,9 +52,7 @@ impl ShapesPool {
} }
self.shapes.extend( self.shapes.extend(
iter::repeat_with( iter::repeat_with(|| Box::new(Shape::new(Uuid::nil()))).take(additional as usize),
|| Box::new(Shape::new(Uuid::nil()))
).take(additional as usize)
); );
performance::end_measure!("shapes_pool_initialize"); performance::end_measure!("shapes_pool_initialize");
} }
@ -62,11 +60,8 @@ impl ShapesPool {
pub fn add_shape(&mut self, id: Uuid) -> &mut Shape { pub fn add_shape(&mut self, id: Uuid) -> &mut Shape {
if self.counter >= self.shapes.len() { if self.counter >= self.shapes.len() {
let additional = (self.shapes.len() as f32 * SHAPES_POOL_ALLOC_MULTIPLIER) as usize; let additional = (self.shapes.len() as f32 * SHAPES_POOL_ALLOC_MULTIPLIER) as usize;
self.shapes.extend( self.shapes
iter::repeat_with( .extend(iter::repeat_with(|| Box::new(Shape::new(Uuid::nil()))).take(additional));
|| Box::new(Shape::new(Uuid::nil()))
).take(additional)
);
} }
let new_shape = &mut self.shapes[self.counter]; let new_shape = &mut self.shapes[self.counter];
new_shape.id = id; new_shape.id = id;