SLProject
4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
|
SLProject can be built to run in a web browser. We use the Emscripten toolchain, which compiles C/C++ code to a binary instruction format called WebAssembly. The generated Wasm module can be loaded by ordinary JavaScript at runtime. The browser then compiles the instructions to native machine code and runs it, making it possible to run high-performance code in the browser. To access OpenGL functions, browsers expose the WebGL API, which Emscripten wraps in standard OpenGL headers. SLProject uses WebGL 2, which is based on OpenGL 3.0 ES. Not all scenes from app-demo can run in the browser because OpenGL 4.0 functions are not available or because some OpenCV modules can't be compiled for WebAssembly.
The Emscripten toolchain contains a modified Clang compiler, some runtime libraries, an implementation of the C, C++ and POSIX APIs, and ports of some popular libraries such as SDL, GLFW, libpng or zlib. This allows us to take code written for desktop platforms and port it to the Web without much effort.
Emscripten uses standard browser APIs to implement its libraries. For example, a call to the C function printf
might use the JavaScript function console.log
internally. Here is how a few commonly used libraries are implented behind the scenes:
Even though Emscripten provides APIs for most browser features, it is sometimes still necessary to call some JavaScript functions directly. For example, there is currently no camera API. Emscripten provides a EM_ASM
macro to embed JavaScript code directly into C/C++ code, which is comparable to inline assembly in native compilers. This allows us to call browser APIs directly in C++ and to pass values between JavaScript and C++.
JavaScript runs its code in a event loop. Code is executed when an event is triggered, such as a keypress or a new animation frame. The browser can only continue its work when the event is processed by the JavaScript code. This is why you can't run an infinite loop in JavaScript as this would block the browser window. JavaScript APIs usually don't block for this reason, but return a Promise
that represents an asynchronous operation. The calling code can then specify callbacks that are run when the operation completes or fails.
This asynchronous thinking leads to some limitations when developing for Emscripten:
while
loop in AppEmscripten.cpp
. Instead, we submit a function to the browser that is called when a new animation frame is available. The Emscripten function to do this is emscripten_request_animation_frame
.SLAssetLoader
to download the files on its worker thread instead.SLAssetLoader
because I/O is not allowed on the main thread.main
function that are used when a new thread is created. This is done by adding the linker flag -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency + 4
that sets the Web Worker pool size to the number of logical processors available plus 4 for good measure. For more information, see the Pthreads page in the Emscripten docs.When running natively, SLProject uses the file system to load and store data. In the browser environment, the type of storage depends on the asset type.
SLIOReaderFetch
). To store images, we open a popup in the browser that displays the image and contains a download link (SLIOWriterBrowserPopup
).SLIOReaderFetch
).SLIOReaderFetch
).SLIOWriterMemory
and SLIOReaderMemory
)SLIOReaderLocalStorage
and SLIOWriterLocalStorage
). Static config files (e.g. calibrations) are downloaded from a remote server (SLIOReaderFetch
).