Replay each VR eye in one render pass

- Added eye_pass_plan.hpp: an eye keeps drawing in the render pass it has open across the frame's
  GX copies (only the mono render performs them), skips passes a later clear of the whole color and
  depth erases, and splits only for a clear of color alone or depth alone.
- render_stereo_eye follows that plan; the cockpit fallback is drawn inside the last pass instead of
  a render pass of its own that loaded the eye back.
- The stencil is now cleared where the eye actually starts, so a cockpit mask drawn in an erased pass
  can no longer punch holes in the HUD.
- [vr] single_pass_eyes (default on, live from F10) and debug.wiicompiled.eye_passes 0/1 on the Quest
  switch back to one render pass per recorded pass for A/B timing.
- Each new plan shape is logged once ("Eye replay plan: ...").
- Tests: EyePassPlan cases in gx_fifo_tests; stereo_multiplayer_smoke and stereo_frame_worker_smoke
  pass on D3D12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
iChris4andClaude Opus 5.5 committed 2026-09-24 18:18:08 +02:00
1 parent 13311ea626
commit ceeeba0332
12 files changed
+527 -18

No files matched your search

+15
View File
@@ -61,6 +61,7 @@ struct RuntimeUserConfig {
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;
@@ -509,6 +510,10 @@ inline void EnsureConfigFile() {
"# 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"
@@ -757,6 +762,7 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
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");
@@ -1088,6 +1094,11 @@ inline bool SetVrSkipCopyClears(bool 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");
@@ -1553,6 +1564,10 @@ 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);
}