diff --git a/.gitignore b/.gitignore index 1c69c0a..10356c5 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ project.lock.json /tools/ /TEMP/ /tmp/ +/.scratch/ /test_output/ *.log output.txt diff --git a/Launcher/Build-Installer.ps1 b/Launcher/Build-Installer.ps1 index 41662b7..f547d4c 100644 --- a/Launcher/Build-Installer.ps1 +++ b/Launcher/Build-Installer.ps1 @@ -98,7 +98,7 @@ Assert-Directory $dependencySources 'Pinned offline dependency sources' # to compile (launcher/Prepare-NativePrebuilt.ps1). # Kept in step with InstalledLayout.DependencyNames by Test-PinnedFacts.ps1: the installed host # refuses to call a toolkit complete unless every one of these directories is present. -$requiredDependencies = @('abseil-cpp','cppwinrt','dawn_prebuilt','fmt','freetype','imgui','libusb','native_prebuilt','png','SDL','sqlite3','tracy','xxhash','zlib','zstd') +$requiredDependencies = @('abseil-cpp','cppwinrt','dawn_prebuilt','fmt','freetype','imgui','libusb','native_prebuilt','openxr','png','SDL','sqlite3','tracy','xxhash','zlib','zstd') # The precompiled archives are only interchangeable with what the user's machine # compiles if both came from this toolchain and this flag set, so a stale package @@ -225,6 +225,7 @@ Write-Host '[3/6] Writing manifests and third-party license inventory...' Copy-Item (Join-Path $portableTools 'README-LICENSES.txt') (Join-Path $payloadRoot 'licenses\Portable-build-tools.txt') Copy-Item (Join-Path $repoRoot 'aurora-main\LICENSE') (Join-Path $payloadRoot 'licenses\Aurora-LICENSE.txt') Copy-Item (Join-Path $dependencySources 'cppwinrt\LICENSE.txt') (Join-Path $payloadRoot 'licenses\CppWinRT-LICENSE.txt') +Copy-Item (Join-Path $dependencySources 'openxr\LICENSE') (Join-Path $payloadRoot 'licenses\OpenXR-SDK-LICENSE.txt') # The precompiled aurora/third-party archives are built from the very sources # already shipped under build-workspace\Dependencies and aurora-main, so they add # no third-party component and therefore no new license obligation. diff --git a/Launcher/LocalBuild.ps1 b/Launcher/LocalBuild.ps1 index b4538be..cf8c399 100644 --- a/Launcher/LocalBuild.ps1 +++ b/Launcher/LocalBuild.ps1 @@ -189,12 +189,13 @@ $pins = Get-MkwProjectPins $project # (the real RAM guard, capping concurrent clang compiles of memory-hungry translated TUs via the Ninja # MKW_TRANSLATED_COMPILE_JOBS pool), and $globalJobs (Ninja's overall parallelism for everything else). # An explicit -Parallel pins all three. -$memoryGiB = [math]::Max(1, [math]::Floor((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB)) if ($Parallel -gt 0) { $translatorThreads = $Parallel $translatedJobs = $Parallel $globalJobs = $Parallel } else { + $memoryGiB = [math]::Max(1, [math]::Floor( + (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB)) $translatorThreads = [math]::Max(1, [math]::Min([Environment]::ProcessorCount, 16)) $translatedJobs = [math]::Max(1, [math]::Min([Environment]::ProcessorCount, [math]::Floor($memoryGiB / 2))) diff --git a/Launcher/WiiCompiled.Setup.Windows/InstalledLayout.cs b/Launcher/WiiCompiled.Setup.Windows/InstalledLayout.cs index 598343e..03521d6 100644 --- a/Launcher/WiiCompiled.Setup.Windows/InstalledLayout.cs +++ b/Launcher/WiiCompiled.Setup.Windows/InstalledLayout.cs @@ -27,7 +27,7 @@ internal static class InstalledLayout public static readonly string[] DependencyNames = [ "abseil-cpp", "cppwinrt", "dawn_prebuilt", "fmt", "freetype", "imgui", "libusb", "native_prebuilt", - "png", "SDL", "sqlite3", "tracy", "xxhash", "zlib", "zstd" + "openxr", "png", "SDL", "sqlite3", "tracy", "xxhash", "zlib", "zstd" ]; } diff --git a/OPENXR.md b/OPENXR.md new file mode 100644 index 0000000..0d08d01 --- /dev/null +++ b/OPENXR.md @@ -0,0 +1,95 @@ +# Experimental OpenXR VR + +WiiCompiled has an opt-in OpenXR rendering path. The first functional backend is Windows D3D12. +It asks the OpenXR runtime for the required GPU before Aurora creates Dawn, then copies each eye +on that same D3D12 device and queue into the acquired OpenXR swapchain images. Eye submission +stays on the GPU; there is no CPU texture readback and no second graphics device. + +This is an experimental renderer, not yet a release-ready VR mode. + +## Requirements + +- A Windows OpenXR runtime selected as the system's active runtime. +- A connected headset supported by that runtime. +- A D3D12-capable GPU and driver accepted by both OpenXR and Dawn. +- A build made with `MKW_ENABLE_OPENXR=ON`, which defaults on for Windows and off elsewhere while + the Vulkan bridge remains capability-gated. + +OpenXR remains disabled until requested in `Config.toml`. The file is next to the installed game +configuration and is created with the following defaults: + +```toml +[vr] +enabled = false +required = false +render_scale = 1.0 +world_units_per_meter = 500.0 +hud_distance_meters = 2.0 +stop_at_display_copy = true +skip_copy_clears = true +``` + +Set `enabled = true`, close the game completely, and start it again. These settings are read only +at launch. The in-game F10 settings bar also exposes the enable switch, but a restart is still +required. + +`required = false` is the safe default: an absent runtime, disconnected headset, unsupported GPU, +or graphics-binding failure is logged and the game continues in ordinary desktop mode. Set it to +`true` only when a failed VR startup should stop the game with an error. + +`render_scale` scales the per-eye size recommended by the OpenXR runtime. +`world_units_per_meter` controls the scale of headset translation in the game world. +`hud_distance_meters` controls the distance of the head-locked virtual screen. +`stop_at_display_copy` ends eye replay at the final `GXCopyDisp`, matching the frame shown on the +desktop. `skip_copy_clears` independently suppresses the EFB reset performed after a copy. Both +default on and can be changed live from the F10 settings bar for diagnostics. + +## Presentation policy + +The runtime deliberately fails safe instead of guessing which Mario Kart camera is active: + +- Menus, loading screens, unclassified scenes, and multiplayer render on a head-locked virtual + screen. +- A PAL `RMCP01` race scene switches to immersive stereo only after translated-code observers + confirm exactly one distinct race camera for the current GX frame. +- Leaving the race or observing zero or multiple cameras immediately returns presentation to the + virtual screen. Session/runtime loss safely tears down XR and continues on the desktop mirror. + +Aurora records the original GX frame once and replays it for both OpenXR eyes. Perspective GX draws +receive asymmetric headset projections; orthographic and unclassified draws retain their original +GX transforms during immersive replay. Menus and unsafe whole scenes use the virtual-screen path. +Head pose is sampled by the OpenXR pacing thread, while Aurora's frame worker consumes a +short-lived immutable stereo packet. Each sealed GX frame and immersive packet carry the same +policy-generation tag; a mismatch is rendered in mono and the acquired XR frame is canceled, so an +asynchronous menu/race transition cannot replay race transforms over unsafe content. A pause or +minimized window can withdraw an unencoded packet and end that compositor frame without layers; an +encoded-work stall requests teardown at Aurora's next safe producer boundary. All OpenXR session +and swapchain calls remain on their owning thread. + +## Backend status + +| Backend | Status | +| --- | --- | +| Windows D3D12 | Implemented: same-adapter, same-device asynchronous OpenXR submission. | +| Linux Vulkan | Capability-gated scaffold. The pinned Dawn package does not expose the complete native Vulkan instance/device/queue context needed for safe same-device OpenXR interop, so the runtime logs the limitation and falls back to desktop rendering. | +| Other platforms | Not wired yet. | + +The Vulkan path intentionally does not create an unrelated Vulkan device or use a CPU readback as +a workaround. It accepts a future explicit Dawn native context, including external queue locking, +so it can be enabled once Aurora exposes those handles safely. + +## Current limitations + +- Only the project's supported PAL `RMCP01` translation has race instrumentation addresses. +- Motion-controller/Wii Remote emulation and OpenXR action bindings are not implemented yet; use + the existing game-controller input path. +- Dedicated Quest, Android, and Apple visionOS packaging is not implemented. The static recompilation + architecture avoids a runtime JIT, but each platform still needs an Aurora graphics bridge, + windowing/lifecycle work, and packaging. +- Scene-specific comfort options, culling fixes, replay/spectator classification, and a broader VR + settings UI beyond the current enable/replay controls are future work. +- The desktop window remains available as a mirror/fallback. + +OpenXR diagnostics are written to the normal run log under +`%LOCALAPPDATA%\WiiCompiled\Logs`. Search for `OpenXR` when reporting a startup or submission +failure. diff --git a/README.md b/README.md index 8dd8426..0895bcc 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ The graphics layer is built on **High internal resolution.** Play at several times the console's resolution. +**Experimental OpenXR VR.** +Windows builds can render through a D3D12 OpenXR runtime without CPU readback. Menus and +unsupported scenes appear as a head-locked virtual screen; a validated single-camera race switches +to immersive stereo rendering. VR is opt-in and falls back to the normal desktop renderer if the +runtime or headset is unavailable. See [`OPENXR.md`](OPENXR.md) for setup, configuration, and the +current limitations. + **Music ducking.** Start playing something else, Spotify, a YouTube video, and the game automatically mutes its own music until the other audio stops. Optional, if you'd @@ -178,6 +185,8 @@ All translated output is verified against real hardware behavior and most import project's whole graphics layer sits on. MIT licensed. - **[Dawn](https://dawn.googlesource.com/dawn)** - Google's WebGPU implementation, powering aurora's Direct3D, Vulkan and OpenGL backends. +- **[OpenXR](https://www.khronos.org/openxr/)** - the Khronos cross-platform API used by the + experimental VR renderer. - **[Dolphin Emulator](https://github.com/dolphin-emu/dolphin)** - an invaluable reference for Wii hardware behavior during development, plus the source of the free DSP coefficient ROM and the unmodified default WiiConnect24 bootstrap tree bundled with the runtime. diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index 6214087..c297c89 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -141,6 +141,7 @@ included in the installer's `licenses/` folder. | SQLite | 3.51.3 amalgamation | Public domain | | | Tracy Profiler | pinned commit | BSD-3-Clause | | | C++/WinRT | - | MIT (Microsoft) | | +| OpenXR-SDK | 1.1.61 | Apache-2.0 | | | nodtool (disc image extraction) | v2.0.0-alpha.10 | MIT OR Apache-2.0 | | ### Dual-licensed components - elections made by this project