diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 470d303e21..780f8da7b9 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -717,6 +717,7 @@ + @@ -1306,6 +1307,7 @@ + diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 5428facc55..632120ee09 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -169,6 +169,8 @@ add_library(videocommon VideoConfig.h VideoState.cpp VideoState.h + Widescreen.cpp + Widescreen.h XFMemory.cpp XFMemory.h XFStructs.cpp diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index d5fa440417..0e7f6b96d9 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -14,11 +14,11 @@ #include "VideoCommon/FrameDumper.h" #include "VideoCommon/OnScreenUI.h" #include "VideoCommon/PostProcessing.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoEvents.h" +#include "VideoCommon/Widescreen.h" std::unique_ptr g_presenter; @@ -246,7 +246,7 @@ float Presenter::CalculateDrawAspectRatio() const const float aspect_ratio = VideoInterface::GetAspectRatio(); if (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) + (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) { return AspectToWidescreen(aspect_ratio); } @@ -346,7 +346,7 @@ std::tuple Presenter::ApplyStandardAspectCrop(float width, float h const float current_aspect = width / height; const float expected_aspect = (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) ? + (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) ? (16.0f / 9.0f) : (4.0f / 3.0f); if (current_aspect > expected_aspect) @@ -373,7 +373,7 @@ void Presenter::UpdateDrawRectangle() if (g_ActiveConfig.bWidescreenHack) { float source_aspect = VideoInterface::GetAspectRatio(); - if (g_renderer && g_renderer->IsGameWidescreen()) + if (g_widescreen->IsGameWidescreen()) source_aspect = AspectToWidescreen(source_aspect); const float adjust = source_aspect / draw_aspect_ratio; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index d324dacac6..e597ee1e82 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -26,19 +26,14 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" #include "Core/System.h" -#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" -#include "VideoCommon/BoundingBox.h" -#include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/Present.h" -#include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -48,15 +43,6 @@ std::unique_ptr g_renderer; Renderer::Renderer() : m_prev_efb_format{PixelFormat::INVALID_FMT} { - UpdateWidescreen(); - - m_config_changed_handle = - ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); - - // VertexManager doesn't maintain statistics in Wii mode. - if (!SConfig::GetInstance().bWii) - m_update_widescreen_handle = - AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } Renderer::~Renderer() = default; @@ -166,85 +152,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } -void Renderer::UpdateWidescreen() -{ - 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; - } -} - -// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. -void Renderer::UpdateWidescreenHeuristic() -{ - 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; - - UpdateWidescreen(); - - // 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; - - // 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& persp = flush_statistics.perspective; - const auto& ortho = flush_statistics.orthographic; - - const auto ortho_looks_anamorphic = looks_anamorphic(ortho); - - if (looks_anamorphic(persp) || ortho_looks_anamorphic) - { - // If either perspective or orthographic projections look anamorphic, it's a safe bet. - m_is_game_widescreen = true; - } - else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho))) - { - // 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 situation which was orthographically anamorphic - // we won't consider orthographic data for changes from 16:9 to 4:3. - m_is_game_widescreen = false; - } - - m_was_orthographically_anamorphic = ortho_looks_anamorphic; -} - -void Renderer::OnConfigChanged(u32 bits) -{ - if (bits & CONFIG_CHANGE_BIT_ASPECT_RATIO) - UpdateWidescreen(); -} - bool Renderer::UseVertexDepthRange() { // We can't compute the depth range in the vertex shader if we don't support depth clamp. @@ -266,10 +173,5 @@ bool Renderer::UseVertexDepthRange() void Renderer::DoState(PointerWrap& p) { - p.Do(m_is_game_widescreen); - - if (p.IsReadMode()) - { - m_was_orthographically_anamorphic = false; - } + p.Do(m_prev_efb_format); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index afdfffccb9..e2ff0d0a06 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -37,28 +37,14 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - bool IsGameWidescreen() const { return m_is_game_widescreen; } - PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } static bool UseVertexDepthRange(); void DoState(PointerWrap& p); - void OnConfigChanged(u32 bits); - -protected: - void UpdateWidescreen(); - void UpdateWidescreenHeuristic(); - - bool m_is_game_widescreen = false; - bool m_was_orthographically_anamorphic = false; - private: PixelFormat m_prev_efb_format; - - EventHook m_update_widescreen_handle; - EventHook m_config_changed_handle; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 3eeceb3c5e..95b58322d2 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -64,6 +64,7 @@ #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoState.h" +#include "VideoCommon/Widescreen.h" VideoBackendBase* g_video_backend = nullptr; @@ -359,6 +360,7 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, g_framebuffer_manager = std::make_unique(); g_shader_cache = std::make_unique(); g_graphics_mod_manager = std::make_unique(); + g_widescreen = std::make_unique(); auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); @@ -407,6 +409,7 @@ void VideoBackendBase::ShutdownShared() g_shader_cache.reset(); g_vertex_manager.reset(); g_renderer.reset(); + g_widescreen.reset(); g_presenter.reset(); g_gfx.reset(); diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 5aae09e354..45b06e4973 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -26,6 +26,7 @@ #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VertexShaderManager.h" +#include "VideoCommon/Widescreen.h" #include "VideoCommon/XFMemory.h" void VideoCommon_DoState(PointerWrap& p) @@ -105,6 +106,9 @@ void VideoCommon_DoState(PointerWrap& p) g_bounding_box->DoState(p); p.DoMarker("Bounding Box"); + g_widescreen->DoState(p); + p.DoMarker("Widescreen"); + // Refresh state. if (p.IsReadMode()) { diff --git a/Source/Core/VideoCommon/Widescreen.cpp b/Source/Core/VideoCommon/Widescreen.cpp new file mode 100644 index 0000000000..ac8fa2fcfd --- /dev/null +++ b/Source/Core/VideoCommon/Widescreen.cpp @@ -0,0 +1,113 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Widescreen.h" + +#include "Common/ChunkFile.h" +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigManager.h" + +#include "VideoCommon/VertexManagerBase.h" + +std::unique_ptr g_widescreen; + +WidescreenManager::WidescreenManager() +{ + Update(); + + m_config_changed = ConfigChangedEvent::Register([this](u32 bits) { + if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO)) + Update(); + }, "Widescreen"); + + // VertexManager doesn't maintain statistics in Wii mode. + if (!SConfig::GetInstance().bWii) + { + m_update_widescreen = + AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); + } +} + + +void WidescreenManager::Update() +{ + 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; + } +} + +// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. +void WidescreenManager::UpdateWidescreenHeuristic() +{ + 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; + + 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)) + return; + + // 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& persp = flush_statistics.perspective; + const auto& ortho = flush_statistics.orthographic; + + const auto ortho_looks_anamorphic = looks_anamorphic(ortho); + + if (looks_anamorphic(persp) || ortho_looks_anamorphic) + { + // If either perspective or orthographic projections look anamorphic, it's a safe bet. + m_is_game_widescreen = true; + } + else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho))) + { + // 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 situation which was orthographically anamorphic + // we won't consider orthographic data for changes from 16:9 to 4:3. + m_is_game_widescreen = false; + } + + m_was_orthographically_anamorphic = ortho_looks_anamorphic; +} + +void WidescreenManager::DoState(PointerWrap& p) +{ + p.Do(m_is_game_widescreen); + + if (p.IsReadMode()) + { + m_was_orthographically_anamorphic = false; + } +} \ No newline at end of file diff --git a/Source/Core/VideoCommon/Widescreen.h b/Source/Core/VideoCommon/Widescreen.h new file mode 100644 index 0000000000..437149cb54 --- /dev/null +++ b/Source/Core/VideoCommon/Widescreen.h @@ -0,0 +1,34 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + + +#include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" + +class PointerWrap; + +// This class is responsible for tracking the game's aspect ratio. +class WidescreenManager +{ +public: + WidescreenManager(); + + bool IsGameWidescreen() const { return m_is_game_widescreen; } + + void DoState(PointerWrap& p); +private: + void Update(); + void UpdateWidescreenHeuristic(); + + bool m_is_game_widescreen = false; + bool m_was_orthographically_anamorphic = false; + + EventHook m_update_widescreen; + EventHook m_config_changed; +}; + +extern std::unique_ptr g_widescreen; \ No newline at end of file