diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index e3cbf1844..6b3c97e2c 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4323,7 +4323,7 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawEnumSetting(bsi, FSUI_ICONSTR(ICON_FA_CROP_ALT, "Crop Mode"), FSUI_CSTR("Determines how much of the area typically not visible on a consumer TV set to crop/hide."), "Display", "CropMode", Settings::DEFAULT_DISPLAY_CROP_MODE, &Settings::ParseDisplayCropMode, - &Settings::GetDisplayCropModeName, &Settings::GetDisplayCropModeDisplayName, DisplayCropMode::Count); + &Settings::GetDisplayCropModeName, &Settings::GetDisplayCropModeDisplayName, DisplayCropMode::MaxCount); DrawEnumSetting( bsi, FSUI_ICONSTR(ICON_FA_EXPAND, "Scaling"), diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 284218a45..4340c2042 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -594,45 +594,62 @@ float GPU::ComputeVerticalFrequency() const float GPU::ComputeDisplayAspectRatio() const { if (g_settings.debugging.show_vram) - { return static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT); - } - else if (g_settings.display_force_4_3_for_24bit && m_GPUSTAT.display_area_color_depth_24) - { + + // Display off => Doesn't matter. + if (m_crtc_state.display_width == 0 || m_crtc_state.display_height == 0) return 4.0f / 3.0f; - } - else if (g_settings.display_aspect_ratio == DisplayAspectRatio::Auto) + + // PAR 1:1 is not corrected. + if (g_settings.display_aspect_ratio == DisplayAspectRatio::PAR1_1) + return static_cast(m_crtc_state.display_width) / static_cast(m_crtc_state.display_height); + + float ar = 4.0f / 3.0f; + if (!g_settings.display_force_4_3_for_24bit || !m_GPUSTAT.display_area_color_depth_24) { - const CRTCState& cs = m_crtc_state; - float relative_width = static_cast(cs.horizontal_visible_end - cs.horizontal_visible_start); - float relative_height = static_cast(cs.vertical_visible_end - cs.vertical_visible_start); - - if (relative_width <= 0 || relative_height <= 0) - return 4.0f / 3.0f; - - if (m_GPUSTAT.pal_mode) + if (g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow && g_gpu_device->HasMainSwapChain()) { - relative_width /= static_cast(PAL_HORIZONTAL_ACTIVE_END - PAL_HORIZONTAL_ACTIVE_START); - relative_height /= static_cast(PAL_VERTICAL_ACTIVE_END - PAL_VERTICAL_ACTIVE_START); + // Match window has already been corrected. + return static_cast(g_gpu_device->GetMainSwapChain()->GetWidth()) / + static_cast(g_gpu_device->GetMainSwapChain()->GetHeight()); + } + else if (g_settings.display_aspect_ratio == DisplayAspectRatio::Custom) + { + ar = static_cast(g_settings.display_aspect_ratio_custom_numerator) / + static_cast(g_settings.display_aspect_ratio_custom_denominator); } else { - relative_width /= static_cast(NTSC_HORIZONTAL_ACTIVE_END - NTSC_HORIZONTAL_ACTIVE_START); - relative_height /= static_cast(NTSC_VERTICAL_ACTIVE_END - NTSC_VERTICAL_ACTIVE_START); + ar = g_settings.GetDisplayAspectRatioValue(); } - return (relative_width / relative_height) * (4.0f / 3.0f); } - else if (g_settings.display_aspect_ratio == DisplayAspectRatio::PAR1_1) - { - if (m_crtc_state.display_width == 0 || m_crtc_state.display_height == 0) - return 4.0f / 3.0f; - return static_cast(m_crtc_state.display_width) / static_cast(m_crtc_state.display_height); + return ComputeAspectRatioCorrection() * ar; +} + +float GPU::ComputeAspectRatioCorrection() const +{ + const CRTCState& cs = m_crtc_state; + float relative_width = static_cast(cs.horizontal_visible_end - cs.horizontal_visible_start); + float relative_height = static_cast(cs.vertical_visible_end - cs.vertical_visible_start); + if (relative_width <= 0 || relative_height <= 0 || + g_settings.display_crop_mode == DisplayCropMode::OverscanUncorrected) + { + return 1.0f; + } + + if (m_GPUSTAT.pal_mode) + { + relative_width /= static_cast(PAL_HORIZONTAL_ACTIVE_END - PAL_HORIZONTAL_ACTIVE_START); + relative_height /= static_cast(PAL_VERTICAL_ACTIVE_END - PAL_VERTICAL_ACTIVE_START); } else { - return g_settings.GetDisplayAspectRatioValue(); + relative_width /= static_cast(NTSC_HORIZONTAL_ACTIVE_END - NTSC_HORIZONTAL_ACTIVE_START); + relative_height /= static_cast(NTSC_VERTICAL_ACTIVE_END - NTSC_VERTICAL_ACTIVE_START); } + + return (relative_width / relative_height); } void GPU::UpdateCRTCConfig() @@ -725,6 +742,10 @@ void GPU::UpdateCRTCDisplayParameters() (std::min(cs.regs.X2, horizontal_total) / cs.dot_clock_divider) * cs.dot_clock_divider; const u16 vertical_display_start = std::min(cs.regs.Y1, vertical_total); const u16 vertical_display_end = std::min(cs.regs.Y2, vertical_total); + const u16 old_horizontal_visible_start = cs.horizontal_visible_start; + const u16 old_horizontal_visible_end = cs.horizontal_visible_end; + const u16 old_vertical_visible_start = cs.vertical_visible_start; + const u16 old_vertical_visible_end = cs.vertical_visible_end; if (m_GPUSTAT.pal_mode) { @@ -739,6 +760,7 @@ void GPU::UpdateCRTCDisplayParameters() break; case DisplayCropMode::Overscan: + case DisplayCropMode::OverscanUncorrected: cs.horizontal_visible_start = static_cast(std::max(0, 628 + g_settings.display_active_start_offset)); cs.horizontal_visible_end = static_cast(std::max(cs.horizontal_visible_start, 3188 + g_settings.display_active_end_offset)); @@ -776,6 +798,7 @@ void GPU::UpdateCRTCDisplayParameters() break; case DisplayCropMode::Overscan: + case DisplayCropMode::OverscanUncorrected: cs.horizontal_visible_start = static_cast(std::max(0, 608 + g_settings.display_active_start_offset)); cs.horizontal_visible_end = static_cast(std::max(cs.horizontal_visible_start, 3168 + g_settings.display_active_end_offset)); @@ -872,6 +895,13 @@ void GPU::UpdateCRTCDisplayParameters() << height_shift; } + if (old_horizontal_visible_start != cs.horizontal_visible_start || + old_horizontal_visible_end != cs.horizontal_visible_end || + old_vertical_visible_start != cs.vertical_visible_start || old_vertical_visible_end != cs.vertical_visible_end) + { + System::UpdateGTEAspectRatio(); + } + if (cs.display_vram_width != old_vram_width || cs.display_vram_height != old_vram_height) UpdateResolutionScale(); } diff --git a/src/core/gpu.h b/src/core/gpu.h index 5c18c9f98..55c3b8d4c 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -184,6 +184,7 @@ public: float ComputeHorizontalFrequency() const; float ComputeVerticalFrequency() const; float ComputeDisplayAspectRatio() const; + float ComputeAspectRatioCorrection() const; static std::unique_ptr CreateHardwareRenderer(Error* error); static std::unique_ptr CreateSoftwareRenderer(Error* error); diff --git a/src/core/gte.cpp b/src/core/gte.cpp index f6d340f41..6b0dcb1ff 100644 --- a/src/core/gte.cpp +++ b/src/core/gte.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "gte.h" - #include "cpu_core.h" #include "cpu_core_private.h" #include "cpu_pgxp.h" @@ -227,47 +226,22 @@ bool GTE::DoState(StateWrapper& sw) return !sw.HasError(); } -void GTE::UpdateAspectRatio(u32 window_width, u32 window_height) +void GTE::SetAspectRatio(DisplayAspectRatio aspect, u32 custom_num, u32 custom_denom) { - if (!g_settings.gpu_widescreen_hack) - { - s_config.aspect_ratio = DisplayAspectRatio::R4_3; + s_config.aspect_ratio = aspect; + if (aspect != DisplayAspectRatio::Custom) return; - } - - s_config.aspect_ratio = g_settings.display_aspect_ratio; - - u32 num, denom; - switch (s_config.aspect_ratio) - { - case DisplayAspectRatio::MatchWindow: - { - num = window_width; - denom = window_height; - } - 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 x = 4u * custom_denom; + const u32 y = 3u * custom_num; const u32 gcd = std::gcd(x, y); s_config.custom_aspect_ratio_numerator = x / gcd; s_config.custom_aspect_ratio_denominator = y / gcd; s_config.custom_aspect_ratio_f = - static_cast((4.0 / 3.0) / (static_cast(num) / static_cast(denom))); + static_cast((4.0 / 3.0) / (static_cast(custom_num) / static_cast(custom_denom))); } u32 GTE::ReadRegister(u32 index) @@ -709,7 +683,6 @@ void GTE::RTPS(const s16 V[3], u8 shift, bool lm, bool last) break; case DisplayAspectRatio::Custom: - case DisplayAspectRatio::MatchWindow: Sx = ((((s64(result) * s64(REGS.IR1)) * s64(s_config.custom_aspect_ratio_numerator)) / s64(s_config.custom_aspect_ratio_denominator)) + s64(REGS.OFX)); @@ -764,7 +737,6 @@ void GTE::RTPS(const s16 V[3], u8 shift, bool lm, bool last) switch (s_config.aspect_ratio) { - case DisplayAspectRatio::MatchWindow: case DisplayAspectRatio::Custom: precise_x = precise_x * s_config.custom_aspect_ratio_f; break; diff --git a/src/core/gte.h b/src/core/gte.h index 444b39c10..eeb702251 100644 --- a/src/core/gte.h +++ b/src/core/gte.h @@ -6,12 +6,14 @@ class StateWrapper; +enum class DisplayAspectRatio : u8; + namespace GTE { void Initialize(); void Reset(); bool DoState(StateWrapper& sw); -void UpdateAspectRatio(u32 window_width, u32 window_height); +void SetAspectRatio(DisplayAspectRatio aspect, u32 custom_num, u32 custom_denom); // control registers are offset by +32 u32 ReadRegister(u32 index); diff --git a/src/core/host.cpp b/src/core/host.cpp index 1d26548be..22a23be10 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -465,13 +465,11 @@ void Host::UpdateDisplayWindow(bool fullscreen) return; } - const u32 new_width = g_gpu_device->GetMainSwapChain()->GetWidth(); - const u32 new_height = g_gpu_device->GetMainSwapChain()->GetHeight(); - const float f_width = static_cast(new_width); - const float f_height = static_cast(new_height); + const float f_width = static_cast(g_gpu_device->GetMainSwapChain()->GetWidth()); + const float f_height = static_cast(g_gpu_device->GetMainSwapChain()->GetHeight()); ImGuiManager::WindowResized(f_width, f_height); InputManager::SetDisplayWindowSize(f_width, f_height); - System::DisplayWindowResized(new_width, new_height); + System::DisplayWindowResized(); } void Host::ResizeDisplayWindow(s32 width, s32 height, float scale) @@ -489,13 +487,11 @@ void Host::ResizeDisplayWindow(s32 width, s32 height, float scale) return; } - const u32 new_width = g_gpu_device->GetMainSwapChain()->GetWidth(); - const u32 new_height = g_gpu_device->GetMainSwapChain()->GetHeight(); - const float f_width = static_cast(new_width); - const float f_height = static_cast(new_height); + const float f_width = static_cast(g_gpu_device->GetMainSwapChain()->GetWidth()); + const float f_height = static_cast(g_gpu_device->GetMainSwapChain()->GetHeight()); ImGuiManager::WindowResized(f_width, f_height); InputManager::SetDisplayWindowSize(f_width, f_height); - System::DisplayWindowResized(new_width, new_height); + System::DisplayWindowResized(); } void Host::ReleaseGPUDevice() diff --git a/src/core/settings.cpp b/src/core/settings.cpp index f3bce23b7..cbecae52e 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1614,10 +1614,11 @@ const char* Settings::GetDisplayDeinterlacingModeDisplayName(DisplayDeinterlacin "DisplayDeinterlacingMode"); } -static constexpr const std::array s_display_crop_mode_names = {"None", "Overscan", "Borders"}; +static constexpr const std::array s_display_crop_mode_names = {"None", "Overscan", "OverscanUncorrected", "Borders"}; static constexpr const std::array s_display_crop_mode_display_names = { TRANSLATE_DISAMBIG_NOOP("Settings", "None", "DisplayCropMode"), TRANSLATE_DISAMBIG_NOOP("Settings", "Only Overscan Area", "DisplayCropMode"), + TRANSLATE_DISAMBIG_NOOP("Settings", "Only Overscan Area (Aspect Uncorrected)", "DisplayCropMode"), TRANSLATE_DISAMBIG_NOOP("Settings", "All Borders", "DisplayCropMode"), }; @@ -1662,7 +1663,7 @@ static constexpr const std::array s_display_aspect_ratio_names = { "20:9", "PAR 1:1"}; static constexpr const std::array s_display_aspect_ratio_values = { - -1.0f, -1.0f, -1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 19.0f / 9.0f, 20.0f / 9.0f, -1.0f}; + 4.0f / 3.0f, 4.0f / 3.0f, 4.0f / 3.0f, 4.0f / 3.0f, 16.0f / 9.0f, 19.0f / 9.0f, 20.0f / 9.0f, -1.0f}; std::optional Settings::ParseDisplayAspectRatio(const char* str) { @@ -1691,28 +1692,7 @@ const char* Settings::GetDisplayAspectRatioDisplayName(DisplayAspectRatio ar) float Settings::GetDisplayAspectRatioValue() const { - switch (display_aspect_ratio) - { - case DisplayAspectRatio::MatchWindow: - { - if (!g_gpu_device || !g_gpu_device->HasMainSwapChain()) - return s_display_aspect_ratio_values[static_cast(DEFAULT_DISPLAY_ASPECT_RATIO)]; - - return static_cast(g_gpu_device->GetMainSwapChain()->GetWidth()) / - static_cast(g_gpu_device->GetMainSwapChain()->GetHeight()); - } - - 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)]; - } - } + return s_display_aspect_ratio_values[static_cast(display_aspect_ratio)]; } static constexpr const std::array s_display_alignment_names = {"LeftOrTop", "Center", "RightOrBottom"}; diff --git a/src/core/system.cpp b/src/core/system.cpp index 8ee6aa5e9..ccc70d709 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1941,9 +1941,6 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, fullscreen, error)) return false; - if (GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain()) - GTE::UpdateAspectRatio(swap_chain->GetWidth(), swap_chain->GetHeight()); - if (g_settings.gpu_pgxp_enable) CPU::PGXP::Initialize(); @@ -1965,6 +1962,7 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b s_state.cpu_thread_handle = Threading::ThreadHandle::GetForCallingThread(); + UpdateGTEAspectRatio(); UpdateThrottlePeriod(); UpdateMemorySaveStateSettings(); @@ -4402,8 +4400,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) (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))) { - if (GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain()) - GTE::UpdateAspectRatio(swap_chain->GetWidth(), swap_chain->GetHeight()); + UpdateGTEAspectRatio(); } if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || @@ -5651,8 +5648,7 @@ void System::ToggleWidescreen() Settings::GetDisplayAspectRatioDisplayName(g_settings.display_aspect_ratio), 5.0f)); } - if (GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain()) - GTE::UpdateAspectRatio(swap_chain->GetWidth(), swap_chain->GetHeight()); + UpdateGTEAspectRatio(); } void System::ToggleSoftwareRendering() @@ -5698,13 +5694,12 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/) Host::RequestResizeHostDisplay(static_cast(requested_width), static_cast(requested_height)); } -void System::DisplayWindowResized(u32 width, u32 height) +void System::DisplayWindowResized() { if (!IsValid()) return; - if (g_settings.gpu_widescreen_hack && g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow) - GTE::UpdateAspectRatio(width, height); + UpdateGTEAspectRatio(); g_gpu->RestoreDeviceContext(); g_gpu->UpdateResolutionScale(); @@ -5719,6 +5714,47 @@ void System::DisplayWindowResized(u32 width, u32 height) } } +void System::UpdateGTEAspectRatio() +{ + if (!IsValid()) + return; + + DisplayAspectRatio gte_ar = g_settings.display_aspect_ratio; + u32 custom_num = 0; + u32 custom_denom = 0; + if (!g_settings.gpu_widescreen_hack) + { + // No WS hack => no correction. + gte_ar = DisplayAspectRatio::R4_3; + } + else if (gte_ar == DisplayAspectRatio::Custom) + { + // Custom AR => use values. + custom_num = g_settings.display_aspect_ratio_custom_numerator; + custom_denom = g_settings.display_aspect_ratio_custom_denominator; + } + else if (gte_ar == DisplayAspectRatio::MatchWindow) + { + if (const GPUSwapChain* main_swap_chain = g_gpu_device->GetMainSwapChain()) + { + // Pre-apply the native aspect ratio correction to the window size. + // MatchWindow does not correct the display aspect ratio, so we need to apply it here. + const float correction = g_gpu->ComputeAspectRatioCorrection(); + custom_num = + static_cast(std::max(std::round(static_cast(main_swap_chain->GetWidth()) / correction), 1.0f)); + custom_denom = std::max(main_swap_chain->GetHeight(), 1u); + gte_ar = DisplayAspectRatio::Custom; + } + else + { + // Assume 4:3 until we get a window. + gte_ar = DisplayAspectRatio::R4_3; + } + } + + GTE::SetAspectRatio(gte_ar, custom_num, custom_denom); +} + bool System::PresentDisplay(bool explicit_present, u64 present_time) { // acquire for IO.MousePos. diff --git a/src/core/system_private.h b/src/core/system_private.h index e47baf4c0..e79882ff3 100644 --- a/src/core/system_private.h +++ b/src/core/system_private.h @@ -33,8 +33,11 @@ void FrameDone(); GPUVSyncMode GetEffectiveVSyncMode(); bool ShouldAllowPresentThrottle(); -/// Call when host display size changes, use with "match display" aspect ratio setting. -void DisplayWindowResized(u32 width, u32 height); +/// Call when host display size changes. +void DisplayWindowResized(); + +/// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting. +void UpdateGTEAspectRatio(); /// Performs mandatory hardware checks. bool PerformEarlyHardwareChecks(Error* error); diff --git a/src/core/types.h b/src/core/types.h index fae40cffa..4ebcc2dfb 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -141,8 +141,9 @@ enum class DisplayCropMode : u8 { None, Overscan, + OverscanUncorrected, Borders, - Count + MaxCount }; enum class DisplayAspectRatio : u8 diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index c0b52fd92..a3c540a50 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -671,7 +671,7 @@ void GraphicsSettingsWidget::setupAdditionalUi() QString::fromUtf8(Settings::GetDisplayDeinterlacingModeDisplayName(static_cast(i)))); } - for (u32 i = 0; i < static_cast(DisplayCropMode::Count); i++) + for (u32 i = 0; i < static_cast(DisplayCropMode::MaxCount); i++) { m_ui.displayCropMode->addItem( QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(static_cast(i)))); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index c0cd615ea..d01ed6eda 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2030,7 +2030,7 @@ void MainWindow::connectSignals() Settings::DEFAULT_GPU_RENDERER, GPURenderer::Count); SettingWidgetBinder::BindMenuToEnumSetting( m_ui.menuCropMode, "Display", "CropMode", &Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName, - &Settings::GetDisplayCropModeDisplayName, Settings::DEFAULT_DISPLAY_CROP_MODE, DisplayCropMode::Count); + &Settings::GetDisplayCropModeDisplayName, Settings::DEFAULT_DISPLAY_CROP_MODE, DisplayCropMode::MaxCount); SettingWidgetBinder::BindMenuToEnumSetting(m_ui.menuLogLevel, "Logging", "LogLevel", &Settings::ParseLogLevelName, &Settings::GetLogLevelName, &Settings::GetLogLevelDisplayName, Settings::DEFAULT_LOG_LEVEL, Log::Level::MaxCount);