♻️ 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"
[features]
default = ["profile"]
default = []
profile = ["profile-macros", "profile-raf"]
profile-macros = []
profile-raf = []

View file

@ -24,7 +24,7 @@ EMSDK_QUIET=1 . /usr/local/emsdk/emsdk_env.sh;
export EM_CACHE="/tmp/emsdk_cache";
_CARGO_PARAMS="";
_CARGO_PARAMS="${@:2}";
if [ "$_BUILD_MODE" = "release" ]; then
_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;
mod debug;
#[cfg(target_arch = "wasm32")]
mod emscripten;
mod math;
mod mem;
mod options;
mod performance;
mod render;
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);
if zoom_changed {
with_state!(state, {
if state.render_state.options.is_profile_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 crate::performance;
#[cfg(target_arch = "wasm32")]
use crate::run_script;
use crate::view::Viewbox;
use crate::wapi;
@ -154,7 +152,7 @@ impl RenderState {
}
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) {
@ -640,6 +638,12 @@ impl RenderState {
.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_mask {
match element.shape_type {
@ -810,13 +814,13 @@ impl RenderState {
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);
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);
let (rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape);
let new_tiles: HashSet<(i32, i32)> = (rsx..=rex)
.flat_map(|x| (rsy..=rey).map(move |y| (x, y)))
.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(
&mut self,
tree: &mut HashMap<Uuid, Shape>,

View file

@ -1,15 +1,15 @@
use crate::debug;
use crate::options;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RenderOptions {
pub debug_flags: u32,
pub flags: u32,
pub dpr: Option<f32>,
}
impl Default for RenderOptions {
fn default() -> Self {
Self {
debug_flags: 0x00,
flags: 0x00,
dpr: None,
}
}
@ -17,7 +17,11 @@ impl Default for RenderOptions {
impl RenderOptions {
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 {

View file

@ -73,7 +73,7 @@ impl<'a> State<'a> {
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);
let (rsx, rsy, rex, rey) = self.render_state.get_tiles_for_shape(&shape);
for x in rsx..=rex {
for y in rsy..=rey {
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) {
self.render_state
.rebuild_tiles(&mut self.shapes, &self.modifiers, &self.structure);