feat: add Meta Horizon OS support#320
Merged
doranteseduardo merged 15 commits intov2.54.1from Apr 27, 2026
Merged
Conversation
- Add OpenXR AAR (1.1.38) via local Maven repo and Khronos headers - Configure CMake with OpenXR::openxr_loader and c++_shared STL - Implement VROSceneRendererOpenXR: session, EGL, swapchains, frame loop - Implement VROInputControllerOpenXR: action set, Touch/Touch Plus bindings - Add VRODriverOpenGLAndroidOpenXR and VRODisplayOpenGLOpenXR (per-eye FBO) - Add ViroViewOpenXR (Java bootstrap) and nativeCreateRendererOpenXR JNI - Add standalone openxrtest/ module for on-device validation - Fix 7 on-device crashes: STL linkage, loader init order, session state, EGL context ownership, uninitialised renderer Result: stereo render at 90fps on Quest 3, no crashes.
…er events - VROSceneRendererOpenXR: replace POC color-clear with real VRORenderer pipeline; prepareFrame() + renderEye() per eye; fix _driver shadowing bug (_openxrDriver + base _driver both set in constructor); initPassthrough() loads 8 XR_FB_passthrough function pointers, creates XrPassthroughFB + XrPassthroughLayerFB; setPassthroughEnabled() start/pause subsystem and layer; proper destroySession() teardown - VROInputControllerOpenXR: wire right aim pose -> updateHitNode/onMove/processGazeEvent; right trigger -> ClickDown/Up; menu/B button -> BackButton ClickDown/Up; add file-local xrPoseToMatrix + xrAimForward helpers (fixes linkage bug); _prevMenuButton edge detection - VRORenderer_JNI: nativeSetPassthroughEnabled JNI method with dynamic_pointer_cast - Renderer.java / ViroViewOpenXR.java: setPassthroughEnabled() public API surface - openxrtest/MainActivity: dummy Viro scene (red box at 0,0,-2) for device validation
zero m[12..14] before passing to prepareFrame; per-eye view matrices (which need the full pose) are computed separately in renderEye.
… Quest 3 All renderer features confirmed working in stereo at 90fps on Quest 3. Two bugs found and fixed during M1 device validation: 1. Reference space: createReferenceSpace() was trying STAGE first. After the Quest Guardian was configured between M0 and M1 testing, STAGE succeeded — placing Y=0 at the floor with the eye at Y≈1.6m. Objects at Viro world origin appeared 39° below center, past the Quest 3's physical downward FOV, invisible with no error. Fixed: force XR_REFERENCE_SPACE_TYPE_LOCAL so the world origin matches eye level at session start, consistent with all other Viro platforms. 2. endFrame() never called: VROSceneRendererOpenXR::renderFrame() called prepareFrame() and renderEye() but never _renderer->endFrame(). endFrame() is where VROFrameScheduler::processTasks() runs — without it, texture hydration tasks queue forever. Models parsed and downloaded correctly but onObject3DLoaded never fired and nothing appeared, with zero error logs. Fixed: call _renderer->endFrame(_driver) after both eye renders, before xrEndFrame.
Dual-controller input (VROInputControllerOpenXR)
- Separate FLOAT trigger/grip actions per hand (threshold 0.5), edge-detected
- A/B/X/Y/Menu buttons as BOOLEAN actions; B+Menu both map to BackButton
- Thumbstick Vector2f actions → onScroll with dead zone 0.15
- Haptic output: xrApplyHapticFeedback per hand via triggerHaptic()
- New ViroOculus sources in VROInputType.h: LeftController=4, AButton=5,
XButton=6, YButton=7, LeftGrip=8, RightGrip=9, LeftThumbstick=10,
RightThumbstick=11
- recenterTracking() rewritten: VIEW space → head pose → yaw extraction →
new LOCAL reference space at head XZ; was broken (located space against itself)
- openxrtest scene updated with interactive hover+click nodes for validation
Hand tracking (XR_EXT_hand_tracking + XR_FB_hand_tracking_aim)
- initHandTracking() loads 3 EXT function pointers, creates left/right trackers
- processHands(): per-frame xrLocateHandJointsEXT (26 joints), chains
XrHandTrackingAimStateFB for aimPose + pinchStrengthIndex
- Pinch: pinchStrengthIndex >= 0.7 (FB aim) or thumb-tip<2cm (fallback)
- Grab: middle-tip to palm < 6cm → RightGrip/LeftGrip click events
- Right hand = primary pointer (updateHitNode + processGazeEvent)
- Left hand = pose-only (onMove, no hit test)
- Graceful no-op if extensions unavailable at runtime
Infrastructure fixes (blocked all input silently):
- VROInputControllerBase::processGazeEvent: add null guard on _hitResult;
updateHitNode returns early when _scene==nullptr, leaving _hitResult null,
previously causing SIGSEGV on first frames with hand tracking active
- VROPlatformUtil: add direct C++ renderer task queue
(VROPlatformSetUseDirectRendererQueue / VROPlatformDrainRendererQueue);
OpenXR has no GLSurfaceView so mRenderQueue==null and all
VROPlatformDispatchAsyncRenderer tasks were silently dropped — setScene
never executed, _scene never set, zero input events
- VROPlatformUtil: replace FindClass("com/viro/core/internal/PlatformUtil")
with GetObjectClass(sPlatformUtil) in 4 call sites; native threads attached
with AttachCurrentThread(nullptr) use bootstrap classloader, app classes
not found → null jclass → ART JNI abort in VROPlatformDispatchAsyncBackground
Device validated on Quest 3: trigger/grip/A/B/X/Y clicks and hover confirmed
in logcat; hand aim ray hover and grab events confirmed.
The encoder-input EGL surface was declared as sRGB, causing Tensor/Mali drivers to gamma-linearize every framebuffer write and the encoder to emit color_transfer=iec61966-2-1 on the MP4. The result: mis-tagged pixels unrepairable by remux, and GPU throughput throttled to ~10 fps on Mali-G710. Exynos devices ignored the flag and worked by accident. Drop the EGL_GL_COLORSPACE_SRGB_KHR attribute; the encoder now emits BT.709 SDR consistently across SoCs and per-write gamma is gone. Under VROColorRenderingMode::Linear (HDR) the scene framebuffer holds linear RGB values that used to rely on the driver's implicit gamma from the dropped flag. Run the existing gamma post-process explicitly on the recording path for that mode; non-linear mode blits through. Fix a dormant bug in the gamma post-process while there: the samplers list named "hdr_texture" but the shader sampled from "srgb_texture", leaving the sampler unbound and producing black frames. The recording path is the first caller to exercise it in HDR.
The function takes a ReactVisionCCA::RVCCAGeospatialProvider* and dereferences it to call computeArPosition, but RVCCAGeospatialProvider is only forward-declared when RVCCA_AVAILABLE=0, so the definition fails to compile in any build without the proprietary libreactvisioncca headers present. All call sites are already inside #if RVCCA_AVAILABLE blocks; wrap the definition to match.
…perf Fix Android video recording: color pipeline + RVCCA build guard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces significant updates to support OpenXR on Android, including new build/test infrastructure, input handling improvements, and core platform changes for better thread and renderer management. The most important changes are grouped below:
OpenXR Support and Build/Test Infrastructure:
openxrtestAndroid test app, enabling OpenXR development and testing on Android [1] [2] [3] [4].AndroidOpenXRplatform type in theVROPlatformTypeenum to distinguish OpenXR at the platform level.Renderer and Platform Threading Improvements:
VROPlatformSetUseDirectRendererQueueandVROPlatformDrainRendererQueue, and updates to how tasks are dispatched and flushed [1] [2] [3].FindClasstoGetObjectClassthroughoutVROPlatformUtil.cppto ensure compatibility with pure-native threads (such as OpenXR render threads) that lack the standard Java class loader, preventing crashes [1] [2] [3].Input Handling Enhancements:
ViroOculus::InputSourceenum to include more granular input sources (e.g., left/right controllers, A/B/X/Y buttons, grips, thumbsticks), improving input event handling for OpenXR devices.VROInputControllerBase::processGazeEventto prevent dereferencing a null_hitResult, increasing robustness.API and Miscellaneous Improvements:
hasRenderContext()method toVRORendererto allow clients to check if the render context has been initialized.rvGetSceneinVROARSessionfor future extensibility.