diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 072568827..b2c878ddf 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -151,6 +151,7 @@ void Config::ReadValues() { Settings::values.layout_option = static_cast(sdl2_config->GetInteger("Layout", "layout_option", 0)); Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); + Settings::values.upright_screen = sdl2_config->GetBoolean("Layout", "upright_screen", false); Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false); Settings::values.custom_top_left = static_cast(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index ca154c4a7..9c441e354 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -183,6 +183,10 @@ custom_bottom_bottom = # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent swap_screen = +# Toggle upright orientation, for book style games. +# 0 (default): Off, 1: On +upright_screen = + # Dumps textures as PNG to dump/textures/[Title ID]/. # 0 (default): Off, 1: On dump_textures = diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 7601d485b..662385086 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -296,6 +296,7 @@ void Config::ReadLayoutValues() { Settings::values.layout_option = static_cast(ReadSetting(QStringLiteral("layout_option")).toInt()); Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool(); + Settings::values.upright_screen = ReadSetting(QStringLiteral("upright_screen"), false).toBool(); Settings::values.custom_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool(); Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt(); Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 0).toInt(); @@ -765,6 +766,7 @@ void Config::SaveLayoutValues() { WriteSetting(QStringLiteral("filter_mode"), Settings::values.filter_mode, true); WriteSetting(QStringLiteral("layout_option"), static_cast(Settings::values.layout_option)); WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false); + WriteSetting(QStringLiteral("upright_screen"), Settings::values.upright_screen, false); WriteSetting(QStringLiteral("custom_layout"), Settings::values.custom_layout, false); WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0); WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0); diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 4067d64bc..3619adb34 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -53,6 +53,7 @@ void ConfigureEnhancements::SetConfiguration() { ui->layout_combobox->setCurrentIndex(static_cast(Settings::values.layout_option)); ui->swap_screen->setChecked(Settings::values.swap_screen); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); + ui->upright_screen->setChecked(Settings::values.upright_screen); ui->toggle_dump_textures->setChecked(Settings::values.dump_textures); ui->toggle_custom_textures->setChecked(Settings::values.custom_textures); ui->toggle_preload_textures->setChecked(Settings::values.preload_textures); @@ -101,6 +102,7 @@ void ConfigureEnhancements::ApplyConfiguration() { static_cast(ui->layout_combobox->currentIndex()); Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked(); + Settings::values.upright_screen = ui->upright_screen->isChecked(); Settings::values.dump_textures = ui->toggle_dump_textures->isChecked(); Settings::values.custom_textures = ui->toggle_custom_textures->isChecked(); Settings::values.preload_textures = ui->toggle_preload_textures->isChecked(); diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index 26c65ed59..289c1178e 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -239,6 +239,13 @@ + + + + Rotate Screens Upright + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index dd56f4b13..457de1b60 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -618,6 +618,8 @@ void GMainWindow::ConnectMenuEvents() { &GMainWindow::ChangeScreenLayout); connect(ui.action_Screen_Layout_Swap_Screens, &QAction::triggered, this, &GMainWindow::OnSwapScreens); + connect(ui.action_Screen_Layout_Upright_Screens, &QAction::triggered, this, + &GMainWindow::OnRotateScreens); // Movie connect(ui.action_Record_Movie, &QAction::triggered, this, &GMainWindow::OnRecordMovie); @@ -1435,6 +1437,11 @@ void GMainWindow::OnSwapScreens() { Settings::Apply(); } +void GMainWindow::OnRotateScreens() { + Settings::values.upright_screen = ui.action_Screen_Layout_Upright_Screens->isChecked(); + Settings::Apply(); +} + void GMainWindow::OnCheats() { CheatDialog cheat_dialog(this); cheat_dialog.exec(); @@ -2032,6 +2039,7 @@ void GMainWindow::SyncMenuUISettings() { ui.action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option == Settings::LayoutOption::SideScreen); ui.action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen); + ui.action_Screen_Layout_Upright_Screens->setChecked(Settings::values.upright_screen); } void GMainWindow::RetranslateStatusBar() { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 0fa04f5b8..7ec4fa185 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -187,6 +187,7 @@ private slots: void ChangeScreenLayout(); void ToggleScreenLayout(); void OnSwapScreens(); + void OnRotateScreens(); void OnCheats(); void ShowFullscreen(); void HideFullscreen(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 60dff0417..c950fefd5 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -110,6 +110,7 @@ + @@ -425,6 +426,14 @@ Swap Screens + + + true + + + Rotate Upright + + Check for Updates diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index f393e4c76..23c33f627 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -118,6 +118,11 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_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); + touch_state->touch_x = 1.f - touch_state->touch_x; + } + touch_state->touch_pressed = true; } @@ -145,17 +150,21 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) } else { switch (Settings::values.layout_option) { case Settings::LayoutOption::SingleScreen: - layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen); + layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::LargeScreen: - layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); + layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::SideScreen: - layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen); + layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::Default: default: - layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); + layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; } } diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index c66a4bc95..2c6c9cdce 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -15,9 +15,17 @@ static const float TOP_SCREEN_ASPECT_RATIO = static_cast(Core::kScreenTopHeight) / Core::kScreenTopWidth; static const float BOT_SCREEN_ASPECT_RATIO = static_cast(Core::kScreenBottomHeight) / Core::kScreenBottomWidth; +static const float TOP_SCREEN_UPRIGHT_ASPECT_RATIO = + static_cast(Core::kScreenTopWidth) / Core::kScreenTopHeight; +static const float BOT_SCREEN_UPRIGHT_ASPECT_RATIO = + static_cast(Core::kScreenBottomWidth) / Core::kScreenBottomHeight; u32 FramebufferLayout::GetScalingRatio() const { - return static_cast(((top_screen.GetWidth() - 1) / Core::kScreenTopWidth) + 1); + if (is_rotated) { + return static_cast(((top_screen.GetWidth() - 1) / Core::kScreenTopWidth) + 1); + } else { + return static_cast(((top_screen.GetWidth() - 1) / Core::kScreenTopHeight) + 1); + } } // Finds the largest size subrectangle contained in window area that is confined to the aspect ratio @@ -30,57 +38,108 @@ static Common::Rectangle maxRectangle(Common::Rectangle window_area, static_cast(std::round(scale * screen_aspect_ratio))}; } -FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped) { +FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool upright) { ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}}; - // Default layout gives equal screen sizes to the top and bottom screen - Common::Rectangle screen_window_area{0, 0, width, height / 2}; - Common::Rectangle top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); - Common::Rectangle bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + FramebufferLayout res{width, height, true, true, {}, {}, !upright}; + Common::Rectangle screen_window_area; + Common::Rectangle top_screen; + Common::Rectangle bot_screen; + float emulation_aspect_ratio; + if (upright) { + // Default layout gives equal screen sizes to the top and bottom screen + screen_window_area = {0, 0, width / 2, height}; + top_screen = maxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO); + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO); + // both screens width are taken into account by dividing by 2 + emulation_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO / 2; + } else { + // Default layout gives equal screen sizes to the top and bottom screen + screen_window_area = {0, 0, width, height / 2}; + top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + // both screens height are taken into account by multiplying by 2 + emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2; + } float window_aspect_ratio = static_cast(height) / width; - // both screens height are taken into account by multiplying by 2 - float emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2; if (window_aspect_ratio < emulation_aspect_ratio) { - // Apply borders to the left and right sides of the window. - top_screen = - top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); - bot_screen = - bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); + // Window is wider than the emulation content => apply borders to the right and left sides + if (upright) { + // Recalculate the bottom screen to account for the height difference between right and + // left + screen_window_area = {0, 0, top_screen.GetWidth(), height}; + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO); + bot_screen = + bot_screen.TranslateY((top_screen.GetHeight() - bot_screen.GetHeight()) / 2); + if (swapped) { + bot_screen = bot_screen.TranslateX(width / 2 - bot_screen.GetWidth()); + } else { + top_screen = top_screen.TranslateX(width / 2 - top_screen.GetWidth()); + } + } else { + top_screen = + top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); + bot_screen = + bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); + } } else { // Window is narrower than the emulation content => apply borders to the top and bottom - // Recalculate the bottom screen to account for the width difference between top and bottom - screen_window_area = {0, 0, width, top_screen.GetHeight()}; - bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); - bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2); - if (swapped) { - bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight()); + if (upright) { + top_screen = top_screen.TranslateY( + (screen_window_area.GetHeight() - top_screen.GetHeight()) / 2); + bot_screen = bot_screen.TranslateY( + (screen_window_area.GetHeight() - bot_screen.GetHeight()) / 2); } else { - top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight()); + // Recalculate the bottom screen to account for the width difference between top and + // bottom + screen_window_area = {0, 0, width, top_screen.GetHeight()}; + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2); + if (swapped) { + bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight()); + } else { + top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight()); + } } } - // Move the top screen to the bottom if we are swapped. - res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen; - res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2); + if (upright) { + // Move the top screen to the right if we are swapped. + res.top_screen = swapped ? top_screen.TranslateX(width / 2) : top_screen; + res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(width / 2); + } else { + // Move the top screen to the bottom if we are swapped. + res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen; + res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2); + } return res; } -FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped) { +FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool upright) { ASSERT(width > 0); ASSERT(height > 0); // The drawing code needs at least somewhat valid values for both screens // so just calculate them both even if the other isn't showing. - FramebufferLayout res{width, height, !swapped, swapped, {}, {}}; + FramebufferLayout res{width, height, !swapped, swapped, {}, {}, !upright}; Common::Rectangle screen_window_area{0, 0, width, height}; - Common::Rectangle top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); - Common::Rectangle bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + Common::Rectangle top_screen; + Common::Rectangle bot_screen; + float emulation_aspect_ratio; + if (upright) { + top_screen = maxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO); + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_UPRIGHT_ASPECT_RATIO); + emulation_aspect_ratio = + (swapped) ? BOT_SCREEN_UPRIGHT_ASPECT_RATIO : TOP_SCREEN_UPRIGHT_ASPECT_RATIO; + } else { + top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; + } float window_aspect_ratio = static_cast(height) / width; - float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; if (window_aspect_ratio < emulation_aspect_ratio) { top_screen = @@ -96,11 +155,11 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped) { return res; } -FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped) { +FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright) { ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}}; + FramebufferLayout res{width, height, true, true, {}, {}, true}; // Split the window into two parts. Give 4x width to the main screen and 1x width to the small // To do that, find the total emulation box and maximize that based on window size float window_aspect_ratio = static_cast(height) / width; @@ -133,11 +192,11 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped) { return res; } -FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped) { +FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped, bool upright) { ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}}; + FramebufferLayout res{width, height, true, true, {}, {}, true}; // Aspect ratio of both screens side by side const float emulation_aspect_ratio = static_cast(Core::kScreenTopHeight) / (Core::kScreenTopWidth + Core::kScreenBottomWidth); @@ -170,7 +229,7 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) { ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}}; + FramebufferLayout res{width, height, true, true, {}, {}, !Settings::values.upright_screen}; Common::Rectangle top_screen{ Settings::values.custom_top_left, Settings::values.custom_top_top, @@ -194,14 +253,25 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { int width, height; switch (Settings::values.layout_option) { case Settings::LayoutOption::SingleScreen: - if (Settings::values.swap_screen) { - width = Core::kScreenBottomWidth * res_scale; - height = Core::kScreenBottomHeight * res_scale; + if (Settings::values.upright_screen) { + if (Settings::values.swap_screen) { + width = Core::kScreenBottomHeight * res_scale; + height = Core::kScreenBottomWidth * res_scale; + } else { + width = Core::kScreenTopHeight * res_scale; + height = Core::kScreenTopWidth * res_scale; + } } else { - width = Core::kScreenTopWidth * res_scale; - height = Core::kScreenTopHeight * res_scale; + if (Settings::values.swap_screen) { + width = Core::kScreenBottomWidth * res_scale; + height = Core::kScreenBottomHeight * res_scale; + } else { + width = Core::kScreenTopWidth * res_scale; + height = Core::kScreenTopHeight * res_scale; + } } - layout = SingleFrameLayout(width, height, Settings::values.swap_screen); + layout = SingleFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::LargeScreen: if (Settings::values.swap_screen) { @@ -211,18 +281,26 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; height = Core::kScreenTopHeight * res_scale; } - layout = LargeFrameLayout(width, height, Settings::values.swap_screen); + layout = LargeFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::SideScreen: width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; height = Core::kScreenTopHeight * res_scale; - layout = SideFrameLayout(width, height, Settings::values.swap_screen); + layout = SideFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; case Settings::LayoutOption::Default: default: - width = Core::kScreenTopWidth * res_scale; - height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; - layout = DefaultFrameLayout(width, height, Settings::values.swap_screen); + if (Settings::values.upright_screen) { + width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + height = Core::kScreenTopWidth * res_scale; + } else { + width = Core::kScreenTopWidth * res_scale; + height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + } + layout = DefaultFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); break; } } diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index c565d33b8..5feb16f64 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -16,6 +16,7 @@ struct FramebufferLayout { bool bottom_screen_enabled; Common::Rectangle top_screen; Common::Rectangle bottom_screen; + bool is_rotated = true; /** * Returns the ration of pixel size of the top screen, compared to the native size of the 3DS @@ -31,7 +32,7 @@ struct FramebufferLayout { * @param is_swapped if true, the bottom screen will be displayed above the top screen * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); /** * Factory method for constructing a FramebufferLayout with only the top or bottom screen @@ -40,7 +41,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped); * @param is_swapped if true, the bottom screen will be displayed (and the top won't be displayed) * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); /** * Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom @@ -51,7 +52,7 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped); * @param is_swapped if true, the bottom screen will be the large display * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); /** * Factory method for constructing a Frame with the Top screen and bottom @@ -62,7 +63,7 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped); * @param is_swapped if true, the bottom screen will be the left display * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout SideFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout SideFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); /** * Factory method for constructing a custom FramebufferLayout diff --git a/src/core/settings.h b/src/core/settings.h index 2d139e1b2..78b11912c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -150,6 +150,7 @@ struct Values { LayoutOption layout_option; bool swap_screen; + bool upright_screen; bool custom_layout; u16 custom_top_left; u16 custom_top_top; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index b1fe38a7e..5046895e0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -781,6 +781,35 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa state.Apply(); } +void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, + float h) { + const auto& texcoords = screen_info.display_texcoords; + + const std::array vertices = {{ + ScreenRectVertex(x, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x + w, y, texcoords.top, texcoords.right), + ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left), + ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left), + }}; + + u16 scale_factor = VideoCore::GetResolutionScaleFactor(); + glUniform4f(uniform_i_resolution, screen_info.texture.width * scale_factor, + screen_info.texture.height * scale_factor, + 1.0 / (screen_info.texture.width * scale_factor), + 1.0 / (screen_info.texture.height * scale_factor)); + glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h); + state.texture_units[0].texture_2d = screen_info.display_texture; + state.texture_units[0].sampler = filter_sampler.handle; + state.Apply(); + + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + state.texture_units[0].texture_2d = 0; + state.texture_units[0].sampler = 0; + state.Apply(); +} + /** * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD * rotation. @@ -819,6 +848,40 @@ void RendererOpenGL::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info state.Apply(); } +void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, + const ScreenInfo& screen_info_r, float x, float y, + float w, float h) { + const auto& texcoords = screen_info_l.display_texcoords; + + const std::array vertices = {{ + ScreenRectVertex(x, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x + w, y, texcoords.top, texcoords.right), + ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left), + ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left), + }}; + + u16 scale_factor = VideoCore::GetResolutionScaleFactor(); + glUniform4f(uniform_i_resolution, screen_info_l.texture.width * scale_factor, + screen_info_l.texture.height * scale_factor, + 1.0 / (screen_info_l.texture.width * scale_factor), + 1.0 / (screen_info_l.texture.height * scale_factor)); + glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h); + state.texture_units[0].texture_2d = screen_info_l.display_texture; + state.texture_units[1].texture_2d = screen_info_r.display_texture; + state.texture_units[0].sampler = filter_sampler.handle; + state.texture_units[1].sampler = filter_sampler.handle; + state.Apply(); + + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + state.texture_units[0].texture_2d = 0; + state.texture_units[1].texture_2d = 0; + state.texture_units[0].sampler = 0; + state.texture_units[1].sampler = 0; + state.Apply(); +} + /** * Draws the emulated screens to the emulator window. */ @@ -866,44 +929,85 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) { glUniform1i(uniform_layer, 0); if (layout.top_screen_enabled) { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { - DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, - (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { - DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, - (float)top_screen.top, (float)top_screen.GetWidth() / 2, - (float)top_screen.GetHeight()); - glUniform1i(uniform_layer, 1); - DrawSingleScreenRotated(screen_infos[1], - ((float)top_screen.left / 2) + ((float)layout.width / 2), - (float)top_screen.top, (float)top_screen.GetWidth() / 2, - (float)top_screen.GetHeight()); - } else if (stereo_single_screen) { - DrawSingleScreenStereoRotated(screen_infos[0], screen_infos[1], (float)top_screen.left, - (float)top_screen.top, (float)top_screen.GetWidth(), - (float)top_screen.GetHeight()); + if (layout.is_rotated) { + if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, + (float)top_screen.top, (float)top_screen.GetWidth(), + (float)top_screen.GetHeight()); + } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, + (float)top_screen.top, (float)top_screen.GetWidth() / 2, + (float)top_screen.GetHeight()); + glUniform1i(uniform_layer, 1); + DrawSingleScreenRotated(screen_infos[1], + ((float)top_screen.left / 2) + ((float)layout.width / 2), + (float)top_screen.top, (float)top_screen.GetWidth() / 2, + (float)top_screen.GetHeight()); + } else if (stereo_single_screen) { + DrawSingleScreenStereoRotated( + screen_infos[0], screen_infos[1], (float)top_screen.left, (float)top_screen.top, + (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); + } + } else { + if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + DrawSingleScreen(screen_infos[0], (float)top_screen.left, (float)top_screen.top, + (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); + } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top, + (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight()); + glUniform1i(uniform_layer, 1); + DrawSingleScreen(screen_infos[1], + ((float)top_screen.left / 2) + ((float)layout.width / 2), + (float)top_screen.top, (float)top_screen.GetWidth() / 2, + (float)top_screen.GetHeight()); + } else if (stereo_single_screen) { + DrawSingleScreenStereo(screen_infos[0], screen_infos[1], (float)top_screen.left, + (float)top_screen.top, (float)top_screen.GetWidth(), + (float)top_screen.GetHeight()); + } } } glUniform1i(uniform_layer, 0); if (layout.bottom_screen_enabled) { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { - DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left, - (float)bottom_screen.top, (float)bottom_screen.GetWidth(), - (float)bottom_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { - DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left / 2, - (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, - (float)bottom_screen.GetHeight()); - glUniform1i(uniform_layer, 1); - DrawSingleScreenRotated(screen_infos[2], - ((float)bottom_screen.left / 2) + ((float)layout.width / 2), - (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, - (float)bottom_screen.GetHeight()); - } else if (stereo_single_screen) { - DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2], - (float)bottom_screen.left, (float)bottom_screen.top, - (float)bottom_screen.GetWidth(), - (float)bottom_screen.GetHeight()); + if (layout.is_rotated) { + if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left, + (float)bottom_screen.top, (float)bottom_screen.GetWidth(), + (float)bottom_screen.GetHeight()); + } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + DrawSingleScreenRotated( + screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top, + (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight()); + glUniform1i(uniform_layer, 1); + DrawSingleScreenRotated( + screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2), + (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, + (float)bottom_screen.GetHeight()); + } else if (stereo_single_screen) { + DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2], + (float)bottom_screen.left, (float)bottom_screen.top, + (float)bottom_screen.GetWidth(), + (float)bottom_screen.GetHeight()); + } + } else { + if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + DrawSingleScreen(screen_infos[2], (float)bottom_screen.left, + (float)bottom_screen.top, (float)bottom_screen.GetWidth(), + (float)bottom_screen.GetHeight()); + } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2, + (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, + (float)bottom_screen.GetHeight()); + glUniform1i(uniform_layer, 1); + DrawSingleScreen(screen_infos[2], + ((float)bottom_screen.left / 2) + ((float)layout.width / 2), + (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, + (float)bottom_screen.GetHeight()); + } else if (stereo_single_screen) { + DrawSingleScreenStereo(screen_infos[2], screen_infos[2], (float)bottom_screen.left, + (float)bottom_screen.top, (float)bottom_screen.GetWidth(), + (float)bottom_screen.GetHeight()); + } } } } diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 465ebfb6e..96df7f8ac 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -77,9 +77,12 @@ private: const GPU::Regs::FramebufferConfig& framebuffer); void DrawScreens(const Layout::FramebufferLayout& layout); void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h); + void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h); void DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l, const ScreenInfo& screen_info_r, float x, float y, float w, float h); + void DrawSingleScreenStereo(const ScreenInfo& screen_info_l, const ScreenInfo& screen_info_r, + float x, float y, float w, float h); void UpdateFramerate(); // Loads framebuffer from emulated memory into the display information structure