mirror of
https://github.com/daniel-lynch/ovrplugin-openxr-shim.git
synced 2026-10-06 04:04:09 +02:00
docs+build: hygiene pass — close leak risk, de-drift docs, fix build prereqs
Repo hygiene round following a full review. No shim behaviour changes. Leak risk: - .gitignore: ignore CLAUDE.md (personal assistant-lane config, was one `git add -A` away from a public commit) and scratch_obj/. Docs vs. reality: - shim/README.md: rewritten. It described a pre-implementation skeleton with "core fns are TODO stubs returning -1005", three mutually inconsistent stub counts, and four completed milestones listed as open. Now carries the verified breakdown: 438/438 exports = 371 generated stubs + 46 core + 7 layers + 2 Vulkan queries + 12 passthru trampolines. - TESTING.md: dropped the self-contradicting "NOT yet" block (5 of 6 items were done or misstated, and contradicted the same file 45 lines above). Path B now points at tools/desktop-harness, which exists, instead of the orphaned shim/tests/harness.c. Path A prereqs marked as the record they are. - HOST.md: corrected the runtime assumption. The OpenXR runtime inside Lepton is SteamVR (vendor/etc/openxr/1/active_runtime.json -> vrclient.so), not Monado. Favourable: SteamVR emulates Oculus Touch by default and advertises the XR_FB_foveation family, so the existing input and foveation paths should carry over. The old "remaining unknowns" are resolved by Lepton's published source and replaced with the items to check before a first Frame boot. - README.md: same runtime correction. - docs/research/RECON.md: the four passages prescribing an entitlement NOP/stub/bypass are corrected in place rather than merely disclaimed by the top banner, which they contradicted. Build correctness: - shim/build_android.sh: missing patchelf is now fatal. It warned and exited 0, producing a .so that cannot resolve the OpenXR loader at runtime. - scripts/fetch_deps.sh + packaging/build_openxr_loader.sh: pin the OpenXR and Vulkan header versions (were tracking `main`), overridable via OPENXR_TAG / VULKAN_HEADERS_TAG; require cmake for the loader build. - packaging/steamframe_patches.sh: use the apktool.jar that fetch_deps.sh downloads. Its prereq check demanded an `apktool` binary on PATH that the documented setup never provides, so it could not run after a clean setup. - shim/gen_stubs.sh: it reads all_exports.txt, not shim_surface.txt; comment and emitted banner corrected. stubs.c regenerated (banner line only). - shim/src/core.c: split seven `if (out) ...; return ...;` one-liners. Host build now compiles with zero warnings, down from seven. Verified: host build 0 warnings; gen_stubs.sh output identical on regeneration; bash -n clean on all edited scripts; pinned header/tarball URLs return 200 and the tag tarball extracts to the expected directory name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
574f41a0e4
commit
1f3dc40c07
13 files changed
+304
-161
No files matched your search
+88
-47
@@ -25,7 +25,7 @@ Idea: build the shim as an Android arm64 `.so`, drop it into the RE4 APK in plac
|
||||
of the real `libOVRPlugin.so`, sideload, run. Our shim calls the Quest's own
|
||||
`libopenxr_loader` -> Meta's OpenXR runtime.
|
||||
|
||||
Prereqs to do first (these are the currently-open work items):
|
||||
Prereqs (all done — kept as the record of what Path A needed):
|
||||
1. **NDK arm64 build** of the shim — DONE. `shim/build_android.sh` -> NDK r27c ->
|
||||
build/arm64/libOVRPlugin.so (aarch64, 438/438 drop-in, NEEDED libopenxr_loader).
|
||||
2. **Instance handshake** — DONE. src/android_init.c: JNI_OnLoad captures the JavaVM;
|
||||
@@ -34,9 +34,9 @@ Prereqs to do first (these are the currently-open work items):
|
||||
Application-context reflection fallback. [VERIFY-ON-HW] whether Meta's runtime
|
||||
accepts the Application context vs requiring the real Activity, and the
|
||||
PreInitialize3-creates-instance-before-activity ordering.
|
||||
3. **Manifest** — add the OpenXR usage declarations Meta's runtime expects
|
||||
(`<uses-feature android:name="android.hardware.vr.headtracking">` already there;
|
||||
add OpenXR `<meta-data>`/intent bits per Meta's OpenXR mobile docs).
|
||||
3. **Manifest** — no change needed in the end. RE4 VR is already a shipping Quest VR app
|
||||
and declares `<uses-feature android:name="android.hardware.vr.headtracking">`;
|
||||
`packaging/inspect_manifest.sh` reports any gaps.
|
||||
4. **Entitlement** — on Quest you OWN RE4 and the Quest has the real Meta Horizon
|
||||
platform service, so leave the ORIGINAL libovrplatformloader.so untouched and only
|
||||
swap libOVRPlugin.so. Logged into the owning account, the real ovr_Entitlement check
|
||||
@@ -55,61 +55,102 @@ cp shim/build/arm64/libOVRPlugin.so apk/lib/arm64-v8a/libOVRPlugin.so
|
||||
adb install -r re4vr-shim.apk # or push OBB + sideload
|
||||
adb logcat | grep -iE 'xrr|OVRPlugin|openxr' # watch the [xrr] logs
|
||||
```
|
||||
Expected first-run signal: instance/session create succeed in logcat; if the frame
|
||||
loop spins and the projection layer submits, you get an image (even if poses/input
|
||||
are rough). Known rough edges on first run: depth (Unsupported), input (controllers
|
||||
return NotYetImplemented), the [VERIFY-ON-HW] swapchain index lockstep.
|
||||
`packaging/repack.sh` now does the repack, align and re-sign in one step; the manual
|
||||
sequence above is kept only to show what it does.
|
||||
|
||||
## Path B — Monado simulated (fast iteration, works on the Mac)
|
||||
First-run signal to look for: instance and session create succeeding in logcat, then the
|
||||
frame loop spinning and the projection layer submitting. (Historically the rough edges on a
|
||||
first run were depth, input and the swapchain index lockstep; input and rendering are now
|
||||
working — see "Current state" below.)
|
||||
|
||||
Monado has a simulated/headless HMD driver — no real headset. Run it in a Linux
|
||||
arm64 VM (UTM/QEMU on Apple Silicon), point an OpenXR loader at it, and run the
|
||||
harness (tests/harness.c) which drives the ovrp_* sequence:
|
||||
## Path B — desktop OpenXR harness (fast iteration) — BUILT
|
||||
|
||||
`tools/desktop-harness/` drives the `ovrp_*` sequence against a real OpenXR runtime with no
|
||||
headset and no RE4:
|
||||
PreInitialize3 -> Initialize5 -> SetupLayer -> [WaitToBeginFrame -> BeginFrame4 ->
|
||||
GetNodePoseState3 -> EndFrame4] xN -> Shutdown2
|
||||
and asserts each returns ovrpSuccess. This exercises the real OpenXR calls without
|
||||
RE4 or hardware. Build:
|
||||
asserting each returns `ovrpSuccess`. Build and run:
|
||||
```
|
||||
# inside the Linux arm64 env, with Monado + openxr loader installed:
|
||||
cc -std=c11 -Iinclude -Ithird_party/openxr tests/harness.c \
|
||||
src/*.c -lopenxr_loader -lvulkan -o harness # needs a Vulkan device/headless
|
||||
XR_RUNTIME_JSON=/path/to/monado/openxr_monado-dev.json ./harness
|
||||
shim/build_host.sh # -> build/host/{libOVRPlugin.so,harness}
|
||||
tools/desktop-harness/run.sh # headless against monado-service
|
||||
```
|
||||
Note: Initialize5 needs real Vulkan handles; for a pure-logic smoke test the harness
|
||||
can pass a headless VkInstance/Device (or we add a "no-gfx" build flag that skips
|
||||
xrCreateSession to test the non-rendering calls first).
|
||||
Needs libvulkan and an OpenXR loader (Debian: `libvulkan-dev libopenxr-loader1
|
||||
libopenxr-dev`); without the loader `build_host.sh` compiles the objects and stops.
|
||||
|
||||
Worth adding when chasing a portability bug: run it with `VK_LAYER_KHRONOS_validation` and
|
||||
the OpenXR core-validation API layer enabled. The harness currently passes without them, so
|
||||
it does not yet catch usage-flag or queue-family mistakes that a strict runtime would reject.
|
||||
|
||||
Note: `shim/tests/harness.c` is the original superseded smoke test; its build line is stale
|
||||
and no script references it.
|
||||
|
||||
## Path C — Steam Frame (the target)
|
||||
|
||||
Same arm64 shim `.so`, but the APK runs under **Lepton** (Valve's Waydroid/AOSP) and
|
||||
the OpenXR runtime is **Monado**. Once Frame ships: NDK build -> repack -> sideload
|
||||
into Lepton -> the open question is whether Lepton exposes the OpenXR loader +
|
||||
Vulkan swapchain sharing across the container (see HOST.md unknowns). Path A having
|
||||
worked makes this mostly a packaging/runtime-plumbing exercise.
|
||||
Same arm64 shim `.so`, but the APK runs under **Lepton** (Valve's Waydroid fork, now open
|
||||
source) and the OpenXR runtime inside the container is **SteamVR** — Lepton's
|
||||
`vendor/etc/openxr/1/active_runtime.json` names `steamvr` and points at a host-mounted
|
||||
`vrclient.so`. (Earlier notes here and in `HOST.md` assumed Monado; that was wrong.)
|
||||
|
||||
What that buys us: SteamVR presents Frame controllers as emulating Oculus Touch by default,
|
||||
so the shim's Touch bindings should bind; and it advertises the `XR_FB_foveation` family plus
|
||||
`XR_META_foveation_eye_tracked`, so the existing foveation path survives. Lepton also mounts
|
||||
the host's mesa/turnip/zink and gralloc into the container, which answers the old
|
||||
`HOST.md` unknown about sharing swapchain images across the container boundary.
|
||||
|
||||
Steam Frame shipped 2026-09-14. Flow: NDK build -> `repack.sh` -> `steamframe_patches.sh`
|
||||
(the Build spoof is required — Lepton reports `ro.product.manufacturer=Valve`) -> sideload
|
||||
via adb into a Lepton container. Path A having worked makes this mostly packaging and
|
||||
runtime-plumbing, but see `HOST.md` for the specific items to check first.
|
||||
|
||||
---
|
||||
|
||||
## Current state (what's ready to test vs not)
|
||||
## Current state
|
||||
|
||||
WIRED & building (host x86-64, validation only):
|
||||
- Session lifecycle: instance/system/session create, event-driven state machine.
|
||||
- Frame loop: xrWaitFrame/Begin/EndFrame, predicted display time.
|
||||
- Poses: xrLocateViews (eyes) + xrLocateSpace (head).
|
||||
- Swapchains: xrCreateSwapchain from ovrpLayerDesc, enumerate VkImages, per-frame
|
||||
acquire/wait/release, real XrCompositionLayerProjection submit.
|
||||
- 20 OpenXR functions; 239/239 ovrp_ symbols.
|
||||
**Path A is done and field-verified.** RE4 VR boots and is playable on a Quest 2 through the
|
||||
shim on Meta's OpenXR runtime: stereo rendering, head and controller tracking, buttons,
|
||||
grips, haptics, save loading. The frame-pacing "ghost" that dominated development is fixed
|
||||
(game-thread pacing; see `docs/research/ghost-fix-2026-06-27.md`). Shipped and verified:
|
||||
|
||||
NOT yet (the to-do list before a meaningful Quest run):
|
||||
- arm64/Android NDK build (host build only so far).
|
||||
- [ANDROID-TODO] JavaVM/activity -> XrInstanceCreateInfoAndroidKHR + xrInitializeLoaderKHR.
|
||||
- Input: controller/hand action sets (GetControllerState4 returns NotYetImplemented).
|
||||
- Depth layer (SetupLayerDepth -> Unsupported).
|
||||
- Map ovrp_GetInstance/DeviceExtensionsVk -> xrGetVulkan*ExtensionsKHR (so the app
|
||||
creates its VkInstance/Device with the runtime's required extensions).
|
||||
- [VERIFY-ON-HW] swapchain index lockstep assumption.
|
||||
- Session lifecycle, event-driven state machine, Android instance handshake
|
||||
(`src/android_init.c`).
|
||||
- Frame loop with `xrWaitFrame` on the game thread and a FIFO frameState handoff to the
|
||||
render thread.
|
||||
- Poses via `xrLocateViews` and `xrLocateSpace`; `LOCAL_FLOOR` when the game asks for floor
|
||||
level.
|
||||
- Swapchains from `ovrpLayerDesc`, per-frame acquire/wait/release, real projection-layer
|
||||
submit, layer z-order fix so splash quads composite above the eye layer.
|
||||
- Input action sets for Touch controllers plus haptics (`src/xr_input.c`).
|
||||
- Vulkan extension queries mapped to `xrGetVulkan*ExtensionsKHR` (`src/vk_session.c`).
|
||||
- CPU/GPU perf levels forwarded via `XR_EXT_performance_settings`; game-driven foveation.
|
||||
- 438/438 `ovrp_` symbols; around 48 OpenXR entry points.
|
||||
|
||||
## Pick-up-tomorrow shortlist
|
||||
1. Install Android NDK; cross-build the shim to arm64 (proves it builds for target).
|
||||
2. Stand up the Monado-sim Linux arm64 VM on the Mac + run tests/harness.c (Path B
|
||||
smoke test of the non-gfx calls).
|
||||
3. Then start the [ANDROID-TODO] instance handshake (gates the real Quest run).
|
||||
**Open / known gaps:**
|
||||
- Depth layer submission is built but gated off (`debug.re4vr.depth`); Meta's runtime accepts
|
||||
but does not use plain KHR depth for reprojection.
|
||||
- Swapchain index lockstep with UE's own `TextureStage` is assumed, not enforced — a
|
||||
mismatch is logged but not corrected.
|
||||
- Residual `XR_FRAME_DISCARDED` hiccups from the frameState ring dropping its oldest entry
|
||||
under overflow instead of applying back-pressure.
|
||||
- In-game black near load zones is a level-streaming / memory stall, mitigated rather than
|
||||
fixed. See `docs/handoffs/HANDOFF-2026-06-27.md`.
|
||||
- Several `debug.re4vr.*` paths are documented dead ends kept for reference.
|
||||
|
||||
## Next: Path C (Steam Frame)
|
||||
|
||||
Frame shipped 2026-09-14, so this is the live front. Highest-value items before a first boot
|
||||
attempt, in order:
|
||||
1. **Page alignment.** The shim's ELF segments and the repacked APK are 4 KB-aligned
|
||||
(`zipalign -p 4`); Valve's Unreal docs reference a 16 KB page-alignment requirement. If
|
||||
the Frame kernel uses 16 KB pages the library will not load. Check first: it is a
|
||||
`-Wl,-z,max-page-size=16384` plus `zipalign -P 16` fix, but it would present as a
|
||||
mystery launch failure.
|
||||
2. **Swapchain usage flags.** `setup_layer` requests only COLOR_ATTACHMENT and SAMPLED.
|
||||
Meta over-provisions mutable, broadly-usable images; a spec-following runtime gives you
|
||||
exactly what you asked for, and UE 4.25 needs a mutable format for its linear UNORM view.
|
||||
3. **Refresh rate.** 72 Hz is hardcoded in `ovrp_GetSystemDisplayFrequency2` and in the
|
||||
frame-budget constant. Frame runs 72/90/120/144. Derive it from
|
||||
`predictedDisplayPeriod`.
|
||||
4. **Session events.** Only READY and STOPPING are handled, so focus loss and quit from the
|
||||
Steam overlay never reach the game.
|
||||
5. **The per-frame GPU flush-wait.** It exists because Meta's compositor does not sync
|
||||
against our submit. A/B it on Frame with `debug.re4vr.noflushwait`.
|
||||
Reference in new issue
Block a user