From 01886d95ff3c57edfb6c369b872205acf56f1587 Mon Sep 17 00:00:00 2001 From: Sebastien Jourdain Date: Wed, 13 May 2026 13:10:09 -0600 Subject: [PATCH] feat(wasm64): add support for 32 and 64 bits --- examples/vtk/cone.py | 20 +++++++----- src/trame_vtklocal/module/__init__.py | 6 ++-- src/trame_vtklocal/module/wasm.py | 22 +++++++------ vue-components/src/components/VtkLocal.js | 38 +++++++++++++++-------- 4 files changed, 54 insertions(+), 32 deletions(-) diff --git a/examples/vtk/cone.py b/examples/vtk/cone.py index 899d832..e3bdfe3 100644 --- a/examples/vtk/cone.py +++ b/examples/vtk/cone.py @@ -1,10 +1,10 @@ +# Required for vtk factory +import vtkmodules.vtkRenderingOpenGL2 # noqa from trame.app import get_server -from trame.ui.html import DivLayout -from trame.widgets import html, client -from trame_vtklocal.widgets import vtklocal from trame.decorators import TrameApp, change, trigger - +from trame.ui.html import DivLayout from vtkmodules.vtkFiltersSources import vtkConeSource +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa from vtkmodules.vtkRenderingCore import ( vtkActor, vtkPolyDataMapper, @@ -13,9 +13,8 @@ vtkRenderWindowInteractor, ) -# Required for vtk factory -import vtkmodules.vtkRenderingOpenGL2 # noqa -from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa +from trame.widgets import client, html +from trame_vtklocal.widgets import vtklocal CLIENT_TYPE = "vue3" @@ -81,6 +80,9 @@ def on_opacity_change(self, opacity, **kwargs): self.actor.property.opacity = float(opacity) self.html_view.update_throttle() + def on_camera(self, camera): + print(camera) + def _ui(self): with DivLayout(self.server) as layout: client.Style("body { margin: 0; }") @@ -94,6 +96,10 @@ def _ui(self): emit_memory=True, memory_vtk="mem_vtk = $event", memory_arrays="mem_blob = $event", + config=( + "{rendering: 'webgl', exec: 'sync', mode: 'wasm32'}", + ), # when using 64 => camera observer is not triggered (internal error) + camera=(self.on_camera, "[$event]"), ) html.Div( "Scene: {{ (mem_vtk / 1024).toFixed(1) }}KB - " diff --git a/src/trame_vtklocal/module/__init__.py b/src/trame_vtklocal/module/__init__.py index 08b4510..8e75143 100644 --- a/src/trame_vtklocal/module/__init__.py +++ b/src/trame_vtklocal/module/__init__.py @@ -1,7 +1,8 @@ from pathlib import Path + from trame_vtklocal import __version__ -from trame_vtklocal.module.wasm import register_wasm from trame_vtklocal.module.protocol import ObjectManagerHelper +from trame_vtklocal.module.wasm import register_wasm __all__ = [ "serve", @@ -34,4 +35,5 @@ def setup(trame_server, **kwargs): HELPERS_PER_SERVER[trame_server.name] = ObjectManagerHelper( trame_server, addon_serdes_registrars=kwargs.pop("addon_serdes_registrars", []) ) - trame_server.enable_module(register_wasm(serve_path, **kwargs)) + trame_server.enable_module(register_wasm(serve_path, wasm_bits="wasm64", **kwargs)) + trame_server.enable_module(register_wasm(serve_path, wasm_bits="wasm32", **kwargs)) diff --git a/src/trame_vtklocal/module/wasm.py b/src/trame_vtklocal/module/wasm.py index 8174a5d..c2dfa9d 100644 --- a/src/trame_vtklocal/module/wasm.py +++ b/src/trame_vtklocal/module/wasm.py @@ -1,9 +1,10 @@ import asyncio -import aiohttp -from pathlib import Path import os import shutil import tarfile +from pathlib import Path + +import aiohttp from packaging.version import parse from trame_vtklocal import __version__ @@ -50,18 +51,17 @@ async def setup_wasm_directory(target_directory, wasm_url): Path(dest_file).unlink() -def get_wasm_info(): +def get_wasm_info(wasm_bits="wasm32"): from vtkmodules.vtkCommonCore import vtkVersion vtk_version = vtkVersion() - wasm_bits = "wasm32" version = vtk_version.GetVTKVersion() url = f"https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-{wasm_bits}-emscripten/{version}/vtk-{version}-{wasm_bits}-emscripten.tar.gz" return version, url -def register_wasm(serve_path, **kwargs): +def register_wasm(serve_path, wasm_bits="wasm32", **kwargs): """Register the VTK WebAssembly files in the given serve path. Keywords: wasm_url: The URL to the VTK WebAssembly files. It is used if wasm_dir is not provided and VTK_WASM_DIR_OVERRIDE @@ -69,10 +69,10 @@ def register_wasm(serve_path, **kwargs): wasm_base_name: The base name of the VTK WebAssembly files. (default: "vtk") wasm_dir: The directory containing the VTK WebAssembly files. """ - version, wasm_url = get_wasm_info() + version, wasm_url = get_wasm_info(wasm_bits) wasm_base_name = kwargs.get("wasm_base_name", "vtk") - BASE_URL = f"__trame_vtklocal_{__version__}/wasm/{version}" - dest_directory = Path(serve_path) / "wasm" / version + BASE_URL = f"__trame_vtklocal_{__version__}/{wasm_bits}/{version}" + dest_directory = Path(serve_path) / wasm_bits / version # get wasm directory from kwargs or environment variable wasm_dir = kwargs.get("wasm_dir", os.environ.get("VTK_WASM_DIR_OVERRIDE")) @@ -116,7 +116,9 @@ def register_wasm(serve_path, **kwargs): return dict( state={ - "__trame_vtklocal_wasm_url": BASE_URL, - "__trame_vtklocal_wasm_base_name": wasm_base_name, + f"__trame_vtklocal_{wasm_bits}": { + "url": BASE_URL, + "basename": wasm_base_name, + }, }, ) diff --git a/vue-components/src/components/VtkLocal.js b/vue-components/src/components/VtkLocal.js index abf2be1..4534186 100644 --- a/vue-components/src/components/VtkLocal.js +++ b/vue-components/src/components/VtkLocal.js @@ -81,6 +81,7 @@ export default { default: () => ({ rendering: "webgl", exec: "sync", + mode: "wasm32", }), }, listeners: { @@ -102,17 +103,20 @@ export default { autoResize: { type: Boolean, default: true, - } + }, }, setup(props, { emit }) { + const trame = inject("trame"); + // Create global WASM handler if missing if (props.useHandler && !WASM_HANDLERS[props.useHandler]) { WASM_HANDLERS[props.useHandler] = new RemoteSession(); } + const wasmBits = props.config?.mode || "wasm32"; + const { url: wasmURL, basename: wasmBaseName } = trame.state.get( + `__trame_vtklocal_${wasmBits}`, + ); - const trame = inject("trame"); - const wasmURL = trame.state.get("__trame_vtklocal_wasm_url"); - const wasmBaseName = trame.state.get("__trame_vtklocal_wasm_base_name"); const cameraTags = []; const listenersTags = []; const container = ref(null); @@ -256,11 +260,11 @@ export default { if (props.emitMemory) { emit( "memory-vtk", - wasmManager.sceneManager.getTotalVTKDataObjectMemoryUsage(), + Number(wasmManager.sceneManager.getTotalVTKDataObjectMemoryUsage()), ); emit( "memory-arrays", - wasmManager.sceneManager.getTotalBlobMemoryUsage(), + Number(wasmManager.sceneManager.getTotalBlobMemoryUsage()), ); } } @@ -314,7 +318,11 @@ export default { // startWebXR ---------------------------------------------------------------- function startWebXR(mode, requiredFeatures, optionalFeatures) { - wasmManager.sceneManager.startWebXR(mode, requiredFeatures, optionalFeatures); + wasmManager.sceneManager.startWebXR( + mode, + requiredFeatures, + optionalFeatures, + ); } // stopWebXR ---------------------------------------------------------------- @@ -406,12 +414,16 @@ export default { } else { // New API - starting with vtk 9.5 wasmManager.cameraIds.forEach((cid) => { - cameraTags.push([ - cid, - wasmManager.sceneManager.observe(cid, "ModifiedEvent", () => { - emit("camera", wasmManager.getState(cid)); - }), - ]); + try { + cameraTags.push([ + cid, + wasmManager.sceneManager.observe(cid, "ModifiedEvent", () => { + emit("camera", wasmManager.getState(cid)); + }), + ]); + } catch (err) { + console.error("wasm64 has issue with observer", err); + } }); }