mirror of
https://github.com/penpot/penpot.git
synced 2025-05-30 04:56:11 +02:00
Merge pull request #6490 from penpot/azazeln28-refactor-iteration-performance
♻️ Refactor tile iteration
This commit is contained in:
commit
334d7833d5
6 changed files with 160 additions and 114 deletions
|
@ -1,5 +1,3 @@
|
||||||
use skia_safe as skia;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
mod emscripten;
|
mod emscripten;
|
||||||
mod math;
|
mod math;
|
||||||
|
@ -9,19 +7,21 @@ mod performance;
|
||||||
mod render;
|
mod render;
|
||||||
mod shapes;
|
mod shapes;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod tiles;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod uuid;
|
mod uuid;
|
||||||
mod view;
|
mod view;
|
||||||
mod wapi;
|
mod wapi;
|
||||||
mod wasm;
|
mod wasm;
|
||||||
|
|
||||||
use crate::mem::SerializableResult;
|
|
||||||
use crate::shapes::{BoolType, ConstraintH, ConstraintV, StructureEntry, TransformEntry, Type};
|
|
||||||
use crate::utils::uuid_from_u32_quartet;
|
|
||||||
use crate::uuid::Uuid;
|
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use math::{Bounds, Matrix};
|
use math::{Bounds, Matrix};
|
||||||
|
use mem::SerializableResult;
|
||||||
|
use shapes::{BoolType, ConstraintH, ConstraintV, StructureEntry, TransformEntry, Type};
|
||||||
|
use skia_safe as skia;
|
||||||
use state::State;
|
use state::State;
|
||||||
|
use utils::uuid_from_u32_quartet;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub(crate) static mut STATE: Option<Box<State>> = None;
|
pub(crate) static mut STATE: Option<Box<State>> = None;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,3 @@
|
||||||
use skia_safe::{self as skia, image, Matrix, RRect, Rect};
|
|
||||||
|
|
||||||
use crate::uuid::Uuid;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use crate::performance;
|
|
||||||
use crate::view::Viewbox;
|
|
||||||
use crate::wapi;
|
|
||||||
|
|
||||||
mod blend;
|
mod blend;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod fills;
|
mod fills;
|
||||||
|
@ -19,24 +9,65 @@ mod shadows;
|
||||||
mod strokes;
|
mod strokes;
|
||||||
mod surfaces;
|
mod surfaces;
|
||||||
mod text;
|
mod text;
|
||||||
mod tiles;
|
|
||||||
|
|
||||||
use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type};
|
use skia_safe::{self as skia, image, Matrix, RRect, Rect};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use gpu_state::GpuState;
|
use gpu_state::GpuState;
|
||||||
use options::RenderOptions;
|
use options::RenderOptions;
|
||||||
use surfaces::{SurfaceId, Surfaces};
|
use surfaces::{SurfaceId, Surfaces};
|
||||||
|
|
||||||
|
use crate::performance;
|
||||||
|
use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type};
|
||||||
|
use crate::tiles::{self, TileRect, TileViewbox, TileWithDistance};
|
||||||
|
use crate::uuid::Uuid;
|
||||||
|
use crate::view::Viewbox;
|
||||||
|
use crate::wapi;
|
||||||
|
|
||||||
pub use blend::BlendMode;
|
pub use blend::BlendMode;
|
||||||
pub use fonts::*;
|
pub use fonts::*;
|
||||||
pub use images::*;
|
pub use images::*;
|
||||||
|
|
||||||
// This is the extra are used for tile rendering.
|
// This is the extra are used for tile rendering.
|
||||||
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
|
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
|
||||||
|
const VIEWPORT_DEFAULT_CAPACITY: usize = 24 * 12;
|
||||||
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
||||||
const NODE_BATCH_THRESHOLD: i32 = 10;
|
const NODE_BATCH_THRESHOLD: i32 = 10;
|
||||||
|
|
||||||
struct NodeRenderState {
|
pub struct PendingTiles {
|
||||||
id: Uuid,
|
pub list: Vec<tiles::TileWithDistance>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingTiles {
|
||||||
|
pub fn new_empty() -> Self {
|
||||||
|
Self {
|
||||||
|
list: Vec::with_capacity(VIEWPORT_DEFAULT_CAPACITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, tile_viewbox: &TileViewbox) {
|
||||||
|
self.list.clear();
|
||||||
|
for y in tile_viewbox.interest_rect.1..=tile_viewbox.interest_rect.3 {
|
||||||
|
for x in tile_viewbox.interest_rect.0..=tile_viewbox.interest_rect.2 {
|
||||||
|
let tile = tiles::Tile(x, y);
|
||||||
|
let distance = tiles::manhattan_distance(tile, tile_viewbox.center);
|
||||||
|
self.list.push((x, y, distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<TileWithDistance> {
|
||||||
|
self.list.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort(&mut self) {
|
||||||
|
self.list.sort_by(|a, b| b.2.cmp(&a.2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NodeRenderState {
|
||||||
|
pub id: Uuid,
|
||||||
// We use this bool to keep that we've traversed all the children inside this node.
|
// We use this bool to keep that we've traversed all the children inside this node.
|
||||||
visited_children: bool,
|
visited_children: bool,
|
||||||
// This is used to clip the content of frames.
|
// This is used to clip the content of frames.
|
||||||
|
@ -95,13 +126,14 @@ pub(crate) struct RenderState {
|
||||||
pub current_tile: Option<tiles::Tile>,
|
pub current_tile: Option<tiles::Tile>,
|
||||||
pub sampling_options: skia::SamplingOptions,
|
pub sampling_options: skia::SamplingOptions,
|
||||||
pub render_area: Rect,
|
pub render_area: Rect,
|
||||||
|
pub tile_viewbox: tiles::TileViewbox,
|
||||||
pub tiles: tiles::TileHashMap,
|
pub tiles: tiles::TileHashMap,
|
||||||
pub pending_tiles: Vec<tiles::TileWithDistance>,
|
pub pending_tiles: PendingTiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
||||||
// First we retrieve the extended area of the viewport that we could render.
|
// 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(
|
let TileRect(isx, isy, iex, iey) = tiles::get_tiles_for_viewbox_with_interest(
|
||||||
viewbox,
|
viewbox,
|
||||||
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||||
scale,
|
scale,
|
||||||
|
@ -136,6 +168,7 @@ impl RenderState {
|
||||||
// This is used multiple times everywhere so instead of creating new instances every
|
// This is used multiple times everywhere so instead of creating new instances every
|
||||||
// time we reuse this one.
|
// time we reuse this one.
|
||||||
|
|
||||||
|
let viewbox = Viewbox::new(width as f32, height as f32);
|
||||||
let tiles = tiles::TileHashMap::new();
|
let tiles = tiles::TileHashMap::new();
|
||||||
|
|
||||||
RenderState {
|
RenderState {
|
||||||
|
@ -143,7 +176,7 @@ impl RenderState {
|
||||||
options: RenderOptions::default(),
|
options: RenderOptions::default(),
|
||||||
surfaces,
|
surfaces,
|
||||||
fonts,
|
fonts,
|
||||||
viewbox: Viewbox::new(width as f32, height as f32),
|
viewbox,
|
||||||
cached_viewbox: Viewbox::new(0., 0.),
|
cached_viewbox: Viewbox::new(0., 0.),
|
||||||
cached_target_snapshot: None,
|
cached_target_snapshot: None,
|
||||||
images: ImageStore::new(),
|
images: ImageStore::new(),
|
||||||
|
@ -155,7 +188,12 @@ impl RenderState {
|
||||||
sampling_options,
|
sampling_options,
|
||||||
render_area: Rect::new_empty(),
|
render_area: Rect::new_empty(),
|
||||||
tiles,
|
tiles,
|
||||||
pending_tiles: vec![],
|
tile_viewbox: tiles::TileViewbox::new_with_interest(
|
||||||
|
viewbox,
|
||||||
|
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||||
|
1.0,
|
||||||
|
),
|
||||||
|
pending_tiles: PendingTiles::new_empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +237,7 @@ impl RenderState {
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.resize(&mut self.gpu_state, dpr_width, dpr_height);
|
.resize(&mut self.gpu_state, dpr_width, dpr_height);
|
||||||
self.viewbox.set_wh(width as f32, height as f32);
|
self.viewbox.set_wh(width as f32, height as f32);
|
||||||
|
self.tile_viewbox.update(self.viewbox, self.get_scale());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_and_submit(&mut self) {
|
pub fn flush_and_submit(&mut self) {
|
||||||
|
@ -211,11 +250,12 @@ impl RenderState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) {
|
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) {
|
||||||
let x = self.current_tile.unwrap().0;
|
|
||||||
let y = self.current_tile.unwrap().1;
|
|
||||||
|
|
||||||
let tile_rect = self.get_current_aligned_tile_bounds();
|
let tile_rect = self.get_current_aligned_tile_bounds();
|
||||||
self.surfaces.cache_current_tile_texture((x, y), tile_rect);
|
self.surfaces.cache_current_tile_texture(
|
||||||
|
&self.tile_viewbox,
|
||||||
|
&self.current_tile.unwrap(),
|
||||||
|
&tile_rect,
|
||||||
|
);
|
||||||
|
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.draw_cached_tile_surface(self.current_tile.unwrap(), rect);
|
.draw_cached_tile_surface(self.current_tile.unwrap(), rect);
|
||||||
|
@ -470,11 +510,12 @@ impl RenderState {
|
||||||
navigate_zoom * self.options.dpr(),
|
navigate_zoom * self.options.dpr(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let (start_tile_x, start_tile_y, _, _) = tiles::get_tiles_for_viewbox_with_interest(
|
let TileRect(start_tile_x, start_tile_y, _, _) =
|
||||||
self.cached_viewbox,
|
tiles::get_tiles_for_viewbox_with_interest(
|
||||||
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
self.cached_viewbox,
|
||||||
scale,
|
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||||
);
|
scale,
|
||||||
|
);
|
||||||
let offset_x = self.viewbox.area.left * self.cached_viewbox.zoom;
|
let offset_x = self.viewbox.area.left * self.cached_viewbox.zoom;
|
||||||
let offset_y = self.viewbox.area.top * self.cached_viewbox.zoom;
|
let offset_y = self.viewbox.area.top * self.cached_viewbox.zoom;
|
||||||
|
|
||||||
|
@ -522,13 +563,6 @@ impl RenderState {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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,
|
|
||||||
scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
let viewbox_cache_size = get_cache_size(self.viewbox, scale);
|
let viewbox_cache_size = get_cache_size(self.viewbox, scale);
|
||||||
let cached_viewbox_cache_size = get_cache_size(self.cached_viewbox, scale);
|
let cached_viewbox_cache_size = get_cache_size(self.cached_viewbox, scale);
|
||||||
if viewbox_cache_size != cached_viewbox_cache_size {
|
if viewbox_cache_size != cached_viewbox_cache_size {
|
||||||
|
@ -539,32 +573,15 @@ impl RenderState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then we get the real amount of tiles rendered for the current viewbox.
|
debug::render_debug_tiles_for_viewbox(self);
|
||||||
let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox(self.viewbox, self.options.dpr());
|
|
||||||
debug::render_debug_tiles_for_viewbox(self, isx, isy, iex, iey);
|
|
||||||
let tile_center = ((iex - isx) / 2, (iey - isy) / 2);
|
|
||||||
|
|
||||||
performance::begin_measure!("tile_cache");
|
performance::begin_measure!("tile_cache");
|
||||||
self.pending_tiles = vec![];
|
self.pending_tiles.update(&self.tile_viewbox);
|
||||||
self.surfaces.cache_clear_visited();
|
|
||||||
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));
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
performance::end_measure!("tile_cache");
|
performance::end_measure!("tile_cache");
|
||||||
|
|
||||||
self.pending_nodes = vec![];
|
self.pending_nodes = vec![];
|
||||||
// reorder by distance to the center.
|
// reorder by distance to the center.
|
||||||
self.pending_tiles.sort_by(|a, b| b.2.cmp(&a.2));
|
self.pending_tiles.sort();
|
||||||
self.current_tile = None;
|
self.current_tile = None;
|
||||||
self.render_in_progress = true;
|
self.render_in_progress = true;
|
||||||
self.apply_drawing_to_render_canvas(None);
|
self.apply_drawing_to_render_canvas(None);
|
||||||
|
@ -654,8 +671,7 @@ impl RenderState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_tile_bounds(&mut self) -> Rect {
|
pub fn get_current_tile_bounds(&mut self) -> Rect {
|
||||||
// TODO: check if we need to add dpr to something else here
|
let tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap();
|
||||||
let (tile_x, tile_y) = self.current_tile.unwrap();
|
|
||||||
let scale = self.get_scale();
|
let scale = self.get_scale();
|
||||||
let offset_x = self.viewbox.area.left * scale;
|
let offset_x = self.viewbox.area.left * scale;
|
||||||
let offset_y = self.viewbox.area.top * scale;
|
let offset_y = self.viewbox.area.top * scale;
|
||||||
|
@ -676,7 +692,7 @@ impl RenderState {
|
||||||
// with the global tile grid, which is useful for rendering tiles in a
|
// with the global tile grid, which is useful for rendering tiles in a
|
||||||
/// consistent and predictable layout.
|
/// consistent and predictable layout.
|
||||||
pub fn get_current_aligned_tile_bounds(&mut self) -> Rect {
|
pub fn get_current_aligned_tile_bounds(&mut self) -> Rect {
|
||||||
let (tile_x, tile_y) = self.current_tile.unwrap();
|
let tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap();
|
||||||
let scale = self.get_scale();
|
let scale = self.get_scale();
|
||||||
let start_tile_x =
|
let start_tile_x =
|
||||||
(self.viewbox.area.left * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
|
(self.viewbox.area.left * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
|
||||||
|
@ -877,7 +893,7 @@ impl RenderState {
|
||||||
// let's check if there are more pending nodes
|
// let's check if there are more pending nodes
|
||||||
if let Some(next_tile_with_distance) = self.pending_tiles.pop() {
|
if let Some(next_tile_with_distance) = self.pending_tiles.pop() {
|
||||||
let (x, y, _) = next_tile_with_distance;
|
let (x, y, _) = next_tile_with_distance;
|
||||||
let next_tile = (x, y);
|
let next_tile = tiles::Tile(x, y);
|
||||||
self.update_render_context(next_tile);
|
self.update_render_context(next_tile);
|
||||||
|
|
||||||
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
||||||
|
@ -921,15 +937,15 @@ impl RenderState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tiles_for_shape(&mut self, shape: &Shape) -> (i32, i32, i32, i32) {
|
pub fn get_tiles_for_shape(&mut self, shape: &Shape) -> TileRect {
|
||||||
let tile_size = tiles::get_tile_size(self.get_scale());
|
let tile_size = tiles::get_tile_size(self.viewbox.zoom);
|
||||||
tiles::get_tiles_for_rect(shape.extrect(), tile_size)
|
tiles::get_tiles_for_rect(shape.extrect(), tile_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_tile_for(&mut self, shape: &Shape) {
|
pub fn update_tile_for(&mut self, shape: &Shape) {
|
||||||
let (rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape);
|
let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape);
|
||||||
let new_tiles: HashSet<(i32, i32)> = (rsx..=rex)
|
let new_tiles: HashSet<tiles::Tile> = (rsx..=rex)
|
||||||
.flat_map(|x| (rsy..=rey).map(move |y| (x, y)))
|
.flat_map(|x| (rsy..=rey).map(move |y| tiles::Tile(x, y)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Update tiles where the shape was
|
// Update tiles where the shape was
|
||||||
|
|
|
@ -75,13 +75,8 @@ pub fn render_debug_shape(render_state: &mut RenderState, element: &Shape, inter
|
||||||
.draw_rect(rect, &paint);
|
.draw_rect(rect, &paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_debug_tiles_for_viewbox(
|
pub fn render_debug_tiles_for_viewbox(render_state: &mut RenderState) {
|
||||||
render_state: &mut RenderState,
|
let tiles::TileRect(sx, sy, ex, ey) = render_state.tile_viewbox.interest_rect;
|
||||||
sx: i32,
|
|
||||||
sy: i32,
|
|
||||||
ex: i32,
|
|
||||||
ey: i32,
|
|
||||||
) {
|
|
||||||
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
||||||
let mut paint = skia::Paint::default();
|
let mut paint = skia::Paint::default();
|
||||||
paint.set_style(skia::PaintStyle::Stroke);
|
paint.set_style(skia::PaintStyle::Stroke);
|
||||||
|
@ -102,7 +97,9 @@ pub fn render_debug_viewbox_tiles(render_state: &mut RenderState) {
|
||||||
paint.set_color(skia::Color::from_rgb(255, 0, 127));
|
paint.set_color(skia::Color::from_rgb(255, 0, 127));
|
||||||
paint.set_stroke_width(1.);
|
paint.set_stroke_width(1.);
|
||||||
|
|
||||||
let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox(render_state.viewbox, scale);
|
let tile_size = tiles::get_tile_size(scale);
|
||||||
|
let tiles::TileRect(sx, sy, ex, ey) =
|
||||||
|
tiles::get_tiles_for_rect(render_state.viewbox.area, tile_size);
|
||||||
let str_rect = format!("{} {} {} {}", sx, sy, ex, ey);
|
let str_rect = format!("{} {} {} {}", sx, sy, ex, ey);
|
||||||
|
|
||||||
let debug_font = render_state.fonts.debug_font();
|
let debug_font = render_state.fonts.debug_font();
|
||||||
|
@ -135,11 +132,12 @@ pub fn render_debug_tiles(render_state: &mut RenderState) {
|
||||||
paint.set_color(skia::Color::from_rgb(127, 0, 255));
|
paint.set_color(skia::Color::from_rgb(127, 0, 255));
|
||||||
paint.set_stroke_width(1.);
|
paint.set_stroke_width(1.);
|
||||||
|
|
||||||
let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox(render_state.viewbox, scale);
|
|
||||||
let tile_size = tiles::get_tile_size(scale);
|
let tile_size = tiles::get_tile_size(scale);
|
||||||
|
let tiles::TileRect(sx, sy, ex, ey) =
|
||||||
|
tiles::get_tiles_for_rect(render_state.viewbox.area, tile_size);
|
||||||
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 = tiles::Tile(x, y);
|
||||||
let shape_count = render_state.tiles.get_shapes_at(tile).iter().len();
|
let shape_count = render_state.tiles.get_shapes_at(tile).iter().len();
|
||||||
if shape_count == 0 {
|
if shape_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::shapes::Shape;
|
use crate::shapes::Shape;
|
||||||
use skia_safe::{self as skia, IRect, Paint, RRect};
|
use skia_safe::{self as skia, IRect, Paint, RRect};
|
||||||
|
|
||||||
use super::{gpu_state::GpuState, tiles::Tile, tiles::TILE_SIZE};
|
use super::{gpu_state::GpuState, tiles::Tile, tiles::TileViewbox, tiles::TILE_SIZE};
|
||||||
|
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -241,15 +241,12 @@ impl Surfaces {
|
||||||
.reset_matrix();
|
.reset_matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cache_clear_visited(&mut self) {
|
pub fn cache_current_tile_texture(
|
||||||
self.tiles.clear_visited();
|
&mut self,
|
||||||
}
|
tile_viewbox: &TileViewbox,
|
||||||
|
tile: &Tile,
|
||||||
pub fn cache_visit(&mut self, tile: Tile) {
|
tile_rect: &skia::Rect,
|
||||||
self.tiles.visit(tile);
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cache_current_tile_texture(&mut self, tile: Tile, tile_rect: skia::Rect) {
|
|
||||||
let rect = IRect::from_xywh(
|
let rect = IRect::from_xywh(
|
||||||
self.margins.width,
|
self.margins.width,
|
||||||
self.margins.height,
|
self.margins.height,
|
||||||
|
@ -258,7 +255,7 @@ impl Surfaces {
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(snapshot) = self.current.image_snapshot_with_bounds(rect) {
|
if let Some(snapshot) = self.current.image_snapshot_with_bounds(rect) {
|
||||||
self.tiles.add(tile, snapshot.clone());
|
self.tiles.add(tile_viewbox, tile, snapshot.clone());
|
||||||
self.cache.canvas().draw_image_rect(
|
self.cache.canvas().draw_image_rect(
|
||||||
snapshot.clone(),
|
snapshot.clone(),
|
||||||
None,
|
None,
|
||||||
|
@ -290,14 +287,12 @@ impl Surfaces {
|
||||||
|
|
||||||
pub struct TileTextureCache {
|
pub struct TileTextureCache {
|
||||||
grid: HashMap<Tile, skia::Image>,
|
grid: HashMap<Tile, skia::Image>,
|
||||||
visited: HashMap<Tile, bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileTextureCache {
|
impl TileTextureCache {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
grid: HashMap::new(),
|
grid: HashMap::new(),
|
||||||
visited: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,13 +306,13 @@ impl TileTextureCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, tile: Tile, image: skia::Image) {
|
pub fn add(&mut self, tile_viewbox: &TileViewbox, tile: &Tile, image: skia::Image) {
|
||||||
if self.grid.len() > TEXTURES_CACHE_CAPACITY {
|
if self.grid.len() > TEXTURES_CACHE_CAPACITY {
|
||||||
let marked: Vec<_> = self
|
let marked: Vec<_> = self
|
||||||
.grid
|
.grid
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter_map(|(tile, _)| {
|
.filter_map(|(tile, _)| {
|
||||||
if !self.visited.contains_key(tile) {
|
if !tile_viewbox.is_visible(tile) {
|
||||||
Some(*tile)
|
Some(*tile)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -327,7 +322,7 @@ impl TileTextureCache {
|
||||||
.collect();
|
.collect();
|
||||||
self.remove_list(marked);
|
self.remove_list(marked);
|
||||||
}
|
}
|
||||||
self.grid.insert(tile, image);
|
self.grid.insert(*tile, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Image, String> {
|
pub fn get(&mut self, tile: Tile) -> Result<&mut skia::Image, String> {
|
||||||
|
@ -346,12 +341,4 @@ impl TileTextureCache {
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.grid.clear();
|
self.grid.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_visited(&mut self) {
|
|
||||||
self.visited.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visit(&mut self, tile: Tile) {
|
|
||||||
self.visited.insert(tile, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use skia_safe as skia;
|
||||||
use crate::render::RenderState;
|
use crate::render::RenderState;
|
||||||
use crate::shapes::Shape;
|
use crate::shapes::Shape;
|
||||||
use crate::shapes::StructureEntry;
|
use crate::shapes::StructureEntry;
|
||||||
|
use crate::tiles;
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
/// A pool allocator for `Shape` objects that attempts to minimize memory reallocations.
|
/// A pool allocator for `Shape` objects that attempts to minimize memory reallocations.
|
||||||
|
@ -129,10 +130,10 @@ impl<'a> State<'a> {
|
||||||
pub fn delete_shape(&mut self, id: Uuid) {
|
pub fn delete_shape(&mut self, id: Uuid) {
|
||||||
// We don't really do a self.shapes.remove so that redo/undo keep working
|
// We don't really do a self.shapes.remove so that redo/undo keep working
|
||||||
if let Some(shape) = self.shapes.get(&id) {
|
if let Some(shape) = self.shapes.get(&id) {
|
||||||
let (rsx, rsy, rex, rey) = self.render_state.get_tiles_for_shape(shape);
|
let tiles::TileRect(rsx, rsy, rex, rey) = self.render_state.get_tiles_for_shape(shape);
|
||||||
for x in rsx..=rex {
|
for x in rsx..=rex {
|
||||||
for y in rsy..=rey {
|
for y in rsy..=rey {
|
||||||
let tile = (x, y);
|
let tile = tiles::Tile(x, y);
|
||||||
self.render_state.surfaces.remove_cached_tile_surface(tile);
|
self.render_state.surfaces.remove_cached_tile_surface(tile);
|
||||||
self.render_state.tiles.remove_shape_at(tile, id);
|
self.render_state.tiles.remove_shape_at(tile, id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,52 @@ use indexmap::IndexSet;
|
||||||
use skia_safe as skia;
|
use skia_safe as skia;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
pub type Tile = (i32, i32);
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub struct Tile(pub i32, pub i32);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub struct TileRect(pub i32, pub i32, pub i32, pub i32);
|
||||||
|
|
||||||
|
impl TileRect {
|
||||||
|
pub fn contains(&self, tile: &Tile) -> bool {
|
||||||
|
tile.0 >= self.0 && tile.1 >= self.1 && tile.0 <= self.2 && tile.1 <= self.3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TileViewbox {
|
||||||
|
pub visible_rect: TileRect,
|
||||||
|
pub interest_rect: TileRect,
|
||||||
|
pub interest: i32,
|
||||||
|
pub center: Tile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileViewbox {
|
||||||
|
pub fn new_with_interest(viewbox: Viewbox, interest: i32, scale: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
visible_rect: get_tiles_for_viewbox(viewbox, scale),
|
||||||
|
interest_rect: get_tiles_for_viewbox_with_interest(viewbox, interest, scale),
|
||||||
|
interest,
|
||||||
|
center: get_tile_center_for_viewbox(viewbox, scale),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, viewbox: Viewbox, scale: f32) {
|
||||||
|
self.visible_rect = get_tiles_for_viewbox(viewbox, scale);
|
||||||
|
self.interest_rect = get_tiles_for_viewbox_with_interest(viewbox, self.interest, scale);
|
||||||
|
self.center = get_tile_center_for_viewbox(viewbox, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_visible(&self, tile: &Tile) -> bool {
|
||||||
|
self.visible_rect.contains(tile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type TileWithDistance = (i32, i32, i32);
|
pub type TileWithDistance = (i32, i32, i32);
|
||||||
|
|
||||||
pub const TILE_SIZE: f32 = 512.;
|
pub const TILE_SIZE: f32 = 512.;
|
||||||
|
|
||||||
// @see https://en.wikipedia.org/wiki/Taxicab_geometry
|
// @see https://en.wikipedia.org/wiki/Taxicab_geometry
|
||||||
pub fn manhattan_distance(a: (i32, i32), b: (i32, i32)) -> i32 {
|
pub fn manhattan_distance(a: Tile, b: Tile) -> i32 {
|
||||||
(a.0 - b.0).abs() + (a.1 - b.1).abs()
|
(a.0 - b.0).abs() + (a.1 - b.1).abs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,17 +57,17 @@ pub fn get_tile_dimensions() -> skia::ISize {
|
||||||
(TILE_SIZE as i32, TILE_SIZE as i32).into()
|
(TILE_SIZE as i32, TILE_SIZE as i32).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tiles_for_rect(rect: skia::Rect, tile_size: f32) -> (i32, i32, i32, i32) {
|
pub fn get_tiles_for_rect(rect: skia::Rect, tile_size: f32) -> TileRect {
|
||||||
// start
|
// start
|
||||||
let sx = (rect.left / tile_size).floor() as i32;
|
let sx = (rect.left / tile_size).floor() as i32;
|
||||||
let sy = (rect.top / tile_size).floor() as i32;
|
let sy = (rect.top / tile_size).floor() as i32;
|
||||||
// end
|
// end
|
||||||
let ex = (rect.right / tile_size).floor() as i32;
|
let ex = (rect.right / tile_size).floor() as i32;
|
||||||
let ey = (rect.bottom / tile_size).floor() as i32;
|
let ey = (rect.bottom / tile_size).floor() as i32;
|
||||||
(sx, sy, ex, ey)
|
TileRect(sx, sy, ex, ey)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tiles_for_viewbox(viewbox: Viewbox, scale: f32) -> (i32, i32, i32, i32) {
|
pub fn get_tiles_for_viewbox(viewbox: Viewbox, scale: f32) -> TileRect {
|
||||||
let tile_size = get_tile_size(scale);
|
let tile_size = get_tile_size(scale);
|
||||||
get_tiles_for_rect(viewbox.area, tile_size)
|
get_tiles_for_rect(viewbox.area, tile_size)
|
||||||
}
|
}
|
||||||
|
@ -37,12 +76,17 @@ pub fn get_tiles_for_viewbox_with_interest(
|
||||||
viewbox: Viewbox,
|
viewbox: Viewbox,
|
||||||
interest: i32,
|
interest: i32,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
) -> (i32, i32, i32, i32) {
|
) -> TileRect {
|
||||||
let (sx, sy, ex, ey) = get_tiles_for_viewbox(viewbox, scale);
|
let TileRect(sx, sy, ex, ey) = get_tiles_for_viewbox(viewbox, scale);
|
||||||
(sx - interest, sy - interest, ex + interest, ey + interest)
|
TileRect(sx - interest, sy - interest, ex + interest, ey + interest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tile_pos((x, y): Tile, scale: f32) -> (f32, f32) {
|
pub fn get_tile_center_for_viewbox(viewbox: Viewbox, scale: f32) -> Tile {
|
||||||
|
let TileRect(sx, sy, ex, ey) = get_tiles_for_viewbox(viewbox, scale);
|
||||||
|
Tile((ex - sx) / 2, (ey - sy) / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tile_pos(Tile(x, y): Tile, scale: f32) -> (f32, f32) {
|
||||||
(
|
(
|
||||||
x as f32 * get_tile_size(scale),
|
x as f32 * get_tile_size(scale),
|
||||||
y as f32 * get_tile_size(scale),
|
y as f32 * get_tile_size(scale),
|
Loading…
Add table
Add a link
Reference in a new issue