video_core: Stereo Settings Improvements: Full Width SBS, Swap Eyes, Select which display(s) to render on (#1212)

This commit is contained in:
David Griswold 2026-01-03 23:40:59 +03:00 committed by GitHub
parent 48cbeb0993
commit 4bdcf342b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 358 additions and 201 deletions

View File

@ -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
}
}
}

View File

@ -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? =

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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 =

View File

@ -238,9 +238,8 @@
</integer-array>
<string-array name="render3dModes">
<item>@string/off</item>
<item>@string/side_by_side</item>
<item>@string/reverse_side_by_side</item>
<item>@string/side_by_side_full</item>
<item>@string/anaglyph</item>
<item>@string/interlaced</item>
<item>@string/reverse_interlaced</item>
@ -248,7 +247,6 @@
</string-array>
<integer-array name="render3dValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
@ -257,6 +255,20 @@
<item>6</item>
</integer-array>
<string-array name="render3dWhichDisplay">
<item>@string/off</item>
<item>@string/render_3d_which_display_both</item>
<item>@string/render_3d_which_display_primary</item>
<item>@string/render_3d_which_display_secondary</item>
</string-array>
<integer-array name="render3dDisplayValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
<string-array name="graphicsApiNames">
<item>@string/opengles</item>
<item>@string/vulkan</item>

View File

@ -289,10 +289,18 @@
<string name="debug_warning">Warning: Modifying these settings will slow emulation</string>
<string name="stereoscopy">Stereoscopy</string>
<string name="render3d">Stereoscopic 3D Mode</string>
<string name="render3d_description">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.</string>
<string name="factor3d">Depth</string>
<string name="factor3d_description">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</string>
<string name="disable_right_eye_render">Disable Right Eye Render</string>
<string name="disable_right_eye_render_description">Greatly improves performance in some applications, but can cause flickering in others.</string>
<string name="swap_eyes_3d">Swap Eyes</string>
<string name="swap_eyes_3d_description">Swaps which eye is shown in which side. Combined with Side by Side mode makes it possible to see 3D by crossing your eyes!</string>
<string name="render_3d_which_display">Render Stereoscopic 3D</string>
<string name="render_3d_which_display_description">Whether to enable stereoscopic 3D, and on which displays. The single display options are only relevant when multiple displays are connected.</string>
<string name="render_3d_which_display_both">On (All Displays)</string>
<string name="render_3d_which_display_primary">On (Primary Display Only)</string>
<string name="render_3d_which_display_secondary">On (Secondary Display Only)</string>
<string name="cardboard_vr">Cardboard VR</string>
<string name="cardboard_screen_size">Cardboard Screen Size</string>
<string name="cardboard_screen_size_description">Scales the screen to a percentage of its original size.</string>
@ -723,7 +731,7 @@
<!-- Render 3D modes -->
<string name="side_by_side">Side by Side</string>
<string name="reverse_side_by_side">Reverse Side by Side</string>
<string name="side_by_side_full">Side by Side Full Width</string>
<string name="anaglyph">Anaglyph</string>
<string name="interlaced">Interlaced</string>
<string name="reverse_interlaced">Reverse Interlaced</string>

View File

@ -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);

View File

@ -57,6 +57,7 @@ void ConfigureEnhancements::SetConfiguration() {
ui->render_3d_combobox->setCurrentIndex(
static_cast<int>(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<int>(Settings::values.mono_render_option.GetValue()));
@ -111,6 +112,7 @@ void ConfigureEnhancements::ApplyConfiguration() {
ui->resolution_factor_combobox);
Settings::values.render_3d =
static_cast<Settings::StereoRenderOption>(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<Settings::MonoRenderOption>(ui->mono_rendering_eye->currentIndex());

View File

@ -113,7 +113,7 @@
<item>
<widget class="QCheckBox" name="toggle_linear_filter">
<property name="text">
<string>Enable linear filtering</string>
<string>Enable Linear Filtering</string>
</property>
</widget>
</item>
@ -236,7 +236,7 @@
</item>
<item>
<property name="text">
<string>Reverse Side by Side</string>
<string>Side by Side Full Width</string>
</property>
</item>
<item>
@ -318,14 +318,21 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="disable_right_eye_render">
<property name="text">
<string>Disable right eye rendering</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QCheckBox" name="disable_right_eye_render">
<property name="text">
<string>Disable Right Eye Rendering</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="swap_eyes_3d">
<property name="text">
<string>Swap Eyes</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -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 =

View File

@ -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);

View File

@ -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<StereoRenderOption> render_3d{StereoRenderOption::Off, "render_3d"};
SwitchableSetting<u32> factor_3d{0, "factor_3d"};
SwitchableSetting<bool> swap_eyes_3d{false, "swap_eyes_3d"};
SwitchableSetting<StereoWhichDisplay> render_3d_which_display{StereoWhichDisplay::None,
"render_3d_which_display"};
SwitchableSetting<MonoRenderOption> mono_render_option{MonoRenderOption::LeftEye,
"mono_render_option"};

View File

@ -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<unsigned>(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<unsigned, unsigned> 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<float>(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);
}

View File

@ -258,6 +258,8 @@ public:
return is_secondary;
}
Settings::StereoRenderOption get3DMode() const;
protected:
EmuWindow();
EmuWindow(bool is_secondary);

View File

@ -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<float>(height) / static_cast<float>(width);
const float window_aspect_ratio = static_cast<float>(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<int>(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();
}

View File

@ -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();
};
/**

View File

@ -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<float>(top_screen.left);
const float top_screen_top = static_cast<float>(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<int>(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<float>((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<float>((top_screen_left / 2) + (layout.width / 2)),
top_screen_top, top_screen_width / 2, top_screen_height, orientation);
DrawSingleScreen(screen_infos[rightside],
static_cast<float>(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<float>(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<float>((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<float>((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: {

View File

@ -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,

View File

@ -575,8 +575,7 @@ void RendererVulkan::FillScreen(Common::Vec3<u8> 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<float>(top_screen.left);
const float top_screen_top = static_cast<float>(top_screen.top);
const float top_screen_width = static_cast<float>(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<int>(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<float>((top_screen_left / 2) + (layout.width / 2)),
DrawSingleScreen(rightside, static_cast<float>((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<float>((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<float>(layout.cardboard.top_screen_right_eye + (layout.width / 2)),
rightside,
static_cast<float>(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<float>((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<float>((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);

View File

@ -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();