From b3aa6ad93b16e026601f2138396db256eadf9fc9 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Wed, 13 Dec 2023 00:27:01 +0200 Subject: [PATCH] Video: implement custom aspect ratio support (already exposed to Qt). This also renamed some variables/functions. --- Source/Core/Core/Config/GraphicsSettings.cpp | 4 ++ Source/Core/Core/Config/GraphicsSettings.h | 2 + .../Config/Graphics/AdvancedWidget.cpp | 5 +- .../Config/Graphics/GeneralWidget.cpp | 64 +++++++++++++++---- .../DolphinQt/Config/Graphics/GeneralWidget.h | 5 ++ Source/Core/DolphinQt/HotkeyScheduler.cpp | 7 +- Source/Core/VideoCommon/Present.cpp | 57 ++++++++++++----- Source/Core/VideoCommon/VideoConfig.cpp | 2 + Source/Core/VideoCommon/VideoConfig.h | 11 +++- Source/Core/VideoCommon/Widescreen.cpp | 15 +++-- 10 files changed, 129 insertions(+), 43 deletions(-) diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 37466e63cb..c82e11dddb 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -21,6 +21,10 @@ const Info GFX_ADAPTER{{System::GFX, "Hardware", "Adapter"}, 0}; const Info GFX_WIDESCREEN_HACK{{System::GFX, "Settings", "wideScreenHack"}, false}; const Info GFX_ASPECT_RATIO{{System::GFX, "Settings", "AspectRatio"}, AspectMode::Auto}; +const Info GFX_CUSTOM_ASPECT_RATIO_WIDTH{{System::GFX, "Settings", "CustomAspectRatioWidth"}, + 1}; +const Info GFX_CUSTOM_ASPECT_RATIO_HEIGHT{{System::GFX, "Settings", "CustomAspectRatioHeight"}, + 1}; const Info GFX_SUGGESTED_ASPECT_RATIO{{System::GFX, "Settings", "SuggestedAspectRatio"}, AspectMode::Auto}; const Info GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD{ diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 33f08daf27..d641ce4aaa 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -28,6 +28,8 @@ extern const Info GFX_ADAPTER; extern const Info GFX_WIDESCREEN_HACK; extern const Info GFX_ASPECT_RATIO; +extern const Info GFX_CUSTOM_ASPECT_RATIO_WIDTH; +extern const Info GFX_CUSTOM_ASPECT_RATIO_HEIGHT; extern const Info GFX_SUGGESTED_ASPECT_RATIO; extern const Info GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD; extern const Info GFX_WIDESCREEN_HEURISTIC_ASPECT_RATIO_SLOP; diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp index afdedc3ea0..9f86ea5ed1 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp @@ -359,8 +359,9 @@ void AdvancedWidget::AddDescriptions() "level 9 but finish in significantly less time.

" "If unsure, leave this at 6."); static const char TR_CROPPING_DESCRIPTION[] = QT_TR_NOOP( - "Crops the picture from its native aspect ratio to 4:3 or " - "16:9.

If unsure, leave this unchecked."); + "Crops the picture from its native aspect ratio (which rarely exactly matches 4:3 or 16:9)," + " to the specific user target aspect ratio (e.g. 4:3 or 16:9).

" + "If unsure, leave this unchecked."); static const char TR_PROGRESSIVE_SCAN_DESCRIPTION[] = QT_TR_NOOP( "Enables progressive scan if supported by the emulated software. Most games don't have " "any issue with this.

If unsure, leave this " diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp index 6ea5f82c0c..aa4a7c5563 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp @@ -20,6 +20,7 @@ #include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h" +#include "DolphinQt/Config/ConfigControls/ConfigInteger.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" @@ -54,9 +55,20 @@ void GeneralWidget::CreateWidgets() m_video_layout = new QGridLayout(); m_backend_combo = new ToolTipComboBox(); - m_aspect_combo = - new ConfigChoice({tr("Auto"), tr("Force 16:9"), tr("Force 4:3"), tr("Stretch to Window")}, - Config::GFX_ASPECT_RATIO); + m_aspect_combo = new ConfigChoice( + {tr("Auto"), tr("Force 16:9"), tr("Force 4:3"), tr("Stretch to Window"), tr("Custom")}, + Config::GFX_ASPECT_RATIO); + m_custom_aspect_label = new QLabel(tr("Custom Aspect Ratio:")); + m_custom_aspect_label->setHidden(true); + constexpr int MAX_CUSTOM_ASPECT_RATIO_RESOLUTION = 10000; + m_custom_aspect_width = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, + Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH); + m_custom_aspect_width->setEnabled(false); + m_custom_aspect_width->setHidden(true); + m_custom_aspect_height = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, + Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT); + m_custom_aspect_height->setEnabled(false); + m_custom_aspect_height->setHidden(true); m_adapter_combo = new ToolTipComboBox; m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC); m_enable_fullscreen = new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN); @@ -70,16 +82,20 @@ void GeneralWidget::CreateWidgets() } m_video_layout->addWidget(new QLabel(tr("Backend:")), 0, 0); - m_video_layout->addWidget(m_backend_combo, 0, 1); + m_video_layout->addWidget(m_backend_combo, 0, 1, 1, -1); m_video_layout->addWidget(new QLabel(tr("Adapter:")), 1, 0); - m_video_layout->addWidget(m_adapter_combo, 1, 1); + m_video_layout->addWidget(m_adapter_combo, 1, 1, 1, -1); m_video_layout->addWidget(new QLabel(tr("Aspect Ratio:")), 3, 0); - m_video_layout->addWidget(m_aspect_combo, 3, 1); + m_video_layout->addWidget(m_aspect_combo, 3, 1, 1, -1); - m_video_layout->addWidget(m_enable_vsync, 4, 0); - m_video_layout->addWidget(m_enable_fullscreen, 4, 1); + m_video_layout->addWidget(m_custom_aspect_label, 4, 0); + m_video_layout->addWidget(m_custom_aspect_width, 4, 1); + m_video_layout->addWidget(m_custom_aspect_height, 4, 2); + + m_video_layout->addWidget(m_enable_vsync, 5, 0); + m_video_layout->addWidget(m_enable_fullscreen, 5, 1, 1, -1); // Other auto* m_options_box = new QGroupBox(tr("Other")); @@ -138,6 +154,14 @@ void GeneralWidget::ConnectWidgets() Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index); emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); }); + connect(m_aspect_combo, qOverload(&QComboBox::currentIndexChanged), this, [&](int index) { + const bool is_custom_aspect_ratio = (index == static_cast(AspectMode::Custom)); + m_custom_aspect_width->setEnabled(is_custom_aspect_ratio); + m_custom_aspect_height->setEnabled(is_custom_aspect_ratio); + m_custom_aspect_label->setHidden(!is_custom_aspect_ratio); + m_custom_aspect_width->setHidden(!is_custom_aspect_ratio); + m_custom_aspect_height->setHidden(!is_custom_aspect_ratio); + }); } void GeneralWidget::LoadSettings() @@ -145,6 +169,13 @@ void GeneralWidget::LoadSettings() // Video Backend m_backend_combo->setCurrentIndex(m_backend_combo->findData( QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))))); + + const bool is_custom_aspect_ratio = (Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::Custom); + m_custom_aspect_width->setEnabled(is_custom_aspect_ratio); + m_custom_aspect_height->setEnabled(is_custom_aspect_ratio); + m_custom_aspect_label->setHidden(!is_custom_aspect_ratio); + m_custom_aspect_width->setHidden(!is_custom_aspect_ratio); + m_custom_aspect_height->setHidden(!is_custom_aspect_ratio); } void GeneralWidget::SaveSettings() @@ -207,11 +238,15 @@ void GeneralWidget::AddDescriptions() QT_TR_NOOP("Uses the main Dolphin window for rendering rather than " "a separate render window.

If unsure, leave " "this unchecked."); - static const char TR_ASPECT_RATIO_DESCRIPTION[] = QT_TR_NOOP( - "Selects which aspect ratio to use when rendering.

Auto: Uses the native aspect " - "ratio
Force 16:9: Mimics an analog TV with a widescreen aspect ratio.
Force 4:3: " - "Mimics a standard 4:3 analog TV.
Stretch to Window: Stretches the picture to the " - "window size.

If unsure, select Auto."); + static const char TR_ASPECT_RATIO_DESCRIPTION[] = + QT_TR_NOOP("Selects which aspect ratio to use when rendering.
" + "Each game can have a slightly different native aspect ratio." + "

Auto: Uses the native aspect ratio" + "
Force 16:9: Mimics an analog TV with a widescreen aspect ratio." + "
Force 4:3: Mimics a standard 4:3 analog TV." + "
Stretch to Window: Stretches the picture to the window size." + "
Custom: For games running with specific custom aspect ratio cheats.

" + "If unsure, select Auto."); static const char TR_VSYNC_DESCRIPTION[] = QT_TR_NOOP( "Waits for vertical blanks in order to prevent tearing.

Decreases performance " "if emulation speed is below 100%.

If unsure, leave " @@ -260,6 +295,9 @@ void GeneralWidget::AddDescriptions() m_aspect_combo->SetTitle(tr("Aspect Ratio")); m_aspect_combo->SetDescription(tr(TR_ASPECT_RATIO_DESCRIPTION)); + m_custom_aspect_width->SetTitle(tr("Custom Aspect Ratio Width")); + m_custom_aspect_height->SetTitle(tr("Custom Aspect Ratio Height")); + m_enable_vsync->SetDescription(tr(TR_VSYNC_DESCRIPTION)); m_enable_fullscreen->SetDescription(tr(TR_FULLSCREEN_DESCRIPTION)); diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h index 346eec9013..4f908bfa30 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h @@ -9,10 +9,12 @@ class ConfigBool; class ConfigChoice; +class ConfigInteger; class ConfigRadioInt; class GraphicsWindow; class QCheckBox; class QComboBox; +class QLabel; class QRadioButton; class QGridLayout; class ToolTipComboBox; @@ -41,6 +43,9 @@ private: ToolTipComboBox* m_backend_combo; ToolTipComboBox* m_adapter_combo; ConfigChoice* m_aspect_combo; + QLabel* m_custom_aspect_label; + ConfigInteger* m_custom_aspect_width; + ConfigInteger* m_custom_aspect_height; ConfigBool* m_enable_vsync; ConfigBool* m_enable_fullscreen; diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index a21e177377..9e3f5e4a16 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -402,12 +402,15 @@ void HotkeyScheduler::Run() case AspectMode::Stretch: OSD::AddMessage("Stretch"); break; - case AspectMode::Analog: + case AspectMode::ForceStandard: OSD::AddMessage("Force 4:3"); break; - case AspectMode::AnalogWide: + case AspectMode::ForceWide: OSD::AddMessage("Force 16:9"); break; + case AspectMode::Custom: + OSD::AddMessage("Custom"); + break; case AspectMode::Auto: default: OSD::AddMessage("Auto"); diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 5e9d391c95..e51d52107c 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -28,9 +28,10 @@ static constexpr int VIDEO_ENCODER_LCM = 4; namespace VideoCommon { -static float AspectToWidescreen(float aspect) +// Stretches the native/internal analog resolution aspect ratio from ~4:3 to ~16:9 +static float SourceAspectRatioToWidescreen(float source_aspect) { - return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); + return source_aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); } static std::tuple FindClosestIntegerResolution(float width, float height, @@ -292,15 +293,22 @@ float Presenter::CalculateDrawAspectRatio(bool allow_stretch) const return (static_cast(m_backbuffer_width) / static_cast(m_backbuffer_height)); auto& vi = Core::System::GetInstance().GetVideoInterface(); - const float aspect_ratio = vi.GetAspectRatio(); + const float source_aspect_ratio = vi.GetAspectRatio(); - if (aspect_mode == AspectMode::AnalogWide || + // This will scale up the source ~4:3 resolution to its equivalent ~16:9 resolution + if (aspect_mode == AspectMode::ForceWide || (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) { - return AspectToWidescreen(aspect_ratio); + return SourceAspectRatioToWidescreen(source_aspect_ratio); + } + // For the "custom" mode, we force the exact target aspect ratio, without + // acknowleding the difference between the source aspect ratio and 4:3. + else if (aspect_mode == AspectMode::Custom) + { + return g_ActiveConfig.GetCustomAspectRatio(); } - return aspect_ratio; + return source_aspect_ratio; } void Presenter::AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, @@ -414,7 +422,7 @@ void Presenter::SetSuggestedWindowSize(int width, int height) Host_RequestRenderWindowSize(out_width, out_height); } -// Crop to exactly 16:9 or 4:3 if enabled and not AspectMode::Stretch. +// Crop to exact forced aspect ratios if enabled and not AspectMode::Stretch. std::tuple Presenter::ApplyStandardAspectCrop(float width, float height, bool allow_stretch) const { @@ -426,13 +434,28 @@ std::tuple Presenter::ApplyStandardAspectCrop(float width, float h if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch) return {width, height}; - // Force 4:3 or 16:9 by cropping the image. + // Force aspect ratios by cropping the image. const float current_aspect = width / height; - const float expected_aspect = - (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) ? - (16.0f / 9.0f) : - (4.0f / 3.0f); + float expected_aspect; + switch (aspect_mode) + { + default: + case AspectMode::Auto: + expected_aspect = g_widescreen->IsGameWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f); + break; + case AspectMode::ForceWide: + expected_aspect = 16.0f / 9.0f; + break; + case AspectMode::ForceStandard: + expected_aspect = 4.0f / 3.0f; + break; + // There should be no cropping needed in the custom case, + // as output should always exactly match the target aspect ratio + case AspectMode::Custom: + expected_aspect = g_ActiveConfig.GetCustomAspectRatio(); + break; + } + if (current_aspect > expected_aspect) { // keep height, crop width @@ -457,11 +480,13 @@ void Presenter::UpdateDrawRectangle() if (g_ActiveConfig.bWidescreenHack) { auto& vi = Core::System::GetInstance().GetVideoInterface(); - float source_aspect = vi.GetAspectRatio(); + float source_aspect_ratio = vi.GetAspectRatio(); + // If the game is meant to be in widescreen (or forced to), + // scale the source aspect ratio to it. if (g_widescreen->IsGameWidescreen()) - source_aspect = AspectToWidescreen(source_aspect); + source_aspect_ratio = SourceAspectRatioToWidescreen(source_aspect_ratio); - const float adjust = source_aspect / draw_aspect_ratio; + const float adjust = source_aspect_ratio / draw_aspect_ratio; if (adjust > 1) { // Vert+ diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 426d8f7c8b..975b5c0f8d 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -85,6 +85,8 @@ void VideoConfig::Refresh() bWidescreenHack = Config::Get(Config::GFX_WIDESCREEN_HACK); aspect_mode = Config::Get(Config::GFX_ASPECT_RATIO); + custom_aspect_width = Config::Get(Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH); + custom_aspect_height = Config::Get(Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT); suggested_aspect_mode = Config::Get(Config::GFX_SUGGESTED_ASPECT_RATIO); widescreen_heuristic_transition_threshold = Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 0cff6e7c66..31c10059d5 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -21,10 +21,11 @@ constexpr int EFB_SCALE_AUTO_INTEGRAL = 0; enum class AspectMode : int { - Auto, - AnalogWide, - Analog, + Auto, // 4:3 or 16:9 + ForceWide, // 16:9 + ForceStandard, // 4:3 Stretch, + Custom, }; enum class StereoMode : int @@ -105,6 +106,8 @@ struct VideoConfig final bool bVSyncActive = false; bool bWidescreenHack = false; AspectMode aspect_mode{}; + int custom_aspect_width = 1; + int custom_aspect_height = 1; AspectMode suggested_aspect_mode{}; u32 widescreen_heuristic_transition_threshold = 0; float widescreen_heuristic_aspect_ratio_slop = 0.f; @@ -365,6 +368,8 @@ struct VideoConfig final bool UsingUberShaders() const; u32 GetShaderCompilerThreads() const; u32 GetShaderPrecompilerThreads() const; + + float GetCustomAspectRatio() const { return (float)custom_aspect_width / custom_aspect_height; } }; extern VideoConfig g_Config; diff --git a/Source/Core/VideoCommon/Widescreen.cpp b/Source/Core/VideoCommon/Widescreen.cpp index 7591d098a4..8b63082525 100644 --- a/Source/Core/VideoCommon/Widescreen.cpp +++ b/Source/Core/VideoCommon/Widescreen.cpp @@ -36,18 +36,18 @@ void WidescreenManager::Update() m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); // suggested_aspect_mode overrides SYSCONF_WIDESCREEN - if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) + if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceStandard) m_is_game_widescreen = false; - else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) + else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceWide) m_is_game_widescreen = true; // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. if (!g_ActiveConfig.bWidescreenHack) { const auto aspect_mode = g_ActiveConfig.aspect_mode; - if (aspect_mode == AspectMode::Analog) + if (aspect_mode == AspectMode::ForceStandard) m_is_game_widescreen = false; - else if (aspect_mode == AspectMode::AnalogWide) + else if (aspect_mode == AspectMode::ForceWide) m_is_game_widescreen = true; } } @@ -64,9 +64,10 @@ void WidescreenManager::UpdateWidescreenHeuristic() Update(); - // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. - if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || - g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) + // If widescreen hack isn't active and aspect_mode (user setting) + // is set to a forced aspect ratio, don't use heuristic. + if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::ForceStandard || + g_ActiveConfig.aspect_mode == AspectMode::ForceWide)) return; // Modify the threshold based on which aspect ratio we're already using: