Files
daniel-lynch--ovrplugin-ope…/tools/desktop-harness/README.md
T
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

97 lines
4.0 KiB
Markdown
Raw 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.
# Desktop harness — drive the shim against Monado on a PC
The shim is normally exercised only on a Quest (inside RE4's APK). This harness lets you
run its **OpenXR path on a Linux desktop**, headless, against
[Monado](https://monado.freedesktop.org/)'s *simulated HMD* — no headset, no game, no
`libUE4`. It's the fast iteration loop for the Steam Frame / Monado / Lepton bring-up.
## What it is
`harness.c` stands in for the game (UE4 + `OculusHMD`). It creates a Vulkan
instance/device the way UE's VulkanRHI does, then calls our `ovrp_*` exports **in UE's
order**:
```
PreInitialize3 -> Get{Instance,Device}ExtensionsVk -> Initialize5
-> CalculateEyeLayerDesc2 -> SetupLayer (once)
-> per frame: Update3, WaitToBeginFrame, BeginFrame4, GetLayerTexture2,
(clear the eye image), EndFrame4
-> Shutdown2
```
The shim does the real OpenXR work underneath — `xrCreateInstance`, `xrGetSystem`,
`xrCreateSession` (Vulkan binding), `xrCreateSwapchain`, the `xrWaitFrame/Begin/EndFrame`
loop — against whatever runtime the OpenXR loader selects. Here that's Monado's simulated
HMD with the **NULL compositor** (renders nowhere), so it runs over SSH / in CI.
It ships nothing from Capcom/Epic/Meta — it only calls our own public `ovrp_*` ABI.
## Prereqs (Debian/Ubuntu)
```sh
sudo apt-get install monado-service libopenxr1-monado libopenxr-loader1 libopenxr-dev \
libvulkan-dev
```
## Build & run
```sh
shim/build_host.sh # builds build/host/libOVRPlugin.so + build/host/harness
tools/desktop-harness/run.sh # brings up monado-service headless, runs 300 frames
tools/desktop-harness/run.sh 1000 # custom frame count
```
Logs land in `build/host/monado.log` and `build/host/harness.log`.
### The scene (pose→view validation)
Each frame the harness queries the shim's per-eye pose (`ovrp_GetNodePoseState3` for
`EyeLeft`/`EyeRight`, with true IPD separation) and FOV, builds per-pixel world rays, and
renders a **world-locked procedural scene** — checkerboard floor 1.6 m below the eye, sky
gradient, and an orbiting sun — into the acquired eye image. This exercises the shim's
pose/FOV math: the two eyes show correct stereo parallax, and the world counter-moves as the
head pose changes (Monado's simulated HMD sways, so there's real motion). The first few
frames log per-eye pose + FOV. CPU-rendered (fine at the sim's 128×128; it's a test tool, not
a fast path).
### Watch it (windowed)
`VISIBLE=1` uses Monado's main compositor (mirror window) + the imgui debug GUI instead of
the NULL compositor, so you can watch the scene (floor grid, horizon, orbiting sun, stereo
parallax) and inspect swapchains. Needs a display — run it from the physical desktop session,
not over SSH, and give it a big frame count:
```sh
VISIBLE=1 tools/desktop-harness/run.sh 3600
```
## What "pass" looks like
`harness.log` should show the lifecycle succeed and frames present:
```
[harness] PreInitialize3 OK (XrInstance + system up)
[harness] VkInstance created
[harness] VkDevice + graphics queue (family 0) created
[harness] Initialize5 OK (XrSession created)
[harness] SetupLayer OK layerId=0 swapchainStages=3
[harness] ...
[harness] loop done: NNN/NNN frames presented
[harness] Shutdown2 OK — clean exit
```
Exit code 0 = frames presented; 2 = ran but presented nothing (session never reached the
running state — check `monado.log`); 1 = a hard failure.
## Limits / notes
- This validates the **OpenXR + Vulkan binding + frame loop + swapchain** path. It does
*not* reproduce the game-thread/render-thread pacing or GPU load that drove the on-Quest
"ghost"; those are device-side behaviours. It's for ABI/path correctness and porting to
new runtimes, not perf tuning.
- The Android session path (`XR_KHR_android_create_instance`, `xrInitializeLoaderKHR`, the
JavaVM/Activity chain) is `#ifdef __ANDROID__`-guarded in `xr_runtime.c` /
`android_init.c`, so the same sources serve both targets.
- `passthru.c` (P4 native forwarding) is Android-only; on host its arm64 trampolines fall
back to plain stubs and passthru stays inactive.