WebXR in C++

~ 3.5 Minute Read.

Web XR in C++… what?

I recently had the joy of linking javascript to C++ code for a tiny game we’re building at Vhite Rabbit.

Compilers like Emscripten or cheerp — which I didn’t get a chance to try just yet — allow you to compile your C++ or C code to ASM.js or WebAssembly. Since you will want access to some Web APIs, you will need to link some javascript to your WASM code.

WebXR

In my case, I wanted a minimal API to interact with WebXR — which is The Future™ because it removes the friction of downloading and installing games, which lowers the threshold of “big enough to be worth trying out”. 1

A while ago I updated Emscriptens VR API from WebVR 1.0 to 1.1, adding lots of documentation in the process. I even contributed a WebVR Example 2 to Magnum, the OpenGL/WebGL/GLES and soon Vulkan graphics engine of my choice.

Since then, a lot has changed — even the name is now WebXR Device API — and I did not have the time to do a full blown wrapper for emscripten yet. Additionally, the API is not fully stable yet and I will wait for that.

Let’s get into some more technical details for those who are interested:

Linking JS in CMake

For emscripten you can check the documentation here. While that page and the libraries that come with emscripten should get you all the way there, I thought it may be helpful to give a overview anyway. With a header like:

#ifndef WEBXR_H_
#define WEBXR_H_

#ifdef __cplusplus
extern "C" {
#endif

/** Init WebXR rendering */
extern void init_webxr(/* ... */);

#ifdef __cplusplus
}
#endif

#endif

You can link the implementing javascript library:

mergeInto(LibraryManager.library, {
    init_webxr: function(/* ... */) { }
);

… super easily by adding appropriate CMake flags:

if(CORRADE_TARGET_EMSCRIPTEN)
    # "link" the webxr javascript library
    set_property(TARGET your-target APPEND_STRING PROPERTY
        LINK_FLAGS " --js-library library_webxr.js")
endif()

Where CORRADE_TARGET_EMSCRIPTEN is a flag provided by Corrade. This works without, if you want to unconditionally link the js, though. I use the toolchains that are also used in Magnum.

Additionally I recommend adding proper debug flags:

# Some handy debug flags
set_property(TARGET your-target APPEND_STRING PROPERTY
    LINK_FLAGS_DEBUG " -s ASSERTIONS=1 -s DEMANGLE_SUPPORT=1")

Working with the heap

To pass data between C++ and JS with emscripten, you will want to use the heap. While passing smaller data via function arguments and return values is trivial, you can pass entire structures by allocating heap memory:

var yourStruct = Module._malloc(SIZEOF_YOUR_STRUCT);

To then set its values using setValue 3:

setValue(yourStruct + 0, 42.0, 'double')
setValue(yourStruct + 8, 42.0, 'float')
setValue(yourStruct + 12, 42, 'i64')
setValue(yourStruct + 20, 42, 'i32')
setValue(yourStruct + 24, 42, 'i16')
setValue(yourStruct + 28, 42, 'i8')

Finally, passing the pointer and if you’re done using that heap memory, you should be deallocating it either in javascript:

Module._free(yourStruct);

or in C/C++ as you would usually do (I hope).

I hope this helps someone out there, I know it would have saved be a minute to have this a little more condensed and in one place.

Written in 60 minutes, edited in 5 minutes.

Footnotes

  1. I just noticed it’s missing an origin trial token may not run!

  2. This will probably be a blog post of its own sometime.

  3. In emscripten’s libraries you will often see makeSetValue, which is meant to work with the JSON library which stores the offsets of the members of all the C structs, though.