From b8abf2df955fcba030e3c35e7924007a55ba2a25 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Fri, 11 Dec 2020 23:49:52 -0800 Subject: [PATCH 1/7] GPU: Don't apply display offsets when not cropping --- src/core/gpu.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 69af1826c..578d709ae 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -545,10 +545,10 @@ void GPU::UpdateCRTCDisplayParameters() switch (crop_mode) { case DisplayCropMode::None: - cs.horizontal_active_start = static_cast(std::max(0, 487 + g_settings.display_active_start_offset)); - cs.horizontal_active_end = static_cast(std::max(0, 3282 + g_settings.display_active_end_offset)); - cs.vertical_active_start = static_cast(std::max(0, 20 + g_settings.display_line_start_offset)); - cs.vertical_active_end = static_cast(std::max(0, 308 + g_settings.display_line_end_offset)); + cs.horizontal_active_start = 487; + cs.horizontal_active_end = 3282; + cs.vertical_active_start = 20; + cs.vertical_active_end = 308; break; case DisplayCropMode::Overscan: @@ -572,10 +572,10 @@ void GPU::UpdateCRTCDisplayParameters() switch (crop_mode) { case DisplayCropMode::None: - cs.horizontal_active_start = static_cast(std::max(0, 488 + g_settings.display_active_start_offset)); - cs.horizontal_active_end = static_cast(std::max(0, 3288 + g_settings.display_active_end_offset)); - cs.vertical_active_start = static_cast(std::max(0, 16 + g_settings.display_line_start_offset)); - cs.vertical_active_end = static_cast(std::max(0, 256 + g_settings.display_line_end_offset)); + cs.horizontal_active_start = 488; + cs.horizontal_active_end = 3288; + cs.vertical_active_start = 16; + cs.vertical_active_end = 256; break; case DisplayCropMode::Overscan: From 78e42d9b4cc23ded4d34b47d9f978509f3fc5473 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Fri, 11 Dec 2020 23:59:09 -0800 Subject: [PATCH 2/7] GPU: Clean up CRTC variable naming --- src/core/gpu.cpp | 105 +++++++++++++++++++++++++---------------------- src/core/gpu.h | 34 ++++++++++----- 2 files changed, 79 insertions(+), 60 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 578d709ae..0a1f3c99a 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -161,13 +161,13 @@ bool GPU::DoState(StateWrapper& sw, bool update_display) sw.Do(&m_crtc_state.display_vram_width); sw.Do(&m_crtc_state.display_vram_height); sw.Do(&m_crtc_state.horizontal_total); - sw.Do(&m_crtc_state.horizontal_active_start); - sw.Do(&m_crtc_state.horizontal_active_end); + sw.Do(&m_crtc_state.horizontal_visible_start); + sw.Do(&m_crtc_state.horizontal_visible_end); sw.Do(&m_crtc_state.horizontal_display_start); sw.Do(&m_crtc_state.horizontal_display_end); sw.Do(&m_crtc_state.vertical_total); - sw.Do(&m_crtc_state.vertical_active_start); - sw.Do(&m_crtc_state.vertical_active_end); + sw.Do(&m_crtc_state.vertical_visible_start); + sw.Do(&m_crtc_state.vertical_visible_end); sw.Do(&m_crtc_state.vertical_display_start); sw.Do(&m_crtc_state.vertical_display_end); sw.Do(&m_crtc_state.fractional_ticks); @@ -545,25 +545,27 @@ void GPU::UpdateCRTCDisplayParameters() switch (crop_mode) { case DisplayCropMode::None: - cs.horizontal_active_start = 487; - cs.horizontal_active_end = 3282; - cs.vertical_active_start = 20; - cs.vertical_active_end = 308; + cs.horizontal_visible_start = PAL_HORIZONTAL_ACTIVE_START; + cs.horizontal_visible_end = PAL_HORIZONTAL_ACTIVE_END; + cs.vertical_visible_start = PAL_VERTICAL_ACTIVE_START; + cs.vertical_visible_end = PAL_VERTICAL_ACTIVE_END; break; case DisplayCropMode::Overscan: - cs.horizontal_active_start = static_cast(std::max(0, 628 + g_settings.display_active_start_offset)); - cs.horizontal_active_end = static_cast(std::max(0, 3188 + g_settings.display_active_end_offset)); - cs.vertical_active_start = static_cast(std::max(0, 30 + g_settings.display_line_start_offset)); - cs.vertical_active_end = static_cast(std::max(0, 298 + g_settings.display_line_end_offset)); + 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)); + cs.vertical_visible_start = static_cast(std::max(0, 30 + g_settings.display_line_start_offset)); + cs.vertical_visible_end = + static_cast(std::max(cs.vertical_visible_start, 298 + g_settings.display_line_end_offset)); break; case DisplayCropMode::Borders: default: - cs.horizontal_active_start = horizontal_display_start; - cs.horizontal_active_end = horizontal_display_end; - cs.vertical_active_start = vertical_display_start; - cs.vertical_active_end = vertical_display_end; + cs.horizontal_visible_start = horizontal_display_start; + cs.horizontal_visible_end = horizontal_display_end; + cs.vertical_visible_start = vertical_display_start; + cs.vertical_visible_end = vertical_display_end; break; } } @@ -572,25 +574,27 @@ void GPU::UpdateCRTCDisplayParameters() switch (crop_mode) { case DisplayCropMode::None: - cs.horizontal_active_start = 488; - cs.horizontal_active_end = 3288; - cs.vertical_active_start = 16; - cs.vertical_active_end = 256; + cs.horizontal_visible_start = NTSC_HORIZONTAL_ACTIVE_START; + cs.horizontal_visible_end = NTSC_HORIZONTAL_ACTIVE_END; + cs.vertical_visible_start = NTSC_VERTICAL_ACTIVE_START; + cs.vertical_visible_end = NTSC_VERTICAL_ACTIVE_END; break; case DisplayCropMode::Overscan: - cs.horizontal_active_start = static_cast(std::max(0, 608 + g_settings.display_active_start_offset)); - cs.horizontal_active_end = static_cast(std::max(0, 3168 + g_settings.display_active_end_offset)); - cs.vertical_active_start = static_cast(std::max(0, 24 + g_settings.display_line_start_offset)); - cs.vertical_active_end = static_cast(std::max(0, 248 + g_settings.display_line_end_offset)); + 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)); + cs.vertical_visible_start = static_cast(std::max(0, 24 + g_settings.display_line_start_offset)); + cs.vertical_visible_end = + static_cast(std::max(cs.vertical_visible_start, 248 + g_settings.display_line_end_offset)); break; case DisplayCropMode::Borders: default: - cs.horizontal_active_start = horizontal_display_start; - cs.horizontal_active_end = horizontal_display_end; - cs.vertical_active_start = vertical_display_start; - cs.vertical_active_end = vertical_display_end; + cs.horizontal_visible_start = horizontal_display_start; + cs.horizontal_visible_end = horizontal_display_end; + cs.vertical_visible_start = vertical_display_start; + cs.vertical_visible_end = vertical_display_end; break; } } @@ -601,8 +605,8 @@ void GPU::UpdateCRTCDisplayParameters() const u8 height_shift = m_force_progressive_scan ? y_shift : BoolToUInt8(m_GPUSTAT.vertical_interlace); // Determine screen size. - cs.display_width = (cs.horizontal_active_end - cs.horizontal_active_start) / cs.dot_clock_divider; - cs.display_height = (cs.vertical_active_end - cs.vertical_active_start) << height_shift; + cs.display_width = (cs.horizontal_visible_end - cs.horizontal_visible_start) / cs.dot_clock_divider; + cs.display_height = (cs.vertical_visible_end - cs.vertical_visible_start) << height_shift; // Determine number of pixels outputted from VRAM (in general, round to 4-pixel multiple). // TODO: Verify behavior if values are outside of the active video portion of scanline. @@ -618,17 +622,17 @@ void GPU::UpdateCRTCDisplayParameters() // Determine if we need to adjust the VRAM rectangle (because the display is starting outside the visible area) or add // padding. u16 horizontal_skip_pixels; - if (horizontal_display_start >= cs.horizontal_active_start) + if (horizontal_display_start >= cs.horizontal_visible_start) { - cs.display_origin_left = (horizontal_display_start - cs.horizontal_active_start) / cs.dot_clock_divider; - cs.display_vram_left = std::min(m_crtc_state.regs.X, VRAM_WIDTH - 1); + cs.display_origin_left = (horizontal_display_start - cs.horizontal_visible_start) / cs.dot_clock_divider; + cs.display_vram_left = std::min(cs.regs.X, VRAM_WIDTH - 1); horizontal_skip_pixels = 0; } else { - horizontal_skip_pixels = (cs.horizontal_active_start - horizontal_display_start) / cs.dot_clock_divider; + horizontal_skip_pixels = (cs.horizontal_visible_start - horizontal_display_start) / cs.dot_clock_divider; cs.display_origin_left = 0; - cs.display_vram_left = std::min(m_crtc_state.regs.X + horizontal_skip_pixels, VRAM_WIDTH - 1); + cs.display_vram_left = std::min(cs.regs.X + horizontal_skip_pixels, VRAM_WIDTH - 1); } // apply the crop from the start (usually overscan) @@ -637,28 +641,29 @@ void GPU::UpdateCRTCDisplayParameters() // Apply crop from the end by shrinking VRAM rectangle width if display would end outside the visible area. cs.display_vram_width = std::min(cs.display_vram_width, cs.display_width - cs.display_origin_left); - if (vertical_display_start >= cs.vertical_active_start) + if (vertical_display_start >= cs.vertical_visible_start) { - cs.display_origin_top = (vertical_display_start - cs.vertical_active_start) << y_shift; - cs.display_vram_top = m_crtc_state.regs.Y; + cs.display_origin_top = (vertical_display_start - cs.vertical_visible_start) << y_shift; + cs.display_vram_top = cs.regs.Y; } else { cs.display_origin_top = 0; - cs.display_vram_top = m_crtc_state.regs.Y + ((cs.vertical_active_start - vertical_display_start) << y_shift); + cs.display_vram_top = cs.regs.Y + ((cs.vertical_visible_start - vertical_display_start) << y_shift); } - if (vertical_display_end <= cs.vertical_active_end) + if (vertical_display_end <= cs.vertical_visible_end) { - cs.display_vram_height = (vertical_display_end - std::min(vertical_display_end, std::max(vertical_display_start, - cs.vertical_active_start))) - << height_shift; + cs.display_vram_height = + (vertical_display_end - + std::min(vertical_display_end, std::max(vertical_display_start, cs.vertical_visible_start))) + << height_shift; } else { cs.display_vram_height = - (cs.vertical_active_end - - std::min(cs.vertical_active_end, std::max(vertical_display_start, cs.vertical_active_start))) + (cs.vertical_visible_end - + std::min(cs.vertical_visible_end, std::max(vertical_display_start, cs.vertical_visible_start))) << height_shift; } } @@ -895,8 +900,8 @@ bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y } *out_line = - (static_cast(display_y) >> BoolToUInt8(m_GPUSTAT.vertical_interlace)) + m_crtc_state.vertical_active_start; - *out_tick = (static_cast(display_x) * m_crtc_state.dot_clock_divider) + m_crtc_state.horizontal_active_start; + (static_cast(display_y) >> BoolToUInt8(m_GPUSTAT.vertical_interlace)) + m_crtc_state.vertical_visible_start; + *out_tick = (static_cast(display_x) * m_crtc_state.dot_clock_divider) + m_crtc_state.horizontal_visible_start; return true; } @@ -1539,9 +1544,9 @@ void GPU::DrawDebugStateWindow() cs.horizontal_display_start / cs.dot_clock_divider, cs.horizontal_display_end / cs.dot_clock_divider, cs.vertical_display_start, cs.vertical_display_end); ImGui::Text("Cropping: %s", Settings::GetDisplayCropModeName(g_settings.display_crop_mode)); - ImGui::Text("Visible Display Range: %u-%u (%u-%u), %u-%u", cs.horizontal_active_start, cs.horizontal_active_end, - cs.horizontal_active_start / cs.dot_clock_divider, cs.horizontal_active_end / cs.dot_clock_divider, - cs.vertical_active_start, cs.vertical_active_end); + ImGui::Text("Visible Display Range: %u-%u (%u-%u), %u-%u", cs.horizontal_visible_start, cs.horizontal_visible_end, + cs.horizontal_visible_start / cs.dot_clock_divider, cs.horizontal_visible_end / cs.dot_clock_divider, + cs.vertical_visible_start, cs.vertical_visible_end); ImGui::Text("Display Resolution: %ux%u", cs.display_width, cs.display_height); ImGui::Text("Display Origin: %u, %u", cs.display_origin_left, cs.display_origin_top); ImGui::Text("Displayed/Visible VRAM Portion: %ux%u @ (%u, %u)", cs.display_vram_width, cs.display_vram_height, diff --git a/src/core/gpu.h b/src/core/gpu.h index bc2dd43a5..1b6ba782b 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -56,6 +56,18 @@ public: PAL_TOTAL_LINES = 314, }; + enum : u16 + { + NTSC_HORIZONTAL_ACTIVE_START = 488, + NTSC_HORIZONTAL_ACTIVE_END = 3288, + NTSC_VERTICAL_ACTIVE_START = 16, + NTSC_VERTICAL_ACTIVE_END = 256, + PAL_HORIZONTAL_ACTIVE_START = 487, + PAL_HORIZONTAL_ACTIVE_END = 3282, + PAL_VERTICAL_ACTIVE_START = 20, + PAL_VERTICAL_ACTIVE_END = 308, + }; + // Base class constructor. GPU(); virtual ~GPU(); @@ -89,7 +101,7 @@ public: } void EndDMAWrite(); - /// Returns false if the DAC is loading any data from VRAM. + /// Returns true if no data is being sent from VRAM to the DAC or that no portion of VRAM would be visible on screen. ALWAYS_INLINE bool IsDisplayDisabled() const { return m_GPUSTAT.display_disable || m_crtc_state.display_vram_width == 0 || m_crtc_state.display_vram_height == 0; @@ -475,28 +487,30 @@ protected: u16 display_width; u16 display_height; - // Top-left corner where the VRAM is displayed. Depending on the CRTC config, this may indicate padding. + // Top-left corner in screen coordinates where the outputted portion of VRAM is first visible. u16 display_origin_left; u16 display_origin_top; - // Rectangle describing the displayed area of VRAM, in coordinates. + // Rectangle in VRAM coordinates describing the area of VRAM that is visible on screen. u16 display_vram_left; u16 display_vram_top; u16 display_vram_width; u16 display_vram_height; - u16 horizontal_total; - u16 horizontal_sync_start; // <- not currently saved to state, so we don't have to bump the version - u16 horizontal_active_start; - u16 horizontal_active_end; + u16 horizontal_visible_start; + u16 horizontal_visible_end; + u16 vertical_visible_start; + u16 vertical_visible_end; + u16 horizontal_display_start; u16 horizontal_display_end; - u16 vertical_total; - u16 vertical_active_start; - u16 vertical_active_end; u16 vertical_display_start; u16 vertical_display_end; + u16 horizontal_total; + u16 horizontal_sync_start; // <- not currently saved to state, so we don't have to bump the version + u16 vertical_total; + TickCount fractional_ticks; TickCount current_tick_in_scanline; u32 current_scanline; From 866cbdca4b907a1a8588ef4749e63d4d6761050b Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Sat, 12 Dec 2020 00:02:24 -0800 Subject: [PATCH 3/7] GPU: Prevent potential overflow in CRTC visible area calculation --- src/core/gpu.cpp | 16 ++++++++++++++++ src/core/gpu.h | 1 + 2 files changed, 17 insertions(+) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 0a1f3c99a..00f5e8e52 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -568,6 +568,14 @@ void GPU::UpdateCRTCDisplayParameters() cs.vertical_visible_end = vertical_display_end; break; } + cs.horizontal_visible_start = + std::clamp(cs.horizontal_visible_start, PAL_HORIZONTAL_ACTIVE_START, PAL_HORIZONTAL_ACTIVE_END); + cs.horizontal_visible_end = + std::clamp(cs.horizontal_visible_end, cs.horizontal_visible_start, PAL_HORIZONTAL_ACTIVE_END); + cs.vertical_visible_start = + std::clamp(cs.vertical_visible_start, PAL_VERTICAL_ACTIVE_START, PAL_VERTICAL_ACTIVE_END); + cs.vertical_visible_end = + std::clamp(cs.vertical_visible_end, cs.vertical_visible_start, PAL_VERTICAL_ACTIVE_END); } else { @@ -597,6 +605,14 @@ void GPU::UpdateCRTCDisplayParameters() cs.vertical_visible_end = vertical_display_end; break; } + cs.horizontal_visible_start = + std::clamp(cs.horizontal_visible_start, NTSC_HORIZONTAL_ACTIVE_START, NTSC_HORIZONTAL_ACTIVE_END); + cs.horizontal_visible_end = + std::clamp(cs.horizontal_visible_end, cs.horizontal_visible_start, NTSC_HORIZONTAL_ACTIVE_END); + cs.vertical_visible_start = + std::clamp(cs.vertical_visible_start, NTSC_VERTICAL_ACTIVE_START, NTSC_VERTICAL_ACTIVE_END); + cs.vertical_visible_end = + std::clamp(cs.vertical_visible_end, cs.vertical_visible_start, NTSC_VERTICAL_ACTIVE_END); } // If force-progressive is enabled, we only double the height in 480i mode. This way non-interleaved 480i framebuffers diff --git a/src/core/gpu.h b/src/core/gpu.h index 1b6ba782b..47242a74f 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -497,6 +497,7 @@ protected: u16 display_vram_width; u16 display_vram_height; + // Visible range of the screen, in GPU ticks/lines. Clamped to lie within the active video region. u16 horizontal_visible_start; u16 horizontal_visible_end; u16 vertical_visible_start; From 1c8a896a550a40533a68529fafd109683184fcc6 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Mon, 14 Dec 2020 17:30:02 -0800 Subject: [PATCH 4/7] GPU: Wrap display VRAM coordinates properly --- src/core/gpu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 00f5e8e52..af6028218 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -641,14 +641,14 @@ void GPU::UpdateCRTCDisplayParameters() if (horizontal_display_start >= cs.horizontal_visible_start) { cs.display_origin_left = (horizontal_display_start - cs.horizontal_visible_start) / cs.dot_clock_divider; - cs.display_vram_left = std::min(cs.regs.X, VRAM_WIDTH - 1); + cs.display_vram_left = cs.regs.X; horizontal_skip_pixels = 0; } else { horizontal_skip_pixels = (cs.horizontal_visible_start - horizontal_display_start) / cs.dot_clock_divider; cs.display_origin_left = 0; - cs.display_vram_left = std::min(cs.regs.X + horizontal_skip_pixels, VRAM_WIDTH - 1); + cs.display_vram_left = (cs.regs.X + horizontal_skip_pixels) % VRAM_WIDTH; } // apply the crop from the start (usually overscan) @@ -665,7 +665,7 @@ void GPU::UpdateCRTCDisplayParameters() else { cs.display_origin_top = 0; - cs.display_vram_top = cs.regs.Y + ((cs.vertical_visible_start - vertical_display_start) << y_shift); + cs.display_vram_top = (cs.regs.Y + ((cs.vertical_visible_start - vertical_display_start) << y_shift)) % VRAM_HEIGHT; } if (vertical_display_end <= cs.vertical_visible_end) From 2c76ad204a45c8858942c8561375dfc403032552 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Mon, 14 Dec 2020 17:35:01 -0800 Subject: [PATCH 5/7] GPU: Fix operator warning --- src/core/gpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/gpu.h b/src/core/gpu.h index 47242a74f..13024a26b 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -110,13 +110,13 @@ public: /// Returns true if scanout should be interlaced. ALWAYS_INLINE bool IsInterlacedDisplayEnabled() const { - return (!m_force_progressive_scan) & m_GPUSTAT.vertical_interlace; + return (!m_force_progressive_scan) && m_GPUSTAT.vertical_interlace; } /// Returns true if interlaced rendering is enabled and force progressive scan is disabled. ALWAYS_INLINE bool IsInterlacedRenderingEnabled() const { - return (!m_force_progressive_scan) & m_GPUSTAT.SkipDrawingToActiveField(); + return (!m_force_progressive_scan) && m_GPUSTAT.SkipDrawingToActiveField(); } /// Returns the number of pending GPU ticks. From b4fb1e20d8d4b2e5170e58dce2f6d74ce06e4314 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Sun, 13 Dec 2020 23:46:06 -0800 Subject: [PATCH 6/7] Settings: Remove unused variables --- src/core/settings.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/settings.h b/src/core/settings.h index 23bc41716..8547fb447 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -116,10 +116,6 @@ struct Settings s16 display_active_end_offset = 0; s8 display_line_start_offset = 0; s8 display_line_end_offset = 0; - s8 display_crop_left = 0; - s8 display_crop_right = 0; - s8 display_crop_top = 0; - s8 display_crop_bottom = 0; bool display_force_4_3_for_24bit = false; bool gpu_24bit_chroma_smoothing = false; bool display_linear_filtering = true; From de8f03bd755f693f91c6ca4da43a7100c2043187 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Fri, 11 Dec 2020 21:37:53 -0800 Subject: [PATCH 7/7] GPU: Add auto (game native) aspect ratio --- src/core/gpu.cpp | 33 +++++++++++++++++++ src/core/gte.cpp | 2 ++ src/core/host_display.cpp | 1 - src/core/settings.cpp | 9 ++--- src/core/settings.h | 4 +-- src/core/types.h | 1 + .../libretro_host_interface.cpp | 5 +-- src/duckstation-qt/displaysettingswidget.cpp | 6 ++-- 8 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index af6028218..4804e3e3e 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -7,6 +7,7 @@ #include "host_display.h" #include "host_interface.h" #include "interrupt_controller.h" +#include "settings.h" #include "stb_image_write.h" #include "system.h" #include "timers.h" @@ -456,9 +457,41 @@ float GPU::ComputeVerticalFrequency() const float GPU::GetDisplayAspectRatio() const { if (g_settings.display_force_4_3_for_24bit && m_GPUSTAT.display_area_color_depth_24) + { return 4.0f / 3.0f; + } + else if (g_settings.display_aspect_ratio == DisplayAspectRatio::Auto) + { + 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) + { + 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 + { + 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) * (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); + } else + { return Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio); + } } void GPU::UpdateCRTCConfig() diff --git a/src/core/gte.cpp b/src/core/gte.cpp index 3b81c1809..d4e04e5b3 100644 --- a/src/core/gte.cpp +++ b/src/core/gte.cpp @@ -655,6 +655,7 @@ static void RTPS(const s16 V[3], u8 shift, bool lm, bool last) 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: @@ -747,6 +748,7 @@ static void RTPS(const s16 V[3], u8 shift, bool lm, bool last) precise_x = (precise_x * 7.0f) / 6.0f; break; + case DisplayAspectRatio::Auto: case DisplayAspectRatio::R4_3: case DisplayAspectRatio::PAR1_1: default: diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index c39aa745f..c285b1525 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -138,7 +138,6 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32* ou s32* out_height, s32* out_left_padding, s32* out_top_padding, float* out_scale, float* out_y_scale, bool apply_aspect_ratio) const { - apply_aspect_ratio = (m_display_aspect_ratio > 0) ? apply_aspect_ratio : false; const float y_scale = apply_aspect_ratio ? ((static_cast(m_display_width) / static_cast(m_display_height)) / m_display_aspect_ratio) : diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d6c0e795c..63c802b70 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -625,10 +625,11 @@ 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 = { - {"4:3", "16:9", "16:10", "19:9", "21: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 = { - {4.0f / 3.0f, 16.0f / 9.0f, 16.0f / 10.0f, 19.0f / 9.0f, 21.0f / 9.0f, 8.0f / 7.0f, 5.0f / 4.0f, 3.0f / 2.0f, +static std::array s_display_aspect_ratio_names = {{"Auto (Game Native)", "4:3", "16:9", "16:10", + "19:9", "21: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, 21.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) diff --git a/src/core/settings.h b/src/core/settings.h index 8547fb447..0dc68e7fd 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -111,7 +111,7 @@ struct Settings bool gpu_pgxp_cpu = false; bool gpu_pgxp_preserve_proj_fp = false; DisplayCropMode display_crop_mode = DisplayCropMode::None; - DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3; + DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::Auto; s16 display_active_start_offset = 0; s16 display_active_end_offset = 0; s8 display_line_start_offset = 0; @@ -300,7 +300,7 @@ struct Settings #endif static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan; - static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::R4_3; + static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto; static constexpr ControllerType DEFAULT_CONTROLLER_1_TYPE = ControllerType::DigitalController; static constexpr ControllerType DEFAULT_CONTROLLER_2_TYPE = ControllerType::None; static constexpr MemoryCardType DEFAULT_MEMORY_CARD_1_TYPE = MemoryCardType::PerGameTitle; diff --git a/src/core/types.h b/src/core/types.h index d3fca8b69..84653dfbe 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -85,6 +85,7 @@ enum class DisplayCropMode : u8 enum class DisplayAspectRatio : u8 { + Auto, R4_3, R16_9, R16_10, diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index e6f850885..5068f9191 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -704,7 +704,8 @@ static std::array s_option_definitions = {{ {"duckstation_Display.AspectRatio", "Aspect Ratio", "Sets the core-provided aspect ratio.", - {{"4:3", "4:3"}, + {{"Auto", "Auto (Game Native)"}, + {"4:3", "4:3"}, {"16:9", "16:9"}, {"16:10", "16:10"}, {"19:9", "19:9"}, @@ -715,7 +716,7 @@ static std::array s_option_definitions = {{ {"2:1 (VRAM 1:1)", "2:1 (VRAM 1:1)"}, {"1:1", "1:1"}, {"PAR 1:1", "PAR 1:1"}}, - "4:3"}, + "Auto"}, {"duckstation_Main.LoadDevicesFromSaveStates", "Load Devices From Save States", "Sets whether the contents of devices and memory cards will be loaded when a save state is loaded.", diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp index 99c0009de..ef53cee73 100644 --- a/src/duckstation-qt/displaysettingswidget.cpp +++ b/src/duckstation-qt/displaysettingswidget.cpp @@ -62,9 +62,9 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW "renderers.
This option is only supported in Direct3D and Vulkan. OpenGL will always use the default " "device.")); dialog->registerWidgetHelp( - m_ui.displayAspectRatio, tr("Aspect Ratio"), QStringLiteral("4:3"), - tr("Changes the aspect ratio used to display the console's output to the screen. The default " - "is 4:3 which matches a typical TV of the era.")); + m_ui.displayAspectRatio, tr("Aspect Ratio"), QStringLiteral("Auto (Game Native)"), + tr("Changes the aspect ratio used to display the console's output to the screen. The default is Auto (Game Native) " + "which automatically adjusts the aspect ratio to match how a game would be shown on a typical TV of the era.")); dialog->registerWidgetHelp( m_ui.displayCropMode, tr("Crop Mode"), tr("Only Overscan Area"), tr("Determines how much of the area typically not visible on a consumer TV set to crop/hide.
"