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.
The GLES-only stand-in left Vulkan games (UNDERDOGS: VkFormat 43, faces=6) with the runtime's refusal, so OVRPlugin
crashed in ovrp_EndFrame4. When there's no GLES context, the adapter now makes a cube-compatible VkImage (6 layers,
the requested format/size/mips, usage from the XR flags) with the app's device/queue and a private transient pool,
transitions it to COLOR_ATTACHMENT_OPTIMAL, and enumerates it as a Vulkan image; its cube layers are still dropped.
GLES behaviour is unchanged. Adapter rebuilt deterministically (both ABIs).
After "Waiting for app", a failure line or 20 s without a logcat line start FramePort's own reader, from the
launcher's start (logcat -T <epoch>) when the mirror never wrote, else from just before its last line. The reader
restarts from where it ended, stops when Lepton's lines come back, the container's log buffer is 16 MiB, and every
decision goes to logcat-keeper.log.
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.
Holding the Frame's menu button turned 32-bit (and other VrApi bridge)
games 90-120 degrees to the side. The bridge's app space is LOCAL plus
the head's yaw at the start; the runtime's recenter moves LOCAL to the
head, so that old yaw was applied again. On a LOCAL reference space
change (and on vrapi_RecenterPose, ignored before) the origin is taken
from the head again in the first frame after the change, and VrApi's
recenter count goes up. Owner-verified with Gunjack.
The upstream patches are regenerated against OVRPort 5e7df52 (they
were missing the FOV cache changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Gunjack (UE 4.10, Gear VR) has no separate reload or special-weapon input on
the touchpad: every swipe goes to UE4's Gesture_Flick, and
ATDPlayerController::OnFlick reads the flick angle. Below 45 or above 315
degrees (+X, a swipe towards the back of the Gear VR touchpad) it reloads;
135 to 225 degrees (-X, a swipe forward) fires the special weapon. Up and down
swipes do nothing. The bridge sent the stick's up/down as Y swipes, so in
Gunjack those directions did nothing.
For games that read their AInputQueue (NativeActivity), the right stick's
up/down now swipe along the touchpad's own X axis: up = swipe forward
(special weapon), down = swipe back (reload). Left/right are unchanged.
Unity games (AFFECTED, Spunky Dungeon) keep their mapping.
Rebuilt liba32bridge.so. Gunjack was rebuilt and installed on the dev Frame;
the headless launch test passes. Not yet tried in the headset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gunjack (UE 4.10, Gear VR) imports no vrapi input functions: it reads the
Gear VR touchpad and back button as Android input events from its
AInputQueue (native_app_glue). The bridge delivered its controller events
through Activity.dispatchKeyEvent, which never reaches native code, so no
control worked at all.
input.c now queues synthetic events in front of the game's AInputQueue:
- AInputQueue_attachLooper (without a callback) registers the queue;
ALooper_pollAll/pollOnce return its ident while a synthetic event waits
(ALooper_wake ends a blocking poll), AInputQueue_getEvent hands it out,
finishEvent frees it.
- The AInputQueue/AInputEvent/AKeyEvent/AMotionEvent imports are written
by hand ("!" in imports.txt) and answer for synthetic events; real
events go to libandroid as before.
- Touch positions are scaled to the activity's window size
(native_activity.c records it).
Mapping (right controller, as for AFFECTED: The Manor): trigger = hold
the touchpad, A = tap, right stick = swipe, B / left menu = back. Unity
and other Java-delivered games are unchanged. A new input_start (VR
re-entry) ends the previous poll thread.
Also: the libandroid functions the host side calls directly are weak
references again, so the PC test harness links (it broke with the
AAsset calls of the signature loader); bridge/test/run.py passes.
Headless on the Frame: "controllers: the game reads its AInputQueue",
72 fps. Not yet verified in the headset.
Gunjack (CCP, UE 4.10, Gear VR) quit after loading because AndroidMain compares
GameActivity.AndroidThunkJava_GetSignature() with the original signing
certificate, and then crashed in the Frame's runtime when it entered VR again.
a32 bridge:
- frame.a32_bridge: original_signature() = Base64.DEFAULT(SHA-1(signing
certificate DER)) of the original APK (META-INF/*.RSA|DSA|EC), stored as
assets/fp_a32_signature.txt for libUE4.so games (convert(), shared with
bridge/convert.py)
- the bridge loads it at activity creation and answers guest calls on the
method ID of AndroidThunkJava_GetSignature()Ljava/lang/String; with it
(replaces the unused hex/OpenSSL prototype)
VrApi bridge: after loading, Unreal leaves VR mode and enters it again on the
same GL context; the first frame of a second OpenXR session there aborted in
vrclient (CSharedSemaphoreLinux::WaitFromGPU from the GL binding's
PreBeginFrame). vrapi_LeaveVrMode now keeps a running GLES session and
vrapi_EnterVrMode reuses it for the same context (another context still gets
a new session; Vulkan unchanged). upstream-patches/runtime.cpp.patch updated,
libvrapi.so and liba32bridge.so rebuilt.
Verified on the Frame: headless launch test passes (72 fps after re-entry);
owner-verified in the headset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>