diff --git a/common/src/app/common/buffer.cljc b/common/src/app/common/buffer.cljc index 69413db7db..55ec4f83c8 100644 --- a/common/src/app/common/buffer.cljc +++ b/common/src/app/common/buffer.cljc @@ -127,6 +127,12 @@ (finally (.order ~target ByteOrder/LITTLE_ENDIAN)))))) +(defn wrap + [data] + #?(:clj (let [buffer (ByteBuffer/wrap ^bytes data)] + (.order buffer ByteOrder/LITTLE_ENDIAN)) + :cljs (new js/DataView (.-buffer ^js data)))) + (defn allocate [size] #?(:clj (let [buffer (ByteBuffer/allocate (int size))] diff --git a/common/src/app/common/types/path/impl.cljc b/common/src/app/common/types/path/impl.cljc index 6e513f0057..bae9d4840d 100644 --- a/common/src/app/common/types/path/impl.cljc +++ b/common/src/app/common/types/path/impl.cljc @@ -29,6 +29,7 @@ #?(:clj (set! *warn-on-reflection* true)) (def ^:const SEGMENT-U8-SIZE 28) +(def ^:const SEGMENT-U32-SIZE (/ SEGMENT-U8-SIZE 4)) (defprotocol IPathData (-write-to [_ buffer offset] "write the content to the specified buffer") diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 382320d6ea..74b0ea8c7d 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -530,25 +530,24 @@ nil))))))) modif-tree)) + +(def ^:private xf:parse-geometry-modifier + (let [default-transform (gmt/matrix)] + (keep (fn [[id data]] + (cond + (= id uuid/zero) + nil + + (ctm/has-geometry? (:modifiers data)) + (d/vec2 id (ctm/modifiers->transform (:modifiers data))) + + ;; Unit matrix is used for reflowing + :else + (d/vec2 id default-transform)))))) + (defn- parse-geometry-modifiers [modif-tree] - (into - [] - (keep - (fn [[id data]] - (cond - (= id uuid/zero) - nil - - (ctm/has-geometry? (:modifiers data)) - {:id id - :transform (ctm/modifiers->transform (:modifiers data))} - - ;; Unit matrix is used for reflowing - :else - {:id id - :transform (gmt/matrix)}))) - modif-tree)) + (into [] xf:parse-geometry-modifier modif-tree)) (defn- extract-property-changes [modif-tree] @@ -573,6 +572,8 @@ (update [_ state] (assoc state :workspace-wasm-modifiers modifiers)))) +(def ^:private xf:map-key (map key)) + #_:clj-kondo/ignore (defn set-wasm-modifiers [modif-tree & {:keys [ignore-constraints ignore-snap-pixel] @@ -591,16 +592,17 @@ (watch [_ state _] (wasm.api/clean-modifiers) (let [prev-wasm-props (:prev-wasm-props state) - wasm-props (:wasm-props state) - objects (dsh/lookup-page-objects state) + wasm-props (:wasm-props state) + objects (dsh/lookup-page-objects state) pixel-precision false] (set-wasm-props! objects prev-wasm-props wasm-props) (let [structure-entries (parse-structure-modifiers modif-tree)] (wasm.api/set-structure-modifiers structure-entries) (let [geometry-entries (parse-geometry-modifiers modif-tree) - modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)] + modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)] (wasm.api/set-modifiers modifiers) - (let [selrect (wasm.api/get-selection-rect (->> geometry-entries (map :id)))] + (let [ids (into [] xf:map-key geometry-entries) + selrect (wasm.api/get-selection-rect ids)] (rx/of (set-temporary-selrect selrect) (set-temporary-modifiers modifiers))))))))) @@ -649,16 +651,14 @@ ;; way we don't have to check all the attributes (assoc :attrs transform-attrs)) - geometry-entries (parse-geometry-modifiers modif-tree) + geometry-entries + (parse-geometry-modifiers modif-tree) snap-pixel? (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) transforms - (into - {} - (map (fn [{:keys [id transform]}] [id transform])) - (wasm.api/propagate-modifiers geometry-entries snap-pixel?)) + (into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel?)) modif-tree (propagate-structure-modifiers modif-tree (dsh/lookup-page-objects state)) @@ -709,10 +709,9 @@ (gm/set-objects-modifiers objects)) modifiers - (->> modif-tree - (map (fn [[id {:keys [modifiers]}]] - {:id id - :transform (ctm/modifiers->transform modifiers)})))] + (mapv (fn [[id {:keys [modifiers]}]] + (d/vec2 id (ctm/modifiers->transform modifiers))) + modif-tree)] (wasm.api/set-modifiers modifiers)))))) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 04be0977a7..ee19c45074 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -10,8 +10,6 @@ ["react-dom/server" :as rds] [app.common.data :as d :refer [not-empty?]] [app.common.data.macros :as dm] - [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt] [app.common.types.fills :as types.fills] [app.common.types.fills.impl :as types.fills.impl] [app.common.types.path :as path] @@ -27,6 +25,7 @@ [app.render-wasm.deserializers :as dr] [app.render-wasm.helpers :as h] [app.render-wasm.mem :as mem] + [app.render-wasm.mem.heap32 :as mem.h32] [app.render-wasm.performance :as perf] [app.render-wasm.serializers :as sr] [app.render-wasm.serializers.color :as sr-clr] @@ -41,39 +40,17 @@ (def use-dpr? (contains? cf/flags :render-wasm-dpr)) -;; -;; List of common entry sizes. -;; -;; All of these entries are in bytes so we need to adjust -;; these values to work with TypedArrays of 32 bits. -;; (def ^:const UUID-U8-SIZE 16) (def ^:const UUID-U32-SIZE (/ UUID-U8-SIZE 4)) (def ^:const MODIFIER-U8-SIZE 40) +(def ^:const MODIFIER-U32-SIZE (/ MODIFIER-U8-SIZE 4)) (def ^:const MODIFIER-TRANSFORM-U8-OFFSET-SIZE 16) (def ^:const GRID-LAYOUT-ROW-U8-SIZE 5) (def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 5) (def ^:const GRID-LAYOUT-CELL-U8-SIZE 37) -(defn modifier-get-entries-size - "Returns the list of a modifier list in bytes" - [modifiers] - (mem/get-list-size modifiers MODIFIER-U8-SIZE)) - -(defn grid-layout-get-row-entries-size - [rows] - (mem/get-list-size rows GRID-LAYOUT-ROW-U8-SIZE)) - -(defn grid-layout-get-column-entries-size - [columns] - (mem/get-list-size columns GRID-LAYOUT-COLUMN-U8-SIZE)) - -(defn grid-layout-get-cell-entries-size - [cells] - (mem/get-list-size cells GRID-LAYOUT-CELL-U8-SIZE)) - (def dpr (if use-dpr? (if (exists? js/window) js/window.devicePixelRatio 1.0) 1.0)) @@ -175,20 +152,20 @@ (defn set-shape-children [children] - (let [heap (mem/get-heap-u32) - length (count children)] - (perf/begin-measure "set-shape-children") - (when (pos? length) - (let [offset (mem/alloc->offset-32 (* UUID-U8-SIZE length))] - (reduce (fn [offset id] - (sr/heapu32-set-uuid id heap offset) - (+ offset UUID-U32-SIZE)) - offset - children))) - (let [result (h/call wasm/internal-module "_set_children")] - (perf/end-measure "set-shape-children") - result))) + (when-not ^boolean (empty? children) + (perf/begin-measure "set-shape-children") + (let [heap (mem/get-heap-u32) + size (mem/get-alloc-size children UUID-U8-SIZE) + offset (mem/alloc->offset-32 size)] + (reduce (fn [offset id] + (mem.h32/write-uuid offset heap id)) + offset + children) + + (let [result (h/call wasm/internal-module "_set_children")] + (perf/end-measure "set-shape-children") + result)))) (defn- get-string-length [string] @@ -396,31 +373,38 @@ (h/call wasm/internal-module "_set_shape_blur" type hidden value))) (defn set-shape-corners - [corners] - (let [r1 (or (get corners 0) 0) - r2 (or (get corners 1) 0) - r3 (or (get corners 2) 0) - r4 (or (get corners 3) 0)] - (h/call wasm/internal-module "_set_shape_corners" r1 r2 r3 r4))) + [shape] + (let [r1 (dm/get-prop shape :r1)] + (when (some? r1) + (let [r2 (dm/get-prop shape :r2) + r3 (dm/get-prop shape :r3) + r4 (dm/get-prop shape :r4)] + (h/call wasm/internal-module "_set_shape_corners" + (d/nilv r1 0) + (d/nilv r2 0) + (d/nilv r3 0) + (d/nilv r4 0)))))) (defn set-flex-layout [shape] - (let [dir (-> (or (dm/get-prop shape :layout-flex-dir) :row) sr/translate-layout-flex-dir) - gap (dm/get-prop shape :layout-gap) - row-gap (or (dm/get-prop gap :row-gap) 0) - column-gap (or (dm/get-prop gap :column-gap) 0) + (let [dir (-> (get shape :layout-flex-dir :row) + (sr/translate-layout-flex-dir)) + gap (get shape :layout-gap) + row-gap (get gap :row-gap 0) + column-gap (get gap :column-gap 0) - align-items (-> (or (dm/get-prop shape :layout-align-items) :start) sr/translate-layout-align-items) - align-content (-> (or (dm/get-prop shape :layout-align-content) :stretch) sr/translate-layout-align-content) - justify-items (-> (or (dm/get-prop shape :layout-justify-items) :start) sr/translate-layout-justify-items) - justify-content (-> (or (dm/get-prop shape :layout-justify-content) :stretch) sr/translate-layout-justify-content) - wrap-type (-> (or (dm/get-prop shape :layout-wrap-type) :nowrap) sr/translate-layout-wrap-type) + align-items (-> (get shape :layout-align-items) sr/translate-layout-align-items) + align-content (-> (get shape :layout-align-content) sr/translate-layout-align-content) + justify-items (-> (get shape :layout-justify-items) sr/translate-layout-justify-items) + justify-content (-> (get shape :layout-justify-content) sr/translate-layout-justify-content) + wrap-type (-> (get shape :layout-wrap-type) sr/translate-layout-wrap-type) + + padding (get shape :layout-padding) + padding-top (get padding :p1 0) + padding-right (get padding :p2 0) + padding-bottom (get padding :p3 0) + padding-left (get padding :p4 0)] - padding (dm/get-prop shape :layout-padding) - padding-top (or (dm/get-prop padding :p1) 0) - padding-right (or (dm/get-prop padding :p2) 0) - padding-bottom (or (dm/get-prop padding :p3) 0) - padding-left (or (dm/get-prop padding :p4) 0)] (h/call wasm/internal-module "_set_flex_layout_data" dir @@ -438,21 +422,22 @@ (defn set-grid-layout-data [shape] - (let [dir (-> (or (dm/get-prop shape :layout-grid-dir) :row) sr/translate-layout-grid-dir) - gap (dm/get-prop shape :layout-gap) - row-gap (or (dm/get-prop gap :row-gap) 0) - column-gap (or (dm/get-prop gap :column-gap) 0) + (let [dir (-> (get shape :layout-grid-dir :row) + (sr/translate-layout-grid-dir)) + gap (get shape :layout-gap) + row-gap (get gap :row-gap 0) + column-gap (get gap :column-gap 0) - align-items (-> (or (dm/get-prop shape :layout-align-items) :start) sr/translate-layout-align-items) - align-content (-> (or (dm/get-prop shape :layout-align-content) :stretch) sr/translate-layout-align-content) - justify-items (-> (or (dm/get-prop shape :layout-justify-items) :start) sr/translate-layout-justify-items) - justify-content (-> (or (dm/get-prop shape :layout-justify-content) :stretch) sr/translate-layout-justify-content) + align-items (-> (get shape :layout-align-items) sr/translate-layout-align-items) + align-content (-> (get shape :layout-align-content) sr/translate-layout-align-content) + justify-items (-> (get shape :layout-justify-items) sr/translate-layout-justify-items) + justify-content (-> (get shape :layout-justify-content) sr/translate-layout-justify-content) - padding (dm/get-prop shape :layout-padding) - padding-top (or (dm/get-prop padding :p1) 0) - padding-right (or (dm/get-prop padding :p2) 0) - padding-bottom (or (dm/get-prop padding :p3) 0) - padding-left (or (dm/get-prop padding :p4) 0)] + padding (get shape :layout-padding) + padding-top (get padding :p1 0) + padding-right (get padding :p2 0) + padding-bottom (get padding :p3 0) + padding-left (get padding :p4 0)] (h/call wasm/internal-module "_set_grid_layout_data" @@ -470,53 +455,51 @@ (defn set-grid-layout-rows [entries] - (let [size (grid-layout-get-row-entries-size entries) - offset (mem/alloc size) + (let [size (mem/get-alloc-size entries GRID-LAYOUT-ROW-U8-SIZE) + offset (mem/alloc size) + dview (mem/get-data-view)] + + (reduce (fn [offset {:keys [type value]}] + ;; NOTE: because of the nature of the grid row data + ;; structure memory layout we can't use fully 32 bits + ;; alligned writes, so for heteregeneus writes we use + ;; the buffer abstraction (DataView) for perform + ;; surgical writes. + (mem/write-u8 dview (+ offset 0) (sr/translate-grid-track-type type)) + (mem/write-f32 dview (+ offset 1) value) + (+ offset GRID-LAYOUT-ROW-U8-SIZE)) + offset + entries) - heap - (js/Uint8Array. - (.-buffer (mem/get-heap-u8)) - offset - size)] - (loop [entries (seq entries) - current-offset 0] - (when-not (empty? entries) - (let [{:keys [type value]} (first entries)] - (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ current-offset 0)) - (.set heap (sr/f32->u8 value) (+ current-offset 1)) - (recur (rest entries) (+ current-offset GRID-LAYOUT-ROW-U8-SIZE))))) (h/call wasm/internal-module "_set_grid_rows"))) (defn set-grid-layout-columns [entries] - (let [size (grid-layout-get-column-entries-size entries) + (let [size (mem/get-alloc-size entries GRID-LAYOUT-COLUMN-U8-SIZE) offset (mem/alloc size) + dview (mem/get-data-view)] + + (reduce (fn [offset {:keys [type value]}] + ;; NOTE: because of the nature of the grid column data + ;; structure memory layout we can't use fully 32 bits + ;; alligned writes, so for heteregeneus writes we use + ;; the buffer abstraction (DataView) for perform + ;; surgical writes. + (mem/write-u8 dview (+ offset 0) (sr/translate-grid-track-type type)) + (mem/write-f32 dview (+ offset 1) value) + (+ offset GRID-LAYOUT-COLUMN-U8-SIZE)) + offset + entries) - heap - (js/Uint8Array. - (.-buffer (mem/get-heap-u8)) - offset - size)] - (loop [entries (seq entries) - current-offset 0] - (when-not (empty? entries) - (let [{:keys [type value]} (first entries)] - (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ current-offset 0)) - (.set heap (sr/f32->u8 value) (+ current-offset 1)) - (recur (rest entries) (+ current-offset GRID-LAYOUT-COLUMN-U8-SIZE))))) (h/call wasm/internal-module "_set_grid_columns"))) (defn set-grid-layout-cells [cells] (let [entries (vals cells) - size (grid-layout-get-cell-entries-size entries) - offset (mem/alloc size) - - heap - (js/Uint8Array. - (.-buffer (mem/get-heap-u8)) - offset - size)] + size (mem/get-alloc-size cells GRID-LAYOUT-CELL-U8-SIZE) + offset (mem/alloc size) + heap (-> (mem/get-heap-u8) + (mem/view offset size))] (loop [entries (seq entries) current-offset 0] @@ -628,7 +611,7 @@ (h/call wasm/internal-module "_add_shape_shadow" rgba blur spread x y (sr/translate-shadow-style style) hidden) (recur (inc index))))))) -(declare propagate-apply) +;; (declare propagate-apply) (defn set-shape-text-content [shape-id content] @@ -722,11 +705,6 @@ bool-type (dm/get-prop shape :bool-type) grow-type (dm/get-prop shape :grow-type) 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)] @@ -742,6 +720,7 @@ (set-shape-opacity opacity) (set-shape-hidden hidden) (set-shape-children children) + (set-shape-corners shape) (when (and (= type :group) masked) (set-masked masked)) (when (some? blur) @@ -756,7 +735,6 @@ (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 (= type :text) (set-shape-grow-type grow-type)) @@ -827,134 +805,90 @@ (defn set-focus-mode [entries] - (let [offset (mem/alloc->offset-32 (* (count entries) 16)) - heapu32 (mem/get-heap-u32)] + (when-not ^boolean (empty? entries) + (let [size (mem/get-alloc-size entries UUID-U8-SIZE) + heap (mem/get-heap-u32) + offset (mem/alloc->offset-32 size)] - (loop [entries (seq entries) - current-offset offset] - (when-not (empty? entries) - (let [id (first entries)] - (sr/heapu32-set-uuid id heapu32 current-offset) - (recur (rest entries) (+ current-offset (mem/->offset-32 16)))))) + (reduce (fn [offset id] + (mem.h32/write-uuid offset heap id)) + offset + entries) - (h/call wasm/internal-module "_set_focus_mode") - (clear-drawing-cache) - (request-render "set-focus-mode"))) + (h/call wasm/internal-module "_set_focus_mode") + (clear-drawing-cache) + (request-render "set-focus-mode")))) (defn set-structure-modifiers [entries] - (when-not (empty? entries) - (let [offset (mem/alloc->offset-32 (mem/get-list-size entries 44)) + (when-not ^boolean (empty? entries) + (let [size (mem/get-alloc-size entries 44) + offset (mem/alloc->offset-32 size) heapu32 (mem/get-heap-u32) heapf32 (mem/get-heap-f32)] - (loop [entries (seq entries) - current-offset offset] - (when-not (empty? entries) - (let [{:keys [type parent id index value] :as entry} (first entries)] - (sr/heapu32-set-u32 (sr/translate-structure-modifier-type type) heapu32 (+ current-offset 0)) - (sr/heapu32-set-u32 (or index 0) heapu32 (+ current-offset 1)) - (sr/heapu32-set-uuid parent heapu32 (+ current-offset 2)) - (sr/heapu32-set-uuid id heapu32 (+ current-offset 6)) - (aset heapf32 (+ current-offset 10) value) - (recur (rest entries) (+ current-offset 11))))) + + + (reduce (fn [offset {:keys [type parent id index value]}] + (-> offset + (mem.h32/write-u32 heapu32 (sr/translate-structure-modifier-type type)) + (mem.h32/write-u32 heapu32 (d/nilv index 0)) + (mem.h32/write-uuid heapu32 parent) + (mem.h32/write-uuid heapu32 id) + (mem.h32/write-f32 heapf32 value))) + offset + entries) + (h/call wasm/internal-module "_set_structure_modifiers")))) (defn propagate-modifiers [entries pixel-precision] - (when (d/not-empty? entries) - (let [offset (mem/alloc->offset-32 (modifier-get-entries-size entries)) - heapf32 (mem/get-heap-f32) - heapu32 (mem/get-heap-u32)] + (when-not ^boolean (empty? entries) + (let [heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32) + size (mem/get-alloc-size entries MODIFIER-U8-SIZE) + offset (mem/alloc->offset-32 size)] - (loop [entries (seq entries) - current-offset offset] - (when-not (empty? entries) - (let [{:keys [id transform]} (first entries)] - (sr/heapu32-set-uuid id heapu32 current-offset) - (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE))) - (recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE)))))) + (reduce (fn [offset [id transform]] + (-> offset + (mem.h32/write-uuid heapu32 id) + (mem.h32/write-matrix heapf32 transform))) + offset + entries) + + (let [offset (-> (h/call wasm/internal-module "_propagate_modifiers" pixel-precision) + (mem/->offset-32)) + length (aget heapu32 offset) + max-offset (+ offset 1 (* length MODIFIER-U32-SIZE)) + result (loop [result (transient []) + offset (inc offset)] + (if (< offset max-offset) + (let [entry (dr/read-modifier-entry heapu32 heapf32 offset)] + (recur (conj! result entry) + (+ offset MODIFIER-U32-SIZE))) + (persistent! result)))] - (let [result-offset (h/call wasm/internal-module "_propagate_modifiers" pixel-precision) - heapf32 (mem/get-heap-f32) - heapu32 (mem/get-heap-u32) - len (aget heapu32 (mem/->offset-32 result-offset)) - result - (->> (range 0 len) - (mapv #(dr/heap32->entry heapu32 heapf32 (mem/->offset-32 (+ result-offset 4 (* % MODIFIER-U8-SIZE))))))] (mem/free) - result)))) -(defn propagate-apply - [entries pixel-precision] - (when (d/not-empty? entries) - (let [offset (mem/alloc->offset-32 (modifier-get-entries-size entries)) - heapf32 (mem/get-heap-f32) - heapu32 (mem/get-heap-u32)] - - (loop [entries (seq entries) - current-offset offset] - (when-not (empty? entries) - (let [{:keys [id transform]} (first entries)] - (sr/heapu32-set-uuid id heapu32 current-offset) - (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE))) - (recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE)))))) - - (let [offset (h/call wasm/internal-module "_propagate_apply" pixel-precision) - heapf32 (mem/get-heap-f32) - width (aget heapf32 (mem/->offset-32 (+ offset 0))) - height (aget heapf32 (mem/->offset-32 (+ offset 4))) - cx (aget heapf32 (mem/->offset-32 (+ offset 8))) - cy (aget heapf32 (mem/->offset-32 (+ offset 12))) - - a (aget heapf32 (mem/->offset-32 (+ offset 16))) - b (aget heapf32 (mem/->offset-32 (+ offset 20))) - c (aget heapf32 (mem/->offset-32 (+ offset 24))) - d (aget heapf32 (mem/->offset-32 (+ offset 28))) - e (aget heapf32 (mem/->offset-32 (+ offset 32))) - f (aget heapf32 (mem/->offset-32 (+ offset 36))) - transform (gmt/matrix a b c d e f)] - - (mem/free) - (request-render "set-modifiers") - - {:width width - :height height - :center (gpt/point cx cy) - :transform transform})))) - (defn get-selection-rect [entries] - (when (d/not-empty? entries) - (let [offset (mem/alloc->offset-32 (* (count entries) 16)) - heapu32 (mem/get-heap-u32)] - (loop [entries (seq entries) - current-offset offset] - (when-not (empty? entries) - (let [id (first entries)] - (sr/heapu32-set-uuid id heapu32 current-offset) - (recur (rest entries) (+ current-offset (mem/->offset-32 16)))))) + (when-not ^boolean (empty? entries) + (let [size (mem/get-alloc-size entries UUID-U8-SIZE) + offset (mem/alloc->offset-32 size) + heapu32 (mem/get-heap-u32) + heapf32 (mem/get-heap-f32)] - (let [offset (h/call wasm/internal-module "_get_selection_rect") - heap (mem/get-heap-f32) - width (aget heap (mem/->offset-32 (+ offset 0))) - height (aget heap (mem/->offset-32 (+ offset 4))) - cx (aget heap (mem/->offset-32 (+ offset 8))) - cy (aget heap (mem/->offset-32 (+ offset 12))) - a (aget heap (mem/->offset-32 (+ offset 16))) - b (aget heap (mem/->offset-32 (+ offset 20))) - c (aget heap (mem/->offset-32 (+ offset 24))) - d (aget heap (mem/->offset-32 (+ offset 28))) - e (aget heap (mem/->offset-32 (+ offset 32))) - f (aget heap (mem/->offset-32 (+ offset 36)))] + (reduce (fn [offset id] + (mem.h32/write-uuid offset heapu32 id)) + offset + entries) + (let [offset (-> (h/call wasm/internal-module "_get_selection_rect") + (mem/->offset-32)) + result (dr/read-selection-rect heapf32 offset)] (mem/free) - {:width width - :height height - :center (gpt/point cx cy) - :transform (gmt/matrix a b c d e f)})))) - + result)))) (defn set-canvas-background [background] @@ -968,22 +902,26 @@ (defn set-modifiers [modifiers] - (when-not (empty? modifiers) - (let [offset (mem/alloc->offset-32 (* MODIFIER-U8-SIZE (count modifiers))) - heapu32 (mem/get-heap-u32) - heapf32 (mem/get-heap-f32)] - (loop [entries (seq modifiers) - current-offset offset] - (when-not (empty? entries) - (let [{:keys [id transform]} (first entries)] - (sr/heapu32-set-uuid id heapu32 current-offset) - (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE))) - (recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE)))))) + ;; We need to ensure efficient operations + (assert (vector? modifiers) "expected a vector for `set-modifiers`") - (h/call wasm/internal-module "_set_modifiers") + (let [length (count modifiers)] + (when (pos? length) + (let [offset (mem/alloc->offset-32 (* MODIFIER-U8-SIZE length)) + heapu32 (mem/get-heap-u32) + heapf32 (mem/get-heap-f32)] - (request-render "set-modifiers")))) + (reduce (fn [offset [id transform]] + (-> offset + (mem.h32/write-uuid heapu32 id) + (mem.h32/write-matrix heapf32 transform))) + offset + modifiers) + + (h/call wasm/internal-module "_set_modifiers") + + (request-render "set-modifiers"))))) (defn initialize [base-objects zoom vbox background] @@ -1065,41 +1003,42 @@ (defn shape-to-path [id] (use-shape id) - (let [offset (h/call wasm/internal-module "_current_to_path") - offset (mem/->offset-32 offset) - heapu32 (mem/get-heap-u32) - - length (aget heapu32 offset) - data (mem/slice heapu32 - (+ offset 1) - (+ offset 1 (* length (/ path.impl/SEGMENT-U8-SIZE 4)))) + (let [offset (-> (h/call wasm/internal-module "_current_to_path") + (mem/->offset-32)) + heap (mem/get-heap-u32) + length (aget heap offset) + data (mem/slice heap + (+ offset 1) + (* length path.impl/SEGMENT-U32-SIZE)) content (path/from-bytes data)] (mem/free) content)) +(defn- calculate-bool* + [bool-type] + (-> (h/call wasm/internal-module "_calculate_bool" (sr/translate-bool-type bool-type)) + (mem/->offset-32))) + (defn calculate-bool [bool-type ids] - (let [num-ids (count ids) - offset (mem/alloc->offset-32 (* UUID-U8-SIZE num-ids)) - heap (mem/get-heap-u32)] + + (let [size (mem/get-alloc-size ids UUID-U8-SIZE) + heap (mem/get-heap-u32) + offset (mem/alloc->offset-32 size)] (reduce (fn [offset id] - (sr/heapu32-set-uuid id heap offset) - (+ offset UUID-U32-SIZE)) + (mem.h32/write-uuid offset heap id)) offset - (rseq ids))) + (rseq ids)) - (let [offset (h/call wasm/internal-module "_calculate_bool" (sr/translate-bool-type bool-type)) - offset (mem/->offset-32 offset) - heapu32 (mem/get-heap-u32) - - length (aget heapu32 offset) - data (mem/slice heapu32 - (+ offset 1) - (+ offset 1 (* length (/ path.impl/SEGMENT-U8-SIZE 4)))) - content (path/from-bytes data)] - (mem/free) - content)) + (let [offset (calculate-bool* bool-type) + length (aget heap offset) + data (mem/slice heap + (+ offset 1) + (* length path.impl/SEGMENT-U32-SIZE)) + content (path/from-bytes data)] + (mem/free) + content))) (defonce module (delay diff --git a/frontend/src/app/render_wasm/deserializers.cljs b/frontend/src/app/render_wasm/deserializers.cljs index 3c7f1f16c0..dd718d82c4 100644 --- a/frontend/src/app/render_wasm/deserializers.cljs +++ b/frontend/src/app/render_wasm/deserializers.cljs @@ -5,25 +5,44 @@ ;; Copyright (c) KALEIDOS INC (ns app.render-wasm.deserializers (:require + [app.common.data :as d] [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] [app.common.uuid :as uuid])) -(defn heap32->entry +(defn read-modifier-entry [heapu32 heapf32 offset] (let [id1 (aget heapu32 (+ offset 0)) id2 (aget heapu32 (+ offset 1)) id3 (aget heapu32 (+ offset 2)) id4 (aget heapu32 (+ offset 3)) - a (aget heapf32 (+ offset 4)) - b (aget heapf32 (+ offset 5)) - c (aget heapf32 (+ offset 6)) - d (aget heapf32 (+ offset 7)) - e (aget heapf32 (+ offset 8)) - f (aget heapf32 (+ offset 9)) + a (aget heapf32 (+ offset 4)) + b (aget heapf32 (+ offset 5)) + c (aget heapf32 (+ offset 6)) + d (aget heapf32 (+ offset 7)) + e (aget heapf32 (+ offset 8)) + f (aget heapf32 (+ offset 9))] - id (uuid/from-unsigned-parts id1 id2 id3 id4)] + (d/vec2 (uuid/from-unsigned-parts id1 id2 id3 id4) + (gmt/matrix a b c d e f)))) - {:id id + +(defn read-selection-rect + [heapf32 offset] + (let [width (aget heapf32 (+ offset 0)) + height (aget heapf32 (+ offset 1)) + cx (aget heapf32 (+ offset 2)) + cy (aget heapf32 (+ offset 3)) + a (aget heapf32 (+ offset 4)) + b (aget heapf32 (+ offset 5)) + c (aget heapf32 (+ offset 6)) + d (aget heapf32 (+ offset 7)) + e (aget heapf32 (+ offset 8)) + f (aget heapf32 (+ offset 9))] + {:width width + :height height + :center (gpt/point cx cy) :transform (gmt/matrix a b c d e f)})) + diff --git a/frontend/src/app/render_wasm/mem.cljs b/frontend/src/app/render_wasm/mem.cljs index fec386b5a5..59562329c8 100644 --- a/frontend/src/app/render_wasm/mem.cljs +++ b/frontend/src/app/render_wasm/mem.cljs @@ -6,6 +6,7 @@ (ns app.render-wasm.mem (:require + [app.common.buffer :as buf] [app.render-wasm.helpers :as h] [app.render-wasm.wasm :as wasm])) @@ -15,10 +16,12 @@ ;; Divides the value by 4 (bit-shift-right value 2)) -(defn get-list-size - "Returns the size of a list in bytes" - [list list-item-size] - (* list-item-size (count list))) +(defn get-alloc-size + "Calculate allocation size for a sequential collection of identical + objects of the specified size." + [coll item-size] + (assert (counted? coll) "`coll` should be constant time countable") + (* item-size (count coll))) (defn alloc "Allocates an arbitrary amount of bytes (aligned to 4 bytes). @@ -61,5 +64,26 @@ (defn slice "Returns a copy of a portion of a typed array into a new typed array object selected from start to end." - [heap start end] - (.slice ^js heap start end)) + [heap offset size] + (.slice ^js heap offset (+ offset size))) + +(defn view + "Returns a new typed array on the same ArrayBuffer store and with the + same element types as for this typed array." + [heap offset size] + (.subarray ^js heap offset (+ offset size))) + +(defn get-data-view + "Returns a heap wrapped in a DataView for surgical write operations" + [] + (buf/wrap (get-heap-u8))) + +(defn write-u8 + "Write unsigned int8. Expects a DataView instance" + [target offset value] + (buf/write-byte target offset value)) + +(defn write-f32 + "Write float32. Expects a DataView instance" + [target offset value] + (buf/write-float target offset value)) diff --git a/frontend/src/app/render_wasm/mem/heap32.cljs b/frontend/src/app/render_wasm/mem/heap32.cljs new file mode 100644 index 0000000000..ed5f8137fb --- /dev/null +++ b/frontend/src/app/render_wasm/mem/heap32.cljs @@ -0,0 +1,51 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.render-wasm.mem.heap32 + "A memory write helpers that uses 32 bits addressed offsets." + (:require + [app.common.data.macros :as dm] + [app.common.uuid :as uuid])) + +(defn write-u32 + [offset heap value] + (assert (instance? js/Uint32Array heap) "expected Uint32Array instance for `heap`") + (aset heap offset value) + (inc offset)) + +(defn write-f32 + [offset heap value] + (assert (instance? js/Float32Array heap) "expected Float32Array instance for `heap`") + (aset heap offset value) + (inc offset)) + +(defn write-uuid + "Write a uuid to 32 bits addressed heap and return the offset + after write." + [offset heap id] + (assert (instance? js/Uint32Array heap) "expected Uint32Array instance for `heap`") + (let [buffer (uuid/get-u32 id)] + (.set heap buffer offset) + (+ offset 4))) + +(defn write-matrix + "Write a matrix to 32 bits addressed heap and return the offset + after write." + [offset heap matrix] + (assert (instance? js/Float32Array heap) "expected Float32Array instance for `heap`") + (let [a (dm/get-prop matrix :a) + b (dm/get-prop matrix :b) + c (dm/get-prop matrix :c) + d (dm/get-prop matrix :d) + e (dm/get-prop matrix :e) + f (dm/get-prop matrix :f)] + (aset heap (+ offset 0) a) + (aset heap (+ offset 1) b) + (aset heap (+ offset 2) c) + (aset heap (+ offset 3) d) + (aset heap (+ offset 4) e) + (aset heap (+ offset 5) f) + (+ offset 6))) diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index 1d6754a939..5efd965b6d 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -6,7 +6,6 @@ (ns app.render-wasm.serializers (:require - [app.common.data.macros :as dm] [app.common.uuid :as uuid] [cuerdas.core :as str])) @@ -55,31 +54,6 @@ (catch :default _e [uuid/zero]))) -(defn heapu32-set-u32 - [value heap offset] - (aset heap offset value)) - -(defn heapu32-set-uuid - [id heap offset] - (let [buffer (uuid/get-u32 id)] - (.set heap buffer offset) - buffer)) - -(defn heapf32-set-matrix - [matrix heap offset] - (let [a (dm/get-prop matrix :a) - b (dm/get-prop matrix :b) - c (dm/get-prop matrix :c) - d (dm/get-prop matrix :d) - e (dm/get-prop matrix :e) - f (dm/get-prop matrix :f)] - (aset heap (+ offset 0) a) - (aset heap (+ offset 1) b) - (aset heap (+ offset 2) c) - (aset heap (+ offset 3) d) - (aset heap (+ offset 4) e) - (aset heap (+ offset 5) f))) - (defn translate-shape-type [type] (case type @@ -197,7 +171,8 @@ :start 0 :end 1 :center 2 - :stretch 3)) + :stretch 3 + 0)) (defn translate-layout-align-content [align-content] @@ -208,7 +183,8 @@ :space-between 3 :space-around 4 :space-evenly 5 - :stretch 6)) + :stretch 6 + 6)) (defn translate-layout-justify-items [justify-items] @@ -216,7 +192,8 @@ :start 0 :end 1 :center 2 - :stretch 3)) + :stretch 3 + 0)) (defn translate-layout-justify-content [justify-content] @@ -227,13 +204,15 @@ :space-between 3 :space-around 4 :space-evenly 5 - :stretch 6)) + :stretch 6 + 6)) (defn translate-layout-wrap-type [wrap-type] (case wrap-type :wrap 0 - :nowrap 1)) + :nowrap 1 + 1)) (defn translate-grid-track-type [type] diff --git a/render-wasm/src/shapes/layouts.rs b/render-wasm/src/shapes/layouts.rs index 1019ee79d1..edb2e80548 100644 --- a/render-wasm/src/shapes/layouts.rs +++ b/render-wasm/src/shapes/layouts.rs @@ -385,6 +385,7 @@ impl GridData { } } +// FIXME: use transmute #[derive(Debug)] #[repr(C)] pub struct RawGridTrack {