mirror of
https://github.com/penpot/penpot.git
synced 2026-06-15 20:02:17 +00:00
Merge pull request #10191 from penpot/superalex-fix-viewer-webgl-issues
🐛 Fix some viewer webgl issues
This commit is contained in:
commit
b06942c668
@ -12,6 +12,7 @@
|
||||
[app.render-wasm.wasm :as wasm]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.webapi :as webapi]
|
||||
[goog.events :as events]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]))
|
||||
@ -30,14 +31,16 @@
|
||||
(atom {:os-canvas nil
|
||||
:page-key nil
|
||||
:canvas-w 0
|
||||
:canvas-h 0}))
|
||||
:canvas-h 0
|
||||
:dpr 1}))
|
||||
|
||||
(defn- reset-viewer-snapshot! []
|
||||
(reset! viewer-snapshot
|
||||
{:os-canvas nil
|
||||
:page-key nil
|
||||
:canvas-w 0
|
||||
:canvas-h 0}))
|
||||
:canvas-h 0
|
||||
:dpr 1}))
|
||||
|
||||
(defn- draw-bitmap!
|
||||
[canvas os-canvas object-id vis-w vis-h finish]
|
||||
@ -135,9 +138,12 @@
|
||||
(resolve nil))
|
||||
vis-w (.-width canvas)
|
||||
vis-h (.-height canvas)
|
||||
dpr (wasm.api/get-dpr)
|
||||
snap @viewer-snapshot
|
||||
same-page? (and (some? page-key) (identical? page-key (:page-key snap)))
|
||||
same-size? (and (= vis-w (:canvas-w snap)) (= vis-h (:canvas-h snap)))
|
||||
same-size? (and (= vis-w (:canvas-w snap))
|
||||
(= vis-h (:canvas-h snap))
|
||||
(= dpr (:dpr snap)))
|
||||
os (:os-canvas snap)
|
||||
do-render! (fn [os-canvas]
|
||||
(viewer-do-render! page-objects canvas os-canvas object-id
|
||||
@ -151,7 +157,7 @@
|
||||
(do
|
||||
(when-not same-size?
|
||||
(wasm.api/resize-offscreen-canvas! os vis-w vis-h)
|
||||
(swap! viewer-snapshot assoc :canvas-w vis-w :canvas-h vis-h))
|
||||
(swap! viewer-snapshot assoc :canvas-w vis-w :canvas-h vis-h :dpr dpr))
|
||||
(do-render! os))
|
||||
(let [os-canvas (js/OffscreenCanvas. vis-w vis-h)]
|
||||
(when (wasm.api/initialized?)
|
||||
@ -162,7 +168,8 @@
|
||||
{:os-canvas os-canvas
|
||||
:page-key page-key
|
||||
:canvas-w vis-w
|
||||
:canvas-h vis-h})
|
||||
:canvas-h vis-h
|
||||
:dpr dpr})
|
||||
(wasm.api/initialize-viewport
|
||||
page-objects scale size
|
||||
:background-opacity 0
|
||||
@ -192,50 +199,71 @@
|
||||
(let [key (events/listen section "scroll" (fn [_] (sync!)))]
|
||||
#(events/unlistenByKey key))))))))
|
||||
|
||||
(defn- use-viewer-dpr-key
|
||||
"Bump a counter when browser zoom changes devicePixelRatio so WASM canvases
|
||||
are resized like the workspace viewport."
|
||||
[]
|
||||
(let [dpr-key (mf/use-state 0)]
|
||||
(mf/use-effect
|
||||
(mf/deps [])
|
||||
(fn []
|
||||
(webapi/on-dpr-change (fn [_] (swap! dpr-key inc)))))
|
||||
@dpr-key))
|
||||
|
||||
(defn- use-viewer-wasm-layers!
|
||||
[page-id page-objects size scale frame-id not-fixed-ref fixed-ref
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids]
|
||||
(mf/use-layout-effect
|
||||
(mf/deps page-id page-objects size scale frame-id
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids)
|
||||
(fn []
|
||||
(when (get page-objects frame-id)
|
||||
(->> @wasm.api/module
|
||||
(p/fmap
|
||||
(fn [ready?]
|
||||
(when ready?
|
||||
(let [not-fixed-canvas (mf/ref-val not-fixed-ref)
|
||||
fixed-canvas (mf/ref-val fixed-ref)
|
||||
passes
|
||||
(cond-> []
|
||||
not-fixed-canvas
|
||||
(conj {:canvas not-fixed-canvas
|
||||
:opts (cond-> {}
|
||||
(seq not-fixed-include-ids)
|
||||
(assoc :include-ids not-fixed-include-ids))})
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids delta dpr-key]
|
||||
;; The hot-areas SVG shifts every object by `-(size + delta)` so the frame
|
||||
;; `selrect` lands flush against the overlay snap side, ignoring the extra
|
||||
;; padding reserved for shadows/blur/strokes. Bake the same `delta` into the
|
||||
;; WASM view origin so the rendered canvas aligns with that SVG (otherwise it
|
||||
;; appears offset by the shadow margin).
|
||||
(let [render-size (-> size
|
||||
(update :x + (:x delta 0))
|
||||
(update :y + (:y delta 0)))]
|
||||
(mf/use-layout-effect
|
||||
(mf/deps page-id page-objects render-size scale frame-id dpr-key
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids)
|
||||
(fn []
|
||||
(when (get page-objects frame-id)
|
||||
(->> @wasm.api/module
|
||||
(p/fmap
|
||||
(fn [ready?]
|
||||
(when ready?
|
||||
(let [not-fixed-canvas (mf/ref-val not-fixed-ref)
|
||||
fixed-canvas (mf/ref-val fixed-ref)
|
||||
passes
|
||||
(cond-> []
|
||||
not-fixed-canvas
|
||||
(conj {:canvas not-fixed-canvas
|
||||
:opts (cond-> {}
|
||||
(seq not-fixed-include-ids)
|
||||
(assoc :include-ids not-fixed-include-ids))})
|
||||
|
||||
(and fixed-canvas (seq fixed-include-ids))
|
||||
(conj {:canvas fixed-canvas
|
||||
:opts (cond-> {:include-ids fixed-include-ids}
|
||||
(seq fixed-clear-fills-ids)
|
||||
(assoc :clear-fills-ids fixed-clear-fills-ids))}))]
|
||||
(when (seq passes)
|
||||
(enqueue-wasm-render!
|
||||
(fn []
|
||||
(reduce (fn [chain {:keys [canvas opts]}]
|
||||
(p/then chain
|
||||
#(render-viewer-frame* page-id page-objects
|
||||
canvas size scale frame-id
|
||||
nil opts)))
|
||||
(p/resolved nil)
|
||||
passes)))))))))))))
|
||||
(and fixed-canvas (seq fixed-include-ids))
|
||||
(conj {:canvas fixed-canvas
|
||||
:opts (cond-> {:include-ids fixed-include-ids}
|
||||
(seq fixed-clear-fills-ids)
|
||||
(assoc :clear-fills-ids fixed-clear-fills-ids))}))]
|
||||
(when (seq passes)
|
||||
(enqueue-wasm-render!
|
||||
(fn []
|
||||
(reduce (fn [chain {:keys [canvas opts]}]
|
||||
(p/then chain
|
||||
#(render-viewer-frame* page-id page-objects
|
||||
canvas render-size scale frame-id
|
||||
nil opts)))
|
||||
(p/resolved nil)
|
||||
passes))))))))))))))
|
||||
|
||||
(defn use-viewer-wasm-viewport!
|
||||
"WASM render passes and fixed-scroll DOM sync for the viewer viewport."
|
||||
[page-id page-objects size scale frame-id
|
||||
not-fixed-ref fixed-ref fixed-scroll-layer-ref
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids]
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids delta]
|
||||
(use-fixed-scroll-sync! (some? fixed-scroll-layer-ref) fixed-scroll-layer-ref)
|
||||
(use-viewer-wasm-layers! page-id page-objects size scale frame-id
|
||||
not-fixed-ref fixed-ref
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids))
|
||||
(let [dpr-key (use-viewer-dpr-key)]
|
||||
(use-viewer-wasm-layers! page-id page-objects size scale frame-id
|
||||
not-fixed-ref fixed-ref
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids
|
||||
delta dpr-key)))
|
||||
|
||||
@ -12,12 +12,17 @@
|
||||
[app.main.render-viewer-wasm :as rwv]
|
||||
[app.main.ui.viewer.shapes :as shapes]
|
||||
[app.main.ui.viewer.viewport-common :as vpc]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- canvas-dimensions
|
||||
"Physical canvas pixels (CSS layout size × DPR), matching the workspace WASM path."
|
||||
[scale size]
|
||||
{:width (js/Math.round (* scale (:base-width size)))
|
||||
:height (js/Math.round (* scale (:base-height size)))})
|
||||
(let [css-w (js/Math.round (* scale (:base-width size)))
|
||||
css-h (js/Math.round (* scale (:base-height size)))
|
||||
dpr (wasm.api/get-dpr)]
|
||||
{:width (js/Math.round (* css-w dpr))
|
||||
:height (js/Math.round (* css-h dpr))}))
|
||||
|
||||
(mf/defc wasm-hotspots-svg*
|
||||
[{:keys [vbox size class prepared prepared-all prepared-frame shape-filter]}]
|
||||
@ -145,7 +150,8 @@
|
||||
page-id objects size scale frame-id
|
||||
not-fixed-wasm-ref fixed-wasm-ref
|
||||
(when has-fixed? fixed-layer-ref)
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids)
|
||||
not-fixed-include-ids fixed-include-ids fixed-clear-fills-ids
|
||||
delta)
|
||||
|
||||
[:& (mf/provider shapes/base-frame-ctx) {:value (get prepared-all (:id base))}
|
||||
[:& (mf/provider shapes/frame-offset-ctx) {:value offset}
|
||||
|
||||
@ -1920,15 +1920,6 @@
|
||||
(h/call wasm/internal-module "_set_view_end")
|
||||
(reset! view-interaction-active? false)))
|
||||
|
||||
(defn resize-offscreen-canvas!
|
||||
"Resize a persistent OffscreenCanvas to new physical-pixel dimensions and
|
||||
update the WASM render surfaces accordingly (via `_resize_viewbox`). The
|
||||
design state (shape pool) is preserved so `set-objects` is not needed again."
|
||||
[canvas new-physical-w new-physical-h]
|
||||
(set! (.-width canvas) new-physical-w)
|
||||
(set! (.-height canvas) new-physical-h)
|
||||
(resize-viewbox (/ new-physical-w dpr) (/ new-physical-h dpr)))
|
||||
|
||||
(defn- debug-flags
|
||||
[]
|
||||
(cond-> 0
|
||||
@ -1939,6 +1930,22 @@
|
||||
(contains? cf/flags :render-wasm-info)
|
||||
(bit-or 2r00000000000000000000000000001000)))
|
||||
|
||||
(defn set-render-options!
|
||||
"Updates WASM render options with a new DPR value."
|
||||
[new-dpr]
|
||||
(h/call wasm/internal-module "_set_render_options" (debug-flags) new-dpr))
|
||||
|
||||
(defn resize-offscreen-canvas!
|
||||
"Resize a persistent OffscreenCanvas to new physical-pixel dimensions and
|
||||
update the WASM render surfaces accordingly (via `_resize_viewbox`). The
|
||||
design state (shape pool) is preserved so `set-objects` is not needed again."
|
||||
[canvas new-physical-w new-physical-h]
|
||||
(let [dpr (get-dpr)]
|
||||
(set! (.-width canvas) new-physical-w)
|
||||
(set! (.-height canvas) new-physical-h)
|
||||
(set-render-options! dpr)
|
||||
(resize-viewbox (/ new-physical-w dpr) (/ new-physical-h dpr))))
|
||||
|
||||
(defn- wasm-get-numeric-value
|
||||
[name]
|
||||
(when-let [raw (let [p (rt/get-params @st/state)]
|
||||
@ -1953,11 +1960,6 @@
|
||||
(let [setter-name (str/concat "_set_" (name param-name))]
|
||||
(h/call wasm/internal-module setter-name value))))
|
||||
|
||||
(defn set-render-options!
|
||||
"Updates WASM render options with a new DPR value."
|
||||
[new-dpr]
|
||||
(h/call wasm/internal-module "_set_render_options" (debug-flags) new-dpr))
|
||||
|
||||
(defn- canvas-css-size
|
||||
"Return canvas size in CSS pixels.
|
||||
|
||||
|
||||
@ -907,6 +907,14 @@ impl RenderState {
|
||||
|
||||
pub fn reset_canvas(&mut self) {
|
||||
self.surfaces.reset(self.background_color);
|
||||
self.surfaces.clear_backbuffer(self.background_color);
|
||||
self.surfaces.clear_target(self.background_color);
|
||||
}
|
||||
|
||||
/// Drop cached tile textures before a one-shot `render_sync_shape` render.
|
||||
pub fn prepare_sync_shape_render(&mut self) {
|
||||
self.surfaces.clear_tile_atlas();
|
||||
self.surfaces.invalidate_tile_cache();
|
||||
}
|
||||
|
||||
/// NOTE:
|
||||
@ -1085,11 +1093,6 @@ impl RenderState {
|
||||
self.include_filter.is_some()
|
||||
}
|
||||
|
||||
fn reset_viewer_masked_surfaces(&mut self) {
|
||||
self.surfaces.clear_backbuffer(self.background_color);
|
||||
self.surfaces.clear_tile_atlas();
|
||||
}
|
||||
|
||||
/// True when the shape or any descendant is whitelisted.
|
||||
pub fn shape_visible_in_include_filter(&self, shape_id: &Uuid, tree: ShapesPoolRef) -> bool {
|
||||
let Some(ref include) = self.include_filter else {
|
||||
@ -2192,13 +2195,6 @@ impl RenderState {
|
||||
self.interactive_target_seeded = false;
|
||||
}
|
||||
|
||||
// Viewer fixed-scroll passes reuse the same WASM context; `reset` does not
|
||||
// clear Backbuffer, so pass 2 would otherwise keep pass-1 pixels in regions
|
||||
// that render no shapes for the current mask. Target is cleared in present_frame.
|
||||
if self.viewer_masked_pass() {
|
||||
self.reset_viewer_masked_surfaces();
|
||||
}
|
||||
|
||||
let surface_ids = SurfaceId::Strokes as u32
|
||||
| SurfaceId::Fills as u32
|
||||
| SurfaceId::InnerShadows as u32
|
||||
|
||||
@ -80,7 +80,9 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn render_sync_shape(&mut self, id: &Uuid, timestamp: i32) -> Result<FrameType> {
|
||||
get_render_state().start_render_loop(Some(id), &self.shapes, timestamp, true)
|
||||
let render_state = get_render_state();
|
||||
render_state.prepare_sync_shape_render();
|
||||
render_state.start_render_loop(Some(id), &self.shapes, timestamp, true)
|
||||
}
|
||||
|
||||
pub fn render_shape_pixels(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user