From 60bc88a0757a8f819bab798169e1e63155516bf6 Mon Sep 17 00:00:00 2001 From: Aitor Moreno Date: Thu, 20 Mar 2025 19:24:02 +0100 Subject: [PATCH] :bug: Fix tile cache exhaustion --- render-wasm/src/render.rs | 2 + render-wasm/src/render/surfaces.rs | 98 +++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index bd0d074e41..f1595aa1dc 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -462,10 +462,12 @@ impl RenderState { ey + interest_delta */ self.pending_tiles = vec![]; + self.surfaces.cache_clear_visited(); for y in sy..=ey { for x in sx..=ex { let tile = (x, y); self.pending_tiles.push(tile); + self.surfaces.cache_visit(tile); } } self.current_tile = None; diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index c1653bd54d..154f0f0e7b 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -238,6 +238,14 @@ impl Surfaces { .reset_matrix(); } + pub fn cache_clear_visited(&mut self) { + self.tiles.clear_visited(); + } + + pub fn cache_visit(&mut self, tile: Tile) { + self.tiles.visit(tile); + } + pub fn cache_tile_surface(&mut self, tile: Tile, id: SurfaceId, color: skia::Color) { let sampling_options = self.sampling_options; let mut tile_surface = self.tiles.get_or_create(tile).unwrap(); @@ -272,14 +280,26 @@ impl Surfaces { } pub fn remove_cached_tiles(&mut self) { - self.tiles.clear(); + self.tiles.clear_grid(); } } pub struct SurfaceRef { + pub index: usize, + pub in_use: bool, pub surface: skia::Surface, } +impl Clone for SurfaceRef { + fn clone(&self) -> Self { + Self { + index: self.index, + in_use: self.in_use, + surface: self.surface.clone(), + } + } +} + pub struct SurfacePool { pub surfaces: Vec, pub index: usize, @@ -292,25 +312,47 @@ impl SurfacePool { surfaces.push(surface.new_surface_with_dimensions(dims).unwrap()) } - SurfacePool { + Self { index: 0, surfaces: surfaces .into_iter() - .map(|surface| SurfaceRef { surface: surface }) + .enumerate() + .map(|(index, surface)| SurfaceRef { + index, + in_use: false, + surface: surface, + }) .collect(), } } - pub fn allocate(&mut self) -> Result { + pub fn clear(&mut self) { + for surface in self.surfaces.iter_mut() { + surface.in_use = false; + } + } + + pub fn deallocate(&mut self, surface_ref_to_deallocate: &SurfaceRef) { + let surface_ref = self + .surfaces + .get_mut(surface_ref_to_deallocate.index) + .unwrap(); + surface_ref.in_use = false; + } + + pub fn allocate(&mut self) -> Option { let start = self.index; let len = self.surfaces.len(); loop { self.index = (self.index + 1) % len; if self.index == start { - return Err("Not enough surfaces in the pool".into()); + return None; } - if let Some(surface_ref) = self.surfaces.get(self.index) { - return Ok(surface_ref.surface.clone()); + if let Some(surface_ref) = self.surfaces.get_mut(self.index) { + if !surface_ref.in_use { + surface_ref.in_use = true; + return Some(surface_ref.clone()); + } } } } @@ -318,14 +360,16 @@ impl SurfacePool { pub struct TileSurfaceCache { pool: SurfacePool, - grid: HashMap, + grid: HashMap, + visited: HashMap, } impl TileSurfaceCache { pub fn new(pool: SurfacePool) -> Self { - TileSurfaceCache { + Self { pool, grid: HashMap::new(), + visited: HashMap::new(), } } @@ -334,13 +378,30 @@ impl TileSurfaceCache { } pub fn get_or_create(&mut self, tile: Tile) -> Result { - let surface = self.pool.allocate()?; - self.grid.insert(tile, surface.clone()); - Ok(surface) + 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) { + 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()); + } } pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Surface, String> { - Ok(self.grid.get_mut(&tile).unwrap()) + Ok(&mut self.grid.get_mut(&tile).unwrap().surface) } pub fn remove(&mut self, tile: Tile) -> bool { @@ -351,7 +412,16 @@ impl TileSurfaceCache { true } - pub fn clear(&mut self) { + pub fn clear_grid(&mut self) { self.grid.clear(); + self.pool.clear(); + } + + pub fn clear_visited(&mut self) { + self.visited.clear(); + } + + pub fn visit(&mut self, tile: Tile) { + self.visited.insert(tile, true); } }