diff --git a/docker/devenv/Dockerfile b/docker/devenv/Dockerfile index 3b292c69e..d205e0201 100644 --- a/docker/devenv/Dockerfile +++ b/docker/devenv/Dockerfile @@ -9,7 +9,7 @@ ENV NODE_VERSION=v20.11.1 \ BABASHKA_VERSION=1.3.189 \ CLJFMT_VERSION=0.12.0 \ RUSTUP_VERSION=1.27.1 \ - RUST_VERSION=1.81.0 \ + RUST_VERSION=1.82.0 \ LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 @@ -265,6 +265,16 @@ RUN set -eux; \ rm rustup-init; \ chmod -R a+w $RUSTUP_HOME $CARGO_HOME; +WORKDIR /usr/local + +# Install emscripten SDK and activate it +RUN set -eux; \ + git clone https://github.com/emscripten-core/emsdk.git; \ + cd emsdk; \ + ./emsdk install latest; \ + ./emsdk activate latest; \ + rustup target add wasm32-unknown-emscripten; + WORKDIR /home COPY files/nginx.conf /etc/nginx/nginx.conf diff --git a/docker/devenv/files/bashrc b/docker/devenv/files/bashrc index da3fdfde0..63252fdfc 100644 --- a/docker/devenv/files/bashrc +++ b/docker/devenv/files/bashrc @@ -11,6 +11,8 @@ alias lsf='ls -h *(.)' # init Cargo / Rust env . "/usr/local/cargo/env" +# init emscripten +EMSDK_QUIET=1 . "/usr/local/emsdk/emsdk_env.sh" # include .bashrc if it exists if [ -f "$HOME/.bashrc.local" ]; then diff --git a/frontend/package.json b/frontend/package.json index 48ee01e78..63c2b0144 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,6 @@ "build:storybook": "yarn run build:storybook:assets && yarn run build:storybook:cljs && storybook build", "build:storybook:assets": "node ./scripts/build-storybook-assets.js", "build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook", - "build:renderer": "yarn run wasm-pack build ./renderer --target web --out-dir ../resources/public/js/renderer --release", "build:app": "clojure -M:dev:shadow-cljs release main", "e2e:server": "node ./scripts/e2e-server.js", "fmt:clj": "cljfmt fix --parallel=true src/ test/", diff --git a/frontend/renderer/Cargo.lock b/frontend/renderer/Cargo.lock deleted file mode 100644 index c14faa4ad..000000000 --- a/frontend/renderer/Cargo.lock +++ /dev/null @@ -1,324 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cc" -version = "1.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "minicov" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" -dependencies = [ - "cc", - "walkdir", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "renderer" -version = "0.1.0" -dependencies = [ - "wasm-bindgen", - "wasm-bindgen-test", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "syn" -version = "2.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "minicov", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "web-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/frontend/renderer/src/lib.rs b/frontend/renderer/src/lib.rs deleted file mode 100644 index e4b08cfb0..000000000 --- a/frontend/renderer/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -use wasm_bindgen::prelude::*; - -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - -#[wasm_bindgen] -pub fn print(msg: &str) { - log(msg); -} - -#[cfg(test)] -mod tests { - use super::*; - use wasm_bindgen_test::*; - - wasm_bindgen_test_configure!(run_in_browser); - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } - - #[wasm_bindgen_test] - fn it_works_in_wasm() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 78c0f8615..7ac3535bf 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -75,7 +75,6 @@ [app.main.repo :as rp] [app.main.streams :as ms] [app.main.worker :as uw] - [app.renderer-v2 :as renderer] [app.util.dom :as dom] [app.util.globals :as ug] [app.util.http :as http] @@ -357,8 +356,8 @@ (dcm/retrieve-comment-threads file-id) (fetch-bundle project-id file-id)) - (when (contains? cf/flags :renderer-v2) - (rx/of (renderer/init))) + ;; (when (contains? cf/flags :renderer-v2) + ;; (rx/of (renderer/init))) (->> stream (rx/filter dch/commit?) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 082c7cb52..9fef0d2df 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.config :as cf] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.persistence :as dps] @@ -32,7 +31,6 @@ [app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox]] [app.main.ui.workspace.viewport :refer [viewport]] - [app.renderer-v2 :as renderer] [app.util.debug :as dbg] [app.util.dom :as dom] [app.util.globals :as globals] @@ -204,10 +202,6 @@ (ntf/hide) (dw/finalize-file project-id file-id)))) - (mf/with-effect [file-ready?] - (when (and file-ready? (contains? cf/flags :renderer-v2)) - (renderer/print-msg "hello from wasm fn!"))) - [:& (mf/provider ctx/current-file-id) {:value file-id} [:& (mf/provider ctx/current-project-id) {:value project-id} [:& (mf/provider ctx/current-team-id) {:value team-id} diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0e906c47e..4bf48ddd0 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -50,8 +50,10 @@ [app.main.ui.workspace.viewport.utils :as utils] [app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]] [app.main.ui.workspace.viewport.widgets :as widgets] + [app.render-wasm :as render.wasm] [app.util.debug :as dbg] [beicon.v2.core :as rx] + [promesa.core :as p] [rumext.v2 :as mf])) ;; --- Viewport @@ -137,6 +139,9 @@ [viewport-ref on-viewport-ref] (create-viewport-ref) + canvas-ref (mf/use-ref nil) + canvas-init (mf/use-ref false) + ;; VARS disable-paste (mf/use-var false) in-viewport? (mf/use-var false) @@ -269,6 +274,21 @@ rule-area-size (/ rulers/ruler-area-size zoom)] + (when ^boolean render.wasm/enabled? + (mf/with-effect [] + (when-let [canvas (mf/ref-val canvas-ref)] + (->> render.wasm/module + (p/fmap (fn [ready?] + (when ready? + (mf/set-ref-val! canvas-init true) + (render.wasm/assign-canvas canvas))))) + (fn [] + (render.wasm/clear-canvas)))) + + (mf/with-effect [vbox' base-objects] + (when (mf/ref-val canvas-init) + (render.wasm/draw-objects base-objects zoom vbox')))) + (hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?) (hooks/setup-viewport-size vport viewport-ref) (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? read-only?) @@ -312,50 +332,59 @@ :layout layout :viewport-ref viewport-ref}])] - [:svg - {:id "render" - :class (stl/css :render-shapes) - :xmlns "http://www.w3.org/2000/svg" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot "https://penpot.app/xmlns" - :preserveAspectRatio "xMidYMid meet" - :key (str "render" page-id) - :width (:width vport 0) - :height (:height vport 0) - :view-box (utils/format-viewbox vbox) - :style {:background-color background - :pointer-events "none"} - :fill "none"} + (if ^boolean render.wasm/enabled? + [:canvas {:id "render" + :ref canvas-ref + :class (stl/css :render-shapes) + :key (dm/str "render" page-id) + :width (:width vport 0) + :height (:height vport 0) + :style {:background-color background + :pointer-events "none"}}] + [:svg + {:id "render" + :class (stl/css :render-shapes) + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot "https://penpot.app/xmlns" + :preserveAspectRatio "xMidYMid meet" + :key (str "render" page-id) + :width (:width vport 0) + :height (:height vport 0) + :view-box (utils/format-viewbox vbox) + :style {:background-color background + :pointer-events "none"} + :fill "none"} - [:defs - [:linearGradient {:id "frame-placeholder-gradient"} - [:animateTransform - {:attributeName "gradientTransform" - :type "translate" - :from "-1 0" - :to "1 0" - :dur "2s" - :repeatCount "indefinite"}] - [:stop {:offset "0%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}] - [:stop {:offset "50%" :stop-color (str "color-mix(in srgb-linear, " background " 80%, #777)") :stop-opacity 1}] - [:stop {:offset "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]] + [:defs + [:linearGradient {:id "frame-placeholder-gradient"} + [:animateTransform + {:attributeName "gradientTransform" + :type "translate" + :from "-1 0" + :to "1 0" + :dur "2s" + :repeatCount "indefinite"}] + [:stop {:offset "0%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}] + [:stop {:offset "50%" :stop-color (str "color-mix(in srgb-linear, " background " 80%, #777)") :stop-opacity 1}] + [:stop {:offset "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]] - (when (dbg/enabled? :show-export-metadata) - [:& use/export-page {:page page}]) + (when (dbg/enabled? :show-export-metadata) + [:& use/export-page {:page page}]) - ;; We need a "real" background shape so layer transforms work properly in firefox - [:rect {:width (:width vbox 0) - :height (:height vbox 0) - :x (:x vbox 0) - :y (:y vbox 0) - :fill background}] + ;; We need a "real" background shape so layer transforms work properly in firefox + [:rect {:width (:width vbox 0) + :height (:height vbox 0) + :x (:x vbox 0) + :y (:y vbox 0) + :fill background}] - [:& (mf/provider ctx/current-vbox) {:value vbox'} - [:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)} - ;; Render root shape - [:& shapes/root-shape {:key page-id - :objects base-objects - :active-frames @active-frames}]]]] + [:& (mf/provider ctx/current-vbox) {:value vbox'} + [:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)} + ;; Render root shape + [:& shapes/root-shape {:key page-id + :objects base-objects + :active-frames @active-frames}]]]]) [:svg.viewport-controls {:xmlns "http://www.w3.org/2000/svg" diff --git a/frontend/src/app/render_wasm.cljs b/frontend/src/app/render_wasm.cljs new file mode 100644 index 000000000..afb3038ba --- /dev/null +++ b/frontend/src/app/render_wasm.cljs @@ -0,0 +1,88 @@ +;; 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 + "A WASM based render API" + (:require + [app.common.data.macros :as dm] + [app.config :as cf] + [promesa.core :as p])) + +(def enabled? + (contains? cf/flags :render-wasm)) + +(defonce ^:dynamic internal-module #js {}) +(defonce ^:dynamic internal-gpu-state #js {}) + +(defn draw-objects [objects zoom vbox] + (let [draw-rect (unchecked-get internal-module "_draw_rect") + translate (unchecked-get internal-module "_translate") + reset-canvas (unchecked-get internal-module "_reset_canvas") + scale (unchecked-get internal-module "_scale") + flush (unchecked-get internal-module "_flush") + gpu-state internal-gpu-state] + + (js/requestAnimationFrame + (fn [] + (reset-canvas gpu-state) + (scale gpu-state zoom zoom) + + (let [x (dm/get-prop vbox :x) + y (dm/get-prop vbox :y)] + (translate gpu-state (- x) (- y))) + + (run! (fn [shape] + (let [selrect (dm/get-prop shape :selrect) + x1 (dm/get-prop selrect :x1) + y1 (dm/get-prop selrect :y1) + x2 (dm/get-prop selrect :x2) + y2 (dm/get-prop selrect :y2)] + (draw-rect gpu-state x1 y1 x2 y2))) + (vals objects)) + + (flush gpu-state))))) + +(def canvas-options + #js {:antialias true + :depth true + :stencil true + :alpha true}) + +(defn clear-canvas + [] + ;; TODO: perform corresponding cleaning + ) + +(defn assign-canvas + [canvas] + (let [gl (unchecked-get internal-module "GL") + init-fn (unchecked-get internal-module "_init") + + context (.getContext ^js canvas "webgl2" canvas-options) + + ;; Register the context with emscripten + handle (.registerContext ^js gl context #js {"majorVersion" 2}) + _ (.makeContextCurrent ^js gl handle) + + ;; Initialize Skia + state (init-fn (.-width ^js canvas) + (.-height ^js canvas))] + + (set! (.-width canvas) (.-clientWidth ^js canvas)) + (set! (.-height canvas) (.-clientHeight ^js canvas)) + (set! internal-gpu-state state))) + +(defonce module + (->> (js/dynamicImport "/js/render_wasm.js") + (p/mcat (fn [module] + (let [default (unchecked-get module "default")] + (default)))) + (p/fmap (fn [module] + (set! internal-module module) + true)) + (p/merr (fn [cause] + (js/console.error cause) + (p/resolved false))))) diff --git a/frontend/src/app/renderer_v2.cljs b/frontend/src/app/renderer_v2.cljs deleted file mode 100644 index 10509cf8e..000000000 --- a/frontend/src/app/renderer_v2.cljs +++ /dev/null @@ -1,38 +0,0 @@ -;; 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.renderer-v2 - (:require - [app.config :as cf] - [beicon.v2.core :as rx] - [potok.v2.core :as ptk])) - -(defonce internal-module #js {}) - -(defn on-module-loaded - [module'] - (let [init-fn (.-default ^js module')] - (->> (rx/from (init-fn)) - (rx/map (constantly module'))))) - -(defn- on-module-initialized - [module] - (set! internal-module module)) - -(defn print-msg [msg] - (let [print-fn (.-print internal-module)] - (print-fn msg))) - -(defn init - [] - (ptk/reify ::init - ptk/WatchEvent - (watch [_ _ _] - (let [module-uri (assoc cf/public-uri :path "/js/renderer/renderer.js")] - (->> (rx/from (js/dynamicImport (str module-uri))) - (rx/mapcat on-module-loaded) - (rx/tap on-module-initialized) - (rx/ignore)))))) diff --git a/frontend/renderer/.gitignore b/render-wasm/.gitignore similarity index 100% rename from frontend/renderer/.gitignore rename to render-wasm/.gitignore diff --git a/render-wasm/Cargo.lock b/render-wasm/Cargo.lock new file mode 100644 index 000000000..f8079959d --- /dev/null +++ b/render-wasm/Cargo.lock @@ -0,0 +1,657 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "prettyplease" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "render" +version = "0.1.0" +dependencies = [ + "gl", + "skia-safe", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "skia-bindings" +version = "0.78.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29880a81b088de322e9c5306236c70761a61b5fa4df3c15c93bad3ce890ce34c" +dependencies = [ + "bindgen", + "cc", + "flate2", + "heck", + "lazy_static", + "regex", + "serde_json", + "tar", + "toml", +] + +[[package]] +name = "skia-safe" +version = "0.78.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f15700ac678c06649077495acbba07e7ae01e5ca46b7dc18213f2c3477ada71" +dependencies = [ + "bitflags", + "lazy_static", + "skia-bindings", +] + +[[package]] +name = "syn" +version = "2.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "xml-rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" diff --git a/frontend/renderer/Cargo.toml b/render-wasm/Cargo.toml similarity index 55% rename from frontend/renderer/Cargo.toml rename to render-wasm/Cargo.toml index 56724cc1a..66cff051d 100644 --- a/frontend/renderer/Cargo.toml +++ b/render-wasm/Cargo.toml @@ -1,19 +1,18 @@ [package] -name = "renderer" +name = "render" version = "0.1.0" edition = "2021" repository = "https://github.com/penpot/penpot" -license-file = "../../../../LICENSE" +license-file = "../LICENSE" description = "Wasm-based canvas renderer for Penpot" -[lib] -crate-type = ["cdylib"] +[[bin]] +name = "render_wasm" +path = "src/main.rs" [dependencies] -wasm-bindgen = "0.2.93" +gl = "0.14.0" +skia-safe = { version = "0.78.2", features = ["gl"] } [profile.release] opt-level = "s" - -[dev-dependencies] -wasm-bindgen-test = "0.3.43" diff --git a/render-wasm/build b/render-wasm/build new file mode 100755 index 000000000..9d90c4c66 --- /dev/null +++ b/render-wasm/build @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +export _BUILD_MODE=${1:-debug}; + +export EMSDK_QUIET=1 +export EMCC_CFLAGS="--no-entry -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s MAX_WEBGL_VERSION=2 -s MODULARIZE=1 -s EXPORT_NAME=createRustSkiaModule -s EXPORTED_RUNTIME_METHODS=GL -s ENVIRONMENT=web -s EXPORT_ES6=1 -sMODULARIZE" + +source /usr/local/emsdk/emsdk_env.sh; + +set -x + +_SCRIPT_DIR=$(dirname $0); +_CARGO_PARAMS="--target=wasm32-unknown-emscripten"; + +if [ "$_BUILD_MODE" = "release" ]; then + _CARGO_PARAMS="--release $_CARGO_PARAMS" +fi + +pushd $_SCRIPT_DIR; +cargo build $_CARGO_PARAMS + +cp target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.js ../frontend/resources/public/js/ +cp target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/ + +popd diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs new file mode 100644 index 000000000..bbef6bdf2 --- /dev/null +++ b/render-wasm/src/main.rs @@ -0,0 +1,148 @@ +use skia_safe::gpu::{self, gl::FramebufferInfo, DirectContext}; +use std::boxed::Box; + +use skia_safe as skia; + +extern "C" { + pub fn emscripten_GetProcAddress( + name: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_void; +} + +struct GpuState { + context: DirectContext, + framebuffer_info: FramebufferInfo, +} + +/// This struct holds the state of the Rust application between JS calls. +/// +/// It is created by [init] and passed to the other exported functions. Note that rust-skia data +/// structures are not thread safe, so a state must not be shared between different Web Workers. +pub struct State { + gpu_state: GpuState, + surface: skia::Surface, +} + +impl State { + fn new(gpu_state: GpuState, surface: skia::Surface) -> Self { + State { gpu_state, surface } + } + + fn set_surface(&mut self, surface: skia::Surface) { + self.surface = surface; + } +} + +fn init_gl() { + unsafe { + gl::load_with(|addr| { + let addr = std::ffi::CString::new(addr).unwrap(); + emscripten_GetProcAddress(addr.into_raw() as *const _) as *const _ + }); + } +} + +/// This needs to be done once per WebGL context. +fn create_gpu_state() -> GpuState { + let interface = skia_safe::gpu::gl::Interface::new_native().unwrap(); + let context = skia_safe::gpu::direct_contexts::make_gl(interface, None).unwrap(); + let framebuffer_info = { + let mut fboid: gl::types::GLint = 0; + unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + + FramebufferInfo { + fboid: fboid.try_into().unwrap(), + format: skia_safe::gpu::gl::Format::RGBA8.into(), + protected: skia_safe::gpu::Protected::No, + } + }; + + GpuState { + context, + framebuffer_info, + } +} + +/// Create the Skia surface that will be used for rendering. +fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) -> skia::Surface { + let backend_render_target = + gpu::backend_render_targets::make_gl((width, height), 1, 8, gpu_state.framebuffer_info); + + gpu::surfaces::wrap_backend_render_target( + &mut gpu_state.context, + &backend_render_target, + skia_safe::gpu::SurfaceOrigin::BottomLeft, + skia_safe::ColorType::RGBA8888, + None, + None, + ) + .unwrap() +} + +fn render_rect(surface: &mut skia::Surface, rect: skia::Rect, color: skia::Color) { + let mut paint = skia::Paint::default(); + paint.set_style(skia::PaintStyle::Fill); + paint.set_color(color); + paint.set_anti_alias(true); + surface.canvas().draw_rect(rect, &paint); +} + +/// This is called from JS after the WebGL context has been created. +#[no_mangle] +pub extern "C" fn init(width: i32, height: i32) -> Box { + let mut gpu_state = create_gpu_state(); + let surface = create_surface(&mut gpu_state, width, height); + + let state = State::new(gpu_state, surface); + + Box::new(state) +} + +/// This is called from JS when the window is resized. +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn resize_surface(state: *mut State, width: i32, height: i32) { + let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); + let surface = create_surface(&mut state.gpu_state, width, height); + state.set_surface(surface); +} + +/// Draws a rect at the specified coordinates with the give ncolor +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn draw_rect(state: *mut State, x1: f32, y1: f32, x2: f32, y2: f32) { + let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); + let r = skia::Rect::new(x1, y1, x2, y2); + render_rect(&mut state.surface, r, skia::Color::RED); +} + +#[no_mangle] +pub unsafe extern "C" fn flush(state: *mut State) { + let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); + state + .gpu_state + .context + .flush_and_submit_surface(&mut state.surface, None); +} + +#[no_mangle] +pub unsafe extern "C" fn translate(state: *mut State, dx: f32, dy: f32) { + (*state).surface.canvas().translate((dx, dy)); +} + +#[no_mangle] +pub unsafe extern "C" fn scale(state: *mut State, sx: f32, sy: f32) { + (*state).surface.canvas().scale((sx, sy)); +} + +#[no_mangle] +pub unsafe extern "C" fn reset_canvas(state: *mut State) { + let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); + state.surface.canvas().clear(skia_safe::Color::TRANSPARENT); + state.surface.canvas().reset_matrix(); + flush(state); +} + +fn main() { + init_gl(); +}