Add Meta Quest OpenXR VR support for Android#2
Conversation
Android gains phone/quest product flavors: phone is today's build, quest passes PICASIM_QUEST=ON to CMake, links the Khronos OpenXR Android loader (org.khronos.openxr:openxr_loader_for_android:1.1.49 via prefab, minSdk 29 for this flavor as the loader requires 24+) and merges a manifest overlay with the Quest metadata (vr.headtracking, com.oculus.intent.category.VR, supportedDevices, no orientation lock). CMake: new PICASIM_QUEST option; on Android it enables VR, finds the OpenXR prefab package, defines PICASIM_VR_SUPPORT/PICASIM_QUEST and links GLESv3 + openxr_loader. Non-quest Android keeps VR forced off. CI builds both flavors and uploads a separate quest APK artifact; APK paths updated for the flavored output layout. QUEST.md documents building, sideloading and input (RC transmitter as SDL gamepad, no VR controller support). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123xeZJrveacZTdK8bCv7YX
Adds the Android path to the OpenXR layer, all behind PICASIM_ANDROID with the Win32/desktop-GL path unchanged: - XR_USE_GRAPHICS_API_OPENGL_ES + XR_USE_PLATFORM_ANDROID, with EGL, GLES3 and JNI headers ahead of openxr_platform.h - one-time xrInitializeLoaderKHR with the JavaVM and activity obtained from SDL, before any other OpenXR call (mandatory on Android) - instance extensions XR_KHR_opengl_es_enable and XR_KHR_android_create_instance, with XrInstanceCreateInfoAndroidKHR chained into instance creation - session creation via XrGraphicsBindingOpenGLESAndroidKHR built from the current EGL display/context, with the EGLConfig recovered from the context's EGL_CONFIG_ID - swapchain images typed XrSwapchainImageOpenGLESKHR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123xeZJrveacZTdK8bCv7YX
The VR rendering code was written against desktop GL; this makes it compile and behave on an ES 3.0 context (Quest), with every change inert unless PICASIM_ANDROID/PICASIM_QUEST is defined: - Window: request an ES 3.0 context and GLES3 headers for the quest flavor (non-quest Android stays on ES 2.0) - glDepthRange -> glDepthRangef on GLES (double variant doesn't exist) - MSAA color renderbuffer uses GL_SRGB8_ALPHA8 on Android: GLES3 requires matching internal formats on multisample resolve blits and the swapchain prefers sRGB - panorama depth copy: GLES cannot glCopyTexImage2D depth, so it is replaced by a depth blit through a temporary FBO; depth texture allocated with GL_UNSIGNED_INT as GLES3 rejects DEPTH_COMPONENT24 + GL_FLOAT - desktop mirror-window rendering and per-eye mirror blits compiled out on Android (no mirror on device; swapping the invisible SDL surface could stall the XR frame loop), and the eye mirror FBOs are skipped entirely, saving GPU memory Known runtime follow-ups documented in QUEST.md territory: swapchain format fallback if the runtime lacks SRGB8_ALPHA8, and possible double sRGB encoding to tune in the swapchain choice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123xeZJrveacZTdK8bCv7YX
|
Warning Review limit reached
Next review available in: 54 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PicaSim.cpp's VR block included glad unconditionally; it had only ever compiled on Windows. Use GLES3 headers on Android like the rest of the VR path. First compile failure caught by CI on the quest flavor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123xeZJrveacZTdK8bCv7YX
Summary
Adds OpenXR-based VR support for Meta Quest headsets (Quest 2/Pro/3) via a new Android Gradle product flavor. The regular Android build remains unchanged (phone flavor, VR off). This is an experimental feature—the build system is in place, but rendering is still being ported to GLES3.
Key Changes
Build System & Configuration
questGradle product flavor alongside existingphoneflavor; both use the same application ID so only one can be installed at a timeorg.khronos.openxr:openxr_loader_for_android:1.1.49) via Gradle prefab support, exposed to CMake asOpenXR::openxr_loader-DPICASIM_QUEST=ONto enable VR on Android; setsPICASIM_VR_SUPPORT=1,PICASIM_QUEST=1, and links GLESv3 in addition to GLESv2Android Manifest & Metadata
android/app/src/quest/AndroidManifest.xml) with:com.oculus.intent.category.VR)OpenXR Runtime Implementation (
OpenXRRuntime.cpp)CreateInstance(): Retrieves JavaVM and activity from SDL, initializes Android OpenXR loader viaxrInitializeLoaderKHRbefore any other OpenXR callsCreateInstance(): SelectsXR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAMEon Android vs.XR_KHR_OPENGL_ENABLE_EXTENSION_NAMEon desktop; includesXR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAMEin enabled extensionsCreateInstance(): Passes JavaVM and activity to runtime viaXrInstanceCreateInfoAndroidKHRchained structCreateSessionInternal(): Android path retrieves EGL display/context from SDL, finds matching EGLConfig viaGetEGLConfigForContext()helper, creates graphics binding withXrGraphicsBindingOpenGLESAndroidKHRXrSwapchainImageOpenGLESKHRon Android vs.XrSwapchainImageOpenGLKHRon desktopGraphics & Rendering (
RenderManager.cpp,Window.cpp,VRManager.cpp,VRMenuRenderer.cpp)glBlitFramebuffer,glRenderbufferStorageMultisample, sized internal formatsglDepthRangef()(float variant) on GLES,glDepthRange()on desktopGL_SRGB8_ALPHA8on Quest (matches OpenXR runtime preference) vs.GL_RGBA8on desktopGL_UNSIGNED_INTtype on GLES3 (required forGL_DEPTH_COMPONENT24) vs.GL_FLOATon desktopglBlitFramebufferon GLES3 (sinceglCopyTexImage2Dcannot copy from depth buffer) vs.glCopyTexImage2Don desktophttps://claude.ai/code/session_0123xeZJrveacZTdK8bCv7YX