Skip to content

feat: add Meta Horizon OS support#320

Merged
doranteseduardo merged 15 commits intov2.54.1from
metahorizon-support
Apr 27, 2026
Merged

feat: add Meta Horizon OS support#320
doranteseduardo merged 15 commits intov2.54.1from
metahorizon-support

Conversation

@doranteseduardo
Copy link
Copy Markdown
Member

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:

  • Added OpenXR loader as a local Maven dependency and included new Gradle and manifest files for an openxrtest Android test app, enabling OpenXR development and testing on Android [1] [2] [3] [4].
  • Introduced a new AndroidOpenXR platform type in the VROPlatformType enum to distinguish OpenXR at the platform level.

Renderer and Platform Threading Improvements:

  • Implemented a direct renderer queue system for OpenXR and headless-GL renderers, allowing render threads to process queued work without relying on Java/GLSurfaceView. This includes new functions: VROPlatformSetUseDirectRendererQueue and VROPlatformDrainRendererQueue, and updates to how tasks are dispatched and flushed [1] [2] [3].
  • Changed JNI class lookups from FindClass to GetObjectClass throughout VROPlatformUtil.cpp to 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:

  • Expanded the ViroOculus::InputSource enum 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.
  • Added a null check in VROInputControllerBase::processGazeEvent to prevent dereferencing a null _hitResult, increasing robustness.

API and Miscellaneous Improvements:

  • Added a hasRenderContext() method to VRORenderer to allow clients to check if the render context has been initialized.
  • Introduced a new virtual method rvGetScene in VROARSession for future extensibility.

benmotanoti and others added 15 commits April 8, 2026 15:45
- 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
@doranteseduardo doranteseduardo merged commit 9e4e54d into v2.54.1 Apr 27, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants