Mesa enforces OVR_multiview's rule that a draw's program declares as many views as the draw framebuffer has and drops the draw silently; Doom3Quest compiles every vertex shader with layout(num_views=2) and draws its HUD/PDA into 2D-texture FBOs, which stay black on the Frame. native/glmv (libfpglmv.so, same length as libGLESv3.so): the engine library's dlopen string is rewritten to it and it becomes the first DT_NEEDED. It records stage sources at link time and, when a multiview program draws into a framebuffer without views, draws with a lazily built single-view twin (num_views layout blanked, gl_ViewID_OVR -> 0u; attribute locations, block bindings and uniform values copied), then rebinds the original. Opt-in and experimental (analysis gl_multiview_libs, ANALYSIS_VERSION 6), setting gl_mv_debug, triage gl-multiview-twin-failed. Host tests: the rewriter on Doom3Quest's 19 shaders (GPL-3.0 fixtures) and glmv.c against a stand-in GL that applies Mesa's rule. Untested on the device.
12 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, proximity_emul = finger proximity (XR_FB_touch_controller_proximity, which the Frame lacks) from the capacitive touch inputs: OVRPlugin's hand_thumb_proximity/hand_trigger_proximity actions get extra Touch bindings); input_diag.c = controller-input diagnostics (setting input_diag, off by default and hooked only when on; logs suggested interaction profiles and the runtime's answers, each hand's current profile, functions the runtime lacks and failing input/haptics/perf calls, each line once; tested on the host by tests/test_input_diag.py); 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, vrapi_PollEvent/RecenterPose/SetDisplayRefreshRate/GetSystemPropertyFloatArray (BlazeRush imports them, GitHub #57), diagnostics behind -DOVP_GL_DIAG. upstream-patches/ reproduces these sources from the fork's native/vrapi (checked 2026-10-06 against its latest commit); upstream PRs Android-XR-Bridge/OVRPort#3-#8, tracked in GitHub #75. |
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. With patch frame.asset_files (marker byte @FPASSETS@, or env FRAMEPORT_ASSET_FILES=1) it also lists the data's *.pak content files as installed default asset files and answers ovr_AssetFile_DownloadById for them at once (a completed DownloadUpdate, then the result with the path). 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. |
glmv/ |
arm64-v8a/libfpglmv.so |
Single-view draws of OVR_multiview programs (patch frame.gl_multiview_fbo, opt-in, GitHub #77). Mesa enforces OVR_multiview's rule that a draw's program declares as many views as the draw framebuffer has; engines that compile every shader with num_views=2 and also draw into ordinary 2D framebuffers (Doom3Quest's HUD/PDA pool) lose those draws. The engine library gets it as first DT_NEEDED (direct gl* imports) and its dlopen("libGLESv3.so") string is rewritten to libfpglmv.so (same length; its qgl* dlsym table); linked --no-as-needed to libGLESv3 so dlsym on its handle finds everything it doesn't wrap. It records stage sources at glLinkProgram, caches the draw framebuffer's color-attachment view count per bind, and when a multiview program draws into a 0-view framebuffer it binds a lazily built twin (glmv_rewrite.h: num_views layout blanked, gl_ViewID_OVR -> (0u); attribute locations + uniform block bindings copied, default-block uniforms copied before every such draw with a shadow to skip unchanged ones), draws, rebinds the original. Logcat tag GLMV; gl_mv_debug=1. The rewriter is host-tested on Doom3Quest's 19 shaders (tests/test_gl_multiview_fbo.py); untested on the device. |
zinkfix/ |
arm64-v8a/libVkLayer_fp_shaderfix.so |
Shader-fix Vulkan layer VK_LAYER_FP_shader_fix for OpenGL ES games (patch frame.zink_shader_fix): sits between Zink (Mesa's GL on Vulkan) and the driver and inserts the words of zink_shader_fix (same format as the Vulkan shim's vk_shader_fix: size + SHA-256 match) into SPIR-V modules; zink_shader_dump=1 writes every distinct module to files/fp_spirv/. The engine library loads it first (DT_NEEDED); its constructor puts the layer in front of Android's GraphicsEnv debug layer list (private Android 11 getDebugLayers/setDebugLayers, found through libvulkan.so's dependencies) before EGL starts, so the loader picks it up from the app's lib dir. Exports only the enumeration functions and VK_LAYER_FP_shader_fixGet*ProcAddr (never plain vk* entry points). Vader Immortal's lightspeed shaders (Klownicle, GitHub #49). |
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. |
venc/ |
linux-arm64-bin/fp_venc |
Hardware H.264 encoder for the live view, run on the Frame (spec: venc/SPEC.md). Reads SteamVR's "headset view" webcam (v4l2loopback /dev/video99, RGB24), box-scales and converts it to NV12 (BT.709 limited range; convert.c, NEON with a bit-identical scalar reference, also built for x86_64 by the host test), and feeds the SoC's V4L2 stateful encoder (dev-encoder.rst, Qualcomm iris /dev/video-enc0): CBR, no B-frames, SPS/PPS on every IDR, one frame per slot of a fixed grid (fps = a divisor of the panel refresh read through DRM KMS, default cap 45), Annex B on stdout. stdin k = keyframe, q/EOF = drain and stop; --probe prints the setup as JSON, --selftest compares NEON and scalar output. Fully static and freestanding (raw syscalls, own _start; only the NDK's kernel UAPI headers, staged in .cache/uapi-arm64), byte-reproducible. Lives in linux-arm64-bin/ because linux-arm64/ is uploaded whole as the OpenXR layer. |
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.