diff --git a/android/app/src/main/java/org/wiicompiled/quest/launcher/SettingsPage.kt b/android/app/src/main/java/org/wiicompiled/quest/launcher/SettingsPage.kt index 3789507..297dfeb 100644 --- a/android/app/src/main/java/org/wiicompiled/quest/launcher/SettingsPage.kt +++ b/android/app/src/main/java/org/wiicompiled/quest/launcher/SettingsPage.kt @@ -286,11 +286,14 @@ class SettingsPage( format = { "%.1f m".format(it) }, write = { c, value -> c.setFloat("vr", "hud_width_meters", value) }, ) - toggle( - R.string.vr_passthrough, R.string.vr_passthrough_helper, - read = { it.bool("vr", "passthrough") ?: true }, - write = { c, value -> c.setBool("vr", "passthrough", value) }, - ) + // Horizon OS's XR_FB_passthrough; SteamVR on the Steam Frame has no such layer. + if (!BuildConfig.STEAM_FRAME) { + toggle( + R.string.vr_passthrough, R.string.vr_passthrough_helper, + read = { it.bool("vr", "passthrough") ?: true }, + write = { c, value -> c.setBool("vr", "passthrough", value) }, + ) + } } } diff --git a/runtime/include/runtime_config.h b/runtime/include/runtime_config.h index f2e45b4..7dc44a0 100644 --- a/runtime/include/runtime_config.h +++ b/runtime/include/runtime_config.h @@ -189,8 +189,9 @@ inline constexpr uint32_t kPostProcessingBloomPath = 0x10u; inline constexpr uint32_t kDisabledPostProcessingPathsDefault = kPostProcessingBloomPath; // Headset eye size as a fraction of what the OpenXR runtime recommends. A -// standalone headset renders on a mobile GPU, so the Quest starts below it; -// the launcher's first Config.toml (GameStorage.kt) writes the same value. +// 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__) inline constexpr float kVrRenderScaleDefault = 0.8f; #define MKW_VR_RENDER_SCALE_DEFAULT_TEXT "0.8" @@ -306,7 +307,8 @@ inline bool IsSupportedVrRefreshRate(uint32_t value) { // 4x4 pixel blocks, the higher the level the closer to the centre. Whether the // GPU device gets fragment density maps at all is decided at launch, so going // from "off" to a level takes a restart; between levels and back to "off" it is -// live. The Quest starts at "medium"; elsewhere it does nothing. +// live. The standalone headsets (Quest, Steam Frame) start at "medium"; +// elsewhere it does nothing. #if defined(__ANDROID__) inline constexpr const char* kVrFoveationDefault = "medium"; #else @@ -1719,9 +1721,16 @@ inline bool SetVrRaceView(VrRaceView view) { // The room, through the headset's cameras, around the menu screen and every // other virtual screen, a Flat Screen race included, and around the immersive -// window (never a fully immersive race). Only the Quest offers it; the -// launcher's Settings page shows the same default. -inline bool VrPassthrough(bool fallback = true) { +// window (never a fully immersive race). Only the Quest offers it +// (XR_FB_passthrough, which SteamVR does not have); the launcher's Settings +// page shows the same default. +#if defined(MKW_HEADSET_STEAM_FRAME) +inline constexpr bool kVrPassthroughDefault = false; +#else +inline constexpr bool kVrPassthroughDefault = true; +#endif + +inline bool VrPassthrough(bool fallback = kVrPassthroughDefault) { return Get().vrPassthrough.value_or(fallback); } diff --git a/runtime/src/settings_overlay.cpp b/runtime/src/settings_overlay.cpp index 4492c01..8983d58 100644 --- a/runtime/src/settings_overlay.cpp +++ b/runtime/src/settings_overlay.cpp @@ -147,7 +147,9 @@ 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) bool g_vrPassthrough = RuntimeConfigFile::VrPassthrough(); +#endif bool g_vrHandTracking = RuntimeConfigFile::VrHandTracking(); // Menu labels for the foveation levels, index-matched to RuntimeConfigFile::kVrFoveationLevels and to // aurora_set_stereo_foveation. @@ -1489,6 +1491,7 @@ void DrawVrSettings() { } } #if defined(__ANDROID__) +#if !defined(MKW_HEADSET_STEAM_FRAME) if (ImGui::Checkbox("Passthrough around the menu screen", &g_vrPassthrough)) { mkw::vr::OpenXRSetPassthrough(g_vrPassthrough); RuntimeConfigFile::SetVrPassthrough(g_vrPassthrough); @@ -1500,6 +1503,7 @@ void DrawVrSettings() { "fully virtual; the immersive window and the Flat Screen race have the room " "around them too. Applies immediately."); } +#endif // Shows the live level Aurora holds. g_vrFoveation = static_cast(aurora_get_stereo_foveation()); if (ImGui::Combo("Foveated rendering", &g_vrFoveation, kVrFoveationLabels.data(), diff --git a/runtime/src/vr/openxr_integration.cpp b/runtime/src/vr/openxr_integration.cpp index 81e5651..8da12af 100644 --- a/runtime/src/vr/openxr_integration.cpp +++ b/runtime/src/vr/openxr_integration.cpp @@ -423,7 +423,11 @@ public: "XR_KHR_convert_timespec_time", "XR_KHR_android_thread_settings", "XR_FB_display_refresh_rate", "XR_EXT_performance_settings", - "XR_FB_passthrough", "XR_VALVE_frame_controller_interaction"}; + "XR_VALVE_frame_controller_interaction"}; +#if !defined(MKW_HEADSET_STEAM_FRAME) + // Horizon OS's room view; the Steam Frame build neither asks for it nor offers the setting. + config.optional_extensions.push_back("XR_FB_passthrough"); +#endif AddHandMeshExtensions(config); config.instance_create_next = OpenXRAndroidInstanceCreateNext(); #endif diff --git a/runtime/tests/vr_config_tests.cpp b/runtime/tests/vr_config_tests.cpp index 4cb5e6f..409a4aa 100644 --- a/runtime/tests/vr_config_tests.cpp +++ b/runtime/tests/vr_config_tests.cpp @@ -68,6 +68,15 @@ int main() { Require(std::string_view(MKW_VR_REFRESH_RATE_DEFAULT_TEXT) == "0"); #endif + // [vr] passthrough: Horizon OS's room view, which the Steam Frame build does not offer. + Require(Parse("[vr]\npassthrough = false\n").vrPassthrough == false); + Require(!Parse("[vr]\n").vrPassthrough.has_value()); +#if defined(MKW_HEADSET_STEAM_FRAME) + Require(!RuntimeConfigFile::kVrPassthroughDefault); +#else + Require(RuntimeConfigFile::kVrPassthroughDefault); +#endif + // [vr] hand_tracking: the cockpit hands follow the headset's hand tracking. Require(Parse("[vr]\nhand_tracking = true\n").vrHandTracking == true); Require(Parse("[vr]\nhand_tracking = false\n").vrHandTracking == false);