🐛 Fix artifacts while panning in wasm render

This commit is contained in:
Alejandro Alonso
2025-08-08 08:49:07 +02:00
parent f2c431d029
commit 2f83f22753
3 changed files with 45 additions and 30 deletions

View File

@@ -890,6 +890,20 @@ impl RenderState {
) )
} }
pub fn get_aligned_tile_bounds(&mut self, tile: tiles::Tile) -> Rect {
let scale = self.get_scale();
let start_tile_x =
(self.viewbox.area.left * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
let start_tile_y =
(self.viewbox.area.top * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
Rect::from_xywh(
(tile.0 as f32 * tiles::TILE_SIZE) - start_tile_x,
(tile.1 as f32 * tiles::TILE_SIZE) - start_tile_y,
tiles::TILE_SIZE,
tiles::TILE_SIZE,
)
}
// Returns the bounds of the current tile relative to the viewbox, // Returns the bounds of the current tile relative to the viewbox,
// aligned to the nearest tile grid origin. // aligned to the nearest tile grid origin.
// //
@@ -899,18 +913,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 tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap(); self.get_aligned_tile_bounds(self.current_tile.unwrap())
let scale = self.get_scale();
let start_tile_x =
(self.viewbox.area.left * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
let start_tile_y =
(self.viewbox.area.top * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE;
Rect::from_xywh(
(tile_x as f32 * tiles::TILE_SIZE) - start_tile_x,
(tile_y as f32 * tiles::TILE_SIZE) - start_tile_y,
tiles::TILE_SIZE,
tiles::TILE_SIZE,
)
} }
pub fn render_shape_tree_partial_uncached( pub fn render_shape_tree_partial_uncached(
@@ -1174,29 +1177,32 @@ impl RenderState {
modifiers: &HashMap<Uuid, Matrix>, modifiers: &HashMap<Uuid, Matrix>,
) { ) {
let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape, tree, modifiers); let TileRect(rsx, rsy, rex, rey) = self.get_tiles_for_shape(shape, tree, modifiers);
let old_tiles: HashSet<tiles::Tile> = self
.tiles
.get_tiles_of(shape.id)
.map_or(HashSet::new(), |tiles| tiles.iter().cloned().collect());
let new_tiles: HashSet<tiles::Tile> = (rsx..=rex) let new_tiles: HashSet<tiles::Tile> = (rsx..=rex)
.flat_map(|x| (rsy..=rey).map(move |y| tiles::Tile(x, y))) .flat_map(|x| (rsy..=rey).map(move |y| tiles::Tile(x, y)))
.collect(); .collect();
// Update tiles where the shape was // First, remove the shape from all tiles where it was previously located
if let Some(tiles) = self.tiles.get_tiles_of(shape.id) { for tile in old_tiles {
for tile in tiles.iter() { self.remove_cached_tile_shape(tile, shape.id);
self.surfaces.remove_cached_tile_surface(*tile);
}
// Remove shape from tiles not used
let diff: HashSet<_> = tiles.difference(&new_tiles).cloned().collect();
for tile in diff.iter() {
self.tiles.remove_shape_at(*tile, shape.id);
}
} }
// Update tiles matching the actual selrect // Then, add the shape to the new tiles
for tile in new_tiles { for tile in new_tiles {
self.tiles.add_shape_at(tile, shape.id); self.tiles.add_shape_at(tile, shape.id);
self.surfaces.remove_cached_tile_surface(tile);
} }
} }
pub fn remove_cached_tile_shape(&mut self, tile: tiles::Tile, id: Uuid) {
let rect = self.get_aligned_tile_bounds(tile);
self.surfaces
.remove_cached_tile_surface(tile, rect, self.background_color);
self.tiles.remove_shape_at(tile, id);
}
pub fn rebuild_tiles_shallow( pub fn rebuild_tiles_shallow(
&mut self, &mut self,
tree: &ShapesPool, tree: &ShapesPool,
@@ -1205,7 +1211,7 @@ impl RenderState {
) { ) {
performance::begin_measure!("rebuild_tiles_shallow"); performance::begin_measure!("rebuild_tiles_shallow");
self.tiles.invalidate(); self.tiles.invalidate();
self.surfaces.remove_cached_tiles(); self.surfaces.remove_cached_tiles(self.background_color);
let mut nodes = vec![Uuid::nil()]; let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() { while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) { if let Some(shape) = tree.get(&shape_id) {
@@ -1235,7 +1241,7 @@ impl RenderState {
) { ) {
performance::begin_measure!("rebuild_tiles"); performance::begin_measure!("rebuild_tiles");
self.tiles.invalidate(); self.tiles.invalidate();
self.surfaces.remove_cached_tiles(); self.surfaces.remove_cached_tiles(self.background_color);
let mut nodes = vec![Uuid::nil()]; let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() { while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) { if let Some(shape) = tree.get(&shape_id) {

View File

@@ -303,7 +303,16 @@ impl Surfaces {
self.tiles.has(tile) self.tiles.has(tile)
} }
pub fn remove_cached_tile_surface(&mut self, tile: Tile) -> bool { pub fn remove_cached_tile_surface(
&mut self,
tile: Tile,
rect: skia::Rect,
color: skia::Color,
) -> bool {
// Clear the specific tile area in the cache surface with color
let mut paint = skia::Paint::default();
paint.set_color(color);
self.cache.canvas().draw_rect(rect, &paint);
self.tiles.remove(tile) self.tiles.remove(tile)
} }
@@ -320,8 +329,9 @@ impl Surfaces {
.draw_image_rect(&image, None, rect, &skia::Paint::default()); .draw_image_rect(&image, None, rect, &skia::Paint::default());
} }
pub fn remove_cached_tiles(&mut self) { pub fn remove_cached_tiles(&mut self, color: skia::Color) {
self.tiles.clear(); self.tiles.clear();
self.cache.canvas().clear(color);
} }
} }

View File

@@ -105,8 +105,7 @@ impl State {
for x in rsx..=rex { for x in rsx..=rex {
for y in rsy..=rey { for y in rsy..=rey {
let tile = tiles::Tile(x, y); let tile = tiles::Tile(x, y);
self.render_state.surfaces.remove_cached_tile_surface(tile); self.render_state.remove_cached_tile_shape(tile, id);
self.render_state.tiles.remove_shape_at(tile, id);
} }
} }
} }