mirror of
https://github.com/daniel-lynch/ovrplugin-openxr-shim.git
synced 2026-10-06 01:00:05 +02:00
An independent reimplementation of Meta's libOVRPlugin ABI on top of OpenXR, so VrApi-era Meta Quest VR titles can run on non-Meta OpenXR runtimes (Monado, Steam Frame) instead of being locked to Meta hardware. Original code only — no Meta/Epic/Capcom binaries, headers, or assets. Includes a desktop harness that drives the shim against Monado headless. Scope/legal: interoperability; entitlement handling is out of scope. See README for the legal/scope section and docs/ for the research trail and design notes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D6sFYGXZPsq3v7xtcDES6g
4.8 KiB
4.8 KiB
RE4 VR Shim — Handoff (2026-06-24 session)
Entry point for the next session. Detailed investigation log: NEXT-SESSION-LATENCY.md.
TL;DR
- The head-motion ghosting is frame drops caused by the synchronous tile-memory flush-wait serializing CPU↔GPU (proven: removing it on the title dropped frame time 13–36ms → ~3ms and killed the ghosting).
- Shipped fix (always on): perf levels. We were no-op'ing the game's CPU/GPU level
requests; now forwarded via
XR_EXT_performance_settings. Confirmed on device: GPU clock 305→587MHz, CPU steady 2419MHz, framerate up. The default build is playable (synchronous path + perf fix). - Copy-ring (the real serialization fix) is built but blocked on an MSAA-resolve
interaction in gameplay — see below. Default OFF (
debug.re4vr.copyring). - Caveat — the title still ghosts INITIALLY in every build we tried. Default build:
title ghosting reduced by the perf fix but not gone. Copy-ring on: title ghosts at
first, then "looks good after a while" (settles, not clean from frame 1). Likely
cause of the initial ghosting in both: the title's asset streaming / load hitches
(the ~49ms
CPU&GPUspikes in VrApi, Stale frames), which NONE of the fixes address. So: perf fix + copy-ring attack the steady-state flush-wait drops; the initial load-driven hitching is a separate, still-open cause.
Build / deploy
./shim/build_android.sh && ./packaging/repack.sh && adb install -r packaging/out/re4vr-shim.apk
adb logcat -d -s xrr # shim logs
Device = Quest 2 (USB serial , or wireless :5555).
Package com.Armature.VR4, activity com.epicgames.ue4.GameActivity.
Runtime debug toggles (system props; relaunch unless noted)
debug.re4vr.copyring 1— shim copy-ring (eye-only; gameplay black, see below)debug.re4vr.sscap 1— cap supersample 1.2x→1.0x (free GPU/bandwidth)debug.re4vr.diag 1|2|3— 1=projection only(drop quads), 2=force mono, 3=2x zoom (live)debug.re4vr.dump N— one-shot eye-texture readback to /sdcard/Android/data/com.Armature.VR4/files/debug.re4vr.depth 1— DEAD END (UE passes None; Meta ignores plain KHR depth)debug.re4vr.pipeline 1— DEAD END (render-ahead by holding OpenXR images; breaks UE stage lockstep)debug.re4vr.noflushwait 1— perf probe: skip flush-wait (renders black; FPS recovers → confirms serialization)
The open problem: copy-ring gameplay = MSAA resolve into our shim
RenderDoc (offline replay over USB) showed:
- UE renders the eye with 2× MSAA into its own target, render pass resolve attachment = our copy-ring shim (the image we copy to OpenXR). No stage mismatch.
- On the title that resolve lands → copy-ring works. In gameplay the resolved shim isn't the scene at our copy (white/garbage) → black/flashing.
- So: UE's MSAA resolve into our externally-created shim VkImage works on the title
but not in gameplay. Next suspects: the shim's
MUTABLE_FORMAT/sRGB-vs-UNORM resolve view, create flags vs a valid resolve-dst, or Adreno tile-MSAA specifics. Single-shim made it worse (UE needs distinct per-stage images for TAA/buffering). - The fundamental tension: removing the flush-wait needs pipelining, which breaks UE's 1:1 stage↔acquire lockstep (the synchronous path relies on it). Copy-ring decouples via shim images but inherits the MSAA-resolve issue.
RenderDoc offline-replay pipeline (set up this session — no headset needed to analyze)
- App must be debuggable:
tools/apktool.jardecode → addandroid:debuggable="true"→ rebuild → sign (debug.keystore) →packaging/out/re4vr-dbg.apk. (RE-uses the shim.) - RenderDoc server installed (
org.renderdoc.renderdoccmd.arm64). Capture over USB (wireless adb drops the replay; local desktop replay of an Android capture fails). - Headless analysis:
qrenderdoc --python script.pywithadb://<usb-serial>→ CreateRemoteServerConnection → CopyCaptureToRemote → remote.OpenCapture. Scripts + captures in~/renderdoc-captures/(rd_*.py, RE4/black.rdc = gameplay, RE4/title.rdc). Useaction.outputsfor RT attribution and SaveTexture (NOT GetMinMax/Typeless — it lies) for content.
Recommended next steps
- Chase the MSAA-resolve-into-shim failure (RenderDoc pipeline is ready): compare the title resolve (works) vs gameplay (fails) render-pass setup; try shim WITHOUT MUTABLE_FORMAT, or matching UE's exact resolve-target image create info.
- If copy-ring stays blocked, the perf fix is the shipped win; consider lighter levers (sscap) and accept residual menu-screen ghosting.
Notes
- Diagnostic logging is still in the shim (rate-limited; strip before public release).
- Depth format mapping was corrected (None=10; D16=6/D24_S8=7/D32_FP=8/D32_S824=9).