quest-native
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNative OpenXR on Horizon OS
Horizon OS 上的原生 OpenXR
A Quest app written in C or C++ talks to the headset through OpenXR and to the
phone-class OS underneath through Android. Most of what breaks sits in that
seam: a manifest that is legal for sideloading and illegal for the Store, a loader
version that crashes instead of degrading, a frame loop that stops drawing the
moment focus is lost.
Read for project discovery, target/version
contracts, engine handoff, a vertical slice and an audit. Read
for feature/permission decisions, tracking/room
lifecycle, colocation and camera or AI input.
references/project-playbook.mdreferences/mixed-reality.mdFor a whole-product roadmap or stage audit, use ; a single technical task stays with this owner. If absent, identify the current stage, its evidence and the next prerequisite inline.
quest-lifecycle一个使用 C 或 C++ 编写的 Quest 应用通过 OpenXR 与头显通信,并通过 Android 与底层的类手机操作系统通信。大多数问题都集中在这个衔接处:一份可用于侧载但不符合商店要求的 manifest、一个在失败时直接崩溃而不是降级运行的 loader 版本,或一个在失去焦点的瞬间便停止绘制的帧循环。
阅读 ,了解项目发现、目标/版本约定、引擎交接、垂直切片和审计。阅读 ,了解功能/权限决策、跟踪/房间生命周期、共置,以及摄像头或 AI 输入。
references/project-playbook.mdreferences/mixed-reality.md对于完整的产品路线图或阶段审计,请使用 ;单项技术任务仍由本 skill 负责。如果该 skill 不存在,请在正文中说明当前阶段、相关证据和下一项前置条件。
quest-lifecycleStep 0 — is this project even on this lane?
第 0 步——这个项目真的属于这条技术路线吗?
| The project is | Owner |
|---|---|
| C / C++ against OpenXR, Gradle + NDK | this skill |
| Unity | Meta's |
| Unreal | |
| Kotlin / Spatial SDK | Meta's |
| WebXR, IWSDK, PWA | |
| An existing 2D Android app | Meta's |
Being on the wrong lane costs more than any single bug in this file: Unity answers
do not transfer to a NativeActivity, and OpenXR extension names do not transfer to
Unity's OVR wrappers.
| 项目类型 | 负责方 |
|---|---|
| 使用 OpenXR、Gradle + NDK 的 C / C++ 项目 | 本 skill |
| Unity | Meta 的 |
| Unreal | |
| Kotlin / Spatial SDK | Meta 的 |
| WebXR、IWSDK、PWA | 本套件中的 |
| 现有的 2D Android 应用 | Meta 的 |
走错技术路线造成的代价,可能高于本文件中的任何一个单独 bug:Unity 的解决方案无法直接套用到 NativeActivity,OpenXR 扩展名称也无法套用到 Unity 的 OVR 封装。
Step 1 — verify, then answer
第 1 步——先验证,再回答
Horizon OS ships on its own cadence and the API surface moves with it. Any claim
about a Quest API, a required SDK level or a manifest element is checked against
Meta's own docs before it reaches code:
bash
metavr docs search "openxr swapchain" # the CLI; the MCP server exposes the same searchEvery documentation page has a Markdown twin — no HTML scraping, no login:
text
index: https://developers.meta.com/horizon/llmstxt/documentation/native/llms.txt/
page: https://developers.meta.com/horizon/llmstxt/documentation/native/android/<slug>.mdreferences/doc-map.mdhz-quest-verify-firstHorizon OS 按自己的节奏发布,API 界面也会随之变化。任何关于 Quest API、必需 SDK 级别或 manifest 元素的说法,都必须在写入代码之前对照 Meta 官方文档进行核验:
bash
metavr docs search "openxr swapchain" # the CLI; the MCP server exposes the same search每个文档页面都有对应的 Markdown 版本——无需抓取 HTML,也无需登录:
text
index: https://developers.meta.com/horizon/llmstxt/documentation/native/llms.txt/
page: https://developers.meta.com/horizon/llmstxt/documentation/native/android/<slug>.mdreferences/doc-map.mdhz-quest-verify-firstStep 2 — the manifest, which is two manifests
第 2 步——manifest,其实有两套
Development and release requirements differ, and only the release set is
enforced at review (, ). Writing the dev manifest and
submitting it without checking the release delta can fail review.
VRC.Quest.Packaging.1.4Current Meta manifest segment for an immersive OpenXR app (rechecked 2026-09-21):
xml
<uses-feature android:name="android.hardware.vr.headtracking"
android:required="true" android:version="1" />
<!-- in the launching activity -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.oculus.intent.category.VR" />
</intent-filter>- with
android.hardware.vr.headtrackingis what makes the app an immersive app; a 2D panel app omits it or setsrequired="true".required="false" - , theme
android:screenOrientation="landscape", and the longTheme.Black.NoTitleBar.Fullscreenlist from the manifest doc. NeverconfigChanges.noHistory - Release only: false or absent,
android:debuggable, a uniqueinstallLocation="auto", andandroid:labellistingcom.oculus.supportedDevices— a device outside that list runs in compatibility mode and reports itself as an older generation.quest2|questpro|quest3|quest3s - A pure NativeActivity app may ship . The Platform SDK is a Java library: adding it removes that option.
android:hasCode="false"
Full element-by-element contract, both manifests side by side:
.
references/manifest-and-gradle.md开发要求与发布要求不同,而且只有发布要求集会在审核时强制执行(、)。编写完开发 manifest 后,若未核对发布差异便直接提交,就可能审核失败。
VRC.Quest.Packaging.1.4适用于沉浸式 OpenXR 应用的当前 Meta manifest 片段(已于 2026-09-21 重新核对):
xml
<uses-feature android:name="android.hardware.vr.headtracking"
android:required="true" android:version="1" />
<!-- in the launching activity -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.oculus.intent.category.VR" />
</intent-filter>- 设置 且
android.hardware.vr.headtracking,正是它使应用成为沉浸式应用;2D 面板应用应省略此项,或设置为required="true"。required="false" - 、主题
android:screenOrientation="landscape",以及 manifest 文档中的长Theme.Black.NoTitleBar.Fullscreen列表。绝不要使用configChanges。noHistory - 仅发布版本:必须为 false 或不设置,
android:debuggable,使用唯一的installLocation="auto",并让android:label列出com.oculus.supportedDevices——不在该列表中的设备会以兼容模式运行,并将自己报告为较旧一代设备。quest2|questpro|quest3|quest3s - 纯 NativeActivity 应用可以采用 。Platform SDK 是一个 Java 库:添加它之后就不再有这个选项。
android:hasCode="false"
逐项完整的约定,以及两套 manifest 的并排对照:
。
references/manifest-and-gradle.mdStep 3 — SDK levels, which changed in 2026
第 3 步——SDK 级别,这些在 2026 年发生了变化
Recommended for in-lifecycle devices (Quest 2, Pro, 3 family): minSdk 32,
targetSdk 34, compileSdk ≥ targetSdk. The legal range is minSdk 29–34 and
targetSdk 32–34 for immersive apps (32–36 for 2D panel apps) — but an app
created since 1 March 2026 must target 34. Read the two tables from the source
before pinning them; they are quoted with their date in
.
references/manifest-and-gradle.mdA lock file is not the build. Grep AND whatever dependency
lock the project keeps: on 2026-09-20 in one Quest project the gradle said
and still said , which is a release
note written against a build that does not exist.
build.gradletargetSdk 34dependencies.lock.json32对于仍在支持生命周期内的设备(Quest 2、Pro、3 系列),推荐使用:minSdk 32、targetSdk 34、compileSdk ≥ targetSdk。沉浸式应用的允许范围是 minSdk 29–34、targetSdk 32–34(2D 面板应用为 32–36)——但自 2026 年 3 月 1 日以来创建的应用必须以 34 为 target。在固定这些值之前,请先从来源读取两张表; 中引用了这两张表,并附有日期。
references/manifest-and-gradle.md锁文件并不等同于实际构建。 请同时搜索 和项目保留的任何依赖锁文件:2026-09-20,某个 Quest 项目的 Gradle 写着 ,但 中仍然是 ——这就相当于在为一版并不存在的构建撰写发布说明。
build.gradletargetSdk 34dependencies.lock.json32Step 4 — the loader, and the two extensions before any of your code
第 4 步——loader,以及在编写任何代码之前需要处理的两个扩展
gradle
android { buildFeatures { prefab true } }
dependencies { implementation 'org.khronos.openxr:openxr_loader_for_android:<ver>' }- Meta supports the Khronos Android loader; no proprietary loader exists any more. Below 1.0.34 the app crashes — it does not fall back.
- Apps on the Khronos loader crash on Horizon OS older than v62, and a non-Quest-1 user below v62 never sees the update in the Store.
- : fetch
XR_KHR_loader_init_androidthroughxrInitializeLoaderKHRwith a null instance, call it withxrGetInstanceProcAddress, and only then create anything.XrLoaderInitInfoAndroidKHR - : the Android-specific parameters ride into
XR_KHR_android_create_instancethrough it.xrCreateInstance
gradle
android { buildFeatures { prefab true } }
dependencies { implementation 'org.khronos.openxr:openxr_loader_for_android:<ver>' }- Meta 支持 Khronos Android loader;现在已不再存在任何专有 loader。版本低于 1.0.34 时应用会崩溃——不会回退。
- 使用 Khronos loader 的应用在低于 v62 的 Horizon OS 上会崩溃,而低于 v62 系统的非 Quest-1 用户根本不会在 Store 中看到更新。
- :通过
XR_KHR_loader_init_android并传入空 instance 来获取xrGetInstanceProcAddress,再使用xrInitializeLoaderKHR调用它,之后才能创建任何内容。XrLoaderInitInfoAndroidKHR - :Android 专用参数通过此扩展传入
XR_KHR_android_create_instance。xrCreateInstance
Step 5 — the frame loop that passes review
第 5 步——能够通过审核的帧循环
- Drive the session off ; the state machine, not a boolean, decides when to begin and end the session.
xrPollEvent - →
xrWaitFrame→ render →xrBeginFrame, and pose everything at the predicted display time the wait returned. Rendering to "now" is judder with a clean profile.xrEndFrame - Focus-aware is a VRC, not a nicety (): when the app loses focus it keeps rendering, hides its hands and controllers, and ignores input. An app that freezes on the system menu fails review.
VRC.Quest.Input.4 - Enumerate swapchain formats with and pick from what came back; a hardcoded format is a portability bug waiting for the next headset. Colour space is explicit (
xrEnumerateSwapchainFormats), not assumed.XR_FB_color_space - 64-bit binaries only (); APK under 1 GB, OBB under 4 GB (
VRC.Quest.Packaging.6)..5
Session states, pacing and the focus rules in detail: .
references/frame-loop.md- 通过 驱动会话;应由状态机(而不是布尔值)决定何时开始和结束会话。
xrPollEvent - →
xrWaitFrame→ 渲染 →xrBeginFrame,并将所有对象的位姿设置在xrEndFrame返回的预测显示时间。渲染到“当前时间”即便在性能分析结果很干净的情况下也会产生抖动。xrWaitFrame - 焦点感知是 VRC 的要求,而不是可选优化():应用失去焦点时必须继续渲染、隐藏手部和控制器,并忽略输入。应用在系统菜单弹出时冻结会审核失败。
VRC.Quest.Input.4 - 使用 枚举 swapchain 格式,并从返回结果中选择;硬编码格式是一个等待下一款头显来暴露的可移植性 bug。色彩空间必须显式指定(
xrEnumerateSwapchainFormats),不能想当然。XR_FB_color_space - 仅支持 64 位二进制文件();APK 小于 1 GB,OBB 小于 4 GB(
VRC.Quest.Packaging.6)。.5
会话状态、帧节奏和焦点规则的详细信息:。
references/frame-loop.mdStep 6 — capabilities, never device models
第 6 步——检查功能能力,而不是设备型号
Check the feature through the chosen SDK/runtime, not just "is this a Quest
Pro". OpenXR extension enumeration is one layer; system properties, permissions,
account restrictions and valid tracking state may also be required. Camera2
capabilities do not reduce to OpenXR extension names. Compatibility mode can
change the reported model, so device-name checks alone are insufficient.
应通过选定的 SDK/运行时检查功能,而不只是判断“这是不是一台 Quest Pro”。OpenXR 扩展枚举只是其中一层;系统属性、权限、账户限制和有效的跟踪状态也可能都是必要条件。Camera2 的能力不能简单等同于 OpenXR 扩展名称。兼容模式可能改变上报的设备型号,因此仅检查设备名称并不充分。
What this skill hands off
本 skill 的后续转交
| Next question | Where |
|---|---|
| It runs but drops frames | |
| How do I get it onto a headset / read its logs | |
| How do I ship it | |
| A WebXR or PWA build instead | |
| 下一个问题 | 去向 |
|---|---|
| 可以运行,但会掉帧 | |
| 如何将其安装到头显 / 读取日志 | |
| 如何发布 | |
| 改为构建 WebXR 或 PWA | |
Gotchas that cost a debugging round
最容易让人多花一轮调试时间的坑
- The dev manifest passes sideloading and fails review. Two documents, two
requirement sets; the release one is .
resources/publish-mobile-manifest - A wrong launch category can look like a rendering bug. The current Meta
release page names ; verify the merged APK against that page and the selected SDK rather than a generic OpenXR snippet.
com.oculus.intent.category.VR - Loader below 1.0.34 crashes on launch with no diagnostic pointing at the loader.
- and the Platform SDK are mutually exclusive — an entitlement check (
hasCode="false", a recommendation rather than a requirement) pulls Java into a native app.VRC.Quest.Security.1 - Samples are the fastest correct answer. (Khronos) and Meta's OpenXR SDK samples —
hello_xr,XrPassthrough,XrSceneModel,XrHandsAndControllers,XrSpaceWarp,XrSpatialAnchor— each demonstrate one extension end to end. Read the sample before writing the extension by hand.XrVirtualKeyboard
Documentation facts above were read from developers.meta.com on 2026-09-20;
re-verify with before quoting a number back to anyone.
metavr docs search- 开发 manifest 能通过侧载,却过不了商店审核。 这是两份文档、两套要求;发布版要求集是 。
resources/publish-mobile-manifest - 错误的启动 category 可能看起来像渲染 bug。 当前 Meta 发布页面指定的是 ;应根据该页面和所选 SDK 核验合并后的 APK,而不是照搬通用 OpenXR 代码片段。
com.oculus.intent.category.VR - loader 低于 1.0.34 会在启动时崩溃,且没有任何指向 loader 的诊断信息。
- 与 Platform SDK 互斥——entitlement 检查(
hasCode="false",属于建议而非要求)会迫使原生应用引入 Java。VRC.Quest.Security.1 - 示例是最快的正确解法。 (Khronos)以及 Meta 的 OpenXR SDK 示例——
hello_xr、XrPassthrough、XrSceneModel、XrHandsAndControllers、XrSpaceWarp、XrSpatialAnchor——每个都端到端演示了一个扩展。手工编写扩展之前,先阅读示例。XrVirtualKeyboard
上述文档事实于 2026-09-20 从 developers.meta.com 读取;向他人引用任何数值之前,请使用 重新验证。
metavr docs searchTriggers
触发词
More phrases that route here: "Quest C++" / "Quest на плюсах", "AndroidManifest for VR" /
"манифест для VR", "openxr_loader_for_android", "xrWaitFrame", "targetSdk for Quest" /
"какой targetSdk для Quest", "IMMERSIVE_HMD", "hello_xr", "passthrough in native" /
"passthrough нативно".
更多会将任务路由到这里的短语:“Quest C++” / “Quest на плюсах”,“AndroidManifest for VR” / “манифест для VR”,“openxr_loader_for_android”,“xrWaitFrame”,“targetSdk for Quest” / “какой targetSdk для Quest”,“IMMERSIVE_HMD”,“hello_xr”,“passthrough in native” / “passthrough нативно”。