From 4bdcf342b63e5adff8a1338a34d1ec3ec570b153 Mon Sep 17 00:00:00 2001 From: David Griswold Date: Sat, 3 Jan 2026 23:40:59 +0300 Subject: [PATCH] video_core: Stereo Settings Improvements: Full Width SBS, Swap Eyes, Select which display(s) to render on (#1212) --- .../citra/citra_emu/display/ScreenLayout.kt | 33 +++++++ .../features/settings/model/BooleanSetting.kt | 4 +- .../features/settings/model/IntSetting.kt | 3 +- .../settings/ui/SettingsFragmentPresenter.kt | 44 +++++++-- src/android/app/src/main/jni/config.cpp | 3 +- src/android/app/src/main/jni/default_ini.h | 12 ++- .../app/src/main/res/values/arrays.xml | 18 +++- .../app/src/main/res/values/strings.xml | 10 +- src/citra_qt/configuration/config.cpp | 4 + .../configuration/configure_enhancements.cpp | 2 + .../configuration/configure_enhancements.ui | 27 +++-- src/citra_sdl/default_ini.h | 4 + src/common/settings.cpp | 4 + src/common/settings.h | 14 ++- src/core/frontend/emu_window.cpp | 93 ++++++++++------- src/core/frontend/emu_window.h | 2 + src/core/frontend/framebuffer_layout.cpp | 99 ++++++++++--------- src/core/frontend/framebuffer_layout.h | 2 + .../renderer_opengl/renderer_opengl.cpp | 98 +++++++++--------- .../renderer_opengl/renderer_opengl.h | 2 +- .../renderer_vulkan/renderer_vulkan.cpp | 79 +++++++-------- .../renderer_vulkan/renderer_vulkan.h | 2 +- 22 files changed, 358 insertions(+), 201 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt index c3877560c..c46dcadd8 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt @@ -63,4 +63,37 @@ enum class SecondaryDisplayLayout(val int: Int) { return entries.firstOrNull { it.int == int } ?: NONE } } +} + +enum class StereoWhichDisplay(val int: Int) { + // These must match what is defined in src/common/settings.h + + NONE(0), // equivalent to StereoRenderOption = Off + BOTH(1), + PRIMARY_ONLY(2), + SECONDARY_ONLY(3); + + companion object { + fun from(int: Int): StereoWhichDisplay { + return entries.firstOrNull { it.int == int } ?: NONE + } + } +} + +enum class StereoMode(val int: Int) { + // These must match what is defined in src/common/settings.h + + OFF(0), + SIDE_BY_SIDE(1), + SIDE_BY_SIDE_FULL(2), + ANAGLYPH(3), + INTERLACED(4), + REVERSE_INTERLACED (5), + CARDBOARD_VR (6); + + companion object { + fun from(int: Int): StereoMode { + return entries.firstOrNull { it.int == int } ?: OFF + } + } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt index e486a4d96..f06324251 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt @@ -19,6 +19,7 @@ enum class BooleanSetting( INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false), ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false), CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false), + SWAP_EYES_3D("swap_eyes_3d",Settings.SECTION_RENDERER,false), PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true), PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false), @@ -87,7 +88,8 @@ enum class BooleanSetting( USE_ARTIC_BASE_CONTROLLER, COMPRESS_INSTALLED_CIA_CONTENT, ANDROID_HIDE_IMAGES, - PERF_OVERLAY_ENABLE // Works in overlay options, but not from the settings menu + PERF_OVERLAY_ENABLE, // Works in overlay options, but not from the settings menu + APPLY_REGION_FREE_PATCH ) fun from(key: String): BooleanSetting? = diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index 5b2016ac9..e26bcd6c6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -17,7 +17,7 @@ enum class IntSetting( CAMERA_OUTER_RIGHT_FLIP("camera_outer_right_flip", Settings.SECTION_CAMERA, 0), GRAPHICS_API("graphics_api", Settings.SECTION_RENDERER, 1), RESOLUTION_FACTOR("resolution_factor", Settings.SECTION_RENDERER, 1), - STEREOSCOPIC_3D_MODE("render_3d", Settings.SECTION_RENDERER, 0), + STEREOSCOPIC_3D_MODE("render_3d", Settings.SECTION_RENDERER, 2), STEREOSCOPIC_3D_DEPTH("factor_3d", Settings.SECTION_RENDERER, 0), STEPS_PER_HOUR("steps_per_hour", Settings.SECTION_SYSTEM, 0), CARDBOARD_SCREEN_SIZE("cardboard_screen_size", Settings.SECTION_LAYOUT, 85), @@ -53,6 +53,7 @@ enum class IntSetting( ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2), TURBO_LIMIT("turbo_limit", Settings.SECTION_CORE, 200), PERFORMANCE_OVERLAY_POSITION("performance_overlay_position", Settings.SECTION_LAYOUT, 0), + RENDER_3D_WHICH_DISPLAY("render_3d_which_display",Settings.SECTION_RENDERER,0), ASPECT_RATIO("aspect_ratio", Settings.SECTION_LAYOUT, 0); override var int: Int = defaultValue diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index cb41525a1..1326401d5 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -16,6 +16,9 @@ import androidx.preference.PreferenceManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.citra.citra_emu.CitraApplication import org.citra.citra_emu.R +import org.citra.citra_emu.display.ScreenLayout +import org.citra.citra_emu.display.StereoMode +import org.citra.citra_emu.display.StereoWhichDisplay import org.citra.citra_emu.features.settings.model.AbstractBooleanSetting import org.citra.citra_emu.features.settings.model.AbstractIntSetting import org.citra.citra_emu.features.settings.model.AbstractSetting @@ -948,17 +951,30 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) ) add(HeaderSetting(R.string.stereoscopy)) + add( + SingleChoiceSetting( + IntSetting.RENDER_3D_WHICH_DISPLAY, + R.string.render_3d_which_display, + R.string.render_3d_which_display_description, + R.array.render3dWhichDisplay, + R.array.render3dDisplayValues, + IntSetting.RENDER_3D_WHICH_DISPLAY.key, + IntSetting.RENDER_3D_WHICH_DISPLAY.defaultValue + ) + ) add( SingleChoiceSetting( IntSetting.STEREOSCOPIC_3D_MODE, R.string.render3d, - 0, + R.string.render3d_description, R.array.render3dModes, R.array.render3dValues, IntSetting.STEREOSCOPIC_3D_MODE.key, - IntSetting.STEREOSCOPIC_3D_MODE.defaultValue + IntSetting.STEREOSCOPIC_3D_MODE.defaultValue, + isEnabled = IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int ) ) + add( SliderSetting( IntSetting.STEREOSCOPIC_3D_DEPTH, @@ -981,6 +997,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) ) ) + add( + SwitchSetting( + BooleanSetting.SWAP_EYES_3D, + R.string.swap_eyes_3d, + R.string.swap_eyes_3d_description, + BooleanSetting.SWAP_EYES_3D.key, + BooleanSetting.SWAP_EYES_3D.defaultValue, + isEnabled = IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int + ) + ) + add(HeaderSetting(R.string.cardboard_vr)) add( SliderSetting( @@ -991,7 +1018,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) 100, "%", IntSetting.CARDBOARD_SCREEN_SIZE.key, - IntSetting.CARDBOARD_SCREEN_SIZE.defaultValue.toFloat() + IntSetting.CARDBOARD_SCREEN_SIZE.defaultValue.toFloat(), + isEnabled = IntSetting.STEREOSCOPIC_3D_MODE.int == StereoMode.CARDBOARD_VR.int ) ) add( @@ -1003,7 +1031,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) 100, "%", IntSetting.CARDBOARD_X_SHIFT.key, - IntSetting.CARDBOARD_X_SHIFT.defaultValue.toFloat() + IntSetting.CARDBOARD_X_SHIFT.defaultValue.toFloat(), + isEnabled = IntSetting.STEREOSCOPIC_3D_MODE.int == StereoMode.CARDBOARD_VR.int ) ) add( @@ -1015,7 +1044,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) 100, "%", IntSetting.CARDBOARD_Y_SHIFT.key, - IntSetting.CARDBOARD_Y_SHIFT.defaultValue.toFloat() + IntSetting.CARDBOARD_Y_SHIFT.defaultValue.toFloat(), + isEnabled = IntSetting.STEREOSCOPIC_3D_MODE.int == StereoMode.CARDBOARD_VR.int ) ) @@ -1149,7 +1179,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) R.array.aspectRatioValues, IntSetting.ASPECT_RATIO.key, IntSetting.ASPECT_RATIO.defaultValue, - isEnabled = IntSetting.SCREEN_LAYOUT.int == 1, + isEnabled = IntSetting.SCREEN_LAYOUT.int == ScreenLayout.SINGLE_SCREEN.int, ) ) add( @@ -1197,7 +1227,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) "%", FloatSetting.SECOND_SCREEN_OPACITY.key, FloatSetting.SECOND_SCREEN_OPACITY.defaultValue, - isEnabled = IntSetting.SCREEN_LAYOUT.int == 5 + isEnabled = IntSetting.SCREEN_LAYOUT.int == ScreenLayout.CUSTOM_LAYOUT.int ) ) add(HeaderSetting(R.string.bg_color, R.string.bg_color_description)) diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 7cdfe9960..6d310fea6 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -175,7 +175,8 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.custom_second_layer_opacity); ReadSetting("Renderer", Settings::values.delay_game_render_thread_us); ReadSetting("Renderer", Settings::values.disable_right_eye_render); - + ReadSetting("Renderer", Settings::values.swap_eyes_3d); + ReadSetting("Renderer", Settings::values.render_3d_which_display); // Layout // Somewhat inelegant solution to ensure layout value is between 0 and 5 on read // since older config files may have other values diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index c7185f107..08eaf3283 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -170,13 +170,23 @@ bg_green = custom_second_layer_opacity = # Whether and how Stereoscopic 3D should be rendered -# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR +# 0: Off, 1: Half Width Side by Side, 2 (default): Full Width Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR +# 0 is no longer supported in the interface, as using render_3d_which_display = 0 has the same effect, but supported here for backwards compatibility render_3d = # Change 3D Intensity # 0 - 255: Intensity. 0 (default) factor_3d = +# Swap Eyes in 3d +# true: Swap eyes, false (default): Do not swap eyes +swap_eyes_3d = + +# Which Display to render 3d mode to +# 0 (default) - None. Equivalent to render_3d=0 +# 1: Both, 2: Primary Only, 3: Secondary Only +render_3d_which_display = + # The name of the post processing shader to apply. # Loaded from shaders if render_3d is off or side by side. pp_shader_name = diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 17bda5e66..2a08cd546 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -238,9 +238,8 @@ - @string/off @string/side_by_side - @string/reverse_side_by_side + @string/side_by_side_full @string/anaglyph @string/interlaced @string/reverse_interlaced @@ -248,7 +247,6 @@ - 0 1 2 3 @@ -257,6 +255,20 @@ 6 + + @string/off + @string/render_3d_which_display_both + @string/render_3d_which_display_primary + @string/render_3d_which_display_secondary + + + + 0 + 1 + 2 + 3 + + @string/opengles @string/vulkan diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 99c38f608..529030ed8 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -289,10 +289,18 @@ Warning: Modifying these settings will slow emulation Stereoscopy Stereoscopic 3D Mode + Choose the stereoscopic 3D mode for 3D rendering. Side by Side modes are most common in modern use. Anaglyph and Interlaced modes will always apply to all connected displays. Depth Specifies the value of the 3D slider. This should be set to higher than 0% when Stereoscopic 3D is enabled.\nNote: Depth values over 100% are not possible on real hardware and may cause graphical issues Disable Right Eye Render Greatly improves performance in some applications, but can cause flickering in others. + Swap Eyes + Swaps which eye is shown in which side. Combined with Side by Side mode makes it possible to see 3D by crossing your eyes! + Render Stereoscopic 3D + Whether to enable stereoscopic 3D, and on which displays. The single display options are only relevant when multiple displays are connected. + On (All Displays) + On (Primary Display Only) + On (Secondary Display Only) Cardboard VR Cardboard Screen Size Scales the screen to a percentage of its original size. @@ -723,7 +731,7 @@ Side by Side - Reverse Side by Side + Side by Side Full Width Anaglyph Interlaced Reverse Interlaced diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index b793a6042..4c7d99c93 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -517,6 +517,8 @@ void QtConfig::ReadLayoutValues() { ReadGlobalSetting(Settings::values.render_3d); ReadGlobalSetting(Settings::values.factor_3d); + ReadGlobalSetting(Settings::values.swap_eyes_3d); + ReadGlobalSetting(Settings::values.render_3d_which_display); ReadGlobalSetting(Settings::values.filter_mode); ReadGlobalSetting(Settings::values.pp_shader_name); ReadGlobalSetting(Settings::values.anaglyph_shader_name); @@ -1083,6 +1085,8 @@ void QtConfig::SaveLayoutValues() { WriteGlobalSetting(Settings::values.render_3d); WriteGlobalSetting(Settings::values.factor_3d); + WriteGlobalSetting(Settings::values.swap_eyes_3d); + WriteGlobalSetting(Settings::values.render_3d_which_display); WriteGlobalSetting(Settings::values.filter_mode); WriteGlobalSetting(Settings::values.pp_shader_name); WriteGlobalSetting(Settings::values.anaglyph_shader_name); diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index a40654a5d..aba7a7ac8 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -57,6 +57,7 @@ void ConfigureEnhancements::SetConfiguration() { ui->render_3d_combobox->setCurrentIndex( static_cast(Settings::values.render_3d.GetValue())); + ui->swap_eyes_3d->setChecked(Settings::values.swap_eyes_3d.GetValue()); ui->factor_3d->setValue(Settings::values.factor_3d.GetValue()); ui->mono_rendering_eye->setCurrentIndex( static_cast(Settings::values.mono_render_option.GetValue())); @@ -111,6 +112,7 @@ void ConfigureEnhancements::ApplyConfiguration() { ui->resolution_factor_combobox); Settings::values.render_3d = static_cast(ui->render_3d_combobox->currentIndex()); + Settings::values.swap_eyes_3d = ui->swap_eyes_3d->isChecked(); Settings::values.factor_3d = ui->factor_3d->value(); Settings::values.mono_render_option = static_cast(ui->mono_rendering_eye->currentIndex()); diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index f26943444..ebf8520c4 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -113,7 +113,7 @@ - Enable linear filtering + Enable Linear Filtering @@ -236,7 +236,7 @@ - Reverse Side by Side + Side by Side Full Width @@ -318,14 +318,21 @@ - - - Disable right eye rendering - - - <html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.</p></body></html> - - + + + Disable Right Eye Rendering + + + <html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.</p></body></html> + + + + + + + Swap Eyes + + diff --git a/src/citra_sdl/default_ini.h b/src/citra_sdl/default_ini.h index 3fb471449..4c97dbb89 100644 --- a/src/citra_sdl/default_ini.h +++ b/src/citra_sdl/default_ini.h @@ -163,6 +163,10 @@ render_3d = # 0 - 100: Intensity. 0 (default) factor_3d = +# Swap Eyes in 3D +# true or false (default) +swap_eyes_3d = + # Change Default Eye to Render When in Monoscopic Mode # 0 (default): Left, 1: Right mono_render_option = diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 0af00c2bf..564090d0b 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -108,6 +108,9 @@ void LogSettings() { log_setting("Renderer_DisableRightEyeRender", values.disable_right_eye_render.GetValue()); log_setting("Stereoscopy_Render3d", values.render_3d.GetValue()); log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue()); + log_setting("Stereoscopy_Swap_Eyes", values.swap_eyes_3d.GetValue()); + log_setting("Stereoscopy_Render_3d_to_which_display", + values.render_3d_which_display.GetValue()); log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue()); if (values.render_3d.GetValue() == StereoRenderOption::Anaglyph) { log_setting("Renderer_AnaglyphShader", values.anaglyph_shader_name.GetValue()); @@ -219,6 +222,7 @@ void RestoreGlobalState(bool is_powered_on) { values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); values.render_3d.SetGlobal(true); + values.swap_eyes_3d.SetGlobal(true); values.factor_3d.SetGlobal(true); values.filter_mode.SetGlobal(true); values.pp_shader_name.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index ee65c984e..a85ad3c0a 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -72,7 +72,7 @@ enum class SmallScreenPosition : u32 { enum class StereoRenderOption : u32 { Off = 0, SideBySide = 1, - ReverseSideBySide = 2, + SideBySideFull = 2, Anaglyph = 3, Interlaced = 4, ReverseInterlaced = 5, @@ -86,6 +86,14 @@ enum class MonoRenderOption : u32 { RightEye = 1, }; +// on android, which displays to render stereo mode to +enum class StereoWhichDisplay : u32 { + None = 0, // equivalent to StereoRenderOption = Off + Both = 1, + PrimaryOnly = 2, + SecondaryOnly = 3 +}; + enum class AudioEmulation : u32 { HLE = 0, LLE = 1, @@ -562,6 +570,10 @@ struct Values { SwitchableSetting render_3d{StereoRenderOption::Off, "render_3d"}; SwitchableSetting factor_3d{0, "factor_3d"}; + SwitchableSetting swap_eyes_3d{false, "swap_eyes_3d"}; + + SwitchableSetting render_3d_which_display{StereoWhichDisplay::None, + "render_3d_which_display"}; SwitchableSetting mono_render_option{MonoRenderOption::LeftEye, "mono_render_option"}; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index ee81b177e..3216a1675 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -25,9 +25,8 @@ public: std::mutex mutex; bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false - - float touch_x = 0.0f; ///< Touchpad X-position - float touch_y = 0.0f; ///< Touchpad Y-position + float touch_x = 0.0f; ///< Touchpad X-position + float touch_y = 0.0f; ///< Touchpad Y-position private: class Device : public Input::TouchDevice { @@ -56,24 +55,44 @@ EmuWindow::EmuWindow(bool is_secondary_) : is_secondary{is_secondary_} { EmuWindow::~EmuWindow() = default; +Settings::StereoRenderOption EmuWindow::get3DMode() const { + Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); +#ifndef ANDROID + // on desktop, if separate windows and this is the bottom screen, then no stereo + if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows && + ((!is_secondary && Settings::values.swap_screen.GetValue()) || + (is_secondary && !Settings::values.swap_screen.GetValue()))) { + render_3d_mode = Settings::StereoRenderOption::Off; + } +#else + // adjust the StereoRenderOption setting to Off if appropriate on mobile + Settings::StereoWhichDisplay whichDisplay = Settings::values.render_3d_which_display.GetValue(); + if (whichDisplay == Settings::StereoWhichDisplay::None || + whichDisplay == Settings::StereoWhichDisplay::PrimaryOnly && is_secondary || + whichDisplay == Settings::StereoWhichDisplay::SecondaryOnly && !is_secondary) { + render_3d_mode = Settings::StereoRenderOption::Off; + } +#endif + return render_3d_mode; +} + bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, unsigned framebuffer_y) { #ifndef ANDROID // If separate windows and the touch is in the primary (top) screen, ignore it. if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows && - !is_secondary && !Settings::values.swap_screen.GetValue()) { + ((!is_secondary && !Settings::values.swap_screen.GetValue()) || + (is_secondary && Settings::values.swap_screen.GetValue()))) { return false; } #endif + Settings::StereoRenderOption render_3d_mode = get3DMode(); - if (!layout.bottom_screen_enabled) { - return false; + if (framebuffer_x > layout.width / 2 && + render_3d_mode == Settings::StereoRenderOption::SideBySideFull) { + framebuffer_x = static_cast(framebuffer_x - layout.width / 2); } - - Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); - - if (render_3d_mode == Settings::StereoRenderOption::SideBySide || - render_3d_mode == Settings::StereoRenderOption::ReverseSideBySide) { + if (render_3d_mode == Settings::StereoRenderOption::SideBySide) { return (framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom && ((framebuffer_x >= layout.bottom_screen.left / 2 && @@ -97,25 +116,19 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns } std::tuple EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const { - Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); - bool separate_win = false; -#ifndef ANDROID - separate_win = - (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows); -#endif + + Settings::StereoRenderOption render_3d_mode = get3DMode(); if (new_x >= framebuffer_layout.width / 2) { - if ((render_3d_mode == Settings::StereoRenderOption::SideBySide || - render_3d_mode == Settings::StereoRenderOption::ReverseSideBySide) && - !separate_win) + if (render_3d_mode == Settings::StereoRenderOption::SideBySide || + render_3d_mode == Settings::StereoRenderOption::SideBySideFull) new_x -= framebuffer_layout.width / 2; else if (render_3d_mode == Settings::StereoRenderOption::CardboardVR) new_x -= (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); } - if ((render_3d_mode == Settings::StereoRenderOption::SideBySide || - render_3d_mode == Settings::StereoRenderOption::ReverseSideBySide) && - !separate_win) { + + if (render_3d_mode == Settings::StereoRenderOption::SideBySide) { new_x = std::max(new_x, framebuffer_layout.bottom_screen.left / 2); new_x = std::min(new_x, framebuffer_layout.bottom_screen.right / 2 - 1); } else { @@ -140,29 +153,22 @@ void EmuWindow::CreateTouchState() { } bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { - Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); - bool separate_win = false; -#ifndef ANDROID - separate_win = - (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows); -#endif - if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return false; + Settings::StereoRenderOption render_3d_mode = get3DMode(); if (framebuffer_x >= framebuffer_layout.width / 2) { - if ((render_3d_mode == Settings::StereoRenderOption::SideBySide || - render_3d_mode == Settings::StereoRenderOption::ReverseSideBySide) && - !separate_win) + if (render_3d_mode == Settings::StereoRenderOption::SideBySide || + render_3d_mode == Settings::StereoRenderOption::SideBySideFull) framebuffer_x -= framebuffer_layout.width / 2; else if (render_3d_mode == Settings::StereoRenderOption::CardboardVR) framebuffer_x -= (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); } + std::scoped_lock guard(touch_state->mutex); - if ((render_3d_mode == Settings::StereoRenderOption::SideBySide || - render_3d_mode == Settings::StereoRenderOption::ReverseSideBySide) && - !separate_win) { + + if (render_3d_mode == Settings::StereoRenderOption::SideBySide) { touch_state->touch_x = static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left / 2) / (framebuffer_layout.bottom_screen.right / 2 - @@ -204,8 +210,13 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_portrait_mode) { Layout::FramebufferLayout layout; - const Settings::LayoutOption layout_option = Settings::values.layout_option.GetValue(); + const Settings::StereoRenderOption stereo_option = get3DMode(); + bool render_full_stereo = (stereo_option == Settings::StereoRenderOption::SideBySideFull); + bool is_bottom = is_secondary; + if (Settings::values.swap_screen.GetValue()) + is_bottom = !is_bottom; + const Settings::PortraitLayoutOption portrait_layout_option = Settings::values.portrait_layout_option.GetValue(); const auto min_size = is_portrait_mode @@ -215,6 +226,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po width = std::max(width, min_size.first); height = std::max(height, min_size.second); + if (render_full_stereo) { + width = width / 2; + } if (is_portrait_mode) { switch (portrait_layout_option) { case Settings::PortraitLayoutOption::PortraitTopFullWidth: @@ -280,11 +294,16 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po layout = Layout::AndroidSecondaryLayout(width, height); } #endif + + if (render_full_stereo) { + layout.width = width * 2; + } UpdateMinimumWindowSize(min_size); if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { layout = Layout::GetCardboardSettings(layout); } + layout.render_3d_mode = stereo_option; NotifyFramebufferLayoutChanged(layout); } diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index f217a7eac..d5b7803cc 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -258,6 +258,8 @@ public: return is_secondary; } + Settings::StereoRenderOption get3DMode() const; + protected: EmuWindow(); EmuWindow(bool is_secondary); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index c64a5afb1..392db8867 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -79,7 +79,7 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up // TODO: This is kind of gross, make it platform agnostic. -OS #ifdef ANDROID - const float window_aspect_ratio = static_cast(height) / static_cast(width); + const float window_aspect_ratio = static_cast(height) / width; const auto aspect_ratio_setting = Settings::values.aspect_ratio.GetValue(); float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; @@ -413,53 +413,56 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar bool is_portrait) { u32 width, height, gap; gap = (int)(Settings::values.screen_gap.GetValue()) * res_scale; + + FramebufferLayout layout; if (is_portrait) { auto layout_option = Settings::values.portrait_layout_option.GetValue(); switch (layout_option) { case Settings::PortraitLayoutOption::PortraitCustomLayout: - return CustomFrameLayout( - std::max(Settings::values.custom_portrait_top_x.GetValue() + - Settings::values.custom_portrait_top_width.GetValue(), - Settings::values.custom_portrait_bottom_x.GetValue() + - Settings::values.custom_portrait_bottom_width.GetValue()), - std::max(Settings::values.custom_portrait_top_y.GetValue() + - Settings::values.custom_portrait_top_height.GetValue(), - Settings::values.custom_portrait_bottom_y.GetValue() + - Settings::values.custom_portrait_bottom_height.GetValue()), - Settings::values.swap_screen.GetValue(), is_portrait); + width = std::max(Settings::values.custom_portrait_top_x.GetValue() + + Settings::values.custom_portrait_top_width.GetValue(), + Settings::values.custom_portrait_bottom_x.GetValue() + + Settings::values.custom_portrait_bottom_width.GetValue()); + height = std::max(Settings::values.custom_portrait_top_y.GetValue() + + Settings::values.custom_portrait_top_height.GetValue(), + Settings::values.custom_portrait_bottom_y.GetValue() + + Settings::values.custom_portrait_bottom_height.GetValue()); + layout = CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + is_portrait); + + break; case Settings::PortraitLayoutOption::PortraitTopFullWidth: width = Core::kScreenTopWidth * res_scale; // clang-format off height = (static_cast(Core::kScreenTopHeight + Core::kScreenBottomHeight * 1.25) * res_scale) + gap; // clang-format on - return PortraitTopFullFrameLayout(width, height, - Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); + layout = + PortraitTopFullFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); + break; case Settings::PortraitLayoutOption::PortraitOriginal: width = Core::kScreenTopWidth * res_scale; height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; - return PortraitOriginalLayout(width, height, Settings::values.swap_screen.GetValue()); + layout = PortraitOriginalLayout(width, height, Settings::values.swap_screen.GetValue()); + break; } } else { auto layout_option = Settings::values.layout_option.GetValue(); switch (layout_option) { case Settings::LayoutOption::CustomLayout: - return CustomFrameLayout(std::max(Settings::values.custom_top_x.GetValue() + - Settings::values.custom_top_width.GetValue(), - Settings::values.custom_bottom_x.GetValue() + - Settings::values.custom_bottom_width.GetValue()), - std::max(Settings::values.custom_top_y.GetValue() + - Settings::values.custom_top_height.GetValue(), - Settings::values.custom_bottom_y.GetValue() + - Settings::values.custom_bottom_height.GetValue()), - Settings::values.swap_screen.GetValue(), is_portrait); - - case Settings::LayoutOption::SingleScreen: -#ifndef ANDROID - case Settings::LayoutOption::SeparateWindows: -#endif - { + layout = + CustomFrameLayout(std::max(Settings::values.custom_top_x.GetValue() + + Settings::values.custom_top_width.GetValue(), + Settings::values.custom_bottom_x.GetValue() + + Settings::values.custom_bottom_width.GetValue()), + std::max(Settings::values.custom_top_y.GetValue() + + Settings::values.custom_top_height.GetValue(), + Settings::values.custom_bottom_y.GetValue() + + Settings::values.custom_bottom_height.GetValue()), + Settings::values.swap_screen.GetValue(), is_portrait); + break; + case Settings::LayoutOption::SingleScreen: { const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue(); if (swap_screens) { width = Core::kScreenBottomWidth * res_scale; @@ -471,8 +474,10 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar if (Settings::values.upright_screen.GetValue()) { std::swap(width, height); } - return SingleFrameLayout(width, height, swap_screens, - Settings::values.upright_screen.GetValue()); + + layout = SingleFrameLayout(width, height, swap_screens, + Settings::values.upright_screen.GetValue()); + break; } case Settings::LayoutOption::LargeScreen: { @@ -501,10 +506,11 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar if (Settings::values.upright_screen.GetValue()) { std::swap(width, height); } - return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue(), - Settings::values.large_screen_proportion.GetValue(), - Settings::values.small_screen_position.GetValue()); + layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue(), + Settings::values.large_screen_proportion.GetValue(), + Settings::values.small_screen_position.GetValue()); + break; } case Settings::LayoutOption::SideScreen: width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale + gap; @@ -513,10 +519,10 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar if (Settings::values.upright_screen.GetValue()) { std::swap(width, height); } - return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue(), 1, - Settings::SmallScreenPosition::MiddleRight); - + layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue(), 1, + Settings::SmallScreenPosition::MiddleRight); + break; case Settings::LayoutOption::HybridScreen: height = Core::kScreenTopHeight * res_scale; @@ -532,9 +538,9 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar std::swap(width, height); } - return HybridScreenLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); - + layout = HybridScreenLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); + break; case Settings::LayoutOption::Default: default: width = Core::kScreenTopWidth * res_scale; @@ -543,10 +549,13 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar if (Settings::values.upright_screen.GetValue()) { std::swap(width, height); } - return DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); + layout = DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); + break; } } + + return layout; UNREACHABLE(); } diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 8fcb56549..1d15e2c89 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -48,6 +48,8 @@ struct FramebufferLayout { u32 GetScalingRatio() const; static float GetAspectRatioValue(Settings::AspectRatio aspect_ratio); + + Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); }; /** diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e8ec1682c..967fd21fa 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -326,7 +326,7 @@ void RendererOpenGL::InitOpenGLObjects() { glSamplerParameteri(samplers[i].handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - ReloadShader(); + ReloadShader(Settings::values.render_3d.GetValue()); // Generate VBO handle for drawing vertex_buffer.Create(); @@ -372,10 +372,10 @@ void RendererOpenGL::InitOpenGLObjects() { state.Apply(); } -void RendererOpenGL::ReloadShader() { +void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) { // Link shaders and get variable locations std::string shader_data = fragment_shader_precision_OES; - if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { + if (render_3d == Settings::StereoRenderOption::Anaglyph) { if (Settings::values.anaglyph_shader_name.GetValue() == "Dubois (builtin)") { shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG; } else { @@ -388,9 +388,8 @@ void RendererOpenGL::ReloadShader() { shader_data += shader_text; } } - } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d.GetValue() == - Settings::StereoRenderOption::ReverseInterlaced) { + } else if (render_3d == Settings::StereoRenderOption::Interlaced || + render_3d == Settings::StereoRenderOption::ReverseInterlaced) { shader_data += HostShaders::OPENGL_PRESENT_INTERLACED_FRAG; } else { if (Settings::values.pp_shader_name.GetValue() == "None (builtin)") { @@ -411,17 +410,16 @@ void RendererOpenGL::ReloadShader() { state.Apply(); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture"); - if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph || - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced) { + if (render_3d == Settings::StereoRenderOption::Anaglyph || + render_3d == Settings::StereoRenderOption::Interlaced || + render_3d == Settings::StereoRenderOption::ReverseInterlaced) { uniform_color_texture_r = glGetUniformLocation(shader.handle, "color_texture_r"); } - if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced) { + if (render_3d == Settings::StereoRenderOption::Interlaced || + render_3d == Settings::StereoRenderOption::ReverseInterlaced) { GLuint uniform_reverse_interlaced = glGetUniformLocation(shader.handle, "reverse_interlaced"); - if (Settings::values.render_3d.GetValue() == - Settings::StereoRenderOption::ReverseInterlaced) + if (render_3d == Settings::StereoRenderOption::ReverseInterlaced) glUniform1i(uniform_reverse_interlaced, 1); else glUniform1i(uniform_reverse_interlaced, 0); @@ -658,7 +656,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f // Update fragment shader before drawing shader.Release(); // Link shaders and get variable locations - ReloadShader(); + ReloadShader(layout.render_3d_mode); } const auto& top_screen = layout.top_screen; @@ -676,9 +674,9 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f glUniform1i(uniform_color_texture, 0); const bool stereo_single_screen = - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph || - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced; + layout.render_3d_mode == Settings::StereoRenderOption::Anaglyph || + layout.render_3d_mode == Settings::StereoRenderOption::Interlaced || + layout.render_3d_mode == Settings::StereoRenderOption::ReverseInterlaced; // Bind a second texture for the right eye if in Anaglyph mode if (stereo_single_screen) { @@ -730,6 +728,9 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, if (!layout.top_screen_enabled) { return; } + int leftside, rightside; + leftside = Settings::values.swap_eyes_3d.GetValue() ? 1 : 0; + rightside = Settings::values.swap_eyes_3d.GetValue() ? 0 : 1; const float top_screen_left = static_cast(top_screen.left); const float top_screen_top = static_cast(top_screen.top); @@ -738,7 +739,7 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, const auto orientation = layout.is_rotated ? Layout::DisplayOrientation::Landscape : Layout::DisplayOrientation::Portrait; - switch (Settings::values.render_3d.GetValue()) { + switch (layout.render_3d_mode) { case Settings::StereoRenderOption::Off: { const int eye = static_cast(Settings::values.mono_render_option.GetValue()); DrawSingleScreen(screen_infos[eye], top_screen_left, top_screen_top, top_screen_width, @@ -746,29 +747,29 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, break; } case Settings::StereoRenderOption::SideBySide: { - DrawSingleScreen(screen_infos[0], top_screen_left / 2, top_screen_top, top_screen_width / 2, - top_screen_height, orientation); + DrawSingleScreen(screen_infos[leftside], top_screen_left / 2, top_screen_top, + top_screen_width / 2, top_screen_height, orientation); glUniform1i(uniform_layer, 1); - DrawSingleScreen(screen_infos[1], + DrawSingleScreen(screen_infos[rightside], static_cast((top_screen_left / 2) + (layout.width / 2)), top_screen_top, top_screen_width / 2, top_screen_height, orientation); break; } - case Settings::StereoRenderOption::ReverseSideBySide: { - DrawSingleScreen(screen_infos[1], top_screen_left / 2, top_screen_top, top_screen_width / 2, + case Settings::StereoRenderOption::SideBySideFull: { + DrawSingleScreen(screen_infos[leftside], top_screen_left, top_screen_top, top_screen_width, top_screen_height, orientation); glUniform1i(uniform_layer, 1); - DrawSingleScreen(screen_infos[0], - static_cast((top_screen_left / 2) + (layout.width / 2)), - top_screen_top, top_screen_width / 2, top_screen_height, orientation); + DrawSingleScreen(screen_infos[rightside], + static_cast(top_screen_left + layout.width / 2), top_screen_top, + top_screen_width, top_screen_height, orientation); break; } case Settings::StereoRenderOption::CardboardVR: { - DrawSingleScreen(screen_infos[0], top_screen_left, top_screen_top, top_screen_width, + DrawSingleScreen(screen_infos[leftside], top_screen_left, top_screen_top, top_screen_width, top_screen_height, orientation); glUniform1i(uniform_layer, 1); DrawSingleScreen( - screen_infos[1], + screen_infos[rightside], static_cast(layout.cardboard.top_screen_right_eye + (layout.width / 2)), top_screen_top, top_screen_width, top_screen_height, orientation); break; @@ -776,8 +777,8 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, case Settings::StereoRenderOption::Anaglyph: case Settings::StereoRenderOption::Interlaced: case Settings::StereoRenderOption::ReverseInterlaced: { - DrawSingleScreenStereo(screen_infos[0], screen_infos[1], top_screen_left, top_screen_top, - top_screen_width, top_screen_height, orientation); + DrawSingleScreenStereo(screen_infos[leftside], screen_infos[rightside], top_screen_left, + top_screen_top, top_screen_width, top_screen_height, orientation); break; } } @@ -797,31 +798,30 @@ void RendererOpenGL::DrawBottomScreen(const Layout::FramebufferLayout& layout, const auto orientation = layout.is_rotated ? Layout::DisplayOrientation::Landscape : Layout::DisplayOrientation::Portrait; - bool separate_win = false; -#ifndef ANDROID - separate_win = - (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows); -#endif - - switch (Settings::values.render_3d.GetValue()) { + switch (layout.render_3d_mode) { case Settings::StereoRenderOption::Off: { DrawSingleScreen(screen_infos[2], bottom_screen_left, bottom_screen_top, bottom_screen_width, bottom_screen_height, orientation); break; } case Settings::StereoRenderOption::SideBySide: // Bottom screen is identical on both sides - case Settings::StereoRenderOption::ReverseSideBySide: { - if (separate_win) { - DrawSingleScreen(screen_infos[2], bottom_screen_left, bottom_screen_top, - bottom_screen_width, bottom_screen_height, orientation); - } else { - DrawSingleScreen(screen_infos[2], bottom_screen_left / 2, bottom_screen_top, - bottom_screen_width / 2, bottom_screen_height, orientation); - glUniform1i(uniform_layer, 1); - DrawSingleScreen( - screen_infos[2], static_cast((bottom_screen_left / 2) + (layout.width / 2)), - bottom_screen_top, bottom_screen_width / 2, bottom_screen_height, orientation); - } + { + + DrawSingleScreen(screen_infos[2], bottom_screen_left / 2, bottom_screen_top, + bottom_screen_width / 2, bottom_screen_height, orientation); + glUniform1i(uniform_layer, 1); + DrawSingleScreen( + screen_infos[2], static_cast((bottom_screen_left / 2) + (layout.width / 2)), + bottom_screen_top, bottom_screen_width / 2, bottom_screen_height, orientation); + + break; + } + case Settings::StereoRenderOption::SideBySideFull: { + DrawSingleScreen(screen_infos[2], bottom_screen_left, bottom_screen_top, + bottom_screen_width, bottom_screen_height, orientation); + glUniform1i(uniform_layer, 1); + DrawSingleScreen(screen_infos[2], bottom_screen_left + layout.width / 2, bottom_screen_top, + bottom_screen_width, bottom_screen_height, orientation); break; } case Settings::StereoRenderOption::CardboardVR: { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c37e3833f..2f2b318ed 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -56,7 +56,7 @@ public: private: void InitOpenGLObjects(); - void ReloadShader(); + void ReloadShader(Settings::StereoRenderOption render_3d); void PrepareRendertarget(); void RenderScreenshot(); void RenderToMailbox(const Layout::FramebufferLayout& layout, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index ceb2e2f3a..51f5b2c7f 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -575,8 +575,7 @@ void RendererVulkan::FillScreen(Common::Vec3 color, const TextureInfo& textu }); } -void RendererVulkan::ReloadPipeline() { - const Settings::StereoRenderOption render_3d = Settings::values.render_3d.GetValue(); +void RendererVulkan::ReloadPipeline(Settings::StereoRenderOption render_3d) { switch (render_3d) { case Settings::StereoRenderOption::Anaglyph: current_pipeline = 1; @@ -748,7 +747,9 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, if (!layout.top_screen_enabled) { return; } - + int leftside, rightside; + leftside = Settings::values.swap_eyes_3d.GetValue() ? 1 : 0; + rightside = Settings::values.swap_eyes_3d.GetValue() ? 0 : 1; const float top_screen_left = static_cast(top_screen.left); const float top_screen_top = static_cast(top_screen.top); const float top_screen_width = static_cast(top_screen.GetWidth()); @@ -756,7 +757,7 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, const auto orientation = layout.is_rotated ? Layout::DisplayOrientation::Landscape : Layout::DisplayOrientation::Portrait; - switch (Settings::values.render_3d.GetValue()) { + switch (layout.render_3d_mode) { case Settings::StereoRenderOption::Off: { const int eye = static_cast(Settings::values.mono_render_option.GetValue()); DrawSingleScreen(eye, top_screen_left, top_screen_top, top_screen_width, top_screen_height, @@ -764,35 +765,36 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, break; } case Settings::StereoRenderOption::SideBySide: { - DrawSingleScreen(0, top_screen_left / 2, top_screen_top, top_screen_width / 2, + DrawSingleScreen(leftside, top_screen_left / 2, top_screen_top, top_screen_width / 2, top_screen_height, orientation); draw_info.layer = 1; - DrawSingleScreen(1, static_cast((top_screen_left / 2) + (layout.width / 2)), + DrawSingleScreen(rightside, static_cast((top_screen_left / 2) + (layout.width / 2)), top_screen_top, top_screen_width / 2, top_screen_height, orientation); break; } - case Settings::StereoRenderOption::ReverseSideBySide: { - DrawSingleScreen(1, top_screen_left / 2, top_screen_top, top_screen_width / 2, + case Settings::StereoRenderOption::SideBySideFull: { + DrawSingleScreen(leftside, top_screen_left, top_screen_top, top_screen_width, top_screen_height, orientation); draw_info.layer = 1; - DrawSingleScreen(0, static_cast((top_screen_left / 2) + (layout.width / 2)), - top_screen_top, top_screen_width / 2, top_screen_height, orientation); + DrawSingleScreen(rightside, top_screen_left + layout.width / 2, top_screen_top, + top_screen_width, top_screen_height, orientation); break; } case Settings::StereoRenderOption::CardboardVR: { - DrawSingleScreen(0, top_screen_left, top_screen_top, top_screen_width, top_screen_height, - orientation); + DrawSingleScreen(leftside, top_screen_left, top_screen_top, top_screen_width, + top_screen_height, orientation); draw_info.layer = 1; DrawSingleScreen( - 1, static_cast(layout.cardboard.top_screen_right_eye + (layout.width / 2)), + rightside, + static_cast(layout.cardboard.top_screen_right_eye + (layout.width / 2)), top_screen_top, top_screen_width, top_screen_height, orientation); break; } case Settings::StereoRenderOption::Anaglyph: case Settings::StereoRenderOption::Interlaced: case Settings::StereoRenderOption::ReverseInterlaced: { - DrawSingleScreenStereo(0, 1, top_screen_left, top_screen_top, top_screen_width, - top_screen_height, orientation); + DrawSingleScreenStereo(leftside, rightside, top_screen_left, top_screen_top, + top_screen_width, top_screen_height, orientation); break; } } @@ -812,31 +814,29 @@ void RendererVulkan::DrawBottomScreen(const Layout::FramebufferLayout& layout, const auto orientation = layout.is_rotated ? Layout::DisplayOrientation::Landscape : Layout::DisplayOrientation::Portrait; - bool separate_win = false; -#ifndef ANDROID - separate_win = - (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows); -#endif - - switch (Settings::values.render_3d.GetValue()) { + switch (layout.render_3d_mode) { case Settings::StereoRenderOption::Off: { DrawSingleScreen(2, bottom_screen_left, bottom_screen_top, bottom_screen_width, bottom_screen_height, orientation); + break; } case Settings::StereoRenderOption::SideBySide: // Bottom screen is identical on both sides - case Settings::StereoRenderOption::ReverseSideBySide: { - if (separate_win) { - DrawSingleScreen(2, bottom_screen_left, bottom_screen_top, bottom_screen_width, - bottom_screen_height, orientation); - } else { - DrawSingleScreen(2, bottom_screen_left / 2, bottom_screen_top, bottom_screen_width / 2, - bottom_screen_height, orientation); - draw_info.layer = 1; - DrawSingleScreen(2, static_cast((bottom_screen_left / 2) + (layout.width / 2)), - bottom_screen_top, bottom_screen_width / 2, bottom_screen_height, - orientation); - } + { + DrawSingleScreen(2, bottom_screen_left / 2, bottom_screen_top, bottom_screen_width / 2, + bottom_screen_height, orientation); + draw_info.layer = 1; + DrawSingleScreen(2, static_cast((bottom_screen_left / 2) + (layout.width / 2)), + bottom_screen_top, bottom_screen_width / 2, bottom_screen_height, + orientation); + break; + } + case Settings::StereoRenderOption::SideBySideFull: { + DrawSingleScreen(2, bottom_screen_left, bottom_screen_top, bottom_screen_width, + bottom_screen_height, orientation); + draw_info.layer = 1; + DrawSingleScreen(2, bottom_screen_left + layout.width / 2, bottom_screen_top, + bottom_screen_width, bottom_screen_height, orientation); break; } case Settings::StereoRenderOption::CardboardVR: { @@ -851,13 +851,8 @@ void RendererVulkan::DrawBottomScreen(const Layout::FramebufferLayout& layout, case Settings::StereoRenderOption::Anaglyph: case Settings::StereoRenderOption::Interlaced: case Settings::StereoRenderOption::ReverseInterlaced: { - if (separate_win) { - DrawSingleScreen(2, bottom_screen_left, bottom_screen_top, bottom_screen_width, - bottom_screen_height, orientation); - } else { - DrawSingleScreenStereo(2, 2, bottom_screen_left, bottom_screen_top, bottom_screen_width, - bottom_screen_height, orientation); - } + DrawSingleScreenStereo(2, 2, bottom_screen_left, bottom_screen_top, bottom_screen_width, + bottom_screen_height, orientation); break; } } @@ -871,7 +866,7 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout& clear_color.float32[2] = Settings::values.bg_blue.GetValue(); } if (settings.shader_update_requested.exchange(false)) { - ReloadPipeline(); + ReloadPipeline(layout.render_3d_mode); } PrepareDraw(frame, layout); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 15807a4f3..29ed8a66e 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -80,7 +80,7 @@ public: void TryPresent(int timeout_ms, bool is_secondary) override {} private: - void ReloadPipeline(); + void ReloadPipeline(Settings::StereoRenderOption render_3d); void CompileShaders(); void BuildLayouts(); void BuildPipelines();