diff --git a/android/app/src/main/res/values-es/arrays.xml b/android/app/src/main/res/values-es/arrays.xml
index 746d5e9d2..4ae8d947e 100644
--- a/android/app/src/main/res/values-es/arrays.xml
+++ b/android/app/src/main/res/values-es/arrays.xml
@@ -45,6 +45,7 @@
- Auto (Nativo del juego)
+ - Auto (Match Display)
- 4:3
- 16:9
- 16:10
diff --git a/android/app/src/main/res/values-it/arrays.xml b/android/app/src/main/res/values-it/arrays.xml
index c12144117..ace19f3c9 100644
--- a/android/app/src/main/res/values-it/arrays.xml
+++ b/android/app/src/main/res/values-it/arrays.xml
@@ -45,6 +45,7 @@
- Auto (Game Native)
+ - Auto (Match Display)
- 4:3
- 16:9
- 16:10
diff --git a/android/app/src/main/res/values-nl/arrays.xml b/android/app/src/main/res/values-nl/arrays.xml
index c1e3e4aa4..2ff0056a1 100644
--- a/android/app/src/main/res/values-nl/arrays.xml
+++ b/android/app/src/main/res/values-nl/arrays.xml
@@ -45,6 +45,7 @@
- Auto (Game Native)
+ - Auto (Match Display)
- 4:3
- 16:9
- 16:10
diff --git a/android/app/src/main/res/values-pt-rBR/arrays.xml b/android/app/src/main/res/values-pt-rBR/arrays.xml
index 7e73bb9e3..678cc19d1 100644
--- a/android/app/src/main/res/values-pt-rBR/arrays.xml
+++ b/android/app/src/main/res/values-pt-rBR/arrays.xml
@@ -45,6 +45,7 @@
- Auto (Nativo)
+ - Auto (Match Display)
- 4:3
- 16:9
- 16:10
diff --git a/android/app/src/main/res/values-ru/arrays.xml b/android/app/src/main/res/values-ru/arrays.xml
index 914e01cfd..7745642d6 100644
--- a/android/app/src/main/res/values-ru/arrays.xml
+++ b/android/app/src/main/res/values-ru/arrays.xml
@@ -44,8 +44,9 @@
- Все границы
- - Автонастройка (нативное игре)
- - 4:3
+ - Автонастройка (нативное игре)
+ - Auto (Match Display)
+ - 4:3
- 16:9
- 16:10
- 19:9
diff --git a/android/app/src/main/res/values/arrays.xml b/android/app/src/main/res/values/arrays.xml
index f325f0be3..82e703dcd 100644
--- a/android/app/src/main/res/values/arrays.xml
+++ b/android/app/src/main/res/values/arrays.xml
@@ -89,6 +89,7 @@
- Auto (Game Native)
+ - Auto (Match Display)
- 4:3
- 16:9
- 16:10
@@ -105,6 +106,7 @@
- Auto (Game Native)
+ - Auto (Match Window)
- 4:3
- 16:9
- 16:10
diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp
index ed086e83e..a5a1b90fa 100644
--- a/src/core/gpu.cpp
+++ b/src/core/gpu.cpp
@@ -492,7 +492,7 @@ float GPU::GetDisplayAspectRatio() const
}
else
{
- return Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio);
+ return g_settings.GetDisplayAspectRatioValue();
}
}
diff --git a/src/core/gte.cpp b/src/core/gte.cpp
index 53b78710b..71a492385 100644
--- a/src/core/gte.cpp
+++ b/src/core/gte.cpp
@@ -3,10 +3,13 @@
#include "common/bitutils.h"
#include "common/state_wrapper.h"
#include "cpu_core.h"
+#include "host_display.h"
+#include "host_interface.h"
#include "pgxp.h"
#include "settings.h"
#include
#include
+#include
namespace GTE {
@@ -19,6 +22,11 @@ static constexpr s32 IR0_MAX_VALUE = 0x1000;
static constexpr s32 IR123_MIN_VALUE = -(INT64_C(1) << 15);
static constexpr s32 IR123_MAX_VALUE = (INT64_C(1) << 15) - 1;
+static DisplayAspectRatio s_aspect_ratio = DisplayAspectRatio::R4_3;
+static u32 s_custom_aspect_ratio_numerator;
+static u32 s_custom_aspect_ratio_denominator;
+static float s_custom_aspect_ratio_f;
+
#define REGS CPU::g_state.gte_regs
ALWAYS_INLINE static u32 CountLeadingBits(u32 value)
@@ -148,6 +156,7 @@ ALWAYS_INLINE static u32 TruncateRGB(s32 value)
void Initialize()
{
+ UpdateAspectRatio();
Reset();
}
@@ -162,6 +171,55 @@ bool DoState(StateWrapper& sw)
return !sw.HasError();
}
+void UpdateAspectRatio()
+{
+ if (!g_settings.gpu_widescreen_hack)
+ {
+ s_aspect_ratio = DisplayAspectRatio::R4_3;
+ return;
+ }
+
+ s_aspect_ratio = g_settings.display_aspect_ratio;
+
+ u32 num, denom;
+ switch (s_aspect_ratio)
+ {
+ case DisplayAspectRatio::MatchWindow:
+ {
+ const HostDisplay* display = g_host_interface->GetDisplay();
+ if (!display)
+ {
+ s_aspect_ratio = DisplayAspectRatio::R4_3;
+ return;
+ }
+
+ num = display->GetWindowWidth();
+ denom = display->GetWindowHeight();
+ }
+ break;
+
+ case DisplayAspectRatio::Custom:
+ {
+ num = g_settings.display_aspect_ratio_custom_numerator;
+ denom = g_settings.display_aspect_ratio_custom_denominator;
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ // (4 / 3) / (num / denom) => gcd((4 * denom) / (3 * num))
+ const u32 x = 4u * denom;
+ const u32 y = 3u * num;
+ const u32 gcd = std::gcd(x, y);
+
+ s_custom_aspect_ratio_numerator = x / gcd;
+ s_custom_aspect_ratio_denominator = y / gcd;
+
+ s_custom_aspect_ratio_f = static_cast((4.0 / 3.0) / (static_cast(num) / static_cast(denom)));
+}
+
u32 ReadRegister(u32 index)
{
DebugAssert(index < countof(REGS.r32));
@@ -614,66 +672,65 @@ static void RTPS(const s16 V[3], u8 shift, bool lm, bool last)
const s64 result = static_cast(ZeroExtend64(UNRDivide(REGS.H, REGS.SZ3)));
s64 Sx;
- if (g_settings.gpu_widescreen_hack)
+ switch (s_aspect_ratio)
{
- const DisplayAspectRatio ar = g_settings.display_aspect_ratio;
- switch (ar)
- {
- case DisplayAspectRatio::R16_9:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(4)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R16_9:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(4)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R16_10:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(5)) / s64(6)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R16_10:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(5)) / s64(6)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R19_9:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(12)) / s64(19)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R19_9:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(12)) / s64(19)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R20_9:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(5)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R20_9:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(5)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R21_9:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(9)) / s64(16)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R21_9:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(9)) / s64(16)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R32_9:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(8)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R32_9:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(3)) / s64(8)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R8_7:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(7)) / s64(6)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R8_7:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(7)) / s64(6)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R5_4:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(16)) / s64(15)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R5_4:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(16)) / s64(15)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R3_2:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(8)) / s64(9)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R3_2:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(8)) / s64(9)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R2_1:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(2)) / s64(3)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R2_1:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(2)) / s64(3)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::R1_1:
- Sx = ((((s64(result) * s64(REGS.IR1)) * s64(7)) / s64(6)) + s64(REGS.OFX));
- break;
+ case DisplayAspectRatio::R1_1:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(7)) / s64(6)) + s64(REGS.OFX));
+ break;
- case DisplayAspectRatio::Auto:
- case DisplayAspectRatio::R4_3:
- case DisplayAspectRatio::PAR1_1:
- default:
- Sx = (s64(result) * s64(REGS.IR1) + s64(REGS.OFX));
- break;
- }
- }
- else
- {
- Sx = (s64(result) * s64(REGS.IR1) + s64(REGS.OFX));
+ case DisplayAspectRatio::Custom:
+ case DisplayAspectRatio::MatchWindow:
+ Sx = ((((s64(result) * s64(REGS.IR1)) * s64(s_custom_aspect_ratio_numerator)) /
+ s64(s_custom_aspect_ratio_denominator)) +
+ s64(REGS.OFX));
+ break;
+
+ case DisplayAspectRatio::Auto:
+ case DisplayAspectRatio::R4_3:
+ case DisplayAspectRatio::PAR1_1:
+ default:
+ Sx = (s64(result) * s64(REGS.IR1) + s64(REGS.OFX));
+ break;
}
const s64 Sy = s64(result) * s64(REGS.IR2) + s64(REGS.OFY);
@@ -713,70 +770,68 @@ static void RTPS(const s16 V[3], u8 shift, bool lm, bool last)
const float precise_h_div_sz = float(REGS.H) / precise_z;
const float fofx = float(REGS.OFX) / float(1 << 16);
const float fofy = float(REGS.OFY) / float(1 << 16);
- float precise_x;
- if (g_settings.gpu_widescreen_hack)
+ float precise_x = precise_ir1 * precise_h_div_sz;
+
+ switch (s_aspect_ratio)
{
- precise_x = precise_ir1 * precise_h_div_sz;
- const DisplayAspectRatio ar = g_settings.display_aspect_ratio;
- switch (ar)
- {
- case DisplayAspectRatio::R16_9:
- precise_x = (precise_x * 3.0f) / 4.0f;
- break;
+ case DisplayAspectRatio::R16_9:
+ precise_x = (precise_x * 3.0f) / 4.0f;
+ break;
- case DisplayAspectRatio::R16_10:
- precise_x = (precise_x * 5.0f) / 6.0f;
- break;
+ case DisplayAspectRatio::R16_10:
+ precise_x = (precise_x * 5.0f) / 6.0f;
+ break;
- case DisplayAspectRatio::R19_9:
- precise_x = (precise_x * 12.0f) / 19.0f;
- break;
+ case DisplayAspectRatio::R19_9:
+ precise_x = (precise_x * 12.0f) / 19.0f;
+ break;
- case DisplayAspectRatio::R20_9:
- precise_x = (precise_x * 3.0f) / 5.0f;
- break;
+ case DisplayAspectRatio::R20_9:
+ precise_x = (precise_x * 3.0f) / 5.0f;
+ break;
- case DisplayAspectRatio::R21_9:
- precise_x = (precise_x * 9.0f) / 16.0f;
- break;
+ case DisplayAspectRatio::R21_9:
+ precise_x = (precise_x * 9.0f) / 16.0f;
+ break;
- case DisplayAspectRatio::R32_9:
- precise_x = (precise_x * 3.0f) / 8.0f;
- break;
+ case DisplayAspectRatio::R32_9:
+ precise_x = (precise_x * 3.0f) / 8.0f;
+ break;
- case DisplayAspectRatio::R8_7:
- precise_x = (precise_x * 7.0f) / 6.0f;
- break;
+ case DisplayAspectRatio::R8_7:
+ precise_x = (precise_x * 7.0f) / 6.0f;
+ break;
- case DisplayAspectRatio::R5_4:
- precise_x = (precise_x * 16.0f) / 15.0f;
- break;
+ case DisplayAspectRatio::R5_4:
+ precise_x = (precise_x * 16.0f) / 15.0f;
+ break;
- case DisplayAspectRatio::R3_2:
- precise_x = (precise_x * 8.0f) / 9.0f;
- break;
+ case DisplayAspectRatio::R3_2:
+ precise_x = (precise_x * 8.0f) / 9.0f;
+ break;
- case DisplayAspectRatio::R2_1:
- precise_x = (precise_x * 2.0f) / 3.0f;
- break;
+ case DisplayAspectRatio::R2_1:
+ precise_x = (precise_x * 2.0f) / 3.0f;
+ break;
- case DisplayAspectRatio::R1_1:
- precise_x = (precise_x * 7.0f) / 6.0f;
- break;
+ case DisplayAspectRatio::R1_1:
+ precise_x = (precise_x * 7.0f) / 6.0f;
+ break;
- case DisplayAspectRatio::Auto:
- case DisplayAspectRatio::R4_3:
- case DisplayAspectRatio::PAR1_1:
- default:
- break;
- }
- precise_x += fofx;
- }
- else
- {
- precise_x = fofx + (precise_ir1 * precise_h_div_sz);
+ case DisplayAspectRatio::MatchWindow:
+ case DisplayAspectRatio::Custom:
+ precise_x = precise_x * s_custom_aspect_ratio_f;
+ break;
+
+ case DisplayAspectRatio::Auto:
+ case DisplayAspectRatio::R4_3:
+ case DisplayAspectRatio::PAR1_1:
+ default:
+ break;
}
+ precise_x += fofx;
+
float precise_y = fofy + (precise_ir2 * precise_h_div_sz);
precise_x = std::clamp(precise_x, -1024.0f, 1023.0f);
diff --git a/src/core/gte.h b/src/core/gte.h
index 716148c13..217a897b7 100644
--- a/src/core/gte.h
+++ b/src/core/gte.h
@@ -8,6 +8,7 @@ namespace GTE {
void Initialize();
void Reset();
bool DoState(StateWrapper& sw);
+void UpdateAspectRatio();
// control registers are offset by +32
u32 ReadRegister(u32 index);
diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp
index 78095a2d9..cd025bd91 100644
--- a/src/core/host_interface.cpp
+++ b/src/core/host_interface.cpp
@@ -533,6 +533,8 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetIntValue("Display", "LineEndOffset", 0);
si.SetStringValue("Display", "AspectRatio",
Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO));
+ si.SetIntValue("Display", "CustomAspectRatioNumerator", 4);
+ si.GetIntValue("Display", "CustomAspectRatioDenominator", 3);
si.SetBoolValue("Display", "Force4_3For24Bit", false);
si.SetBoolValue("Display", "LinearFiltering", true);
si.SetBoolValue("Display", "IntegerScaling", false);
@@ -803,6 +805,14 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
g_gpu->UpdateSettings();
}
+ if (g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
+ (g_settings.display_aspect_ratio == DisplayAspectRatio::Custom &&
+ (g_settings.display_aspect_ratio_custom_numerator != old_settings.display_aspect_ratio_custom_numerator ||
+ g_settings.display_aspect_ratio_custom_denominator != old_settings.display_aspect_ratio_custom_denominator)))
+ {
+ GTE::UpdateAspectRatio();
+ }
+
if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
(g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling ||
g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu)))
@@ -899,6 +909,15 @@ void HostInterface::SetUserDirectoryToProgramDirectory()
m_user_directory = program_directory;
}
+void HostInterface::OnHostDisplayResized()
+{
+ if (System::IsValid())
+ {
+ if (g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow)
+ GTE::UpdateAspectRatio();
+ }
+}
+
std::string HostInterface::GetUserDirectoryRelativePath(const char* format, ...) const
{
std::va_list ap;
diff --git a/src/core/host_interface.h b/src/core/host_interface.h
index 945b4fcdc..debce9633 100644
--- a/src/core/host_interface.h
+++ b/src/core/host_interface.h
@@ -183,6 +183,9 @@ protected:
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
virtual void SetMouseMode(bool relative, bool hide_cursor);
+ /// Call when host display size changes, use with "match display" aspect ratio setting.
+ virtual void OnHostDisplayResized();
+
/// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory();
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0a45b4bed..d44559794 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -3,6 +3,7 @@
#include "common/file_system.h"
#include "common/make_array.h"
#include "common/string_util.h"
+#include "host_display.h"
#include "host_interface.h"
#include
#include
@@ -222,6 +223,10 @@ void Settings::Load(SettingsInterface& si)
ParseDisplayAspectRatio(
si.GetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(DEFAULT_DISPLAY_ASPECT_RATIO)).c_str())
.value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
+ display_aspect_ratio_custom_numerator = static_cast(
+ std::clamp(si.GetIntValue("Display", "CustomAspectRatioNumerator", 4), 1, std::numeric_limits::max()));
+ display_aspect_ratio_custom_denominator = static_cast(
+ std::clamp(si.GetIntValue("Display", "CustomAspectRatioDenominator", 3), 1, std::numeric_limits::max()));
display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
display_active_start_offset = static_cast(si.GetIntValue("Display", "ActiveStartOffset", 0));
display_active_end_offset = static_cast(si.GetIntValue("Display", "ActiveEndOffset", 0));
@@ -395,6 +400,8 @@ void Settings::Save(SettingsInterface& si) const
si.SetIntValue("Display", "LineEndOffset", display_line_end_offset);
si.SetBoolValue("Display", "Force4_3For24Bit", display_force_4_3_for_24bit);
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
+ si.SetIntValue("Display", "CustomAspectRatioNumerator", display_aspect_ratio_custom_numerator);
+ si.GetIntValue("Display", "CustomAspectRatioDenominator", display_aspect_ratio_custom_denominator);
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
si.SetBoolValue("Display", "IntegerScaling", display_integer_scaling);
si.SetBoolValue("Display", "Stretch", display_stretch);
@@ -767,12 +774,12 @@ const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
return s_display_crop_mode_display_names[static_cast(crop_mode)];
}
-static std::array s_display_aspect_ratio_names = {
- {TRANSLATABLE("DisplayAspectRatio", "Auto (Game Native)"), "4:3", "16:9", "16:10", "19:9", "20:9", "21:9", "32:9",
- "8:7", "5:4", "3:2", "2:1 (VRAM 1:1)", "1:1", "PAR 1:1"}};
-static constexpr std::array s_display_aspect_ratio_values = {
- {-1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 16.0f / 10.0f, 19.0f / 9.0f, 20.0f / 9.0f, 64.0f / 27.0f, 32.0f / 9.0f,
- 8.0f / 7.0f, 5.0f / 4.0f, 3.0f / 2.0f, 2.0f / 1.0f, 1.0f, -1.0f}};
+static std::array s_display_aspect_ratio_names = {
+ {TRANSLATABLE("DisplayAspectRatio", "Auto (Game Native)"), "Auto (Match Window)", "Custom", "4:3", "16:9", "16:10",
+ "19:9", "20:9", "21:9", "32:9", "8:7", "5:4", "3:2", "2:1 (VRAM 1:1)", "1:1", "PAR 1:1"}};
+static constexpr std::array s_display_aspect_ratio_values = {
+ {-1.0f, -1.0f, -1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 16.0f / 10.0f, 19.0f / 9.0f, 20.0f / 9.0f, 64.0f / 27.0f,
+ 32.0f / 9.0f, 8.0f / 7.0f, 5.0f / 4.0f, 3.0f / 2.0f, 2.0f / 1.0f, 1.0f, -1.0f}};
std::optional Settings::ParseDisplayAspectRatio(const char* str)
{
@@ -793,9 +800,32 @@ const char* Settings::GetDisplayAspectRatioName(DisplayAspectRatio ar)
return s_display_aspect_ratio_names[static_cast(ar)];
}
-float Settings::GetDisplayAspectRatioValue(DisplayAspectRatio ar)
+float Settings::GetDisplayAspectRatioValue() const
{
- return s_display_aspect_ratio_values[static_cast(ar)];
+ switch (display_aspect_ratio)
+ {
+ case DisplayAspectRatio::MatchWindow:
+ {
+ const HostDisplay* display = g_host_interface->GetDisplay();
+ if (!display)
+ return s_display_aspect_ratio_values[static_cast(DEFAULT_DISPLAY_ASPECT_RATIO)];
+
+ const u32 width = display->GetWindowWidth();
+ const u32 height = display->GetWindowHeight() - display->GetDisplayTopMargin();
+ return static_cast(width) / static_cast(height);
+ }
+
+ case DisplayAspectRatio::Custom:
+ {
+ return static_cast(display_aspect_ratio_custom_numerator) /
+ static_cast(display_aspect_ratio_custom_denominator);
+ }
+
+ default:
+ {
+ return s_display_aspect_ratio_values[static_cast(display_aspect_ratio)];
+ }
+ }
}
static std::array s_audio_backend_names = {{
diff --git a/src/core/settings.h b/src/core/settings.h
index 352ab4d5b..71e24e74a 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -127,6 +127,8 @@ struct Settings
bool gpu_pgxp_depth_buffer = false;
DisplayCropMode display_crop_mode = DisplayCropMode::None;
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::Auto;
+ u16 display_aspect_ratio_custom_numerator = 0;
+ u16 display_aspect_ratio_custom_denominator = 0;
s16 display_active_start_offset = 0;
s16 display_active_end_offset = 0;
s8 display_line_start_offset = 0;
@@ -256,6 +258,8 @@ struct Settings
return audio_output_muted ? 0 : (fast_forwarding ? audio_fast_forward_volume : audio_output_volume);
}
+ float GetDisplayAspectRatioValue() const;
+
bool HasAnyPerGameMemoryCards() const;
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
@@ -316,7 +320,6 @@ struct Settings
static std::optional ParseDisplayAspectRatio(const char* str);
static const char* GetDisplayAspectRatioName(DisplayAspectRatio ar);
- static float GetDisplayAspectRatioValue(DisplayAspectRatio ar);
static std::optional ParseAudioBackend(const char* str);
static const char* GetAudioBackendName(AudioBackend backend);
diff --git a/src/core/types.h b/src/core/types.h
index 07455237b..8ad8f7611 100644
--- a/src/core/types.h
+++ b/src/core/types.h
@@ -94,6 +94,8 @@ enum class DisplayCropMode : u8
enum class DisplayAspectRatio : u8
{
Auto,
+ MatchWindow,
+ Custom,
R4_3,
R16_9,
R16_10,
diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp
index 2b28f468f..1f00148f1 100644
--- a/src/duckstation-qt/displaysettingswidget.cpp
+++ b/src/duckstation-qt/displaysettingswidget.cpp
@@ -25,6 +25,10 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.displayAspectRatio, "Display", "AspectRatio",
&Settings::ParseDisplayAspectRatio, &Settings::GetDisplayAspectRatioName,
Settings::DEFAULT_DISPLAY_ASPECT_RATIO);
+ SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.customAspectRatioNumerator, "Display",
+ "CustomAspectRatioNumerator", 1);
+ SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.customAspectRatioDenominator, "Display",
+ "CustomAspectRatioDenominator", 1);
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.displayCropMode, "Display", "CropMode",
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
Settings::DEFAULT_DISPLAY_CROP_MODE);
@@ -59,10 +63,13 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW
&DisplaySettingsWidget::onGPUAdapterIndexChanged);
connect(m_ui.fullscreenMode, QOverload::of(&QComboBox::currentIndexChanged), this,
&DisplaySettingsWidget::onGPUFullscreenModeIndexChanged);
+ connect(m_ui.displayAspectRatio, QOverload::of(&QComboBox::currentIndexChanged), this,
+ &DisplaySettingsWidget::onAspectRatioChanged);
connect(m_ui.displayIntegerScaling, &QCheckBox::stateChanged, this,
&DisplaySettingsWidget::onIntegerFilteringChanged);
populateGPUAdaptersAndResolutions();
onIntegerFilteringChanged();
+ onAspectRatioChanged();
dialog->registerWidgetHelp(
m_ui.renderer, tr("Renderer"),
@@ -283,3 +290,13 @@ void DisplaySettingsWidget::onIntegerFilteringChanged()
m_ui.displayLinearFiltering->setEnabled(!m_ui.displayIntegerScaling->isChecked());
m_ui.displayStretch->setEnabled(!m_ui.displayIntegerScaling->isChecked());
}
+
+void DisplaySettingsWidget::onAspectRatioChanged()
+{
+ const bool is_custom =
+ static_cast(m_ui.displayAspectRatio->currentIndex()) == DisplayAspectRatio::Custom;
+
+ m_ui.customAspectRatioNumerator->setVisible(is_custom);
+ m_ui.customAspectRatioDenominator->setVisible(is_custom);
+ m_ui.customAspectRatioSeparator->setVisible(is_custom);
+}
diff --git a/src/duckstation-qt/displaysettingswidget.h b/src/duckstation-qt/displaysettingswidget.h
index d582de82a..b3fcbc3a6 100644
--- a/src/duckstation-qt/displaysettingswidget.h
+++ b/src/duckstation-qt/displaysettingswidget.h
@@ -21,6 +21,7 @@ private Q_SLOTS:
void onGPUAdapterIndexChanged();
void onGPUFullscreenModeIndexChanged();
void onIntegerFilteringChanged();
+ void onAspectRatioChanged();
private:
void setupAdditionalUi();
diff --git a/src/duckstation-qt/displaysettingswidget.ui b/src/duckstation-qt/displaysettingswidget.ui
index 9ac7ca856..05e497589 100644
--- a/src/duckstation-qt/displaysettingswidget.ui
+++ b/src/duckstation-qt/displaysettingswidget.ui
@@ -111,7 +111,38 @@
-
-
+
+
-
+
+
+ -
+
+
+ 1
+
+
+ 9999
+
+
+
+ -
+
+
+ :
+
+
+
+ -
+
+
+ 1
+
+
+ 9999
+
+
+
+
-
diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp
index 310305111..beb508837 100644
--- a/src/frontend-common/common_host_interface.cpp
+++ b/src/frontend-common/common_host_interface.cpp
@@ -575,6 +575,8 @@ void CommonHostInterface::OnHostDisplayResized()
const u32 new_height = m_display ? m_display->GetWindowHeight() : 0;
const float new_scale = m_display ? m_display->GetWindowScale() : 1.0f;
+ HostInterface::OnHostDisplayResized();
+
ImGui::GetIO().DisplaySize.x = static_cast(new_width);
ImGui::GetIO().DisplaySize.y = static_cast(new_height);
diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h
index 8061f8720..277073495 100644
--- a/src/frontend-common/common_host_interface.h
+++ b/src/frontend-common/common_host_interface.h
@@ -412,13 +412,13 @@ protected:
void UpdateSpeedLimiterState();
void RecreateSystem() override;
+ void OnHostDisplayResized() override;
void ApplyGameSettings(bool display_osd_messages);
void ApplyControllerCompatibilitySettings(u64 controller_mask, bool display_osd_messages);
bool CreateHostDisplayResources();
void ReleaseHostDisplayResources();
- void OnHostDisplayResized();
virtual void DrawImGuiWindows();