summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/Present.cpp
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2023-12-18 05:19:06 +0100
committerGitHub <noreply@github.com>2023-12-18 05:19:06 +0100
commit715a551daccc2a8e782089f7a70902ddf1a91a09 (patch)
tree7a2ccaa8e55d0718846140e2f45bad4f0ad96ce0 /Source/Core/VideoCommon/Present.cpp
parentde8bc32dc53a4821a97e5dccb37527598fe98c6c (diff)
parenta6dfeed3189b43a2267f5819e91e7a1c105ef3a9 (diff)
Merge pull request #12170 from Filoppi/custom_aspect_ratio
Add support for custom aspect ratios
Diffstat (limited to 'Source/Core/VideoCommon/Present.cpp')
-rw-r--r--Source/Core/VideoCommon/Present.cpp138
1 files changed, 91 insertions, 47 deletions
diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp
index 279d07893e..bf4b6af33b 100644
--- a/Source/Core/VideoCommon/Present.cpp
+++ b/Source/Core/VideoCommon/Present.cpp
@@ -4,6 +4,7 @@
#include "VideoCommon/Present.h"
#include "Common/ChunkFile.h"
+#include "Core/Config/GraphicsSettings.h"
#include "Core/HW/VideoInterface.h"
#include "Core/Host.h"
#include "Core/System.h"
@@ -13,6 +14,7 @@
#include "Present.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/FrameDumper.h"
+#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/OnScreenUI.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/Statistics.h"
@@ -28,9 +30,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<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)
{
+ 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_height = backbuffer_height;
UpdateDrawRectangle();
+
+ OnBackbufferSet(size_changed, is_first);
}
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_height = info.height;
m_backbuffer_scale = info.scale;
m_backbuffer_format = info.format;
if (m_onscreen_ui)
m_onscreen_ui->SetScale(info.scale);
+
+ OnBackbufferSet(size_changed, is_first);
+}
+
+void Presenter::OnBackbufferSet(bool size_changed, bool is_first_set)
+{
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)
@@ -292,15 +321,22 @@ float Presenter::CalculateDrawAspectRatio(bool allow_stretch) const
return (static_cast<float>(m_backbuffer_width) / static_cast<float>(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<int>* target_rect,
@@ -365,38 +401,29 @@ void* Presenter::GetNewSurfaceHandle()
u32 Presenter::AutoIntegralScale() const
{
- const float efb_aspect_ratio = static_cast<float>(EFB_WIDTH) / EFB_HEIGHT;
- const float target_aspect_ratio =
- static_cast<float>(m_target_rectangle.GetWidth()) / m_target_rectangle.GetHeight();
-
- u32 target_width;
- u32 target_height;
-
- // Instead of using the entire window (back buffer) resolution,
- // find the portion of it that will actually contain the EFB output,
- // and ignore the portion that will likely have black bars.
- if (target_aspect_ratio >= efb_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));
- }
+ // Take the source resolution (XFB) and stretch it on the target aspect ratio.
+ // If the target resolution is larger (on either x or y), we scale the source
+ // by a integer multiplier until it won't have to be scaled up anymore.
+ u32 source_width = m_last_xfb_width;
+ u32 source_height = m_last_xfb_height;
+ const u32 target_width = m_target_rectangle.GetWidth();
+ const u32 target_height = m_target_rectangle.GetHeight();
+ const float source_aspect_ratio = (float)source_width / source_height;
+ const float target_aspect_ratio = (float)target_width / target_height;
+ if (source_aspect_ratio >= target_aspect_ratio)
+ source_width = std::round(source_height * target_aspect_ratio);
else
- {
- target_width = m_target_rectangle.GetWidth();
- target_height = static_cast<u32>(
- std::round((static_cast<float>(m_target_rectangle.GetHeight()) * target_aspect_ratio) /
- efb_aspect_ratio));
- }
-
- // Calculate a scale based on the adjusted window size
- 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);
+ source_height = std::round(source_width / target_aspect_ratio);
+ const u32 width_scale =
+ source_width > 0 ? ((target_width + (source_width - 1)) / source_width) : 1;
+ const u32 height_scale =
+ 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),
+ static_cast<u32>(Config::Get(Config::GFX_MAX_EFB_SCALE)));
}
-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
// "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);
}
-// 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,
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)
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 +499,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+
@@ -652,7 +696,7 @@ void Presenter::Present()
// 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.
- SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
+ SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
}
return;
}
@@ -694,7 +738,7 @@ void Presenter::Present()
{
// 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.
- SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
+ SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
}
if (m_onscreen_ui)