Files
Daniel LynchandClaude Opus 5 1f3dc40c07 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>
2026-09-18 02:15:10 -04:00

5.4 KiB

Target host platform — Steam Frame

Researched 2026-06-23; corrected 2026-09-18 against the shipped hardware and Lepton's published source. Answers "can the dumped APK run on Steam Frame, and do we need Android given SteamOS is Linux?"

Correction (2026-09-18): the 2026-06 research assumed the OpenXR runtime inside Lepton would be Monado. It is SteamVR. Lepton ships vendor/etc/openxr/1/active_runtime.json naming runtime steamvr (VALVE_runtime_is_steamvr: true) and pointing at a host-mounted /data/steamvr/runtime/bin/androidarm64/vrclient.so. Sections below are updated; treat any remaining "Monado" reference in older docs as superseded.

Do we need the Android side? YES.

The game is an Android binary, not a Linux one — ARM64==ARM64 does NOT bridge:

  • libUE4.so links bionic libc (ABI-incompatible with SteamOS glibc).
  • Depends on Android system libs: liblog, libandroid (ANativeActivity, AAssetManager, input), libOpenSLES; boots via a Java/JNI NativeActivity.
  • Reads OBB assets via Android AssetManager/storage paths. => Cannot run the .so bare on SteamOS. Must run inside an Android runtime. Only Android-free path = full native source recompile (no source -> not viable).

The host pieces all exist (and are open)

  • Lepton = Valve's official Android-on-Linux layer; a Waydroid/AOSP fork built specifically to run Quest APKs on Steam Frame, with sideloading. APKs run native ARM64, no emulation (the "Waydroid needs x86" caveat is about Waydroid on x86 PCs; Frame is ARM so it doesn't apply). Walkabout Mini Golf (Quest title) already cited running on it.
  • SteamVR is the OpenXR runtime apps see inside Lepton, bind-mounted in from the host (not Monado — see the correction above). It advertises the XR_FB_foveation family, XR_FB_swapchain_update_state, XR_META_foveation_eye_tracked and XR_EXT_eye_gaze_interaction, and it presents Frame controllers as emulating Oculus Touch by default (falling back Frame profile -> generic -> Touch). Both facts are favourable: our Touch bindings and our FB-foveation path should work unchanged.
  • Lepton also mounts the host's graphics stack into the container (mesa/turnip/zink, gralloc minigbm_msm) plus host Vulkan layers including a foveated-rendering injector and a renderpass optimizer.

Architecture

Steam Frame (SteamOS / Arch Linux, ARM64)
└─ Lepton (AOSP/Waydroid container, native ARM64)
    └─ RE4 VR APK (unmodified bionic Android binary)
        ├─ libUE4.so → [SHIM libOVRPlugin] → OpenXR → SteamVR (vrclient.so) → Frame compositor
        └─ ovr_* Platform SDK → out of scope (no entitlement code ships in this repo —
           a valid entitlement is the user's responsibility; see README "Legal / scope")

Why the shim IS the project

Meta ended VrApi support 2022-08-31; OpenXR is the only supported Quest API and Valve's whole stack is OpenXR (SteamVR on Frame). So:

  • OpenXR Quest games -> Lepton+SteamVR likely run them with little/no work.
  • VrApi games (RE4 VR) -> won't: Lepton/AOSP will never ship Meta's proprietary libvrapi.so, so the unmodified game finds no VR runtime. The OVRPlugin->OpenXR shim is exactly what bridges a dead-API VrApi game to Frame's OpenXR stack.

Status of the old unknowns (resolved 2026-09-18)

Steam Frame shipped 2026-09-14 and Lepton is open source (MIT for the tool), so the three 2026-06 unknowns are answered:

  1. Does Lepton expose an OpenXR runtime to apps inside the container? Yes — SteamVR, via the bind-mounted active_runtime.json and vrclient.so described above.
  2. Can a sideloaded app reach the runtime + compositor? Yes. Lepton documents adb sideloading (lepton install_app, or adb install against the container), and its own installer pushes an adjacent obb/ directory into the app's data — which matters for us, since RE4 VR ships its assets as OBBs.
  3. Vulkan swapchain sharing across the container GPU boundary — a non-issue by design: Lepton mounts the host graphics drivers into the container rather than proxying them.

New items to check before a first boot attempt

  • Page alignment (check this first). Our shim's ELF LOAD segments align at 4 KB and repack.sh runs zipalign -p 4. Valve's Unreal docs reference a 16 KB page-alignment requirement on this platform. If the Frame kernel uses 16 KB pages, the library will not load, and it would present as an unexplained launch failure. Fix is -Wl,-z,max-page-size=16384 at link time plus zipalign -P 16.
  • The Build spoof in packaging/steamframe_patches.sh is confirmed necessary. Lepton sets ro.product.manufacturer=Valve and ro.product.model=Lepton, so UE's Oculus-HMD gate is false without it and our shim is never called.
  • Swapchain usage flags. setup_layer requests only COLOR_ATTACHMENT and SAMPLED. Meta over-provisions; a spec-following runtime does not.
  • Refresh rate. 72 Hz is hardcoded in two places; Frame runs 72/90/120/144.
  • UECommandLine.txt. Lepton's installer pushes one into /data/steam_app, which may be a route for the UE streaming CVars that the baked-commandline dead end blocked on Quest. Unverified, but cheap to try.
  • App SDK level. RE4 VR is minSdkVersion 25 / targetSdkVersion 29, arm64-v8a only. Lepton rejects APKs whose SDK level is higher than the container's, so a low target should be fine — but it is untested.