diff --git a/frontend/src/app/main/data/render_wasm.cljs b/frontend/src/app/main/data/render_wasm.cljs new file mode 100644 index 000000000..e55d98754 --- /dev/null +++ b/frontend/src/app/main/data/render_wasm.cljs @@ -0,0 +1,17 @@ +(ns app.main.data.render-wasm + (:require + [potok.v2.core :as ptk])) + +(defn context-lost + [] + (ptk/reify ::context-lost + ptk/UpdateEvent + (update [_ state] + (update state :render-state #(assoc % :lost true))))) + +(defn context-restored + [] + (ptk/reify ::context-restored + ptk/UpdateEvent + (update [_ state] + (update state :render-state #(dissoc % :lost))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index f0a8db187..9a8ac024a 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -114,6 +114,12 @@ ;; ---- Workspace refs +(def render-state + (l/derived :render-state st/state)) + +(def render-context-lost? + (l/derived :lost render-state)) + (def workspace-local (l/derived :workspace-local st/state)) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index a6fd17f43..f5c6963cf 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -111,6 +111,8 @@ modifiers (mf/deref refs/workspace-modifiers) text-modifiers (mf/deref refs/workspace-text-modifier) + render-context-lost? (mf/deref refs/render-context-lost?) + objects-modified (mf/with-memo [base-objects text-modifiers modifiers] (apply-modifiers-to-selected selected base-objects text-modifiers modifiers)) @@ -175,6 +177,8 @@ mode-inspect? (= options-mode :inspect) + on-render-restore-context #(.reload js/location) + on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?) on-context-menu (actions/on-context-menu hover hover-ids read-only?) on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? read-only?) @@ -277,9 +281,9 @@ (p/fmap (fn [ready?] (when ready? (reset! canvas-init? true) - (render.wasm/assign-canvas canvas))))) + (render.wasm/setup-canvas canvas))))) (fn [] - (render.wasm/clear-canvas)))) + (render.wasm/dispose-canvas canvas)))) (mf/with-effect [objects-modified canvas-init?] (when @canvas-init? @@ -635,4 +639,11 @@ {:objects base-objects :zoom zoom :vbox vbox - :bottom-padding (when palete-size (+ palete-size 8))}]]]]])) + :bottom-padding (when palete-size (+ palete-size 8))}]]]] + + (when render-context-lost? + [:div {:id "context-lost" :class (stl/css :context-lost)} + [:h1 "GL Error Screen"] + [:button + {:on-click on-render-restore-context} + "Restore context"]])])) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.scss b/frontend/src/app/main/ui/workspace/viewport_wasm.scss index 727a6c529..a83fde465 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.scss +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.scss @@ -35,3 +35,13 @@ right: 0; z-index: 10; } + +.context-lost { + position: fixed; + inset: 0; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); + display: grid; + place-items: center; + cursor: default; +} diff --git a/frontend/src/app/render_wasm.cljs b/frontend/src/app/render_wasm.cljs index 44086efd7..20c769d93 100644 --- a/frontend/src/app/render_wasm.cljs +++ b/frontend/src/app/render_wasm.cljs @@ -11,6 +11,10 @@ [app.common.files.helpers :as cfh] [app.common.types.shape.impl] [app.config :as cf] + [app.main.data.render-wasm :as drw] + [app.main.store :as st] + [app.util.debug :as dbg] + [app.util.dom :as dom] [promesa.core :as p])) (def enabled? @@ -82,29 +86,67 @@ :stencil true :alpha true}) -(defn clear-canvas - [] - ;; TODO: perform corresponding cleaning - ) +(defn init-skia + [canvas] + (let [init-fn (unchecked-get internal-module "_init") + state (init-fn (.-width ^js canvas) + (.-height ^js canvas))] + (set! internal-gpu-state state))) -(defn assign-canvas +;; NOTE: This function can be called externally +;; by the button in the context lost component (shown +;; in viewport-wasm) or called internally by +;; on-webgl-context +(defn restore-canvas + [canvas] + (st/emit! (drw/context-restored)) + ;; We need to reinitialize skia when the + ;; context is restored. + (init-skia canvas)) + +;; Handles both events: webglcontextlost and +;; webglcontextrestored +(defn on-webgl-context + [event] + (dom/prevent-default event) + (if (= (.-type event) "webglcontextlost") + (st/emit! (drw/context-lost)) + (restore-canvas (dom/get-target event)))) + +(defn dispose-canvas + [canvas] + ;; TODO: perform corresponding cleaning + (.removeEventListener canvas "webglcontextlost" on-webgl-context) + (.removeEventListener canvas "webglcontextrestored" on-webgl-context)) + +(defn init-debug-webgl-context-state + [context] + (let [context-extension (.getExtension ^js context "WEBGL_lose_context") + info-extension (.getExtension ^js context "WEBGL_debug_renderer_info")] + (set! (.-penpotGL js/window) #js {:context context-extension + :renderer info-extension}) + (js/console.log "WEBGL_lose_context" context-extension) + (js/console.log "WEBGL_debug_renderer_info" info-extension))) + +(defn setup-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) + _ (.makeContextCurrent ^js gl handle)] - ;; Initialize Skia - state (init-fn (.-width ^js canvas) - (.-height ^js canvas))] + (when (dbg/enabled? :gl-context) + (init-debug-webgl-context-state context)) + + (.addEventListener canvas "webglcontextlost" on-webgl-context) + (.addEventListener canvas "webglcontextrestored" on-webgl-context) (set! (.-width canvas) (.-clientWidth ^js canvas)) (set! (.-height canvas) (.-clientHeight ^js canvas)) - (set! internal-gpu-state state))) + + (init-skia canvas))) (defonce module (->> (js/dynamicImport "/js/render_wasm.js") diff --git a/frontend/src/app/util/debug.cljs b/frontend/src/app/util/debug.cljs index 094550cef..8342da71c 100644 --- a/frontend/src/app/util/debug.cljs +++ b/frontend/src/app/util/debug.cljs @@ -89,7 +89,10 @@ :display-touched ;; Show some visual indicators for bool shape - :bool-shapes}) + :bool-shapes + + ;; Show some information about the WebGL context. + :gl-context}) (defn enable! [option] diff --git a/render-wasm/Cargo.lock b/render-wasm/Cargo.lock index f8079959d..7595770cd 100644 --- a/render-wasm/Cargo.lock +++ b/render-wasm/Cargo.lock @@ -96,6 +96,22 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "emscripten-functions" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c026cc030b24957ca45d9555f9fa241d6b3a01d725cd98a25924de249b840a" +dependencies = [ + "cc", + "emscripten-functions-sys", +] + +[[package]] +name = "emscripten-functions-sys" +version = "4.1.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65715a5f07b03636d7cd5508a45d1b62486840cb7d91a66564a73f1d7aa70b79" + [[package]] name = "equivalent" version = "1.0.1" @@ -370,6 +386,8 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" name = "render" version = "0.1.0" dependencies = [ + "emscripten-functions", + "emscripten-functions-sys", "gl", "skia-safe", ] diff --git a/render-wasm/Cargo.toml b/render-wasm/Cargo.toml index 66cff051d..a0de796ea 100644 --- a/render-wasm/Cargo.toml +++ b/render-wasm/Cargo.toml @@ -11,6 +11,8 @@ name = "render_wasm" path = "src/main.rs" [dependencies] +emscripten-functions = "0.2.3" +emscripten-functions-sys = "4.1.67" gl = "0.14.0" skia-safe = { version = "0.78.2", features = ["gl"] }