mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 19:06:18 +00:00
Merge pull request #8926 from penpot/superalex-wasm-render-performance
🎉 Wasm render performance improvements
This commit is contained in:
commit
a87552bc45
@ -369,7 +369,8 @@
|
|||||||
;; in the future (when we handle the UI in the render) should be better to
|
;; in the future (when we handle the UI in the render) should be better to
|
||||||
;; have a "wasm.api/pointer-move" function that works as an entry point for
|
;; have a "wasm.api/pointer-move" function that works as an entry point for
|
||||||
;; all the pointer-move events.
|
;; all the pointer-move events.
|
||||||
(wasm.api/text-editor-pointer-move (.-x off-pt) (.-y off-pt))
|
(when (wasm.api/text-editor-has-focus?)
|
||||||
|
(wasm.api/text-editor-pointer-move (.-x off-pt) (.-y off-pt)))
|
||||||
|
|
||||||
(rx/push! move-stream pt)
|
(rx/push! move-stream pt)
|
||||||
(reset! last-position raw-pt)
|
(reset! last-position raw-pt)
|
||||||
|
|||||||
@ -173,7 +173,6 @@
|
|||||||
(get base-objects parent-id)))))
|
(get base-objects parent-id)))))
|
||||||
|
|
||||||
zoom (d/check-num zoom 1)
|
zoom (d/check-num zoom 1)
|
||||||
prev-zoom (mf/use-ref zoom)
|
|
||||||
|
|
||||||
drawing-tool (:tool drawing)
|
drawing-tool (:tool drawing)
|
||||||
drawing-obj (:object drawing)
|
drawing-obj (:object drawing)
|
||||||
@ -405,8 +404,7 @@
|
|||||||
|
|
||||||
(mf/with-effect [vbox zoom]
|
(mf/with-effect [vbox zoom]
|
||||||
(when (and @canvas-init? initialized?)
|
(when (and @canvas-init? initialized?)
|
||||||
(wasm.api/set-view-box (mf/ref-val prev-zoom) zoom vbox))
|
(wasm.api/set-view-box zoom vbox)))
|
||||||
(mf/set-ref-val! prev-zoom zoom))
|
|
||||||
|
|
||||||
(mf/with-effect [background]
|
(mf/with-effect [background]
|
||||||
(when (and @canvas-init? initialized?)
|
(when (and @canvas-init? initialized?)
|
||||||
|
|||||||
@ -989,34 +989,17 @@
|
|||||||
(render (js/performance.now))))]
|
(render (js/performance.now))))]
|
||||||
(fns/debounce do-render DEBOUNCE_DELAY_MS)))
|
(fns/debounce do-render DEBOUNCE_DELAY_MS)))
|
||||||
|
|
||||||
(def render-pan
|
|
||||||
(letfn [(do-render-pan [ts]
|
|
||||||
;; Check if context is still initialized before executing
|
|
||||||
;; to prevent errors when navigating quickly
|
|
||||||
(when wasm/context-initialized?
|
|
||||||
(perf/begin-measure "render-pan")
|
|
||||||
(render ts)
|
|
||||||
(perf/end-measure "render-pan")))]
|
|
||||||
(fns/throttle do-render-pan THROTTLE_DELAY_MS)))
|
|
||||||
|
|
||||||
(defn set-view-box
|
(defn set-view-box
|
||||||
[prev-zoom zoom vbox]
|
[zoom vbox]
|
||||||
(let [is-pan (mth/close? prev-zoom zoom)]
|
(perf/begin-measure "set-view-box")
|
||||||
(perf/begin-measure "set-view-box")
|
(h/call wasm/internal-module "_set_view_start")
|
||||||
(h/call wasm/internal-module "_set_view_start")
|
(h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
|
||||||
(h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
|
(perf/end-measure "set-view-box")
|
||||||
|
|
||||||
(if is-pan
|
(perf/begin-measure "render-from-cache")
|
||||||
(do (perf/end-measure "set-view-box")
|
(h/call wasm/internal-module "_render_from_cache" 0)
|
||||||
(perf/begin-measure "set-view-box::pan")
|
(render-finish)
|
||||||
(render-pan)
|
(perf/end-measure "render-from-cache"))
|
||||||
(render-finish)
|
|
||||||
(perf/end-measure "set-view-box::pan"))
|
|
||||||
(do (perf/end-measure "set-view-box")
|
|
||||||
(perf/begin-measure "set-view-box::zoom")
|
|
||||||
(h/call wasm/internal-module "_render_from_cache" 0)
|
|
||||||
(render-finish)
|
|
||||||
(perf/end-measure "set-view-box::zoom")))))
|
|
||||||
|
|
||||||
(defn update-text-rect!
|
(defn update-text-rect!
|
||||||
[id]
|
[id]
|
||||||
|
|||||||
@ -31,6 +31,9 @@
|
|||||||
[app.main.errors :as errors]
|
[app.main.errors :as errors]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.render-wasm.helpers :as wasm.h]
|
||||||
|
[app.render-wasm.mem :as wasm.mem]
|
||||||
|
[app.render-wasm.wasm :as wasm]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
@ -117,6 +120,43 @@
|
|||||||
(js/console.log str (json/->js val))
|
(js/console.log str (json/->js val))
|
||||||
val))
|
val))
|
||||||
|
|
||||||
|
(defn- wasm-read-len-prefixed-utf8
|
||||||
|
"Reads a `[u32 byte_len][utf8 bytes...]` buffer returned by WASM and frees it.
|
||||||
|
Returns a JS string (possibly empty)."
|
||||||
|
[ptr]
|
||||||
|
(when (and ptr (not (zero? ptr)))
|
||||||
|
(let [heap-u8 (wasm.mem/get-heap-u8)
|
||||||
|
heap-u32 (wasm.mem/get-heap-u32)
|
||||||
|
len (aget heap-u32 (wasm.mem/->offset-32 ptr))
|
||||||
|
start (+ ptr 4)
|
||||||
|
end (+ start len)
|
||||||
|
decoder (js/TextDecoder. "utf-8")
|
||||||
|
text (.decode decoder (.subarray heap-u8 start end))]
|
||||||
|
(wasm.mem/free)
|
||||||
|
text)))
|
||||||
|
|
||||||
|
(defn ^:export wasmCacheConsole
|
||||||
|
"Logs the current render-wasm cache surface as an image in the JS console."
|
||||||
|
[]
|
||||||
|
(let [module wasm/internal-module
|
||||||
|
f (when module (unchecked-get module "_debug_cache_console"))]
|
||||||
|
(if (fn? f)
|
||||||
|
(wasm.h/call module "_debug_cache_console")
|
||||||
|
(js/console.warn "[debug] render-wasm module not ready or missing _debug_cache_console"))))
|
||||||
|
|
||||||
|
(defn ^:export wasmCacheBase64
|
||||||
|
"Returns the cache surface PNG base64 (empty string if missing/empty)."
|
||||||
|
[]
|
||||||
|
(let [module wasm/internal-module
|
||||||
|
f (when module (unchecked-get module "_debug_cache_base64"))]
|
||||||
|
(if (fn? f)
|
||||||
|
(let [ptr (wasm.h/call module "_debug_cache_base64")
|
||||||
|
s (or (wasm-read-len-prefixed-utf8 ptr) "")]
|
||||||
|
s)
|
||||||
|
(do
|
||||||
|
(js/console.warn "[debug] render-wasm module not ready or missing _debug_cache_base64")
|
||||||
|
""))))
|
||||||
|
|
||||||
(when (exists? js/window)
|
(when (exists? js/window)
|
||||||
(set! (.-dbg ^js js/window) json/->js)
|
(set! (.-dbg ^js js/window) json/->js)
|
||||||
(set! (.-pp ^js js/window) pprint))
|
(set! (.-pp ^js js/window) pprint))
|
||||||
|
|||||||
@ -337,6 +337,9 @@ pub(crate) struct RenderState {
|
|||||||
/// Preview render mode - when true, uses simplified rendering for progressive loading
|
/// Preview render mode - when true, uses simplified rendering for progressive loading
|
||||||
pub preview_mode: bool,
|
pub preview_mode: bool,
|
||||||
pub export_context: Option<(Rect, f32)>,
|
pub export_context: Option<(Rect, f32)>,
|
||||||
|
/// Cleared at the beginning of a render pass; set to true after we clear Cache the first
|
||||||
|
/// time we are about to blit a tile into Cache for this pass.
|
||||||
|
pub cache_cleared_this_render: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
||||||
@ -411,6 +414,7 @@ impl RenderState {
|
|||||||
ignore_nested_blurs: false,
|
ignore_nested_blurs: false,
|
||||||
preview_mode: false,
|
preview_mode: false,
|
||||||
export_context: None,
|
export_context: None,
|
||||||
|
cache_cleared_this_render: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,6 +669,13 @@ impl RenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) -> Result<()> {
|
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) -> Result<()> {
|
||||||
|
// Decide *now* (at the first real cache blit) whether we need to clear Cache.
|
||||||
|
// This avoids clearing Cache on renders that don't actually paint tiles (e.g. hover/UI),
|
||||||
|
// while still preventing stale pixels from surviving across full-quality renders.
|
||||||
|
if !self.options.is_fast_mode() && !self.cache_cleared_this_render {
|
||||||
|
self.surfaces.clear_cache(self.background_color);
|
||||||
|
self.cache_cleared_this_render = true;
|
||||||
|
}
|
||||||
let tile_rect = self.get_current_aligned_tile_bounds()?;
|
let tile_rect = self.get_current_aligned_tile_bounds()?;
|
||||||
self.surfaces.cache_current_tile_texture(
|
self.surfaces.cache_current_tile_texture(
|
||||||
&self.tile_viewbox,
|
&self.tile_viewbox,
|
||||||
@ -1497,6 +1508,7 @@ impl RenderState {
|
|||||||
performance::begin_measure!("render");
|
performance::begin_measure!("render");
|
||||||
performance::begin_measure!("start_render_loop");
|
performance::begin_measure!("start_render_loop");
|
||||||
|
|
||||||
|
self.cache_cleared_this_render = false;
|
||||||
self.reset_canvas();
|
self.reset_canvas();
|
||||||
let surface_ids = SurfaceId::Strokes as u32
|
let surface_ids = SurfaceId::Strokes as u32
|
||||||
| SurfaceId::Fills as u32
|
| SurfaceId::Fills as u32
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
use super::{tiles, RenderState, SurfaceId};
|
use super::{tiles, RenderState, SurfaceId};
|
||||||
|
use crate::with_state_mut;
|
||||||
|
use crate::STATE;
|
||||||
|
use macros::wasm_error;
|
||||||
use skia_safe::{self as skia, Rect};
|
use skia_safe::{self as skia, Rect};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -210,3 +213,13 @@ pub fn console_debug_surface_rect(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;')"))
|
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[wasm_error]
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub extern "C" fn debug_cache_console() -> Result<()> {
|
||||||
|
with_state_mut!(state, {
|
||||||
|
console_debug_surface(state.render_state_mut(), SurfaceId::Cache);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -533,6 +533,15 @@ impl Surfaces {
|
|||||||
self.clear_all_dirty();
|
self.clear_all_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears the whole cache surface without disturbing its configured transform.
|
||||||
|
pub fn clear_cache(&mut self, color: skia::Color) {
|
||||||
|
let canvas = self.cache.canvas();
|
||||||
|
canvas.save();
|
||||||
|
canvas.reset_matrix();
|
||||||
|
canvas.clear(color);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cache_current_tile_texture(
|
pub fn cache_current_tile_texture(
|
||||||
&mut self,
|
&mut self,
|
||||||
tile_viewbox: &TileViewbox,
|
tile_viewbox: &TileViewbox,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user