From 0edf10b22010fd311e8d729e50472b05baa5a153 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 09:51:08 +0000 Subject: [PATCH] Give the Steam Frame's native build the standalone headset defaults 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 Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3 --- aurora-main/include/aurora/aurora.h | 4 ++++ aurora-main/lib/aurora.cpp | 2 +- runtime/include/runtime_config.h | 18 +++++++++++++----- runtime/src/settings_overlay.cpp | 14 +++++++------- runtime/src/vr/openxr_integration.cpp | 5 +++++ runtime/tests/vr_config_tests.cpp | 4 ++-- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/aurora-main/include/aurora/aurora.h b/aurora-main/include/aurora/aurora.h index 46bec06..ff9120f 100644 --- a/aurora-main/include/aurora/aurora.h +++ b/aurora-main/include/aurora/aurora.h @@ -289,6 +289,10 @@ typedef struct { bool hasD3D12AdapterLuid; uint32_t d3d12AdapterLuidLow; int32_t d3d12AdapterLuidHigh; + // Nobody watches the desktop window while the headset runs (a standalone + // headset such as the Steam Frame's native build): skip presenting it and stop + // the mono render after the last pass the eyes sample, as Android always does. + bool xrHeadsetOnly; } AuroraConfig; typedef struct { diff --git a/aurora-main/lib/aurora.cpp b/aurora-main/lib/aurora.cpp index 961cfae..3ab35d1 100644 --- a/aurora-main/lib/aurora.cpp +++ b/aurora-main/lib/aurora.cpp @@ -1416,7 +1416,7 @@ bool headset_owns_display() noexcept { #if defined(__ANDROID__) return stereo_frame_provider_active(); #else - return false; + return g_config.xrHeadsetOnly && stereo_frame_provider_active(); #endif } diff --git a/runtime/include/runtime_config.h b/runtime/include/runtime_config.h index 7f9466d..068c124 100644 --- a/runtime/include/runtime_config.h +++ b/runtime/include/runtime_config.h @@ -1,5 +1,12 @@ #pragma once +// The standalone headsets: the Quest (Android) and the Steam Frame, in its Android flavour and its +// native SteamOS build. They render on a mobile GPU with a game thread that is the bottleneck, so +// they share the defaults below and the headset panel's foveation and tracked-hands rows. +#if defined(__ANDROID__) || defined(MKW_HEADSET_STEAM_FRAME) +#define MKW_VR_STANDALONE 1 +#endif + #include #include #include @@ -193,7 +200,7 @@ inline constexpr uint32_t kDisabledPostProcessingPathsDefault = kPostProcessingB // standalone headset renders on a mobile GPU, so the Quest and the Steam Frame // start below it; the launcher's first Config.toml (GameStorage.kt) writes the // same value. -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) inline constexpr float kVrRenderScaleDefault = 0.8f; #define MKW_VR_RENDER_SCALE_DEFAULT_TEXT "0.8" #else @@ -256,7 +263,7 @@ inline constexpr bool kVrCockpitItemThrowDefault = true; // draws them by default; the Quest keeps the game's culling, since every // extra model costs its GPU twice. The macro is the same default for the // config file written on first launch. -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) inline constexpr bool kVrObjectCullingDefault = true; #define MKW_VR_OBJECT_CULLING_DEFAULT_TOML "true" #else @@ -310,7 +317,7 @@ inline bool IsSupportedVrRefreshRate(uint32_t value) { // from "off" to a level takes a restart; between levels and back to "off" it is // live. The standalone headsets (Quest, Steam Frame) start at "medium"; // elsewhere it does nothing. -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) inline constexpr const char* kVrFoveationDefault = "medium"; #else inline constexpr const char* kVrFoveationDefault = "off"; @@ -1646,9 +1653,10 @@ inline bool ShowFps(bool fallback = false) { } // Runs the host side of the GX pipeline on its own thread (gx_thread.h). On by -// default on the Quest, where the game thread is the bottleneck; opt-in elsewhere. +// default on the standalone headsets, where the game thread is the bottleneck; +// opt-in elsewhere. inline bool GxThread() { -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) return Get().gxThread.value_or(true); #else return Get().gxThread.value_or(false); diff --git a/runtime/src/settings_overlay.cpp b/runtime/src/settings_overlay.cpp index 60123d6..a6b430a 100644 --- a/runtime/src/settings_overlay.cpp +++ b/runtime/src/settings_overlay.cpp @@ -146,8 +146,8 @@ bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true); int g_vrRaceView = static_cast(RuntimeConfigFile::GetVrRaceView()); bool g_vrFlatScreen = g_vrRaceView == static_cast(RuntimeConfigFile::VrRaceView::FlatScreen); constexpr std::array kVrRaceViewLabels{"Immersive", "Immersive window", "Flat screen"}; -#if defined(__ANDROID__) -#if !defined(MKW_HEADSET_STEAM_FRAME) +#if defined(MKW_VR_STANDALONE) +#if defined(__ANDROID__) && !defined(MKW_HEADSET_STEAM_FRAME) bool g_vrPassthrough = RuntimeConfigFile::VrPassthrough(); #endif bool g_vrHandTracking = RuntimeConfigFile::VrHandTracking(); @@ -1260,7 +1260,7 @@ void DrawVrSteeringWheelSettings() { "trailed item."); } ImGui::EndDisabled(); -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) ImGui::BeginDisabled(!g_vrHandSteering && g_vrCockpitItemHand == 2); if (ImGui::Checkbox("Tracked hands", &g_vrHandTracking)) { RuntimeConfigFile::SetVrHandTracking(g_vrHandTracking); @@ -1491,8 +1491,8 @@ void DrawVrSettings() { eyes.scaled_height, g_vrRenderScalePercent); } } -#if defined(__ANDROID__) -#if !defined(MKW_HEADSET_STEAM_FRAME) +#if defined(MKW_VR_STANDALONE) +#if defined(__ANDROID__) && !defined(MKW_HEADSET_STEAM_FRAME) if (ImGui::Checkbox("Passthrough around the menu screen", &g_vrPassthrough)) { mkw::vr::OpenXRSetPassthrough(g_vrPassthrough); RuntimeConfigFile::SetVrPassthrough(g_vrPassthrough); @@ -1829,7 +1829,7 @@ void DrawVrCameraSettings() { RuntimeConfigFile::SetVrHandSteering(g_vrHandSteering); RuntimeConfigFile::SetVrCockpitItemHand(RuntimeConfigFile::kVrCockpitItemHandDefault); RuntimeConfigFile::SetVrCockpitItemThrow(g_vrCockpitItemThrow); -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) g_vrHandTracking = RuntimeConfigFile::kVrHandTrackingDefault; RuntimeConfigFile::SetVrHandTracking(g_vrHandTracking); #endif @@ -2531,7 +2531,7 @@ void InitializeRuntimeSettings() noexcept { aurora_set_display_mode(static_cast(g_displayMode)); g_displayMode = static_cast(aurora_get_display_mode()); aurora_set_disable_copy_filter(g_disableCopyFilter); -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) aurora_set_stereo_foveation(static_cast(g_vrFoveation)); #endif aurora_set_stereo_mirror_view(static_cast(g_vrMirrorView)); diff --git a/runtime/src/vr/openxr_integration.cpp b/runtime/src/vr/openxr_integration.cpp index 695b73b..9129939 100644 --- a/runtime/src/vr/openxr_integration.cpp +++ b/runtime/src/vr/openxr_integration.cpp @@ -745,6 +745,11 @@ private: // launched with foveation off does without them. aurora_config.xrFragmentDensityMap = RuntimeConfigFile::VrFoveation() != "off"; #endif +#if defined(MKW_HEADSET_STEAM_FRAME) && !defined(__ANDROID__) + // The Frame's native build: the headset is the only display anyone looks at, so the + // desktop window is neither presented nor fully rendered, as on Android. + aurora_config.xrHeadsetOnly = true; +#endif #if defined(_WIN32) if (kRequiredAuroraBackend != BACKEND_D3D12) return; const auto& requirements = backend_->GraphicsRequirements(); diff --git a/runtime/tests/vr_config_tests.cpp b/runtime/tests/vr_config_tests.cpp index ef3a79c..4e77da5 100644 --- a/runtime/tests/vr_config_tests.cpp +++ b/runtime/tests/vr_config_tests.cpp @@ -39,7 +39,7 @@ int main() { Require(!Parse("[vr]\nfoveation = \"ultra\"\n").vrFoveation.has_value()); Require(!Parse("[vr]\nfoveation = 2\n").vrFoveation.has_value()); Require(!Parse("[vr]\n").vrFoveation.has_value()); -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) Require(std::string_view(RuntimeConfigFile::kVrFoveationDefault) == "medium"); #else Require(std::string_view(RuntimeConfigFile::kVrFoveationDefault) == "off"); @@ -101,7 +101,7 @@ int main() { Require(Parse("[vr]\nobject_culling = false\n").vrObjectCulling == false); Require(!Parse("[vr]\n").vrObjectCulling.has_value()); Require(!Parse("[vr]\nobject_culling = 0\n").vrObjectCulling.has_value()); -#if defined(__ANDROID__) +#if defined(MKW_VR_STANDALONE) Require(RuntimeConfigFile::kVrObjectCullingDefault); Require(std::string_view(MKW_VR_OBJECT_CULLING_DEFAULT_TOML) == "true"); #else