🎉 Improve performance reducing unnecessary calls to set-objects

This commit is contained in:
Alejandro Alonso 2025-03-25 16:21:03 +01:00
parent e012046f62
commit 7728d5b317
20 changed files with 360 additions and 180 deletions

View file

@ -85,6 +85,7 @@
[app.main.streams :as ms]
[app.main.worker :as uw]
[app.render-wasm :as wasm]
[app.render-wasm.api :as api]
[app.util.code-gen.style-css :as css]
[app.util.dom :as dom]
[app.util.globals :as ug]
@ -348,8 +349,10 @@
ptk/WatchEvent
(watch [_ state stream]
(log/debug :hint "initialize-workspace" :file-id (dm/str file-id))
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
rparams (rt/get-params state)]
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
rparams (rt/get-params state)
features (features/get-team-enabled-features state)
render-wasm? (contains? features "render-wasm/v1")]
(->> (rx/merge
(rx/of (ntf/hide)
@ -395,6 +398,13 @@
(rx/filter dch/commit?)
(rx/map deref)
(rx/mapcat (fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
(when render-wasm?
(let [added (->> redo-changes
(filter #(= (:type %) :add-obj))
(map :obj))]
(doseq [shape added]
(api/set-object [] shape))))
(if (and save-undo? (seq undo-changes))
(let [entry {:undo-changes undo-changes
:redo-changes redo-changes

View file

@ -292,10 +292,6 @@
(when @canvas-init?
(wasm.api/resize-viewbox (:width vport) (:height vport))))
(mf/with-effect [@canvas-init? base-objects]
(when (and @canvas-init? @initialized?)
(wasm.api/set-objects base-objects)))
(mf/with-effect [@canvas-init? preview-blend]
(when (and @canvas-init? preview-blend)
(wasm.api/request-render "with-effect")))

View file

@ -16,10 +16,10 @@
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.render :as render]
[app.main.store :as st]
[app.main.ui.shapes.text.fontfaces :as fonts]
[app.render-wasm.helpers :as h]
[app.render-wasm.serializers :as sr]
[app.util.debug :as dbg]
@ -155,15 +155,24 @@
(defn set-shape-children
[shape-ids]
(h/call internal-module "_clear_shape_children")
(run! (fn [id]
(let [buffer (uuid/get-u32 id)]
(h/call internal-module "_add_shape_child"
(aget buffer 0)
(aget buffer 1)
(aget buffer 2)
(aget buffer 3))))
shape-ids))
(let [ENTRY-SIZE 16
ptr
(h/call internal-module "_alloc_bytes" (* ENTRY-SIZE (count shape-ids)))
heap
(js/Uint8Array.
(.-buffer (gobj/get ^js internal-module "HEAPU8"))
ptr
(* ENTRY-SIZE (count shape-ids)))]
(loop [entries (seq shape-ids)
offset 0]
(when-not (empty? entries)
(let [id (first entries)]
(.set heap (sr/uuid->u8 id) offset)
(recur (rest entries) (+ offset ENTRY-SIZE)))))
(h/call internal-module "_set_children")))
(defn- get-string-length [string] (+ (count string) 1))
@ -240,7 +249,6 @@
color (:fill-color fill)
gradient (:fill-color-gradient fill)
image (:fill-image fill)]
(cond
(some? color)
(let [rgba (rgba-from-hex color opacity)]
@ -712,34 +720,7 @@
(aget font-id 3)
font-weight font-style font-size)))
(defn set-shape-text-content [content]
(h/call internal-module "_clear_shape_text")
(let [paragraph-set (first (dm/get-prop content :children))
paragraphs (dm/get-prop paragraph-set :children)
total-paragraphs (count paragraphs)]
(loop [index 0]
(when (< index total-paragraphs)
(let [paragraph (nth paragraphs index)
leaves (dm/get-prop paragraph :children)
total-leaves (count leaves)]
(h/call internal-module "_add_text_paragraph")
(loop [index-leaves 0]
(when (< index-leaves total-leaves)
(let [leaf (nth leaves index-leaves)]
(add-text-leaf leaf)
(recur (inc index-leaves))))))
(recur (inc index))))))
(defn set-view-box
[zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(render nil))
(defn clear-drawing-cache []
(h/call internal-module "_clear_drawing_cache"))
(defn- store-all-fonts
(defn- store-fonts
[fonts]
(keep (fn [font]
(let [font-id (dm/get-prop font :font-id)
@ -755,97 +736,126 @@
:weight weight}]
(store-font-id font-data ttf-id))) fonts))
(defn set-fonts
[objects]
(let [fonts (fonts/shapes->fonts (into [] (vals objects)))
pending (into [] (store-all-fonts fonts))]
(->> (rx/from pending)
(rx/mapcat identity)
(rx/reduce conj [])
(rx/subs! (fn [_]
(clear-drawing-cache)
(request-render "set-fonts"))))))
(defn set-shape-text-content [content]
(h/call internal-module "_clear_shape_text")
(let [paragraph-set (first (dm/get-prop content :children))
paragraphs (dm/get-prop paragraph-set :children)
total-paragraphs (count paragraphs)
fonts (fonts/get-content-fonts content)]
(loop [index 0]
(when (< index total-paragraphs)
(let [paragraph (nth paragraphs index)
leaves (dm/get-prop paragraph :children)
total-leaves (count leaves)]
(h/call internal-module "_add_text_paragraph")
(loop [index-leaves 0]
(when (< index-leaves total-leaves)
(let [leaf (nth leaves index-leaves)]
(add-text-leaf leaf)
(recur (inc index-leaves))))))
(recur (inc index))))
(store-fonts fonts)))
(defn set-view-box
[zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(render nil))
(defn clear-drawing-cache []
(h/call internal-module "_clear_drawing_cache"))
(defn update-shape-tiles []
(h/call internal-module "_update_shape_tiles"))
(defn set-object
[objects shape]
(let [id (dm/get-prop shape :id)
parent-id (dm/get-prop shape :parent-id)
type (dm/get-prop shape :type)
masked (dm/get-prop shape :masked-group)
selrect (dm/get-prop shape :selrect)
constraint-h (dm/get-prop shape :constraints-h)
constraint-v (dm/get-prop shape :constraints-v)
clip-content (if (= type :frame)
(not (dm/get-prop shape :show-content))
false)
rotation (dm/get-prop shape :rotation)
transform (dm/get-prop shape :transform)
fills (if (= type :group)
[] (dm/get-prop shape :fills))
strokes (if (= type :group)
[] (dm/get-prop shape :strokes))
children (dm/get-prop shape :shapes)
blend-mode (dm/get-prop shape :blend-mode)
opacity (dm/get-prop shape :opacity)
hidden (dm/get-prop shape :hidden)
content (dm/get-prop shape :content)
blur (dm/get-prop shape :blur)
corners (when (some? (dm/get-prop shape :r1))
[(dm/get-prop shape :r1)
(dm/get-prop shape :r2)
(dm/get-prop shape :r3)
(dm/get-prop shape :r4)])
svg-attrs (dm/get-prop shape :svg-attrs)
shadows (dm/get-prop shape :shadow)]
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-selrect selrect)
(set-constraints-h constraint-h)
(set-constraints-v constraint-v)
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(when (and (= type :group) masked)
(set-masked masked))
(when (some? blur)
(set-shape-blur blur))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-attrs svg-attrs)
(set-shape-path-content content))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(when (some? corners) (set-shape-corners corners))
(when (some? shadows) (set-shape-shadows shadows))
(when (and (= type :text) (some? content))
(set-shape-text-content content))
(when (or (ctl/any-layout? shape)
(ctl/any-layout-immediate-child? objects shape))
(set-layout-child shape))
(when (ctl/flex-layout? shape)
(set-flex-layout shape))
(when (ctl/grid-layout? shape)
(set-grid-layout shape))
(into [] (concat
(if (and (= type :text) (some? content))
(set-shape-text-content content)
[])
(set-shape-fills fills)
(set-shape-strokes strokes)))))
(defn set-objects
[objects]
(set-fonts objects)
(let [shapes (into [] (vals objects))
total-shapes (count shapes)
pending
(loop [index 0 pending []]
(if (< index total-shapes)
(let [shape (nth shapes index)
id (dm/get-prop shape :id)
parent-id (dm/get-prop shape :parent-id)
type (dm/get-prop shape :type)
masked (dm/get-prop shape :masked-group)
selrect (dm/get-prop shape :selrect)
constraint-h (dm/get-prop shape :constraints-h)
constraint-v (dm/get-prop shape :constraints-v)
clip-content (if (= type :frame)
(not (dm/get-prop shape :show-content))
false)
rotation (dm/get-prop shape :rotation)
transform (dm/get-prop shape :transform)
fills (if (= type :group)
[] (dm/get-prop shape :fills))
strokes (if (= type :group)
[] (dm/get-prop shape :strokes))
children (dm/get-prop shape :shapes)
blend-mode (dm/get-prop shape :blend-mode)
opacity (dm/get-prop shape :opacity)
hidden (dm/get-prop shape :hidden)
content (dm/get-prop shape :content)
blur (dm/get-prop shape :blur)
corners (when (some? (dm/get-prop shape :r1))
[(dm/get-prop shape :r1)
(dm/get-prop shape :r2)
(dm/get-prop shape :r3)
(dm/get-prop shape :r4)])
svg-attrs (dm/get-prop shape :svg-attrs)
shadows (dm/get-prop shape :shadow)]
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-selrect selrect)
(set-constraints-h constraint-h)
(set-constraints-v constraint-v)
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(when (and (= type :group) masked)
(set-masked masked))
(when (some? blur)
(set-shape-blur blur))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-attrs svg-attrs)
(set-shape-path-content content))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(when (some? corners) (set-shape-corners corners))
(when (some? shadows) (set-shape-shadows shadows))
(when (and (= type :text) (some? content))
(set-shape-text-content content))
(when (or (ctl/any-layout? shape)
(ctl/any-layout-immediate-child? objects shape))
(set-layout-child shape))
(when (ctl/flex-layout? shape)
(set-flex-layout shape))
(when (ctl/grid-layout? shape)
(set-grid-layout shape))
(let [pending' (concat (set-shape-fills fills) (set-shape-strokes strokes))]
(recur (inc index) (into pending pending'))))
(let [shape (nth shapes index)
pending' (set-object objects shape)]
(recur (inc index) (into pending pending')))
pending))]
(clear-drawing-cache)
(request-render "set-objects")
@ -917,16 +927,16 @@
(if (empty? modifiers)
(h/call internal-module "_clean_modifiers")
(let [ENTRY_SIZE 40
(let [ENTRY-SIZE 40
ptr
(h/call internal-module "_alloc_bytes" (* ENTRY_SIZE (count modifiers)))
(h/call internal-module "_alloc_bytes" (* ENTRY-SIZE (count modifiers)))
heap
(js/Uint8Array.
(.-buffer (gobj/get ^js internal-module "HEAPU8"))
ptr
(* ENTRY_SIZE (count modifiers)))]
(* ENTRY-SIZE (count modifiers)))]
(loop [entries (seq modifiers)
offset 0]
@ -934,7 +944,7 @@
(let [{:keys [id transform]} (first entries)]
(.set heap (sr/uuid->u8 id) offset)
(.set heap (sr/matrix->u8 transform) (+ offset 16))
(recur (rest entries) (+ offset ENTRY_SIZE)))))
(recur (rest entries) (+ offset ENTRY-SIZE)))))
(h/call internal-module "_set_modifiers")

View file

@ -120,13 +120,14 @@
(api/set-shape-clip-content false))
:rotation (api/set-shape-rotation v)
:transform (api/set-shape-transform v)
:fills (api/set-shape-fills v)
:strokes (api/set-shape-strokes v)
:fills (into [] (api/set-shape-fills v))
:strokes (into [] (api/set-shape-strokes v))
:blend-mode (api/set-shape-blend-mode v)
:opacity (api/set-shape-opacity v)
:hidden (api/set-shape-hidden v)
:shapes (api/set-shape-children v)
:blur (api/set-shape-blur v)
:shadow (api/set-shape-shadows v)
:constraints-h (api/set-constraints-h v)
:constraints-v (api/set-constraints-v v)
@ -139,12 +140,17 @@
(api/set-shape-path-content v)
(= (:type self) :svg-raw)
(api/set-shape-svg-raw-content (api/get-static-markup self)))
(api/set-shape-svg-raw-content (api/get-static-markup self))
(= (:type self) :text)
(into [] (api/set-shape-text-content v)))
nil)
;; when something synced with wasm
;; is modified, we need to request
;; a new render.
(api/clear-drawing-cache)
;; TODO: set-wasm-attrs is called twice with every set
;; (println "set-wasm-attrs" (:id self) k v)
(api/update-shape-tiles)
(api/request-render "set-wasm-attrs")))
(defn- impl-assoc