Files
Daniel LynchandClaude Opus 4.8 a72a79ad29 Initial public release: OVRPlugin→OpenXR interoperability shim
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
2026-06-29 00:48:48 -04:00

75 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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&GPU` spikes 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 <redacted-serial>, or wireless <redacted-ip>: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.jar` decode → add `android: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.py` with
`adb://<usb-serial>` → CreateRemoteServerConnection → CopyCaptureToRemote →
remote.OpenCapture. Scripts + captures in `~/renderdoc-captures/` (rd_*.py,
RE4/black.rdc = gameplay, RE4/title.rdc). Use `action.outputs` for RT attribution and
SaveTexture (NOT GetMinMax/Typeless — it lies) for content.
## Recommended next steps
1. 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.
2. 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).