mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 20:41:37 +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
|
ey + interest_delta
|
||||||
*/
|
*/
|
||||||
self.pending_tiles = vec![];
|
self.pending_tiles = vec![];
|
||||||
|
self.surfaces.cache_clear_visited();
|
||||||
for y in sy..=ey {
|
for y in sy..=ey {
|
||||||
for x in sx..=ex {
|
for x in sx..=ex {
|
||||||
let tile = (x, y);
|
let tile = (x, y);
|
||||||
self.pending_tiles.push(tile);
|
self.pending_tiles.push(tile);
|
||||||
|
self.surfaces.cache_visit(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.current_tile = None;
|
self.current_tile = None;
|
||||||
|
|
|
@ -238,6 +238,14 @@ impl Surfaces {
|
||||||
.reset_matrix();
|
.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) {
|
pub fn cache_tile_surface(&mut self, tile: Tile, id: SurfaceId, color: skia::Color) {
|
||||||
let sampling_options = self.sampling_options;
|
let sampling_options = self.sampling_options;
|
||||||
let mut tile_surface = self.tiles.get_or_create(tile).unwrap();
|
let mut tile_surface = self.tiles.get_or_create(tile).unwrap();
|
||||||
|
@ -272,14 +280,26 @@ impl Surfaces {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_cached_tiles(&mut self) {
|
pub fn remove_cached_tiles(&mut self) {
|
||||||
self.tiles.clear();
|
self.tiles.clear_grid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SurfaceRef {
|
pub struct SurfaceRef {
|
||||||
|
pub index: usize,
|
||||||
|
pub in_use: bool,
|
||||||
pub surface: skia::Surface,
|
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 struct SurfacePool {
|
||||||
pub surfaces: Vec<SurfaceRef>,
|
pub surfaces: Vec<SurfaceRef>,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
|
@ -292,25 +312,47 @@ impl SurfacePool {
|
||||||
surfaces.push(surface.new_surface_with_dimensions(dims).unwrap())
|
surfaces.push(surface.new_surface_with_dimensions(dims).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfacePool {
|
Self {
|
||||||
index: 0,
|
index: 0,
|
||||||
surfaces: surfaces
|
surfaces: surfaces
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|surface| SurfaceRef { surface: surface })
|
.enumerate()
|
||||||
|
.map(|(index, surface)| SurfaceRef {
|
||||||
|
index,
|
||||||
|
in_use: false,
|
||||||
|
surface: surface,
|
||||||
|
})
|
||||||
.collect(),
|
.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 start = self.index;
|
||||||
let len = self.surfaces.len();
|
let len = self.surfaces.len();
|
||||||
loop {
|
loop {
|
||||||
self.index = (self.index + 1) % len;
|
self.index = (self.index + 1) % len;
|
||||||
if self.index == start {
|
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) {
|
if let Some(surface_ref) = self.surfaces.get_mut(self.index) {
|
||||||
return Ok(surface_ref.surface.clone());
|
if !surface_ref.in_use {
|
||||||
|
surface_ref.in_use = true;
|
||||||
|
return Some(surface_ref.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,14 +360,16 @@ impl SurfacePool {
|
||||||
|
|
||||||
pub struct TileSurfaceCache {
|
pub struct TileSurfaceCache {
|
||||||
pool: SurfacePool,
|
pool: SurfacePool,
|
||||||
grid: HashMap<Tile, skia::Surface>,
|
grid: HashMap<Tile, SurfaceRef>,
|
||||||
|
visited: HashMap<Tile, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileSurfaceCache {
|
impl TileSurfaceCache {
|
||||||
pub fn new(pool: SurfacePool) -> Self {
|
pub fn new(pool: SurfacePool) -> Self {
|
||||||
TileSurfaceCache {
|
Self {
|
||||||
pool,
|
pool,
|
||||||
grid: HashMap::new(),
|
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> {
|
pub fn get_or_create(&mut self, tile: Tile) -> Result<skia::Surface, String> {
|
||||||
let surface = self.pool.allocate()?;
|
if let Some(surface_ref) = self.pool.allocate() {
|
||||||
self.grid.insert(tile, surface.clone());
|
self.grid.insert(tile, surface_ref.clone());
|
||||||
Ok(surface)
|
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> {
|
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 {
|
pub fn remove(&mut self, tile: Tile) -> bool {
|
||||||
|
@ -351,7 +412,16 @@ impl TileSurfaceCache {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear_grid(&mut self) {
|
||||||
self.grid.clear();
|
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