diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui
index 83b85ba9a..862125783 100644
--- a/src/citra_qt/configuration/configure_enhancements.ui
+++ b/src/citra_qt/configuration/configure_enhancements.ui
@@ -7,7 +7,7 @@
0
0
440
- 748
+ 781
@@ -199,11 +199,11 @@
xBRZ
- -
-
- MMPX
-
-
+ -
+
+ MMPX
+
+
@@ -239,6 +239,11 @@
Side by Side
+ -
+
+ Top Bottom
+
+
-
Anaglyph
diff --git a/src/common/settings.h b/src/common/settings.h
index 6d3cbd98a..030797930 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -49,10 +49,11 @@ enum class LayoutOption : u32 {
enum class StereoRenderOption : u32 {
Off = 0,
SideBySide = 1,
- Anaglyph = 2,
- Interlaced = 3,
- ReverseInterlaced = 4,
- CardboardVR = 5
+ TopBottom = 2,
+ Anaglyph = 3,
+ Interlaced = 4,
+ ReverseInterlaced = 5,
+ CardboardVR = 6
};
// Which eye to render when 3d is off. 800px wide mode could be added here in the future, when
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index cdefdb541..0b32b5324 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -73,6 +73,13 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
framebuffer_x < layout.bottom_screen.right / 2) ||
(framebuffer_x >= (layout.bottom_screen.left / 2) + (layout.width / 2) &&
framebuffer_x < (layout.bottom_screen.right / 2) + (layout.width / 2))));
+ } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
+ return framebuffer_x >= layout.bottom_screen.left &&
+ framebuffer_x < layout.bottom_screen.right &&
+ ((framebuffer_y >= layout.bottom_screen.top / 2 &&
+ framebuffer_y < layout.bottom_screen.bottom / 2) ||
+ (framebuffer_y >= (layout.bottom_screen.top / 2) + (layout.height / 2) &&
+ framebuffer_y < (layout.bottom_screen.bottom / 2) + (layout.height / 2)));
} else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {
return (framebuffer_y >= layout.bottom_screen.top &&
framebuffer_y < layout.bottom_screen.bottom &&
@@ -91,22 +98,35 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
std::tuple EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const {
if (new_x >= framebuffer_layout.width / 2) {
- if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide)
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
new_x -= framebuffer_layout.width / 2;
- else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR)
+ } else if (Settings::values.render_3d.GetValue() ==
+ Settings::StereoRenderOption::CardboardVR) {
new_x -=
(framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2);
+ }
}
- if (Settings::values.render_3d.GetValue() == 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 {
- new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
- new_x = std::min(new_x, framebuffer_layout.bottom_screen.right - 1);
+ if (new_y >= framebuffer_layout.height / 2) {
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
+ new_y -= framebuffer_layout.height / 2;
+ }
}
- new_y = std::max(new_y, framebuffer_layout.bottom_screen.top);
- new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom - 1);
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
+ new_x = std::clamp(new_x, framebuffer_layout.bottom_screen.left / 2,
+ framebuffer_layout.bottom_screen.right / 2 - 1);
+ } else {
+ new_x = std::clamp(new_x, framebuffer_layout.bottom_screen.left,
+ framebuffer_layout.bottom_screen.right - 1);
+ }
+
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
+ new_y = std::clamp(new_y, framebuffer_layout.bottom_screen.top / 2,
+ framebuffer_layout.bottom_screen.bottom / 2 - 1);
+ } else {
+ new_y = std::clamp(new_y, framebuffer_layout.bottom_screen.top,
+ framebuffer_layout.bottom_screen.bottom - 1);
+ }
return std::make_tuple(new_x, new_y);
}
@@ -122,17 +142,25 @@ void EmuWindow::CreateTouchState() {
}
bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
- if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
return false;
+ }
if (framebuffer_x >= framebuffer_layout.width / 2) {
- if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide)
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
framebuffer_x -= framebuffer_layout.width / 2;
- else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR)
+ } else if (Settings::values.render_3d.GetValue() ==
+ Settings::StereoRenderOption::CardboardVR) {
framebuffer_x -=
(framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2);
+ }
}
- std::lock_guard guard(touch_state->mutex);
+ if (framebuffer_y >= framebuffer_layout.height / 2) {
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
+ framebuffer_y -= framebuffer_layout.height / 2;
+ }
+ }
+ std::scoped_lock lock{touch_state->mutex};
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
touch_state->touch_x =
static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left / 2) /
@@ -143,9 +171,17 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left) /
(framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
}
- touch_state->touch_y =
- static_cast(framebuffer_y - framebuffer_layout.bottom_screen.top) /
- (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+
+ if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
+ touch_state->touch_y =
+ static_cast(framebuffer_y - framebuffer_layout.bottom_screen.top / 2) /
+ (framebuffer_layout.bottom_screen.bottom / 2 -
+ framebuffer_layout.bottom_screen.top / 2);
+ } else {
+ touch_state->touch_y =
+ static_cast(framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+ }
if (!framebuffer_layout.is_rotated) {
std::swap(touch_state->touch_x, touch_state->touch_y);
@@ -157,18 +193,20 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
}
void EmuWindow::TouchReleased() {
- std::lock_guard guard{touch_state->mutex};
+ std::scoped_lock lock{touch_state->mutex};
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
- if (!touch_state->touch_pressed)
+ if (!touch_state->touch_pressed) {
return;
+ }
- if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
+ if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
+ }
TouchPressed(framebuffer_x, framebuffer_y);
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index b0dcb1433..6a493c3fc 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -774,6 +774,15 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,
top_screen_top, top_screen_width / 2, top_screen_height, orientation);
break;
}
+ case Settings::StereoRenderOption::TopBottom: {
+ DrawSingleScreen(screen_infos[0], top_screen_left, top_screen_top / 2, top_screen_width,
+ top_screen_height / 2, orientation);
+ glUniform1i(uniform_layer, 1);
+ DrawSingleScreen(screen_infos[1], top_screen_left,
+ static_cast((top_screen_top / 2) + (layout.height / 2)),
+ top_screen_width, top_screen_height / 2, orientation);
+ break;
+ }
case Settings::StereoRenderOption::CardboardVR: {
DrawSingleScreen(screen_infos[0], top_screen_left, top_screen_top, top_screen_width,
top_screen_height, orientation);
@@ -823,6 +832,15 @@ void RendererOpenGL::DrawBottomScreen(const Layout::FramebufferLayout& layout,
bottom_screen_top, bottom_screen_width / 2, bottom_screen_height, orientation);
break;
}
+ case Settings::StereoRenderOption::TopBottom: {
+ DrawSingleScreen(screen_infos[2], bottom_screen_left, bottom_screen_top / 2,
+ bottom_screen_width, bottom_screen_height / 2, orientation);
+ glUniform1i(uniform_layer, 1);
+ DrawSingleScreen(screen_infos[2], bottom_screen_left,
+ static_cast((bottom_screen_top / 2) + (layout.height / 2)),
+ bottom_screen_width, bottom_screen_height / 2, orientation);
+ break;
+ }
case Settings::StereoRenderOption::CardboardVR: {
DrawSingleScreen(screen_infos[2], bottom_screen_left, bottom_screen_top,
bottom_screen_width, bottom_screen_height, orientation);