mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 00:00:17 +02:00
Refactor VR settings: remove deprecated options and update eye replay handling
This commit is contained in:
1 parent
e4d198ee13
commit
566a22857c
6 files changed
+169
-237
No files matched your search
@@ -47,9 +47,6 @@ hud_width_meters = 2.4
|
||||
hud_virtual_screen = true
|
||||
flat_screen = false
|
||||
immersive_window = false
|
||||
stop_at_display_copy = true
|
||||
skip_copy_clears = true
|
||||
single_pass_eyes = true
|
||||
first_person = false
|
||||
first_person_toggle_click = true
|
||||
first_person_seat = "cockpit"
|
||||
@@ -187,16 +184,17 @@ panel's rectangle), at the same size per pixel, so nothing moves.
|
||||
`hand_tracking` (Quest only, default off) makes the first-person cockpit's hands follow the
|
||||
headset's hand tracking; see "Tracked hands" under
|
||||
[Steering wheel and hand steering](#steering-wheel-and-hand-steering).
|
||||
`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.
|
||||
`single_pass_eyes` draws each eye in one render pass. The desktop image ends a render pass at every
|
||||
GX copy, because the copy reads what was drawn before it; an eye samples the copies the desktop
|
||||
image made and never performs them, so it keeps drawing in the pass it has open, and it leaves out
|
||||
whatever a later clear of the whole color and depth erases. The picture is the same with less GPU
|
||||
memory traffic, which a tiled mobile GPU pays for at every split (the "Eye replay plan" log line
|
||||
reports each new pass structure). It defaults on and is live; turning it off replays one render pass
|
||||
per recorded pass.
|
||||
How each eye is replayed is fixed; the former `stop_at_display_copy`, `skip_copy_clears` and
|
||||
`single_pass_eyes` settings are ignored. An eye ends at the frame's final `GXCopyDisp`, so it holds
|
||||
the frame shown on the desktop. It keeps the EFB reset that follows a display copy: Aurora marks
|
||||
only a display copy's reset, the final one lies past the replay's end, and an earlier one erases
|
||||
exactly what the final copy did not show. And it is drawn in one render pass. The desktop image ends
|
||||
a render pass at every GX copy, because the copy reads what was drawn before it; an eye samples the
|
||||
copies the desktop image made and never performs them, so it keeps drawing in the pass it has open,
|
||||
and it leaves out whatever a later clear of the whole color and depth erases. The picture is the
|
||||
same with less GPU memory traffic, which a tiled mobile GPU pays for at every split (the "Eye replay
|
||||
plan" log line reports each new pass structure). On the Quest, `debug.wiicompiled.eye_passes 0`
|
||||
replays one render pass per recorded pass again, for A/B timing (`docs/quest-port.md`).
|
||||
`first_person` and the `first_person_*` values are the first-person camera described below. All
|
||||
four are live and are also exposed in the F10 settings bar.
|
||||
`performance_level` is the level asked of the runtime through `XR_EXT_performance_settings` for
|
||||
@@ -216,7 +214,7 @@ Settings page has it too.
|
||||
The headset's tracked controllers reach the game through an OpenXR action set synced on the pacing
|
||||
thread (`runtime/src/vr/openxr_input.cpp`), which feeds a virtual SDL gamepad that Aurora assigns
|
||||
to a port like any other. `controller_mode` decides what the game finds on that port, and is live
|
||||
from **F10 > VR > VR controllers**; the game sees a change as a controller reconnection.
|
||||
from **F10 > Controller settings > VR controllers**; the game sees a change as a controller reconnection.
|
||||
|
||||
The pacing thread only publishes that gamepad; the game thread writes it to SDL where it already
|
||||
polls controllers (`OpenXRApplyControllerState`, called from `PAD__Read_HLE` and the overlay's
|
||||
@@ -319,7 +317,7 @@ thumbsticks together** instead); the left controller's menu button and the panel
|
||||
also close it. It can be opened from the desktop as well, with
|
||||
**F10 → VR → Show these settings in the headset**.
|
||||
|
||||
The panel has the F10 bar's menus as tabs (VR, Graphics, Controllers, Audio, Diagnostics) and a
|
||||
The panel has the F10 bar's menus as tabs (VR, Camera, Graphics, Controllers, Audio, Diagnostics) and a
|
||||
*Recenter view* button. Aim a controller at it: the cursor goes where you aim, a trigger (or A / X)
|
||||
selects and drags sliders, and a thumbstick scrolls. Whichever hand last pulled its trigger does the
|
||||
pointing. Changes apply exactly as they do from the F10 bar, and the two stay in step.
|
||||
@@ -540,7 +538,7 @@ full-lock angle as the stick (`wheel_kart_degrees`, `wheel_bike_degrees`), and h
|
||||
aside while it drives.
|
||||
|
||||
**Tracked hands.** `hand_tracking` (Quest only for now, default off; the Quest launcher's
|
||||
Settings > VR and the headset panel's VR tab, under hand steering, which it needs) poses the
|
||||
Settings > VR and the headset panel's Camera tab, under hand steering, which it needs) poses the
|
||||
cockpit hands from the headset's hand tracking instead of curling them with the grip. Two hand
|
||||
trackers (`XR_EXT_hand_tracking`) are located every XR frame at the display time. While the
|
||||
controllers are held the Quest builds the joints from their touch sensors
|
||||
@@ -769,8 +767,9 @@ eye's last draw Aurora covers the eye with one full-screen triangle (`aurora-mai
|
||||
that keeps the colour inside the window with alpha 1 and leaves transparent black outside it, with a
|
||||
one-pixel ramp at the edge. The triangle carries, at each corner, where that pixel's ray meets the
|
||||
window's plane in homogeneous window coordinates (`stereo_replay::window_mask`), which interpolate
|
||||
exactly across the image. With `single_pass_eyes` it is drawn in the eye's own last render pass, so it
|
||||
adds no pass and no tile load; otherwise it takes a pass of its own, as the cockpit overlay does. On
|
||||
exactly across the image. It is drawn in the eye's own last render pass, so it adds no pass and no
|
||||
tile load; an eye replayed one render pass per recorded pass (`debug.wiicompiled.eye_passes 0`)
|
||||
gives it a pass of its own, as the cockpit overlay has. On
|
||||
the Quest the backend submits the passthrough layer, then the projection layer with
|
||||
`XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT` (premultiplied alpha), then the settings panel.
|
||||
The flag travels with the packet, so the eyes Aurora masked and the layer that blends them always
|
||||
@@ -848,7 +847,7 @@ pipeline flag. That is why the launch decides.
|
||||
A density map forces Adreno into binned rendering, where every extra render pass in an eye stores
|
||||
and reloads the whole eye. DolphinXR measured foveation as a net loss on Mario Kart Wii for exactly
|
||||
that reason (its bloom chain splits the frame about 20 times). An eye is therefore foveated only
|
||||
when it is drawn in a single render pass (`single_pass_eyes`); an eye that a partial clear still
|
||||
when it is drawn in a single render pass, as every eye is by default; an eye that a partial clear still
|
||||
splits is drawn at full rate. The session log reports what happened: "Fragment density maps:
|
||||
enabled" at startup, one "eye foveation" line per eye and level with the map's size, and the "Eye
|
||||
replay plan" lines. `debug.wiicompiled.foveation <0-3>` overrides the level for A/B timing, and
|
||||
@@ -858,7 +857,7 @@ What it saves depends on how much of an eye's cost is shading pixels. The number
|
||||
Quest 3 at Luigi Circuit's Grand Prix start: GPU time of both eyes per frame, all settings
|
||||
interleaved within one session (`docs/quest-port.md` has the method).
|
||||
|
||||
| `render_scale` (eye size) | One pass per recorded pass | `single_pass_eyes` | `low` | `medium` | `high` |
|
||||
| `render_scale` (eye size) | One pass per recorded pass | One pass per eye | `low` | `medium` | `high` |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 0.8 (1344x1408) | 5.82 ms | 5.11 ms | 5.09 ms | 5.36 ms | 5.11 ms |
|
||||
| 1.3 (2184x2288) | 6.32 ms | 5.81 ms | 5.33 ms | 5.00 ms | 4.56 ms |
|
||||
|
||||
+2
-2
@@ -144,7 +144,7 @@ Aurora (`lib/webgpu/fdm.cpp`, `lib/gfx/foveation.hpp`) builds one map per eye,
|
||||
32 pixels per texel (42x44 for 1344x1408 eyes). The map is centred on that eye's
|
||||
forward direction and rebuilt when the eye's size, field of view or level
|
||||
changes. It is bound to a second view of the eye texture that only a
|
||||
single-render-pass immersive eye renders through (`single_pass_eyes`). Density
|
||||
single-render-pass immersive eye renders through (every eye, by default). Density
|
||||
bytes are 255, 127 and 63: a fragment covers 1/density pixels rounded down to a
|
||||
supported size, so a half written as 128 could round back to one pixel.
|
||||
Changing the level is live. The launch decides whether the device has density
|
||||
@@ -919,7 +919,7 @@ the app:
|
||||
| `debug.wiicompiled.vtxpad 0` | Turns the stride padding off, to re-check a driver update |
|
||||
| `debug.wiicompiled.validation 1` | Keeps WebGPU validation and robustness on in release builds |
|
||||
| `debug.wiicompiled.panel_layer 0` | Draws the headset settings panel into the eye images instead of on its own quad layer (`OPENXR.md`, Settings in the headset); read about once a second, so it can be switched while the panel is open |
|
||||
| `debug.wiicompiled.eye_passes 0` | Replays each eye in one render pass per recorded pass, as before `single_pass_eyes` (`OPENXR.md`); `1` forces the single pass and an empty value restores the setting. Read about once a second, for A/B timing inside one session |
|
||||
| `debug.wiicompiled.eye_passes 0` | Replays each eye in one render pass per recorded pass, as before eyes were drawn in a single pass (`OPENXR.md`); `1` or an empty value restores the single pass. Read about once a second, for A/B timing inside one session |
|
||||
| `debug.wiicompiled.foveation <0-3>` | Overrides the foveation level (off, low, medium, high) within one session; an empty value restores the setting. Needs a session launched with foveation on. Read about once a second |
|
||||
| `debug.wiicompiled.fdm 0` | Launches without fragment density maps at all, whatever `foveation` says, which also drops their flag from every pipeline; `1` asks for them even with `foveation = "off"` |
|
||||
| `debug.wiicompiled.inject <n>:<button>` | Presses `a`, `b`, `x`, `y`, `start`, `up`, `down`, `left` or `right` for 12 XR frames each time `<n>` changes. As a Wii Remote, `x`/`y`/`start` are 1/2/+, the directions push the Nunchuk stick, and `home`, `c` and `z` also exist. `panel` presses the settings panel's button (left Y, or both thumbsticks as a gamepad), opening or closing it (see `OPENXR.md`). `flick` plays the bare hands' flick, one 150 ms shake of the remote (a trick off a ramp, a wheelie on a bike), with the controllers or none |
|
||||
|
||||
@@ -60,9 +60,6 @@ struct RuntimeUserConfig {
|
||||
std::optional<bool> vrFlatScreen;
|
||||
std::optional<bool> vrImmersiveWindow;
|
||||
std::optional<bool> vrPassthrough;
|
||||
std::optional<bool> vrStopAtDisplayCopy;
|
||||
std::optional<bool> vrSkipCopyClears;
|
||||
std::optional<bool> vrSinglePassEyes;
|
||||
std::optional<std::string> vrMirrorView;
|
||||
std::optional<std::string> vrControllerMode;
|
||||
std::optional<uint32_t> vrFrameInterpolationFps;
|
||||
@@ -538,17 +535,6 @@ inline void EnsureConfigFile() {
|
||||
"# Quest (black elsewhere). Flat Screen mode wins over it.\n"
|
||||
"# Changeable live from the F10 menu.\n"
|
||||
"immersive_window = false\n"
|
||||
"# EFB replay controls for the per-eye views, changeable live\n"
|
||||
"# from the F10 menu. stop_at_display_copy ends each eye at the\n"
|
||||
"# frame's final GXCopyDisp; skip_copy_clears drops the EFB\n"
|
||||
"# reset a GX copy performs afterwards. Both keep that reset\n"
|
||||
"# from erasing the eye, and both are safe to turn off.\n"
|
||||
"stop_at_display_copy = true\n"
|
||||
"skip_copy_clears = true\n"
|
||||
"# Draw each eye in one render pass across the frame's GX copies,\n"
|
||||
"# which only the desktop image performs: the same picture with\n"
|
||||
"# less GPU memory traffic. Changeable live from the F10 menu.\n"
|
||||
"single_pass_eyes = true\n"
|
||||
"# Put the camera at the Player 1 driver's head instead of behind\n"
|
||||
"# the kart, with the horizon kept level. Changeable live from the\n"
|
||||
"# F10 menu, and only during a single-screen race.\n"
|
||||
@@ -796,9 +782,6 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.vrFlatScreen = FindConfigValue<bool>(document, "vr", "flat_screen");
|
||||
config.vrImmersiveWindow = FindConfigValue<bool>(document, "vr", "immersive_window");
|
||||
config.vrPassthrough = FindConfigValue<bool>(document, "vr", "passthrough");
|
||||
config.vrStopAtDisplayCopy = FindConfigValue<bool>(document, "vr", "stop_at_display_copy");
|
||||
config.vrSkipCopyClears = FindConfigValue<bool>(document, "vr", "skip_copy_clears");
|
||||
config.vrSinglePassEyes = FindConfigValue<bool>(document, "vr", "single_pass_eyes");
|
||||
config.vrFirstPerson = FindConfigValue<bool>(document, "vr", "first_person");
|
||||
config.vrFirstPersonToggleClick = FindConfigValue<bool>(document, "vr", "first_person_toggle_click");
|
||||
if (auto value = FindConfigFloat(document, "vr", "first_person_units_per_meter");
|
||||
@@ -1130,21 +1113,6 @@ inline bool SetVrPassthrough(bool value) {
|
||||
return WriteSetting("vr", "passthrough", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrStopAtDisplayCopy(bool value) {
|
||||
Mutable().vrStopAtDisplayCopy = value;
|
||||
return WriteSetting("vr", "stop_at_display_copy", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrSkipCopyClears(bool value) {
|
||||
Mutable().vrSkipCopyClears = value;
|
||||
return WriteSetting("vr", "skip_copy_clears", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrSinglePassEyes(bool value) {
|
||||
Mutable().vrSinglePassEyes = value;
|
||||
return WriteSetting("vr", "single_pass_eyes", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVrFirstPerson(bool value) {
|
||||
Mutable().vrFirstPerson = value;
|
||||
return WriteSetting("vr", "first_person", value ? "true" : "false");
|
||||
@@ -1654,18 +1622,6 @@ inline bool VrPassthrough(bool fallback = true) {
|
||||
return Get().vrPassthrough.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool VrStopAtDisplayCopy(bool fallback = true) {
|
||||
return Get().vrStopAtDisplayCopy.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool VrSkipCopyClears(bool fallback = true) {
|
||||
return Get().vrSkipCopyClears.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool VrSinglePassEyes(bool fallback = true) {
|
||||
return Get().vrSinglePassEyes.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool VrFirstPerson(bool fallback = false) {
|
||||
return Get().vrFirstPerson.value_or(fallback);
|
||||
}
|
||||
|
||||
+138
-163
@@ -139,9 +139,6 @@ bool g_showFps = RuntimeConfigFile::ShowFps();
|
||||
// The same default the VR path itself takes (kVrEnabledDefault), so the F10 switch
|
||||
// shows what an unconfigured installation actually starts in.
|
||||
bool g_vrEnabled = RuntimeConfigFile::VrEnabled(true);
|
||||
bool g_vrStopAtDisplayCopy = RuntimeConfigFile::VrStopAtDisplayCopy(true);
|
||||
bool g_vrSkipCopyClears = RuntimeConfigFile::VrSkipCopyClears(true);
|
||||
bool g_vrSinglePassEyes = RuntimeConfigFile::VrSinglePassEyes(true);
|
||||
bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true);
|
||||
// Race view: Immersive, Immersive window or Flat screen (RuntimeConfigFile::VrRaceView), and
|
||||
// Flat Screen mode as the flag the race view rows below are disabled by.
|
||||
@@ -868,7 +865,43 @@ void DrawRumbleSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
// What the headset's controllers are to the game. One choice for the pair, whichever port they
|
||||
// land on, and live like the rest of the VR settings: without a session it is what the next starts on.
|
||||
void DrawVrControllerSettings() {
|
||||
if (ImGui::Combo("VR controllers", &g_vrControllerMode, kVrControllerModeLabels.data(),
|
||||
static_cast<int>(kVrControllerModeLabels.size()))) {
|
||||
mkw::vr::OpenXRSetControllerMode(static_cast<mkw::vr::OpenXRControllerMode>(g_vrControllerMode));
|
||||
RuntimeConfigFile::SetVrControllerMode(kVrControllerModeNames[static_cast<size_t>(g_vrControllerMode)]);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
||||
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
||||
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
|
||||
" Left: stick = Nunchuk stick, trigger = Z, X = -, menu = +, Y = settings panel\n"
|
||||
" The grips press nothing; they take hold of the wheel with hand steering.\n"
|
||||
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
||||
"Applies immediately; the game sees the controller change as a reconnection.");
|
||||
}
|
||||
if (mkw::vr::OpenXRIsRunning() &&
|
||||
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::WiiRemote) {
|
||||
mkw::vr::OpenXRWiiRemoteSample remote;
|
||||
if (mkw::vr::OpenXRReadWiiRemote(remote)) {
|
||||
if (remote.pointer_valid) {
|
||||
ImGui::TextDisabled("Pointer %+.2f %+.2f | Remote %+.2f %+.2f %+.2f g", remote.pointer[0],
|
||||
remote.pointer[1], remote.acc[0], remote.acc[1], remote.acc[2]);
|
||||
} else {
|
||||
ImGui::TextDisabled("Pointer off screen | Remote %+.2f %+.2f %+.2f g", remote.acc[0],
|
||||
remote.acc[1], remote.acc[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawControllerSettings() {
|
||||
// First, since the per-port settings below stop early when a port has nothing to show.
|
||||
DrawVrControllerSettings();
|
||||
ImGui::Separator();
|
||||
if (ImGui::CollapsingHeader("USB wheel and pedals (player 1)")) {
|
||||
physical_wheel::DrawSettings();
|
||||
ImGui::Separator();
|
||||
@@ -1364,65 +1397,30 @@ void DrawVrSettings() {
|
||||
RuntimeConfigFile::SetVrEnabled(g_vrEnabled);
|
||||
}
|
||||
ImGui::TextDisabled("OpenXR mode changes take effect after restarting the game.");
|
||||
// Live, unlike the enable toggle above, so it is left usable either way:
|
||||
// set before a restart it is simply what the next session starts on.
|
||||
if (ImGui::Combo("Desktop view", &g_vrMirrorView, kVrMirrorViewLabels.data(),
|
||||
static_cast<int>(kVrMirrorViewLabels.size()))) {
|
||||
aurora_set_stereo_mirror_view(static_cast<AuroraStereoMirrorView>(g_vrMirrorView));
|
||||
RuntimeConfigFile::SetVrMirrorView(kVrMirrorViewNames[static_cast<size_t>(g_vrMirrorView)]);
|
||||
// Everything below is live, unlike the enable toggle, so it is left usable either way: set
|
||||
// before a restart it is simply what the next session starts on.
|
||||
if (ImGui::Combo("Race view", &g_vrRaceView, kVrRaceViewLabels.data(),
|
||||
static_cast<int>(kVrRaceViewLabels.size()))) {
|
||||
const auto view = static_cast<RuntimeConfigFile::VrRaceView>(g_vrRaceView);
|
||||
g_vrFlatScreen = view == RuntimeConfigFile::VrRaceView::FlatScreen;
|
||||
RuntimeConfigFile::SetVrRaceView(view);
|
||||
mkw::vr::MkwVRPolicySetImmersiveRaces(!g_vrFlatScreen);
|
||||
mkw::vr::OpenXRSetImmersiveWindow(view == RuntimeConfigFile::VrRaceView::ImmersiveWindow);
|
||||
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"What this window shows while the headset is running. Normal keeps the ordinary "
|
||||
"desktop view, the eye choices mirror what you are actually seeing in the headset, "
|
||||
"and None leaves the window black. Menus reach the headset as a screen showing this "
|
||||
"same desktop image, so the eye choices only differ from Normal during a race.");
|
||||
}
|
||||
ImGui::BeginDisabled(!mkw::vr::OpenXRIsRunning());
|
||||
bool settingsPanelOpen = mkw::vr::OpenXRSettingsPanelOpen();
|
||||
if (ImGui::Checkbox("Show these settings in the headset", &settingsPanelOpen)) {
|
||||
mkw::vr::OpenXRSetSettingsPanelOpen(settingsPanelOpen);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
ImGui::SetTooltip(
|
||||
"Opens these settings on a panel in front of you, in menus and races alike.\n"
|
||||
"In the headset, left Y opens and closes it too (with Gamepad VR controllers,\n"
|
||||
"click both thumbsticks together instead).\n"
|
||||
"Aim at it and pull a trigger to change a setting; push a thumbstick to scroll.\n"
|
||||
"While it is open the game does not see the VR controllers.");
|
||||
}
|
||||
if (ImGui::Combo("VR controllers", &g_vrControllerMode, kVrControllerModeLabels.data(),
|
||||
static_cast<int>(kVrControllerModeLabels.size()))) {
|
||||
mkw::vr::OpenXRSetControllerMode(static_cast<mkw::vr::OpenXRControllerMode>(g_vrControllerMode));
|
||||
RuntimeConfigFile::SetVrControllerMode(kVrControllerModeNames[static_cast<size_t>(g_vrControllerMode)]);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
||||
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
||||
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
|
||||
" Left: stick = Nunchuk stick, trigger = Z, X = -, menu = +, Y = settings panel\n"
|
||||
" The grips press nothing; they take hold of the wheel with hand steering.\n"
|
||||
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
||||
"Applies immediately; the game sees the controller change as a reconnection.");
|
||||
}
|
||||
if (mkw::vr::OpenXRIsRunning() &&
|
||||
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::WiiRemote) {
|
||||
mkw::vr::OpenXRWiiRemoteSample remote;
|
||||
if (mkw::vr::OpenXRReadWiiRemote(remote)) {
|
||||
if (remote.pointer_valid) {
|
||||
ImGui::TextDisabled("Pointer %+.2f %+.2f | Remote %+.2f %+.2f %+.2f g", remote.pointer[0],
|
||||
remote.pointer[1], remote.acc[0], remote.acc[1], remote.acc[2]);
|
||||
} else {
|
||||
ImGui::TextDisabled("Pointer off screen | Remote %+.2f %+.2f %+.2f g", remote.acc[0],
|
||||
remote.acc[1], remote.acc[2]);
|
||||
}
|
||||
}
|
||||
"Immersive plays races all around you in stereo. Immersive window keeps that "
|
||||
"stereo view but shows it only through a window where the menu screen sits, with "
|
||||
"your room around it on the Quest (black elsewhere); look through it from another "
|
||||
"angle and the view shifts as through a real window. Flat screen plays races on "
|
||||
"the menu screen through the game's own camera; the Camera settings and the lean-back "
|
||||
"angle do not apply to it. Applies immediately.");
|
||||
}
|
||||
|
||||
// Live, and usable without a session like the mirror above. It applies when the slider is let
|
||||
// go rather than at every step of a drag, since each new size means new eye swapchains.
|
||||
ImGui::Separator();
|
||||
// Applied when the slider is let go rather than at every step of a drag, since each new size
|
||||
// means new eye swapchains.
|
||||
ImGui::SliderInt("Render resolution", &g_vrRenderScalePercent,
|
||||
static_cast<int>(RuntimeConfigFile::kVrRenderScaleMin * 100.0f),
|
||||
static_cast<int>(RuntimeConfigFile::kVrRenderScaleMax * 100.0f), "%d%%",
|
||||
@@ -1449,7 +1447,38 @@ void DrawVrSettings() {
|
||||
eyes.scaled_height, g_vrRenderScalePercent);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (ImGui::Checkbox("Passthrough around the menu screen", &g_vrPassthrough)) {
|
||||
mkw::vr::OpenXRSetPassthrough(g_vrPassthrough);
|
||||
RuntimeConfigFile::SetVrPassthrough(g_vrPassthrough);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Shows your room through the headset's cameras around the menu screen and every "
|
||||
"other screen outside an immersive race, instead of black. Immersive races stay "
|
||||
"fully virtual; the immersive window and the Flat Screen race have the room "
|
||||
"around them too. Applies immediately.");
|
||||
}
|
||||
// Shows the live level, which debug.wiicompiled.foveation can override.
|
||||
g_vrFoveation = static_cast<int>(aurora_get_stereo_foveation());
|
||||
if (ImGui::Combo("Foveated rendering", &g_vrFoveation, kVrFoveationLabels.data(),
|
||||
static_cast<int>(kVrFoveationLabels.size()))) {
|
||||
aurora_set_stereo_foveation(static_cast<uint32_t>(g_vrFoveation));
|
||||
RuntimeConfigFile::SetVrFoveation(
|
||||
std::string(RuntimeConfigFile::kVrFoveationLevels[static_cast<size_t>(g_vrFoveation)]));
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"%s", aurora_stereo_foveation_available()
|
||||
? "Shades the edges of the race view in 2x2, then 4x4 pixel blocks, where the "
|
||||
"lenses blur the picture anyway, to free GPU time for a higher render scale or a "
|
||||
"steadier frame rate. Higher levels start closer to the centre; High also "
|
||||
"coarsens the corners of the HUD. Menus are never foveated. Applies immediately."
|
||||
: "Shades the edges of the race view in 2x2, then 4x4 pixel blocks, where the "
|
||||
"lenses blur the picture anyway, to free GPU time. This session started with it "
|
||||
"off, or without a GPU that supports it: a new level applies after a restart.");
|
||||
}
|
||||
#endif
|
||||
if (ImGui::Combo("VR frame interpolation (experimental)", &g_vrFrameInterpolationMode,
|
||||
kVrInterpolationLabels.data(), static_cast<int>(kVrInterpolationLabels.size()))) {
|
||||
const auto target = kVrInterpolationFps[static_cast<size_t>(g_vrFrameInterpolationMode)];
|
||||
@@ -1471,65 +1500,21 @@ void DrawVrSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
// Like the mirror above and unlike the enable toggle, these two apply to the
|
||||
// very next frame, so they can be compared from inside a running race.
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VR eye replay (EFB)");
|
||||
if (ImGui::Checkbox("Stop eye at display copy", &g_vrStopAtDisplayCopy)) {
|
||||
aurora_set_stereo_stop_at_display_copy(g_vrStopAtDisplayCopy);
|
||||
RuntimeConfigFile::SetVrStopAtDisplayCopy(g_vrStopAtDisplayCopy);
|
||||
if (ImGui::Combo("Desktop view", &g_vrMirrorView, kVrMirrorViewLabels.data(),
|
||||
static_cast<int>(kVrMirrorViewLabels.size()))) {
|
||||
aurora_set_stereo_mirror_view(static_cast<AuroraStereoMirrorView>(g_vrMirrorView));
|
||||
RuntimeConfigFile::SetVrMirrorView(kVrMirrorViewNames[static_cast<size_t>(g_vrMirrorView)]);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Ends each eye at the frame's final GXCopyDisp, so an eye holds exactly the "
|
||||
"image the game presented. Turn off to replay the whole pass list.");
|
||||
}
|
||||
if (ImGui::Checkbox("Skip EFB copy clears", &g_vrSkipCopyClears)) {
|
||||
aurora_set_stereo_skip_copy_clears(g_vrSkipCopyClears);
|
||||
RuntimeConfigFile::SetVrSkipCopyClears(g_vrSkipCopyClears);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Drops the EFB reset a GX copy performs after copying. That reset prepares the "
|
||||
"Wii's reused EFB for the next frame; an eye attachment is built fresh, so "
|
||||
"replaying it only erases the eye.");
|
||||
}
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + Scaled(380.0f));
|
||||
ImGui::TextDisabled(
|
||||
"Both apply on the next frame. Turning either off restores the raw replay and is "
|
||||
"expected to black out the eyes.");
|
||||
ImGui::PopTextWrapPos();
|
||||
// Shows the live state, which the Quest's debug.wiicompiled.eye_passes can override.
|
||||
g_vrSinglePassEyes = aurora_get_stereo_single_pass_eyes();
|
||||
if (ImGui::Checkbox("One render pass per eye", &g_vrSinglePassEyes)) {
|
||||
aurora_set_stereo_single_pass_eyes(g_vrSinglePassEyes);
|
||||
RuntimeConfigFile::SetVrSinglePassEyes(g_vrSinglePassEyes);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Keeps drawing each eye in the render pass it has open across the frame's GX "
|
||||
"copies, which only the desktop image performs, and skips what a later clear "
|
||||
"erases. Same picture, less GPU memory traffic; turn off to compare.");
|
||||
"What this window shows while the headset is running. Normal keeps the ordinary "
|
||||
"desktop view, the eye choices mirror what you are actually seeing in the headset, "
|
||||
"and None leaves the window black. Menus reach the headset as a screen showing this "
|
||||
"same desktop image, so the eye choices only differ from Normal during a race.");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VR 2D layer");
|
||||
ImGui::BeginDisabled(
|
||||
g_vrFlatScreen || g_vrRaceView == static_cast<int>(RuntimeConfigFile::VrRaceView::ImmersiveWindow));
|
||||
if (ImGui::Checkbox("2D layer on a virtual screen", &g_vrHudVirtualScreen)) {
|
||||
ApplyVrHudVirtualScreen();
|
||||
RuntimeConfigFile::SetVrHudVirtualScreen(g_vrHudVirtualScreen);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
ImGui::SetTooltip(
|
||||
"Puts the minimap, race position, item roulette and the rest of the race HUD on a "
|
||||
"screen fixed ahead of the kart camera. Turn off to leave them stretched across "
|
||||
"the whole view. Its size and distance are the [vr] hud_width_meters and "
|
||||
"hud_distance_meters read at launch. The immersive window is that screen, and "
|
||||
"always carries them.");
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VR view");
|
||||
if (ImGui::Button("Recenter view")) {
|
||||
mkw::vr::OpenXRRequestRecenter();
|
||||
}
|
||||
@@ -1576,59 +1561,46 @@ void DrawVrSettings() {
|
||||
"does pitch the view, and looking sideways while it is set will roll the "
|
||||
"horizon the way a real recline would.");
|
||||
}
|
||||
#if defined(__ANDROID__)
|
||||
if (ImGui::Checkbox("Passthrough around the menu screen", &g_vrPassthrough)) {
|
||||
mkw::vr::OpenXRSetPassthrough(g_vrPassthrough);
|
||||
RuntimeConfigFile::SetVrPassthrough(g_vrPassthrough);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Shows your room through the headset's cameras around the menu screen and every "
|
||||
"other screen outside an immersive race, instead of black. Immersive races stay "
|
||||
"fully virtual; the immersive window and the Flat Screen race have the room "
|
||||
"around them too. Applies immediately.");
|
||||
}
|
||||
// Shows the live level, which debug.wiicompiled.foveation can override.
|
||||
g_vrFoveation = static_cast<int>(aurora_get_stereo_foveation());
|
||||
if (ImGui::Combo("Foveated rendering", &g_vrFoveation, kVrFoveationLabels.data(),
|
||||
static_cast<int>(kVrFoveationLabels.size()))) {
|
||||
aurora_set_stereo_foveation(static_cast<uint32_t>(g_vrFoveation));
|
||||
RuntimeConfigFile::SetVrFoveation(
|
||||
std::string(RuntimeConfigFile::kVrFoveationLevels[static_cast<size_t>(g_vrFoveation)]));
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"%s", aurora_stereo_foveation_available()
|
||||
? "Shades the edges of the race view in 2x2, then 4x4 pixel blocks, where the "
|
||||
"lenses blur the picture anyway, to free GPU time for a higher render scale or a "
|
||||
"steadier frame rate. Higher levels start closer to the centre; High also "
|
||||
"coarsens the corners of the HUD. Menus are never foveated. Applies immediately."
|
||||
: "Shades the edges of the race view in 2x2, then 4x4 pixel blocks, where the "
|
||||
"lenses blur the picture anyway, to free GPU time. This session started with it "
|
||||
"off, or without a GPU that supports it: a new level applies after a restart.");
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VR camera");
|
||||
if (ImGui::Combo("Race view", &g_vrRaceView, kVrRaceViewLabels.data(),
|
||||
static_cast<int>(kVrRaceViewLabels.size()))) {
|
||||
const auto view = static_cast<RuntimeConfigFile::VrRaceView>(g_vrRaceView);
|
||||
g_vrFlatScreen = view == RuntimeConfigFile::VrRaceView::FlatScreen;
|
||||
RuntimeConfigFile::SetVrRaceView(view);
|
||||
mkw::vr::MkwVRPolicySetImmersiveRaces(!g_vrFlatScreen);
|
||||
mkw::vr::OpenXRSetImmersiveWindow(view == RuntimeConfigFile::VrRaceView::ImmersiveWindow);
|
||||
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
|
||||
ImGui::BeginDisabled(
|
||||
g_vrFlatScreen || g_vrRaceView == static_cast<int>(RuntimeConfigFile::VrRaceView::ImmersiveWindow));
|
||||
if (ImGui::Checkbox("2D layer on a virtual screen", &g_vrHudVirtualScreen)) {
|
||||
ApplyVrHudVirtualScreen();
|
||||
RuntimeConfigFile::SetVrHudVirtualScreen(g_vrHudVirtualScreen);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
ImGui::SetTooltip(
|
||||
"Immersive plays races all around you in stereo. Immersive window keeps that "
|
||||
"stereo view but shows it only through a window where the menu screen sits, with "
|
||||
"your room around it on the Quest (black elsewhere); look through it from another "
|
||||
"angle and the view shifts as through a real window. Flat screen plays races on "
|
||||
"the menu screen through the game's own camera; the first-person camera, hand "
|
||||
"steering and the race view settings do not apply to it. Applies immediately.");
|
||||
"Puts the minimap, race position, item roulette and the rest of the race HUD on a "
|
||||
"screen fixed ahead of the kart camera. Turn off to leave them stretched across "
|
||||
"the whole view. Its size and distance are the [vr] hud_width_meters and "
|
||||
"hud_distance_meters read at launch. The immersive window is that screen, and "
|
||||
"always carries them.");
|
||||
}
|
||||
ImGui::BeginDisabled(!mkw::vr::OpenXRIsRunning());
|
||||
bool settingsPanelOpen = mkw::vr::OpenXRSettingsPanelOpen();
|
||||
if (ImGui::Checkbox("Show these settings in the headset", &settingsPanelOpen)) {
|
||||
mkw::vr::OpenXRSetSettingsPanelOpen(settingsPanelOpen);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
ImGui::SetTooltip(
|
||||
"Opens these settings on a panel in front of you, in menus and races alike.\n"
|
||||
"In the headset, left Y opens and closes it too (with Gamepad VR controllers,\n"
|
||||
"click both thumbsticks together instead).\n"
|
||||
"Aim at it and pull a trigger to change a setting; push a thumbstick to scroll.\n"
|
||||
"While it is open the game does not see the VR controllers.");
|
||||
}
|
||||
}
|
||||
|
||||
// The first-person camera and its cockpit's steering wheel: the F10 bar's Camera menu and the
|
||||
// headset panel's Camera tab.
|
||||
void DrawVrCameraSettings() {
|
||||
// All of it shapes the immersive race view, which Flat Screen mode (VR > Race view) replaces.
|
||||
if (g_vrFlatScreen) {
|
||||
ImGui::TextDisabled("Race view (under VR) is Flat screen: these apply to the immersive views.");
|
||||
}
|
||||
// Everything below shapes the immersive race view, which Flat Screen mode replaces.
|
||||
ImGui::BeginDisabled(g_vrFlatScreen);
|
||||
if (ImGui::Checkbox("First-person camera", &g_vrFirstPerson)) {
|
||||
RuntimeConfigFile::SetVrFirstPerson(g_vrFirstPerson);
|
||||
@@ -2155,6 +2127,11 @@ void DrawTopBar() {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Camera")) {
|
||||
DrawVrCameraSettings();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Controller settings")) {
|
||||
DrawControllerSettings();
|
||||
// Nest capture under this menu so opening/closing the modal preserves
|
||||
@@ -2351,6 +2328,7 @@ void DrawVrSettingsPanelWindow() {
|
||||
}
|
||||
};
|
||||
tab("VR", DrawVrSettings);
|
||||
tab("Camera", DrawVrCameraSettings);
|
||||
tab("Graphics", [] {
|
||||
DrawResolutionMenu();
|
||||
ImGui::Separator();
|
||||
@@ -2435,9 +2413,6 @@ 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);
|
||||
aurora_set_stereo_stop_at_display_copy(g_vrStopAtDisplayCopy);
|
||||
aurora_set_stereo_skip_copy_clears(g_vrSkipCopyClears);
|
||||
aurora_set_stereo_single_pass_eyes(g_vrSinglePassEyes);
|
||||
#if defined(__ANDROID__)
|
||||
aurora_set_stereo_foveation(static_cast<uint32_t>(g_vrFoveation));
|
||||
#endif
|
||||
|
||||
@@ -498,6 +498,13 @@ public:
|
||||
interpolation_stopping_ = false;
|
||||
}
|
||||
teardown_requested_.store(false, std::memory_order_release);
|
||||
// How every eye is replayed, fixed: it ends at the frame's final GXCopyDisp, so it holds the
|
||||
// image the game presented; it keeps the EFB reset after a display copy, which can only be
|
||||
// an earlier one's and erases what that last copy did not show; and it is drawn in one
|
||||
// render pass (the Quest's debug.wiicompiled.eye_passes splits it again, for A/B timing).
|
||||
aurora_set_stereo_stop_at_display_copy(true);
|
||||
aurora_set_stereo_skip_copy_clears(false);
|
||||
aurora_set_stereo_single_pass_eyes(true);
|
||||
WithdrawPublishedFrame();
|
||||
aurora_set_stereo_frame_provider(&OpenXRIntegration::ProvideStereoFrame, this);
|
||||
provider_registered_ = true;
|
||||
@@ -1640,7 +1647,8 @@ private:
|
||||
|
||||
// Android: `adb shell setprop debug.wiicompiled.eye_passes 0` replays each eye in one render
|
||||
// pass per recorded pass again, and 1 forces the single pass, to compare the two within one
|
||||
// session. An empty value hands the switch back to the settings. Read about once a second.
|
||||
// session. An empty value restores the single pass every eye has otherwise. Read about once a
|
||||
// second.
|
||||
void PollEyePassesOverride() noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
if (eye_passes_poll_ != 0) {
|
||||
@@ -1658,8 +1666,7 @@ private:
|
||||
return;
|
||||
}
|
||||
eye_passes_override_ = override_value;
|
||||
const bool single =
|
||||
override_value >= 0 ? override_value == 1 : RuntimeConfigFile::VrSinglePassEyes();
|
||||
const bool single = override_value != 0;
|
||||
aurora_set_stereo_single_pass_eyes(single);
|
||||
RT_LOG(RT_TAG_RUNTIME) << "OpenXR: eyes replayed in "
|
||||
<< (single ? "one render pass" : "one render pass per recorded pass")
|
||||
|
||||
@@ -30,11 +30,6 @@ int main() {
|
||||
Require(RuntimeConfigFile::VrFoveationLevelIndex("high") == 3);
|
||||
Require(RuntimeConfigFile::VrFoveationLevelIndex("ultra") == 0);
|
||||
|
||||
// [vr] single_pass_eyes: each eye replayed in one render pass.
|
||||
Require(Parse("[vr]\nsingle_pass_eyes = true\n").vrSinglePassEyes == true);
|
||||
Require(Parse("[vr]\nsingle_pass_eyes = false\n").vrSinglePassEyes == false);
|
||||
Require(!Parse("[vr]\n").vrSinglePassEyes.has_value());
|
||||
|
||||
// [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);
|
||||
|
||||
Reference in new issue
Block a user