From d10d09ccc1eae227db5be0d2908c7aec5610e6a1 Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Fri, 3 Mar 2017 14:36:51 -0800 Subject: [PATCH] VideoCommon: rework anamorphic widescreen heuristic Some widescreen hacks (see below) properly force anamorphic output, but don't make the last projection in a frame 16:9, so Dolphin doesn't display it correctly. This changes the heuristic code to assume a frame is anamorphic based on the total number of vertex flushes in 4:3 and 16:9 projections that frame. It also adds a bit of "aspect ratio inertia" by making it harder to switch aspect ratios, which takes care of aspect ratio flickering that some games / widescreen hacks would be susceptible with the new logic. I've tested this on SSX Tricky's native anamorphic support, Tom Clancy's Splinter Cell (it stayed in 4:3 the whole time), and on the following widescreen hacks for which the heuristic doesn't currently work: Paper Mario: The Thousand-Year Door (Gecko widescreen code from Nintendont) C202F310 00000003 3DC08042 3DE03FD8 91EEF6D8 4E800020 60000000 00000000 04199598 4E800020 C200F500 00000004 3DE08082 3DC0402B 61CE12A2 91CFA1BC 60000000 387D015C 60000000 00000000 C200F508 00000004 3DE08082 3DC04063 61CEE8D3 91CFA1BC 60000000 7FC3F378 60000000 00000000 The Simpsons: Hit & Run (AR widescreen code from the wiki) 04004600 C002A604 04004604 C09F0014 04004608 FC002040 0400460C 4082000C 04004610 C002A608 04004614 EC630032 04004618 48220508 04041A5C 38600001 04224344 C002A60C 04224B1C 4BDDFAE4 044786B0 3FAAAAAB 04479F28 3FA33333 --- Source/Core/Core/BootManager.cpp | 4 +- Source/Core/Core/Core.cpp | 3 -- Source/Core/Core/Core.h | 3 -- Source/Core/VideoCommon/RenderBase.cpp | 36 ++++++++++++--- Source/Core/VideoCommon/RenderBase.h | 1 + Source/Core/VideoCommon/VertexManagerBase.cpp | 45 +++++++++++++++++++ Source/Core/VideoCommon/VertexManagerBase.h | 4 ++ Source/Core/VideoCommon/VertexShaderGen.cpp | 1 - .../Core/VideoCommon/VertexShaderManager.cpp | 29 ------------ 9 files changed, 80 insertions(+), 46 deletions(-) diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index 8d1e8375da..3a0eb452da 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -290,13 +290,11 @@ bool BootCore(const std::string& _rFilename) } } - Core::g_aspect_wide = StartUp.bWii; - // Wii settings if (StartUp.bWii) { IniFile::Section* wii_section = game_ini.GetOrCreateSection("Wii"); - wii_section->Get("Widescreen", &Core::g_aspect_wide, !!StartUp.m_wii_aspect_ratio); + wii_section->Get("Widescreen", &StartUp.m_wii_aspect_ratio, !!StartUp.m_wii_aspect_ratio); wii_section->Get("Language", &StartUp.m_wii_language, StartUp.m_wii_language); int source; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index ee9a396eae..d886c3bbdd 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -81,9 +81,6 @@ namespace Core { -// TODO: ugly, remove -bool g_aspect_wide; - static bool s_wants_determinism; // Declarations and definitions diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 291105c9ab..89bdedb25c 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -18,9 +18,6 @@ namespace Core { -// TODO: ugly, remove -extern bool g_aspect_wide; - bool GetIsThrottlerTempDisabled(); void SetIsThrottlerTempDisabled(bool disable); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 104af414cf..7ec8e3367f 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -55,6 +55,8 @@ #include "VideoCommon/Statistics.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureDecoder.h" +#include "VideoCommon/VertexManagerBase.h" +#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" @@ -89,6 +91,11 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) OSDChoice = 0; OSDTime = 0; + + if (SConfig::GetInstance().bWii) + { + m_aspect_wide = SConfig::GetInstance().m_wii_aspect_ratio != 0; + } } Renderer::~Renderer() @@ -447,7 +454,7 @@ float Renderer::CalculateDrawAspectRatio(int target_width, int target_height) // The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio if (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE || - (g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && Core::g_aspect_wide)) + (g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && m_aspect_wide)) { return (static_cast(target_width) / static_cast(target_height)) / AspectToWidescreen(VideoInterface::GetAspectRatio()); @@ -521,7 +528,7 @@ void Renderer::UpdateDrawRectangle() if (g_ActiveConfig.bWidescreenHack) { float source_aspect = VideoInterface::GetAspectRatio(); - if (Core::g_aspect_wide) + if (m_aspect_wide) source_aspect = AspectToWidescreen(source_aspect); float target_aspect; @@ -641,11 +648,10 @@ void Renderer::SetWindowSize(int width, int height) { // Force 4:3 or 16:9 by cropping the image. float current_aspect = scaled_width / scaled_height; - float expected_aspect = - (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE || - (g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && Core::g_aspect_wide)) ? - (16.0f / 9.0f) : - (4.0f / 3.0f); + float expected_aspect = (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE || + (g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && m_aspect_wide)) ? + (16.0f / 9.0f) : + (4.0f / 3.0f); if (current_aspect > expected_aspect) { // keep height, crop width @@ -711,6 +717,22 @@ void Renderer::RecordVideoMemory() void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks, float Gamma) { + // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. + if (!SConfig::GetInstance().bWii) + { + size_t flush_count_4_3, flush_count_anamorphic; + std::tie(flush_count_4_3, flush_count_anamorphic) = + g_vertex_manager->ResetFlushAspectRatioCount(); + 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; + } + // TODO: merge more generic parts into VideoCommon g_renderer->SwapImpl(xfbAddr, fbWidth, fbStride, fbHeight, rc, ticks, Gamma); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 93c22f6202..c6a71d9bd8 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -158,6 +158,7 @@ protected: Common::Event m_screenshot_completed; std::mutex m_screenshot_lock; std::string m_screenshot_name; + bool m_aspect_wide = false; // The framebuffer size int m_target_width = 0; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 2fd3affcaf..b08f311c0a 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -4,12 +4,14 @@ #include "VideoCommon/VertexManagerBase.h" +#include #include #include "Common/BitSet.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" +#include "Core/ConfigManager.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/DataReader.h" @@ -41,6 +43,23 @@ static const PrimitiveType primitive_from_gx[8] = { PRIMITIVE_POINTS, // GX_DRAW_POINTS }; +// Due to the BT.601 standard which the GameCube is based on being a compromise +// between PAL and NTSC, neither standard gets square pixels. They are each off +// by ~9% in opposite directions. +// Just in case any game decides to take this into account, we do both these +// tests with a large amount of slop. +static bool AspectIs4_3(float width, float height) +{ + float aspect = fabsf(width / height); + return fabsf(aspect - 4.0f / 3.0f) < 4.0f / 3.0f * 0.11; // within 11% of 4:3 +} + +static bool AspectIs16_9(float width, float height) +{ + float aspect = fabsf(width / height); + return fabsf(aspect - 16.0f / 9.0f) < 16.0f / 9.0f * 0.11; // within 11% of 16:9 +} + VertexManagerBase::VertexManagerBase() { } @@ -157,6 +176,14 @@ u32 VertexManagerBase::GetRemainingIndices(int primitive) } } +std::pair VertexManagerBase::ResetFlushAspectRatioCount() +{ + std::pair val = std::make_pair(m_flush_count_4_3, m_flush_count_anamorphic); + m_flush_count_4_3 = 0; + m_flush_count_anamorphic = 0; + return val; +} + void VertexManagerBase::Flush() { if (m_is_flushed) @@ -237,6 +264,24 @@ void VertexManagerBase::Flush() // set global vertex constants VertexShaderManager::SetConstants(); + // Track some stats used elsewhere by the anamorphic widescreen heuristic. + if (!SConfig::GetInstance().bWii) + { + float* rawProjection = xfmem.projection.rawProjection; + bool viewport_is_4_3 = AspectIs4_3(xfmem.viewport.wd, xfmem.viewport.ht); + if (AspectIs16_9(rawProjection[2], rawProjection[0]) && viewport_is_4_3) + { + // Projection is 16:9 and viewport is 4:3, we are rendering an anamorphic + // widescreen picture. + m_flush_count_anamorphic++; + } + else if (AspectIs4_3(rawProjection[2], rawProjection[0]) && viewport_is_4_3) + { + // Projection and viewports are both 4:3, we are rendering a normal image. + m_flush_count_4_3++; + } + } + // Calculate ZSlope for zfreeze if (!bpmem.genMode.zfreeze) { diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 20ebd9307d..dae639a47d 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -61,6 +61,8 @@ public: void DoState(PointerWrap& p); + std::pair ResetFlushAspectRatioCount(); + protected: virtual void vDoState(PointerWrap& p) {} PrimitiveType m_current_primitive_type = PrimitiveType::PRIMITIVE_POINTS; @@ -81,6 +83,8 @@ protected: private: bool m_is_flushed = true; + size_t m_flush_count_4_3 = 0; + size_t m_flush_count_anamorphic = 0; virtual void vFlush() = 0; diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp index b590303679..9fe4bfcada 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/VertexShaderGen.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include #include #include "Common/Assert.h" diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 76a91e0f05..8818c7f387 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -92,23 +92,6 @@ static float PHackValue(std::string sValue) return f; } -// Due to the BT.601 standard which the GameCube is based on being a compromise -// between PAL and NTSC, neither standard gets square pixels. They are each off -// by ~9% in opposite directions. -// Just in case any game decides to take this into account, we do both these -// tests with a large amount of slop. -static bool AspectIs4_3(float width, float height) -{ - float aspect = fabsf(width / height); - return fabsf(aspect - 4.0f / 3.0f) < 4.0f / 3.0f * 0.11; // within 11% of 4:3 -} - -static bool AspectIs16_9(float width, float height) -{ - float aspect = fabsf(width / height); - return fabsf(aspect - 16.0f / 9.0f) < 16.0f / 9.0f * 0.11; // within 11% of 16:9 -} - void UpdateProjectionHack(int iPhackvalue[], std::string sPhackvalue[]) { float fhackvalue1 = 0, fhackvalue2 = 0; @@ -470,18 +453,6 @@ void VertexShaderManager::SetConstants() g_fProjectionMatrix[14] = -1.0f; g_fProjectionMatrix[15] = 0.0f; - // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. - if (!SConfig::GetInstance().bWii) - { - bool viewport_is_4_3 = AspectIs4_3(xfmem.viewport.wd, xfmem.viewport.ht); - if (AspectIs16_9(rawProjection[2], rawProjection[0]) && viewport_is_4_3) - Core::g_aspect_wide = true; // Projection is 16:9 and viewport is 4:3, we are rendering - // an anamorphic widescreen picture - else if (AspectIs4_3(rawProjection[2], rawProjection[0]) && viewport_is_4_3) - Core::g_aspect_wide = - false; // Project and viewports are both 4:3, we are rendering a normal image. - } - SETSTAT_FT(stats.gproj_0, g_fProjectionMatrix[0]); SETSTAT_FT(stats.gproj_1, g_fProjectionMatrix[1]); SETSTAT_FT(stats.gproj_2, g_fProjectionMatrix[2]);