diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 02e337e8e6..42a5bf81e8 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -10,7 +10,6 @@ ["react-dom/server" :as rds] [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.geom.matrix :as gmt] [app.common.math :as mth] [app.common.types.shape.layout :as ctl] [app.common.types.shape.path :as path] @@ -20,7 +19,9 @@ [app.main.refs :as refs] [app.main.render :as render] [app.render-wasm.api.fonts :as f] + [app.render-wasm.deserializers :as dr] [app.render-wasm.helpers :as h] + [app.render-wasm.mem :as mem] [app.render-wasm.serializers :as sr] [app.render-wasm.wasm :as wasm] [app.util.debug :as dbg] @@ -28,18 +29,51 @@ [app.util.webapi :as wapi] [beicon.v2.core :as rx] [cuerdas.core :as str] - [goog.object :as gobj] [promesa.core :as p] [rumext.v2 :as mf])) ;; (defonce internal-frame-id nil) -;; (defonce internal-module #js {}) +;; (defonce wasm/internal-module #js {}) (defonce 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 CHILD-ENTRY-SIZE 16) +(def MODIFIER-ENTRY-SIZE 40) +(def MODIFIER-ENTRY-TRANSFORM-OFFSET 16) +(def GRID-LAYOUT-ROW-ENTRY-SIZE 5) +(def GRID-LAYOUT-COLUMN-ENTRY-SIZE 5) +(def GRID-LAYOUT-CELL-ENTRY-SIZE 37) +(def GRADIENT-STOP-SIZE 5) + +(defn gradient-stop-get-entries-size + [stops] + (mem/get-list-size stops GRADIENT-STOP-SIZE)) + +(defn modifier-get-entries-size + "Returns the list of a modifier list in bytes" + [modifiers] + (mem/get-list-size modifiers MODIFIER-ENTRY-SIZE)) + +(defn grid-layout-get-row-entries-size + [rows] + (mem/get-list-size rows GRID-LAYOUT-ROW-ENTRY-SIZE)) + +(defn grid-layout-get-column-entries-size + [columns] + (mem/get-list-size columns GRID-LAYOUT-COLUMN-ENTRY-SIZE)) + +(defn grid-layout-get-cell-entries-size + [cells] + (mem/get-list-size cells GRID-LAYOUT-CELL-ENTRY-SIZE)) + (def dpr (if use-dpr? js/window.devicePixelRatio 1.0)) - ;; Based on app.main.render/object-svg (mf/defc object-svg {::mf/props :obj} @@ -154,28 +188,22 @@ (defn set-shape-children [shape-ids] - (let [ENTRY-SIZE 16 - ptr - (h/call wasm/internal-module "_alloc_bytes" (* ENTRY-SIZE (count shape-ids))) + (let [num-shapes (count shape-ids)] + (when (> num-shapes 0) + (let [offset (mem/alloc-bytes (* CHILD-ENTRY-SIZE num-shapes)) + heap (mem/get-heap-u32)] - heap - (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* ENTRY-SIZE (count shape-ids)))] + (loop [entries (seq shape-ids) + current-offset offset] + (when-not (empty? entries) + (let [id (first entries)] + (sr/heapu32-set-uuid id heap (mem/ptr8->ptr32 current-offset)) + (recur (rest entries) (+ current-offset CHILD-ENTRY-SIZE))))) - (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 wasm/internal-module "_set_children"))) + (h/call wasm/internal-module "_set_children"))))) (defn- get-string-length [string] (+ (count string) 1)) - (defn- store-image [id] @@ -187,11 +215,11 @@ (rx/map :body) (rx/mapcat wapi/read-file-as-array-buffer) (rx/map (fn [image] - (let [image-size (.-byteLength image) - image-ptr (h/call wasm/internal-module "_alloc_bytes" image-size) - heap (gobj/get ^js wasm/internal-module "HEAPU8") - mem (js/Uint8Array. (.-buffer heap) image-ptr image-size)] - (.set mem (js/Uint8Array. image)) + (let [size (.-byteLength image) + offset (mem/alloc-bytes size) + heap (mem/get-heap-u8) + mem (js/Uint8Array. (.-buffer heap) offset size)] + (.set heap mem offset) (h/call wasm/internal-module "_store_image" (aget buffer 0) (aget buffer 1) @@ -214,11 +242,10 @@ (some? gradient) (let [stops (:stops gradient) - n-stops (count stops) - mem-size (* 5 n-stops) - stops-ptr (h/call wasm/internal-module "_alloc_bytes" mem-size) - heap (gobj/get ^js wasm/internal-module "HEAPU8") - mem (js/Uint8Array. (.-buffer heap) stops-ptr mem-size)] + size (gradient-stop-get-entries-size stops) + offset (mem/alloc-bytes size) + heap (mem/get-heap-u8) + mem (js/Uint8Array. (.-buffer heap) offset size)] (if (= (:type gradient) :linear) (h/call wasm/internal-module "_add_shape_linear_fill" (:start-x gradient) @@ -277,11 +304,10 @@ (cond (some? gradient) (let [stops (:stops gradient) - n-stops (count stops) - mem-size (* 5 n-stops) - stops-ptr (h/call wasm/internal-module "_alloc_bytes" mem-size) - heap (gobj/get ^js wasm/internal-module "HEAPU8") - mem (js/Uint8Array. (.-buffer heap) stops-ptr mem-size)] + size (gradient-stop-get-entries-size stops) + offset (mem/alloc-bytes size) + heap (mem/get-heap-u8) + mem (js/Uint8Array. (.-buffer heap) offset size)] (if (= (:type gradient) :linear) (h/call wasm/internal-module "_add_shape_stroke_linear_fill" (:start-x gradient) @@ -333,24 +359,24 @@ (merge style)) str (sr/serialize-path-attrs attrs) size (count str) - ptr (h/call wasm/internal-module "_alloc_bytes" size)] - (h/call wasm/internal-module "stringToUTF8" str ptr size) + offset (mem/alloc-bytes size)] + (h/call wasm/internal-module "stringToUTF8" str offset size) (h/call wasm/internal-module "_set_shape_path_attrs" (count attrs)))) (defn set-shape-path-content [content] (let [pdata (path/path-data content) size (* (count pdata) path/SEGMENT-BYTE-SIZE) - offset (h/call wasm/internal-module "_alloc_bytes" size) - heap (gobj/get ^js wasm/internal-module "HEAPU8")] + offset (mem/alloc-bytes size) + heap (mem/get-heap-u8)] (path/-write-to pdata (.-buffer heap) offset) (h/call wasm/internal-module "_set_shape_path_content"))) (defn set-shape-svg-raw-content [content] (let [size (get-string-length content) - ptr (h/call wasm/internal-module "_alloc_bytes" size)] - (h/call wasm/internal-module "stringToUTF8" content ptr size) + offset (mem/alloc-bytes size)] + (h/call wasm/internal-module "stringToUTF8" content offset size) (h/call wasm/internal-module "_set_shape_svg_raw_content"))) @@ -365,8 +391,6 @@ [opacity] (h/call wasm/internal-module "_set_shape_opacity" (or opacity 1))) - - (defn set-constraints-h [constraint] (when constraint @@ -473,93 +497,93 @@ padding-left)) ;; Send Rows - (let [entry-size 5 - entries (:layout-grid-rows shape) - ptr (h/call wasm/internal-module "_alloc_bytes" (* entry-size (count entries))) + (let [entries (:layout-grid-rows shape) + size (grid-layout-get-row-entries-size entries) + offset (mem/alloc-bytes size) heap (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* entry-size (count entries)))] + (.-buffer (mem/get-heap-u8)) + offset + size)] (loop [entries (seq entries) - offset 0] + current-offset 0] (when-not (empty? entries) (let [{:keys [type value]} (first entries)] - (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ offset 0)) - (.set heap (sr/f32->u8 value) (+ offset 1)) - (recur (rest entries) (+ offset entry-size))))) + (.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-ENTRY-SIZE))))) (h/call wasm/internal-module "_set_grid_rows")) ;; Send Columns - (let [entry-size 5 - entries (:layout-grid-columns shape) - ptr (h/call wasm/internal-module "_alloc_bytes" (* entry-size (count entries))) + (let [entries (:layout-grid-columns shape) + size (grid-layout-get-column-entries-size entries) + offset (mem/alloc-bytes size) heap (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* entry-size (count entries)))] + (.-buffer (mem/get-heap-u8)) + offset + size)] (loop [entries (seq entries) - offset 0] + current-offset 0] (when-not (empty? entries) (let [{:keys [type value]} (first entries)] - (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ offset 0)) - (.set heap (sr/f32->u8 value) (+ offset 1)) - (recur (rest entries) (+ offset entry-size))))) + (.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-ENTRY-SIZE))))) (h/call wasm/internal-module "_set_grid_columns")) ;; Send cells - (let [entry-size 37 - entries (-> shape :layout-grid-cells vals) - ptr (h/call wasm/internal-module "_alloc_bytes" (* entry-size (count entries))) + (let [entries (-> shape :layout-grid-cells vals) + size (grid-layout-get-cell-entries-size entries) + offset (mem/alloc-bytes size) heap (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* entry-size (count entries)))] + (.-buffer (mem/get-heap-u8)) + offset + size)] (loop [entries (seq entries) - offset 0] + current-offset 0] (when-not (empty? entries) (let [cell (first entries)] ;; row: [u8; 4], - (.set heap (sr/i32->u8 (:row cell)) (+ offset 0)) + (.set heap (sr/i32->u8 (:row cell)) (+ current-offset 0)) ;; row_span: [u8; 4], - (.set heap (sr/i32->u8 (:row-span cell)) (+ offset 4)) + (.set heap (sr/i32->u8 (:row-span cell)) (+ current-offset 4)) ;; column: [u8; 4], - (.set heap (sr/i32->u8 (:column cell)) (+ offset 8)) + (.set heap (sr/i32->u8 (:column cell)) (+ current-offset 8)) ;; column_span: [u8; 4], - (.set heap (sr/i32->u8 (:column-span cell)) (+ offset 12)) + (.set heap (sr/i32->u8 (:column-span cell)) (+ current-offset 12)) ;; has_align_self: u8, - (.set heap (sr/bool->u8 (some? (:align-self cell))) (+ offset 16)) + (.set heap (sr/bool->u8 (some? (:align-self cell))) (+ current-offset 16)) ;; align_self: u8, - (.set heap (sr/u8 (sr/translate-align-self (:align-self cell))) (+ offset 17)) + (.set heap (sr/u8 (sr/translate-align-self (:align-self cell))) (+ current-offset 17)) ;; has_justify_self: u8, - (.set heap (sr/bool->u8 (some? (:justify-self cell))) (+ offset 18)) + (.set heap (sr/bool->u8 (some? (:justify-self cell))) (+ current-offset 18)) ;; justify_self: u8, - (.set heap (sr/u8 (sr/translate-justify-self (:justify-self cell))) (+ offset 19)) + (.set heap (sr/u8 (sr/translate-justify-self (:justify-self cell))) (+ current-offset 19)) ;; has_shape_id: u8, - (.set heap (sr/bool->u8 (d/not-empty? (:shapes cell))) (+ offset 20)) + (.set heap (sr/bool->u8 (d/not-empty? (:shapes cell))) (+ current-offset 20)) ;; shape_id_a: [u8; 4], ;; shape_id_b: [u8; 4], ;; shape_id_c: [u8; 4], ;; shape_id_d: [u8; 4], - (.set heap (sr/uuid->u8 (or (-> cell :shapes first) uuid/zero)) (+ offset 21)) + (.set heap (sr/uuid->u8 (or (-> cell :shapes first) uuid/zero)) (+ current-offset 21)) - (recur (rest entries) (+ offset entry-size))))) + (recur (rest entries) (+ current-offset GRID-LAYOUT-CELL-ENTRY-SIZE))))) (h/call wasm/internal-module "_set_grid_cells"))) @@ -637,9 +661,9 @@ font-size (js/Number (dm/get-prop leaf :font-size)) buffer (utf8->buffer text) size (.-byteLength buffer) - ptr (h/call wasm/internal-module "_alloc_bytes" size) - heap (gobj/get ^js wasm/internal-module "HEAPU8") - mem (js/Uint8Array. (.-buffer heap) ptr size)] + offset (mem/alloc-bytes size) + heap (mem/get-heap-u8) + mem (js/Uint8Array. (.-buffer heap) offset size)] (.set mem buffer) (h/call wasm/internal-module "_add_text_leaf" (aget font-id 0) @@ -788,51 +812,27 @@ (clear-drawing-cache) (request-render "set-objects"))))))) - -(defn data->entry - [data offset] - (let [id1 (.getUint32 data (+ offset 0) true) - id2 (.getUint32 data (+ offset 4) true) - id3 (.getUint32 data (+ offset 8) true) - id4 (.getUint32 data (+ offset 12) true) - - a (.getFloat32 data (+ offset 16) true) - b (.getFloat32 data (+ offset 20) true) - c (.getFloat32 data (+ offset 24) true) - d (.getFloat32 data (+ offset 28) true) - e (.getFloat32 data (+ offset 32) true) - f (.getFloat32 data (+ offset 36) true) - - id (uuid/from-unsigned-parts id1 id2 id3 id4)] - - {:id id - :transform (gmt/matrix a b c d e f)})) - (defn propagate-modifiers [entries] - (let [entry-size 40 - ptr (h/call wasm/internal-module "_alloc_bytes" (* entry-size (count entries))) - - heap - (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* entry-size (count entries)))] + (let [offset (mem/alloc-bytes-32 (modifier-get-entries-size entries)) + heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32)] (loop [entries (seq entries) - offset 0] + current-offset offset] (when-not (empty? entries) (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))))) + (sr/heapu32-set-uuid id heapu32 current-offset) + (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-TRANSFORM-OFFSET))) + (recur (rest entries) (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-SIZE)))))) - (let [result-ptr (h/call wasm/internal-module "_propagate_modifiers") - heap (js/DataView. (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8"))) - len (.getUint32 heap result-ptr true) + (let [result-offset (h/call wasm/internal-module "_propagate_modifiers") + heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32) + len (aget heapu32 (mem/ptr8->ptr32 result-offset)) result (->> (range 0 len) - (mapv #(data->entry heap (+ result-ptr 4 (* % entry-size)))))] + (mapv #(dr/heap32->entry heapu32 heapf32 (mem/ptr8->ptr32 (+ result-offset 4 (* % MODIFIER-ENTRY-SIZE))))))] (h/call wasm/internal-module "_free_bytes") result))) @@ -848,24 +848,17 @@ (if (empty? modifiers) (h/call wasm/internal-module "_clean_modifiers") - (let [ENTRY-SIZE 40 - - ptr - (h/call wasm/internal-module "_alloc_bytes" (* ENTRY-SIZE (count modifiers))) - - heap - (js/Uint8Array. - (.-buffer (gobj/get ^js wasm/internal-module "HEAPU8")) - ptr - (* ENTRY-SIZE (count modifiers)))] + (let [offset (mem/alloc-bytes-32 (* MODIFIER-ENTRY-SIZE (count modifiers))) + heapu32 (mem/get-heap-u32) + heapf32 (mem/get-heap-f32)] (loop [entries (seq modifiers) - offset 0] + current-offset offset] (when-not (empty? entries) (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))))) + (sr/heapu32-set-uuid id heapu32 current-offset) + (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-TRANSFORM-OFFSET))) + (recur (rest entries) (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-SIZE)))))) (h/call wasm/internal-module "_set_modifiers") diff --git a/frontend/src/app/render_wasm/deserializers.cljs b/frontend/src/app/render_wasm/deserializers.cljs new file mode 100644 index 0000000000..3c7f1f16c0 --- /dev/null +++ b/frontend/src/app/render_wasm/deserializers.cljs @@ -0,0 +1,29 @@ +;; 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.deserializers + (:require + [app.common.geom.matrix :as gmt] + [app.common.uuid :as uuid])) + +(defn heap32->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)) + + id (uuid/from-unsigned-parts id1 id2 id3 id4)] + + {:id id + :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 new file mode 100644 index 0000000000..b0f5e40719 --- /dev/null +++ b/frontend/src/app/render_wasm/mem.cljs @@ -0,0 +1,56 @@ +;; 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 + (:require + [app.render-wasm.helpers :as h] + [app.render-wasm.wasm :as wasm])) + +(defn ptr8->ptr32 + "Returns a 32-bit (4-byte aligned) pointer of an 8-bit pointer" + [value] + ;; Divides the value by 4 + (bit-shift-right value 2)) + +(defn ptr32->ptr8 + "Returns a 8-bit pointer of a 32-bit (4-byte aligned) pointer" + [value] + ;; Multiplies by 4 + (bit-shift-left value 2)) + +(defn get-list-size + "Returns the size of a list in bytes" + [list list-item-size] + (* list-item-size (count list))) + +(defn alloc-bytes + "Allocates an arbitrary amount of bytes" + [size] + (when (= size 0) + (js/console.trace "Tried to allocate 0 bytes")) + (h/call wasm/internal-module "_alloc_bytes" size)) + +(defn alloc-bytes-32 + "Allocates a 4-byte aligned amount of bytes" + [size] + (when (= size 0) + (js/console.trace "Tried to allocate 0 bytes")) + (h/call wasm/internal-module "_alloc_bytes" size)) + +(defn get-heap-u8 + "Returns a Uint8Array view of the heap" + [] + (unchecked-get ^js wasm/internal-module "HEAPU8")) + +(defn get-heap-u32 + "Returns a Uint32Array view of the heap" + [] + (unchecked-get ^js wasm/internal-module "HEAPU32")) + +(defn get-heap-f32 + "Returns a Float32Array view of the heap" + [] + (unchecked-get ^js wasm/internal-module "HEAPF32")) diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index 16acca6eb6..08539274d6 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -6,6 +6,7 @@ (ns app.render-wasm.serializers (:require + [app.common.data.macros :as dm] [app.common.uuid :as uuid] [cuerdas.core :as str])) @@ -43,16 +44,26 @@ (aset u32-arr 3 (aget buffer 3)) (js/Uint8Array. (.-buffer u32-arr)))) -(defn matrix->u8 - [{:keys [a b c d e f]}] - (let [f32-arr (js/Float32Array. 6)] - (aset f32-arr 0 a) - (aset f32-arr 1 b) - (aset f32-arr 2 c) - (aset f32-arr 3 d) - (aset f32-arr 4 e) - (aset f32-arr 5 f) - (js/Uint8Array. (.-buffer f32-arr)))) +(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] diff --git a/render-wasm/src/mem.rs b/render-wasm/src/mem.rs index f9a71c4a2b..cfba1757d9 100644 --- a/render-wasm/src/mem.rs +++ b/render-wasm/src/mem.rs @@ -1,5 +1,9 @@ +use std::alloc::{alloc, Layout}; +use std::ptr; use std::sync::Mutex; +const LAYOUT_ALIGN: usize = 4; + static BUFFERU8: Mutex>>> = Mutex::new(None); #[no_mangle] @@ -10,11 +14,17 @@ pub extern "C" fn alloc_bytes(len: usize) -> *mut u8 { panic!("Bytes already allocated"); } - let mut new_buffer = Box::new(vec![0u8; len]); - let ptr = new_buffer.as_mut_ptr(); - - *guard = Some(new_buffer); - ptr + unsafe { + let layout = Layout::from_size_align_unchecked(len, LAYOUT_ALIGN); + let ptr = alloc(layout) as *mut u8; + if ptr.is_null() { + panic!("Allocation failed"); + } + // TODO: Esto quizá se podría eliminar. + ptr::write_bytes(ptr, 0, len); + *guard = Some(Box::new(Vec::from_raw_parts(ptr, len, len))); + ptr + } } pub fn write_bytes(bytes: Vec) -> *mut u8 {