diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 8aa067dae2..050f8820f4 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1052,10 +1052,16 @@ impl RenderState { } pub fn apply_render_to_final_canvas(&mut self) -> Result<()> { + let current_tile = *self + .current_tile + .as_ref() + .ok_or(Error::CriticalError("Current tile not found".to_string()))?; + // During interactive transforms we render tiles directly into Target; updating the cache // (snapshot -> atlas blit -> tiles.add) can force GPU stalls. Defer cache rebuild until // the interaction ends. if self.options.is_interactive_transform() { + self.surfaces.paint_debug_tile_overlay(¤t_tile); let tile_rect = self.get_current_aligned_tile_bounds()?; self.surfaces.draw_current_tile_into_backbuffer( &tile_rect, @@ -1068,6 +1074,7 @@ impl RenderState { // Viewer masked passes render a partial scene. Reusing the tile texture cache would // SrcOver-blend onto textures from the previous pass and leak pixels into the blob. if self.viewer_masked_pass() { + self.surfaces.paint_debug_tile_overlay(¤t_tile); // Use viewbox-aligned bounds (not grid-snapped) to match interactive-transform // compositing and avoid a visible offset vs the DOM canvas. let tile_rect = self.get_current_tile_bounds()?; @@ -1091,11 +1098,6 @@ impl RenderState { // positions would be wrong — only save to the tile HashMap. let tile_rect = self.get_current_aligned_tile_bounds()?; - let current_tile = *self - .current_tile - .as_ref() - .ok_or(Error::CriticalError("Current tile not found".to_string()))?; - if self.tile_atlas_flushed { crate::get_gpu_state().context.flush_and_submit(); } @@ -3845,6 +3847,7 @@ impl RenderState { if self.options.is_interactive_transform() { // During drag, avoid snapshot-based caching. Draw Current directly // into Target (and Cache) to reduce stalls. + self.surfaces.paint_debug_tile_overlay(¤t_tile); self.surfaces.draw_current_tile_into_backbuffer( &tile_rect, self.background_color, diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index b8f4b51c9d..b93cd9628e 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -1204,6 +1204,33 @@ impl Surfaces { canvas.restore(); } + /// Debug: semi-transparent tint unique per tile coords, baked into Current + /// before atlas/backbuffer blit so tile boundaries are visible on screen. + pub fn paint_debug_tile_overlay(&mut self, tile: &Tile) { + let canvas = self.current.canvas(); + let rect = skia::Rect::from(TILE_DRAWABLE_RECT); + + // Stable pseudo-random RGB from tile coords (same tile → same color). + let h = (tile.x() as u32) + .wrapping_mul(374761393) + .wrapping_add((tile.y() as u32).wrapping_mul(668265263)) + .wrapping_add(0x9E37_79B9); + let r = ((h >> 0) & 0xFF) as u8; + let g = ((h >> 8) & 0xFF) as u8; + let b = ((h >> 16) & 0xFF) as u8; + + let mut paint = skia::Paint::default(); + paint.set_anti_alias(true); + paint.set_style(skia::PaintStyle::Fill); + paint.set_color(skia::Color::from_argb(96, r, g, b)); + canvas.draw_rect(rect, &paint); + + paint.set_style(skia::PaintStyle::Stroke); + paint.set_stroke_width(2.0); + paint.set_color(skia::Color::from_argb(220, r, g, b)); + canvas.draw_rect(rect, &paint); + } + pub fn draw_current_tile_into_tile_atlas( &mut self, tile_viewbox: &TileViewbox, @@ -1212,6 +1239,8 @@ impl Surfaces { skip_cache_surface: bool, tile_doc_rect: skia::Rect, ) { + self.paint_debug_tile_overlay(tile); + let gpu_state = get_gpu_state(); let src = skia::Rect::from(TILE_DRAWABLE_RECT); let sampling = self.sampling_options;