mirror of
https://github.com/penpot/penpot.git
synced 2025-06-04 02:31:38 +02:00
🐛 Fix tile cache exhaustion
This commit is contained in:
parent
7a4c9d9933
commit
60bc88a075
2 changed files with 86 additions and 14 deletions
|
@ -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;
|
||||
|
|
|
@ -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<SurfaceRef>,
|
||||
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<skia::Surface, String> {
|
||||
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<SurfaceRef> {
|
||||
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<Tile, skia::Surface>,
|
||||
grid: HashMap<Tile, SurfaceRef>,
|
||||
visited: HashMap<Tile, bool>,
|
||||
}
|
||||
|
||||
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<skia::Surface, String> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue