Implementation of screen rotation without use of additional layouts.

This is based on what was done using additional layouts, but modified
to have a variable to control rotation and making it so Single Screen
Layout behaves like Upright Single would, and Default Layout behaves
like Upright Double would, when the new variable is used.

Large Layout and Side Layout currently ignore the new variable.
New variable still currently doesn't have a hotkey.
This commit is contained in:
vitor-k 2019-10-25 20:21:10 -03:00 committed by Vitor Kiguchi
parent e74a402c69
commit 89cab445d4
14 changed files with 317 additions and 87 deletions

View file

@ -151,6 +151,7 @@ void Config::ReadValues() {
Settings::values.layout_option = Settings::values.layout_option =
static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0));
Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); 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_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false);
Settings::values.custom_top_left = Settings::values.custom_top_left =
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0));

View file

@ -183,6 +183,10 @@ custom_bottom_bottom =
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
swap_screen = 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]/. # Dumps textures as PNG to dump/textures/[Title ID]/.
# 0 (default): Off, 1: On # 0 (default): Off, 1: On
dump_textures = dump_textures =

View file

@ -296,6 +296,7 @@ void Config::ReadLayoutValues() {
Settings::values.layout_option = Settings::values.layout_option =
static_cast<Settings::LayoutOption>(ReadSetting(QStringLiteral("layout_option")).toInt()); static_cast<Settings::LayoutOption>(ReadSetting(QStringLiteral("layout_option")).toInt());
Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool(); 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_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool();
Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt(); Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt();
Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 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("filter_mode"), Settings::values.filter_mode, true);
WriteSetting(QStringLiteral("layout_option"), static_cast<int>(Settings::values.layout_option)); WriteSetting(QStringLiteral("layout_option"), static_cast<int>(Settings::values.layout_option));
WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false); 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_layout"), Settings::values.custom_layout, false);
WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0); WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0);
WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0); WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0);

View file

@ -53,6 +53,7 @@ void ConfigureEnhancements::SetConfiguration() {
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
ui->swap_screen->setChecked(Settings::values.swap_screen); ui->swap_screen->setChecked(Settings::values.swap_screen);
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); 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_dump_textures->setChecked(Settings::values.dump_textures);
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures); ui->toggle_custom_textures->setChecked(Settings::values.custom_textures);
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures); ui->toggle_preload_textures->setChecked(Settings::values.preload_textures);
@ -101,6 +102,7 @@ void ConfigureEnhancements::ApplyConfiguration() {
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.swap_screen = ui->swap_screen->isChecked();
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->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.dump_textures = ui->toggle_dump_textures->isChecked();
Settings::values.custom_textures = ui->toggle_custom_textures->isChecked(); Settings::values.custom_textures = ui->toggle_custom_textures->isChecked();
Settings::values.preload_textures = ui->toggle_preload_textures->isChecked(); Settings::values.preload_textures = ui->toggle_preload_textures->isChecked();

View file

@ -239,6 +239,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="upright_screen">
<property name="text">
<string>Rotate Screens Upright</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>

View file

@ -618,6 +618,8 @@ void GMainWindow::ConnectMenuEvents() {
&GMainWindow::ChangeScreenLayout); &GMainWindow::ChangeScreenLayout);
connect(ui.action_Screen_Layout_Swap_Screens, &QAction::triggered, this, connect(ui.action_Screen_Layout_Swap_Screens, &QAction::triggered, this,
&GMainWindow::OnSwapScreens); &GMainWindow::OnSwapScreens);
connect(ui.action_Screen_Layout_Upright_Screens, &QAction::triggered, this,
&GMainWindow::OnRotateScreens);
// Movie // Movie
connect(ui.action_Record_Movie, &QAction::triggered, this, &GMainWindow::OnRecordMovie); connect(ui.action_Record_Movie, &QAction::triggered, this, &GMainWindow::OnRecordMovie);
@ -1435,6 +1437,11 @@ void GMainWindow::OnSwapScreens() {
Settings::Apply(); Settings::Apply();
} }
void GMainWindow::OnRotateScreens() {
Settings::values.upright_screen = ui.action_Screen_Layout_Upright_Screens->isChecked();
Settings::Apply();
}
void GMainWindow::OnCheats() { void GMainWindow::OnCheats() {
CheatDialog cheat_dialog(this); CheatDialog cheat_dialog(this);
cheat_dialog.exec(); cheat_dialog.exec();
@ -2032,6 +2039,7 @@ void GMainWindow::SyncMenuUISettings() {
ui.action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option == ui.action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option ==
Settings::LayoutOption::SideScreen); Settings::LayoutOption::SideScreen);
ui.action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen); 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() { void GMainWindow::RetranslateStatusBar() {

View file

@ -187,6 +187,7 @@ private slots:
void ChangeScreenLayout(); void ChangeScreenLayout();
void ToggleScreenLayout(); void ToggleScreenLayout();
void OnSwapScreens(); void OnSwapScreens();
void OnRotateScreens();
void OnCheats(); void OnCheats();
void ShowFullscreen(); void ShowFullscreen();
void HideFullscreen(); void HideFullscreen();

View file

@ -110,6 +110,7 @@
<addaction name="action_Screen_Layout_Side_by_Side"/> <addaction name="action_Screen_Layout_Side_by_Side"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Screen_Layout_Swap_Screens"/> <addaction name="action_Screen_Layout_Swap_Screens"/>
<addaction name="action_Screen_Layout_Upright_Screens"/>
</widget> </widget>
<addaction name="action_Fullscreen"/> <addaction name="action_Fullscreen"/>
<addaction name="action_Single_Window_Mode"/> <addaction name="action_Single_Window_Mode"/>
@ -425,6 +426,14 @@
<string>Swap Screens</string> <string>Swap Screens</string>
</property> </property>
</action> </action>
<action name="action_Screen_Layout_Upright_Screens">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Rotate Upright</string>
</property>
</action>
<action name="action_Check_For_Updates"> <action name="action_Check_For_Updates">
<property name="text"> <property name="text">
<string>Check for Updates</string> <string>Check for Updates</string>

View file

@ -118,6 +118,11 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) / static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
(framebuffer_layout.bottom_screen.bottom - 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; touch_state->touch_pressed = true;
} }
@ -145,17 +150,21 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
} else { } else {
switch (Settings::values.layout_option) { switch (Settings::values.layout_option) {
case Settings::LayoutOption::SingleScreen: 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; break;
case Settings::LayoutOption::LargeScreen: 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; break;
case Settings::LayoutOption::SideScreen: 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; break;
case Settings::LayoutOption::Default: case Settings::LayoutOption::Default:
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; break;
} }
} }

