Doom3Quest-based ports (PreyVR, #306) draw their menu on a curved
cylinder layer submitted once per eye. The Frame's runtime has no
cylinder layers, so FrameBridge shows each as ~7 flat quad strips; a
per-eye pair is ~14 quad layers in one frame, which the runtime rejects
whole (xrEndFrame -> XR_ERROR_LAYER_INVALID, -16). The menu then stays
black while the game runs at 72 fps.
Collapse the pair: drop the right-eye cylinder and show the left eye's
strips to both eyes (the runtime composites a quad to both eyes anyway).
That halves the layer count and drops the per-eye eyeVisibility, and the
runtime accepts the frame. Only frames with per-eye cylinders change --
exactly the ones that were being rejected; mono (BOTH) cylinder games
such as 4XVR are untouched.
Verified on the dev Frame (headless): PreyVR v1.2.4 rebuilt + installed,
the menu frame now submits (one transient -16 on the first frame before
the space is locatable, then clean), 72 fps. Visual confirmation of the
menu needs the headset. Triage signature cylinder-menu-layers-rejected.
No adapter revision bump (installed games are not marked outdated).
Headset-confirmed working configs shared through the issue form:
Tea For God (#295), Ghost Town (#299), Eye of the Temple (#302),
Ocean Rift (#304), Quantum Void (#307), AugmentedEmpire (#311),
Battlemarked (#312), Demeo (#313), Homeworld: Vast Reaches (#314),
Moss (#315), Maestro (#316), Mini Motor Racing X (#317),
Operation Wolf Returns (#318), Ragnarock (#319), Smash Drums (#320),
Vendetta Forever (#321).
Quantum Void, Homeworld: Vast Reaches and Ragnarock go in as status: issues.
Turns on frame.unreal_skip_obb_delivery (and keeps frame.hw_video_decode) so
the fix applies without the user finding the toggle. Not device-verified (the
game is ~41 GB); status unknown, min_app 1.1.2 so only builds that recognise
the obfuscated DownloaderActivity get it.
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.
Merged main (agent 90, ANALYSIS_VERSION 17/18 after main's 15/16) and rebuilt the bridge, foveation and a32
libraries from source (deterministic, no local paths).
Fixes on top:
- a32 loader: apply DT_ANDROID_RELR relocations (Journey of the Gods crashed at start)
- VrApi bridge: vrapi_Get/SetTrackingTransform (floor-level tracking; Shadow Point's player was below the floor)
- a32: never submit an untracked default head pose (hold up to 100 ms, repair or drop); pose consistency per display
time; submit/tracking counters
- VrApi bridge: wait again when xrWaitFrame gives a display time in the refresh just submitted (the runtime rejected
the second frame with AlreadySubmitted; Journey of the Gods ran at ~120 fps on a 90 Hz display and the scene slid
and snapped back); pacing counters
- VrApi bridge: GLES re-entry polls events before reusing the kept session (a lost or exiting session is replaced, a
stopped one waits for READY)
- VrApi bridge: STAGE is the runtime's STAGE space (play-area centre and orientation, unchanged by recentering);
LOCAL_FLOOR keeps the head-based origin
- signer certificate: pick the certificate the SignerInfo names, not the first in the PKCS#7 block
- flat 32-bit apps keep the bridge (it isn't a VR-only fix)
- Frida gadget loads are stripped before translation
- launcher restart test fixed; native/.cache is no longer committed
- catalog: Shadow Point and Journey of the Gods work (headset, 2026-10-14; min_app 1.1.2)
- Check the Steam shortcut on every update, also when nothing else changed: a connection lost during
"Add to Steam" or Steam putting its old shortcuts.vdf back left games without a library entry. The
agent restarts Steam only when the entry really changes.
- Artwork updates for installs without a recorded artwork manifest start from a clean folder again
(an old portrait.jpg next to a new portrait.png was picked at random for the Steam grid).
- OpenSSH uploads only rename .part files that are already complete (sftp's reput refuses them, which
sent every resume to the Paramiko fallback).
- frame.tbxr_vendor revision 2: GL4ES ports get "Update on Frame" for the #191 draw fix.
- Configuration-only updates send the library's current Steam tags.
- finalize drops fingerprints and the artwork record an older app didn't send.
- set_settings: malformed numbers are refused before anything is written; int settings keep a
fractional value.
- Display diagnostics use Windows PowerShell 5.1 without pwsh's PSModulePath; window diagnostics and
the instance owner file can't stop the GUI from starting.
Sports Scramble (a 32-bit Santa Cruz build: UE 4.21, OVRPlugin on VrApi 1.21)
now plays in the headset with both controllers.
a32 bridge:
- AAsset_getBuffer: the host buffer lies above 4 GiB, so the guest gets a
copy in its own memory, freed in AAsset_close (Unreal read its
UE4CommandLine.txt this way and aborted on the NULL it got before)
- AKeyEvent_getFlags and the remaining AConfiguration getters
- Touch controllers for Quest builds: apps that require 6DoF head tracking
(uses-feature android.hardware.vr.headtracking version 1) get
assets/fp_a32_headset.txt = "quest" at conversion; the bridge then passes
both Frame controllers through (input devices, caps, state, tracking,
vibration; struct tails per VrApi minor) and sends no synthetic touchpad
events. Gear VR / Go games keep the remote emulation. A32_TOUCH=1/0
overrides.
Analysis: detect.requires_6dof, headset "quest" for such 32-bit apps
(ANALYSIS_VERSION 13).
Agent 82: Unreal restarts the app once after its first-run shader precompile
(the "optimizing" screen): it starts its own launcher activity and exits, and
Lepton ends the container with the first process. Skipping the restart or
turning it off (r.ProgramBinaryCache.RestartAndroidAfterPrecompile=0) left
the game on that screen for good. launch.sh now starts Lepton once more when
the system log shows the app's own LAUNCHER start right before its
System.exit(0) (_restart_requested; first run kept as
launch-before-restart.log; FRAMEPORT_NO_RESTART=1 turns it off).
upgrade_launchers adds it to existing launchers. Crashes, quitting from the
game and Steam's Exit game don't match.
Catalog: Sports Scramble works (owner, headset, 2026-10-13).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gunjack (CCP, Unreal Engine 4.10 NativeActivity, VrApi, GLES) runs on the
Frame through frame.a32_bridge, confirmed in the headset by the owner
(2026-10-12). The recipe needs only frame.a32_bridge: the engine adds the
adapter and the LAUNCHER fix and turns OVRPort off, the same patch set as
the build now on the dev Frame.
Notes give the controls (right controller): trigger = fire, A = tap, stick
down = reload (swipe back), stick up = special weapon (swipe forward),
B = back. GAMES.md and the patch docs regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32-bit-only APKs (Gear VR / Oculus Go games) are no longer "unsupported": the
recipe engine suggests frame.a32_bridge, which converts the APK with the
translator bridge (convert() shared with bridge/convert.py) and ships
liba32bridge.so / liba32rt.so / liba32stub.so (native/build.py --only a32).
FramePort:
- engine._a32: overport off (OVRPort is 64-bit only; the patch ships OVRPort's
loader chain + its 32-bit platform dispatcher, overport.ensure_runtime_lib),
only frame.a32_bridge/adapter/launcher of the frame patches, 64-bit-only
adapter settings dropped, device.text_input_window for Unity; such patches
are hidden in the UI
- analysis extra.headset (ANALYSIS_VERSION 9): quest / go / gearvr ->
"Gear VR" / "Oculus Go" platform + Steam tag; catalog `headset:` field
- expects_obb ignores Unreal's OBB-inside-the-APK (assets/main.obb.png)
- catalog: com.fallen.affected, net.mobilemischief.spunkydungeon
Bridge / translator:
- VrApi 1.31 + Unity 2019 (no-config EGL contexts, LOCAL_FLOOR/STAGE tracking
space), GL buffer mappings per buffer object (fixes a ~10 MB/s guest leak and
garbled vertices), Go controller mapped to the right Frame controller
- native_app_glue imports (ALooper/AInputQueue/AConfiguration/AAsset*) for
UE4 NativeActivity games (Gunjack: runs, then quits on its own APK signature
check, which FramePort doesn't defeat)
- debugging aids: A32_BREAK, A32_STRACE to logcat, lifecycle/LeaveVrMode logs
vrapi-bridge: honours LOCAL_FLOOR/STAGE, picks a window/RGBA8 ES3 config for
no-config contexts; upstream patch updated, libvrapi.so rebuilt.
Verified on the Frame: Spunky Dungeon (Unity 2019.2, OVRPlugin 1.46) plays at
72 fps; AFFECTED The Manor still runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bHaptics' vest SDK asks for BLUETOOTH_CONNECT at start; on Lepton's Android 11 the permission screen takes the
foreground for a moment and the game pauses (Yupitergrad froze on its loading screen in the reporter's session).
PermissionUtils.requestBluetoothPermission returns at once (Dex.return_early). Suggested by analysis
bhaptics_permission (ANALYSIS_VERSION 14) and triage bhaptics-permission-prompt. On the dev Frame the prompt is gone;
whether it was the whole cause of the freeze is for the reporter to confirm.
Opt-in game setting (Picture -> Draw fades over the picture). Batman: Arkham Shadow fades by dimming its projection
layer with XR_KHR_composition_layer_color_scale_bias; with fade_quad=1 FrameBridge adds a black, alpha-blended quad
right after the dimmed layer (VIEW space, 1 m ahead), opacity = 1 - scale in 16 steps. One 8x8 swapchain, refilled
only when the step changes (Vulkan clear on the app's queue / GLES clear in its context); one swapchain per step hit
XR_ERROR_LIMIT_REACHED. fade_quad=2 also removes the layer's own scale. Device (headless, Batman): swapchain created,
level 16/16 on the first frame, no xrEndFrame errors, 72 fps.
The game showed a canvas being painted as its own compositor layer, which the Frame draws in front of everything.
PaintableMesh.HasCompositorLayerAvailable and ShouldUseCompositorOverlay return false (Cpp2IL), so the game's own
fallback draws the canvas in the scene (Klownicle's finding). Catalog recipe, PLAYBOOK row, test.
Bionic allows 128 pthread keys per process. Unity 6 takes about 40, and with Gecko, Firebase and the Frame's own
libraries Wander 0.3570 ran out: Firestore aborted at start ("cannot create thread specific key for
__cxa_get_globals()", #198, #202). Opt-in patch, suggested for Unity 6 with Firebase C++ or Gecko: libunity.so's
pthread_key_create/_delete and pthread_get/setspecific imports are renamed in place to fp_tkey_* (new
elf.rename_imports: undefined symbols only, shared names refused, versions set global) and libfp_tkeys.so
(native/tlskeys) is added to its DT_NEEDED; keys without a destructor become generation-tagged slots behind one
real key, everything else keeps real keys. Approach and the first helper by Klownicle (#198), reimplemented here.
Triage signature pthread-keys-exhausted, PLAYBOOK row, Wander's recipe updated to 0.3570.
- Agent 83: Valve's Lepton pastes the game's paths unquoted into shell commands (liblepton mounting.sh), so a game on
a card named "Steam Frame 512g" never got its internal storage (no saves, no SharedPreferences). The Quest launcher
now links the game folder to ~/.local/share/frameport/lepton-paths/<pkg> at every start and gives Lepton that path;
upgrade_launchers converts existing launchers, move and uninstall handle the link (#213, #214, #215, #221).
- frame.eos_keychain_loop (opt-in, offered by triage): Epic Online Services' resetKeychain returns at once, so a
keychain it can't write no longer loops forever (Beyond Sandbox, #214).
- Triage lepton-data-link-failed and eos-keychain-loop.
- CryEngine also from libAndroidLauncher.so + a .cryproject (Climb 1, #238); ANALYSIS_VERSION 12.