Skip to content

Standalone VR Improvments (Quest 2/1 Support) - #2

Merged
iChris4 merged 5 commits into
iChris4:openxr-workfrom
darthcircuit:codex/standalone-selective-integration
Sep 26, 2026
Merged

iChris4 merged 5 commits into
iChris4:openxr-workfrom
darthcircuit:codex/standalone-selective-integration

Conversation

@darthcircuit

Copy link
Copy Markdown

I've merged in what I could from my initial work to yours. I didn't have a launcher or anything, so a lot of the stuff i was doing wouldn't fit with the improvements. Yours has more polish, so I preferred yours, but the improvements i've added for Quest 2 and Quest 1 are in place. I confirmed that both headsets will launch the game. Quest 2 performs great, quest 1 needs some love, but i think it can handle it. it runs about half speed, but smooth. I have some other improvements that i want to add (fixing quest 1 stuff, and improving build steps so we're not reliant on windows.) I'm going to add some more pr's later when i get more time, but hopefully this gets us moving in a positive trajectory :)

As a note on Quest 1 builds, the firmware on that headset doesn't support hybrid launches, so i had to create an additional entry for launching the game in immersive mode, and the settings will be it's own entry in the library. There were also some changes to the graphics pipeline on q1.

@iChris4

iChris4 commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Thanks for this, and for testing on a real Quest 1. Getting the menus and previews rendering there is great, and the flavour split is a clean way to handle the different CPU and launcher. I checked it against current openxr-work : it merges with no conflicts, and the merged aurora files compile with both the NDK and the Windows toolchain. There are three things I'd like to sort out before merging.

1. An APK can package the other flavour's game kit

ExportQuestGameKit in android/app/build.gradle.kts picks the most recently modified libmkw_quest_kit_probe.so anywhere under build/intermediates/cxx, not the one from the variant being assembled. That was fine with one variant per build type. Now the flavours differ in -mcpu, and the kit carries those flags (taken from compile_commands.json) as well as the prebuilt runtime objects.

To reproduce: build quest1, then modernQuest, then quest1 again with no native changes. Ninja doesn't relink the quest1 probe, so the newest probe is the modern one, and the quest1 APK ships a cortex-a77 kit. Any game built from that kit (on the headset, with Build-QuestGame.ps1, or with the PC Setup) then dies with SIGILL on a Quest 1. The reverse order gives the modern APK a Kryo kit, which runs but without the newer CPU features.

Build-QuestGame.ps1 (default -Kit) and Run-Quest.ps1 (default -Apk) pick the newest file the same way.

One option: have the export task check that the chosen tree's CMakeCache.txt has the flavour's MKW_ANDROID_CPU, or select the tree by it. Then record the CPU in kit.json so the game build and the import can refuse a mismatch.

2. The Dawn package switch doesn't change anything

I downloaded both pinned v20260603.191052 android-aarch64 packages. Each matches its pinned SHA-256 (27d910… from encounter, 0e63e8… from theofficialgman). Once extracted, though, all 22 files are byte-identical; libwebgpu_dawn.a is 96b568c3… in both. Only the tar entry timestamps differ.

So this change can't be what stops VK_EXT_debug_utils from failing at vkCreateInstance. The sentence in docs/quest-port.md saying the Android package doesn't enable that extension isn't accurate either.

Could you check what actually fixed instance creation on your Quest 1? If the current pin already works there, I'd drop this hunk and that doc sentence, since the hunk also moves away from upstream's pin in patchzyy#215.

3. The pipeline scheduling change applies to every Android headset

The copy workarounds are gated on monterey, but the pipeline_cache.cpp change applies to all of __ANDROID__. So Quest 2/3/3S/Pro also go from about six compile workers to two, at nice 5, with no background prewarm. I haven't measured it on a Quest 3, but these side effects follow from the code:

  • Recipes loaded from the headset's own pipeline cache stay queued until first use, so they're never compiled ahead of time.
  • Because they stay queued, queuedPipelines never reaches zero, and note_pipeline_queue_drained() never runs. The Vulkan pipeline cache is then never stored when prewarm finishes or during idle time in menus, only on request, at race exit and at shutdown.
  • Once more than 256 recipes have loaded, they fill MaxQueuedPipelineBuilds. After that, any new pipeline requested outside a frame compiles synchronously on the calling thread instead of being queued.

The notifyWorker you added for promoted recipes looks right, and waits already promote and notify, so I don't see a hang.

Could you either gate this on Quest 1 like the copy fixes, or split it into its own PR with before/after numbers from a newer headset (first-race hitching and frame times)? CONTRIBUTING.md asks for one change per PR anyway, and this one touches the headsets that already work.

@darthcircuit

Copy link
Copy Markdown
Author

Thanks for the detailed review. I’ve pushed fixes for all three items.

  1. Game-kit export now selects the CMake tree matching the variant’s MKW_ANDROID_CPU and build type. The CPU is recorded in kit.json, and the desktop builder, headset builder, and package importer reject mismatched kits. The PowerShell scripts now select exact flavor paths instead of the newest artifact.

  2. I confirmed the Dawn packages are byte-identical, reverted the package-source change, and removed the inaccurate documentation claim. The package switch therefore wasn’t responsible for the Quest 1 instance-creation behavior.

  3. The two-worker, low-priority, no-background-prewarm scheduling is now gated specifically on ro.product.device == monterey. Other Android headsets keep the upstream scheduling behavior. The promoted-recipe worker notification remains.

I also merged the latest openxr-work. Both Android flavor test suites, the Windows Setup build, and compilation of the merged native pipeline scheduler pass.

I need test on my headsets still and won't get time for a little bit, but i'll do that as soon as i can.

@iChris4

iChris4 commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Thanks, these look good. Points 2 and 3 are resolved. Recording the CPU in the kit is the right design for point 1, and I like that the APK build, game build, on-headset build and import all check it. As pushed, though, the kit export fails on Windows for every variant, for two separate reasons.

1. Debug variants never find a CMake tree. ExportQuestGameKit matches the cache's CMAKE_BUILD_TYPE against variant.buildType, but android/app/src/main/cpp/CMakeLists.txt forces CMAKE_BUILD_TYPE to Release for every variant. So debug builds (the Build-Quest.ps1 default) fail with:

No debug CMake tree for Android CPU cortex-a77. Found: …\.cxx\Debug\<hash>\arm64-v8a (cpu=cortex-a77, type=Release)

The native trees are Release in every variant, so I'd drop the build-type condition and keep only the CPU one.

2. Export-QuestGameKit reads the CPU as empty on Windows. CMake writes CMakeCache.txt with CRLF there, and in a .NET regex $ doesn't match before \r. So (?m)^MKW_ANDROID_CPU(?::[^=\r\n]*)?=([^\r\n]+)$ in QuestGameKit.psm1 returns '', and the release export fails with targets Android CPU '', expected 'cortex-a77'. Adding \r? before the $ fixes it. The Kotlin regex in the Gradle task is fine, because Java's $ handles \r\n.

I reproduced both by running exportModernQuestDebugQuestGameKit and exportModernQuestReleaseQuestGameKit (with -x merge…NativeLibs) against a CMake tree from a Windows build. Everything else checks out: the merge of openxr-work is clean, the aurora files compile with the NDK and on Windows, the Kotlin compiles for both flavours, and each flavour's kit lands in the questGameKit/<variant> path Build-QuestGame.ps1 expects.

@darthcircuit

Copy link
Copy Markdown
Author

Thanks for the clear reproduction. I’ve removed the build-type condition and made the PowerShell CMake-cache regex CRLF-safe. I reproduced both modern Quest debug and release exports against Release-native trees, verified the CRLF case under .NET’s regex engine, and reran both Android flavor test suites. The fixes are in bca303c.

@iChris4

iChris4 commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Thanks for the quick turnaround. bca303c fixes both export problems: on Windows, the modern debug build now exports its kit (both games, cortex-a77) from a fresh flavour tree, and Build-Quest.ps1 accepts it.

I've merged this into openxr-work together with my latest changes. The only conflict was in docs/quest-port.md, where I kept both sides. I also added a small follow-up commit that names the per-variant kit export task in the docs.

I tested the modernQuest build on my Quest 3. I rebuilt both games against the new kit and imported them, then launched from Play and raced. I didn't notice any difference from before. The log shows the normal pipeline scheduling (4 workers with full prewarm, same prewarm time as my earlier sessions) and none of the Quest 1 paths. I don't have a Quest 1, so that side is still untested on the merged code.

When you test on your Quest 1, please use the merged openxr-work rather than your branch, because a few things landed after your testing:

  • The Quest APK now links Dawn built from source with our patches (android/Build-QuestDawn.ps1, for foveated rendering) instead of the prebuilt package. You mentioned Quest 1 rejecting VK_EXT_debug_utils at vkCreateInstance. This is a different Dawn binary from the one you tested, so that's the first thing I'd watch. Build-Quest.ps1 -StockDawn links the prebuilt package if you need to compare.
  • Each eye is now drawn in one render pass, the cockpit hands can follow hand tracking, and foveated rendering is available (off by default).

Let me know how it goes, and thanks again for bringing Quest 1 support.

@iChris4
iChris4 merged commit 83805a4 into iChris4:openxr-work Sep 26, 2026
@darthcircuit

Copy link
Copy Markdown
Author

This is awesome. Thank you! I'm indisposed this next week, but I've got some more stuff I want to add in when I'm available again.

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.

2 participants