mirror of
https://github.com/penpot/penpot.git
synced 2025-05-31 17:36:13 +02:00
Merge pull request #6313 from penpot/superalex-improve-images-performance-wasm
🎉 Improve images performance
This commit is contained in:
commit
484772e3b2
5 changed files with 95 additions and 225 deletions
|
@ -29,6 +29,7 @@
|
||||||
[app.render-wasm.wasm :as wasm]
|
[app.render-wasm.wasm :as wasm]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
|
[app.util.perf :as uperf]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
|
@ -93,11 +94,9 @@
|
||||||
(rds/renderToStaticMarkup)))
|
(rds/renderToStaticMarkup)))
|
||||||
|
|
||||||
;; This should never be called from the outside.
|
;; This should never be called from the outside.
|
||||||
;; This function receives a "time" parameter that we're not using but maybe in the future could be useful (it is the time since
|
|
||||||
;; the window started rendering elements so it could be useful to measure time between frames).
|
|
||||||
(defn- render
|
(defn- render
|
||||||
[_]
|
[timestamp]
|
||||||
(h/call wasm/internal-module "_render")
|
(h/call wasm/internal-module "_render" timestamp)
|
||||||
(set! wasm/internal-frame-id nil))
|
(set! wasm/internal-frame-id nil))
|
||||||
|
|
||||||
|
|
||||||
|
@ -613,7 +612,7 @@
|
||||||
(defn set-view-box
|
(defn set-view-box
|
||||||
[zoom vbox]
|
[zoom vbox]
|
||||||
(h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
|
(h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
|
||||||
(render nil))
|
(render (uperf/now)))
|
||||||
|
|
||||||
(defn clear-drawing-cache []
|
(defn clear-drawing-cache []
|
||||||
(h/call wasm/internal-module "_clear_drawing_cache"))
|
(h/call wasm/internal-module "_clear_drawing_cache"))
|
||||||
|
|
|
@ -146,7 +146,7 @@ impl RenderState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_image(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
|
pub fn add_image(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
|
||||||
self.images.add(id, image_data)
|
self.images.add(id, image_data, &mut self.gpu_state.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_image(&mut self, id: &Uuid) -> bool {
|
pub fn has_image(&mut self, id: &Uuid) -> bool {
|
||||||
|
@ -193,9 +193,7 @@ impl RenderState {
|
||||||
let x = self.current_tile.unwrap().0;
|
let x = self.current_tile.unwrap().0;
|
||||||
let y = self.current_tile.unwrap().1;
|
let y = self.current_tile.unwrap().1;
|
||||||
|
|
||||||
// This caches the current surface into the corresponding tile.
|
self.surfaces.cache_current_tile_texture((x, y));
|
||||||
self.surfaces
|
|
||||||
.cache_tile_surface((x, y), SurfaceId::Current, self.background_color);
|
|
||||||
|
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.draw_cached_tile_surface(self.current_tile.unwrap(), rect);
|
.draw_cached_tile_surface(self.current_tile.unwrap(), rect);
|
||||||
|
|
|
@ -171,15 +171,6 @@ pub fn render(render_state: &mut RenderState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn console_debug_tile_surface(render_state: &mut RenderState, tile: tiles::Tile) {
|
|
||||||
let base64_image = render_state.surfaces.base64_snapshot_tile(tile);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
use crate::math::Rect as MathRect;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
use skia_safe as skia;
|
use skia_safe as skia;
|
||||||
|
use skia_safe::gpu::{surfaces, Budgeted, DirectContext};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub type Image = skia::Image;
|
pub type Image = skia::Image;
|
||||||
|
@ -15,11 +18,41 @@ impl ImageStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
|
pub fn add(
|
||||||
let image_data = skia::Data::new_copy(image_data);
|
&mut self,
|
||||||
|
id: Uuid,
|
||||||
|
image_data: &[u8],
|
||||||
|
context: &mut DirectContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let image_data = unsafe { skia::Data::new_bytes(image_data) };
|
||||||
let image = Image::from_encoded(image_data).ok_or("Error decoding image data")?;
|
let image = Image::from_encoded(image_data).ok_or("Error decoding image data")?;
|
||||||
|
|
||||||
self.images.insert(id, image);
|
let width = image.width();
|
||||||
|
let height = image.height();
|
||||||
|
|
||||||
|
let image_info = skia::ImageInfo::new_n32_premul((width, height), None);
|
||||||
|
let mut surface = surfaces::render_target(
|
||||||
|
context,
|
||||||
|
Budgeted::Yes,
|
||||||
|
&image_info,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.ok_or("Can't create GPU surface")?;
|
||||||
|
|
||||||
|
let dest_rect = MathRect::from_xywh(0.0, 0.0, width as f32, height as f32);
|
||||||
|
|
||||||
|
surface
|
||||||
|
.canvas()
|
||||||
|
.draw_image_rect(&image, None, dest_rect, &skia::Paint::default());
|
||||||
|
|
||||||
|
let gpu_image = surface.image_snapshot();
|
||||||
|
|
||||||
|
// This way we store the image as a texture
|
||||||
|
self.images.insert(id, gpu_image);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
use crate::shapes::Shape;
|
use crate::shapes::Shape;
|
||||||
use crate::view::Viewbox;
|
use crate::view::Viewbox;
|
||||||
use skia_safe::{self as skia, Paint, RRect};
|
use skia_safe::{self as skia, IRect, Paint, RRect};
|
||||||
|
|
||||||
use super::{gpu_state::GpuState, tiles::Tile};
|
use super::{gpu_state::GpuState, tiles::Tile};
|
||||||
|
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const POOL_CAPACITY_MINIMUM: i32 = 32;
|
const TEXTURES_CACHE_CAPACITY: usize = 512;
|
||||||
const POOL_CAPACITY_THRESHOLD: i32 = 4;
|
const TEXTURES_BATCH_DELETE: usize = 32;
|
||||||
|
// This is the amount of extra space we're going to give to all the surfaces to render shapes.
|
||||||
|
// If it's too big it could affect performance.
|
||||||
|
const TILE_SIZE_MULTIPLIER: i32 = 2;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum SurfaceId {
|
pub enum SurfaceId {
|
||||||
|
@ -37,7 +40,7 @@ pub struct Surfaces {
|
||||||
// for drawing debug info.
|
// for drawing debug info.
|
||||||
debug: skia::Surface,
|
debug: skia::Surface,
|
||||||
// for drawing tiles.
|
// for drawing tiles.
|
||||||
tiles: TileSurfaceCache,
|
tiles: TileTextureCache,
|
||||||
sampling_options: skia::SamplingOptions,
|
sampling_options: skia::SamplingOptions,
|
||||||
margins: skia::ISize,
|
margins: skia::ISize,
|
||||||
}
|
}
|
||||||
|
@ -50,13 +53,9 @@ impl Surfaces {
|
||||||
sampling_options: skia::SamplingOptions,
|
sampling_options: skia::SamplingOptions,
|
||||||
tile_dims: skia::ISize,
|
tile_dims: skia::ISize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// This is the amount of extra space we're going
|
|
||||||
// to give to all the surfaces to render shapes.
|
|
||||||
// If it's too big it could affect performance.
|
|
||||||
let extra_tile_size = 2;
|
|
||||||
let extra_tile_dims = skia::ISize::new(
|
let extra_tile_dims = skia::ISize::new(
|
||||||
tile_dims.width * extra_tile_size,
|
tile_dims.width * TILE_SIZE_MULTIPLIER,
|
||||||
tile_dims.height * extra_tile_size,
|
tile_dims.height * TILE_SIZE_MULTIPLIER,
|
||||||
);
|
);
|
||||||
let margins = skia::ISize::new(extra_tile_dims.width / 4, extra_tile_dims.height / 4);
|
let margins = skia::ISize::new(extra_tile_dims.width / 4, extra_tile_dims.height / 4);
|
||||||
|
|
||||||
|
@ -68,12 +67,7 @@ impl Surfaces {
|
||||||
let shape_strokes = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
let shape_strokes = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||||
let debug = target.new_surface_with_dimensions((width, height)).unwrap();
|
let debug = target.new_surface_with_dimensions((width, height)).unwrap();
|
||||||
|
|
||||||
let pool_capacity =
|
let tiles = TileTextureCache::new();
|
||||||
((width / tile_dims.width) * (height / tile_dims.height) * POOL_CAPACITY_THRESHOLD)
|
|
||||||
.max(POOL_CAPACITY_MINIMUM);
|
|
||||||
|
|
||||||
let pool = SurfacePool::with_capacity(&mut target, tile_dims, pool_capacity as usize);
|
|
||||||
let tiles = TileSurfaceCache::new(pool);
|
|
||||||
Surfaces {
|
Surfaces {
|
||||||
target,
|
target,
|
||||||
current,
|
current,
|
||||||
|
@ -82,8 +76,8 @@ impl Surfaces {
|
||||||
shape_fills,
|
shape_fills,
|
||||||
shape_strokes,
|
shape_strokes,
|
||||||
debug,
|
debug,
|
||||||
sampling_options,
|
|
||||||
tiles,
|
tiles,
|
||||||
|
sampling_options,
|
||||||
margins,
|
margins,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,16 +86,6 @@ impl Surfaces {
|
||||||
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height));
|
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base64_snapshot_tile(&mut self, tile: Tile) -> String {
|
|
||||||
let surface = self.tiles.get(tile).unwrap();
|
|
||||||
let image = surface.image_snapshot();
|
|
||||||
let mut context = surface.direct_context();
|
|
||||||
let encoded_image = image
|
|
||||||
.encode(context.as_mut(), skia::EncodedImageFormat::PNG, None)
|
|
||||||
.unwrap();
|
|
||||||
general_purpose::STANDARD.encode(&encoded_image.as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn base64_snapshot(&mut self, id: SurfaceId) -> String {
|
pub fn base64_snapshot(&mut self, id: SurfaceId) -> String {
|
||||||
let surface = self.get_mut(id);
|
let surface = self.get_mut(id);
|
||||||
let image = surface.image_snapshot();
|
let image = surface.image_snapshot();
|
||||||
|
@ -238,26 +222,19 @@ impl Surfaces {
|
||||||
self.tiles.visit(tile);
|
self.tiles.visit(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cache_visited_amount(&self) -> usize {
|
pub fn cache_current_tile_texture(&mut self, tile: Tile) {
|
||||||
self.tiles.visited_amount()
|
let snapshot = self.current.image_snapshot();
|
||||||
}
|
let rect = IRect::from_xywh(
|
||||||
|
self.margins.width,
|
||||||
pub fn cache_visited_capacity(&self) -> usize {
|
self.margins.height,
|
||||||
self.tiles.visited_capacity()
|
snapshot.width() - TILE_SIZE_MULTIPLIER * self.margins.width,
|
||||||
}
|
snapshot.height() - TILE_SIZE_MULTIPLIER * self.margins.height,
|
||||||
|
|
||||||
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();
|
|
||||||
let margins = self.margins;
|
|
||||||
let surface = self.get_mut(id);
|
|
||||||
tile_surface.canvas().clear(color);
|
|
||||||
surface.draw(
|
|
||||||
tile_surface.canvas(),
|
|
||||||
(-margins.width, -margins.height),
|
|
||||||
sampling_options,
|
|
||||||
Some(&skia::Paint::default()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut context = self.current.direct_context();
|
||||||
|
if let Some(snapshot) = snapshot.make_subset(&mut context, &rect) {
|
||||||
|
self.tiles.add(tile, snapshot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_cached_tile_surface(&mut self, tile: Tile) -> bool {
|
pub fn has_cached_tile_surface(&mut self, tile: Tile) -> bool {
|
||||||
|
@ -269,127 +246,25 @@ impl Surfaces {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_cached_tile_surface(&mut self, tile: Tile, rect: skia::Rect) {
|
pub fn draw_cached_tile_surface(&mut self, tile: Tile, rect: skia::Rect) {
|
||||||
let sampling_options = self.sampling_options;
|
let image = self.tiles.get(tile).unwrap();
|
||||||
let tile_surface = self.tiles.get(tile).unwrap();
|
self.target
|
||||||
tile_surface.draw(
|
.canvas()
|
||||||
self.target.canvas(),
|
.draw_image_rect(&image, None, rect, &skia::Paint::default());
|
||||||
(rect.x(), rect.y()),
|
|
||||||
sampling_options,
|
|
||||||
Some(&skia::Paint::default()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_cached_tiles(&mut self) {
|
pub fn remove_cached_tiles(&mut self) {
|
||||||
self.tiles.clear_grid();
|
self.tiles.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SurfaceRef {
|
pub struct TileTextureCache {
|
||||||
pub index: usize,
|
grid: HashMap<Tile, skia::Image>,
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
for _ in 0..capacity {
|
|
||||||
surfaces.push(surface.new_surface_with_dimensions(dims).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
index: 0,
|
|
||||||
surfaces: surfaces
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, surface)| SurfaceRef {
|
|
||||||
index,
|
|
||||||
in_use: false,
|
|
||||||
surface: surface,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
for surface in self.surfaces.iter_mut() {
|
|
||||||
surface.in_use = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.surfaces.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available(&self) -> usize {
|
|
||||||
let mut available: usize = 0;
|
|
||||||
for surface_ref in self.surfaces.iter() {
|
|
||||||
if surface_ref.in_use == false {
|
|
||||||
available += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
available
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deallocate(&mut self, surface_ref_to_deallocate: &SurfaceRef) {
|
|
||||||
let surface_ref = self
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allocate(&mut self) -> Option<SurfaceRef> {
|
|
||||||
let start = self.index;
|
|
||||||
let len = self.surfaces.len();
|
|
||||||
loop {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.index = (self.index + 1) % len;
|
|
||||||
if self.index == start {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TileSurfaceCache {
|
|
||||||
pool: SurfacePool,
|
|
||||||
grid: HashMap<Tile, SurfaceRef>,
|
|
||||||
visited: HashMap<Tile, bool>,
|
visited: HashMap<Tile, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl TileTextureCache {
|
||||||
impl TileSurfaceCache {
|
pub fn new() -> Self {
|
||||||
pub fn new(pool: SurfacePool) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
pool,
|
|
||||||
grid: HashMap::new(),
|
grid: HashMap::new(),
|
||||||
visited: HashMap::new(),
|
visited: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
@ -405,66 +280,40 @@ impl TileSurfaceCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_get_or_create(&mut self, tile: Tile) -> Result<skia::Surface, String> {
|
pub fn add(&mut self, tile: Tile, image: skia::Image) {
|
||||||
// TODO: I don't know yet how to improve this but I don't like it. I think
|
if self.grid.len() > TEXTURES_CACHE_CAPACITY {
|
||||||
// there should be a better solution.
|
let marked: Vec<_> = self
|
||||||
let mut marked = vec![];
|
.grid
|
||||||
for (tile, surface_ref) in self.grid.iter_mut() {
|
.iter_mut()
|
||||||
let exists_as_visited = self.visited.contains_key(tile);
|
.filter_map(|(tile, _)| {
|
||||||
if !exists_as_visited {
|
if !self.visited.contains_key(tile) {
|
||||||
marked.push(tile.clone());
|
Some(tile.clone())
|
||||||
self.pool.deallocate(surface_ref);
|
} else {
|
||||||
continue;
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
let is_visited = self.visited.get(tile).unwrap();
|
.take(TEXTURES_BATCH_DELETE)
|
||||||
if !*is_visited {
|
.collect();
|
||||||
marked.push(tile.clone());
|
self.remove_list(marked);
|
||||||
self.pool.deallocate(surface_ref);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.grid.insert(tile, image);
|
||||||
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<skia::Surface, String> {
|
pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Image, String> {
|
||||||
if let Some(surface_ref) = self.pool.allocate() {
|
let image = self.grid.get_mut(&tile).unwrap();
|
||||||
self.grid.insert(tile, surface_ref.clone());
|
Ok(image)
|
||||||
return Ok(surface_ref.surface.clone());
|
|
||||||
}
|
|
||||||
self.try_get_or_create(tile)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Surface, String> {
|
|
||||||
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 {
|
||||||
if !self.grid.contains_key(&tile) {
|
if !self.grid.contains_key(&tile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let surface_ref_to_deallocate = self.grid.remove(&tile);
|
self.grid.remove(&tile);
|
||||||
self.pool.deallocate(&surface_ref_to_deallocate.unwrap());
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_grid(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.grid.clear();
|
self.grid.clear();
|
||||||
self.pool.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visited_amount(&self) -> usize {
|
|
||||||
self.visited.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visited_capacity(&self) -> usize {
|
|
||||||
self.visited.capacity()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_visited(&mut self) {
|
pub fn clear_visited(&mut self) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue