7.6 KiB
Native components
FrameBridge includes automatic metadata retirement for the verified Meta XR Audio Wwise build; see AUDIO_METADATA.md for its lifetime invariant and regression coverage. It uses the existing adapter installation.
Sources of the prebuilt binaries in ../artifacts/ (committed, with SHA256SUMS). End users never compile anything;
developers rebuild with python native/build.py (downloads NDK r27c, OpenXR headers at pinned commits, d8 into
native/.cache/, git-ignored). The adapter, GL shim and platform compat rebuild byte-identically; the VrApi bridge
embeds debug paths (-g) and the dex depends on the d8 version, so those differ in bytes only.
| Dir | Artifact | What / why |
|---|---|---|
adapter/ |
{arm64-v8a,armeabi-v7a}/libopenxr_loader_generic.so |
FrameBridge: replaces overport's generic loader (which is renamed libopenxr_loader_original.so) and fixes Steam Frame runtime gaps. Hooks xrCreateInstance, swapchains, xrEndFrame, spaces, xrPollEvent, … everything else is forwarded by gen_forwarders.py-generated tail calls. scene_emu.c = Meta scene/spatial-entity emulation; flip_vk.c = Vulkan blit for VERTICAL_FLIP quads. session_fixes.c = per-game session fixes, all off by default and hooked only when on (layer_debug diagnostics, stable_local, focus_hold, aim correction aim_pitch/aim_yaw/aim_forward, refresh_rate); layer_emul_gl.c = equirect_emul (GLES sessions only): 360° equirect/equirect2 layers become one adapter projection layer, drawn by a worker thread with its own EGL context shared with the app's: each 360° image is converted to a cube map only when it changes, and the view is redrawn only after a 4° head turn (GL/EGL resolved with dlopen, so Vulkan games never load them); layer_math.h = their pose/face math, tested on the host by tests/test_layer_emul.py. render_model.c = XR_FB_render_model emulation (setting controller_models, off by default): serves files/framebridge/controller_{left,right}.glb, which the agent converts on the Frame from SteamVR's own Frame controller render models (never shipped by FramePort); tested on the host by tests/test_render_model.py. Settings: libframe_settings.so in the APK + settings.conf/framebridge.conf on the Frame. |
vrapi-bridge/ |
arm64-v8a/libvrapi.so |
VrApi → OpenXR bridge from Android-XR-Bridge/OVRPort native/vrapi at 5e7df52 (GPL-3.0, LICENSE.upstream; unchanged upstream as of OVRPort v1.2.5, which ships the unpatched code as its opt-in patch_vrapi_openxr in experimental CLI builds), with our changes in upstream-patches/: GLES sessions + GL texture swapchains, cylinder→quad layers, GL vertical flip, UNORM↔sRGB format twins, 30 s VR-mode deadline, VALID-only recenter, loading-icon layers skipped, diagnostics behind -DOVP_GL_DIAG. |
platformcompat/ |
arm64-v8a/libovrplatformcompat.so |
Real ovrMessageType_ToString (same fork, native/platform). |
langpack/ |
arm64-v8a/libfp_langpack.so |
Language packs for the Meta Platform SDK (patch frame.langpacks, opt-in). overport's platform loader is a dispatcher in front of Meta's own loader and answers ovr_LanguagePack_GetCurrent/SetCurrent with return 0 (no reply ever). This library becomes the loader's first DT_NEEDED; the loader's own exports of the 31 functions it defines are made STB_LOCAL in .dynsym (elf.hide_exports, nothing moves), so symbol lookups reach the library first. It serves <tag>.lang files found in the game's OBB / app files ($FRAMEPORT_LANGPACK_DIRS overrides the search path) through ovr_LanguagePack_*, ovr_AssetFile_GetList (the loader's list + the packs) and ovr_AssetFile_StatusById; its own message queue is merged into ovr_PopMessage, and every handle that is not ours (an address-range check) goes to the loader's original function, whose address is read at run time from the loader's in-memory .dynsym. Host-tested against a stand-in loader (tests/test_langpack.py, plain cc, no NDK); the bionic/headset side is unverified. |
glshim/ |
arm64-v8a/libglshim.so |
Mesa GLSL compatibility for GLAD engines (hooks eglGetProcAddress): comments out #pragma before #extension, enables GL_EXT_shader_implicit_conversions, hides GL_OVR_multiview (gl_hide_multiview, default 1) and logs failed shaders. -DGLSHIM_TRACE adds per-FBO draw/error tracing. |
xrlayer/ |
linux-arm64/libxr_frameport_timefix.so + XR_APILAYER_FRAMEPORT_timefix.json |
OpenXR API layer for Windows PC VR games under Proton on the Frame (glibc aarch64, built freestanding with the NDK's clang), on by default (patch pcvr.xr_timefix). Retries xrCreateInstance as an OpenXR 1.0 app when the runtime rejects 1.1 (the Frame's SteamVR runtime does, and Proton 11's VR helper asks for 1.1). Also emulates xrConvertTimespecTimeToTimeKHR/xrConvertTimeToTimespecTimeKHR if the runtime refuses them (the Frame's Android runtime does; its Linux runtime, used by Proton, supports them as of 2026-09-29 — so this part is only a fallback; Proton's wineopenxr needs them for XR_KHR_win32_convert_performance_counter_time, which Revive requires) with the adapter's xrWaitFrame-calibrated offset, and drops the extension from xrCreateInstance if the runtime rejects it. Enabled per game by the Proton launch.sh (XR_API_LAYER_PATH, XR_ENABLE_API_LAYERS). tests/test_xrlayer.py builds it for x86_64 and drives it via ctypes. |
oculushmd/ |
win-x64/fp_oculushmd.exe |
PC VR counterpart of overport's patch_oculus_unreal (patch pcvr.oculus_unreal, default for Unreal Rift games on the Frame). Unreal's OculusHMD and LibOVR's ovr_Detect only start when the Windows named event OculusHMDConnected exists and is signalled (the Oculus service creates it). fp_oculushmd.exe <command line> creates it (manual reset, signalled), runs the command (Revive's injector) in a job object and keeps the event until every process in the job — the game and its children — has exited (fallback without job support: 3 minutes); returns the command's exit code and logs FramePort oculushmd: … to stderr (launch.log). Freestanding Windows x64 console exe (no CRT, no Windows SDK): the NDK's clang + lld-link /Brepro with a kernel32 import library generated by llvm-dlltool, so it's byte-reproducible. Tested on Windows from WSL with a build whose event name is -DEVENT_NAME=L"…" (the Oculus service owns the real event on a PC with the Oculus app). |
xrshim/ |
arm64-v8a/libframe_xrshim.so |
FrameBridge extension shim, only in builds with controller_models=1. overport's dispatcher (libopenxr_loader.so) answers xrGetInstanceProcAddr from a fixed table and refuses everything else (overportOXR: Unknown proc addr: xrLoadRenderModelFB), so the adapter never sees XR_FB_render_model lookups. Meta's OVRPlugin gets the function with dlopen("libopenxr_loader.so") + dlsym, so FramePort rewrites that .rodata string in libOVRPlugin.so (same length, in place) to libframe_xrshim.so. The shim returns the adapter's emulated functions (framebridge_extension_proc) and forwards every other name to overport. A DT_NEEDED interposer does not work here (dlsym on a handle searches that library first); tried and dropped. |
java-stubs/ |
dex/oculusos-stubs.dex |
No-op com.oculus.os.AnalyticsEvent / UnifiedTelemetryLogger for native code that looks them up. |
The ovr_* stub library is not prebuilt: src/frameport/analysis/stubgen.py generates it per game from the exact
missing symbols (no compiler needed).
After changing anything here: python native/build.py, commit artifacts + SHA256SUMS, then run frameport parity
(the report marks updated binaries as "expected") and a device launch test.