From 0b5e915af9fe8b882d13097838505fc99739b874 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 22 Nov 2024 08:47:06 +0100 Subject: [PATCH] :tada: Improve performace for zoom and pan with wasm render --- frontend/src/app/render_wasm/api.cljs | 9 +++-- render-wasm/src/main.rs | 8 ++++- render-wasm/src/render.rs | 48 ++++++++++++++++++--------- render-wasm/src/state.rs | 10 ++++-- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 05e0088f9..d26b309dd 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -25,6 +25,11 @@ (h/call internal-module "_render") (set! internal-frame-id nil)) +(defn- render-without-cache + [_] + (h/call internal-module "_render_without_cache") + (set! internal-frame-id nil)) + (defn cancel-render [] (when internal-frame-id @@ -120,13 +125,13 @@ ;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html (h/call internal-module "_set_shape_blend_mode" (translate-blend-mode blend-mode))) -(def debounce-render (fns/debounce render 100)) +(def debounce-render-without-cache (fns/debounce render-without-cache 100)) (defn set-view [zoom vbox] (h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox))) (h/call internal-module "_navigate") - (debounce-render)) + (debounce-render-without-cache)) (defn set-objects [objects] diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 44ab0635d..e8a629d9c 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -41,7 +41,13 @@ pub extern "C" fn init(width: i32, height: i32, debug: u32) { #[no_mangle] pub unsafe extern "C" fn render() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); - state.render_all(); + state.render_all(true); +} + +#[no_mangle] +pub unsafe extern "C" fn render_without_cache() { + let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); + state.render_all(false); } #[no_mangle] diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index fae639c3b..fec208774 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,4 +1,5 @@ use skia::gpu::{self, gl::FramebufferInfo, DirectContext}; +use skia::Contains; use skia_safe as skia; use std::collections::HashMap; use uuid::Uuid; @@ -54,6 +55,8 @@ impl GpuState { pub(crate) struct CachedSurfaceImage { pub image: Image, pub viewbox: Viewbox, + // is_complete indicates if stored image renders the complete shape tree + pub is_complete: bool, } pub(crate) struct RenderState { @@ -176,14 +179,17 @@ impl RenderState { self.reset_canvas(); if let Some(cached_surface_image) = &self.cached_surface_image { // If we are drawing something bigger than the visible let's do a redraw - if (viewbox.x > cached_surface_image.viewbox.x) - || (-viewbox.x + viewbox.area.width() - > -cached_surface_image.viewbox.x + cached_surface_image.viewbox.area.width()) - || (viewbox.y > cached_surface_image.viewbox.y) - || (-viewbox.y + viewbox.area.height() - > -cached_surface_image.viewbox.y + cached_surface_image.viewbox.area.height()) + if !cached_surface_image.is_complete + && ((viewbox.x > cached_surface_image.viewbox.x) + || (-viewbox.x + viewbox.area.width() + > -cached_surface_image.viewbox.x + + cached_surface_image.viewbox.area.width()) + || (viewbox.y > cached_surface_image.viewbox.y) + || (-viewbox.y + viewbox.area.height() + > -cached_surface_image.viewbox.y + + cached_surface_image.viewbox.area.height())) { - self.render_all(viewbox, shapes, debug); + self.render_all(viewbox, shapes, true, debug); } else { let image = &cached_surface_image.image; let paint = skia::Paint::default(); @@ -218,16 +224,20 @@ impl RenderState { &mut self, viewbox: &Viewbox, shapes: &HashMap, + generate_cached_surface_image: bool, debug: u32, // Debug flags ) { self.reset_canvas(); self.scale(viewbox.zoom, viewbox.zoom); self.translate(viewbox.x, viewbox.y); - self.render_shape_tree(&Uuid::nil(), viewbox, shapes); - self.cached_surface_image = Some(CachedSurfaceImage { - image: self.final_surface.image_snapshot(), - viewbox: viewbox.clone(), - }); + let is_complete = self.render_shape_tree(&Uuid::nil(), viewbox, shapes); + if generate_cached_surface_image || self.cached_surface_image.is_none() { + self.cached_surface_image = Some(CachedSurfaceImage { + image: self.final_surface.image_snapshot(), + viewbox: viewbox.clone(), + is_complete, + }); + } if debug & debug::DEBUG_VISIBLE == debug::DEBUG_VISIBLE { self.render_debug(viewbox); } @@ -278,15 +288,22 @@ impl RenderState { ); } - fn render_shape_tree(&mut self, id: &Uuid, viewbox: &Viewbox, shapes: &HashMap) { + // Returns a boolean indicating if the viewbox contains the rendered shapes + fn render_shape_tree( + &mut self, + id: &Uuid, + viewbox: &Viewbox, + shapes: &HashMap, + ) -> bool { let shape = shapes.get(&id).unwrap(); + let mut is_complete = viewbox.area.contains(shape.selrect); if !id.is_nil() { if !shape.selrect.intersects(viewbox.area) { self.render_debug_shape(shape, false); // TODO: This means that not all the shapes are renderer so we // need to call a render_all on the zoom out. - return; + return is_complete; } else { self.render_debug_shape(shape, true); } @@ -303,10 +320,11 @@ impl RenderState { // draw all the children shapes let shape_ids = shape.children.iter(); for shape_id in shape_ids { - self.render_shape_tree(shape_id, viewbox, shapes); + is_complete = self.render_shape_tree(shape_id, viewbox, shapes) && is_complete; } self.final_surface.canvas().restore(); self.drawing_surface.canvas().restore(); + return is_complete; } } diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 903ebdb6e..2f602c970 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -53,9 +53,13 @@ impl<'a> State<'a> { .navigate(&self.viewbox, &self.shapes, self.debug); } - pub fn render_all(&mut self) { - self.render_state - .render_all(&self.viewbox, &self.shapes, self.debug); + pub fn render_all(&mut self, generate_cached_surface_image: bool) { + self.render_state.render_all( + &self.viewbox, + &self.shapes, + generate_cached_surface_image, + self.debug, + ); } pub fn use_shape(&'a mut self, id: Uuid) {