View file

@ -15,9 +15,17 @@ static const float TOP_SCREEN_ASPECT_RATIO =
static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth; static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth;
static const float BOT_SCREEN_ASPECT_RATIO = static const float BOT_SCREEN_ASPECT_RATIO =
static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth; static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth;
static const float TOP_SCREEN_UPRIGHT_ASPECT_RATIO =
static_cast<float>(Core::kScreenTopWidth) / Core::kScreenTopHeight;
static const float BOT_SCREEN_UPRIGHT_ASPECT_RATIO =
static_cast<float>(Core::kScreenBottomWidth) / Core::kScreenBottomHeight;
u32 FramebufferLayout::GetScalingRatio() const { u32 FramebufferLayout::GetScalingRatio() const {
if (is_rotated) {
return static_cast<u32>(((top_screen.GetWidth() - 1) / Core::kScreenTopWidth) + 1); return static_cast<u32>(((top_screen.GetWidth() - 1) / Core::kScreenTopWidth) + 1);
} else {
return static_cast<u32>(((top_screen.GetWidth() - 1) / Core::kScreenTopHeight) + 1);
}
} }
// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio // Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
@ -30,29 +38,63 @@ static Common::Rectangle<T> maxRectangle(Common::Rectangle<T> window_area,
static_cast<T>(std::round(scale * screen_aspect_ratio))}; static_cast<T>(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(width > 0);
ASSERT(height > 0); ASSERT(height > 0);
FramebufferLayout res{width, height, true, true, {}, {}}; FramebufferLayout res{width, height, true, true, {}, {}, !upright};
Common::Rectangle<u32> screen_window_area;
Common::Rectangle<u32> top_screen;
Common::Rectangle<u32> bot_screen;
float emulation_aspect_ratio;
if (upright) {
// Default layout gives equal screen sizes to the top and bottom screen // Default layout gives equal screen sizes to the top and bottom screen
Common::Rectangle<u32> screen_window_area{0, 0, width, height / 2}; screen_window_area = {0, 0, width / 2, height};
Common::Rectangle<u32> top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); top_screen = maxRectangle(screen_window_area, TOP_SCREEN_UPRIGHT_ASPECT_RATIO);
Common::Rectangle<u32> bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_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<float>(height) / width; float window_aspect_ratio = static_cast<float>(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) { if (window_aspect_ratio < emulation_aspect_ratio) {
// Apply borders to the left and right sides of the window. // 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 =
top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
bot_screen = bot_screen =
bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
}
} else { } else {
// Window is narrower than the emulation content => apply borders to the top and bottom // 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 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 {
// Recalculate the bottom screen to account for the width difference between top and
// bottom
screen_window_area = {0, 0, width, top_screen.GetHeight()}; screen_window_area = {0, 0, width, top_screen.GetHeight()};
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2); bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
@ -62,25 +104,42 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped) {
top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight()); top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight());
} }
} }
}
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. // Move the top screen to the bottom if we are swapped.
res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen; res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen;
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2); res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2);
}
return res; return res;
} }
FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped) { FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
ASSERT(width > 0); ASSERT(width > 0);
ASSERT(height > 0); ASSERT(height > 0);
// The drawing code needs at least somewhat valid values for both screens // The drawing code needs at least somewhat valid values for both screens
// so just calculate them both even if the other isn't showing. // 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<u32> screen_window_area{0, 0, width, height}; Common::Rectangle<u32> screen_window_area{0, 0, width, height};
Common::Rectangle<u32> top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); Common::Rectangle<u32> top_screen;
Common::Rectangle<u32> bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); Common::Rectangle<u32> 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<float>(height) / width; float window_aspect_ratio = static_cast<float>(height) / width;
float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
if (window_aspect_ratio < emulation_aspect_ratio) { if (window_aspect_ratio < emulation_aspect_ratio) {
top_screen = top_screen =
@ -96,11 +155,11 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped) {
return res; return res;
} }
FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped) { FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
ASSERT(width > 0); ASSERT(width > 0);
ASSERT(height > 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 // 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 // To do that, find the total emulation box and maximize that based on window size
float window_aspect_ratio = static_cast<float>(height) / width; float window_aspect_ratio = static_cast<float>(height) / width;
@ -133,11 +192,11 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped) {
return res; return res;
} }
FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped) { FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
ASSERT(width > 0); ASSERT(width > 0);
ASSERT(height > 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 // Aspect ratio of both screens side by side
const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) / const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
(Core::kScreenTopWidth + Core::kScreenBottomWidth); (Core::kScreenTopWidth + Core::kScreenBottomWidth);
@ -170,7 +229,7 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) {
ASSERT(width > 0); ASSERT(width > 0);
ASSERT(height > 0); ASSERT(height > 0);
FramebufferLayout res{width, height, true, true, {}, {}}; FramebufferLayout res{width, height, true, true, {}, {}, !Settings::values.upright_screen};
Common::Rectangle<u32> top_screen{ Common::Rectangle<u32> top_screen{
Settings::values.custom_top_left, Settings::values.custom_top_top, Settings::values.custom_top_left, Settings::values.custom_top_top,
@ -194,6 +253,15 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
int width, height; int width, height;
switch (Settings::values.layout_option) { switch (Settings::values.layout_option) {
case Settings::LayoutOption::SingleScreen: case Settings::LayoutOption::SingleScreen:
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 {
if (Settings::values.swap_screen) { if (Settings::values.swap_screen) {
width = Core::kScreenBottomWidth * res_scale; width = Core::kScreenBottomWidth * res_scale;
height = Core::kScreenBottomHeight * res_scale; height = Core::kScreenBottomHeight * res_scale;
@ -201,7 +269,9 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
width = Core::kScreenTopWidth * res_scale; width = Core::kScreenTopWidth * res_scale;
height = Core::kScreenTopHeight * 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; break;
case Settings::LayoutOption::LargeScreen: case Settings::LayoutOption::LargeScreen:
if (Settings::values.swap_screen) { if (Settings::values.swap_screen) {
@ -211,18 +281,26 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale;
height = Core::kScreenTopHeight * 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; break;
case Settings::LayoutOption::SideScreen: case Settings::LayoutOption::SideScreen:
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
height = Core::kScreenTopHeight * 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; break;
case Settings::LayoutOption::Default: case Settings::LayoutOption::Default:
default: default:
if (Settings::values.upright_screen) {
width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
height = Core::kScreenTopWidth * res_scale;
} else {
width = Core::kScreenTopWidth * res_scale; width = Core::kScreenTopWidth * res_scale;
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
layout = DefaultFrameLayout(width, height, Settings::values.swap_screen); }
layout = DefaultFrameLayout(width, height, Settings::values.swap_screen,
Settings::values.upright_screen);
break; break;
} }
} }

View file

@ -16,6 +16,7 @@ struct FramebufferLayout {
bool bottom_screen_enabled; bool bottom_screen_enabled;
Common::Rectangle<u32> top_screen; Common::Rectangle<u32> top_screen;
Common::Rectangle<u32> bottom_screen; Common::Rectangle<u32> bottom_screen;
bool is_rotated = true;
/** /**
* Returns the ration of pixel size of the top screen, compared to the native size of the 3DS * 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 * @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 * @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 * 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) * @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 * @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 * 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 * @param is_swapped if true, the bottom screen will be the large display
* @return Newly created FramebufferLayout object with default screen regions initialized * @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 * 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 * @param is_swapped if true, the bottom screen will be the left display
* @return Newly created FramebufferLayout object with default screen regions initialized * @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 * Factory method for constructing a custom FramebufferLayout

View file

@ -150,6 +150,7 @@ struct Values {
LayoutOption layout_option; LayoutOption layout_option;
bool swap_screen; bool swap_screen;
bool upright_screen;
bool custom_layout; bool custom_layout;
u16 custom_top_left; u16 custom_top_left;
u16 custom_top_top; u16 custom_top_top;

View file

@ -781,6 +781,35 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
state.Apply(); 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<ScreenRectVertex, 4> 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 * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
* rotation. * rotation.
@ -819,6 +848,40 @@ void RendererOpenGL::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info
state.Apply(); 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<ScreenRectVertex, 4> 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. * Draws the emulated screens to the emulator window.
*/ */
@ -866,9 +929,11 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
if (layout.top_screen_enabled) { if (layout.top_screen_enabled) {
if (layout.is_rotated) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left,
(float)top_screen.GetWidth(), (float)top_screen.GetHeight()); (float)top_screen.top, (float)top_screen.GetWidth(),
(float)top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
(float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.top, (float)top_screen.GetWidth() / 2,
@ -879,24 +944,43 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
(float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.top, (float)top_screen.GetWidth() / 2,
(float)top_screen.GetHeight()); (float)top_screen.GetHeight());
} else if (stereo_single_screen) { } else if (stereo_single_screen) {
DrawSingleScreenStereoRotated(screen_infos[0], screen_infos[1], (float)top_screen.left, 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.top, (float)top_screen.GetWidth(),
(float)top_screen.GetHeight()); (float)top_screen.GetHeight());
} }
} }
}
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
if (layout.bottom_screen_enabled) { if (layout.bottom_screen_enabled) {
if (layout.is_rotated) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left, DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
(float)bottom_screen.top, (float)bottom_screen.GetWidth(), (float)bottom_screen.top, (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight()); (float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left / 2, DrawSingleScreenRotated(
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top,
(float)bottom_screen.GetHeight()); (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
glUniform1i(uniform_layer, 1); glUniform1i(uniform_layer, 1);
DrawSingleScreenRotated(screen_infos[2], DrawSingleScreenRotated(
((float)bottom_screen.left / 2) + ((float)layout.width / 2), screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight()); (float)bottom_screen.GetHeight());
} else if (stereo_single_screen) { } else if (stereo_single_screen) {
@ -905,6 +989,26 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
(float)bottom_screen.GetWidth(), (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight()); (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());
}
}
} }
} }

View file

@ -77,9 +77,12 @@ private:
const GPU::Regs::FramebufferConfig& framebuffer); const GPU::Regs::FramebufferConfig& framebuffer);
void DrawScreens(const Layout::FramebufferLayout& layout); void DrawScreens(const Layout::FramebufferLayout& layout);
void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h); 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, void DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
const ScreenInfo& screen_info_r, float x, float y, float w, const ScreenInfo& screen_info_r, float x, float y, float w,
float h); float h);
void DrawSingleScreenStereo(const ScreenInfo& screen_info_l, const ScreenInfo& screen_info_r,
float x, float y, float w, float h);
void UpdateFramerate(); void UpdateFramerate();
// Loads framebuffer from emulated memory into the display information structure // Loads framebuffer from emulated memory into the display information structure