How to Use the Native Embedder API (TrEmbedder) in JSAR
To use the native embedder API in JSAR, subclass the abstract TrEmbedder class from src/runtime/embedder.hpp, implement the pure virtual onEvent method, configure the runtime via configure(), and drive the rendering loop through the four lifecycle hooks: onBeforeRendering(), onOpaquesRenderPass(), onTransparentsRenderPass(), and onAfterRendering().
The native embedder API allows C++ applications to host the JSAR runtime within game engines like Unity, Unreal, or custom OpenGL/DirectX applications. Found in the m-creativelab/jsar-runtime repository, the TrEmbedder class serves as the bridge between your host engine and the JSAR content system, managing initialization, per-frame rendering, and native event handling.
Architecture of the Native Embedder API
The native embedder API centers on the TrEmbedder abstract class defined in src/runtime/embedder.hpp. This base class owns a TrConstellation instance—the central orchestrator declared in src/runtime/constellation.hpp that manages the renderer, content manager, and XR device.
Key architectural components include:
TrEmbedder: The base class that host applications must inherit from. It encapsulates theTrConstellationruntime core and provides configuration, lifecycle management, and event handling APIs.- Lifecycle Hooks: Virtual methods defined in
src/runtime/embedder.cppthat the host calls each frame to execute rendering passes and advance the simulation. - Event System: The pure virtual
onEventmethod that implementations must override to process native events such as RPC requests from JavaScript. - XR Integration: Optional configuration via
configureXrDeviceto enable stereo rendering for AR/VR headsets.
Creating a Custom Embedder Subclass
To integrate JSAR into your application, create a concrete subclass of TrEmbedder and implement the required virtual methods.
Constructor and Renderer Setup
Inside your subclass constructor, specify the host engine type and initialize the Rendering Hardware Interface (RHI). The TrEmbedder constructor automatically instantiates the constellation member, allowing immediate access to constellation->renderer to set up your graphics backend.
#include <runtime/embedder.hpp>
#include <renderer/render_api.hpp>
class MyEmbedder : public TrEmbedder {
public:
MyEmbedder() : TrEmbedder(TrHostEngine::None) {
auto renderer = constellation->renderer;
auto rhi = RHIFactory::CreateRHI(kUnityGfxRendererOpenGLCore, constellation.get());
renderer->setRHI(rhi);
}
// onEvent implementation required
};
Implementing Event Handling
You must implement the pure virtual onEvent method to handle incoming native events. This method receives an events_comm::TrNativeEvent reference and a std::shared_ptr<TrContentRuntime> for sending responses.
bool onEvent(events_comm::TrNativeEvent &event,
std::shared_ptr<TrContentRuntime> content) override {
if (event.type == events_comm::TrNativeEventType::RpcRequest) {
auto request = event.detail<events_comm::TrRpcRequest>();
if (request.method == "ping") {
events_comm::TrRpcResponse resp(true);
resp.message = "pong";
content->respondRpcRequest(resp, event.id);
return true;
}
}
return false;
}
Runtime Lifecycle Management
The embedder follows a strict initialization-to-shutdown lifecycle controlled by the host application.
Configuration and Startup
Call configure(storageDir, proxy, enableXR) with a cache directory path, optional network proxy, and boolean XR flag, followed by start() to initialize the runtime:
MyEmbedder embedder;
if (!embedder.configure("./jsar_cache", "", false)) {
std::cerr << "Configuration failed\n";
return -1;
}
if (!embedder.start()) {
std::cerr << "Startup failed\n";
return -1;
}
Per-Frame Rendering Hooks
Drive the rendering loop by calling these methods in sequence every frame. These methods are implemented in src/runtime/embedder.cpp and forward calls to the internal TrConstellation:
onBeforeRendering()– Prepares the frame and updates the scene.onOpaquesRenderPass()– Renders opaque geometry.onTransparentsRenderPass()– Renders transparent geometry.onAfterRendering()– Finalizes the frame and submits command buffers.
for (int frame = 0; frame < 600; ++frame) {
embedder.onBeforeRendering();
embedder.onOpaquesRenderPass();
embedder.onTransparentsRenderPass();
embedder.onAfterRendering();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
When the host application exits, call shutdown() to clean up all internal services and release resources.
Enabling XR Stereo Rendering
For XR applications, enable stereo rendering during configuration and provide device parameters:
// Enable XR in configure
embedder.configure("./jsar_cache", "", true);
// Configure XR device properties
xr::TrDeviceInit xrInit;
xrInit.active = true;
xrInit.stereoRenderingMode = xr::StereoRenderingMode::SinglePass;
embedder.configureXrDevice(xrInit);
You can monitor runtime performance via getFps() and getUptime(), or retrieve version information using getVersion() during the frame loop.
Complete Implementation Example
The file src/examples/desktop_opengl.cpp provides a reference implementation named DesktopEmbedder. Below is a minimal standalone example combining all required components:
#include <runtime/embedder.hpp>
#include <renderer/render_api.hpp>
#include <iostream>
#include <thread>
#include <chrono>
class MinimalEmbedder : public TrEmbedder {
public:
MinimalEmbedder() : TrEmbedder(TrHostEngine::None) {
auto rhi = RHIFactory::CreateRHI(kUnityGfxRendererOpenGLCore, constellation.get());
constellation->renderer->setRHI(rhi);
}
bool onEvent(events_comm::TrNativeEvent &event,
std::shared_ptr<TrContentRuntime> content) override {
return false;
}
};
int main() {
MinimalEmbedder embedder;
if (!embedder.configure("./jsar_cache", "", false)) {
return -1;
}
if (!embedder.start()) {
return -1;
}
for (int i = 0; i < 600; ++i) {
embedder.onBeforeRendering();
embedder.onOpaquesRenderPass();
embedder.onTransparentsRenderPass();
embedder.onAfterRendering();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
embedder.shutdown();
return 0;
}
Summary
- Subclass
TrEmbedderfromsrc/runtime/embedder.hppto create a host-specific implementation. - Implement
onEventto handle RPC requests and native input events from the JavaScript runtime. - Call
configure()andstart()to initialize; callshutdown()to destroy the runtime cleanly. - Drive rendering via the four lifecycle hooks:
onBeforeRendering,onOpaquesRenderPass,onTransparentsRenderPass, andonAfterRendering. - Access telemetry through
getFps()andgetUptime()for performance monitoring. - Enable XR by passing
truetoconfigure()and providing a filledxr::TrDeviceInitstructure toconfigureXrDevice().
Frequently Asked Questions
What is TrEmbedder in JSAR?
TrEmbedder is the abstract C++ base class defined in src/runtime/embedder.hpp that provides the interface between host applications and the JSAR runtime. It owns the TrConstellation core and declares pure virtual methods, including onEvent, that concrete implementations must provide to handle communication between the host engine and JavaScript content.
How do I handle RPC requests in TrEmbedder?
Override the onEvent method to intercept events of type events_comm::TrNativeEventType::RpcRequest. Use event.detail<events_comm::TrRpcRequest>() to deserialize the request payload, then call content->respondRpcRequest(response, event.id) to send data back to the JavaScript context, as shown in src/examples/desktop_opengl.cpp.
Can I use TrEmbedder without XR support?
Yes. Pass false as the third parameter to configure(const std::string& storageDir, const std::string& proxy, bool enableXR). The runtime operates in standard mono rendering mode unless you explicitly enable XR and call configureXrDevice() with a valid xr::TrDeviceInit configuration.
What rendering backends are supported by TrEmbedder?
The embedder supports multiple rendering backends through the RHI abstraction layer. You can initialize OpenGL, Vulkan, or engine-specific backends (Unity, Unreal, Cocos) by passing the appropriate renderer type constant to RHIFactory::CreateRHI(), then calling constellation->renderer->setRHI() with the resulting interface.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →