Widescreen.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright 2023 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/Widescreen.h"
  4. #include "Common/ChunkFile.h"
  5. #include "Common/Logging/Log.h"
  6. #include "Core/Config/SYSCONFSettings.h"
  7. #include "Core/System.h"
  8. #include "VideoCommon/Statistics.h"
  9. #include "VideoCommon/VertexManagerBase.h"
  10. std::unique_ptr<WidescreenManager> g_widescreen;
  11. WidescreenManager::WidescreenManager()
  12. {
  13. if (std::optional<bool> is_game_widescreen = GetWidescreenOverride())
  14. m_is_game_widescreen = *is_game_widescreen;
  15. // Throw a warning as unsupported aspect ratio modes have no specific behavior to them
  16. const bool is_valid_suggested_aspect_mode =
  17. g_ActiveConfig.suggested_aspect_mode == AspectMode::Auto ||
  18. g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceStandard ||
  19. g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceWide;
  20. if (!is_valid_suggested_aspect_mode)
  21. {
  22. WARN_LOG_FMT(VIDEO,
  23. "Invalid suggested aspect ratio mode: only Auto, 4:3 and 16:9 are supported");
  24. }
  25. m_config_changed = ConfigChangedEvent::Register(
  26. [this](u32 bits) {
  27. if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO))
  28. {
  29. // If the widescreen flag isn't being overridden by any settings,
  30. // reset it to default if heuristic aren't running or to the last
  31. // heuristic value if they were running.
  32. if (std::optional<bool> is_game_widescreen = GetWidescreenOverride())
  33. m_is_game_widescreen = *is_game_widescreen;
  34. else
  35. m_is_game_widescreen = (m_heuristic_state == HeuristicState::Active_Found_Anamorphic);
  36. }
  37. },
  38. "Widescreen");
  39. // VertexManager doesn't maintain statistics in Wii mode.
  40. auto& system = Core::System::GetInstance();
  41. if (!system.IsWii())
  42. {
  43. m_update_widescreen = AfterFrameEvent::Register(
  44. [this](Core::System&) { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic");
  45. }
  46. }
  47. std::optional<bool> WidescreenManager::GetWidescreenOverride() const
  48. {
  49. std::optional<bool> is_game_widescreen;
  50. auto& system = Core::System::GetInstance();
  51. if (system.IsWii())
  52. is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
  53. // suggested_aspect_mode overrides SYSCONF_WIDESCREEN
  54. if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceStandard)
  55. is_game_widescreen = false;
  56. else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::ForceWide)
  57. is_game_widescreen = true;
  58. // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9.
  59. if (!g_ActiveConfig.bWidescreenHack)
  60. {
  61. const auto aspect_mode = g_ActiveConfig.aspect_mode;
  62. if (aspect_mode == AspectMode::ForceStandard)
  63. is_game_widescreen = false;
  64. else if (aspect_mode == AspectMode::ForceWide)
  65. is_game_widescreen = true;
  66. }
  67. return is_game_widescreen;
  68. }
  69. // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode.
  70. // Cheats that change the game aspect ratio to natively unsupported ones won't be recognized here.
  71. void WidescreenManager::UpdateWidescreenHeuristic()
  72. {
  73. // Reset to baseline state before the update
  74. const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount();
  75. const bool was_orthographically_anamorphic = m_was_orthographically_anamorphic;
  76. m_heuristic_state = HeuristicState::Inactive;
  77. m_was_orthographically_anamorphic = false;
  78. // If suggested_aspect_mode (GameINI) is configured don't use heuristic.
  79. // We also don't need to check "GetWidescreenOverride()" in this case as
  80. // nothing would have changed there.
  81. if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto)
  82. return;
  83. std::optional<bool> is_game_widescreen = GetWidescreenOverride();
  84. // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic.
  85. if (g_ActiveConfig.bWidescreenHack || (g_ActiveConfig.aspect_mode != AspectMode::ForceStandard &&
  86. g_ActiveConfig.aspect_mode != AspectMode::ForceWide))
  87. {
  88. // Modify the threshold based on which aspect ratio we're already using:
  89. // If the game's in 4:3, it probably won't switch to anamorphic, and vice-versa.
  90. const u32 transition_threshold = g_ActiveConfig.widescreen_heuristic_transition_threshold;
  91. const auto looks_normal = [transition_threshold](auto& counts) {
  92. return counts.normal_vertex_count > counts.anamorphic_vertex_count * transition_threshold;
  93. };
  94. const auto looks_anamorphic = [transition_threshold](auto& counts) {
  95. return counts.anamorphic_vertex_count > counts.normal_vertex_count * transition_threshold;
  96. };
  97. const auto& persp = flush_statistics.perspective;
  98. const auto& ortho = flush_statistics.orthographic;
  99. g_stats.avg_persp_proj_viewport_ratio = persp.average_ratio.Mean();
  100. g_stats.avg_ortho_proj_viewport_ratio = ortho.average_ratio.Mean();
  101. const auto ortho_looks_anamorphic = looks_anamorphic(ortho);
  102. const auto persp_looks_normal = looks_normal(persp);
  103. if (looks_anamorphic(persp) || ortho_looks_anamorphic)
  104. {
  105. // If either perspective or orthographic projections look anamorphic, it's a safe bet.
  106. is_game_widescreen = true;
  107. m_heuristic_state = HeuristicState::Active_Found_Anamorphic;
  108. }
  109. else if (persp_looks_normal || looks_normal(ortho))
  110. {
  111. // Many widescreen games (or AR/GeckoCodes) use anamorphic perspective projections
  112. // with NON-anamorphic orthographic projections.
  113. // This can cause incorrect changes to 4:3 when perspective projections are temporarily not
  114. // shown. e.g. Animal Crossing's inventory menu.
  115. // Unless we were in a situation which was orthographically anamorphic
  116. // we won't consider orthographic data for changes from 16:9 to 4:3.
  117. if (persp_looks_normal || was_orthographically_anamorphic)
  118. is_game_widescreen = false;
  119. m_heuristic_state = HeuristicState::Active_Found_Normal;
  120. }
  121. else
  122. {
  123. m_heuristic_state = HeuristicState::Active_NotFound;
  124. }
  125. m_was_orthographically_anamorphic = ortho_looks_anamorphic;
  126. }
  127. if (is_game_widescreen.has_value())
  128. m_is_game_widescreen = is_game_widescreen.value();
  129. }
  130. void WidescreenManager::DoState(PointerWrap& p)
  131. {
  132. p.Do(m_is_game_widescreen);
  133. if (p.IsReadMode())
  134. {
  135. m_was_orthographically_anamorphic = false;
  136. m_heuristic_state = HeuristicState::Inactive;
  137. }
  138. }