mirror of
https://github.com/penpot/penpot.git
synced 2026-05-14 20:43:55 +00:00
Merge pull request #9572 from penpot/azazeln28-refactor-target-and-backbuffer-rendering
♻️ Refactor how target and backbuffer works
This commit is contained in:
commit
757aae1df3
@ -165,6 +165,15 @@
|
||||
(js/console.warn "[debug] render-wasm module not ready or missing _debug_atlas_base64")
|
||||
""))))
|
||||
|
||||
(defn ^:export wasmSurfaceConsole
|
||||
"Logs the render-wasm surface id as an image in the JS console."
|
||||
[id]
|
||||
(let [module wasm/internal-module
|
||||
f (when module (unchecked-get module "_debug_surface_console"))]
|
||||
(if (fn? f)
|
||||
(wasm.h/call module "_debug_surface_console" id)
|
||||
(js/console.warn "[debug] render-wasm module not ready or missing _debug_surface_console"))))
|
||||
|
||||
(defn ^:export wasmCacheConsole
|
||||
"Logs the current render-wasm cache surface as an image in the JS console."
|
||||
[]
|
||||
|
||||
@ -804,7 +804,12 @@ impl RenderState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) {
|
||||
self.surfaces.flush(SurfaceId::Backbuffer);
|
||||
}
|
||||
|
||||
pub fn flush_and_submit(&mut self) {
|
||||
self.surfaces.copy_backbuffer_to_target();
|
||||
self.surfaces.flush_and_submit(SurfaceId::Target);
|
||||
}
|
||||
|
||||
@ -816,7 +821,7 @@ impl RenderState {
|
||||
/// This is currently not being used, but it's set there for testing purposes on
|
||||
/// upcoming tasks
|
||||
pub fn render_loading_overlay(&mut self) {
|
||||
let canvas = self.surfaces.canvas(SurfaceId::Target);
|
||||
let canvas = self.surfaces.canvas(SurfaceId::Backbuffer);
|
||||
let skia::ISize { width, height } = canvas.base_layer_size();
|
||||
|
||||
canvas.save();
|
||||
@ -863,8 +868,11 @@ impl RenderState {
|
||||
// the interaction ends.
|
||||
if self.options.is_interactive_transform() {
|
||||
let tile_rect = self.get_current_aligned_tile_bounds()?;
|
||||
self.surfaces
|
||||
.draw_current_tile_direct_target_only(&tile_rect, self.background_color);
|
||||
self.surfaces.draw_current_tile_direct(
|
||||
&tile_rect,
|
||||
self.background_color,
|
||||
surfaces::DrawOnCache::No,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -879,10 +887,12 @@ impl RenderState {
|
||||
// In fast mode the viewport is moving (pan/zoom) so Cache surface
|
||||
// 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()))?;
|
||||
|
||||
self.surfaces.cache_current_tile_texture(
|
||||
&self.tile_viewbox,
|
||||
¤t_tile,
|
||||
@ -1759,7 +1769,7 @@ impl RenderState {
|
||||
// and drawing from it avoids mixing a partially-updated Cache surface with missing tiles.
|
||||
if self.options.is_fast_mode() && self.render_in_progress && self.surfaces.has_atlas() {
|
||||
self.surfaces
|
||||
.draw_atlas_to_target(self.viewbox, self.options.dpr, bg_color);
|
||||
.draw_atlas_to_backbuffer(self.viewbox, self.options.dpr, bg_color);
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render(self);
|
||||
@ -1827,7 +1837,7 @@ impl RenderState {
|
||||
if !cache_covers {
|
||||
// Early return only if atlas exists; otherwise keep cache path.
|
||||
if self.surfaces.has_atlas() {
|
||||
self.surfaces.draw_atlas_to_target(
|
||||
self.surfaces.draw_atlas_to_backbuffer(
|
||||
self.viewbox,
|
||||
self.options.dpr,
|
||||
bg_color,
|
||||
@ -1849,7 +1859,7 @@ impl RenderState {
|
||||
|
||||
// Setup canvas transform
|
||||
{
|
||||
let canvas = self.surfaces.canvas(SurfaceId::Target);
|
||||
let canvas = self.surfaces.canvas(SurfaceId::Backbuffer);
|
||||
canvas.save();
|
||||
canvas.scale((navigate_zoom, navigate_zoom));
|
||||
canvas.translate((translate_x, translate_y));
|
||||
@ -1857,10 +1867,10 @@ impl RenderState {
|
||||
}
|
||||
|
||||
// Draw directly from cache surface, avoiding snapshot overhead
|
||||
self.surfaces.draw_cache_to_target();
|
||||
self.surfaces.draw_cache_to_backbuffer();
|
||||
|
||||
// Restore canvas state
|
||||
self.surfaces.canvas(SurfaceId::Target).restore();
|
||||
self.surfaces.canvas(SurfaceId::Backbuffer).restore();
|
||||
|
||||
// During pure pan (same zoom), draw tiles from the HashMap
|
||||
// on top of the scaled Cache surface. Cached tile textures
|
||||
@ -1967,7 +1977,6 @@ impl RenderState {
|
||||
if !self.interactive_target_seeded {
|
||||
// Seed from the last presented frame; this is stable even when
|
||||
// fast_mode skips cache updates and regardless of atlas coverage.
|
||||
self.surfaces.seed_target_from_backbuffer();
|
||||
self.interactive_target_seeded = true;
|
||||
}
|
||||
} else {
|
||||
@ -2027,6 +2036,7 @@ impl RenderState {
|
||||
self.nested_shadows.clear();
|
||||
// reorder by distance to the center.
|
||||
self.current_tile = None;
|
||||
|
||||
self.render_in_progress = true;
|
||||
|
||||
self.apply_drawing_to_render_canvas(None, SurfaceId::Current);
|
||||
@ -2090,38 +2100,24 @@ impl RenderState {
|
||||
timestamp: i32,
|
||||
) -> Result<()> {
|
||||
performance::begin_measure!("process_animation_frame");
|
||||
self.render_shape_tree_partial(base_object, tree, timestamp, true)?;
|
||||
|
||||
if self.render_in_progress {
|
||||
if tree.len() != 0 {
|
||||
self.render_shape_tree_partial(base_object, tree, timestamp, true)?;
|
||||
}
|
||||
|
||||
// In a pure viewport interaction (pan/zoom), render_from_cache
|
||||
// owns the Target surface — skip flush so we don't present
|
||||
// stale tile positions. The rAF still populates the Cache
|
||||
// surface and tile HashMap so render_from_cache progressively
|
||||
// shows more complete content.
|
||||
//
|
||||
// During interactive shape transforms (drag/resize/rotate) we
|
||||
// still need to flush every rAF so the user sees the updated
|
||||
// shape position — render_from_cache is not in the loop here.
|
||||
if !self.options.is_viewport_interaction() {
|
||||
self.flush_and_submit();
|
||||
}
|
||||
|
||||
if self.render_in_progress {
|
||||
self.cancel_animation_frame();
|
||||
self.render_request_id = Some(wapi::request_animation_frame!());
|
||||
} else {
|
||||
// A full-quality frame is now complete. Refresh Backbuffer and regenerate
|
||||
// the per-shape crop cache so interactive drags can reuse pixels.
|
||||
if !self.options.is_fast_mode() && !self.options.is_interactive_transform() {
|
||||
self.surfaces.copy_target_to_backbuffer();
|
||||
self.rebuild_backbuffer_crop_cache(tree);
|
||||
}
|
||||
wapi::notify_tiles_render_complete!();
|
||||
performance::end_measure!("render");
|
||||
self.flush();
|
||||
self.cancel_animation_frame();
|
||||
self.render_request_id = Some(wapi::request_animation_frame!());
|
||||
} else {
|
||||
// A full-quality frame is now complete. Refresh Backbuffer and regenerate
|
||||
// the per-shape crop cache so interactive drags can reuse pixels.
|
||||
if !self.options.is_fast_mode() && !self.options.is_interactive_transform() {
|
||||
self.surfaces.copy_backbuffer_to_target();
|
||||
self.rebuild_backbuffer_crop_cache(tree);
|
||||
}
|
||||
self.flush_and_submit();
|
||||
wapi::notify_tiles_render_complete!();
|
||||
performance::end_measure!("render");
|
||||
}
|
||||
|
||||
performance::end_measure!("process_animation_frame");
|
||||
Ok(())
|
||||
}
|
||||
@ -2132,9 +2128,7 @@ impl RenderState {
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
) -> Result<()> {
|
||||
if tree.len() != 0 {
|
||||
self.render_shape_tree_partial(base_object, tree, timestamp, false)?;
|
||||
}
|
||||
self.render_shape_tree_partial(base_object, tree, timestamp, false)?;
|
||||
self.flush_and_submit();
|
||||
Ok(())
|
||||
}
|
||||
@ -3308,6 +3302,7 @@ impl RenderState {
|
||||
return Ok(());
|
||||
}
|
||||
performance::end_measure!("render_shape_tree::uncached");
|
||||
|
||||
let tile_rect = self.get_current_tile_bounds()?;
|
||||
// Composite if the walker did work in this PAF (`!is_empty`) OR
|
||||
// the tile has unfinished work from a previous PAF
|
||||
@ -3317,8 +3312,11 @@ 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
|
||||
.draw_current_tile_direct(&tile_rect, self.background_color);
|
||||
self.surfaces.draw_current_tile_direct(
|
||||
&tile_rect,
|
||||
self.background_color,
|
||||
surfaces::DrawOnCache::Yes,
|
||||
);
|
||||
} else {
|
||||
self.apply_render_to_final_canvas(tile_rect)?;
|
||||
}
|
||||
@ -3331,25 +3329,6 @@ impl RenderState {
|
||||
tile_rect,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.surfaces.apply_mut(SurfaceId::Target as u32, |s| {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(self.background_color);
|
||||
s.canvas().draw_rect(tile_rect, &paint);
|
||||
});
|
||||
// Keep Cache surface coherent for render_from_cache.
|
||||
if !self.options.is_fast_mode() {
|
||||
if !self.cache_cleared_this_render {
|
||||
self.surfaces.clear_cache(self.background_color);
|
||||
self.cache_cleared_this_render = true;
|
||||
}
|
||||
let aligned_rect = self.get_aligned_tile_bounds(current_tile);
|
||||
self.surfaces.apply_mut(SurfaceId::Cache as u32, |s| {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(self.background_color);
|
||||
s.canvas().draw_rect(aligned_rect, &paint);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3422,7 +3401,6 @@ impl RenderState {
|
||||
}
|
||||
|
||||
self.render_in_progress = false;
|
||||
|
||||
self.surfaces.gc();
|
||||
|
||||
// Mark cache as valid for render_from_cache.
|
||||
|
||||
@ -187,8 +187,52 @@ pub fn render_debug_shape(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn trap() {
|
||||
run_script!("debugger");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SurfaceBackendKind {
|
||||
BackendTexture, // GPU Framebuffer (Texture)
|
||||
BackendRenderTarget, // GPU Framebuffer (Renderbuffer)
|
||||
Raster, // CPU
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn classify_surface_backend(surface: &mut skia::Surface) -> SurfaceBackendKind {
|
||||
if skia::gpu::surfaces::get_backend_texture(
|
||||
surface,
|
||||
skia_safe::surface::BackendHandleAccess::FlushRead,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return SurfaceBackendKind::BackendTexture;
|
||||
}
|
||||
|
||||
if skia::gpu::surfaces::get_backend_render_target(
|
||||
surface,
|
||||
skia_safe::surface::BackendHandleAccess::FlushRead,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return SurfaceBackendKind::BackendRenderTarget;
|
||||
}
|
||||
|
||||
if surface.peek_pixels().is_some() {
|
||||
return SurfaceBackendKind::Raster;
|
||||
}
|
||||
|
||||
SurfaceBackendKind::Unknown
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
||||
let base64_image = render_state
|
||||
.surfaces
|
||||
@ -198,6 +242,8 @@ pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
||||
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn console_debug_surface_base64(render_state: &mut RenderState, id: SurfaceId) {
|
||||
let base64_image = render_state
|
||||
.surfaces
|
||||
@ -258,3 +304,11 @@ pub extern "C" fn debug_atlas_base64() -> Result<()> {
|
||||
console_debug_surface_base64(get_render_state(), SurfaceId::Atlas);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub extern "C" fn debug_surface_console(id: SurfaceId) -> Result<()> {
|
||||
console_debug_surface(get_render_state(), id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -96,14 +96,6 @@ impl RenderOptions {
|
||||
self.interactive_transform = enabled;
|
||||
}
|
||||
|
||||
/// True only when the viewport is the one being moved (pan/zoom)
|
||||
/// and the dedicated `render_from_cache` path owns Target
|
||||
/// presentation. In this mode `process_animation_frame` must not
|
||||
/// flush to avoid presenting stale tile positions.
|
||||
pub fn is_viewport_interaction(&self) -> bool {
|
||||
self.fast_mode && !self.interactive_transform
|
||||
}
|
||||
|
||||
pub fn is_text_editor_v3(&self) -> bool {
|
||||
self.flags & TEXT_EDITOR_V3 == TEXT_EDITOR_V3
|
||||
}
|
||||
|
||||
@ -26,6 +26,12 @@ const TILE_SIZE_MULTIPLIER: i32 = 2;
|
||||
const MAX_ATLAS_TEXTURE_SIZE: i32 = 4096;
|
||||
const DEFAULT_MAX_ATLAS_TEXTURE_SIZE: i32 = 1024;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DrawOnCache {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum SurfaceId {
|
||||
@ -456,16 +462,21 @@ impl Surfaces {
|
||||
self.atlas_size.width > 0 && self.atlas_size.height > 0
|
||||
}
|
||||
|
||||
/// Draw the persistent atlas onto the target using the current viewbox transform.
|
||||
/// Draw the persistent atlas onto the backbuffer using the current viewbox transform.
|
||||
/// Intended for fast pan/zoom-out previews (avoids per-tile composition).
|
||||
/// Clears Target to `background` first so atlas-uncovered regions don't
|
||||
/// Clears Backbuffer to `background` first so atlas-uncovered regions don't
|
||||
/// show stale content when the atlas only partially covers the viewport.
|
||||
pub fn draw_atlas_to_target(&mut self, viewbox: Viewbox, dpr: f32, background: skia::Color) {
|
||||
pub fn draw_atlas_to_backbuffer(
|
||||
&mut self,
|
||||
viewbox: Viewbox,
|
||||
dpr: f32,
|
||||
background: skia::Color,
|
||||
) {
|
||||
if !self.has_atlas() {
|
||||
return;
|
||||
}
|
||||
|
||||
let canvas = self.target.canvas();
|
||||
let canvas = self.backbuffer.canvas();
|
||||
canvas.save();
|
||||
canvas.reset_matrix();
|
||||
let size = canvas.base_layer_size();
|
||||
@ -597,6 +608,12 @@ impl Surfaces {
|
||||
self.dirty_surfaces = 0;
|
||||
}
|
||||
|
||||
pub fn flush(&mut self, id: SurfaceId) {
|
||||
let gpu_state = get_gpu_state();
|
||||
let surface = self.get_mut(id);
|
||||
gpu_state.context.flush_surface(surface);
|
||||
}
|
||||
|
||||
pub fn flush_and_submit(&mut self, id: SurfaceId) {
|
||||
let gpu_state = get_gpu_state();
|
||||
let surface = self.get_mut(id);
|
||||
@ -614,12 +631,12 @@ impl Surfaces {
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws the cache surface directly to the target canvas.
|
||||
/// Draws the cache surface directly to the backbuffer canvas.
|
||||
/// This avoids creating an intermediate snapshot, reducing GPU stalls.
|
||||
pub fn draw_cache_to_target(&mut self) {
|
||||
pub fn draw_cache_to_backbuffer(&mut self) {
|
||||
let sampling_options = self.sampling_options;
|
||||
self.cache.clone().draw(
|
||||
self.target.canvas(),
|
||||
self.cache.draw(
|
||||
self.backbuffer.canvas(),
|
||||
(0.0, 0.0),
|
||||
sampling_options,
|
||||
Some(&skia::Paint::default()),
|
||||
@ -715,7 +732,7 @@ impl Surfaces {
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface {
|
||||
match id {
|
||||
SurfaceId::Target => &mut self.target,
|
||||
@ -735,6 +752,7 @@ impl Surfaces {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self, id: SurfaceId) -> &skia::Surface {
|
||||
match id {
|
||||
SurfaceId::Target => &self.target,
|
||||
@ -759,23 +777,23 @@ impl Surfaces {
|
||||
(s.width(), s.height())
|
||||
}
|
||||
|
||||
/// Copy the current `Target` contents into the persistent `Backbuffer`.
|
||||
/// Copy the current `Backbuffer` contents into `Target`.
|
||||
/// This is a GPU→GPU copy via Skia (no ReadPixels).
|
||||
pub fn copy_target_to_backbuffer(&mut self) {
|
||||
pub fn copy_backbuffer_to_target(&mut self) {
|
||||
let sampling_options = self.sampling_options;
|
||||
self.target.clone().draw(
|
||||
self.backbuffer.canvas(),
|
||||
self.backbuffer.draw(
|
||||
self.target.canvas(),
|
||||
(0.0, 0.0),
|
||||
sampling_options,
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
}
|
||||
|
||||
/// Seed `Target` from `Backbuffer` (last presented frame).
|
||||
pub fn seed_target_from_backbuffer(&mut self) {
|
||||
/// Seed `Backbuffer` from `Target` (last presented frame).
|
||||
pub fn seed_backbuffer_from_target(&mut self) {
|
||||
let sampling_options = self.sampling_options;
|
||||
self.backbuffer.clone().draw(
|
||||
self.target.canvas(),
|
||||
self.target.draw(
|
||||
self.backbuffer.canvas(),
|
||||
(0.0, 0.0),
|
||||
sampling_options,
|
||||
Some(&skia::Paint::default()),
|
||||
@ -801,7 +819,6 @@ impl Surfaces {
|
||||
.target
|
||||
.new_surface_with_dimensions(dim)
|
||||
.ok_or(Error::CriticalError("Failed to create surface".to_string()))?;
|
||||
// The rest are tile size surfaces
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1052,17 +1069,17 @@ impl Surfaces {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(color);
|
||||
|
||||
self.target.canvas().draw_rect(rect, &paint);
|
||||
self.backbuffer.canvas().draw_rect(rect, &paint);
|
||||
|
||||
self.target
|
||||
self.backbuffer
|
||||
.canvas()
|
||||
.draw_image_rect(&image, None, rect, &skia::Paint::default());
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws a cached tile texture to the Cache surface at the given
|
||||
/// Draws a cached tile texture to the Cache self.backbuffer at the given
|
||||
/// cache-aligned rect. This keeps the Cache surface in sync with
|
||||
/// Target so that `render_from_cache` (used during pan) has the
|
||||
/// Backbuffer so that `render_from_cache` (used during pan) has the
|
||||
/// full scene including tiles served from the texture cache.
|
||||
pub fn draw_cached_tile_to_cache(
|
||||
&mut self,
|
||||
@ -1083,53 +1100,14 @@ impl Surfaces {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the current tile directly to the target and cache surfaces without
|
||||
/// Draws the current tile directly to the backbuffer and cache surfaces without
|
||||
/// creating a snapshot. This avoids GPU stalls from ReadPixels but doesn't
|
||||
/// populate the tile texture cache (suitable for one-shot renders like tests).
|
||||
pub fn draw_current_tile_direct(&mut self, tile_rect: &skia::Rect, color: skia::Color) {
|
||||
let sampling_options = self.sampling_options;
|
||||
let src_rect = IRect::from_xywh(
|
||||
self.margins.width,
|
||||
self.margins.height,
|
||||
self.current.width() - TILE_SIZE_MULTIPLIER * self.margins.width,
|
||||
self.current.height() - TILE_SIZE_MULTIPLIER * self.margins.height,
|
||||
);
|
||||
let src_rect_f = skia::Rect::from(src_rect);
|
||||
|
||||
// Draw background
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(color);
|
||||
self.target.canvas().draw_rect(tile_rect, &paint);
|
||||
|
||||
// Draw current surface directly to target (no snapshot)
|
||||
self.current.clone().draw(
|
||||
self.target.canvas(),
|
||||
(
|
||||
tile_rect.left - src_rect_f.left,
|
||||
tile_rect.top - src_rect_f.top,
|
||||
),
|
||||
sampling_options,
|
||||
None,
|
||||
);
|
||||
|
||||
// Also draw to cache for render_from_cache
|
||||
self.current.clone().draw(
|
||||
self.cache.canvas(),
|
||||
(
|
||||
tile_rect.left - src_rect_f.left,
|
||||
tile_rect.top - src_rect_f.top,
|
||||
),
|
||||
sampling_options,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
/// Same as `draw_current_tile_direct` but draws only into Target.
|
||||
/// Useful during interactive transforms to reduce extra GPU work.
|
||||
pub fn draw_current_tile_direct_target_only(
|
||||
pub fn draw_current_tile_direct(
|
||||
&mut self,
|
||||
tile_rect: &skia::Rect,
|
||||
color: skia::Color,
|
||||
draw_on_cache: DrawOnCache,
|
||||
) {
|
||||
let sampling_options = self.sampling_options;
|
||||
let src_rect = IRect::from_xywh(
|
||||
@ -1140,12 +1118,15 @@ impl Surfaces {
|
||||
);
|
||||
let src_rect_f = skia::Rect::from(src_rect);
|
||||
|
||||
let backbuffer_canvas = self.backbuffer.canvas();
|
||||
// Draw background
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(color);
|
||||
self.target.canvas().draw_rect(tile_rect, &paint);
|
||||
backbuffer_canvas.draw_rect(tile_rect, &paint);
|
||||
|
||||
self.current.clone().draw(
|
||||
self.target.canvas(),
|
||||
// Draw current surface directly to target (no snapshot)
|
||||
self.current.draw(
|
||||
backbuffer_canvas,
|
||||
(
|
||||
tile_rect.left - src_rect_f.left,
|
||||
tile_rect.top - src_rect_f.top,
|
||||
@ -1153,6 +1134,19 @@ impl Surfaces {
|
||||
sampling_options,
|
||||
None,
|
||||
);
|
||||
|
||||
// Also draw to cache for render_from_cache
|
||||
if draw_on_cache == DrawOnCache::Yes {
|
||||
self.current.draw(
|
||||
self.cache.canvas(),
|
||||
(
|
||||
tile_rect.left - src_rect_f.left,
|
||||
tile_rect.top - src_rect_f.top,
|
||||
),
|
||||
sampling_options,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Full cache reset: clears both the tile texture cache and the cache canvas.
|
||||
|
||||
@ -6,21 +6,15 @@ use crate::shapes::{Layout, Type};
|
||||
|
||||
pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::UI);
|
||||
let viewbox = render_state.viewbox;
|
||||
let zoom = viewbox.zoom * render_state.options.dpr;
|
||||
let show_grid_id = render_state.show_grid;
|
||||
|
||||
canvas.clear(Color4f::new(0.0, 0.0, 0.0, 0.0));
|
||||
canvas.save();
|
||||
|
||||
let viewbox = render_state.viewbox;
|
||||
let zoom = viewbox.zoom * render_state.options.dpr;
|
||||
|
||||
canvas.scale((zoom, zoom));
|
||||
|
||||
canvas.translate((-viewbox.area.left, -viewbox.area.top));
|
||||
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::UI);
|
||||
|
||||
let show_grid_id = render_state.show_grid;
|
||||
|
||||
if let Some(id) = show_grid_id {
|
||||
if let Some(shape) = shapes.get(&id) {
|
||||
grid_layout::render_overlay(
|
||||
@ -67,6 +61,7 @@ pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
|
||||
render_state.surfaces.draw_into(
|
||||
SurfaceId::UI,
|
||||
SurfaceId::Target,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user