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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
This commit is contained in:
Claude committed 2026-10-04 09:51:08 +00:00
1 parent 28a25108c6
commit 0edf10b220
6 files changed
+32 -15

No files matched your search

+4
View File
@@ -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 {
+1 -1
View File
@@ -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
}
+13 -5
View File
@@ -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 <algorithm>
#include <array>
#include <cctype>
@@ -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);
+7 -7
View File
@@ -146,8 +146,8 @@ bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true);
int g_vrRaceView = static_cast<int>(RuntimeConfigFile::GetVrRaceView());
bool g_vrFlatScreen = g_vrRaceView == static_cast<int>(RuntimeConfigFile::VrRaceView::FlatScreen);
constexpr std::array<const char*, 3> 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<AuroraDisplayMode>(g_displayMode));
g_displayMode = static_cast<int>(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<uint32_t>(g_vrFoveation));
#endif
aurora_set_stereo_mirror_view(static_cast<AuroraStereoMirrorView>(g_vrMirrorView));
+5
View File
@@ -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();
+2 -2
View File
@@ -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