Merge pull request #5043 from vitor-k/screen-rotate
Implement Upright/Book-style layout
This commit is contained in:
commit
a0f9c795c8
|
@ -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));
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -57,7 +57,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||||
// This must be in alphabetical order according to action name as it must have the same order as
|
// This must be in alphabetical order according to action name as it must have the same order as
|
||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<UISettings::Shortcut, 20> default_hotkeys{
|
const std::array<UISettings::Shortcut, 21> default_hotkeys{
|
||||||
{{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
|
{{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||||
|
@ -70,6 +70,7 @@ const std::array<UISettings::Shortcut, 20> default_hotkeys{
|
||||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
|
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||||
|
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||||
|
@ -296,6 +297,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 +767,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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -410,6 +410,8 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
});
|
});
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Swap Screens", render_window),
|
connect(hotkey_registry.GetHotkey("Main Window", "Swap Screens", render_window),
|
||||||
&QShortcut::activated, ui.action_Screen_Layout_Swap_Screens, &QAction::trigger);
|
&QShortcut::activated, ui.action_Screen_Layout_Swap_Screens, &QAction::trigger);
|
||||||
|
connect(hotkey_registry.GetHotkey("Main Window", "Rotate Screens Upright", render_window),
|
||||||
|
&QShortcut::activated, ui.action_Screen_Layout_Upright_Screens, &QAction::trigger);
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Toggle Screen Layout", render_window),
|
connect(hotkey_registry.GetHotkey("Main Window", "Toggle Screen Layout", render_window),
|
||||||
&QShortcut::activated, this, &GMainWindow::ToggleScreenLayout);
|
&QShortcut::activated, this, &GMainWindow::ToggleScreenLayout);
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
|
connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
|
||||||
|
@ -607,6 +609,9 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
ui.action_Screen_Layout_Swap_Screens->setShortcut(
|
ui.action_Screen_Layout_Swap_Screens->setShortcut(
|
||||||
hotkey_registry.GetHotkey("Main Window", "Swap Screens", this)->key());
|
hotkey_registry.GetHotkey("Main Window", "Swap Screens", this)->key());
|
||||||
ui.action_Screen_Layout_Swap_Screens->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
ui.action_Screen_Layout_Swap_Screens->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||||
|
ui.action_Screen_Layout_Upright_Screens->setShortcut(
|
||||||
|
hotkey_registry.GetHotkey("Main Window", "Rotate Screens Upright", this)->key());
|
||||||
|
ui.action_Screen_Layout_Upright_Screens->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||||
connect(ui.action_Screen_Layout_Default, &QAction::triggered, this,
|
connect(ui.action_Screen_Layout_Default, &QAction::triggered, this,
|
||||||
&GMainWindow::ChangeScreenLayout);
|
&GMainWindow::ChangeScreenLayout);
|
||||||
|
@ -618,6 +623,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 +1442,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 +2044,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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -109,6 +109,7 @@
|
||||||
<addaction name="action_Screen_Layout_Large_Screen"/>
|
<addaction name="action_Screen_Layout_Large_Screen"/>
|
||||||
<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_Upright_Screens"/>
|
||||||
<addaction name="action_Screen_Layout_Swap_Screens"/>
|
<addaction name="action_Screen_Layout_Swap_Screens"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="action_Fullscreen"/>
|
<addaction name="action_Fullscreen"/>
|
||||||
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
return static_cast<u32>(((top_screen.GetWidth() - 1) / Core::kScreenTopWidth) + 1);
|
if (is_rotated) {
|
||||||
|
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,57 +38,108 @@ 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};
|
||||||
// Default layout gives equal screen sizes to the top and bottom screen
|
Common::Rectangle<u32> screen_window_area;
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height / 2};
|
Common::Rectangle<u32> top_screen;
|
||||||
Common::Rectangle<u32> top_screen = maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
|
Common::Rectangle<u32> bot_screen;
|
||||||
Common::Rectangle<u32> bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
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<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
|
||||||
top_screen =
|
if (upright) {
|
||||||
top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
|
// Recalculate the bottom screen to account for the height difference between right and
|
||||||
bot_screen =
|
// left
|
||||||
bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
|
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 {
|
} 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) {
|
||||||
screen_window_area = {0, 0, width, top_screen.GetHeight()};
|
top_screen = top_screen.TranslateY(
|
||||||
bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
|
(screen_window_area.GetHeight() - top_screen.GetHeight()) / 2);
|
||||||
bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
|
bot_screen = bot_screen.TranslateY(
|
||||||
if (swapped) {
|
(screen_window_area.GetHeight() - bot_screen.GetHeight()) / 2);
|
||||||
bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight());
|
|
||||||
} else {
|
} 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.
|
if (upright) {
|
||||||
res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen;
|
// Move the top screen to the right if we are swapped.
|
||||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2);
|
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;
|
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,21 +155,42 @@ 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, {}, {}, !upright};
|
||||||
// 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;
|
||||||
float emulation_aspect_ratio =
|
float emulation_aspect_ratio;
|
||||||
swapped ? Core::kScreenBottomHeight * 4 /
|
float large_screen_aspect_ratio;
|
||||||
(Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth)
|
float small_screen_aspect_ratio;
|
||||||
: Core::kScreenTopHeight * 4 /
|
if (upright) {
|
||||||
(Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth);
|
if (swapped) {
|
||||||
float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
|
emulation_aspect_ratio = (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth) /
|
||||||
float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO;
|
(Core::kScreenBottomHeight * 4);
|
||||||
|
large_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
|
||||||
|
small_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
||||||
|
} else {
|
||||||
|
emulation_aspect_ratio = (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth) /
|
||||||
|
(Core::kScreenTopHeight * 4);
|
||||||
|
large_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
|
||||||
|
small_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (swapped) {
|
||||||
|
emulation_aspect_ratio = Core::kScreenBottomHeight * 4 /
|
||||||
|
(Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth);
|
||||||
|
large_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
||||||
|
small_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
||||||
|
} else {
|
||||||
|
emulation_aspect_ratio = Core::kScreenTopHeight * 4 /
|
||||||
|
(Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth);
|
||||||
|
large_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
||||||
|
small_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||||
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||||
|
@ -119,35 +199,49 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped) {
|
||||||
Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
|
Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
|
||||||
|
|
||||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||||
large_screen =
|
large_screen = large_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||||
large_screen.TranslateX((screen_window_area.GetWidth() - total_rect.GetWidth()) / 2);
|
|
||||||
} else {
|
} else {
|
||||||
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
||||||
}
|
}
|
||||||
// Shift the small screen to the bottom right corner
|
if (upright) {
|
||||||
small_screen =
|
large_screen = large_screen.TranslateY(small_screen.GetHeight());
|
||||||
small_screen.TranslateX(large_screen.right)
|
small_screen = small_screen.TranslateX(large_screen.right - small_screen.GetWidth())
|
||||||
.TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight());
|
.TranslateY(large_screen.top - small_screen.GetHeight());
|
||||||
|
} else {
|
||||||
|
// Shift the small screen to the bottom right corner
|
||||||
|
small_screen =
|
||||||
|
small_screen.TranslateX(large_screen.right)
|
||||||
|
.TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight());
|
||||||
|
}
|
||||||
res.top_screen = swapped ? small_screen : large_screen;
|
res.top_screen = swapped ? small_screen : large_screen;
|
||||||
res.bottom_screen = swapped ? large_screen : small_screen;
|
res.bottom_screen = swapped ? large_screen : small_screen;
|
||||||
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, {}, {}, !upright};
|
||||||
|
|
||||||
// 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) /
|
float emulation_aspect_ratio =
|
||||||
(Core::kScreenTopWidth + Core::kScreenBottomWidth);
|
upright ? static_cast<float>(Core::kScreenTopWidth + Core::kScreenBottomWidth) /
|
||||||
|
Core::kScreenTopHeight
|
||||||
|
: static_cast<float>(Core::kScreenTopHeight) /
|
||||||
|
(Core::kScreenTopWidth + Core::kScreenBottomWidth);
|
||||||
|
|
||||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||||
// Find largest Rectangle that can fit in the window size with the given aspect ratio
|
// Find largest Rectangle that can fit in the window size with the given aspect ratio
|
||||||
Common::Rectangle<u32> screen_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
Common::Rectangle<u32> screen_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||||
// Find sizes of top and bottom screen
|
// Find sizes of top and bottom screen
|
||||||
Common::Rectangle<u32> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
|
Common::Rectangle<u32> top_screen =
|
||||||
Common::Rectangle<u32> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
|
upright ? maxRectangle(screen_rect, TOP_SCREEN_UPRIGHT_ASPECT_RATIO)
|
||||||
|
: maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
|
||||||
|
Common::Rectangle<u32> bot_screen =
|
||||||
|
upright ? maxRectangle(screen_rect, BOT_SCREEN_UPRIGHT_ASPECT_RATIO)
|
||||||
|
: maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
|
||||||
|
|
||||||
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.
|
// Apply borders to the left and right sides of the window.
|
||||||
|
@ -160,9 +254,15 @@ FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped) {
|
||||||
top_screen = top_screen.TranslateY(shift_vertical);
|
top_screen = top_screen.TranslateY(shift_vertical);
|
||||||
bot_screen = bot_screen.TranslateY(shift_vertical);
|
bot_screen = bot_screen.TranslateY(shift_vertical);
|
||||||
}
|
}
|
||||||
// Move the top screen to the right if we are swapped.
|
if (upright) {
|
||||||
res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
|
// Leave the top screen at the top if we are swapped.
|
||||||
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
|
res.top_screen = swapped ? top_screen : top_screen.TranslateY(bot_screen.GetHeight());
|
||||||
|
res.bottom_screen = swapped ? bot_screen.TranslateY(top_screen.GetHeight()) : bot_screen;
|
||||||
|
} else {
|
||||||
|
// Move the top screen to the right if we are swapped.
|
||||||
|
res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
|
||||||
|
res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +270,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,35 +294,69 @@ 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.swap_screen) {
|
if (Settings::values.upright_screen) {
|
||||||
width = Core::kScreenBottomWidth * res_scale;
|
if (Settings::values.swap_screen) {
|
||||||
height = Core::kScreenBottomHeight * res_scale;
|
width = Core::kScreenBottomHeight * res_scale;
|
||||||
|
height = Core::kScreenBottomWidth * res_scale;
|
||||||
|
} else {
|
||||||
|
width = Core::kScreenTopHeight * res_scale;
|
||||||
|
height = Core::kScreenTopWidth * res_scale;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
width = Core::kScreenTopWidth * res_scale;
|
if (Settings::values.swap_screen) {
|
||||||
height = Core::kScreenTopHeight * res_scale;
|
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;
|
break;
|
||||||
case Settings::LayoutOption::LargeScreen:
|
case Settings::LayoutOption::LargeScreen:
|
||||||
if (Settings::values.swap_screen) {
|
if (Settings::values.upright_screen) {
|
||||||
width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale;
|
if (Settings::values.swap_screen) {
|
||||||
height = Core::kScreenBottomHeight * res_scale;
|
width = Core::kScreenBottomHeight * res_scale;
|
||||||
|
height = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale;
|
||||||
|
} else {
|
||||||
|
width = Core::kScreenTopHeight * res_scale;
|
||||||
|
height = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale;
|
if (Settings::values.swap_screen) {
|
||||||
height = Core::kScreenTopHeight * res_scale;
|
width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale;
|
||||||
|
height = Core::kScreenBottomHeight * res_scale;
|
||||||
|
} else {
|
||||||
|
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;
|
break;
|
||||||
case Settings::LayoutOption::SideScreen:
|
case Settings::LayoutOption::SideScreen:
|
||||||
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
if (Settings::values.upright_screen) {
|
||||||
height = Core::kScreenTopHeight * res_scale;
|
width = Core::kScreenTopHeight * res_scale;
|
||||||
layout = SideFrameLayout(width, height, Settings::values.swap_screen);
|
height = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||||
|
} else {
|
||||||
|
width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale;
|
||||||
|
height = Core::kScreenTopHeight * res_scale;
|
||||||
|
}
|
||||||
|
layout = SideFrameLayout(width, height, Settings::values.swap_screen,
|
||||||
|
Settings::values.upright_screen);
|
||||||
break;
|
break;
|
||||||
case Settings::LayoutOption::Default:
|
case Settings::LayoutOption::Default:
|
||||||
default:
|
default:
|
||||||
width = Core::kScreenTopWidth * res_scale;
|
if (Settings::values.upright_screen) {
|
||||||
height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale;
|
||||||
layout = DefaultFrameLayout(width, height, Settings::values.swap_screen);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -87,6 +87,7 @@ void LogSettings() {
|
||||||
LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d);
|
LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d);
|
||||||
LogSetting("Layout_LayoutOption", static_cast<int>(Settings::values.layout_option));
|
LogSetting("Layout_LayoutOption", static_cast<int>(Settings::values.layout_option));
|
||||||
LogSetting("Layout_SwapScreen", Settings::values.swap_screen);
|
LogSetting("Layout_SwapScreen", Settings::values.swap_screen);
|
||||||
|
LogSetting("Layout_UprightScreen", Settings::values.upright_screen);
|
||||||
LogSetting("Utility_DumpTextures", Settings::values.dump_textures);
|
LogSetting("Utility_DumpTextures", Settings::values.dump_textures);
|
||||||
LogSetting("Utility_CustomTextures", Settings::values.custom_textures);
|
LogSetting("Utility_CustomTextures", Settings::values.custom_textures);
|
||||||
LogSetting("Audio_EnableDspLle", Settings::values.enable_dsp_lle);
|
LogSetting("Audio_EnableDspLle", Settings::values.enable_dsp_lle);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,44 +929,85 @@ 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 (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
if (layout.is_rotated) {
|
||||||
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top,
|
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||||
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left,
|
||||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
(float)top_screen.top, (float)top_screen.GetWidth(),
|
||||||
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
|
(float)top_screen.GetHeight());
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
||||||
(float)top_screen.GetHeight());
|
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||||
DrawSingleScreenRotated(screen_infos[1],
|
(float)top_screen.GetHeight());
|
||||||
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
glUniform1i(uniform_layer, 1);
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
DrawSingleScreenRotated(screen_infos[1],
|
||||||
(float)top_screen.GetHeight());
|
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||||
} else if (stereo_single_screen) {
|
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||||
DrawSingleScreenStereoRotated(screen_infos[0], screen_infos[1], (float)top_screen.left,
|
(float)top_screen.GetHeight());
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth(),
|
} else if (stereo_single_screen) {
|
||||||
(float)top_screen.GetHeight());
|
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);
|
glUniform1i(uniform_layer, 0);
|
||||||
if (layout.bottom_screen_enabled) {
|
if (layout.bottom_screen_enabled) {
|
||||||
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
if (layout.is_rotated) {
|
||||||
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
|
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
|
||||||
(float)bottom_screen.GetHeight());
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||||
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
(float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left / 2,
|
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
DrawSingleScreenRotated(
|
||||||
(float)bottom_screen.GetHeight());
|
screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top,
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreenRotated(screen_infos[2],
|
glUniform1i(uniform_layer, 1);
|
||||||
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
DrawSingleScreenRotated(
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||||
(float)bottom_screen.GetHeight());
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||||
} else if (stereo_single_screen) {
|
(float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2],
|
} else if (stereo_single_screen) {
|
||||||
(float)bottom_screen.left, (float)bottom_screen.top,
|
DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2],
|
||||||
(float)bottom_screen.GetWidth(),
|
(float)bottom_screen.left, (float)bottom_screen.top,
|
||||||
(float)bottom_screen.GetHeight());
|
(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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Reference in New Issue