diff --git a/OPENXR.md b/OPENXR.md index 57adc64..8a486ed 100644 --- a/OPENXR.md +++ b/OPENXR.md @@ -211,7 +211,7 @@ its CPU and GPU domains: `boost`, `sustained_high`, `sustained_low`, `power_savi request (see `docs/quest-port.md`); desktop runtimes rarely offer the extension, and the setting then does nothing. It is read at launch, and the session log records whether the runtime accepted it and any later performance notification (a thermal or rendering warning). -`foveation` (Quest only, default `off`) shades the edges of the immersive race view more coarsely: +`foveation` (Quest only, default `medium`) shades the edges of the immersive race view more coarsely: `off`, `low`, `medium` or `high`, see [Foveated rendering](#foveated-rendering). A session launched with it off runs without fragment density maps, so going from `off` to a level takes a restart; between levels, and back to `off`, it is live from the headset panel's VR tab. The launcher's @@ -914,9 +914,11 @@ interleaved within one session (`docs/quest-port.md` has the method). At the Quest's default 0.8 an eye's time goes mostly to geometry and to storing its tiles at full resolution. The Wii's shading is cheap, so foveation saves nothing measurable there, although the -density map verifiably applies (4x4 blocks at the view's edges on High). That is why it defaults to -`off`. At higher render scales it takes 8 to 22% off the eyes, which is where it earns its keep, -bought with a softer periphery. The Quest's GPU applies the density per screen tile, and inside a +density map verifiably applies (4x4 blocks at the view's edges on High). At higher render scales it +takes 8 to 22% off the eyes, which is where it earns its keep, bought with a softer periphery. The +Quest defaults to `medium` all the same: it costs nothing measurable at 0.8, it is already on when +the render scale is raised, and a session launched with a level can change it live, where one +launched with `off` needs a restart. The Quest's GPU applies the density per screen tile, and inside a reduced-rate tile it also samples textures one level blurrier per halving. Most surfaces hide it, but fine animated detail does not: on Retro Rewind's swamp goo the tiles show as squares where the ripples give way to a smoother look. It is no fix for a heavy track: on Retro Rewind's SNES Ghost Valley 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 63c9e61..220bf9e 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 @@ -228,7 +228,7 @@ class SettingsPage( R.string.vr_foveation_off, R.string.vr_foveation_low, R.string.vr_foveation_medium, R.string.vr_foveation_high, ), - read = { stringIndex(it, "vr", "foveation", FOVEATION_LEVELS) }, + read = { stringIndex(it, "vr", "foveation", FOVEATION_LEVELS, FOVEATION_DEFAULT) }, write = { c, index -> c.setString("vr", "foveation", FOVEATION_LEVELS[index]) }, ) choice( @@ -757,8 +757,9 @@ class SettingsPage( val SEATS = listOf("cockpit", "custom") // The runtime's default ("boost") first: an absent key reads as index 0. val PERFORMANCE_LEVELS = listOf("boost", "sustained_high", "sustained_low", "power_savings", "default") - // runtime_config.h's kVrFoveationLevels, the default ("off") first. + // runtime_config.h's kVrFoveationLevels, and its Quest default. val FOVEATION_LEVELS = listOf("off", "low", "medium", "high") + val FOVEATION_DEFAULT = FOVEATION_LEVELS.indexOf("medium") val CONTROLLER_MODES = listOf("wii_remote", "gamepad", "none") val INTERPOLATION_FPS = listOf(0L, 1L, 72L, 90L, 120L) val RESOLUTIONS = listOf(1.0, 1.5, 2.0, 3.0, 4.0) diff --git a/docs/quest-port.md b/docs/quest-port.md index d3b0dc2..26dc488 100644 --- a/docs/quest-port.md +++ b/docs/quest-port.md @@ -179,8 +179,9 @@ at 0.8, and 492 to 525 MHz at 1.3, the unfoveated windows running at the higher So an eye's time on these tracks is mostly geometry and full-resolution tile stores, which a density map does not reduce (the stores stay full size for non-subsampled images). Foveation -stays off by default; it pays only when `render_scale` makes the eyes pixel-bound, as at 1.3 on -Luigi Circuit. +pays only when `render_scale` makes the eyes pixel-bound, as at 1.3 on Luigi Circuit; it +defaults to `medium` anyway, so it is already on when the render scale is raised and can be +changed without a restart. ### Quest 1 renderer compatibility diff --git a/runtime/include/runtime_config.h b/runtime/include/runtime_config.h index bb76315..3a7fe96 100644 --- a/runtime/include/runtime_config.h +++ b/runtime/include/runtime_config.h @@ -261,8 +261,12 @@ inline bool IsSupportedVrPerformanceLevel(std::string_view 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. +// live. The Quest starts at "medium"; elsewhere it does nothing. +#if defined(__ANDROID__) +inline constexpr const char* kVrFoveationDefault = "medium"; +#else inline constexpr const char* kVrFoveationDefault = "off"; +#endif inline constexpr std::array kVrFoveationLevels{"off", "low", "medium", "high"}; inline bool IsSupportedVrFoveation(std::string_view value) { diff --git a/runtime/tests/vr_config_tests.cpp b/runtime/tests/vr_config_tests.cpp index 8101343..609c8f0 100644 --- a/runtime/tests/vr_config_tests.cpp +++ b/runtime/tests/vr_config_tests.cpp @@ -23,7 +23,11 @@ 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__) + Require(std::string_view(RuntimeConfigFile::kVrFoveationDefault) == "medium"); +#else Require(std::string_view(RuntimeConfigFile::kVrFoveationDefault) == "off"); +#endif Require(RuntimeConfigFile::VrFoveationLevelIndex("off") == 0); Require(RuntimeConfigFile::VrFoveationLevelIndex("low") == 1); Require(RuntimeConfigFile::VrFoveationLevelIndex("medium") == 2);