Merge pull request #12170 from Filoppi/custom_aspect_ratio

Add support for custom aspect ratios
This commit is contained in:
Admiral H. Curtiss 2023-12-18 05:19:06 +01:00 committed by GitHub
commit 715a551dac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 209 additions and 88 deletions

View File

@ -21,6 +21,10 @@ const Info<int> GFX_ADAPTER{{System::GFX, "Hardware", "Adapter"}, 0};
const Info<bool> GFX_WIDESCREEN_HACK{{System::GFX, "Settings", "wideScreenHack"}, false}; const Info<bool> GFX_WIDESCREEN_HACK{{System::GFX, "Settings", "wideScreenHack"}, false};
const Info<AspectMode> GFX_ASPECT_RATIO{{System::GFX, "Settings", "AspectRatio"}, AspectMode::Auto}; const Info<AspectMode> GFX_ASPECT_RATIO{{System::GFX, "Settings", "AspectRatio"}, AspectMode::Auto};
const Info<int> GFX_CUSTOM_ASPECT_RATIO_WIDTH{{System::GFX, "Settings", "CustomAspectRatioWidth"},
1};
const Info<int> GFX_CUSTOM_ASPECT_RATIO_HEIGHT{{System::GFX, "Settings", "CustomAspectRatioHeight"},
1};
const Info<AspectMode> GFX_SUGGESTED_ASPECT_RATIO{{System::GFX, "Settings", "SuggestedAspectRatio"}, const Info<AspectMode> GFX_SUGGESTED_ASPECT_RATIO{{System::GFX, "Settings", "SuggestedAspectRatio"},
AspectMode::Auto}; AspectMode::Auto};
const Info<u32> GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD{ const Info<u32> GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD{

View File

@ -28,6 +28,8 @@ extern const Info<int> GFX_ADAPTER;
extern const Info<bool> GFX_WIDESCREEN_HACK; extern const Info<bool> GFX_WIDESCREEN_HACK;
extern const Info<AspectMode> GFX_ASPECT_RATIO; extern const Info<AspectMode> GFX_ASPECT_RATIO;
extern const Info<int> GFX_CUSTOM_ASPECT_RATIO_WIDTH;
extern const Info<int> GFX_CUSTOM_ASPECT_RATIO_HEIGHT;
extern const Info<AspectMode> GFX_SUGGESTED_ASPECT_RATIO; extern const Info<AspectMode> GFX_SUGGESTED_ASPECT_RATIO;
extern const Info<u32> GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD; extern const Info<u32> GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD;
extern const Info<float> GFX_WIDESCREEN_HEURISTIC_ASPECT_RATIO_SLOP; extern const Info<float> GFX_WIDESCREEN_HEURISTIC_ASPECT_RATIO_SLOP;

View File

@ -477,7 +477,7 @@ float VideoInterfaceManager::GetAspectRatio() const
// signal (which would be 4:3) // signal (which would be 4:3)
// This function only deals with standard aspect ratios. For widescreen aspect ratios, // This function only deals with standard aspect ratios. For widescreen aspect ratios,
// multiply the result by 1.33333.. // multiply the result by 1.33333... (the ratio between 16:9 and 4:3)
// 1. Get our active area in BT.601 samples (more or less pixels) // 1. Get our active area in BT.601 samples (more or less pixels)
int active_lines = m_vertical_timing_register.ACV; int active_lines = m_vertical_timing_register.ACV;

View File

@ -388,9 +388,9 @@ public:
u32 GetTicksPerHalfLine() const; u32 GetTicksPerHalfLine() const;
u32 GetTicksPerField() const; u32 GetTicksPerField() const;
// Get the aspect ratio of VI's active area. // Get the aspect ratio of VI's active area (rarely matching pure 4:3).
// This function only deals with standard aspect ratios. For widescreen aspect ratios, multiply // This function only deals with standard aspect ratios. For widescreen aspect ratios, multiply
// the result by 1.33333.. // the result by 1.33333... (the ratio between 16:9 and 4:3)
float GetAspectRatio() const; float GetAspectRatio() const;
// Create a fake VI mode for a fifolog // Create a fake VI mode for a fifolog

View File

@ -359,8 +359,9 @@ void AdvancedWidget::AddDescriptions()
"level 9 but finish in significantly less time.<br><br>" "level 9 but finish in significantly less time.<br><br>"
"<dolphin_emphasis>If unsure, leave this at 6.</dolphin_emphasis>"); "<dolphin_emphasis>If unsure, leave this at 6.</dolphin_emphasis>");
static const char TR_CROPPING_DESCRIPTION[] = QT_TR_NOOP( static const char TR_CROPPING_DESCRIPTION[] = QT_TR_NOOP(
"Crops the picture from its native aspect ratio to 4:3 or " "Crops the picture from its native aspect ratio (which rarely exactly matches 4:3 or 16:9),"
"16:9.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"); " to the specific user target aspect ratio (e.g. 4:3 or 16:9).<br><br>"
"<dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static const char TR_PROGRESSIVE_SCAN_DESCRIPTION[] = QT_TR_NOOP( static const char TR_PROGRESSIVE_SCAN_DESCRIPTION[] = QT_TR_NOOP(
"Enables progressive scan if supported by the emulated software. Most games don't have " "Enables progressive scan if supported by the emulated software. Most games don't have "
"any issue with this.<br><br><dolphin_emphasis>If unsure, leave this " "any issue with this.<br><br><dolphin_emphasis>If unsure, leave this "

View File

@ -561,12 +561,11 @@ void EnhancementsWidget::AddDescriptions()
"causes slowdowns or graphical issues.<br><br><dolphin_emphasis>If unsure, leave " "causes slowdowns or graphical issues.<br><br><dolphin_emphasis>If unsure, leave "
"this unchecked.</dolphin_emphasis>"); "this unchecked.</dolphin_emphasis>");
static const char TR_WIDESCREEN_HACK_DESCRIPTION[] = QT_TR_NOOP( static const char TR_WIDESCREEN_HACK_DESCRIPTION[] = QT_TR_NOOP(
"Forces the game to output graphics for any aspect ratio. Use with \"Aspect Ratio\" set to " "Forces the game to output graphics at any aspect ratio by expanding the view frustum "
"\"Force 16:9\" to force 4:3-only games to run at 16:9.<br><br>Rarely produces good " "without stretching the image.<br>This is a hack, and its results will vary widely game "
"results and " "to game (it often causes the UI to stretch).<br>"
"often partially breaks graphics and game UIs. Unnecessary (and detrimental) if using any " "Game-specific AR/Gecko-code aspect ratio patches are preferable over this if available."
"AR/Gecko-code widescreen patches.<br><br><dolphin_emphasis>If unsure, leave " "<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
"this unchecked.</dolphin_emphasis>");
static const char TR_REMOVE_FOG_DESCRIPTION[] = static const char TR_REMOVE_FOG_DESCRIPTION[] =
QT_TR_NOOP("Makes distant objects more visible by removing fog, thus increasing the overall " QT_TR_NOOP("Makes distant objects more visible by removing fog, thus increasing the overall "
"detail.<br><br>Disabling fog will break some games which rely on proper fog " "detail.<br><br>Disabling fog will break some games which rely on proper fog "

View File

@ -20,6 +20,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
@ -54,9 +55,20 @@ void GeneralWidget::CreateWidgets()
m_video_layout = new QGridLayout(); m_video_layout = new QGridLayout();
m_backend_combo = new ToolTipComboBox(); m_backend_combo = new ToolTipComboBox();
m_aspect_combo = m_aspect_combo = new ConfigChoice(
new ConfigChoice({tr("Auto"), tr("Force 16:9"), tr("Force 4:3"), tr("Stretch to Window")}, {tr("Auto"), tr("Force 16:9"), tr("Force 4:3"), tr("Stretch to Window"), tr("Custom")},
Config::GFX_ASPECT_RATIO); 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_adapter_combo = new ToolTipComboBox;
m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC); m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC);
m_enable_fullscreen = new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN); 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(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(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(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_custom_aspect_label, 4, 0);
m_video_layout->addWidget(m_enable_fullscreen, 4, 1); 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 // Other
auto* m_options_box = new QGroupBox(tr("Other")); auto* m_options_box = new QGroupBox(tr("Other"));
@ -138,6 +154,14 @@ void GeneralWidget::ConnectWidgets()
Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index); Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index);
emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
}); });
connect(m_aspect_combo, qOverload<int>(&QComboBox::currentIndexChanged), this, [&](int index) {
const bool is_custom_aspect_ratio = (index == static_cast<int>(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() void GeneralWidget::LoadSettings()
@ -145,6 +169,13 @@ void GeneralWidget::LoadSettings()
// Video Backend // Video Backend
m_backend_combo->setCurrentIndex(m_backend_combo->findData( m_backend_combo->setCurrentIndex(m_backend_combo->findData(
QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))))); 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() void GeneralWidget::SaveSettings()
@ -207,11 +238,15 @@ void GeneralWidget::AddDescriptions()
QT_TR_NOOP("Uses the main Dolphin window for rendering rather than " QT_TR_NOOP("Uses the main Dolphin window for rendering rather than "
"a separate render window.<br><br><dolphin_emphasis>If unsure, leave " "a separate render window.<br><br><dolphin_emphasis>If unsure, leave "
"this unchecked.</dolphin_emphasis>"); "this unchecked.</dolphin_emphasis>");
static const char TR_ASPECT_RATIO_DESCRIPTION[] = QT_TR_NOOP( static const char TR_ASPECT_RATIO_DESCRIPTION[] =
"Selects which aspect ratio to use when rendering.<br><br>Auto: Uses the native aspect " QT_TR_NOOP("Selects which aspect ratio to use when rendering.<br>"
"ratio<br>Force 16:9: Mimics an analog TV with a widescreen aspect ratio.<br>Force 4:3: " "Each game can have a slightly different native aspect ratio."
"Mimics a standard 4:3 analog TV.<br>Stretch to Window: Stretches the picture to the " "<br><br>Auto: Uses the native aspect ratio"
"window size.<br><br><dolphin_emphasis>If unsure, select Auto.</dolphin_emphasis>"); "<br>Force 16:9: Mimics an analog TV with a widescreen aspect ratio."
"<br>Force 4:3: Mimics a standard 4:3 analog TV."
"<br>Stretch to Window: Stretches the picture to the window size."
"<br>Custom: For games running with specific custom aspect ratio cheats.<br><br>"
"<dolphin_emphasis>If unsure, select Auto.</dolphin_emphasis>");
static const char TR_VSYNC_DESCRIPTION[] = QT_TR_NOOP( static const char TR_VSYNC_DESCRIPTION[] = QT_TR_NOOP(
"Waits for vertical blanks in order to prevent tearing.<br><br>Decreases performance " "Waits for vertical blanks in order to prevent tearing.<br><br>Decreases performance "
"if emulation speed is below 100%.<br><br><dolphin_emphasis>If unsure, leave " "if emulation speed is below 100%.<br><br><dolphin_emphasis>If unsure, leave "
@ -260,6 +295,9 @@ void GeneralWidget::AddDescriptions()
m_aspect_combo->SetTitle(tr("Aspect Ratio")); m_aspect_combo->SetTitle(tr("Aspect Ratio"));
m_aspect_combo->SetDescription(tr(TR_ASPECT_RATIO_DESCRIPTION)); 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_vsync->SetDescription(tr(TR_VSYNC_DESCRIPTION));
m_enable_fullscreen->SetDescription(tr(TR_FULLSCREEN_DESCRIPTION)); m_enable_fullscreen->SetDescription(tr(TR_FULLSCREEN_DESCRIPTION));

View File

@ -9,10 +9,12 @@
class ConfigBool; class ConfigBool;
class ConfigChoice; class ConfigChoice;
class ConfigInteger;
class ConfigRadioInt; class ConfigRadioInt;
class GraphicsWindow; class GraphicsWindow;
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
class QLabel;
class QRadioButton; class QRadioButton;
class QGridLayout; class QGridLayout;
class ToolTipComboBox; class ToolTipComboBox;
@ -41,6 +43,9 @@ private:
ToolTipComboBox* m_backend_combo; ToolTipComboBox* m_backend_combo;
ToolTipComboBox* m_adapter_combo; ToolTipComboBox* m_adapter_combo;
ConfigChoice* m_aspect_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_vsync;
ConfigBool* m_enable_fullscreen; ConfigBool* m_enable_fullscreen;

View File

@ -402,12 +402,15 @@ void HotkeyScheduler::Run()
case AspectMode::Stretch: case AspectMode::Stretch:
OSD::AddMessage("Stretch"); OSD::AddMessage("Stretch");
break; break;
case AspectMode::Analog: case AspectMode::ForceStandard:
OSD::AddMessage("Force 4:3"); OSD::AddMessage("Force 4:3");
break; break;
case AspectMode::AnalogWide: case AspectMode::ForceWide:
OSD::AddMessage("Force 16:9"); OSD::AddMessage("Force 16:9");
break; break;
case AspectMode::Custom:
OSD::AddMessage("Custom");
break;
case AspectMode::Auto: case AspectMode::Auto:
default: default:
OSD::AddMessage("Auto"); OSD::AddMessage("Auto");

View File

@ -4,6 +4,7 @@
#include "VideoCommon/Present.h" #include "VideoCommon/Present.h"
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/HW/VideoInterface.h" #include "Core/HW/VideoInterface.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "Core/System.h" #include "Core/System.h"
@ -13,6 +14,7 @@
#include "Present.h" #include "Present.h"
#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/FrameDumper.h" #include "VideoCommon/FrameDumper.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/OnScreenUI.h" #include "VideoCommon/OnScreenUI.h"
#include "VideoCommon/PostProcessing.h" #include "VideoCommon/PostProcessing.h"
#include "VideoCommon/Statistics.h" #include "VideoCommon/Statistics.h"
@ -28,9 +30,10 @@ static constexpr int VIDEO_ENCODER_LCM = 4;
namespace VideoCommon 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<int, int> FindClosestIntegerResolution(float width, float height, static std::tuple<int, int> FindClosestIntegerResolution(float width, float height,
@ -201,20 +204,46 @@ void Presenter::ProcessFrameDumping(u64 ticks) const
void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height) void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height)
{ {
const bool is_first = m_backbuffer_width == 0 && m_backbuffer_height == 0;
const bool size_changed =
(m_backbuffer_width != backbuffer_width || m_backbuffer_height != backbuffer_height);
m_backbuffer_width = backbuffer_width; m_backbuffer_width = backbuffer_width;
m_backbuffer_height = backbuffer_height; m_backbuffer_height = backbuffer_height;
UpdateDrawRectangle(); UpdateDrawRectangle();
OnBackbufferSet(size_changed, is_first);
} }
void Presenter::SetBackbuffer(SurfaceInfo info) void Presenter::SetBackbuffer(SurfaceInfo info)
{ {
const bool is_first = m_backbuffer_width == 0 && m_backbuffer_height == 0;
const bool size_changed =
(m_backbuffer_width != (int)info.width || m_backbuffer_height != (int)info.height);
m_backbuffer_width = info.width; m_backbuffer_width = info.width;
m_backbuffer_height = info.height; m_backbuffer_height = info.height;
m_backbuffer_scale = info.scale; m_backbuffer_scale = info.scale;
m_backbuffer_format = info.format; m_backbuffer_format = info.format;
if (m_onscreen_ui) if (m_onscreen_ui)
m_onscreen_ui->SetScale(info.scale); m_onscreen_ui->SetScale(info.scale);
OnBackbufferSet(size_changed, is_first);
}
void Presenter::OnBackbufferSet(bool size_changed, bool is_first_set)
{
UpdateDrawRectangle(); UpdateDrawRectangle();
// Automatically update the resolution scale if the window size changed,
// or if the game XFB resolution changed.
if (size_changed && !is_first_set && g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL &&
m_auto_resolution_scale != AutoIntegralScale())
{
g_framebuffer_manager->RecreateEFBFramebuffer();
}
if (size_changed || is_first_set)
{
m_auto_resolution_scale = AutoIntegralScale();
}
} }
void Presenter::ConfigChanged(u32 changed_bits) void Presenter::ConfigChanged(u32 changed_bits)
@ -292,15 +321,22 @@ float Presenter::CalculateDrawAspectRatio(bool allow_stretch) const
return (static_cast<float>(m_backbuffer_width) / static_cast<float>(m_backbuffer_height)); return (static_cast<float>(m_backbuffer_width) / static_cast<float>(m_backbuffer_height));
auto& vi = Core::System::GetInstance().GetVideoInterface(); 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())) (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<int>* target_rect, void Presenter::AdjustRectanglesToFitBounds(MathUtil::Rectangle<int>* target_rect,
@ -365,38 +401,29 @@ void* Presenter::GetNewSurfaceHandle()
u32 Presenter::AutoIntegralScale() const u32 Presenter::AutoIntegralScale() const
{ {
const float efb_aspect_ratio = static_cast<float>(EFB_WIDTH) / EFB_HEIGHT; // Take the source resolution (XFB) and stretch it on the target aspect ratio.
const float target_aspect_ratio = // If the target resolution is larger (on either x or y), we scale the source
static_cast<float>(m_target_rectangle.GetWidth()) / m_target_rectangle.GetHeight(); // by a integer multiplier until it won't have to be scaled up anymore.
u32 source_width = m_last_xfb_width;
u32 target_width; u32 source_height = m_last_xfb_height;
u32 target_height; const u32 target_width = m_target_rectangle.GetWidth();
const u32 target_height = m_target_rectangle.GetHeight();
// Instead of using the entire window (back buffer) resolution, const float source_aspect_ratio = (float)source_width / source_height;
// find the portion of it that will actually contain the EFB output, const float target_aspect_ratio = (float)target_width / target_height;
// and ignore the portion that will likely have black bars. if (source_aspect_ratio >= target_aspect_ratio)
if (target_aspect_ratio >= efb_aspect_ratio) source_width = std::round(source_height * target_aspect_ratio);
{
target_height = m_target_rectangle.GetHeight();
target_width = static_cast<u32>(
std::round((static_cast<float>(m_target_rectangle.GetWidth()) / target_aspect_ratio) *
efb_aspect_ratio));
}
else else
{ source_height = std::round(source_width / target_aspect_ratio);
target_width = m_target_rectangle.GetWidth(); const u32 width_scale =
target_height = static_cast<u32>( source_width > 0 ? ((target_width + (source_width - 1)) / source_width) : 1;
std::round((static_cast<float>(m_target_rectangle.GetHeight()) * target_aspect_ratio) / const u32 height_scale =
efb_aspect_ratio)); source_height > 0 ? ((target_height + (source_height - 1)) / source_height) : 1;
} // Limit to the max to avoid creating textures larger than their max supported resolution.
return std::min(std::max(width_scale, height_scale),
// Calculate a scale based on the adjusted window size static_cast<u32>(Config::Get(Config::GFX_MAX_EFB_SCALE)));
u32 width = EFB_WIDTH * target_width / m_last_xfb_width;
u32 height = EFB_HEIGHT * target_height / m_last_xfb_height;
return std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1);
} }
void Presenter::SetWindowSize(int width, int height) void Presenter::SetSuggestedWindowSize(int width, int height)
{ {
// While trying to guess the best window resolution, we can't allow it to use the // While trying to guess the best window resolution, we can't allow it to use the
// "AspectMode::Stretch" setting because that would self influence the output result, // "AspectMode::Stretch" setting because that would self influence the output result,
@ -414,7 +441,7 @@ void Presenter::SetWindowSize(int width, int height)
Host_RequestRenderWindowSize(out_width, out_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<float, float> Presenter::ApplyStandardAspectCrop(float width, float height, std::tuple<float, float> Presenter::ApplyStandardAspectCrop(float width, float height,
bool allow_stretch) const bool allow_stretch) const
{ {
@ -426,13 +453,28 @@ std::tuple<float, float> Presenter::ApplyStandardAspectCrop(float width, float h
if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch) if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch)
return {width, height}; 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 current_aspect = width / height;
const float expected_aspect = float expected_aspect;
(aspect_mode == AspectMode::AnalogWide || switch (aspect_mode)
(aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) ? {
(16.0f / 9.0f) : default:
(4.0f / 3.0f); 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) if (current_aspect > expected_aspect)
{ {
// keep height, crop width // keep height, crop width
@ -457,11 +499,13 @@ void Presenter::UpdateDrawRectangle()
if (g_ActiveConfig.bWidescreenHack) if (g_ActiveConfig.bWidescreenHack)
{ {
auto& vi = Core::System::GetInstance().GetVideoInterface(); 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()) 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) if (adjust > 1)
{ {
// Vert+ // Vert+
@ -652,7 +696,7 @@ void Presenter::Present()
// Update the window size based on the frame that was just rendered. // Update the window size based on the frame that was just rendered.
// Due to depending on guest state, we need to call this every frame. // Due to depending on guest state, we need to call this every frame.
SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
} }
return; return;
} }
@ -694,7 +738,7 @@ void Presenter::Present()
{ {
// Update the window size based on the frame that was just rendered. // Update the window size based on the frame that was just rendered.
// Due to depending on guest state, we need to call this every frame. // Due to depending on guest state, we need to call this every frame.
SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
} }
if (m_onscreen_ui) if (m_onscreen_ui)

View File

@ -46,18 +46,20 @@ public:
void ConfigChanged(u32 changed_bits); void ConfigChanged(u32 changed_bits);
// Display resolution // Window resolution (display resolution if fullscreen)
int GetBackbufferWidth() const { return m_backbuffer_width; } int GetBackbufferWidth() const { return m_backbuffer_width; }
int GetBackbufferHeight() const { return m_backbuffer_height; } int GetBackbufferHeight() const { return m_backbuffer_height; }
float GetBackbufferScale() const { return m_backbuffer_scale; } float GetBackbufferScale() const { return m_backbuffer_scale; }
u32 AutoIntegralScale() const; u32 AutoIntegralScale() const;
AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; } AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; }
void SetWindowSize(int width, int height); void SetSuggestedWindowSize(int width, int height);
void SetBackbuffer(int backbuffer_width, int backbuffer_height); void SetBackbuffer(int backbuffer_width, int backbuffer_height);
void SetBackbuffer(SurfaceInfo info); void SetBackbuffer(SurfaceInfo info);
void OnBackbufferSet(bool size_changed, bool is_first_set);
void UpdateDrawRectangle(); void UpdateDrawRectangle();
// Returns the target aspect ratio the XFB output should be drawn with.
float CalculateDrawAspectRatio(bool allow_stretch = true) const; float CalculateDrawAspectRatio(bool allow_stretch = true) const;
// Crops the target rectangle to the framebuffer dimensions, reducing the size of the source // Crops the target rectangle to the framebuffer dimensions, reducing the size of the source
@ -103,6 +105,8 @@ private:
void ProcessFrameDumping(u64 ticks) const; void ProcessFrameDumping(u64 ticks) const;
void OnBackBufferSizeChanged();
std::tuple<int, int> CalculateOutputDimensions(int width, int height, std::tuple<int, int> CalculateOutputDimensions(int width, int height,
bool allow_stretch = true) const; bool allow_stretch = true) const;
std::tuple<float, float> ApplyStandardAspectCrop(float width, float height, std::tuple<float, float> ApplyStandardAspectCrop(float width, float height,
@ -126,15 +130,20 @@ private:
Common::Flag m_surface_changed; Common::Flag m_surface_changed;
Common::Flag m_surface_resized; Common::Flag m_surface_resized;
// The presentation rectangle.
// Width and height correspond to the final output resolution.
// Offsets imply black borders (if the window aspect ratio doesn't match the game's one).
MathUtil::Rectangle<int> m_target_rectangle = {}; MathUtil::Rectangle<int> m_target_rectangle = {};
u32 m_auto_resolution_scale = 1;
RcTcacheEntry m_xfb_entry; RcTcacheEntry m_xfb_entry;
MathUtil::Rectangle<int> m_xfb_rect; MathUtil::Rectangle<int> m_xfb_rect;
// Tracking of XFB textures so we don't render duplicate frames. // Tracking of XFB textures so we don't render duplicate frames.
u64 m_last_xfb_id = std::numeric_limits<u64>::max(); u64 m_last_xfb_id = std::numeric_limits<u64>::max();
// These will be set on the first call to SetWindowSize. // These will be set on the first call to SetSuggestedWindowSize.
int m_last_window_request_width = 0; int m_last_window_request_width = 0;
int m_last_window_request_height = 0; int m_last_window_request_height = 0;

View File

@ -91,8 +91,9 @@ static bool IsAnamorphicProjection(const Projection::Raw& projection, const View
const VideoConfig& config) const VideoConfig& config)
{ {
// If ratio between our projection and viewport aspect ratios is similar to 16:9 / 4:3 // If ratio between our projection and viewport aspect ratios is similar to 16:9 / 4:3
// we have an anamorphic projection. This value can be overridden // we have an anamorphic projection. This value can be overridden by a GameINI.
// by a GameINI. // Game cheats that change the aspect ratio to natively unsupported ones
// won't be automatically recognized here.
return std::abs(CalculateProjectionViewportRatio(projection, viewport) - return std::abs(CalculateProjectionViewportRatio(projection, viewport) -
config.widescreen_heuristic_widescreen_ratio) < config.widescreen_heuristic_widescreen_ratio) <

View File

@ -85,6 +85,8 @@ void VideoConfig::Refresh()
bWidescreenHack = Config::Get(Config::GFX_WIDESCREEN_HACK); bWidescreenHack = Config::Get(Config::GFX_WIDESCREEN_HACK);
aspect_mode = Config::Get(Config::GFX_ASPECT_RATIO); 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); suggested_aspect_mode = Config::Get(Config::GFX_SUGGESTED_ASPECT_RATIO);
widescreen_heuristic_transition_threshold = widescreen_heuristic_transition_threshold =
Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD); Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_TRANSITION_THRESHOLD);
@ -287,6 +289,7 @@ void CheckForConfigChanges()
const u32 old_game_mod_changes = const u32 old_game_mod_changes =
g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0; g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0;
const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods; const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods;
const AspectMode old_aspect_mode = g_ActiveConfig.aspect_mode;
const AspectMode old_suggested_aspect_mode = g_ActiveConfig.suggested_aspect_mode; const AspectMode old_suggested_aspect_mode = g_ActiveConfig.suggested_aspect_mode;
const bool old_widescreen_hack = g_ActiveConfig.bWidescreenHack; const bool old_widescreen_hack = g_ActiveConfig.bWidescreenHack;
const auto old_post_processing_shader = g_ActiveConfig.sPostProcessingShader; const auto old_post_processing_shader = g_ActiveConfig.sPostProcessingShader;
@ -336,6 +339,8 @@ void CheckForConfigChanges()
changed_bits |= CONFIG_CHANGE_BIT_BBOX; changed_bits |= CONFIG_CHANGE_BIT_BBOX;
if (old_efb_scale != g_ActiveConfig.iEFBScale) if (old_efb_scale != g_ActiveConfig.iEFBScale)
changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE;
if (old_aspect_mode != g_ActiveConfig.aspect_mode)
changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO;
if (old_suggested_aspect_mode != g_ActiveConfig.suggested_aspect_mode) if (old_suggested_aspect_mode != g_ActiveConfig.suggested_aspect_mode)
changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO; changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO;
if (old_widescreen_hack != g_ActiveConfig.bWidescreenHack) if (old_widescreen_hack != g_ActiveConfig.bWidescreenHack)

View File

@ -21,10 +21,11 @@ constexpr int EFB_SCALE_AUTO_INTEGRAL = 0;
enum class AspectMode : int enum class AspectMode : int
{ {
Auto, Auto, // 4:3 or 16:9
AnalogWide, ForceWide, // 16:9
Analog, ForceStandard, // 4:3
Stretch, Stretch,
Custom,
}; };
enum class StereoMode : int enum class StereoMode : int
@ -105,6 +106,8 @@ struct VideoConfig final
bool bVSyncActive = false; bool bVSyncActive = false;
bool bWidescreenHack = false; bool bWidescreenHack = false;
AspectMode aspect_mode{}; AspectMode aspect_mode{};
int custom_aspect_width = 1;
int custom_aspect_height = 1;
AspectMode suggested_aspect_mode{}; AspectMode suggested_aspect_mode{};
u32 widescreen_heuristic_transition_threshold = 0; u32 widescreen_heuristic_transition_threshold = 0;
float widescreen_heuristic_aspect_ratio_slop = 0.f; float widescreen_heuristic_aspect_ratio_slop = 0.f;
@ -365,6 +368,8 @@ struct VideoConfig final
bool UsingUberShaders() const; bool UsingUberShaders() const;
u32 GetShaderCompilerThreads() const; u32 GetShaderCompilerThreads() const;
u32 GetShaderPrecompilerThreads() const; u32 GetShaderPrecompilerThreads() const;
float GetCustomAspectRatio() const { return (float)custom_aspect_width / custom_aspect_height; }
}; };
extern VideoConfig g_Config; extern VideoConfig g_Config;

View File

@ -36,23 +36,24 @@ void WidescreenManager::Update()
m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
// suggested_aspect_mode overrides 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; 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; 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 widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9.
if (!g_ActiveConfig.bWidescreenHack) if (!g_ActiveConfig.bWidescreenHack)
{ {
const auto aspect_mode = g_ActiveConfig.aspect_mode; const auto aspect_mode = g_ActiveConfig.aspect_mode;
if (aspect_mode == AspectMode::Analog) if (aspect_mode == AspectMode::ForceStandard)
m_is_game_widescreen = false; m_is_game_widescreen = false;
else if (aspect_mode == AspectMode::AnalogWide) else if (aspect_mode == AspectMode::ForceWide)
m_is_game_widescreen = true; m_is_game_widescreen = true;
} }
} }
// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
// Cheats that change the game aspect ratio to natively unsupported ones won't be recognized here.
void WidescreenManager::UpdateWidescreenHeuristic() void WidescreenManager::UpdateWidescreenHeuristic()
{ {
const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount(); const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount();
@ -63,9 +64,10 @@ void WidescreenManager::UpdateWidescreenHeuristic()
Update(); Update();
// If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. // If widescreen hack isn't active and aspect_mode (user setting)
if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || // is set to a forced aspect ratio, don't use heuristic.
g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::ForceStandard ||
g_ActiveConfig.aspect_mode == AspectMode::ForceWide))
return; return;
// Modify the threshold based on which aspect ratio we're already using: // Modify the threshold based on which aspect ratio we're already using:

View File

@ -11,11 +11,14 @@
class PointerWrap; class PointerWrap;
// This class is responsible for tracking the game's aspect ratio. // This class is responsible for tracking the game's aspect ratio.
// This exclusively supports 4:3 or 16:9 detection by default.
class WidescreenManager class WidescreenManager
{ {
public: public:
WidescreenManager(); WidescreenManager();
// Just a helper to tell whether the game seems to be running in widescreen,
// or if it's being forced to.
bool IsGameWidescreen() const { return m_is_game_widescreen; } bool IsGameWidescreen() const { return m_is_game_widescreen; }
void DoState(PointerWrap& p); void DoState(PointerWrap& p);