--update reuses the options an install was made with (saved to install.conf in the work dir) and
builds nothing when the Frame already has the newest release and Retro Rewind pack; --check only
reports. The installed release is stamped as .release-tag beside the game on the Frame.
An install built on the Frame itself (--frame local) also gets a systemd user service the game's
new Updates tab starts: the update runs in the background at low priority, writes each step and
the build percentage to a status file the tab shows, and reopens the game at the end if it was
closed. Progress does not go through Steam notifications: on the Frame, Steam receives them from
steam_notif_daemon but does not display them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyuWx5hNrMpf9R78FZFcbA
When an mbed TLS write timed out, SslWrite called DeleteWiiSocket, which
closed the game's socket and cleared its SSL slot while the game still
held both ids. A later SSL_NEW could then hand the same slot out under
the old id. Now a failed write marks the session failed and shuts the
host socket down; later reads, writes and handshakes on it fail until the
game tears the session down itself.
Ported from DarthMDev/Wiicompiled a2ecc0f (network_ssl.cpp part) and
fcf8646, both on upstream PR #258.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rLZ24cFcqTNBmb4w2dpSq
Every translated guest load and store tests RequiresCheckedAccess(). On
x86-64 and Windows that is a compile-time false, but on AArch64 it read
a global flag, which under PIE is a GOT load plus a byte load and a
branch on every access, re-read after every guest store. The flag is
only true on kernels with pages larger than 4 KiB.
The Steam Frame's SteamOS kernel uses 4 KiB pages (getconf PAGESIZE on
the headset reports 4096), so the native --headset steam_frame build now
folds the test away like x86-64 does. GuestFlat::Initialize checks the
page size at startup in that build and refuses to start with a clear
message if it is ever anything else. Android and other AArch64 builds
still probe at runtime.
Checked with clang --target=aarch64-linux-gnu -O2 -fPIE: a FlatRead32
loop loses both flag loads and the checked Memory::Read32 fallback.
Follows the finding from nx-mod/wiicompiled-nx 18ef16f6, which made the
flag hidden instead; a compile-time constant removes it entirely.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NKSMYZU43wUfYtm1sxGsC
Bootstrap seeding only created missing files, so a WC24 file that had
been emptied (nwc24dl.bin, nwc24fl.bin, misc.bin) stayed at zero bytes
on every boot. WC24 rejects a zero-length download or friend list and
MKW reports that as a save error, which matters now that #7 lets KD
open while offline. A file is re-copied only when it is empty and the
payload's copy is not, so player data is never replaced.
Adapted from nx-mod/wiicompiled-nx d0149e66. That version falls through
to copy_file with copy_options::none, which fails on the existing empty
file; this one overwrites in that case only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NKSMYZU43wUfYtm1sxGsC
On arm64 the AXWii mix (ramped mix-add, volume envelope, aux returns
and the big-endian bus marshalling) ran the scalar loops, since only an
AVX2 form existed. The Neon forms do eight 16-bit samples per step as
two int32x4 halves and keep the scalar loops for the tail.
The header part of rooklz's 43463622; its other changes are left out.
A new test checks every vector form against the scalar loops at every
tail length and at the ramp and clamp extremes; it passes under
qemu-aarch64 (NEON), with -mavx2 and with neither.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NKSMYZU43wUfYtm1sxGsC
The in-game settings bar rewrote Config.toml in place with a truncating
open, so a crash or a full SD card mid-save could leave it empty or cut
short. Settings are now written beside the file and renamed over it, and
a file that exists but can't be read is no longer rewritten from nothing.
Ported from BlackAndBlue95/Strikers-WiiCompiled 435daf3e (the
runtime_config.h part only).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NKSMYZU43wUfYtm1sxGsC
When no backend could create a window and a WebGPU device, aurora hit
an ASSERT and the process aborted with only SDL's last error in the log.
Keep the first adapter, device or window error, return it from
aurora_initialize with a new initializationStatus, and have the runtime
throw it so the existing fatal popup and fatal log record the reason.
ImGui shutdown now skips backends that never initialized, so the
shutdown that follows a failed start does not crash.
From upstream patchzyy/Wiicompiled 6f14bde (#244, KartPad batch).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
From patchzyy/Wiicompiled 6f14bde (#244), runtime and translator parts only:
- Keep KD and NCD devices available while networking is off; only IP and
SSL need the network.
- Run RFLiIsWorking's alarm pump in a private interrupt context so callback
register writes don't clobber the caller, and release the alarm recursion
guard before rescheduling.
- Share one LR continuation dispatch per translated function when it has
several continuation calls.
The renderer half of that commit is left out: it conflicts with the stereo
renderer, and upstream reverted part of it (e409d9f) for stutters.
(cherry picked from part of commit 6f14bde26a4ed6390d4f18e1b3a9c1be6819d589)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011tcyLz63pXjoYEwsFjfg8F
* Implement real TLS for non-Windows via vendored mbed TLS
Windows gets TLS for the guest network HLE's SSL ioctlvs for free from
Schannel; every other platform fell into a stub that always returned
failure, meaning any HTTPS-based network feature (WFC login, fetching
the Retro-WFC payload) silently could not work at all on those
platforms regardless of server availability.
Vendors mbed TLS 3.6.7 LTS under runtime/third_party/mbedtls (same
convention as Crypto++/pugixml - a real source checkout, not a
submodule/FetchContent download) and a standard Mozilla CA bundle
(runtime/assets/certs/cacert.pem, via curl.se's redistribution) copied
next to the built product the same way dsp_coef.bin already is.
Verified against real HTTPS servers: a valid certificate completes the
handshake and an HTTP round-trip; a known-expired certificate is
correctly rejected with a real X509 verification failure, not silently
accepted.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qmdewk7VfVVJTfCVd2WStu
* Fix TLS handshake hang and partial-write truncation on non-Windows
Add a POSIX socket timeout to match Windows' existing 15s one, plus a
deadline on the handshake retry loop itself, so a peer that accepts the
TCP connection but never sends TLS data can no longer hang the thread
forever. Also fix SslWrite to loop on partial mbedTLS writes instead of
returning the first partial count, and add mbedTLS to
THIRD-PARTY-NOTICES.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fetch mbedTLS from a pinned, checksum-verified release instead of vendoring it
Replace the committed mbedTLS source tree with a CMake FetchContent download
of the official mbedtls-3.6.7 release tarball, verified against its signed
SHA-256, matching how aurora-main's own dependencies (SDL, zlib, etc.) are
pulled in. Ships the compiled dependency instead of ~280 tracked upstream
files. CA bundle packaging and THIRD-PARTY-NOTICES.md coverage are unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Limit the mbedTLS dependency to the platforms that use it
The FetchContent block ran on every platform, including Windows, whose builds
configure with FETCHCONTENT_FULLY_DISCONNECTED=ON against the offline
dependency set from Launcher/Prepare-Dependencies.ps1 - which has no
mkw_mbedtls_upstream entry, so a clean Windows configure failed. Windows
compiles the Schannel path (network_ssl.cpp is `#ifndef _WIN32` for mbed TLS)
and never links mbed TLS, so nothing needs preparing there: the fetch, the
linkage and the cacert.pem copy are now guarded to non-Windows, while the
mkw::mbedtls alias stays defined everywhere so the link lines in
PublicProducts.cmake remain platform-independent.
Also copy cacert.pem alongside the installed executable in the Linux and macOS
publication paths (Launcher/local-build.sh and Launcher/macos/publish-app.command),
which already copied the other runtime assets but left the TLS root bundle in
the build directory, so published builds could not verify any certificate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Harden mbed TLS socket I/O handling
* delete wii socket
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: patchzyy <64382339+patchzyy@users.noreply.github.com>
(cherry picked from commit b59e035b872752df8bfc637bba79689c12abf292)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011tcyLz63pXjoYEwsFjfg8F
* Fix macOS controller platform, sync player indices, and preserve socket send error
- Preserve captured socket send error across diagnostic logging in network_socket.cpp
- Use SDL_GetPlatform() instead of hardcoded Windows in controller mapping wizard
- Synchronize Aurora and SDL player indices on port assignment and clear in pad.cpp
- Auto-assign Player 1 on macOS when unconfigured in input.cpp
* fix(input): refine macOS port 0 assignment and persistence logic
- Persist port 0 preference for newly connected controller even if already assigned player index 0
- Verify controller has no preference on other ports before assigning to port 0
(cherry picked from commit 85f250155f8f397c1c463dc51dd4dc2707371d06)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011tcyLz63pXjoYEwsFjfg8F
[vr] adaptive_resolution (off by default, in the VR tab) lowers the
race's eye resolution by 10% steps, down to 70%, while new eye frames
fall below 85% of the game's 60 FPS, and raises it again after three
seconds on time. Adapted from heurazy/Wiicompiled_VR-PLUS.
The eyes render into the top-left corner of the full swapchain image
and the projection layer shows only that corner, so the compositor
upscales them and no swapchain is recreated, unlike render_scale. The
Linux same-device bridge now copies an eye smaller than its target;
the Quest's bridge and layer already did. The PC's D3D12 path copies
whole eyes, so the setting is not offered on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sabhzu6VgoWcHtpSk4C9KZ
Nintendo's GXClearVtxDesc, and Aurora's, keep GX_VA_POS at GX_DIRECT. The
HLE mirror cleared it to GX_NONE, so the next draw could publish no
position attribute and abort shader generation while a scene loads.
Keep the mirror in step, and give the shader a zero position if a
stream still arrives without one.
Ported from heurazy/Wiicompiled_VR-PLUS (c2b2500).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sabhzu6VgoWcHtpSk4C9KZ
The checkbox is drawn on every platform, but its variable was declared
only under MKW_VR_STANDALONE, so the Windows runtime failed to compile
(settings_overlay.cpp: use of undeclared identifier 'g_vrRepeatFrames').
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
The first repeat_frames waited a millisecond for the eyes before spending a
refresh on the retained layer, so nearly every packet also cost a needless
repeat, the next packet missed the game's next frame, and the Steam Frame
showed about 35 new frames a second. The pacing thread now waits until
1.5 ms before the next wake, a period after the last xrWaitFrame returned,
which OpenXRRuntime records.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
SteamVR on the Steam Frame ran the game at half the 120 Hz display rate,
since render-first pacing submits only when a game frame is sealed, and
filled every other refresh itself even with Motion Smoothing off, which
doubled the HUD and the menu screen while the head turned. With the new
[vr] repeat_frames (default on for the Frame, off elsewhere, live), the
pacing thread waits a millisecond for the eyes and otherwise spends the
refresh on a keep-alive cycle that resubmits the retained layer with its
rendered poses, paced by xrWaitFrame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
A SIGSEGV on the Steam Frame logged only the guest state and 'Host stack
trace unavailable on this platform'. With glibc the handler now names the
faulting thread, its pc and lr, and a backtrace, each as module + offset for
addr2line. The Dawn provider also says when a local package (--dawn-package)
replaces the download instead of printing the URL it does not fetch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
MKW_VR_STANDALONE (runtime_config.h) names the standalone headsets: the
Quest, and the Steam Frame in its Android flavour and its native SteamOS
build. They share render scale 0.8, foveation medium, the GX thread,
the game's object culling, and the headset panel's foveation and
tracked-hands rows; passthrough stays Quest-only.
The Frame's native build also sets AuroraConfig::xrHeadsetOnly, so
Aurora neither presents the desktop window nobody watches nor renders
it past the last pass the eyes sample, which Android does always (4 to
6 ms of a 12 ms GPU frame on a Quest 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
Lepton's Vulkan driver has no external memory or sync fd extensions,
so the Steam Frame gets a native SteamOS build instead. Its backend is
the PC's: OpenXR creates Dawn's own Vulkan instance and device, and the
eyes are copied into the swapchain on Dawn's queue, with nothing shared
between devices.
- openxr_vulkan_win32 and Aurora's vulkan_win32_interop now compile on
desktop Linux. Dawn links statically there, so the bridge calls the
patched package's hooks directly, and exists only when the package's
aurora-dawn.json declares the Vulkan hook ABI (AuroraDawnProvider
turns AuroraVulkanAbi into AURORA_DAWN_VULKAN_HOOKS); a stock Dawn
keeps the stubs and VR falls back to the desktop.
- openxr_integration.cpp gains a Linux branch: XR_KHR_vulkan_enable2,
the timespec clock, refresh rate, performance level, the Frame
controllers and eye gaze; fragment density maps are requested on
Linux as on the Quest (fdm.cpp and gpu.cpp, AURORA_FDM=0/1 overrides).
- Controller motion uses XR_KHR_convert_timespec_time on any Linux.
- CMake: Vulkan headers are fetched for Linux OpenXR builds, the
headset option is MKW_HEADSET on Android and Linux, and Linux aarch64
builds take MKW_LINUX_CPU (cortex-x4 for the Steam Frame, else native).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
With [vr] eye_tracked_foveation (on by default on the Steam Frame, off
elsewhere) the runtime asks for XR_EXT_eye_gaze_interaction. When the
system reports an eye tracker, OpenXRInput binds the gaze pose and
locates it for each packet's display time, in the space the eye views
are located in; vr/eye_gaze.h turns it into tangents of each eye's own
view, which AuroraStereoFrame now carries (appended, after the existing
prefix).
Aurora centres the eye's fragment density map on the gaze snapped to a
cell of two map texels (about 3 degrees). Each eye keeps up to 32 maps,
one per cell looked at, so a glance back reuses its map; a new map is
bound once its upload completes, and until then the eye keeps the map
it had. Without a tracked gaze (a blink, no tracker, the setting off)
foveation centres on the forward direction exactly as before: the
forward maps are byte-identical.
Also logs every extension the OpenXR runtime offers at startup, so the
first Steam Frame session shows what SteamVR's Android runtime has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
SteamVR has no XR_FB_passthrough, so the Steam Frame build neither asks
for it nor offers the setting: [vr] passthrough defaults to false there,
and the F10/headset panel and the launcher hide the switch. The Frame
keeps the Android defaults that suit it (render scale 0.8, foveation
medium, the GX thread, object culling, performance level boost) and
starts at 120 Hz (previous commit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
[vr] refresh_rate (Hz, 0 = the headset's own) is requested through
XR_FB_display_refresh_rate each time the session starts running and
whenever the setting changes, matched against the runtime's own list
within half a hertz. Setting it back to 0 restores the rate the session
started at. A rate that is not offered, or one the runtime declines, is
logged and changes nothing.
The game renders 60 frames a second, so at 120 Hz every frame shows for
exactly two refreshes, where 72 or 90 Hz hold some frames longer than
others. The Steam Frame build defaults to 120; every other build keeps
the headset's own rate unless the player picks one.
The F10/headset panel and the Android launcher's Headset section offer
the choice; the interpolation tooltip points at it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
The Steam Frame runs Android apps through Lepton with SteamVR's OpenXR
runtime. A third headset flavour, steamFrame, targets it:
- -mcpu=cortex-x4+nosve for the Snapdragon 8 Gen 3 (its firmware does
not expose SVE, which clang otherwise auto-vectorises with), from one
flavour-to-CPU map that the kit export now reads instead of guessing
from the variant name.
- MKW_ANDROID_HEADSET=steam_frame defines MKW_HEADSET_STEAM_FRAME for
the runtime's own targets, for Frame-specific defaults.
- A manifest without the Horizon OS entries, and FrameEntryActivity as
the single real MAIN/LAUNCHER activity with the Khronos and Oculus VR
categories, which Lepton needs to start an app in VR. It opens the
setup panel and, when the selected game can start, the game on top.
- XR_VALVE_frame_controller_interaction: the Frame controller profile
with its left D-pad (new dpad_* actions, the Wii Remote's D-pad or the
gamepad's), View as menu and the left shoulder as the panel button.
Also requested on Windows for SteamVR streaming to a Frame.
- Build-Quest.ps1, Build-QuestGame.ps1 and Run-Quest.ps1 take
-Headset frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
- Updated stereo_frame_worker_smoke.cpp to allow dynamic headset rates and prediction lead time.
- Improved logging to include motion diagnostics and adjusted frame submission logic based on headset frequency.
- Enhanced stereo_interpolation_test.cpp with additional tests for camera motion separation and playback cadence.
- Introduced MkwVRReadSceneView function to read the camera view matrix for improved scene rendering.
- Modified VR first-person logic to support scene view reading and validation.
- Added scene_camera.hpp to encapsulate camera motion handling and inverse view calculations.
- Ensured that the VR integration layer correctly logs motion diagnostics and handles scene playback accurately.
- Introduced `vrCockpitItemThrow` configuration option to enable throwing items in VR.
- Implemented `ThrowDetector` and `ThrowSequence` classes to manage item throwing mechanics based on hand movements.
- Updated `OpenXRInput` to handle item throwing based on hand swings, integrating with existing VR input systems.
- Enhanced settings overlay to allow users to toggle item throwing functionality.
- Added tests for item throwing mechanics, ensuring correct behavior for various hand movements and conditions.
- Implemented DVDReadVrAsset function to read mapped disc paths for VR assets.
- Created HeldItem structure and ReadHeldItem function for managing held items in the game.
- Developed unit tests for ReadHeldItem to ensure correct functionality and edge case handling.
- Added cockpit item data tests to validate model indexing and parsing of archives.
- Introduced a new configuration option for object culling in VR, allowing the game to hide objects outside the camera's view.
- Implemented native replacements for the Mario Kart functions responsible for scene culling, ensuring accurate behavior in VR.
- Added a new header file `mkw_vr_culling.h` to define the culling logic and structures.
- Created `mkw_vr_culling.cpp` to implement the culling logic, including frustum intersection checks and screen info updates.
- Updated runtime configuration to include the new object culling option, with appropriate getters and setters.
- Enhanced the settings overlay to allow users to toggle object culling in VR.
- Added tests in `vr_culling_tests.cpp` to validate the frustum intersection logic and ensure compliance with original behavior.
- Updated CMake files to include new source files and tests.
- Added support for hiding model arrays in the rendering pipeline to optimize performance during VR gameplay.
- Introduced new functions in `gx_model_visibility` to manage hidden model arrays and their visibility based on game state.
- Enhanced cockpit recentering logic to ensure accurate seat measurements and eye positioning during gameplay.
- Updated `FirstPersonState` to track cockpit height and forward direction, improving VR experience.
- Added tests for Bullet Bill model visibility and cockpit height adjustments to ensure functionality and stability.
- Refactored existing code to accommodate new features and improve overall code organization.
- Added SetRenderScale method to both D3D12 and Vulkan backends to adjust the render scale dynamically.
- Updated eye size calculations to reflect the new render scale in both backends.
- Implemented ResizeWritablePair method to handle resizing of swapchains based on the requested eye sizes.
- Modified BeginFrame methods to ensure swapchains are resized appropriately before rendering.
- Enhanced error handling to maintain current eye sizes when the requested sizes cannot be allocated.
- Added tests to validate render scale functionality, ensuring correct behavior when scaling up and down, including handling of refused sizes.
- Started with the controllers connected, a race stayed on them: Horizon OS switches all input
between controllers and hands, and back to the controllers as soon as one lying on a table moves
(a Quest 3 log went touch_controller, simple_controller, touch_controller within seconds). An
app cannot disconnect the controllers.
- With tracked hands on, the input now resumes XR_META_simultaneous_hands_and_controllers (Meta's
multimodal), which overrides that switching: a controller not in a hand no longer owns it, so the
cameras track that hand at once, and a held controller keeps working. Paused again when the
option goes off; a refused resume is logged and not retried until the option is toggled.
- A hand is bare when its squeeze action is inactive and it either drives khr/simple_controller or
has camera-tracked joints, since a free hand under simultaneous tracking may get a profile our
actions are not bound in. Docs: OPENXR.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Now that holding the wheel engages the race controls, closing the other hand on the rim could
bring thumb and index together on the way and fire an item. A pinch uses an item only while the
hand's grasp is under 0.5; a relaxed hand reads about 0, a hand closed on a rim about 0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- On a Quest 3 the race controls never engaged: grabbing the wheel gave no gas and a right pinch
stayed A. They waited for the game to switch the remote's pointer off, and MKW keeps it on in a
race (the log only ever said "the game's pointer is on").
- The race controls now apply while a bare hand holds the wheel: that holds the gas, and a free
hand's pinch (either hand) uses an item. With no bare hand on the wheel a right pinch stays A,
which is what the pause menu and the results need. The flick still works in the whole cockpit.
- The game-pointer publication (KPAD hook and bridge) is gone. The "tracked hands" source line is
logged at most once a second: camera-tracked hands drop in and out of view often.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A bare hand already feeds no Wii Remote motion (camera-tracked poses are too noisy to
differentiate twice). In a cockpit race, both hands on the wheel rising together or a free bare
hand rising fast now plays one shake on the remote's accelerometer: 150 ms, so the guest sees it
on at least three frames, one cycle up to +2 g and down to the -3.6 g limit, as Dolphin's
emulated shake does. A turn (one hand up, the other down) never flicks, nor does a lone hand on
the wheel; a rise must last three samples and cover 5 cm within 150 ms, tracking jumps reset it,
and flicks are 0.5 s apart. Wii Remote presentation only.
- debug.wiicompiled.inject <n>:flick plays the same shake, with the controllers or unattended, to
tune it apart from the gesture. Docs: OPENXR.md, docs/quest-port.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- With tracked hands on, a hand driving khr/simple_controller with camera-tracked joints is bare:
latched through wheel_tracking_grace (Meta drops the select action while a hand is lost),
cleared as soon as a controller's squeeze is back. Its palm joint stands in for the grip and a
grasp from the middle, ring and little fingers' flexion (0 below 1.2 rad, 1 from 3.0) for the
squeeze, so closing a hand on the rim takes hold under the wheel's own press and release. The
grasp never reaches the game's buttons.
- In a cockpit race a bare hand holding the wheel holds the gas (A / South) and a pinch from a free
bare hand uses an item (Z / L) once the hand has been off the wheel for 0.15 s. Only while the
game has the remote's pointer off: KPAD publishes the game's pointer switch for the VR remote,
so the pause menu and the results keep a right pinch as A. The pacing thread logs each change,
to confirm on the headset that MKW turns the pointer off while driving.
- A held bare hand keeps its last joints drawn through a short loss; bare hands get no haptics.
The headset panel's readout shows grasp, hold and pinch. Docs: OPENXR.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New [vr] hand_tracking (default off, Quest only for now): the Quest launcher's Settings > VR and
the headset panel's VR tab, under hand steering. Two XR_EXT_hand_tracking trackers are located
every XR frame: with the controllers held the Quest builds the joints from their touch sensors
(XR_EXT_hand_tracking_data_source's controller source), once they are put down from its
cameras. The trackers exist only while the option and hand steering are on and also serve the
runtime hand mesh; the extensions (plus XR_FB_hand_tracking_aim) are asked for when either is
on at launch.
- Aurora skins the runtime mesh with the joints themselves (tracked pose times inverse bind pose,
no curl, no grip); runtimes with joints but no mesh get a skeleton; non-finite joints put only
that hand back on its grip curl. AuroraCockpitHand carries the 26 seated-frame joints and radii.
- The manifest declares horizonos.permission.HAND_TRACKING (and the deprecated
com.oculus.permission.HAND_TRACKING), both normal permissions with no prompt on a Quest 3, and
oculus.software.handtracking as optional; without it Horizon OS keeps the app controllers-only.
Bare hands then drive khr/simple_controller, so on Android a hand whose squeeze action is
inactive and select active is treated as bare: with the option off it presses nothing but the
menu gesture, is not drawn and feeds no Wii Remote motion.
- Interaction-profile changes and tracker sources are logged. The pure rules (grasp from finger
flexion, bare latch, pinch gate, bare-hand buttons, flick) live in the OpenXR-free
vr/openxr_hand_tracking.h with mkw_vr_hand_tracking_tests; the driving and flick parts are
wired in the next commits. Docs: OPENXR.md, docs/quest-port.md, README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The pacing thread aims each immersive window eye through the window itself (AimEyesThroughWindow):
it keeps its position but looks square-on at the window's plane through an off-axis frustum just
around it, so the eye image is the window, at the display's pixel density (about 680x380 per eye
at render_scale 0.8 instead of 1344x1408), with a two-pixel border the mask leaves transparent.
The frame's views carry that pose and field of view to the projection layer.
- vulkan_interop.cpp copies an eye smaller than its AHardwareBuffer into the buffer's corner, and
the Quest layer's imageRect is the rendered part of the swapchain image.
- These eyes are not foveated: their field of view follows the head, which would rebuild the
density map every frame.
- Quest only (kWindowShapedEyesSupported); the PC backends copy whole eyes and keep masking them.
debug.wiicompiled.window_eyes 0 renders them whole and masked again for A/B timing.
- Quest 3, paused Retro Rewind race, render_scale 1.0: Immersive window runs the GPU at level 1
(456 MHz, app GPU 11.0 ms, eyes 8.4 ms) where Immersive needs level 3 (599-640 MHz, 13.4 ms,
10.3 ms), about 42% fewer GPU cycles. No edge artifacts, image as sharp. Docs: OPENXR.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Introduced a new configuration option for immersive window mode in runtime_config.h.
- Updated the parsing and setting functions to handle the immersive window state.
- Modified the OpenXR backend to support rendering with the immersive window, blending the race view with the surrounding environment.
- Enhanced the settings overlay to allow users to select between immersive, immersive window, and flat screen race views.
- Implemented GPU rendering logic for the immersive window mask, ensuring correct visual output in various rendering paths.
- Added tests to validate the immersive window functionality and its interaction with existing race view settings.
- Eyes render under a VK_EXT_fragment_density_map: full rate around each eye's forward direction,
2x2 then 4x4 pixel blocks towards the edges ([vr] foveation = off|low|medium|high, default off).
XR_FB_foveation cannot help here: the runtime's maps only shape passes drawing into its
swapchain, and the eyes reach it through a copy.
- aurora-main/patches/dawn/aurora_fdm.inc: Dawn enables the extension only on request and for
dynamic rendering, flags every render pipeline, and chains an immutable RG8 map into any pass
whose first color attachment is a view bound to one (ABI: include/aurora/dawn_fdm_abi.h).
- android/Build-QuestDawn.ps1 builds the pinned Dawn revision with those patches for arm64
(dawn-build CI flags, protobuf off) into a cached package; Build-Quest.ps1 links it
(-StockDawn opts out) and AuroraDawnProvider.cmake enables the ABI from its manifest.
- lib/gfx/foveation.hpp generates the maps (32 px per texel, densities 255/127/63); an eye is
foveated only when single_pass_eyes draws it in one render pass. Menus never are.
- Live level from the headset panel's VR tab and the launcher; the launch decides whether the
device has maps. debug.wiicompiled.foveation and debug.wiicompiled.fdm for A/B.
- Tests: Foveation cases in gx_fifo_tests, mkw_vr_config_tests. Docs: OPENXR.md, quest-port.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>