♻️ Refactor how rebuild_tiles works

This commit is contained in:
Aitor Moreno 2025-04-22 11:18:19 +02:00 committed by Alejandro Alonso
parent fef342b489
commit caf13eb774
8 changed files with 63 additions and 15 deletions

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

@ -24,7 +24,7 @@ EMSDK_QUIET=1 . /usr/local/emsdk/emsdk_env.sh;
export EM_CACHE="/tmp/emsdk_cache"; export EM_CACHE="/tmp/emsdk_cache";
_CARGO_PARAMS=""; _CARGO_PARAMS="${@:2}";
if [ "$_BUILD_MODE" = "release" ]; then if [ "$_BUILD_MODE" = "release" ]; then
_CARGO_PARAMS="--release $_CARGO_PARAMS" _CARGO_PARAMS="--release $_CARGO_PARAMS"

View file

@ -1 +0,0 @@
pub const DEBUG_VISIBLE: u32 = 0x01;

View file

@ -1,10 +1,10 @@
use skia_safe as skia; use skia_safe as skia;
mod debug;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
mod emscripten; mod emscripten;
mod math; mod math;
mod mem; mod mem;
mod options;
mod performance; mod performance;
mod render; mod render;
mod shapes; mod shapes;
@ -140,7 +140,11 @@ pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) {
render_state.viewbox.set_all(zoom, x, y); render_state.viewbox.set_all(zoom, x, y);
if zoom_changed { if zoom_changed {
with_state!(state, { with_state!(state, {
if state.render_state.options.is_profile_rebuild_tiles() {
state.rebuild_tiles(); state.rebuild_tiles();
} else {
state.rebuild_tiles_shallow();
}
}); });
} }
}); });

View file

@ -0,0 +1,2 @@
pub const DEBUG_VISIBLE: u32 = 0x01;
pub const PROFILE_REBUILD_TILES: u32 = 0x02;

View file

@ -4,8 +4,6 @@ use crate::uuid::Uuid;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use crate::performance; use crate::performance;
#[cfg(target_arch = "wasm32")]
use crate::run_script;
use crate::view::Viewbox; use crate::view::Viewbox;
use crate::wapi; use crate::wapi;
@ -154,7 +152,7 @@ impl RenderState {
} }
pub fn set_debug_flags(&mut self, debug: u32) { pub fn set_debug_flags(&mut self, debug: u32) {
self.options.debug_flags = debug; self.options.flags = debug;
} }
pub fn set_dpr(&mut self, dpr: f32) { pub fn set_dpr(&mut self, dpr: f32) {
@ -640,6 +638,12 @@ impl RenderState {
.to_string(), .to_string(),
)?; )?;
// If the shape is not in the tile set, then we update
// it.
if let None = self.tiles.get_tiles_of(node_id) {
self.update_tile_for(element);
}
if visited_children { if visited_children {
if !visited_mask { if !visited_mask {
match element.shape_type { match element.shape_type {
@ -810,13 +814,13 @@ impl RenderState {
Ok(()) Ok(())
} }
pub fn get_tiles_for_rect(&mut self, shape: &Shape) -> (i32, i32, i32, i32) { pub fn get_tiles_for_shape(&mut self, shape: &Shape) -> (i32, i32, i32, i32) {
let tile_size = tiles::get_tile_size(self.viewbox); let tile_size = tiles::get_tile_size(self.viewbox);
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) { pub fn update_tile_for(&mut self, shape: &Shape) {
let (rsx, rsy, rex, rey) = self.get_tiles_for_rect(shape); let (rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape);
let new_tiles: HashSet<(i32, i32)> = (rsx..=rex) let new_tiles: HashSet<(i32, i32)> = (rsx..=rex)
.flat_map(|x| (rsy..=rey).map(move |y| (x, y))) .flat_map(|x| (rsy..=rey).map(move |y| (x, y)))
.collect(); .collect();
@ -840,6 +844,36 @@ impl RenderState {
} }
} }
pub fn rebuild_tiles_shallow(
&mut self,
tree: &mut HashMap<Uuid, Shape>,
modifiers: &HashMap<Uuid, Matrix>,
structure: &HashMap<Uuid, Vec<StructureEntry>>,
) {
performance::begin_measure!("rebuild_tiles_shallow");
self.tiles.invalidate();
self.surfaces.remove_cached_tiles();
let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) {
let mut shape = shape.clone();
if shape_id != Uuid::nil() {
if let Some(modifier) = modifiers.get(&shape_id) {
shape.apply_transform(modifier);
}
self.update_tile_for(&shape);
} else {
// We only need to rebuild tiles from the first level.
let children = modified_children_ids(&shape, structure.get(&shape.id));
for child_id in children.iter() {
nodes.push(*child_id);
}
}
}
}
performance::end_measure!("rebuild_tiles_shallow");
}
pub fn rebuild_tiles( pub fn rebuild_tiles(
&mut self, &mut self,
tree: &mut HashMap<Uuid, Shape>, tree: &mut HashMap<Uuid, Shape>,

View file

@ -1,15 +1,15 @@
use crate::debug; use crate::options;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct RenderOptions { pub struct RenderOptions {
pub debug_flags: u32, pub flags: u32,
pub dpr: Option<f32>, pub dpr: Option<f32>,
} }
impl Default for RenderOptions { impl Default for RenderOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
debug_flags: 0x00, flags: 0x00,
dpr: None, dpr: None,
} }
} }
@ -17,7 +17,11 @@ impl Default for RenderOptions {
impl RenderOptions { impl RenderOptions {
pub fn is_debug_visible(&self) -> bool { pub fn is_debug_visible(&self) -> bool {
self.debug_flags & debug::DEBUG_VISIBLE == debug::DEBUG_VISIBLE self.flags & options::DEBUG_VISIBLE == options::DEBUG_VISIBLE
}
pub fn is_profile_rebuild_tiles(&self) -> bool {
self.flags & options::PROFILE_REBUILD_TILES == options::PROFILE_REBUILD_TILES
} }
pub fn dpr(&self) -> f32 { pub fn dpr(&self) -> f32 {

View file

@ -73,7 +73,7 @@ impl<'a> State<'a> {
pub fn delete_shape(&mut self, id: Uuid) { pub fn delete_shape(&mut self, id: Uuid) {
// We don't really do a self.shapes.remove so that redo/undo keep working // We don't really do a self.shapes.remove so that redo/undo keep working
if let Some(shape) = self.shapes.get(&id) { if let Some(shape) = self.shapes.get(&id) {
let (rsx, rsy, rex, rey) = self.render_state.get_tiles_for_rect(&shape); let (rsx, rsy, rex, rey) = self.render_state.get_tiles_for_shape(&shape);
for x in rsx..=rex { for x in rsx..=rex {
for y in rsy..=rey { for y in rsy..=rey {
let tile = (x, y); let tile = (x, y);
@ -118,6 +118,11 @@ impl<'a> State<'a> {
} }
} }
pub fn rebuild_tiles_shallow(&mut self) {
self.render_state
.rebuild_tiles_shallow(&mut self.shapes, &self.modifiers, &self.structure);
}
pub fn rebuild_tiles(&mut self) { pub fn rebuild_tiles(&mut self) {
self.render_state self.render_state
.rebuild_tiles(&mut self.shapes, &self.modifiers, &self.structure); .rebuild_tiles(&mut self.shapes, &self.modifiers, &self.structure);