Skip to content
Snippets Groups Projects
Commit 540986cd authored by Jaswant Panchumarti (Kitware)'s avatar Jaswant Panchumarti (Kitware)
Browse files

wasm-ui: replace SDL2 with emscripten APIs

- this works in wasm64 and web workers
- the version of sdl2 ported by emscripten did not work in 64-bit and web workers.
- cleaned up cmake code path taken VTK_USE_SDL2 is enabled. vtkSDL2RenderWindowInteractor should still build when enabled.
parent 6f90f44a
No related branches found
No related tags found
No related merge requests found
## vtkWebAssemblyRenderWindowInteractor: Support wasm64 and web workers
`vtkWebAssemblyRenderWindowInteractor` now uses Emscripten HTML5 API for event handling
instead of using SDL2. This update ensures smoother operation in wasm64 and web worker environments.
What this means: You can now completely initialize and start processing events of a `vtkRenderWindowInteractor` within a web worker inside a browser.
......@@ -68,21 +68,24 @@ elseif (APPLE_IOS)
endif ()
elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
# Add some custom overrides
vtk_object_factory_declare(
BASE vtkRenderWindowInteractor
OVERRIDE vtkWebAssemblyRenderWindowInteractor)
list(APPEND classes vtkWebAssemblyRenderWindowInteractor)
# VTK_DEPRECATED_IN_9_3_0() Remove when vtkSDL2RenderWindowInteractor is removed.
if (VTK_USE_SDL2)
vtk_object_factory_declare(
BASE vtkRenderWindowInteractor
OVERRIDE vtkSDL2RenderWindowInteractor)
list(APPEND classes vtkSDL2RenderWindowInteractor)
else ()
vtk_object_factory_declare(
BASE vtkRenderWindowInteractor
OVERRIDE vtkWebAssemblyRenderWindowInteractor)
list(APPEND classes vtkWebAssemblyRenderWindowInteractor)
endif ()
else () # use generic
vtk_object_factory_declare(
BASE vtkRenderWindowInteractor
OVERRIDE vtkGenericRenderWindowInteractor)
endif ()
# VTK_DEPRECATED_IN_9_3_0() Remove when vtkSDL2RenderWindowInteractor is removed.
if (VTK_USE_SDL2)
list(APPEND classes vtkSDL2RenderWindowInteractor)
endif ()
vtk_object_factory_configure(
SOURCE_FILE vtk_object_factory_source
HEADER_FILE vtk_object_factory_header
......@@ -99,13 +102,20 @@ vtk_module_add_module(VTK::RenderingUI
)
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
# The -sUSE_SDL=2 flag will inform emcc driver to add include paths.
vtk_module_compile_options(VTK::RenderingUI
PRIVATE
"-sUSE_SDL=2")
vtk_module_LINK_options(VTK::RenderingUI
PRIVATE
"-sUSE_SDL=2")
# VTK_DEPRECATED_IN_9_3_0() Remove when vtkSDL2RenderWindowInteractor is removed.
if (VTK_USE_SDL2)
# The -sUSE_SDL=2 flag will inform emcc driver to add include paths.
vtk_module_compile_options(VTK::RenderingUI
PRIVATE
"-sUSE_SDL=2")
vtk_module_LINK_options(VTK::RenderingUI
PRIVATE
"-sUSE_SDL=2")
else ()
vtk_module_link_options(VTK::RenderingUI
INTERFACE
"--js-library=${CMAKE_CURRENT_SOURCE_DIR}/vtkWebAssemblyRenderWindowInteractor.js")
endif ()
endif ()
if (VTK_USE_X)
......
This diff is collapsed.
......@@ -20,13 +20,15 @@
#error "vtkWebAssemblyRenderWindowInteractor requires the Emscripten SDK"
#endif
#include "vtkDeprecation.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderingUIModule.h" // For export macro
#include "vtkWrappingHints.h" // For VTK_MARSHALAUTO
#include <deque> // for ivar
#include <map> // for ivar
VTK_ABI_NAMESPACE_BEGIN
class vtkEmscriptenEventHandler;
class VTKRENDERINGUI_EXPORT VTK_MARSHALAUTO vtkWebAssemblyRenderWindowInteractor
: public vtkRenderWindowInteractor
{
......@@ -65,12 +67,9 @@ public:
/**
* Specify the selector of the canvas element in the DOM.
* Note that the current implementation of canvas in SDL2
* is hardcoded to a DOM element with id="canvas".
* Any other values are NOT supported.
*/
vtkGetStringMacro(CanvasSelector);
vtkSetStringMacro(CanvasSelector);
vtkGetStringMacro(CanvasId);
vtkSetStringMacro(CanvasId);
/**
* When true (default), the style of the parent element of canvas will be adjusted
......@@ -80,11 +79,37 @@ public:
vtkSetMacro(ExpandCanvasToContainer, bool);
vtkBooleanMacro(ExpandCanvasToContainer, bool);
struct TimerBridgeData
{
std::shared_ptr<vtkEmscriptenEventHandler> Handler;
int TimerId;
};
/**
* When true (default), a JavaScript `ResizeObserver` is installed on the parent element of
* the canvas. The observer shall adjust the `width` and `height` of the canvas element
* according the dimensions of the parent element.
*/
vtkGetMacro(InstallHTMLResizeObserver, bool);
vtkSetMacro(InstallHTMLResizeObserver, bool);
vtkBooleanMacro(InstallHTMLResizeObserver, bool);
protected:
vtkWebAssemblyRenderWindowInteractor();
~vtkWebAssemblyRenderWindowInteractor() override;
bool ProcessEvent(void* event);
using EventType = int;
using EventData = const void*;
struct Event
{
EventType Type;
EventData Data;
};
std::deque<Event> Events;
std::map<int, TimerBridgeData> Timers;
bool ProcessEvent(Event* event);
///@{
/**
......@@ -97,15 +122,6 @@ protected:
int InternalDestroyTimer(int platformTimerId) override;
///@}
/**
* Sets up resize observer on the parent element of canvas.
* The resize observer will update the interactor's window size
* with the canvas dimensions.
* When ExpandCanvasToContainer is true, the canvas style and parent style
* are also initialized correctly.
*/
void InitializeCanvasElement();
std::map<int, int> VTKToPlatformTimerMap;
/**
......@@ -113,8 +129,9 @@ protected:
*/
void StartEventLoop() override;
char* CanvasSelector = nullptr;
bool ExpandCanvasToContainer = true;
char* CanvasId = nullptr;
bool ExpandCanvasToContainer;
bool InstallHTMLResizeObserver;
private:
vtkWebAssemblyRenderWindowInteractor(const vtkWebAssemblyRenderWindowInteractor&) = delete;
......@@ -122,7 +139,22 @@ private:
bool StartedMessageLoop = false;
bool ResizeObserverInstalled = false;
friend class vtkEmscriptenEventHandler;
std::shared_ptr<vtkEmscriptenEventHandler> Handler;
int RepeatCounter = 0;
};
extern "C"
{
typedef void (*vtkTimerCallbackFunc)(void*);
int vtkCreateTimer(
unsigned long duration, bool isOneShot, vtkTimerCallbackFunc callback, void* userData);
void vtkDestroyTimer(int timerId, bool isOneShot);
int* vtkGetParentElementBoundingRectSize(const char* selector);
void vtkInitializeCanvasElement(const char* selector, bool applyStyle);
}
VTK_ABI_NAMESPACE_END
#endif
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
* All of these functions are invoked from C++ vtkWebAssemblyRenderWindowInteractor.
* They are not meant for public use.
*/
var vtkWebAssemblyRenderWindowInteractor = {
$VTKCanvas__deps: ['$findCanvasEventTarget', 'malloc'],
$VTKCanvas : {
/**
* This is a general function invoked by the proxying methods below.
* Initializes the canvas at elementId and applies style so that the canvas expands to
* take up entire space of the parent element.
* Here is how you can call this from C/C++ code:
* ```cpp
* #include "vtkWebAssemblyRenderWindowInteractor.h" // for vtkInitializeCanvasElement
* vtkInitializeCanvasElement("#canvas", true);
* ```
* @param {number} elementId the selector of the canvas.
* @param {boolean} applyStyle whether to modify the style of the canvas and parent element.
*/
initializeCanvasElement: (elementId, applyStyle) => {
const canvasElem = findCanvasEventTarget(elementId, applyStyle);
if (!canvasElem) {
return;
}
const containerElem = canvasElem.parentElement;
const body = document.querySelector('body');
if (applyStyle) {
if (body === containerElem) {
// fill up entire space of the body.
body.style.margin = 0;
body.style.width = '100vw';
body.style.height = '100vh';
} else {
containerElem.style.position = 'relative';
containerElem.style.width = '100%';
containerElem.style.height = '100%';
}
canvasElem.style.position = 'absolute';
canvasElem.style.top = 0;
canvasElem.style.left = 0;
canvasElem.style.width = '100%';
canvasElem.style.height = '100%';
}
},
/**
* This is a general function invoked by the proxying methods below.
* Get the size of the canvas from the parent element of the canvas, accounting for hi-dpi.
* Here is how you can call this from C/C++ code:
* ```cpp
* #include "vtkWebAssemblyRenderWindowInteractor.h" // for vtkGetParentElementBoundingRectSize
* int32_t* canvasSize = vtkGetParentElementBoundingRectSize("#canvas");
* ```
* @param {number} elementId the selector of the canvas.
* @returns pointer to an integer array containing width and height in pixels.
*/
getParentElementBoundingRectSize: (elementId) => {
const canvasElem = findCanvasEventTarget(elementId);
if (!canvasElem) {
return 0;
}
const containerElem = canvasElem.parentElement;
const dpr = window.devicePixelRatio;
const width = containerElem.getBoundingClientRect().width;
const height = containerElem.getBoundingClientRect().height;
const w = Math.floor(width * dpr + 0.5);
const h = Math.floor(height * dpr + 0.5);
const sizePtr = _malloc(8); // width and height get sent to C++ as 32-bit integers.
const idx = {{{ getHeapOffset('sizePtr', 'i32') }}};
HEAP32.set([w, h], idx);
return sizePtr;
},
},
/**
* Creates a one shot timer on the calling thread.
* Here is how you can call this from C/C++ code:
* ```cpp
* vtkCreateTimer(1000, true, callbackFunc, (void*)userData);
* ```
* @param {number} duration in miliseconds
* @param {boolean} isOneShot whether the timer is one shot i.e, setTimeout vs setInterval
* @param {number} callback pointer to a C/C++ callback function
* @param {number} userData pointer to a a void* argument which will be passed to callback. This is a vtkWebAssemblyRenderWindowInteractor::TimerBridgeData
* @returns an identifier which can be used to destroy the one shot timer.
* @note
* Use this instead of emscripten_set_timeout because VTK applications should be able
* to exit even when timers have not fired.
* `emscripten_set_timeout` keeps the wasm runtime alive until all timers have fired.
*/
vtkCreateTimer__sig: 'ipi**',
vtkCreateTimer: (duration, isOneShot, callback, userData) => {
if (isOneShot) {
return setTimeout(arg => {{{ makeDynCall('vp', 'callback') }}}(arg), duration, userData);
} else {
return setInterval(arg => {{{ makeDynCall('vp', 'callback') }}}(arg), duration, userData);
}
},
/**
* Destroy the given one shot timer.
* ```cpp
* vtkDestroyTimer(timerId, isOneShot);
* ```
* @param {number} platformTimerId
* @param {boolean} isOneShot whether the timer is one shot i.e, clearTimeout vs clearInterval
*/
vtkDestroyTimer: (platformTimerId, isOneShot) => {
if (isOneShot) {
clearTimeout(platformTimerId);
} else {
clearInterval(platformTimerId);
}
},
#if PTHREADS
$getParentElementBoundingRectSizeCallingThread: (elementId) => {
return VTKCanvas.getParentElementBoundingRectSize(elementId);
},
$getParentElementBoundingRectSizeMainThread__proxy: 'sync',
$getParentElementBoundingRectSizeMainThread__deps: ['$getParentElementBoundingRectSizeCallingThread'],
$getParentElementBoundingRectSizeMainThread: (elementId) => getParentElementBoundingRectSizeCallingThread(elementId),
vtkGetParentElementBoundingRectSize__deps: ['$getParentElementBoundingRectSizeMainThread'],
vtkGetParentElementBoundingRectSize__sig: 'pp',
vtkGetParentElementBoundingRectSize: (elementId) => {
return getParentElementBoundingRectSizeMainThread(elementId);
},
#else
vtkGetParentElementBoundingRectSize__sig: 'pp',
vtkGetParentElementBoundingRectSize: (elementId) => {
return VTKCanvas.getParentElementBoundingRectSize(elementId);
},
#endif
#if PTHREADS
$initializeCanvasElementCallingThread: (elementId, applyStyle) => {
VTKCanvas.initializeCanvasElement(elementId, applyStyle);
},
$initializeCanvasElementMainThread__proxy: 'sync',
$initializeCanvasElementMainThread__deps: ['$initializeCanvasElementCallingThread'],
$initializeCanvasElementMainThread: (elementId, applyStyle) => initializeCanvasElementCallingThread(elementId, applyStyle),
vtkInitializeCanvasElement__deps: ['$initializeCanvasElementMainThread'],
vtkInitializeCanvasElement__sig: 'vpi',
vtkInitializeCanvasElement: (elementId, applyStyle) => {
return initializeCanvasElementMainThread(elementId, applyStyle);
},
#else
vtkInitializeCanvasElement__sig: 'vpi',
vtkInitializeCanvasElement: (elementId, applyStyle) => {
VTKCanvas.initializeCanvasElement(elementId, applyStyle);
},
#endif
};
autoAddDeps(vtkWebAssemblyRenderWindowInteractor, '$VTKCanvas');
mergeInto(LibraryManager.library, vtkWebAssemblyRenderWindowInteractor);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment