diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index ef5bbdff45..e0ef988590 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -613,10 +613,7 @@ pub extern "C" fn set_modifiers() { for entry in entries { state.modifiers.insert(entry.id, entry.transform); } - // TODO: Do a more specific rebuild of tiles. For - // example: using only the selected shapes to rebuild - // the tiles affected by the selected shapes. - state.rebuild_tiles(); + state.rebuild_modifier_tiles(); }); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 0a1f775739..0aa98a2d31 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -451,21 +451,28 @@ impl RenderState { }, ); - // TODO: Maybe we should calculate the interest area based on the actual viewport. See how. - let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox_with_interest( + // First we retrieve the extended area of the viewport that we could render. + let (isx, isy, iex, iey) = tiles::get_tiles_for_viewbox_with_interest( self.viewbox, VIEWPORT_INTEREST_AREA_THRESHOLD, ); - debug::render_debug_tiles_for_viewbox(self, sx, sy, ex, ey); - let tile_center = ((ex - sx) / 2, (ey - sy) / 2); + // Then we get the real amount of tiles rendered for the current viewbox. + let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox(self.viewbox); + debug::render_debug_tiles_for_viewbox(self, isx, isy, iex, iey); + let tile_center = ((iex - isx) / 2, (iey - isy) / 2); self.pending_tiles = vec![]; self.surfaces.cache_clear_visited(); - for y in sy..=ey { - for x in sx..=ex { + for y in isy..=iey { + for x in isx..=iex { let tile = (x, y); let distance = tiles::manhattan_distance(tile, tile_center); self.pending_tiles.push((x, y, distance)); - self.surfaces.cache_visit(tile); + // We only need to mark as visited the visible + // tiles, the ones that are outside the viewport + // should not be rendered. + if x >= sx && x <= ex && y >= sy && y <= ey { + self.surfaces.cache_visit(tile); + } } } self.pending_nodes = vec![]; @@ -844,6 +851,20 @@ impl RenderState { } } + pub fn rebuild_modifier_tiles( + &mut self, + tree: &mut HashMap, + modifiers: &HashMap, + ) { + for (uuid, matrix) in modifiers { + if let Some(shape) = tree.get(uuid) { + let mut shape: Shape = shape.clone(); + shape.apply_transform(matrix); + self.update_tile_for(&shape); + } + } + } + pub fn get_scale(&self) -> f32 { self.viewbox.zoom() * self.options.dpr() } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 199232a8da..7e3930119d 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -41,6 +41,7 @@ pub struct Surfaces { margins: skia::ISize, } +#[allow(dead_code)] impl Surfaces { pub fn new( gpu_state: &mut GpuState, @@ -301,6 +302,7 @@ pub struct SurfacePool { pub index: usize, } +#[allow(dead_code)] impl SurfacePool { pub fn with_capacity(surface: &mut skia::Surface, dims: skia::ISize, capacity: usize) -> Self { let mut surfaces = Vec::with_capacity(capacity); @@ -347,6 +349,11 @@ impl SurfacePool { .surfaces .get_mut(surface_ref_to_deallocate.index) .unwrap(); + + // This could happen when the "clear" method of the pool is called. + if surface_ref.in_use == false { + return; + } surface_ref.in_use = false; self.index = surface_ref_to_deallocate.index; } @@ -375,6 +382,7 @@ pub struct TileSurfaceCache { visited: HashMap, } +#[allow(dead_code)] impl TileSurfaceCache { pub fn new(pool: SurfacePool) -> Self { Self { @@ -388,28 +396,46 @@ impl TileSurfaceCache { return self.grid.contains_key(&tile); } + fn remove_list(&mut self, marked: Vec) { + for tile in marked.iter() { + self.grid.remove(tile); + } + } + + fn try_get_or_create(&mut self, tile: Tile) -> Result { + // TODO: I don't know yet how to improve this but I don't like it. I think + // there should be a better solution. + let mut marked = vec![]; + for (tile, surface_ref) in self.grid.iter_mut() { + let exists_as_visited = self.visited.contains_key(tile); + if !exists_as_visited { + marked.push(tile.clone()); + self.pool.deallocate(surface_ref); + continue; + } + + let is_visited = self.visited.get(tile).unwrap(); + if !*is_visited { + marked.push(tile.clone()); + self.pool.deallocate(surface_ref); + } + } + + self.remove_list(marked); + + if let Some(surface_ref) = self.pool.allocate() { + self.grid.insert(tile, surface_ref.clone()); + return Ok(surface_ref.surface.clone()); + } + return Err("Not enough surfaces".into()); + } + pub fn get_or_create(&mut self, tile: Tile) -> Result { if let Some(surface_ref) = self.pool.allocate() { self.grid.insert(tile, surface_ref.clone()); - Ok(surface_ref.surface.clone()) - } else { - // TODO: I don't know yet how to improve this but I don't like it. I think - // there should be a better solution. - for (tile, surface_ref) in self.grid.iter() { - if !self.visited.contains_key(tile) { - self.pool.deallocate(surface_ref); - continue; - } - if !self.visited.get(tile).unwrap() { - self.pool.deallocate(surface_ref); - } - } - if let Some(surface_ref) = self.pool.allocate() { - self.grid.insert(tile, surface_ref.clone()); - return Ok(surface_ref.surface.clone()); - } - return Err("Not enough surfaces".into()); + return Ok(surface_ref.surface.clone()); } + self.try_get_or_create(tile) } pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Surface, String> { @@ -420,7 +446,8 @@ impl TileSurfaceCache { if !self.grid.contains_key(&tile) { return false; } - self.grid.remove(&tile); + let surface_ref_to_deallocate = self.grid.remove(&tile); + self.pool.deallocate(&surface_ref_to_deallocate.unwrap()); true } diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 347880513c..41872e7b06 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -111,4 +111,9 @@ impl<'a> State<'a> { self.render_state .rebuild_tiles(&mut self.shapes, &self.modifiers); } + + pub fn rebuild_modifier_tiles(&mut self) { + self.render_state + .rebuild_modifier_tiles(&mut self.shapes, &self.modifiers); + } }