summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/RenderBase.cpp
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2020-01-25 19:04:50 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2020-02-09 13:22:25 -0600
commit10223da6a87ee572ee534ff4dc6ffe1716986659 (patch)
tree8e0151bed91f65cdb33a96243d95a3a3feb43a98 /Source/Core/VideoCommon/RenderBase.cpp
parente00459f68fd53b6a4d30800ce0548e459b141e5e (diff)
VideoCommon: Tweak widescreen heuristic and clean up some related Renderer logic.
Diffstat (limited to 'Source/Core/VideoCommon/RenderBase.cpp')
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp234
1 files changed, 136 insertions, 98 deletions
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 03e277d563..59c7f12af4 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -102,7 +102,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer
UpdateDrawRectangle();
CalculateTargetSize();
- m_aspect_wide = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN);
+ m_is_game_widescreen = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN);
}
Renderer::~Renderer() = default;
@@ -561,22 +561,21 @@ void Renderer::DrawDebugText()
float Renderer::CalculateDrawAspectRatio() const
{
- if (g_ActiveConfig.aspect_mode == AspectMode::Stretch)
- {
- // If stretch is enabled, we prefer the aspect ratio of the window.
+ const auto aspect_mode = g_ActiveConfig.aspect_mode;
+
+ // If stretch is enabled, we prefer the aspect ratio of the window.
+ if (aspect_mode == AspectMode::Stretch)
return (static_cast<float>(m_backbuffer_width) / static_cast<float>(m_backbuffer_height));
- }
- // The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
- if (g_ActiveConfig.aspect_mode == AspectMode::AnalogWide ||
- (g_ActiveConfig.aspect_mode != AspectMode::Analog && m_aspect_wide))
- {
- return AspectToWidescreen(VideoInterface::GetAspectRatio());
- }
- else
+ const float aspect_ratio = VideoInterface::GetAspectRatio();
+
+ if (aspect_mode == AspectMode::AnalogWide ||
+ (aspect_mode == AspectMode::Auto && m_is_game_widescreen))
{
- return VideoInterface::GetAspectRatio();
+ return AspectToWidescreen(aspect_ratio);
}
+
+ return aspect_ratio;
}
void Renderer::AdjustRectanglesToFitBounds(MathUtil::Rectangle<int>* target_rect,
@@ -728,9 +727,7 @@ std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
void Renderer::UpdateDrawRectangle()
{
- // The rendering window size
- const float win_width = static_cast<float>(m_backbuffer_width);
- const float win_height = static_cast<float>(m_backbuffer_height);
+ const float draw_aspect_ratio = CalculateDrawAspectRatio();
// Update aspect ratio hack values
// Won't take effect until next frame
@@ -738,28 +735,10 @@ void Renderer::UpdateDrawRectangle()
if (g_ActiveConfig.bWidescreenHack)
{
float source_aspect = VideoInterface::GetAspectRatio();
- if (m_aspect_wide)
+ if (m_is_game_widescreen)
source_aspect = AspectToWidescreen(source_aspect);
- float target_aspect = 0.0f;
-
- switch (g_ActiveConfig.aspect_mode)
- {
- case AspectMode::Stretch:
- target_aspect = win_width / win_height;
- break;
- case AspectMode::Analog:
- target_aspect = VideoInterface::GetAspectRatio();
- break;
- case AspectMode::AnalogWide:
- target_aspect = AspectToWidescreen(VideoInterface::GetAspectRatio());
- break;
- case AspectMode::Auto:
- default:
- target_aspect = source_aspect;
- break;
- }
- float adjust = source_aspect / target_aspect;
+ const float adjust = source_aspect / draw_aspect_ratio;
if (adjust > 1)
{
// Vert+
@@ -775,40 +754,24 @@ void Renderer::UpdateDrawRectangle()
}
else
{
- // Hack is disabled
+ // Hack is disabled.
g_Config.fAspectRatioHackW = 1;
g_Config.fAspectRatioHackH = 1;
}
- // get the picture aspect ratio
- const float draw_aspect_ratio = CalculateDrawAspectRatio();
+ // The rendering window size
+ const float win_width = static_cast<float>(m_backbuffer_width);
+ const float win_height = static_cast<float>(m_backbuffer_height);
// Make ControllerInterface aware of the render window region actually being used
// to adjust mouse cursor inputs.
g_controller_interface.SetAspectRatioAdjustment(draw_aspect_ratio / (win_width / win_height));
- float draw_width, draw_height, crop_width, crop_height;
- draw_width = crop_width = draw_aspect_ratio;
- draw_height = crop_height = 1;
+ float draw_width = draw_aspect_ratio;
+ float draw_height = 1;
- // crop the picture to a standard aspect ratio
- if (g_ActiveConfig.bCrop && g_ActiveConfig.aspect_mode != AspectMode::Stretch)
- {
- float expected_aspect = (g_ActiveConfig.aspect_mode == AspectMode::AnalogWide ||
- (g_ActiveConfig.aspect_mode != AspectMode::Analog && m_aspect_wide)) ?
- (16.0f / 9.0f) :
- (4.0f / 3.0f);
- if (crop_width / crop_height >= expected_aspect)
- {
- // the picture is flatter than it should be
- crop_width = crop_height * expected_aspect;
- }
- else
- {
- // the picture is skinnier than it should be
- crop_height = crop_width / expected_aspect;
- }
- }
+ // Crop the picture to a standard aspect ratio. (if enabled)
+ auto [crop_width, crop_height] = ApplyStandardAspectCrop(draw_width, draw_height);
// scale the picture to fit the rendering window
if (win_width / win_height >= crop_width / crop_height)
@@ -851,6 +814,34 @@ void Renderer::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.
+std::tuple<float, float> Renderer::ApplyStandardAspectCrop(float width, float height) const
+{
+ const auto aspect_mode = g_ActiveConfig.aspect_mode;
+
+ if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch)
+ return {width, height};
+
+ // Force 4:3 or 16:9 by cropping the image.
+ const float current_aspect = width / height;
+ const float expected_aspect = (aspect_mode == AspectMode::AnalogWide ||
+ (aspect_mode == AspectMode::Auto && m_is_game_widescreen)) ?
+ (16.0f / 9.0f) :
+ (4.0f / 3.0f);
+ if (current_aspect > expected_aspect)
+ {
+ // keep height, crop width
+ width = height * expected_aspect;
+ }
+ else
+ {
+ // keep width, crop height
+ height = width / expected_aspect;
+ }
+
+ return {width, height};
+}
+
std::tuple<int, int> Renderer::CalculateOutputDimensions(int width, int height) const
{
width = std::max(width, 1);
@@ -858,25 +849,8 @@ std::tuple<int, int> Renderer::CalculateOutputDimensions(int width, int height)
auto [scaled_width, scaled_height] = ScaleToDisplayAspectRatio(width, height);
- if (g_ActiveConfig.bCrop)
- {
- // Force 4:3 or 16:9 by cropping the image.
- float current_aspect = scaled_width / scaled_height;
- float expected_aspect = (g_ActiveConfig.aspect_mode == AspectMode::AnalogWide ||
- (g_ActiveConfig.aspect_mode != AspectMode::Analog && m_aspect_wide)) ?
- (16.0f / 9.0f) :
- (4.0f / 3.0f);
- if (current_aspect > expected_aspect)
- {
- // keep height, crop width
- scaled_width = scaled_height * expected_aspect;
- }
- else
- {
- // keep width, crop height
- scaled_height = scaled_width / expected_aspect;
- }
- }
+ // Apply crop if enabled.
+ std::tie(scaled_width, scaled_height) = ApplyStandardAspectCrop(scaled_width, scaled_height);
width = static_cast<int>(std::ceil(scaled_width));
height = static_cast<int>(std::ceil(scaled_height));
@@ -1163,30 +1137,88 @@ void Renderer::EndUIFrame()
BeginImGuiFrame();
}
-void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
+// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
+void Renderer::UpdateWidescreenHeuristic()
{
- const AspectMode suggested = g_ActiveConfig.suggested_aspect_mode;
- if (suggested == AspectMode::Analog || suggested == AspectMode::AnalogWide)
+ // VertexManager maintains no statistics in Wii mode.
+ if (SConfig::GetInstance().bWii)
+ return;
+
+ const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount();
+
+ // If suggested_aspect_mode (GameINI) is configured don't use heuristic.
+ if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto)
+ return;
+
+ // 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))
+ return;
+
+ const auto& persp = flush_statistics.perspective;
+ const auto& ortho = flush_statistics.orthographic;
+
+ DEBUG_LOG(VIDEO,
+ "flush stats: persp: 4:3=%ld 16:9=%ld other=%ld | ortho: 4:3=%ld 16:9=%ld other=%ld",
+ persp.normal_flush_count, persp.anamorphic_flush_count, persp.other_flush_count,
+ ortho.normal_flush_count, ortho.anamorphic_flush_count, ortho.other_flush_count);
+
+ DEBUG_LOG(VIDEO,
+ "vertex stats: persp: 4:3=%ld 16:9=%ld other=%ld | ortho: 4:3=%ld 16:9=%ld other=%ld",
+ persp.normal_vertex_count, persp.anamorphic_vertex_count, persp.other_vertex_count,
+ ortho.normal_vertex_count, ortho.anamorphic_vertex_count, ortho.other_vertex_count);
+
+ // Modify the threshold based on which aspect ratio we're already using:
+ // If the game's in 4:3, it probably won't switch to anamorphic, and vice-versa.
+ static constexpr u32 TRANSITION_THRESHOLD = 3;
+
+ const auto looks_normal = [](auto& counts) {
+ return counts.normal_vertex_count > counts.anamorphic_vertex_count * TRANSITION_THRESHOLD;
+ };
+ const auto looks_anamorphic = [](auto& counts) {
+ return counts.anamorphic_vertex_count > counts.normal_vertex_count * TRANSITION_THRESHOLD;
+ };
+
+ const auto ortho_looks_anamorphic = looks_anamorphic(ortho);
+
+ if (looks_anamorphic(persp) || ortho_looks_anamorphic)
{
- m_aspect_wide = suggested == AspectMode::AnalogWide;
+ // If either perspective or orthographic projections look anamorphic, it's a safe bet.
+ m_is_game_widescreen = true;
}
- else if (SConfig::GetInstance().bWii)
+ else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho)))
{
- m_aspect_wide = Config::Get(Config::SYSCONF_WIDESCREEN);
+ // Many widescreen games (or AR/GeckoCodes) use anamorphic perspective projections
+ // with NON-anamorphic orthographic projections.
+ // This can cause incorrect changes to 4:3 when perspective projections are temporarily not
+ // shown. e.g. Animal Crossing's inventory menu.
+ // Unless we were in a sitation which was orthographically anamorphic
+ // we won't consider orthographic data for changes from 16:9 to 4:3.
+ m_is_game_widescreen = false;
}
- else
- {
- // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
- const auto [flush_count_4_3, flush_count_anamorphic] =
- g_vertex_manager->ResetFlushAspectRatioCount();
- const size_t flush_total = flush_count_4_3 + flush_count_anamorphic;
- // Modify the threshold based on which aspect ratio we're already using: if
- // the game's in 4:3, it probably won't switch to anamorphic, and vice-versa.
- if (m_aspect_wide)
- m_aspect_wide = !(flush_count_4_3 > 0.75 * flush_total);
- else
- m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
+ m_was_orthographically_anamorphic = ortho_looks_anamorphic;
+}
+
+void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
+{
+ if (SConfig::GetInstance().bWii)
+ m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
+
+ // suggested_aspect_mode overrides SYSCONF_WIDESCREEN
+ if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog)
+ m_is_game_widescreen = false;
+ else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide)
+ 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)
+ m_is_game_widescreen = false;
+ else if (aspect_mode == AspectMode::AnalogWide)
+ m_is_game_widescreen = true;
}
// Ensure the last frame was written to the dump.
@@ -1225,6 +1257,10 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
if (!IsHeadless())
{
BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}});
+
+ if (!is_duplicate_frame)
+ UpdateWidescreenHeuristic();
+
UpdateDrawRectangle();
// Adjust the source rectangle instead of using an oversized viewport to render the XFB.
@@ -1659,7 +1695,7 @@ bool Renderer::UseVertexDepthRange() const
void Renderer::DoState(PointerWrap& p)
{
- p.Do(m_aspect_wide);
+ p.Do(m_is_game_widescreen);
p.Do(m_frame_count);
p.Do(m_prev_efb_format);
p.Do(m_last_xfb_ticks);
@@ -1673,6 +1709,8 @@ void Renderer::DoState(PointerWrap& p)
// Force the next xfb to be displayed.
m_last_xfb_id = std::numeric_limits<u64>::max();
+ m_was_orthographically_anamorphic = false;
+
// And actually display it.
Swap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height, m_last_xfb_ticks);
}