diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index c53bcee72..0aa98a2d3 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![]; @@ -851,7 +858,7 @@ impl RenderState { ) { for (uuid, matrix) in modifiers { if let Some(shape) = tree.get(uuid) { - let mut shape = shape.clone(); + let mut shape: Shape = shape.clone(); shape.apply_transform(matrix); self.update_tile_for(&shape); } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 199232a8d..7e3930119 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 }