Files
spoopyghosty0--frameport/native
spoopyghosty0 0df0bd16b5 hevc: tone map HDR (PQ/HLG) Main 10 to SDR BT.709 instead of flat colours
The software Main 10 path narrowed HDR10 pictures to 8 bits as they were, so
PQ shown as SDR looked grey and BT.2020 colours dull. hdr_tonemap.h maps
brightness with BT.2390's EETF (203 cd/m2 reference white = SDR white,
highlights rolled off up to MaxCLL / the mastering peak / 1000 cd/m2) and
converts colour per chroma sample through linear light into BT.709, moving
out-of-gamut colours toward grey of the same luminance. The component then
prefers its bitstream colour aspects, so surfaces and its RGBA conversion see
BT.709. Bands run in the colour helper threads (about 10 ms per 1080p picture
on one big core). Decoder revision 10; artifacts rebuilt with build.py.
2026-10-19 12:41:20 -04:00
..

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.

adapter/swapchain_pool.c implements default-off GLES swapchain_pool (#387): at eight live app runtime swapchains, eligible single-layer colour images use private GL textures packed into up to two lazy 4096² sRGB runtime atlases. EndFrame copies each referenced source once and rewrites the final quads (including cylinder strips), cylinders and stereo projection views after existing layer fixes. OpenXR image ownership, GL bindings, scissor state, source fences, rectangle reuse and session cleanup are covered by tests/test_swapchain_pool.py (FRAMEPORT_NATIVE_TESTS=1, host compiler + cached OpenXR and GLES/EGL headers). Vulkan is unaffected. Host-tested; 4XVR submenu interaction, visual correctness and copy cost still need a headset check.

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
luajit/ arm64-v8a/libfp_luajit.so Pinned upstream LuaJIT with full-width lightuserdata (frame.luajit_pointers). Only the verified old Lua engine's shared public functions become imports; the original xLua API 105 bindings, LuaSocket helpers and luaopen_perflib remain in the game. No game library is bundled. Build: python native/build.py --only luajit; source revision + archive SHA-256 in luajit/build.py, MIT notices in luajit/COPYRIGHT and artifacts/licenses/LuaJIT.txt. runtime.c checks GC64 and the retained xlua_gl global-reference offset. smoke.c compares a real low-address allocation with the original library's own native static pointer; the unmodified library fails only the high-address case, the patched one round-trips both exactly. Startup verified on Frame; gameplay unverified.
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), legacy compositor debug/volume frame flags accepted, full-rate Vulkan foveation images allocated as density attachments (The Climb, GitHub #238), 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.
tlskeys/ arm64-v8a/libfp_tkeys.so Thread-key pool for Unity (patch frame.unity_tls_key_pool, opt-in, GitHub #198; approach and the first helper by Klownicle). Bionic allows 128 pthread keys per process; Unity 6 plus large native libraries (Firebase, Gecko) and the Frame's own libraries use them up (Wander: cannot create thread specific key for __cxa_get_globals()). libunity.so's four key imports are renamed to fp_tkey_create/_delete/_get/_set (elf.rename_imports, same .dynstr bytes, versions set global) and this library is added to its DT_NEEDED: keys without a destructor become slots (512, generation-tagged, 0x40000000 + slot) in a per-thread table under one real key; keys with a destructor and all other libraries keep real keys. Lock-free get/set. Logs fp_tkeys: pooling Unity's thread keys once (tag FrameBridge). Host-tested (rename); not yet run 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).
xrruntime/ arm64-v8a/libfp_xrruntime.so Runtime negotiation endpoint for embedded Khronos loaders (frame.static_openxr_loader). Loads FrameBridge by soname, forwards Android loader initialization through its GIPA, and returns FrameBridge GIPA. The unmodified inner Khronos loader is reused from OVRPort’s generic loader (also when overport: False); its paths remain system paths. Built with --only xrruntime.
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.

adapter.fixed_resolution (off by default, #406/#394) keeps META recommended layer resolution at tracked full swapchain size. Both adapter GIPA and xrshim's existing extension entry point serve it; the setting adds OVRPlugin shim routing. The extension stays visible, avoiding dependence on a plugin fallback. No adapter revision bump for this opt-in fix. See DEVICE_NOTES "Fixed layer resolution / Batman" for verification.