mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-17 13:01:29 +02:00
Medal of Honor (com.Virtuos.R6DLM) keeps Epic's DownloaderActivity and log text but renames the delivery predicates and OBBData, so expansionFilesDelivered wasn't found and frame.unreal_skip_obb_delivery was never offered. Detection now also matches a boolean method in the package's DownloaderActivity, called from onCreate, that carries Epic's "Checking for file : " log and calls the expansion-downloader helpers; the patch rewrites those too. ANALYSIS_VERSION 18 -> 19. Heuristics eval unchanged (10/66); no existing recipe affected.
99 KiB
99 KiB
Porting playbook
Everything here was hit while porting Quest and Rift games to the Steam Frame (September–October 2026; 38 catalog recipes). The machine-readable
version is catalog/triage.yaml (used by frameport test / the Job screen); keep both in sync.
Fast path for a new game
frameport scan <folder>→ check the suggested recipe (frameport show <pkg>). Direct-VrApi engines get the bridge.frameport build <pkg>→ all static checks must pass (32-bit-only →frame.a32_bridge, experimental).frameport install <pkg>→frameport test <pkg>: want RUNNING + "Submitting frames" at ~72 fps.- Put the headset on and look. Headless tests can't judge visuals (no FOCUSED state without the headset worn).
- Fix by symptom below, rebuild, repeat. When it's good: Save as known-good (GUI) or add a catalog YAML.
Startup failures (visible in launch.log)
| Symptom | Cause | Fix |
|---|---|---|
Startup exits through libxlua.so (lj_err_throw), then Unity aborts in mutex cleanup; captured Lua error is bad light userdata pointer (GitHub #257, BattleTalent 4.7) |
This older LuaJIT encodes lightuserdata in 47 bits; Frame supplies valid wider native pointers. Controlled tests with the unchanged library pass a real low-address allocation and fail its own high-address static pointer | frame.luajit_pointers automatically selects a full-pointer Lua engine only for the verified library SHA-256, retaining xLua bindings and custom helpers. The scoped patch reaches OpenXR startup for 60 s headless; rendering and gameplay remain unverified. A lj_err_throw cleanup stack without the original error message is insufficient to infer this cause |
| A GL4ES Team Beef game draws stretched triangles or spikes across characters and the scene (GitHub #191, Lambda1VR) | The newer GL shim exports GLES draw functions that intercept GL4ES's desktop OpenGL calls, bypassing its client-array and immediate-mode translation | frame.tbxr_vendor keeps the shim's five draw symbols private when the APK bundles libgl4es.so. GL queries and single-sampled framebuffer fixes remain active; other games retain the draw hooks. A/B eye captures on SteamOS 0.4.5: old shim normal, current shim spikes, current shim with private draw symbols normal at ~72 fps. Worn-headset gameplay still needs confirmation |
A Team Beef game reports a crash after manually quitting: fdm_injection_xrDestroySwapchain → TBXR_LeaveVR (GitHub #191, Lambda1VR) |
Valve's foveation layer dereferences a null pointer during swapchain cleanup | Triage tbxr-fdm-shutdown-crash identifies this as a teardown crash, separate from startup and distorted geometry. No verified cleanup fix yet |
Conversion fails with NoSuchElementException: Sequence is empty in PatcherContext.appName (GitHub #269, Accounting+) |
OVRPort assumes an application label exists; some APKs only label their activity | FramePort retries that exact failure once using a temporary APK whose application inherits the activity's label; existing labels and the original APK are kept |
Updating a flat Android app after saving its recipe fails with no OVRPort patches selected (GitHub #271, Plezy) |
older FramePort loads the saved recipe without restoring its unchanged-install setting or applying the flat-app build rules, then tries an empty VR conversion | update FramePort: saved recipes keep as_is, and flat apps still skip OVRPort; existing catalog-derived recipes refresh at the next app update. Until then, enable "Install as is" for an app whose APK needs no fixes |
A launch test suggests a patch (for example triage sdl-no-clipboard → frame.sdl_clipboard) but the game page doesn't show it / lists it as not applicable; or a game added long ago lacks warnings newer ones get (Android version, web wrapper, missing OBB) (GitHub #104, Dramatic Shape) |
the library entry was analyzed by an older FramePort: the analysis field the patch reads (sdl_java, min_sdk, web_wrapper, expects_obb, …) is missing, so applies() is false |
entries whose analysis.extra.analysis_version is below detect.ANALYSIS_VERSION are analyzed again at the GUI's start (background) and before a build (pipeline.refresh_analyses); by hand: game menu → "Analyze again", then Update on Frame. An unreadable APK is marked analysis_failed (retried after the next version bump). Adding an analysis field: bump ANALYSIS_VERSION |
FDM layer loads but diagnostic logging reports fdm_injection_CreateInstance: skipped |
Layer loading alone does not enable injection on the tested SteamOS 0.4.5 build; a skipped helper instance alone also does not establish game-wide inactivity | Opt-in device.foveation mode tracked: loads Valve's layer and sets FDM_DEBUG=enable. Batman probe reached foveated render passes with changing per-eye offsets. Validate graphics and matched gameplay GPU time; do not enable globally. Use debug only for brief diagnostics, preserving existing flags; never benchmark with debug spam. See FRAME_RUNTIME.md. |
| One eye (usually the right) shimmers or jitters in the 3D view while menus and panels look steady; pacing is clean (GitHub #69, e.g. RTCWQuest, Moss) | Valve's eye-tracked foveation layer (VALVE_fdm_injection) moves its low-density regions with each eye's gaze, and the right eye's gaze filter restarts often (CEyePoseUKF R: Large dt … Bootstrapped in ~/.local/share/Steam/logs/eyetracking.txt) |
Customize → "Eye-tracked foveation (Valve)" (device.foveation): Fixed (FDM_DEBUG=disable_offsets; verify injection remains active on the installed runtime) or Off (VK_INSTANCE_LAYERS=""); catalog field foveation: |
A Unity OpenXR game closes right after the OpenXR instance is created: F VrApiLoader: vrapi_SetPropertyInt was called before vrapi_Initialize()!, SIGABRT on UnityMain (GitHub #62, Jurassic World Aftermath) |
the APK still ships Meta's VrApi loader; OVRPlugin (1.89, on OpenXR) calls it without starting VrApi and Meta's loader aborts. The VrApi bridge can't replace it (39 of the loader's 114 functions: OVRPlugin wouldn't link) | frame.vrapi_stub (same exports, each returns 0); triage vrapi-before-init |
A VrApi game exits at once: UnsatisfiedLinkError … dlopen failed: cannot locate symbol "vrapi_PollEvent" (GitHub #57, BlazeRush) |
the engine imports a VrApi function the VrApi bridge didn't implement | the bridge has vrapi_PollEvent, RecenterPose, SetDisplayRefreshRate, GetSystemPropertyFloatArray (rebuild the game); the static check "VrApi functions resolvable" names any other missing one; triage vrapi-symbol-missing |
A Team Beef port (Lambda1VR, RTCWQuest, …) exits at once: UnsatisfiedLinkError: dlopen failed: library "libopenxr_loader_valve.so" not found (GitHub #59) |
TBXR loads openxr_loader_<Build.MANUFACTURER> (Lepton: valve) and takes the Meta path only when strstr(OPENXR_HMD, "meta") matches, else Pico's |
frame.tbxr_vendor (empty libopenxr_loader_valve.so + the "meta" literal → "alve"). Game data (xash/, …) goes to /sdcard: upload it with the Files tab to the game's storage. Black eyes with TBXR: Incomplete frame buffer object: GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: TBXR always uses multisampled render-to-texture (even --msaa 1); frame.tbxr_vendor links the GL shim, which hands it single-sampled versions (log GL shim: multisampled render-to-texture … drawn single-sampled) |
Black screen after VR starts; Asset status error (code 100) in DLC_Manager:AssetStatusCallback (GitHub #203, e.g. PixelRipped 1995) |
Meta asset-file status fails after entitlement succeeds; the log does not identify the native call or asset ID | Try frame.asset_files_stub (opt-in, triage-only): native/langpack answers GetList with an empty successful list and StatusById with installed status, an empty path and the requested ID. No DLC files are supplied; download requests retain their existing behavior. Disable with FRAMEPORT_ASSET_FILES_STUB=0. Host-tested only; whether this fixes startup needs a game/device test |
Missing sound or extra content in a game whose OBB folder has separate *.pak files (GitHub #61, Star Wars: Tales from the Galaxy's Edge: seasons + Wwise banks) |
the game asks Meta's platform for these asset files (ovr_AssetFile_GetList / DownloadById); OVRPort's loader doesn't list them |
copy every content/sound-bank PAK and use frame.asset_files (native/langpack lists the data's *.pak as installed default assets, IAP free, and answers DownloadById at once with the path; off: env FRAMEPORT_ASSET_FILES=0). Tales also needs the splash and depth-size fixes below |
A SideQuest backup has apk/ and a sibling obb/ with content/sound-bank PAKs, but the Frame only has the main OBB (GitHub #61, Tales from the Galaxy's Edge) |
older scans treated that layout as loose expansion files and selected only the named OBBs; the asset-file patch then found no PAKs | the scanner chooses the OBB folder by version first, then keeps the whole sibling data folder when all its expansion files are selected for this APK; app updates repair affected library entries and refresh asset/language-pack detection. Reinstall to copy the missing files, or scan the backup again first. Folders with other packages or unselected OBB versions still send only the selected OBBs |
Tales from the Galaxy's Edge stops during map loading (GitHub #61): game thread in FlushRenderingCommands / UEngine::LoadMap, render thread in FSplash::RenderFrame_RenderThread / ovrp_WaitToBeginFrame / SteamVR WaitFrame |
the separate Oculus loading splash waits for an unbegun frame while map travel waits for rendering | frame.unreal_splash_bypass: returns from the splash renderer, with a checked symbol, size and prologue. The last image stays visible during loading; the normal renderer and menu layers continue. Headset test with the depth-size fix: gameplay, both eyes, controller input and sound work |
| Upper part of the intro/environment is black or clipped, but a separate menu panel is intact (GitHub #61, Tales from the Galaxy's Edge) | FSceneRenderTargets::AllocMobileMultiViewDepth caches the initial Android window dimensions (1920x1016/1020), leaving the depth attachment shorter than the eye color texture (1728x1728); drawing is limited by the smaller attachment |
frame.unreal_multiview_depth: refreshes both cached depth dimensions from the current scene BufferSize before allocation. Checks the exported symbol, function size and surrounding instructions, then replaces only the branch that skips refresh. Keeps multiview and multisampling. Headset test: full view and gameplay work |
cannot locate symbol "ovrPeerConnectionState_ToString" (or another ovr<Enum>_ToString) at start (BlazeRush, GitHub #57) |
OVRPort's platform loader lacks several Platform SDK enum helpers | frame.ovrstubs now stubs them too; *_ToString stubs return "" (not NULL) |
Exits right after the OpenXR instance is created: Failed to create XR session: -50 (XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING, Lambda1VR) |
the game skips xrGet*GraphicsRequirementsKHR; Meta's runtime tolerates it, SteamVR's doesn't. Lambda1VR's TBXR doesn't even enable XR_KHR_opengl_es_enable (only XR_EXT_local_floor) |
FrameBridge adds XR_KHR_opengl_es_enable when an app enables no graphics API extension, asks for the requirements on the game's behalf and retries xrCreateSession once (logs added XR_KHR_opengl_es_enable …, the app skipped …; asked for it; every launch logs the final extension list xrCreateInstance with N extension(s)); triage graphics-requirements-missing. Verified headless: Lambda1VR runs at 72 fps |
Exits at start: OVRAvatar-Loader: DisplayErrorAndExit: Failed to launch SystemActivities after ovrAvatar_Initialize: Failed to load AvatarSDK driver (BlazeRush, GitHub #57) |
Meta's avatar loader needs Horizon; it then tries Meta's error screen and aborts | frame.avatar_stub (same exports, do nothing); triage avatar-driver-missing. Games that ship the library but don't start avatars at launch (Lucky's Tale, BattleSisters, Arcsmith) don't need it |
| BlazeRush (GitHub #57, Klownicle's guide): the first menu takes A, the next ignores all input; car bodies and controllers invisible; coloured streaks/flashes from particles; narrow square view; blurry, or displaced with black borders after a scale change | the menus wait for a Meta avatar the stub never delivers; bone indices fed with glVertexAttribPointer (Mesa converts them to floats); mediump particle maths; FOV read once before any session (bridge said 90x90); old program binaries in its shader cache; user_config.xml keeps the old resolution | catalog recipe: frame.blazerush (avatar wait NOP + avatar renderer RET + cache folder cache/br_shader_v1, exact build only), GL shim gl_int_attribs=1 + gl_highp_markers=in uvec2 inBoneIndices;|uniform InstanceData|void main() (gl_hide_multiview=0, gl_hide_msrtt=0 keep its GL as tested), the VrApi bridge's saved FOV (framebridge-vrapi-fov.txt; triage vrapi-fov-before-tracking), scale=1.7, device.config_sync (agent writes r_3dwidth/r_3dheight/r_width/r_height from the runtime's eye size × scale before each start); triage gl-integer-attribs |
| Frame freezes or the game is killed (OOM) on the first controller vibration (for example Lucky's Tale's save slots, BattleSisters, Sniper Elite VR's tutorial grab: memory grows ~1 GB/s until the kill) | OVRPort runtimes up to 3.4.3-23204ea read the haptic envelope's duration (ns) as seconds and allocate gigabytes of samples (GitHub #9, ovrport/app#73); also reached from Unity's legacy ovrp_SetControllerHaptics; perf page faults all in libopenxr_loader.so xrApplyHapticFeedback |
adapter.haptic_fix (envelope → one plain vibration in native/xrshim; PCM vibrations (XrHapticPcmVibrationFB) are converted the same way). Fixed in runtime 3.4.3-aa54c3f: builds made with it leave the workaround out by themselves (log "not needed: adapter.haptic_fix", frameport show "last build left out"); rebuild an older build to switch |
| Controller vibrations much stronger than on a Quest (The Boys VR, Jurassic World, BONELAB, …) | OVRPlugin games send amplitude envelopes; haptic_fix turned each into one vibration at the envelope's peak for its whole duration (a short fading pulse became a long full-strength buzz) |
haptic_fix now uses the envelope's RMS; Game settings → "Vibration strength" (haptic_scale, 0-1) scales every vibration in FrameBridge; launch.log shows the first requests (haptic: …). OVRPlugin stops a vibration with amplitude 0 (2 s duration): FrameBridge turns that into xrStopHapticFeedback (without it the last buzz ran its full 2 s) |
Play or a launch test fails at once: Game files missing at <path> (storage not mounted?) in launch.log, or "… files are on a drive that isn't inserted" (GitHub #90) |
the game was installed or moved to a microSD card (<mount>/FramePort/<pkg>) that isn't inserted or mounted now |
insert the card; or move the game back (game menu → Move to…, frameport frame move <pkg> --to internal) once the card is back. frameport frame drives shows what the agent sees (exFAT/NTFS cards are refused: format in SteamOS) |
| A Linux app started from Desktop Mode's menu closes after ~2 s, or opens on the wrong display (GitHub #84) | its launcher predates agent v63: the "Steam parent gone" watchdog ended it when Plasma's launcher exited, and it took gamescope's DISPLAY from Steam | connect FramePort once (agent v63's upgrade_launchers adds the FRAMEPORT_DESKTOP checks; the menu entry runs env FRAMEPORT_DESKTOP=1 <anchor>/launch.sh). Game Mode controller problems in such apps are Steam Input's (a separate issue) |
| A Linux app (AppImage) shows no/FramePort's placeholder icon in Desktop Mode or Steam (GitHub #99) | before agent v64 only FramePort's art set's icon was used, and a lone AppImage has no store art | connect FramePort once (agent v64 copies the app's .DirIcon / .desktop Icon= to <anchor>/artwork/app-icon.* and refreshes the menu entry); reinstall to also get it into the library + Steam shortcut. Still the placeholder: check <base>/app/squashfs-root/.DirIcon and the .desktop file's Icon= (an icon outside the app folder or only an XPM isn't used); a chosen icon (artwork/.icon-source = custom) always wins |
| An overlay app (fpsVR, a wrist watch) installed on the Frame never appears, or SteamVR doesn't start it | not registered with the Frame's SteamVR, or registered with a manifest SteamVR skips: on the Frame vrserver reads only binary_path_linux_arm (vrserver.txt: "must specify binary_path for launch_type binary. Skipping") |
pcvr.vr_overlay (Windows) / a Linux app with a bundled .vrmanifest (is_dashboard_overlay): agent v75 register_vr_overlay (check vrcmd --overlays, ~/.config/openvr/config/appconfig.json manifest_paths); the app's own --install writes binary_path_linux only. fpsVR under Proton never loads OpenVR (quits after 60 s): unsupported for now |
| An overlay app with Start with SteamVR on doesn't start by itself after the Frame or SteamVR starts (it works when started by hand) | SteamVR's own auto-launch flag (SetApplicationAutoLaunch) didn't persist on the Frame (no autolaunch entry in ~/.config/openvr/config/steamvr.vrsettings) |
agent v76: FramePort's service frameport-vr-overlays.service starts it at every new vrserver. Check systemctl --user status frameport-vr-overlays, ~/.local/share/frameport/vr-overlays.log (new SteamVR (vrserver N) → <pkg>: started / running / couldn't start it: …), the deployment's overlay.autostart, and that ~/.local/share/frameport/vr-overlays.disabled doesn't exist; frameport frame info (ensure_host_fixes) installs a missing service |
Every game suddenly fails to start: crun: create keyring …: Disk quota exceeded, is not a running context |
rootless podman leaked a kernel keyring per launch; 200-key quota exhausted | keyring = false in ~/.config/containers/containers.conf (FramePort agent does it), then reboot the Frame once |
Launch test "failed" although the game runs in the headset: frames paced, then OVRPortVrApi: EnterVrMode: no tracked head pose before deadline, then a crash (e.g. zink: DEVICE LOST + SIGSEGV in a gdrv thread) (GitHub #57, BlazeRush) |
the VrApi bridge ends the VR session after 30 s without head tracking, which only happens without a worn headset; the game's renderer then crashes while tearing down | none needed: launch tests (triage(..., headless=True)) treat an exit or crash after a headless_end line (catalog/triage.yaml) as a pass when frames were paced before it (info finding headless-end); play sessions ignore it. Add new such markers to headless_end |
Launch test EXITED, launch.log has Target app (<pkg>) did not start and no Start proc …:<pkg> (GitHub #179) |
Lepton occasionally doesn't start the app on the first start after an APK change; Android's WebViewLoader-: System.exit at boot is not the game (triage app-not-started) |
start it again |
A game keeps no progress between starts although it saves (its storage gets a new Profiles/Oculus/<id>/-style folder per start; Walkabout Mini Golf, GitHub #182) |
OVRPort's platform loader answers ovr_GetLoggedInUserID with a made-up id (6e18 + random()), new at every start; games file saves under the user id |
frame.ovr_stable_user (default for games with the platform library): a fixed id per game and keystore. Old progress: copy the newest old profile into the new id's folder |
| A Unity app's text field shows a caret for a moment and loses focus; no keyboard appears (for example Stremio VR login) | Unity's TMP_InputField/InputField wait for Android's on-screen keyboard and close themselves without one (Lepton has none); headless VR apps also have no focused Android window, so no key press reaches them | frame.unity_text_input (Cpp2IL finds TouchScreenKeyboardShouldBeUsed/isKeyboardUsingEvents, rewritten to false/true) + device.text_input_window (lepton-show-flatscreen: Steam's keyboard and Type on Frame work) |
| FramePort's games disappear after Steam restarts, especially after a devkit registration (GitHub #263) | Steam saves Shortcuts / AppName with capital letters; older FramePort reads them case-sensitively and writes a second shortcut map that Steam ignores |
agent v86 reads and updates keys case-insensitively, keeping Steam's spelling, appids and tags; update FramePort and add the missing games to the library again |
Play gives Steam's "Game configuration unavailable"; Steam's console_log: GameAction [AppID <id>] … RequestingLicense → UpdatingAppInfo → LaunchApp failed with AppError_9 |
this Frame's Steam never loaded FramePort's shortcuts.vdf entry (it treats the id as a store app); see the casing fix above, other causes remain unconfirmed (GitHub #21/#30) | automatic since agent v43: Play registers the game through Steam's devkit interface ("Devkit Game: …", devkit_register) and starts that; uninstall removes it. Agent v58: a devkit entry Steam forgot after a restart is added again live on Play (no NOT_IN_LIBRARY → no Steam restart), and installs/art updates of devkit games only copy art (no Steam restart, GitHub #41) |
Steam's Exit game leaves the game running (container lepton-steamlaunch-<appid> still up) |
Steam stopped only its reaper; launch.sh and Lepton (setsid) never got a signal (GitHub #36) |
agent v44: launch.sh's 2 s loop ends the game when its parent is gone (upgrade_launchers adds it to existing launchers); by hand: podman kill lepton-steamlaunch-<appid> |
Black screen at start while audio/ExoPlayer runs; log xrEndFrame: dropped N unusable layer(s), a quad layer with swapchain 0x0 (for example I Am Monkey's intro video) |
The game plays video into an Android-surface swapchain (XR_KHR_android_surface_swapchain); the Frame's runtime lists the extension but returns FUNCTION_UNSUPPORTED | adapter surface_emul (default on): FrameBridge gives the player a SurfaceTexture-backed Surface and copies each frame into an ordinary swapchain (log surface_emul: …) |
The game quits right after start: Something failed to initialize. Quitting! / We don't have write permission to …/files/cloud/data (for example SUPERHOT); or stays at loading with UnauthorizedAccessException in StartupLoading.Start (GitHub #239, Jurassic World Aftermath) |
The game created a cloud-save folder with mode 1700; inside Lepton the app writes through the folder's group. Its startup routine can fail before the permission watchdog repairs the newly created folder | launch.sh fix_perms adds owner + group write to every folder in the game's storage (agent 41). Close and start the game again without uninstalling: the folders are now repaired (Aftermath confirmed in the headset). Agent 84 creates files/cloud/data before fix_perms and startup, including in upgraded launchers, to prevent the first-start race. Triage startup-save-permission fails the launch test despite rendering frames |
The whole Frame slows to a halt, then the game is killed (Out of memory: Killed process … anon-rss:10+ GB), for example Lucky's Tale |
The game's own memory grows ~1 GB/s on Unity's Loading.PreloadManager thread (perf page-fault profile); not the flat window, not the text-input patches |
none yet: mark unsupported; profile with perf record -p <pid> -e page-faults --call-graph fp (works as steamos, perf_event_paranoid 2) |
A Linux app exits at once: cannot execute binary file: Exec format error in launch.log |
an x86_64 program started directly (installed before FramePort ran x86 builds through FEX, or by hand) | reinstall it: x86_64 Linux apps install FEX on the Frame and start through it (agent v61, kind: linux_x86; FEX also needs STEAM_COMPAT_DATA_PATH: "No compat data path?" in launch.log = an agent v60 launcher, reinstall); triage linux-x86-no-fex. Prefer an arm64 build when the app offers one |
APP_ACTIVITY is empty, nothing starts |
Manifest has category INFO only; Lepton needs LAUNCHER | frame.launcher (automatic) |
PC VR game on the Frame shows as a flat window / Revive: Unable to load LibOVRRT DLL / LoaderInstance::CreateInstance chained CreateInstance call failed |
Frame SteamVR runtime rejects OpenXR apiVersion 1.1 (XR_ERROR_API_VERSION_UNSUPPORTED), which Proton 11's VR helper requests |
pcvr.xr_timefix (Frame OpenXR layer, default on): retries xrCreateInstance as 1.0 |
Unreal PC VR game on the Frame runs as a flat window although OpenXR works (no LogHMD OVRPlugin lines; Unreal logs nothing when it skips the Oculus plugin) / launch.log: FramePort oculushmd: could not create the OculusHMDConnected event |
UE's OculusHMD (and LibOVR's ovr_Detect) only start when the Windows event OculusHMDConnected exists and is signaled; on a PC the Oculus service creates it. Revive hooks OpenEventW for it, but that relies on Detours patching Wine's (ARM64EC) kernelbase |
pcvr.oculus_unreal (default for Unreal Rift games; PC VR counterpart of overport's patch_oculus_unreal): launch.sh runs the injector through fp_oculushmd.exe, which provides the real event until the game exits |
| PC VR game runs as a flat 2D window (no VR) | SteamVR was not running, so Revive/LibOVR had no runtime to bind to | FramePort now auto-starts SteamVR on Play; make sure SteamVR comes up before the game |
| PC VR game crashed launching via Steam on the OpenXR backend | Revive's newer OpenXR backend was less reliable | PC now defaults to Revive's OpenVR (SteamVR) backend; reinstall to apply |
Oculus PC VR game: Unable to load LibOVRRT DLL / -3001 on the Frame |
The game can't find a VR runtime (Revive's LoadLibrary hook doesn't work under Proton-arm64) | pcvr.libovr_redirect (default on) symlinks Revive's runtime as LibOVRRT{64,32}_1.dll in the exe dir — pure substitution; a build that checks the runtime signature then hits -3021 (not bypassed) |
| Oculus Rift game won't run on the Frame | Oculus/LibOVR game: needs Revive, whose hooks don't work under Proton-arm64, and FramePort won't defeat the Oculus runtime signature check | Play it on this PC (SteamVR + Revive). Only OpenVR/OpenXR-native Rift games run on the Frame |
| Un-cracked Oculus PC VR game: "Initializing OVR session" then exits / signature check on the runtime | Revive's hooks don't install on Proton-arm64; the Oculus shim rejects the unsigned Revive runtime | Not runnable on the Frame without the repack's crack extracted (not done by FramePort); use the PC version |
PC VR game: Failed to initialize Oculus API (-3001) / Unable to load LibOVRRT DLL |
Revive's LoadLibrary hook doesn't work under Proton arm64 (ARM64EC kernelbase); the game's LibOVR shim finds no runtime DLL | Symlink LibOVRRT64_1.dll → Revive's DLL in the prefix's system32 (manual so far) → then -3021 (runtime signature check; open) |
| Uploads to the Frame are slow | The link, storage or transfer backend can limit throughput | Check the job's "Transfer link"; try the Frame's hotspot or USB cable. Large install files prefer system OpenSSH SFTP, retaining paired-host checks and .part resume; unavailable or failed native uploads fall back to Paramiko. The log names the backend. Settings → Installing → Parallel uploads accepts 1–8 (default 3; saved preferences are preserved). More is not always faster. Measure the production path with scripts/upload_bench.py --methods app-files-3 --check-resume (three repetitions, checksum verification) |
PC VR game exits right away: Unhandled Exception: 0xc06d007e (after FOnlineSubsystemOculus::InitWithWindowsPlatform) |
Delay-loaded LibOVRPlatform64_1.dll (Oculus Platform SDK, installed with the Oculus app) is missing |
None on the Frame (entitlement check; FramePort doesn't replace it). PC mode with the Oculus app |
Unity PC VR game (Oculus + SteamVR build: OVRPlugin.dll and openvr_api.dll in <Name>_Data/Plugins) opens a window and quits after ~20 s; no VR Vulkan instance after the game's DXVK device in launch.log; Unity log (unity log … Player.log, agent v67): OpenVR failed initialization / Initialization of device … failed |
Unity's built-in VR tries its SDKs in list order (Oculus first) unless -vrmode picks one; a catalog recipe verified with another build of the game (for example SUPERHOT VR's OpenXR build, GitHub #105) used to drop the build's own arguments |
pcvr.launch_args = -vrmode OpenVR (detected for such builds; kept when the catalog's xr differs from the build's). A game's Electron launcher next to it (SHVR.exe) is ranked below the game |
| Unreal PC VR game opens the crash reporter instead of closing | UE starts CrashReportClient.exe on a crash | pcvr.no_crash_reporter (default for Unreal Rift games): -nocrashreports + CrashReportClient.exe renamed .disabled in the Frame copy |
A backup's game was added without its data (library data_dir null, data_bytes 0) although the folder has .obb files |
The OBBs sit in a layout the scan didn't know: <game>/obb/<package>/, Android/obb/<package>/, or SideQuest-style <game>/apk/x.apk + <game>/obb/<package>/ |
sources/quest_dump.find_data_dir finds a <package> folder with .obb files ≤3 levels below the APK's folder or ≤2 below its parent (skipping neighboring folders with APKs: other games); an apk/apks folder counts as part of its game folder. Add the folder again (Analyze again doesn't look for data) |
"Failed to correctly load the game files" at the menu; Error loading FMOD bank "…/Android/obb/<package>/Master.bank": ERROR (GitHub #239, Jurassic World Aftermath Collection); launch test previously passed at 72 fps |
SideQuest's <package>/<backup time>/apk/x.apk + obb/Master.bank + obb/Master.strings.bank backup was scanned without data: these are raw sound banks, not .obb expansion files, so the sibling-folder search missed them |
quest_dump.find_data_dir checks the parent game folder's obb/ (or obbs/) when the APK is in apk/ or apks/, including raw assets with or without a package subfolder. Add the backup folder again and reinstall; triage fmod-bank-missing fails the launch test even when the error screen renders frames. Copy the whole data folder from the same backup as the APK |
A game from an AXRB download starts and stops at once (Steam shows Resume), no OBB on the Frame; or a SideQuest backup adds a second game named after an APK in its obb/ folder (reddit 2026-10-11) |
AXRB keeps its own PC builds in AXRB/patched/<package>/*-axrb.apk (OVRPort-converted for its runtime, no OBBs); the scan added them over the original AXRB/<app id>/<binary id>/base.apk. SideQuest's VR4 backup has Unreal's VR4-Android-Shipping-arm64.apk next to the OBBs |
quest_dump._axrb_patched skips AXRB's patched copies; a rescan replaces entries made from them; AXRB downloads send every non-APK file next to base.apk (DLC, assets) and are named after the package; APKs inside another game's data folder are ignored; SideQuest backups are named after the package folder, not the backup time. docs/FAQ.md has both layouts |
Game hangs at start / never loads although its .obb files are right there: next to the APK, in an obb folder beside an apk folder inside a <package> folder, or a SideQuest backup (<timestamp>_<versionCode>.apk) (GitHub #85, #91) |
No folder named after the package, so the folder-layout checks found nothing and no OBB was uploaded | quest_dump.find_data then looks for the files by name, (main|patch).<versionCode>.<package>.obb (any case), in the APK's folder, its parent and up to 3 levels below each (folders with their own APKs are other games); prefers the folder and files of the APK's own versionCode, else the newest older, else any version. When that folder also holds other things (the APK itself, other games' OBBs), the library entry gets data_files and only those files are uploaded, counted and deleted with the game. Add the folder again |
CryEngine game: Powered by CRYENGINE, then SIGTRAP in libAndroidLauncher.so during CreateSystemInterface, before any OpenXR instance (GitHub #238, The Climb 666.0) |
Installed without its OBB: the SideQuest backup has apk/com.crytek.climb.apk + obb/main.com.crytek.climb.obb, but the scan required a versionCode in the OBB name and skipped it. The reported backtrace's fatal-error call formats Engine initialization failed: Open Engine Paks: Unable to find "engine" pak files (%s) |
quest_dump.find_data accepts (main|patch).<package>.obb as well as numbered names, keeping the original filename. Update on Frame finds the previously skipped data; a rescan finds it too. Analysis version 12 recognizes monolithic CryEngine through assets/game.cryproject, Vulkan through assets/system.cfg (r_Driver = vk), and an expected OBB through the launcher's expansion-path format strings; a missing OBB now gets the existing missing-obb warning/finding. This fixes the original missing-content startup failure; the rendering failures below were found once the data was installed |
The Climb 666.0: audio with no headset picture, OVRPortVrApi: Unsupported frame flags or swap interval; then SIGSEGV in vulkan.freedreno.so during vkCmdEndRenderPass once frames are accepted (GitHub #238) |
The game submits the obsolete Quest texture-density debug flag (0x100), which the bridge rejected. It also binds the bridge's full-rate texture as a fragment-density attachment even though foveation support is reported off; Turnip reads density maps on the CPU, but an ordinary sampled image has no mapped backing memory |
Updated frame.vrapi_bridge accepts the compositor debug/volume flags and creates the compatibility texture with VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT. Do not gate that usage on the advertised format features (the Frame reports R8G8_UNORM without the density bit). device.files selects r_vr_foveated = 0 when the engine contains that cvar; The Climb 2 keeps r_variable_rate_shading = 0. Triage vrapi-frame-rejected marks rejected frames as fatal despite pacing logs. Picture confirmed on the headset. With the corrected config, an uncapped launch stayed running for over four minutes at a median 72 fps with no native crash or GPU fault. The user confirmed working menus and gameplay on 2026-10-12 |
Unreal game: JNI_OnLoad (OVRPlugin), then silence: no OpenXR instance, no crash, no frames (GitHub #85 TRIANGLE STRATEGY; launch-test finding missing-obb) |
Installed without its OBB: the manifest says com.epicgames.ue4.GameActivity.bHasOBBFiles = true (UE5: com.epicgames.unreal.…) but the library has no data folder (data_dir null, data_bytes 0) |
Put the .obb files in a <package> (or obb/) folder next to the APK, add the folder again, reinstall. Analysis extra.expects_obb; the game page, the install question and frameport scan/install warn; launch tests that sent no frames get the missing-obb finding from the library (the game logs nothing). Unity split-binary builds count too (extra.unity_split, analysis/unity_split.py): an XR-plugin build (Oculus XR Plugin / Unity OpenXR library) without assets/bin/Data/UnitySubsystems/ in the APK, or BuildSettings listing more scenes than the APK has levelN files (globalgamemanagers loose or read from the head of data.unity3d); exact on the 48 Unity APKs of the dumps (13 split, 17 full games; OBBs of full builds are asset bundles or sound banks) |
Unreal game: launch.log ends with ActivityTaskManager: START u0 {… cmp=<pkg>/.DownloaderActivity} and Displayed <pkg>/.DownloaderActivity, then nothing: no OpenXR instance, no crash (GitHub #159 Contractors; triage unreal-obb-check-stuck, also when the OBB is on the Frame) |
Epic's DownloaderActivity (started by GameActivity.onResume before the engine's main init) checks every OBB's name + size compiled into the APK (OBBData); with manifest meta-data com.epicgames.ue4.GameActivity.bVerifyOBBOnStartUp=true it then reads the whole OBB and compares each entry's CRC32 (cached in files/cacheFile.txt by the OBB's mtime). Its progress and error screens are invisible in Lepton (headless VR); a CRC mismatch waits for a button forever. A missing / wrong-size OBB starts Google Play's downloader (or a "No Google Play Store Key" dialog) and also waits |
frame.unreal_skip_obb_check (suggested by analysis unreal_verify_obb): sets that meta-data to false (one boolean in the manifest, axml.set_meta_data_bool), which skips only the CRC pass. Still stuck: the OBB doesn't match the APK version (name main.<versionCode>.<pkg>.obb and its size) - copy APK + OBB from the same install |
| Unreal game has CRC verification off but the delivery bypass is unavailable (GitHub #159, Medal of Honor: Above and Beyond) | Obfuscation renamed expansionFilesDelivered: MoH calls boolean predicates a() and d() from its package DownloaderActivity.onCreate, both logging Checking for file : and calling Google expansion filename/delivery helpers. The second gate can show “Select OBB to use” |
Analysis recognizes these predicates by activity, caller, log and helper return types; stuck-downloader triage offers frame.unreal_skip_obb_delivery as an experimental opt-in. CRC stays off. The inspected build checks three Java OBB entries and has native indexed-patch filename support; its backup has 22 OBBs and 34 MP4s, all selected from the package folder. Bypassing Java delivery does not establish native data completeness or headset success |
Unreal game still stays on DownloaderActivity with CRC checking off (bVerifyOBBOnStartUp=false) and a fully uploaded matching OBB (GitHub #283, Into The Radius) |
The separate Java expansionFilesDelivered() name and size gate can reject an OBB through Android external storage in Lepton; disabling CRC does not bypass it |
Try frame.unreal_skip_obb_delivery (opt-in, experimental, triage-only). It returns true from known Unreal/package delivery predicates in every dex, preserving normal downloader success and main init. Analysis unreal_obb_delivery (version 16) controls applicability. The native engine still needs complete, matching, readable OBB data; unverified on a device |
INSTALL_FAILED_NO_MATCHING_ABIS |
32-bit-only APK (Gear VR / Go / early Quest); Frame has no AArch32 | frame.a32_bridge (suggested for every 32-bit-only APK, experimental): the a32 translator runs the game's 32-bit libraries inside the 64-bit app (native/a32translator/bridge) |
a32 bridge faults at a low Thumb PC while loading a library, with the same unrelocated pointer in .init_array (e.g. Journey of the Gods: 0x1259e9 in OVRPlugin) |
The guest loader ignored the older DT_ANDROID_RELR / DT_ANDROID_RELRSZ tags, leaving relative pointers at their link addresses |
Fixed in the guest loader: both Android and standard RELR tags use the existing RELR decoder. Rebuild the a32 runtime and APK; host regression tests cover both tag families, but game startup still needs a Frame run |
32-bit game on the bridge: framerate falls over minutes, purple/garbled geometry, then Could not allocate memory: System out of memory! (Spunky Dungeon) |
The bridge's guest copies of mapped GL buffers were tracked per buffer target: a second buffer mapped on the same target lost the first one's copy (never written back: garbage vertices; never freed: ~10 MB/s until the guest's 4 GiB were full) | Fixed in the bridge (gl.c: mappings per buffer object). Check: the guest's [anon:scudo:*] maps below 4 GiB in /proc//maps must stay flat |
| 32-bit game on the bridge loads, leaves VR and its engine threads end, no crash, no finish() (Gunjack) | The game checks its own APK signature (UE4 AndroidThunkJava_GetSignature = Base64 of the signing certificate's SHA-1, compared in AndroidMain): every FramePort build is re-signed |
frame.a32_bridge stores the original APK's value in assets/fp_a32_signature.txt (Unreal games) and the bridge answers the guest's call with it (log AndroidThunkJava_GetSignature: original signature returned). Find other such checks with A32_BREAK on the game's exit path (vrapi_LeaveVrMode logs its caller) |
| 32-bit NativeActivity game on the bridge ignores every controller (Gunjack, UE 4.10 Gear VR: no vrapi input functions, it reads the Gear VR touchpad/back button as Android input events from its AInputQueue) | The bridge sent its controller events through Activity.dispatchKeyEvent, which a NativeActivity never passes to native code |
input.c puts synthetic touchpad (motion, touchscreen source) and back-key events in front of the game's AInputQueue: ALooper_pollAll/pollOnce return the queue's ident while one waits, AInputQueue_getEvent hands it out, the AInputEvent/AKeyEvent/AMotionEvent imports answer for it. Right trigger = hold the touchpad, A = tap, right stick = swipe (up/down = swipe forward/back along the touchpad's X axis: Gunjack's OnFlick takes flick angles <45°/>315° as reload, 135-225° as special weapon), B/left menu = back (log controllers: the game reads its AInputQueue) |
GLES VrApi game aborts in vrclient.so (CSharedSemaphoreLinux::WaitFromGPU ← CSxrGraphicsBindingGLOpenVR::PreBeginFrame) right after it left VR mode and entered it again (Gunjack after loading) |
The Frame's runtime fails the first frame of a second OpenXR session on the same GL context | VrApi bridge: vrapi_LeaveVrMode keeps a running GLES session, vrapi_EnterVrMode with the same context reuses it (log Entered VR mode again (kept GLES session)) |
| 32-bit Unity game on the bridge: player stands in the floor (Spunky Dungeon) | Unity's floor-level tracking origin: vrapi_SetTrackingSpace(1) (LOCAL_FLOOR) was ignored |
The VrApi bridge puts the origin on the STAGE floor for LOCAL_FLOOR/STAGE |
Crash at start: NoClassDefFoundError: … Landroid/window/OnBackInvokedCallback; or NoSuchMethodError: No static method storeStoreFence()V in class Ljava/lang/invoke/VarHandle; (GitHub #71/#72, triage android-too-new) |
The APK needs Android 13+ (minSdk 33 or more; those were 34) and calls its classes at start; Lepton's Android is 11 (API 30). OVRPort lowers minSdk to 29, so it installs anyway. minSdk ≤ 32 (Quest's Android 12L) is fine: 16 working catalog games declare 32 | None (analysis extra.min_sdk > 32 → a warning on the game page/CLI and in the recipe notes; never unsupported from the manifest alone, the launch test's triage decides). Waits for Valve to update Lepton's Android |
Nothing opens; TWALauncherActivity: Using URL from Manifest (https://…), Creating TwaLauncher for com.oculus.browser, NameNotFoundException: com.oculus.browser (GitHub #86 Mahjong Table VR, triage web-wrapper) |
The APK is a Trusted Web Activity (Bubblewrap / Meta's PWA packaging): a website that opens in Meta's browser, which Lepton doesn't have | None: open the URL in a browser. Analysis extra.web_wrapper (manifest meta-data android.support.customtabs.trusted.DEFAULT_URL, resolved through resources.arsc, or androidbrowserhelper's LauncherActivity) → a warning with the URL (not unsupported: the manifest alone never decides) |
UnsatisfiedLinkError / cannot locate symbol "ovr_…" |
overport's platform loader lacks Meta platform functions | frame.ovrstubs (automatic, generated stubs) |
missing ovrMessageType_ToString |
same, but the game needs a real string | frame.ovrplatformcompat (automatic) |
The game stays in its built-in language or waits at start although its OBB / files hold language packs (<tag>.lang, for example de.lang) |
overport's platform loader answers ovr_LanguagePack_GetCurrent/SetCurrent with request id 0, so the game never gets the pack's path |
frame.langpacks (opt-in, experimental, shown for games whose data has *.lang; native/langpack, build it with python native/build.py --only langpack). Default pack: the one the game applies, else env FRAMEPORT_LANGPACK=<tag>, else the only pack; search path override FRAMEPORT_LANGPACK_DIRS. Unreal games (Deadpool VR) gray out a pack whose asset Metadata differs from the game's version string: the patch writes the APK's versionName as Metadata (env FRAMEPORT_LANGPACK_META overrides) and reports the pack under the /storage/emulated/0/Android/obb/<pkg>/ spelling of its path; with /sdcard/… the text switched but the dialogue stayed silent (verified in the headset with Deadpool VR) |
ClassNotFoundException com.oculus.os.AnalyticsEvent → abort |
Quest telemetry lookup in Meta XR Audio (Unreal build) or native code | frame.metaxr_telemetry + frame.oculusos (automatic) |
JNI DETECTED ERROR, GetStringUTFChars … NULL |
CheckJNI is on because overport marks the app debuggable | frame.nodebug |
Unreal game shows a one-time "optimizing" screen on its very first start, then closes (System.exit status 0 right after the system log's ActivityTaskManager: START … LAUNCHER … pkg=<own package> … from uid 10xxx, Lepton "Exited!"); the next start works |
UE 4.21+ restarts the app after its first-run shader precompile (r.ProgramBinaryCache.RestartAndroidAfterPrecompile, AndroidThunkJava_RestartApplication); Lepton ends the container with the first process, the restarted one with it (Sports Scramble). Skipping the restart or setting the cvar to 0 (-ini:Engine:[ConsoleVariables]:… in UE4CommandLine.txt: read, no restart requested) left Sports Scramble on the "optimizing" level for good: the game relies on the restart |
launch.sh starts Lepton once more (agent v86 _restart_requested matches both log lines within 5 s; launch-before-restart.log keeps the first run; FRAMEPORT_NO_RESTART=1 turns it off). Works for 32- and 64-bit games |
Unreal game quits a few seconds after start (System.exit) |
ForceQuit after a failed Quest platform check | alternate build with patch_remove_unreal_force_quit (use_alt) |
Unreal game crashes a few seconds after the logo with no backtrace (Unreal's own signal handler hides it: logcat-crash.log empty, Zygote exited due to signal 11); the crashing thread is GameThread, pc in libaaudio.so, lr libovrplatformloader.so (ovr_Microphone_GetOutputBufferMaxSize+0x10); the last platform call is ovr_Microphone_Create (for example The Walking Dead: Saints & Sinners Ch. 2) |
OVRPort's platform loader opens the microphone's AAudio stream only in ovr_Microphone_Start, but GetOutputBufferMaxSize reads that stream unchecked; Unreal's Oculus voice asks for the size right after Create |
frame.ovr_microphone (NULL check rewritten in place; size 0 until the microphone starts). OVRPort runtime 3.4.3-aa54c3f has the check itself: builds made with it leave the patch out (log "not needed: frame.ovr_microphone", patches/upstream.py) |
xrCreateSwapchain -26 / format unsupported (GLES) |
Frame takes only sRGB formats, no MSAA | adapter swapchain_fix (default on) |
| Frames rejected, "Waiting…" forever | a layer uses a failed swapchain or an extension that isn't enabled (for example equirect2) | adapter layer_fix (default on) |
xrConvert…TimeKHR FUNCTION_UNSUPPORTED spam; VrApi bridge stalls before recenter |
runtime lacks timespec conversion | current adapter emulates it |
Crash on the first frame, backtrace libVkLayer_fossilize.so ← FVulkanRenderPass::FVulkanRenderPass (crash logcat) |
the engine leaves the pNext of unused Vulkan attachment references uninitialized; Lepton always loads Steam's Fossilize layer (guest is userdebug, so even a non-debuggable APK gets it), which follows the pointer | frame.vk_sanitize (default on for Unreal; Vulkan shim libfp_vk.so; triage fossilize-renderpass), for example Deadpool VR |
SIGABRT right after start, backtrace libopenxr_loader.so (xrCreateSwapchain+…) |
overport's dispatcher aborts on swapchains > 4096 px ("Wrong createInfo size"); video players use 7680×3840 theater textures | frame.swapchain_limit (default on; guard → 16384 px; triage swapchain-size-abort), for example 4XVR |
Video/menu panel missing, log dropped N unusable layer(s) with cylinder (1000017000) or equirect2 (1000091000) layers |
the Frame runtime lacks XR_KHR_composition_layer_cylinder/equirect(2)/cube (its SteamVR runtime only composites quad + projection layers) | adapter shows cylinders as flat strips (setting cylinder_strips, default on); 360° equirect layers (theaters, 360° videos): adapter.equirect_emul (GLES only, per game) |
| 360° theater / 360° video black or missing (for example 4XVR) | equirect layers dropped | adapter.equirect_emul=1: a worker thread with a shared GLES context converts each 360° image to a cube map when it changes and draws one adapter projection layer from it for every frame with exactly that frame's views; it replaces the 360° layers in place, so the game's own projection layer (balcony, controllers, 360° videos it draws itself) stays on top. Quads can't be used: the Frame draws quad layers above every projection layer whatever the order. Log: first 360 view ready, in 5 s: N 360 image update(s) … M view redraw(s). Upside down / mirrored / behind you: equirect_flip 1/2/4 (frameport settings <pkg> equirect_flip=1, no rebuild). Never Vulkan (a Vulkan renderer hung the Frame's GPU in AC Nexus) |
Fades to black don't show: you see the level loading behind a loading screen (Batman: Arkham Shadow, GitHub #197); with layer_debug=1 launch.log shows color scale 0.00 0.00 0.00 … on the projection layer |
the game fades by dimming its eye image with XR_KHR_composition_layer_color_scale_bias; whether the Frame's runtime applies it isn't confirmed in the headset yet | adapter.fade_quad=1 (opt-in, native/adapter/fade_quad.c): FrameBridge adds a black, alpha-blended quad right after the dimmed projection layer (VIEW space, 1 m ahead, 6×6 m; opacity = 1 − scale in 16 steps from one 8×8 swapchain refilled only when the step changes: Vulkan clear / GLES clear; one swapchain per step hit XR_ERROR_LIMIT_REACHED in Batman), log fade_quad: drawing fades over the picture; fade_quad=2 also removes the layer's own scale (if both show, the fade is doubled) |
| Vermillion: the hand and brush disappear over the canvas while painting, but still paint (GitHub #200) | the game shows a canvas being painted as its own compositor (quad) layer and turns the canvas's scene renderer off; the Frame draws quad layers above every projection layer whatever the order, so the canvas covers the hand (Vulkan game: no GL layer emulation) | frame.vermillion_scene_canvas (catalog): PaintableMesh.HasCompositorLayerAvailable / ShouldUseCompositorOverlay -> false in libil2cpp.so (Cpp2IL), so the game's own fallback draws the canvas in the scene (Klownicle's finding) |
Unity game hangs right after its first frames, no pacing: lines; SIGQUIT dump shows UnityMain in a game library's JNI_OnLoad → usleep (for example SKYBOX: libskybox.so, SignatureChecker) |
the game checks its APK signing certificate; every port is re-signed | can't be fixed without defeating anti-tamper (FramePort doesn't): mark "Can't run". Diagnose hangs: podman exec <container> kill -3 <pid> writes all thread stacks to launch.log |
| Video player doesn't list sent videos (for example 4XVR "Internal Storage" only shows some) | the player lists its own /sdcard folder (4XVR: /sdcard/4XPlayer), not /sdcard/Movies; Android's media index doesn't work in Lepton | game page "Add videos" / game menu "Add videos and files" (Videos + the app's own folder, hard links) or frameport frame send <files> --app <pkg> |
One eye gray in a stereo video, log xrCreateSwapchain … result=-10 |
the runtime refused a swapchain (XR_ERROR_LIMIT_REACHED) | fewer/smaller swapchains: lower equirect_res; unresolved |
VrApi game (32-bit bridge or frame.vrapi_bridge): holding the menu button to recenter turns the view ~90-120 degrees to the side |
the bridge's app space is LOCAL + the head's yaw at the start; the Frame's recenter moves LOCAL to the head, so the old yaw was added again | fixed in the VrApi bridge (2026-10-13): on XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING (LOCAL) and on vrapi_RecenterPose it takes the origin from the head again (log recentered: head yaw …); rebuild the game |
| Menu/screen jumps to where you look (for example 4XVR) | the app re-creates LOCAL spaces (4XVR: every 2–4 s) and the Frame places them at the current head yaw, or the app recenters after the Frame's brief focus dips | turn on adapter.layer_debug, check that LOCAL space sits at … deg vs focus_hold: hid a … ms focus dip; patches: adapter.stable_local (new LOCAL spaces line up with the first) / adapter.focus_hold (on by default: hides dips up to focus_hold_ms once the game has been focused for a second; never at start-up: a global debounce broke AC Nexus) |
| Pointer misses menu items | the Frame's aim pose differs from Touch's | adapter.layer_debug logs aim in grip … pitch/yaw; correct with aim_pitch/aim_yaw/aim_forward (frameport settings, no rebuild) |
| Video stutters although fps is steady | video frame rate doesn't divide the refresh rate (30 fps at 72 Hz) | adapter.refresh_rate (for example 90 for 30 fps, 72 for 24 fps); layer_debug logs the offered rates and the app's own requests |
| PC VR game judders/stutters although the GPU keeps up; vrcompositor.txt "Timed out. N total" high or frames dropped | the game misses the headset's refresh (for example 96 Hz = 10.4 ms; Stormland's slow frames take 10.7 ms) | pcvr.steamvr_tuning (default on): next Play sets SteamVR per-app preferredRefreshRate + motionSmoothingOverride via fp_vrsettings.exe |
| Quest app keeps recentering / snapping the view when you turn your head; log shows session state 5→4→3→4→5 within a second | the Frame briefly takes focus; the app recenters on focus changes | adapter focus_hold (on by default; an earlier global debounce made AC Nexus stay black and was removed) |
| Video player / app finds no local videos; MediaProvider "Requested path /home/steamos/... doesn't appear under ..." | Lepton's /sdcard is a symlink to a host path, so Android's media index rejects every file | upload in the Files tab (Videos = /sdcard/Movies) and browse folders in the app |
| Batman: Arkham Shadow closes during smoke-bomb effects; later input-tree corruption | Meta XR Audio Wwise deletes queued metadata still referenced by the current audio frame, then writes through the stale pointer | FrameBridge automatically retires metadata after current-frame references disappear, for the verified AArch64 SDK build only; see AUDIO_METADATA.md |
Game stays on a loading or "Waiting" box although it reached FOCUSED; log FrameBridge: xrEndFrame failed -25 then no more pacing: lines (for example PowerWash Simulator on some Frames, GitHub #39) |
The eye image rect ends a few pixels past its swapchain (Unity rounds the swapchain width, the runtime's recommended size differs per Frame); SteamVR rejects every frame with XR_ERROR_SWAPCHAIN_RECT_INVALID | FrameBridge clamps every projection/quad image rect to its swapchain (rect_clamp, default on since 0.11.0; log rect_clamp: view N image rect clamped): rebuild + reinstall the game |
Unity game (OVRPlugin, GLES or Vulkan) crashes ~10 s in: FrameBridge: xrCreateSwapchain … faces=6 … result=-2, OVRPlugin CreateSwapchain for eye 0: 0x0, 0 stages, then render-thread SIGSEGV in memset ← libOVRPlugin ← ovrp_EndFrame4 (libgallium/ANR follow; for example Budget Cuts Ultimate, GitHub #107); triage cube-swapchain-refused |
The game shows a cube-map layer (OVROverlay cubemap); the Frame's runtime has no cube layers and refuses cube swapchains (XR_ERROR_RUNTIME_FAILURE); OVRPlugin doesn't check and writes into the image list it never got | FrameBridge cube_standin (default on): a refused cube swapchain is served as a GL cube map in the game's context or a device-local Vulkan cube image transitioned to COLOR_ATTACHMENT_OPTIMAL, and its layers are dropped (log cube_standin: runtime refused …): rebuild + reinstall. Vulkan path covers UNDERDOGS (GitHub #296); host-tested, headset verification pending |
Game (Unity, GLES) freezes, zink: DEVICE LOST |
multisampled render-to-texture hangs the GPU | frame.unity_no_msaa; if it persists: unfixable → PC version |
Unreal GLES game crashes ~3 s after start: Fatal signal 11 … fault addr 0x10000 in tid … (RHIThread), #01 … libgallium_dri.so (GitHub #83 Star Wars Pinball VR, triage unreal-msrtt-crash) |
Unreal's mobile MSAA renders through multisampled render-to-texture; Zink's find_rp_state then indexes rendering_state_cache[6] one past its end (sample count ≥ 32) and calls a junk hash function (0x10000). Mesa bug (no bounds check) |
frame.unreal_gl_shim (GL shim with gl_hide_msrtt, multiview kept for Unreal: Unreal 4.25 only enables multiview with GL_OVR_multiview, GL_OVR_multiview2 and GL_OVR_multiview_multisampled_render_to_texture, so the shim keeps the last one visible for Unreal and maps glFramebufferTextureMultisampleMultiviewOVR to the single-sampled glFramebufferTextureMultiviewOVR; log multiview multisampled render-to-texture (N samples) drawn single-sampled) |
Crash with SIGILL (ILL_ILLOPN) and *pc=0xd50323bf (autiasp), for example on Unreal's HttpManager thread (GitHub #83 Star Wars Pinball VR, triage pac-unpaired) |
The engine's OpenSSL ARMv8 assembly has autiasp without a matching paciasp (libUE4.so: 38 paciasp, 40 autiasp; Poly1305 NEON). Quest CPUs treat PAC hints as NOPs, the Frame's CPU checks them | frame.pac_hints: in a library with unpaired counts every paciasp/autiasp becomes a NOP (in place) |
launch.log stops at Waiting for app … (nothing after it, logcat: Unexpected EOF! or Logcat read failure); the game runs, but Steam's "Resume game" menu isn't closed, the launch test shows nothing and session triage has no log (Vader Immortal on Lepton 3.0.5, Under Cover on 2.8.14; a silent mirror twice on 3.0.5 on 2026-10-14) |
Lepton's logcat mirror died or never wrote | _logcat_keeper (started by launch.sh, added to older launchers by upgrade_launchers; agent v91) reads the container's logcat itself after a failure line or 20 s without a logcat line (podman exec lepton-steamlaunch-<appid> logcat -v threadtime -T <epoch>: from the launcher's start, or from just before the mirror's last line), sets a 16 MiB log buffer and appends to launch.log; log <base>/logcat-keeper.log |
A game no longer starts after a reinstall that was launch-tested right away: logcat Zip: EOCD not found, …/base.apk is not zip / Failed to parse …base.apk, no Start proc for the game (VR HOT, 2026-10-09) |
The launch test's 45 s ran from the launcher, but the first start after an APK change spends ~1–2 min booting Lepton and installing the app: stopping the container mid-install left a broken installed copy | Agent v68: the test window counts from Lepton's Waiting for app (at most 240 s extra for boot + install). Repair a broken one: touch <base>/lepton-app/game.apk (Lepton re-installs it on the next start) |
Unreal game (OVRPlugin) never starts VR: launch.log has OVRPlugin: JNI_OnLoad but never CompositorOpenXR::PreInitialize, no OpenXR session, then a crash a few seconds in (for example Star Wars Pinball VR, UE 4.25: null pointer in FSceneRenderer::GetMultiViewSceneColor, GitHub #83) |
Unreal's Oculus module looks up every ovrp_* function of the OVRPlugin it was built against (InitializeOculusPluginWrapper, all ANDed); OVRPort's OpenXR OVRPlugin lacks a few old ones (UE 4.25 / OVRPlugin 1.44: ovrp_GetPTWNear), so the wrapper fails and OculusHMD is never pre-initialized |
frame.unreal_ovrp_entrypoints (suggested when analysis unreal_ovrp_lookups name functions the shipped OVRPlugin lacks): generated libfp_ovrpstubs.so (DT_NEEDED of libOVRPlugin.so; dlsym on the plugin's handle searches its dependencies) with a stand-in per missing name returning ovrpFailure (-1000); build note lists the names |
Only the Android home screen shows; the log has Unity's VR device as None (for example Accounting+, Unity 2017) |
Unity 2017–2018 built-in Oculus support starts VR only when com.oculus.systemactivities is installed; Lepton has no Meta system apps, so Unity falls back to non-VR |
frame.unity_oculus_check: that package name in libunity.so → android, plus native/ovrpshim (libfp_ovrp.so): Unity 2017's legacy frame loop (ovrp_Update2/ovrp_BeginFrame) never calls ovrp_WaitToBeginFrame, so without the shim no xrWaitFrame happens, CompositorOpenXR::Update … outside of frame bounds floods the log and the dashboard freezes. With it: log ovrp frame loop shim: waited for frame N, ~71 fps. Accounting+ then stops at "press any button" although its OVRInput gets clean input (INPUT_PROBE in the patch logs connected controllers/buttons/input focus per call) — unresolved |
Stuck on the loading screen or quits at start; Unable to open archive file, is corrupted! Remove it and launch unity again or Failed to read data for the AssetBundle in launch.log (for example Batman, GitHub #155) |
The game's data folder (OBB) is incomplete or from another version than the APK | Copy the whole data folder again from the same install as the APK, add the game again, reinstall (triage unity-data-missing) |
Black flat window, then a GPU hang: kernel hangcheck detected gpu lockup (offending task the game), zink: DEVICE LOST, Unity render-thread crash; outside of frame bounds thousands of times a second (for example BattleSisters, Unity 2019.4) |
Unity 2019 on its built-in VR (no libOculusXRPlugin.so) uses the same legacy frame loop as Unity 2017: no xrWaitFrame, the render thread floods the GPU |
frame.unity_oculus_check adds the ovrpshim frame wait for these too (revision 2) |
Unity built-in VR game (with frame.unity_oculus_check) freezes at a scene switch: the log stops after Boot: Activating Scene: …, the process stays alive, every thread sleeps (for example Sniper Elite VR, Unity 2019.4) |
The shim's xrWaitFrame blocks until the previously waited frame is begun; Unity skipped beginning it at the scene switch and its render thread then waited for the main thread (gdb: UnityMain in vrclient CSxrSession::StartNextFrame ← ovrp_WaitToBeginFrame ← fpov_Update2). OVRPlugin also only begins the frame index it waited for |
frame.unity_oculus_check revision 5: libunity.so's ovrp_BeginFrame/ovrp_EndFrame go through the shim, which waits ≤ 50 ms for the last waited frame to begin, else skips the wait (log the last waited frame wasn't begun), and gives a begin/end of another index the waited one |
| Unity built-in VR game: the hands/weapons trail the controllers by centimetres when moving (headset fine, 72 fps), for example Sniper Elite VR | Unity's physics-step ovrp_Update2 (prediction 0) makes OVRPlugin locate the nodes at the monotonic clock's "now", the XrTime base on a Quest but 0.05–0.9 s behind the Frame's XrTime; the runtime extrapolates backwards and the render-step reads after it get those poses too. A larger prediction is capped by OVRPlugin (~0.07 s) |
frame.unity_oculus_check revision 5 holds the physics-step update back: both steps read the render update's display-time poses |
OVRPlugin game: the hands lag behind the controllers (also with ovrp_hold_physics on or off; rendering fine), for example BattleSisters |
OVRPlugin passes its monotonic-clock "now" on as the OpenXR time (OVRPort's dispatcher converts XR_KHR_convert_timespec_time 1:1: the Frame's runtime lacks it, and FrameBridge's emulation is never asked), but the Frame's XrTime runs ahead of CLOCK_MONOTONIC (2.56 s on SteamOS 0.4.5): with pose_debug=1 the hand spaces show time - predicted display time -2564 ms; BattleSisters also asks for its head at XrTime 0.1 s every frame |
adapter.pose_time_fix (FrameBridge, suggested for Unity built-in OVRPlugin games): a located time nearer the monotonic clock than XrTime's "now" is moved by the clock offset measured at xrWaitFrame, one more than 0.5 s before the display time goes to "now" (log pose_time_fix: …; pose_debug=1 counts both per 5 s). Once a build has this FrameBridge it is toggled in settings.conf (no rebuild) The offset is the largest of the last ~2 s of xrWaitFrame samples (hitches made single samples dip by up to 2.5 s and pushed fixed times into the future); far-past requests get the frame's display time. Headless survey of 54 games (2026-10-09): no head/view request on the monotonic clock in any other game; UE4 OVRPlugin 1.89 games (Robo Recall, Vader, Phantom, Time Stall) and The Room VR ask for head/views at XrTime ≈ 0 every frame, which works today; hands only show in the headset. Suggested for Unity built-in Oculus games only. A time equal (±1 ms) to one of the last 6 predicted display times is never moved, and monotonic times are only told apart when the clocks are more than 4 display periods apart and the time is clearly nearer the monotonic "now" (GitHub #49: on a Frame with XrTime only ~68 ms ahead, a display-time request after a hitch was moved +67.7 ms) |
| Unreal game stuck on a loading image after the intro, frames keep coming (72 fps), no crash (for example Vader Immortal) | suspect: a Meta Platform SDK request no message ever answers | diagnostics: frame.ovr_trace (opt-in) logs every ovr_* call, the answers ovr_PopMessage returns and every 10 s the unanswered requests (fp_ovrtrace in launch.log). Never fake an entitlement answer |
Own-engine game crashes in libVkLayer_fossilize.so (Fossilize's recording thread, for example Roblox) |
same as Deadpool VR: uninitialised pointers in what Fossilize records | frame.vk_sanitize now also covers own-engine libraries that load libvulkan.so by name |
Crash right after the game gets focus, in vrclient.so … UpdateActionStateInternal / sxr_xrSyncActions (for example Myst) |
a race in the Frame runtime's input code on the first sync after focus (not every launch) | adapter sync_guard (input syncs one at a time with event polling, paused 250 ms after FOCUSED) |
| View jumps sideways / game pauses or freezes for a moment every few seconds (for example Blade & Sorcery, BattleGlide) | the Frame drops focus for ~0.5 s (FOCUSED→VISIBLE→SYNCHRONIZED); games pause or re-align the player (OnVRPresence … Teleport) |
adapter focus_hold |
SDL / LÖVE app crashes at start: NullPointerException … ClipboardManager.addPrimaryClipChangedListener in SDLClipboardHandler.<init> (for example Dramatic Shape) |
Lepton's Android has no clipboard service (service check clipboard: not found) |
frame.sdl_clipboard (that call → nops in classes.dex, in place, apk/dex.py); reproduced and verified with LÖVE for Android 11.5 |
Godot 4 app crashes at start: java.lang.NullPointerException: null cannot be cast to non-null type android.content.ClipboardManager at org.godotengine.godot.Godot.<init> (Godot 4.3/4.4; 4.2 when the clipboard is first used, in Godot$mClipboard$2.invoke) (for example EndoparasiticVR, GitHub #134); triage godot-no-clipboard |
Lepton's Android has no clipboard service and Godot's Kotlin code casts it with a non-null as ClipboardManager (Intrinsics.checkNotNull before the check-cast). Godot 4.1 (Java) and 4.5+ (as?) don't throw |
frame.godot_clipboard (suggested by analysis godot_clipboard): that checkNotNull call → nops in classes.dex (check-cast accepts null), and Godot's getClipboard/hasClipboard/setClipboard return at once ("" / false / nothing; Dex.return_early), in place; matched exactly, other dex files untouched. Checked against the Maven godot-lib 4.2.2/4.3.0/4.4.1 and the 4.3/4.4.1 release export templates (D8 re-reads the result); not yet run on the device |
Game crashes at start: java.lang.NoSuchMethodError: No virtual method getAvailableCommunicationDevices()… in class Landroid/media/AudioManager at com.vivox.sdk.AudioChangeListener.checkAudioRouteAndApplyChanges (for example Green Hell VR, GitHub #101); triage vivox-api31 |
Newer Vivox (voice chat) builds call Android 12 audio-routing methods without a version check; Lepton is Android 11. Older Vivox builds (Eleven Table Tennis, BattleSisters) don't have that code | frame.vivox_audio_route (suggested by analysis vivox_api31): every AudioChangeListener method that calls one of them returns at once (Dex.return_early: return-void / const/4 v0, 0; return v0, in place); voice chat keeps the default audio route |
A game on a microSD card or another drive doesn't start, loses its saves or loops at start; launch.log has ln: cannot create symbolic link from 'Frame' to '/data/data/<pkg>' or Couldn't create directory for SharedPreferences file /data/user/0/<pkg>/… (GitHub #213/#214/#215/#221: a card named "Steam Frame 512g"); triage lepton-data-link-failed |
Valve's Lepton pastes STEAM_COMPAT_DATA_PATH unquoted into sh -c "ln -s …/internal/<pkg> /data/data/<pkg>" (liblepton mounting.sh) and APK paths into unquoted substitutions, so a path with a space never gets the app's internal storage |
agent v83: the Quest launcher links the game folder to ~/.local/share/frameport/lepton-paths/<pkg> at every start and gives Lepton that path (app_dir_lines); upgrade_launchers converts existing launchers at the next connection; deployment.json keeps the real path |
Start never finishes, 100 % CPU, launch.log of gigabytes repeating EOSSDK.createKeychain / resetKeychain (Beyond Sandbox, GitHub #214); triage eos-keychain-loop |
Epic Online Services' Java keychain can't write its SharedPreferences and resets/creates without end (seen when the app had no internal storage, row above) | fix the storage first; frame.eos_keychain_loop (opt-in, offered by triage; analysis eos_keychain) makes EOSSDK.resetKeychain return at once (Dex.return_early) |
Unity game freezes on its loading screen ~10-15 s in (Yupitergrad, GitHub #235); launch.log has GrantPermissionsActivity: Permission grant result … permission=android.permission.BLUETOOTH_CONNECT, then OVRPlugin SetupDisplayObjects(): Unable to find EGLConfig for configId 0 and no more frames; triage bhaptics-permission-prompt |
bHaptics' vest SDK (com.bhaptics.commons.PermissionUtils.requestBluetoothPermission) asks for Android 12's BLUETOOTH_CONNECT; on Lepton's Android 11 it can't be granted at install, so Android's permission screen takes the foreground for a moment, the game pauses/resumes and OVRPlugin's GLES compositor stops drawing |
frame.bhaptics_permission (suggested by analysis bhaptics_permission): the request returns at once (Dex.return_early) |
Unity game aborts at start: Abort message: 'cannot create thread specific key for __cxa_get_globals()' (SIGABRT in a library such as libFirebaseCppApp-*.so, for example Wander 0.3570, GitHub #198); triage pthread-keys-exhausted |
Bionic allows 128 pthread keys per process; Unity 6 takes ~40 (most without a destructor), large native libraries (Gecko libxul.so, Firebase, avatar SDKs) and the Frame's own libraries (Mesa/Zink, FrameBridge, OVRPort, Valve's Vulkan layers) take the rest |
frame.unity_tls_key_pool (opt-in; suggested for Unity 6 with Firebase C++ or Gecko): libunity.so's pthread_key_*/pthread_get/setspecific imports are renamed to fp_tkey_* (elf.rename_imports, versions set global) and libfp_tkeys.so (native/tlskeys) is added to its DT_NEEDED; destructor-less keys become slots behind one real key (logs fp_tkeys: pooling Unity's thread keys). Approach by Klownicle (#198) |
Controllers do nothing (for example stuck on a setup screen); log: xrSuggestInteractionProfileBindings … XR_ERROR_PATH_UNSUPPORTED |
the game only suggests bindings for Meta's newer profiles (Touch Plus / Touch Pro); the Frame's Android runtime knows oculus/touch_controller, not those |
adapter profile_remap (default on): the bindings are suggested again for Touch (log: controller profile … -> oculus/touch_controller) |
| Some buttons do nothing or the wrong thing (for example a Quest game's X/Y on the Frame's left controller) | the game binds only Quest Touch and SteamVR maps Touch onto the Frame controllers with its own remap (Touch left X/Y land on the d-pad, right X and Y repeat B), or the runtime rejected a profile or an input call | turn on adapter input_diag (diagnostics, off by default; frameport settings <pkg> input_diag=1) and look for input_diag: lines: bindings: <profile> accepted or unsupported: interaction profile <profile> -> <result> with the rejected paths, bindings: /user/hand/<hand> uses <profile>, unsupported: function <name>, unsupported: xrCreateAction -> <result> (<action>) |
Unity game hangs or the whole Frame restarts (for example in a menu); log: The current MSAA level is 0, but the recommended MSAA level is 4. Switching to the recommended level. (for example Lucky's Tale) |
Meta's OVRManager (useRecommendedMSAALevel) turns 4x MSAA on at runtime, past QualitySettings; multisampled render-to-texture on GLES/Zink hangs the GPU |
frame.unity_runtime_msaa_off (Cpp2IL: OVRDisplay.get_recommendedMSAALevel → 0) |
| Unity game: one eye shows only effects or gray, the other is fine (for example I Am Cat, Oculus XR Plugin on GLES) | the game's single-pass multiview rendering goes wrong for array slice 1 (the adapter submits both slices correctly) | frame.unity_multipass (Cpp2IL: OculusSettings.GetStereoRenderingMode → MultiPass); swap_eyes=1 tells whether the game or the adapter is at fault |
| Setup/intro loops every launch (Espire 2) | save folders created without write permission | launcher repairs permissions every 2 s (built in) |
| VrApi bridge: never enters VR | Frame reaches FOCUSED later than Quest; tracking only when worn | bridge waits 30 s; test in the headset |
| Unreal game (ILMxLAB, for example Vader Immortal) plays its intro, then stays on its loading card (portrait + progress bar) at 72 fps and ignores input (GitHub #49) | the menu waits for UVRUtils::GetQuestShaderPrecompilePercent() to reach 100 %; the precompile only starts when IsRunningOnSantaCruz() (a Quest), the other branch returns 0.0 |
frame.unreal_quest_precompile (non-Quest branch returns 1.0; found by Klownicle); suggested when the engine exports that function (analysis unreal_quest_gates) |
Video in a game or player stutters, falls behind or drops to a few fps (4K/8K, for example a video player's 360° video, Batman's cutscenes); OMX.google.* / c2.android.* decoders in the log |
Lepton's Android has only software decoders | frame.hw_video_decode (suggested when the APK uses MediaCodec/ExoPlayer/VLC; no APK change, reinstall): OMX.frameport.{avc,hevc,vp9}.decoder on the Frame's Iris hardware (H.264 High, HEVC Main, VP9 profile 0 up to 4096x2304; 8-bit; up to 8192 px), mounted into this game's container only (log Iris hardware video/… decoder active; launch.log FramePort video: loading the Iris hardware codec plugin). Iris … unavailable / initialization failed (-12) (triage hw-video-decoder-busy): another decoder holds the hardware, usually Steam's own hardware video decoding: turn it off in Steam, restart Steam. Video broken with it: Settings → Installing → Hardware video decoding off (all games) or FRAMEPORT_NO_HW_VIDEO=1 %command% (one game) |
Picture problems (headset on)
| Symptom | Cause | Fix |
|---|---|---|
Found after playing: the game page's "Last session" (or frameport session <pkg>) lists a finding (agent v70 session_log: the newest plays.log session's launch.log, ≤4 MB, + its crash logcat + kernel GPU lines) |
launch tests never reach FOCUSED or gameplay; the same catalog/triage.yaml signatures (sync_guard, pac_hints, unreal_gl_shim, vk_spec_fixes, …) now also run on real sessions, plus the frame-rate and focus-dip findings below |
"Try this setting" (FrameBridge settings: written on the Frame at once via set_settings, used from the next start) / "Rebuild with this patch" (APK patches); launch-test sessions (test <unix> in plays.log) are skipped |
Textures flicker, jump or smear while moving in an OVRPlugin game that renders space-warp swapchains (xrCreateSwapchain 376x376 format=97 / format=129; triage space-warp-used, info + question) |
Application SpaceWarp: half the frames plus motion vectors; the Frame's reprojection of them misbehaves (Into The Radius 2, Metro Awakening) | adapter hide_space_warp=1; offered as "Did textures flicker…?" after a play session, never applied on its own |
The game froze or closed and the kernel log has hangcheck / GPU fault / msm_drm … recover lines during the session (triage gpu-hang, play sessions only) |
a shader or effect hangs the Frame's GPU (for example Vader Immortal's lightspeed jump) | capture the shaders (adapter.vk_shader_dump=1 Vulkan, adapter.zink_shader_dump=1 OpenGL ES: triage keeps the one matching the session's swapchain formats), play to the hang again and send a problem report (diagnostics carry the newest dumped modules); the maintainer adds a vk_shader_fix / zink_shader_fix catalog entry. zink-device-lost keeps its MSAA suggestions |
The game keeps pausing or recentring for a moment while worn (session finding focus-dips: ≥3 focus: back after N ms lines longer than the game's focus_hold_ms, up to 5 s) |
the Frame's wear sensor flickers "HMD off"; FrameBridge logs every runtime focus loss/return (always, before focus_hold) | adapter.focus_hold_ms=<longest dip + 250 ms, in 500 ms steps, ≤5000> (or adapter.focus_hold=1 when off) |
Judder or smearing on head turns; FrameBridge pacing below the display rate (session finding slow-frames: >30 % of the 5 s windows under 90 % of the nearest refresh rate ≥ the session's 90th-percentile fps, after the first two windows, ≥1 min) |
the game renders below the refresh rate; the runtime reprojects | adapter.scale=<current × 0.85> first, then frame.unity_runtime_msaa_off (Unity) / adapter.hide_space_warp (Unreal 5 + OVRPlugin) |
| Unreal game (ILMxLAB, for example Vader Immortal): controllers track but grip/trigger do nothing, grabbing never works (Unreal's own input values move, the game's stay 0) | URPOCKeyMapManagerComponent picks the Quest or the Gear VR key set by IsRunningOnSantaCruz(); on the Frame it takes Gear VR, empty in a Quest build |
frame.unreal_quest_keymap (the Oculus case keeps the Quest set, for action and axis mappings; found by Klownicle, GitHub #49) |
The hands' thumbs never move when you touch the thumbstick or buttons (Unreal ThumbUp stays 1, for example Vader Immortal) |
OVRPlugin reports thumb proximity from XR_FB_touch_controller_proximity, which the Frame's runtime lacks; OVRPort's loader offers it anyway and the proximity actions never get a binding | adapter proximity_emul=1 (thumb proximity from the touch inputs: thumbstick, face buttons, thumb rest; 2 = also the index finger from trigger touch); log proximity_emul: finger proximity bound to touch. For UE4 Oculus games this didn't animate the thumbs (Vader, GitHub #49): use frame.unreal_thumb_touch (found by Klownicle): OculusInput::FOculusInput::SendControllerEvents computes ThumbUp from NearTouches (masks 0x2/0x8); the patch makes it read Touches (the slot 4 bytes before) with masks 0x0f00 (X/Y/stick/thumb rest) / 0x000f (A/B/stick/thumb rest), keeping the inversion; three instructions, matched exactly (build note OculusInput ThumbUp: from the capacitive touches). Matches Vader Immortal Ep. I, Robo Recall, Phantom: Covert Ops, Time Stall, Star Wars: Tales from the Galaxy's Edge; newer UE4 builds (for example Asgard's Wrath 2, RE4, In Death) compile it differently (shared mask registers) and aren't matched |
Vulkan game (Unreal / own engine with the Vulkan shim): the GPU hangs at one effect or cutscene (kernel hangcheck detected gpu lockup, freeze/exit; for example a VR4 cutscene, GitHub #140) |
a SPIR-V shader reads a loop counter/accumulator before setting it (VR4's campaign shader, GitHub #10) | adapter vk_shader_dump=1 (frame.vk_sanitize's shim; log vk shim: dumping SPIR-V modules to …) writes every distinct module once to files/fp_vk_shaders/<size>_<sha256>.spv + index.txt (<seq> <ms> <unix ms> <name> new|known|again); play to the hang, collect diagnostics (target/shaders/fp_vk_shaders/, newest 4 MB) and take the modules first created (new) shortly before the kernel's hangcheck time; disassemble (spirv-dis), find the variable read before its first store, write a vk_shader_fix (<size>:<sha256>:<byte offset>:<words> = inserted OpStores; log vk shim: fixed shader module); switch the dump off again |
OpenGL ES game: memory and GPU mappings grow every frame (~20 MB/s, /dev/dri/renderD128 mappings ~6 per frame) until the frame rate collapses; the game's xrclient_<package>.txt (Steam logs) has one SXR_GL_CHECK: error:0x502 ... glGenTextures before the session starts (GitHub #285, Vader Immortal) |
The game leaves GL errors pending on its context; the runtime's GL interop reads glGetError after its own calls and takes the stale error as its own failure. From then on CVRCompositorSharedTextures::BeginCommandBuffer allocates a new command buffer every frame instead of reusing one (perf page-fault stacks) |
Adapter gl_error_clear=1 (GLES only) clears pending GL errors before xrCreateSwapchain/xrBeginFrame/xrEndFrame; Vader's catalog recipes set it. Verified headless 2026-10-14: 191 GPU mappings and ~1.07 GB flat over 100 s (before: +430 mappings/s, +22 MB/s), no SXR_GL_CHECK |
| OpenGL ES game: the GPU hangs at one effect (freeze/exit, compositor watchdog, for example Vader Immortal's lightspeed jump) | a shader (as Zink compiles it) reads loop counters/accumulators before setting them | capture the SPIR-V (adapter zink_shader_dump=1 writes every module to files/fp_spirv/, or a GPU capture), then a zink_shader_fix entry (same format as vk_shader_fix) + frame.zink_shader_fix (Vulkan layer under Zink, activated through GraphicsEnv's debug layer list); log shader fix layer: fixed shader module N; triage zink-shader-fix-mismatch when a Frame update changed the driver's output |
| 2D Android app: Steam says it's running but nothing shows | Lepton runs apps headless unless the app folder has lepton-show-flatscreen |
FramePort adds it for apps without VR (agent v28+); reinstall apps installed before |
| A phone/tablet app shows nothing in the headset, or a VR app opens as a flat window | FramePort's VR/2D guess (vr_kind) is wrong for this app |
patch device.display_mode → Flat window or VR (Customize → "Show as VR or as a flat window"), then Update on Frame |
| An install button opens another program (or nothing) instead of FramePort | another program owns the link type, or the switch is off, or macOS (Flet can't receive links there) | Settings → Install links → Use FramePort for these links; else Add games → Install from a link… (paste the button's address) |
2D Android game ignores the controllers: they only move a pointer, an SDL game keeps its touch controls ("no controller attached"); Android's log lists only wayland_touch/wayland_keyboard/wayland_pointer (GitHub #162, Skate 3) |
Lepton's Android gets keyboard, pointer and touch from the Wayland seat only: no gamepad device | patch device.steam_gamepad (2D apps; suggested when the manifest declares android.hardware.gamepad or LEANBACK_LAUNCHER; no APK change, reinstall): the launcher puts FramePort's Podman wrapper (~/.local/share/frameport/agent/bin/podman) first on PATH, which binds the host's /dev/input at /dev/fp-input plus an Xbox 360 key layout (Vendor_28de_Product_11ff.kl, incl. D-pad buttons), and exports LEPTON_ENV_SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=1; the launcher's _gamepad_links worker (agent v85) keeps /dev/input/eventN links to Steam Input's virtual pads (uinput, 28de, "Microsoft X-Box 360 pad N") in step while the game runs, so a pad Steam makes later (controllers woke up, reconnected) is added and a gone one removed (Android's EventHub follows through inotify). Check: <base>/gamepad.log (pads: event5 / pads: none), launch.log EventHub: New device … pad, podman exec lepton-steamlaunch-<appid> dumpsys input lists the pad. Steam Input must be on for the shortcut; Steam activates the shortcut's own layout while the game has focus (controller.txt Queueing activation for controller: 0 app: <shortcut appid as signed int>), and Steam's UI layout (app 769) while the dashboard is open. Off for one start: FRAMEPORT_NO_GAMEPAD=1 %command% |
| 2D Android app: back/home/recents buttons cover the app's own buttons | Android's navigation bar | patch device.hide_navbar (on by default for apps without VR) sets qemu.hw.mainkeys=1; reinstall to apply |
| Black screen, audio works, GLES engine with direct VrApi | Mesa rejects Quest-style GLSL | frame.gl_shim (logs GLShim: SHADER COMPILE FAILED + source lines) |
Black screen, audio works, log Unsupported VrApi layer type N |
bridge drops frames containing that layer | extend the bridge (cylinder=3 is converted to a quad already) |
Only some draws visible (for example controllers) + glGetError 0x502 |
multiview shaders used on single-view FBOs | GL shim gl_hide_multiview=1 (default) |
| Own GLES engine (OpenXR, not VrApi): eye view fine, but HUD / menu / PDA panels black (for example Doom3Quest, GitHub #77; no error in the log) | every shader declares layout(num_views=2) in;, also the ones drawn into the engine's single-layer offscreen framebuffers (2D textures/renderbuffers); OVR_multiview makes such draws INVALID_OPERATION and Mesa drops them (draw_validate.c), Quest's driver doesn't |
frame.gl_multiview_fbo (opt-in, experimental: libfpglmv.so interposes GLES and draws those cases with a single-view twin of the program; logs GLMV: twin built: program N -> M, per-5-s single-view draws counters; gl_mv_debug=1 adds glGetError checks; triage gl-multiview-twin-failed). Alternative: the game's own fix (two-layer multiview pool, Xandrix's patch in #77) |
| Upside-down image in a GL bridge game | GL images start at the bottom row | fixed in the bridge (swap angleUp/Down for GL chains) |
| UI panels upside down (AC Nexus) | XrCompositionLayerImageLayoutFB VERTICAL_FLIP unsupported | adapter flip_emul=1 (default); rotating quads makes them vanish |
| Passthrough black (BAM) | XR_FB_passthrough missing | adapter passthrough_emul=1 (default) + patch_force_passthrough for MR-only games |
| Quest Touch controllers (or none) shown in a game that uses Meta's runtime controller models | XR_FB_render_model missing on the Frame | adapter controller_models=1: the agent converts the Frame's SteamVR controller models to glb at install, the adapter serves them. Games with their own controller meshes aren't affected (none of the 34 catalog games use runtime models) |
overportOXR: Unknown proc addr: xr… for a function the adapter emulates |
overport's dispatcher only forwards functions in its own table | route the plugin's lookups through native/xrshim (done for XR_FB_render_model when controller_models=1) |
| MR game stuck waiting for room data (Demeter) | no Meta scene API | adapter scene_emul=1 (+ frame.meta_permissions) |
| Hand-tracking game janky (Silhouette) | Frame synthesizes hands from controllers | controller_fix=0 passes hands through; not really fixable |
| Eye distortion while moving (Arcsmith, Time Stall) | unknown (not eye swap, tracking, Valve layers, depth or pacing) | unresolved |
| The Climb 666.0: hand outlines ripple in both eyes while menus and gameplay work (GitHub #238) | Unknown. Sessions paced around 72 fps, controller tracking reported valid, and SteamVR motion smoothing was already off; these checks do not establish the cause | Unresolved after wearer comparisons on 2026-10-12: disabling Valve's Vulkan layers, setting r_AntialiasingMode = 1, and setting r_AntialiasingMode = 0 with r_MotionBlur = 0 each gave the same or worse result. These were config comparisons; runtime cvar values were not verified. Restored the playable configuration (r_vr_foveated = 0 with the default Vulkan layers), retaining the startup/rendering crash fixes above. Do not keep the unsuccessful experimental settings as a fix |
| One eye gray / only effects (I Am Cat, Unity Oculus XR Plugin on GLES) | the game's multiview pass draws array slice 1 wrong on Zink | frame.unity_multipass (also rewrites the getter's inlined read in OculusLoader.Initialize; check for two array=1 eye swapchains) |
View vibrates even when holding still after frame.unity_multipass (I Am Cat) |
poses/times are consistent, but two passes on Zink make every frame one period late, and the game clamps its physics step to 10 ms | not fixed (scale 0.8 didn't help); next ideas: fix multiview instead, or the game's fixed timestep |
| Game freezes / pauses for 0.5-2 s now and then, no flicker (for example Blade & Sorcery, Lucky's Tale) | the headset's wear sensor flickers off while worn (logs/eyetracking.txt HMD off, stopping eye tracking … HMD on), the Frame turns it into a focus loss and Quest games that pause on focus loss pause |
adapter focus_hold (on by default since 2026-10-05, dips up to focus_hold_ms = 5000 ms are hidden; longer ones still pause) |
| Minor glitches on some objects (Myst, Unreal Vulkan) | unknown; not application space warp (turning it off changed nothing and ran worse) | unresolved |
Unity game crashes or hangs right as Vulkan starts, after SLZ Graphics plugin loading! and OVRPlugin's pre-init xrDestroyInstance: SIGSEGV with pc 0 (or pc == fault address in an unloaded library), x16/x23 in libSLZQuestNative.so (BONELAB 1.2974) |
Stress Level Zero's graphics plugin hooks Unity's Vulkan start-up (IUnityGraphicsVulkanV2 interception: vkCreateInstance/vkCreateDevice) and vkCreateSampler; its vkCreateInstance wrapper calls an invalid pointer on the Frame | frame.slz_vulkan_hooks (its two registrations become no-ops; Unity starts Vulkan itself; ~35 s of shader prewarming without its pipeline cache) |
| Unity game shows a picture but the player body is frozen: no head tracking, the controllers stay on the model, no buttons, although OpenXR input works (BONELAB 1.2068, Oculus XR Plugin) | OVRPlugin reports the headset as not worn (ovrp_GetUserPresent2 → 0) a few seconds after start, and the game's rig only follows a present user (Marrow XRHMD.IsUserPresent = Unity's HMD UserPresence) |
frame.unity_user_presence (the Oculus XR Plugin's lookup → libfp_ovrp.so, reports present; log user presence: OVRPlugin 0 …) |
Unreal 5 game crashes at its first render pass, backtrace vulkan.freedreno.so ← libVkLayer_fossilize.so ← libfp_vk.so (create_render_pass2) (Metro Awakening) |
a subpass without a depth attachment names a depth/stencil resolve attachment; the Frame's Turnip reads the missing depth attachment (NULL) | the Vulkan shim drops that resolve (always, FramePort after 0.11.0; log dropped the depth resolve of subpass N (no depth attachment)); triage unreal-vulkan-driver-crash |
Unreal 5 game crashes in vulkan.freedreno.so called straight from libUnreal.so right after its swapchains, or a few seconds into the first frames (Metro Awakening) |
Unreal turns on fragment-density-map foveation because Turnip offers VK_EXT_fragment_density_map, then records barriers (to FRAGMENT_DENSITY_MAP_OPTIMAL) and image views (2D array R8G8) for a density map the Frame never provides (VK_NULL_HANDLE); Turnip dereferences it |
adapter vk_hide_fdm=1 (the game doesn't see the extension) + vk_spec_fixes=1 (leaves out null-image barriers, refuses null-image views); triage unreal-fdm-missing. Found by disassembling the driver at the crash pc: the struct offsets (0x58 = VkSubpassDescription2, 0x48/0x60 = image barriers, viewType/format in registers) name the call |
Own-engine game crashes in je_free from its own library on the first frames (Roblox) |
the game frees memory it doesn't own; not the Vulkan shim or the format fallback | unsupported |
Live view (Live view tab, install/livestream.py)
| Symptom | Cause | Fix |
|---|---|---|
| Status line says "software encoder" (30 fps, the Frame's CPU busier) | fp_venc --probe failed (missing upload, encoder busy or refused a format: exit 4) or the helper couldn't be put on the Frame |
on the Frame: ~/.local/share/frameport/bin/fp_venc --probe; echo $? and --selftest; the app log names upload failures. FP_VENC_DISABLE=1 forces the fallback for tests |
| Picture drifts out of sync with the sound | the hardware path relies on one H.264 frame per fps slot (ffmpeg -framerate); fp_venc: stats … skipped= > 0 on stderr means slots were skipped |
see the stats line (every 10 s); a stopped/suspended Frame skips slots on purpose |
| Viewer joins only after several seconds | the keyframe request (k on the channel) didn't reach the encoder; the viewer then waits for the next regular keyframe (4 s) |
check live: encoder=hardware came first (requests are sent only then) |
Debugging techniques that worked
- A user's problem report:
frameport diag inspect <FramePort-diag-*.zip>re-triages its launch log with the current signatures; the zip has the recipe, analysis, ELF/PE imports, Frame logs and versions (docs/DIAGNOSTICS.md). - Read
<base>/launch.log(logcat mirror). Filter the game's pid:Start proc <pid>:<package>. - Monitor tab (GUI, while a game runs): fps from FrameBridge's pacing lines next to GPU busy %, the hottest sensor, memory pressure and per-process CPU/GPU/memory. A memory runaway (Lucky's Tale, Vader Immortal) shows as rising memory + "waiting" before the OOM kill; a GPU-bound game as GPU near 100 % with fps under target; heat as the temperature tile turning amber/red. Sources and costs: FRAME_RUNTIME.md "Monitoring sources".
- The adapter logs as
FrameBridge(settings, xrCreateInstance result, swapchain retries, pacing fps). - For GLES/GLAD engines, wrap
eglGetProcAddressto see shader compile errors (build the shim with-DGLSHIM_TRACEfor per-FBO draw counts and draw-call errors; the bridge has-DOVP_GL_DIAGfor eye-image readback). - Anything visual needs one headset session per iteration — batch hypotheses into each build.
- Headless launch tests never get focus: they prove startup, not play. Crashes right after FOCUSED (Myst), focus
dips and judder only show up in a headset session: ask for one, then
frameport diag collect <pkg>(it contains the headset sessions too, plus this boot's kernel log). - Which library a stale pointer belonged to: turn on the Android linker's log for one run by adding
debug.ld.app.<pkg>=dlopenas an extra line ofLEPTON_GFXRECON_FP_PROPS(first line0) in the game's launch.sh on the Frame (back it up, restore it after) and look fordlclose: unloading "…". - Memory runaways / busy loops:
perfworks as steamos (perf_event_paranoid2, user space only;ptrace_scope1 blocks eu-stack/gdb/debuggerd).perf record -p <pid> -e page-faults -c 64 --call-graph fpnames the thread touching new memory (perf script -F comm,tid,ip,dso);-t <tid> -F 499samples one thread. Map IL2CPP addresses with a full Cpp2IL diffable-cs dump (methodOffset= address − the r-xp mapping of libil2cpp.so). Sample memory per mapping name from/proc/<pid>/smaps([anon:libc_malloc]= native heap). - Hangs (every thread asleep): first ask for a diagnostics zip saved while the game hangs:
target/threads.txt(agent v79) shows each thread's wait and syscall (futex = a lock, ppoll/recvfrom + an internet socket = network). Then backtraces of all threads with gdb. Root inside the Lepton container may ptrace the game (the host'sptrace_scope1 doesn't apply there) and the guest hasgdbserver64;debuggerd -bfails (no tombstoned). Copy every mapped library to a local sysroot (podman exec <container> cat /proc/<pid>/maps, thentar -chf - -T <list>throughpodman exec -i; the app's own libs from the installedlepton-app/game.apkat their/data/app/…/lib/arm64path), then on the PC:gdb -batch -ex "set sysroot <dir>" -ex "target remote | ssh steamos@<frame> podman exec -i lepton-steamlaunch-<appid> gdbserver64 - --attach <pid in the container>" -ex "thread apply all bt 25" -ex detach(set sysroot target:makes gdbserver assert in linux_mntns_readlink)./proc/<pid>/task/*/syscall(read inside the container) shows each thread's syscall and pc without a debugger. - Unity catches native crashes itself: no debuggerd backtrace in Lepton's logcat-crash.log, but a
tombstone_00in the game'sAndroid/data/<pkg>/files/with the memory map (map pc/lr/registers to libraries). - Focus losses: the game sees FOCUSED→VISIBLE→SYNCHRONIZED (OVRPlugin
[XR_SESSION], FrameBridgefocus_hold:lines); the cause is on the host:~/.local/share/Steam/logs/vrserver.txt(HMD off/on,entering standby,SystemButtonDownin vrclient_vrcompositor.txt) and XRService ([UserPresence],IMUFallback). Compare the times (Lepton logs UTC, host logs local time). - A Unity getter patch that changes nothing: the compiler may have inlined it (no
BLto its address in libil2cpp.so) — patch the field read in the caller (Il2cppReturnPatch.field_loads). - Fixes found by users count: a reporter's shader capture (VR4) became the
vk_shader_fixrecipe data (match by size- SHA-256, insert words); verify on the device that the log shows the module being fixed.
- Don't install or test on the Frame while the owner is playing: adding a library entry restarts Steam (FramePort now skips the restart when the shortcut is unchanged and waits while a game runs), and a launch test starts a game.
- Catalog YAML: a
:inside an unquoted value breaks the file and the loader skipped it silently (the game vanished from the list);tests/test_catalog_files.pynow catches it. - Keep the known-good APK and roll back if a change regresses (
PATCHED/_known-good-*).
When it can't run on the Frame
Stream PC VR instead: native Steam version if one exists (Espire 1: 669290; HITMAN WoA: 1659040; Sniper Elite VR),
or the Rift version through Revive (Journey of the Gods, Shadow Point). The catalog stores these as
pcvr_alternative. FramePort handles Rift games itself (next section).
Oculus Rift (PC VR) games
Scan a folder with Rift game dumps (Windows game folders) like Quest dumps; they get ids rift.<slug> and the
"PC VR (Proton and Revive)" patch group. Two ways to run them:
- This PC (
--to pc, GUI "Install on this PC"): a non-Steam shortcut in the local Windows Steam runsReviveInjector.exe /openxr "<game.exe>"(FramePort's portable Revive; on WSL copied to%LOCALAPPDATA%\FramePort). Play on the Frame by streaming from SteamVR. - The Frame (GUI "Install on Frame"): game + Revive are uploaded to
~/Applications/quest-frame/rift.*, and launch.sh runs them with the Frame's ARM64 Proton (install it first: Frame page → PC VR games (Proton) → Install…). Experimental.
| Symptom | Cause | Fix |
|---|---|---|
| "Proton isn't installed on the Frame yet" | ARM64 Proton / Steam Linux Runtime 4 (arm64) not downloaded | Frame page → PC VR games (Proton) → Install… (frameport frame proton --install; --in-headset only asks Steam, confirm in the headset) |
Game quits at once; ovrPlatformInitialize_NotEntitled / entitlement failed |
Oculus Platform SDK entitlement check (FramePort flags these: "Uses the Oculus Platform SDK") | PC mode with the Oculus app installed and a license you own. FramePort doesn't bypass entitlement checks |
Failed to create process in ReviveInjector.txt |
wrong exe, or 32/64-bit mismatch | check the detected exe on the game page; rescan |
XR_ERROR_RUNTIME_UNAVAILABLE / no OpenXR runtime (PC) |
SteamVR not running / not the OpenXR runtime | start SteamVR, set it as OpenXR runtime; or pcvr.revive_openvr |
XR_ERROR_EXTENSION_NOT_PRESENT / xrConvertTimespecTimeToTimeKHR failed (Frame) |
ReviveXR needs XR_KHR_win32_convert_performance_counter_time; wineopenxr builds it on the host's timespec conversion. The Frame's Linux runtime supports it (2026-09-29), unlike its Android runtime |
pcvr.xr_timefix (off by default): FramePort's timefix OpenXR layer emulates it like the Quest adapter |
Proton game hangs at start in a headless/SSH launch; log no driver could be loaded / explorer process failed to start |
no display session (DISPLAY/GAMESCOPE_WAYLAND_DISPLAY) | the Proton launch.sh takes them from the running Steam (built in) |
Unreal game starts (window created) then CrashReportClient runs |
the game crashed under Proton; often no VR runtime reached it (Revive off) | keep pcvr.revive on (repacks' bundled LibRevive64.dll isn't loaded by itself); read the game log + crash summary in the launch log |
VK_ERROR_DEVICE_LOST under DXVK |
freedreno GPU hang | pcvr.proton_log to capture; PC mode |