diff --git a/frontend/resources/wasm-playground/js/lib.js b/frontend/resources/wasm-playground/js/lib.js index 0f27cf83a..d653398a1 100644 --- a/frontend/resources/wasm-playground/js/lib.js +++ b/frontend/resources/wasm-playground/js/lib.js @@ -181,6 +181,18 @@ export function set_parent(id) { Module._set_parent(...buffer); } +function debounce(fn, delay) { + let timeout; + return (...args) => { + clearTimeout(timeout); + timeout = setTimeout(() => fn(...args), delay); + }; +} + +const debouncedRender = debounce(() => { + Module._render(Date.now()); +}, 100); + export function setupInteraction(canvas) { canvas.addEventListener("wheel", (e) => { e.preventDefault(); @@ -191,7 +203,8 @@ export function setupInteraction(canvas) { offsetX -= (mouseX - offsetX) * (zoomFactor - 1); offsetY -= (mouseY - offsetY) * (zoomFactor - 1); Module._set_view(scale, offsetX, offsetY); - Module._render(Date.now()); + Module._render_from_cache(); + debouncedRender(); }); canvas.addEventListener("mousedown", (e) => { @@ -209,7 +222,8 @@ export function setupInteraction(canvas) { lastX = e.offsetX; lastY = e.offsetY; Module._set_view(scale, offsetX, offsetY); - Module._render(Date.now()); + Module._render_from_cache(); + debouncedRender(); } }); diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 9899b5838..ece21a6d1 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -30,8 +30,8 @@ [app.render-wasm.serializers.fills :as sr-fills] [app.render-wasm.wasm :as wasm] [app.util.debug :as dbg] + [app.util.functions :as fns] [app.util.http :as http] - [app.util.perf :as uperf] [app.util.webapi :as wapi] [beicon.v2.core :as rx] [promesa.core :as p] @@ -101,6 +101,8 @@ (h/call wasm/internal-module "_render" timestamp) (set! wasm/internal-frame-id nil)) +(def debounce-render (fns/debounce render 100)) + (defn cancel-render [_] (when wasm/internal-frame-id @@ -662,7 +664,8 @@ (defn set-view-box [zoom vbox] (h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox))) - (render (uperf/now))) + (h/call wasm/internal-module "_render_from_cache") + (debounce-render)) (defn clear-drawing-cache [] (h/call wasm/internal-module "_clear_drawing_cache")) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index a850a9baf..c094c0d5e 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -91,9 +91,19 @@ pub extern "C" fn set_canvas_background(raw_color: u32) { } #[no_mangle] -pub extern "C" fn render(timestamp: i32) { +pub extern "C" fn render(_: i32) { with_state!(state, { - state.start_render_loop(timestamp).expect("Error rendering"); + state + .start_render_loop(performance::get_time()) + .expect("Error rendering"); + }); +} + +#[no_mangle] +pub extern "C" fn render_from_cache(_: i32) { + with_state!(state, { + let render_state = state.render_state(); + render_state.render_from_cache(); }); } @@ -137,17 +147,16 @@ pub extern "C" fn resize_viewbox(width: i32, height: i32) { pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) { with_state!(state, { let render_state = state.render_state(); - let zoom_changed = zoom != render_state.viewbox.zoom; render_state.viewbox.set_all(zoom, x, y); - if zoom_changed { - with_state!(state, { - if state.render_state.options.is_profile_rebuild_tiles() { - state.rebuild_tiles(); - } else { - state.rebuild_tiles_shallow(); - } - }); - } + with_state!(state, { + // We can have renders in progress + state.render_state.cancel_animation_frame(); + if state.render_state.options.is_profile_rebuild_tiles() { + state.rebuild_tiles(); + } else { + state.rebuild_tiles_shallow(); + } + }); }); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 3ef87c40e..5d9709e9d 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -494,8 +494,16 @@ impl RenderState { .update_render_context(self.render_area, self.get_scale()); } - fn render_from_cache(&mut self) { - let scale = self.get_scale(); + pub fn cancel_animation_frame(&mut self) { + if self.render_in_progress { + if let Some(frame_id) = self.render_request_id { + wapi::cancel_animation_frame!(frame_id); + } + } + } + + pub fn render_from_cache(&mut self) { + let scale = self.get_cached_scale(); if let Some(snapshot) = &self.cached_target_snapshot { let canvas = self.surfaces.canvas(SurfaceId::Target); canvas.save(); @@ -525,6 +533,7 @@ impl RenderState { canvas.clear(self.background_color); canvas.draw_image(snapshot, (0, 0), Some(&skia::Paint::default())); canvas.restore(); + self.flush_and_submit(); } } @@ -535,19 +544,12 @@ impl RenderState { structure: &HashMap>, timestamp: i32, ) -> Result<(), String> { - if self.render_in_progress { - if let Some(frame_id) = self.render_request_id { - wapi::cancel_animation_frame!(frame_id); - } - } + let scale = self.get_scale(); + self.tile_viewbox.update(self.viewbox, scale); performance::begin_measure!("render"); performance::begin_measure!("start_render_loop"); - // If we have cached data let's do a fast render from it - self.render_from_cache(); - - let scale = self.get_scale(); self.reset_canvas(); self.surfaces.apply_mut( &[ @@ -601,9 +603,7 @@ impl RenderState { self.flush_and_submit(); if self.render_in_progress { - if let Some(frame_id) = self.render_request_id { - wapi::cancel_animation_frame!(frame_id); - } + self.cancel_animation_frame(); self.render_request_id = Some(wapi::request_animation_frame!()); } else { performance::end_measure!("render"); @@ -1041,4 +1041,8 @@ impl RenderState { pub fn get_scale(&self) -> f32 { self.viewbox.zoom() * self.options.dpr() } + + pub fn get_cached_scale(&self) -> f32 { + self.cached_viewbox.zoom() * self.options.dpr() + } }