From f289b06e0db8bba6a3491d6e3cf0c2dd1b7a29fa Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 3 May 2025 02:48:44 -0500 Subject: [PATCH] Common: Make HookableEvent use non-static data. Co-authored-by: Dentomologist --- Source/Core/Common/HookableEvent.h | 151 ++++++++++-------- Source/Core/Core/AchievementManager.cpp | 54 ++++--- Source/Core/Core/AchievementManager.h | 4 +- Source/Core/Core/Core.cpp | 8 +- Source/Core/Core/FifoPlayer/FifoRecorder.cpp | 2 +- Source/Core/Core/HW/VideoInterface.cpp | 2 +- Source/Core/Core/System.cpp | 13 ++ Source/Core/Core/System.h | 3 + .../Achievements/AchievementsWindow.cpp | 2 +- Source/Core/DolphinQt/CheatsManager.cpp | 4 +- .../Core/DolphinQt/Debugger/MemoryWidget.cpp | 3 +- Source/Core/DolphinQt/MenuBar.cpp | 2 +- Source/Core/UICommon/UICommon.cpp | 4 + Source/Core/VideoCommon/AbstractGfx.cpp | 4 +- .../Assets/CustomResourceManager.cpp | 4 +- Source/Core/VideoCommon/BPStructs.cpp | 6 +- Source/Core/VideoCommon/FrameDumper.cpp | 4 +- .../Core/VideoCommon/FramebufferManager.cpp | 4 +- .../Runtime/CustomShaderCache.cpp | 4 +- .../Runtime/GraphicsModManager.cpp | 4 +- Source/Core/VideoCommon/Present.cpp | 18 ++- Source/Core/VideoCommon/ShaderCache.cpp | 4 +- Source/Core/VideoCommon/Statistics.cpp | 36 +++-- Source/Core/VideoCommon/Statistics.h | 3 + Source/Core/VideoCommon/TextureCacheBase.h | 4 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 10 +- Source/Core/VideoCommon/VideoConfig.cpp | 14 +- Source/Core/VideoCommon/VideoConfig.h | 1 + Source/Core/VideoCommon/VideoEvents.h | 58 ++++--- Source/Core/VideoCommon/Widescreen.cpp | 8 +- 30 files changed, 263 insertions(+), 175 deletions(-) diff --git a/Source/Core/Common/HookableEvent.h b/Source/Core/Common/HookableEvent.h index 987880de98a..e2145e57e19 100644 --- a/Source/Core/Common/HookableEvent.h +++ b/Source/Core/Common/HookableEvent.h @@ -3,113 +3,138 @@ #pragma once -#include "Common/Logging/Log.h" -#include "Common/StringLiteral.h" - #include #include #include -#include #include #include +#if defined(_DEBUG) +#include + +#include "Common/Logging/Log.h" +#endif + namespace Common { struct HookBase { - virtual ~HookBase() = default; - -protected: - HookBase() = default; - - // This shouldn't be copied. And since we always wrap it in unique_ptr, no need to move it either - HookBase(const HookBase&) = delete; - HookBase(HookBase&&) = delete; - HookBase& operator=(const HookBase&) = delete; - HookBase& operator=(HookBase&&) = delete; + // A pure virtual destructor makes this class abstract to prevent accidental "slicing". + virtual ~HookBase() = 0; }; +inline HookBase::~HookBase() = default; // EventHook is a handle a registered listener holds. // When the handle is destroyed, the HookableEvent will automatically remove the listener. +// If the handle outlives the HookableEvent, the link will be properly disconnected. using EventHook = std::unique_ptr; // A hookable event system. // -// Define Events in a header as: +// Define Events as: // -// using MyLoveyEvent = HookableEvent<"My lovely event", std::string, u32>; +// HookableEvent my_lovely_event{"My lovely event"}; // // Register listeners anywhere you need them as: -// EventHook myHook = MyLoveyEvent::Register([](std::string foo, u32 bar) { +// EventHook my_hook = my_lovely_event.Register([](std::string foo, u32 bar) { // fmt::print("I've been triggered with {} and {}", foo, bar) // }, "NameOfHook"); // // The hook will be automatically unregistered when the EventHook object goes out of scope. // Trigger events by calling Trigger as: // -// MyLoveyEvent::Trigger("Hello world", 42); +// my_lovely_event.Trigger("Hello world", 42); // -template + +template class HookableEvent { public: using CallbackType = std::function; -private: - struct HookImpl final : public HookBase + explicit HookableEvent(std::string_view event_name) + : m_storage{std::make_shared(event_name)} { - ~HookImpl() override { HookableEvent::Remove(this); } - HookImpl(CallbackType callback, std::string name) - : m_fn(std::move(callback)), m_name(std::move(name)) - { - } - CallbackType m_fn; - std::string m_name; - }; - - struct Storage - { - std::recursive_mutex m_mutex; - std::vector m_listeners; - }; - - // We use the "Construct On First Use" idiom to avoid the static initialization order fiasco. - // https://isocpp.org/wiki/faq/ctors#static-init-order - static Storage& GetStorage() - { - static Storage storage; - return storage; } - static void Remove(HookImpl* handle) - { - auto& storage = GetStorage(); - std::lock_guard lock(storage.m_mutex); - - std::erase(storage.m_listeners, handle); - } - -public: // Returns a handle that will unregister the listener when destroyed. - [[nodiscard]] static EventHook Register(CallbackType callback, std::string name) + // Note: Attempting to add/remove hooks of the event within the callback itself will NOT work. + [[nodiscard]] EventHook Register(CallbackType callback, std::string_view name) { - auto& storage = GetStorage(); - std::lock_guard lock(storage.m_mutex); +#if defined(_DEBUG) + DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, m_storage->event_name); +#endif + auto handle = std::make_unique(m_storage, std::move(callback), name); - DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); - auto handle = std::make_unique(std::move(callback), std::move(name)); - storage.m_listeners.push_back(handle.get()); + std::lock_guard lg(m_storage->listeners_mutex); + m_storage->listeners.push_back(handle.get()); return handle; } - static void Trigger(const CallbackArgs&... args) + void Trigger(const CallbackArgs&... args) { - auto& storage = GetStorage(); - std::lock_guard lock(storage.m_mutex); - - for (const auto& handle : storage.m_listeners) - handle->m_fn(args...); + std::lock_guard lg(m_storage->listeners_mutex); + for (auto* const handle : m_storage->listeners) + std::invoke(handle->callback, args...); } + +private: + struct HookImpl; + + struct Storage + { + explicit Storage(std::string_view name [[maybe_unused]]) + { +#if defined(_DEBUG) + event_name = std::string(name); +#endif + } + + std::mutex listeners_mutex; + std::vector listeners; +#if defined(_DEBUG) + std::string event_name; +#endif + }; + + struct HookImpl final : HookBase + { + HookImpl(const std::shared_ptr storage, CallbackType func, + std::string_view name [[maybe_unused]]) + : weak_storage{storage}, callback{std::move(func)} + { +#if defined(_DEBUG) + hook_name = std::string(name); +#endif + } + + ~HookImpl() override + { + const auto storage = weak_storage.lock(); + if (storage == nullptr) + { +#if defined(_DEBUG) + DEBUG_LOG_FMT(COMMON, "Handler {} outlived event hook", hook_name); +#endif + return; + } + +#if defined(_DEBUG) + DEBUG_LOG_FMT(COMMON, "Removing handler {} of event hook {}", hook_name, storage->event_name); +#endif + std::lock_guard lg(storage->listeners_mutex); + std::erase(storage->listeners, this); + } + + std::weak_ptr weak_storage; + const CallbackType callback; +#if defined(_DEBUG) + std::string hook_name; +#endif + }; + + // shared_ptr storage allows hooks to forget their connection if they outlive the event itself. + std::shared_ptr m_storage; }; } // namespace Common diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 6f093d81816..e08593e6968 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -344,7 +344,7 @@ void AchievementManager::DoFrame() { m_last_rp_time = current_time; rc_client_get_rich_presence_message(m_client, m_rich_presence.data(), RP_SIZE); - UpdateEvent::Trigger(UpdatedItems{.rich_presence = true}); + update_event.Trigger(UpdatedItems{.rich_presence = true}); if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED)) Discord::UpdateDiscordPresence(); } @@ -769,7 +769,7 @@ void AchievementManager::CloseGame() INFO_LOG_FMT(ACHIEVEMENTS, "Game closed."); } - UpdateEvent::Trigger(UpdatedItems{.all = true}); + update_event.Trigger(UpdatedItems{.all = true}); } void AchievementManager::Logout() @@ -783,7 +783,7 @@ void AchievementManager::Logout() Config::SetBaseOrCurrent(Config::RA_API_TOKEN, ""); } - UpdateEvent::Trigger(UpdatedItems{.all = true}); + update_event.Trigger(UpdatedItems{.all = true}); INFO_LOG_FMT(ACHIEVEMENTS, "Logged out from server."); } @@ -922,23 +922,24 @@ void AchievementManager::LoadDefaultBadges() void AchievementManager::LoginCallback(int result, const char* error_message, rc_client_t* client, void* userdata) { + auto& instance = AchievementManager::GetInstance(); if (result != RC_OK) { WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.", Config::Get(Config::RA_USERNAME)); - UpdateEvent::Trigger({.failed_login_code = result}); + instance.update_event.Trigger({.failed_login_code = result}); return; } const rc_client_user_t* user; { - std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; + std::lock_guard lg{instance.GetLock()}; user = rc_client_get_user_info(client); } if (!user) { WARN_LOG_FMT(ACHIEVEMENTS, "Failed to retrieve user information from client."); - UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE}); + instance.update_event.Trigger({.failed_login_code = RC_INVALID_STATE}); return; } @@ -957,15 +958,15 @@ void AchievementManager::LoginCallback(int result, const char* error_message, rc INFO_LOG_FMT(ACHIEVEMENTS, "Attempted to login prior user {}; current user is {}.", user->username, Config::Get(Config::RA_USERNAME)); rc_client_logout(client); - UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE}); + instance.update_event.Trigger({.failed_login_code = RC_INVALID_STATE}); return; } } INFO_LOG_FMT(ACHIEVEMENTS, "Successfully logged in {} to RetroAchievements server.", user->username); - std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; + std::lock_guard lg{instance.GetLock()}; Config::SetBaseOrCurrent(Config::RA_API_TOKEN, user->token); - AchievementManager::GetInstance().FetchPlayerBadge(); + instance.FetchPlayerBadge(); } void AchievementManager::FetchBoardInfo(AchievementId leaderboard_id) @@ -982,6 +983,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* userdata) { + auto& instance = AchievementManager::GetInstance(); u32* leaderboard_id = static_cast(userdata); Common::ScopeGuard on_end_scope([&] { delete leaderboard_id; }); @@ -991,10 +993,10 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro return; } - auto& leaderboard = AchievementManager::GetInstance().m_leaderboard_map[*leaderboard_id]; + auto& leaderboard = instance.m_leaderboard_map[*leaderboard_id]; for (size_t ix = 0; ix < list->num_entries; ix++) { - std::lock_guard lg{AchievementManager::GetInstance().GetLock()}; + std::lock_guard lg{instance.GetLock()}; const auto& response_entry = list->entries[ix]; auto& map_entry = leaderboard.entries[response_entry.index]; map_entry.username.assign(response_entry.user); @@ -1003,7 +1005,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro if (static_cast(ix) == list->user_index) leaderboard.player_index = response_entry.rank; } - UpdateEvent::Trigger({.leaderboards = {*leaderboard_id}}); + instance.update_event.Trigger({.leaderboards = {*leaderboard_id}}); } void AchievementManager::LoadGameCallback(int result, const char* error_message, @@ -1049,7 +1051,7 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message, rc_client_set_read_memory_function(instance.m_client, MemoryPeeker); instance.FetchGameBadges(); instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release); - UpdateEvent::Trigger({.all = true}); + instance.update_event.Trigger({.all = true}); // Set this to a value that will immediately trigger RP instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2}; @@ -1129,7 +1131,7 @@ void AchievementManager::DisplayWelcomeMessage() void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event) { - const auto& instance = AchievementManager::GetInstance(); + auto& instance = AchievementManager::GetInstance(); OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title, client_event->achievement->points), @@ -1137,7 +1139,7 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t (rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW : OSD::Color::CYAN, &instance.GetAchievementBadge(client_event->achievement->id, false)); - UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}}); + instance.update_event.Trigger(UpdatedItems{.achievements = {client_event->achievement->id}}); #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION switch (rc_client_raintegration_get_achievement_state(instance.m_client, client_event->achievement->id)) @@ -1181,12 +1183,13 @@ void AchievementManager::HandleLeaderboardFailedEvent(const rc_client_event_t* c void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t* client_event) { + auto& instance = AchievementManager::GetInstance(); OSD::AddMessage(fmt::format("Scored {} on leaderboard: {}", client_event->leaderboard->tracker_value, client_event->leaderboard->title), OSD::Duration::VERY_LONG, OSD::Color::YELLOW); - AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id); - UpdateEvent::Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}}); + instance.FetchBoardInfo(client_event->leaderboard->id); + instance.update_event.Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}}); } void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event) @@ -1223,7 +1226,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorShowEvent( const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id); if (inserted) instance.m_challenges_updated = true; - UpdateEvent::Trigger(UpdatedItems{.rich_presence = true}); + instance.update_event.Trigger(UpdatedItems{.rich_presence = true}); } void AchievementManager::HandleAchievementChallengeIndicatorHideEvent( @@ -1233,7 +1236,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorHideEvent( const auto removed = instance.m_active_challenges.erase(client_event->achievement->id); if (removed > 0) instance.m_challenges_updated = true; - UpdateEvent::Trigger(UpdatedItems{.rich_presence = true}); + instance.update_event.Trigger(UpdatedItems{.rich_presence = true}); } void AchievementManager::HandleAchievementProgressIndicatorShowEvent( @@ -1249,7 +1252,7 @@ void AchievementManager::HandleAchievementProgressIndicatorShowEvent( OSD::Duration::SHORT, OSD::Color::GREEN, &instance.GetAchievementBadge(client_event->achievement->id, false)); instance.m_last_progress_message = current_time; - UpdateEvent::Trigger(UpdatedItems{.achievements = {client_event->achievement->id}}); + instance.update_event.Trigger(UpdatedItems{.achievements = {client_event->achievement->id}}); } void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event, @@ -1388,7 +1391,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_ { if (!m_client || !HasAPIToken()) { - UpdateEvent::Trigger(callback_data); + update_event.Trigger(callback_data); if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME) DisplayWelcomeMessage(); return; @@ -1434,7 +1437,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_ "RetroAchievements connection failed on image request.\n URL: {}", api_request.url); rc_api_destroy_request(&api_request); - UpdateEvent::Trigger(callback_data); + update_event.Trigger(callback_data); return; } @@ -1467,7 +1470,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_ } *badge = std::move(tmp_badge); - UpdateEvent::Trigger(callback_data); + update_event.Trigger(callback_data); if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT && m_active_challenges.contains(*callback_data.achievements.begin())) { @@ -1543,7 +1546,7 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m rc_client_raintegration_set_event_handler(instance.m_client, RAIntegrationEventHandler); rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker); rc_client_raintegration_set_get_game_name_function(instance.m_client, GameTitleEstimateHandler); - DevMenuUpdateEvent::Trigger(); + instance.dev_menu_update_event.Trigger(); // TODO: hook up menu and dll event handlers break; @@ -1565,11 +1568,12 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event, rc_client_t* client) { + auto& instance = AchievementManager::GetInstance(); switch (event->type) { case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED: case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED: - DevMenuUpdateEvent::Trigger(); + instance.dev_menu_update_event.Trigger(); break; case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE: { diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 625a4fb7180..c1734cbcd3e 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -120,7 +120,7 @@ public: bool rich_presence = false; int failed_login_code = 0; }; - using UpdateEvent = Common::HookableEvent<"AchievementManagerUpdate", const UpdatedItems&>; + Common::HookableEvent update_event{"AchievementManagerUpdate"}; static AchievementManager& GetInstance(); void Init(void* hwnd); @@ -174,7 +174,7 @@ public: std::vector GetActiveLeaderboards() const; #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION - using DevMenuUpdateEvent = Common::HookableEvent<"AchievementManagerDevMenuUpdate">; + Common::HookableEvent<> dev_menu_update_event{"AchievementManagerDevMenuUpdate"}; const rc_client_raintegration_menu_t* GetDevelopmentMenu(); u32 ActivateDevMenuItem(u32 menu_item_id); bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); } diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 61664e2202b..1892e0b3ea5 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -113,7 +113,7 @@ static std::atomic s_state = State::Uninitialized; static std::unique_ptr s_memory_watcher; #endif -void Callback_FramePresented(const PresentInfo& present_info); +static void Callback_FramePresented(const PresentInfo& present_info); struct HostJob { @@ -131,9 +131,6 @@ static thread_local bool tls_is_host_thread = false; static void EmuThread(Core::System& system, std::unique_ptr boot, WindowSystemInfo wsi); -static Common::EventHook s_frame_presented = - AfterPresentEvent::Register(&Core::Callback_FramePresented, "Core Frame Presented"); - bool GetIsThrottlerTempDisabled() { return s_is_throttler_temp_disabled; @@ -661,6 +658,9 @@ static void EmuThread(Core::System& system, std::unique_ptr boot // This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block. system.GetFifo().Prepare(); + const Common::EventHook frame_presented = GetVideoEvents().after_present_event.Register( + &Core::Callback_FramePresented, "Core Frame Presented"); + // Setup our core if (Config::Get(Config::MAIN_CPU_CORE) != PowerPC::CPUCore::Interpreter) { diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp index 04236c46513..7cc22f42be5 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp @@ -250,7 +250,7 @@ void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; - m_end_of_frame_event = AfterFrameEvent::Register( + m_end_of_frame_event = m_system.GetVideoEvents().after_frame_event.Register( [this](const Core::System& system) { const bool was_recording = OpcodeDecoder::g_record_fifo_data; OpcodeDecoder::g_record_fifo_data = IsRecording(); diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index 788d5295ccf..c0f46bed358 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -894,7 +894,7 @@ void VideoInterfaceManager::EndField(FieldType field, u64 ticks) m_system.GetCoreTiming().Throttle(ticks); g_perf_metrics.CountVBlank(); - VIEndFieldEvent::Trigger(); + m_system.GetVideoEvents().vi_end_field_event.Trigger(); Core::OnFrameEnd(m_system); } diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 0170c5fd111..22681cac430 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -59,6 +59,9 @@ struct System::Impl { } + // Built first since other constructors may register hooks right away. + VideoEvents m_video_events; + std::unique_ptr m_sound_stream; bool m_sound_stream_running = false; bool m_audio_dump_started = false; @@ -339,4 +342,14 @@ VideoCommon::CustomResourceManager& System::GetCustomResourceManager() const { return m_impl->m_custom_resource_manager; } + +VideoEvents& System::GetVideoEvents() const +{ + return m_impl->m_video_events; +} } // namespace Core + +VideoEvents& GetVideoEvents() +{ + return Core::System::GetInstance().GetVideoEvents(); +} diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 348f4fcb4a4..e9fdec092d1 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -5,6 +5,8 @@ #include +#include "VideoCommon/VideoEvents.h" + class GeometryShaderManager; class Interpreter; class JitInterface; @@ -198,6 +200,7 @@ public: XFStateManager& GetXFStateManager() const; VideoInterface::VideoInterfaceManager& GetVideoInterface() const; VideoCommon::CustomResourceManager& GetCustomResourceManager() const; + VideoEvents& GetVideoEvents() const; private: System(); diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp index 1aadb12020c..43218c47fd6 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp @@ -31,7 +31,7 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent) CreateMainLayout(); ConnectWidgets(); - m_event_hook = AchievementManager::UpdateEvent::Register( + m_event_hook = AchievementManager::GetInstance().update_event.Register( [this](AchievementManager::UpdatedItems updated_items) { QueueOnObject(this, [this, updated_items = std::move(updated_items)] { AchievementsWindow::UpdateData(std::move(updated_items)); diff --git a/Source/Core/DolphinQt/CheatsManager.cpp b/Source/Core/DolphinQt/CheatsManager.cpp index ecaf4fe2f45..e3dd6392934 100644 --- a/Source/Core/DolphinQt/CheatsManager.cpp +++ b/Source/Core/DolphinQt/CheatsManager.cpp @@ -9,6 +9,7 @@ #include "Core/CheatSearch.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "Core/System.h" #include "DolphinQt/QtUtils/QtUtils.h" #include "DolphinQt/QtUtils/WrapInScrollArea.h" @@ -78,7 +79,8 @@ void CheatsManager::UpdateAllCheatSearchWidgetCurrentValues() void CheatsManager::RegisterAfterFrameEventCallback() { - m_VI_end_field_event = VIEndFieldEvent::Register([this] { OnFrameEnd(); }, "CheatsManager"); + m_VI_end_field_event = m_system.GetVideoEvents().vi_end_field_event.Register( + [this] { OnFrameEnd(); }, "CheatsManager"); } void CheatsManager::RemoveAfterFrameEventCallback() diff --git a/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp b/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp index 212bc491852..b088eeda022 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp @@ -429,7 +429,8 @@ void MemoryWidget::hideEvent(QHideEvent* event) void MemoryWidget::RegisterAfterFrameEventCallback() { - m_vi_end_field_event = VIEndFieldEvent::Register([this] { AutoUpdateTable(); }, "MemoryWidget"); + m_vi_end_field_event = m_system.GetVideoEvents().vi_end_field_event.Register( + [this] { AutoUpdateTable(); }, "MemoryWidget"); } void MemoryWidget::RemoveAfterFrameEventCallback() diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 152bfa648c9..f4d69f41ced 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -295,7 +295,7 @@ void MenuBar::AddToolsMenu() tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); }); #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION m_achievements_dev_menu = tools_menu->addMenu(tr("RetroAchievements Development")); - m_raintegration_event_hook = AchievementManager::DevMenuUpdateEvent::Register( + m_raintegration_event_hook = AchievementManager::GetInstance().dev_menu_update_event.Register( [this] { QueueOnObject(this, [this] { UpdateAchievementDevelopmentMenu(); }); }, "MenuBar"); m_achievements_dev_menu->menuAction()->setVisible(false); #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 40f31a231da..60077bfcd53 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -50,6 +50,7 @@ #include "UICommon/DiscordPresence.h" +#include "VideoCommon/Statistics.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" @@ -138,9 +139,11 @@ void Init() s_config_changed_callback_id = Config::AddConfigChangedCallback(config_changed_callback); Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader()); SConfig::Init(); + g_Config.Init(); Discord::Init(); Common::Log::LogManager::Init(); VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND)); + Statistics::Init(); RefreshConfig(); } @@ -149,6 +152,7 @@ void Shutdown() { Config::RemoveConfigChangedCallback(s_config_changed_callback_id); + Statistics::Shutdown(); GCAdapter::Shutdown(); WiimoteReal::Shutdown(); Common::Log::LogManager::Shutdown(); diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp index 90b85ddffff..ca6ad881b14 100644 --- a/Source/Core/VideoCommon/AbstractGfx.cpp +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -18,8 +18,8 @@ std::unique_ptr g_gfx; AbstractGfx::AbstractGfx() { - m_config_changed = - ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "AbstractGfx"); + m_config_changed = GetVideoEvents().config_changed_event.Register( + [this](u32 bits) { OnConfigChanged(bits); }, "AbstractGfx"); } bool AbstractGfx::IsHeadless() const diff --git a/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp b/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp index f2af4ccbeac..ac8199ef863 100644 --- a/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp +++ b/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp @@ -29,8 +29,8 @@ void CustomResourceManager::Initialize() m_asset_loader.Initialize(); - m_xfb_event = - AfterFrameEvent::Register([this](Core::System&) { XFBTriggered(); }, "CustomResourceManager"); + m_xfb_event = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { XFBTriggered(); }, "CustomResourceManager"); } void CustomResourceManager::Shutdown() diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index dd0cd59570d..1ece9f278ee 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -4,7 +4,6 @@ #include "VideoCommon/BPStructs.h" #include -#include #include #include @@ -341,6 +340,8 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top, bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients()); + auto& system = Core::System::GetInstance(); + // This is as closest as we have to an "end of the frame" // It works 99% of the time. // But sometimes games want to render an XFB larger than the EFB's 640x528 pixel resolution @@ -348,7 +349,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& // render multiple sub-frames and arrange the XFB copies in next to each-other in main memory // so they form a single completed XFB. // See https://dolphin-emu.org/blog/2017/11/19/hybridxfb/ for examples and more detail. - AfterFrameEvent::Trigger(Core::System::GetInstance()); + system.GetVideoEvents().after_frame_event.Trigger(system); // Note: Theoretically, in the future we could track the VI configuration and try to detect // when an XFB is the last XFB copy of a frame. Not only would we get a clean "end of @@ -356,7 +357,6 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& // Might also clean up some issues with games doing XFB copies they don't intend to // display. - auto& system = Core::System::GetInstance(); if (g_ActiveConfig.bImmediateXFB) { // TODO: GetTicks is not sane from the GPU thread. diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index c98141e8eb5..f39424805ce 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -29,8 +29,8 @@ static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) FrameDumper::FrameDumper() { - m_frame_end_handle = - AfterFrameEvent::Register([this](Core::System&) { FlushFrameDump(); }, "FrameDumper"); + m_frame_end_handle = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { FlushFrameDump(); }, "FrameDumper"); } FrameDumper::~FrameDumper() diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 5a3c768311b..fc514811d59 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -84,8 +84,8 @@ bool FramebufferManager::Initialize() return false; } - m_end_of_frame_event = - AfterFrameEvent::Register([this](Core::System&) { EndOfFrame(); }, "FramebufferManager"); + m_end_of_frame_event = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { EndOfFrame(); }, "FramebufferManager"); return true; } diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp index a0dca15d382..0bf8e844ec5 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp @@ -17,8 +17,8 @@ CustomShaderCache::CustomShaderCache() m_async_uber_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); m_async_uber_shader_compiler->StartWorkerThreads(1); // TODO - m_frame_end_handler = AfterFrameEvent::Register([this](Core::System&) { RetrieveAsyncShaders(); }, - "RetrieveAsyncShaders"); + m_frame_end_handler = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { RetrieveAsyncShaders(); }, "RetrieveAsyncShaders"); } CustomShaderCache::~CustomShaderCache() diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp index 70d3a9a5ca8..6a523c61a2f 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp @@ -95,8 +95,8 @@ bool GraphicsModManager::Initialize() g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes); g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); - m_end_of_frame_event = - AfterFrameEvent::Register([this](Core::System&) { EndOfFrame(); }, "ModManager"); + m_end_of_frame_event = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { EndOfFrame(); }, "ModManager"); } return true; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 43f1c8140fe..24c767cc693 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -94,8 +94,8 @@ static void TryToSnapToXFBSize(int& width, int& height, int xfb_width, int xfb_h Presenter::Presenter() { - m_config_changed = - ConfigChangedEvent::Register([this](u32 bits) { ConfigChanged(bits); }, "Presenter"); + m_config_changed = GetVideoEvents().config_changed_event.Register( + [this](u32 bits) { ConfigChanged(bits); }, "Presenter"); } Presenter::~Presenter() @@ -195,14 +195,16 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, } } - BeforePresentEvent::Trigger(present_info); + auto& video_events = GetVideoEvents(); + + video_events.before_present_event.Trigger(present_info); if (!is_duplicate || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) { Present(presentation_time); ProcessFrameDumping(ticks); - AfterPresentEvent::Trigger(present_info); + video_events.after_present_event.Trigger(present_info); } } @@ -216,12 +218,14 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; - BeforePresentEvent::Trigger(present_info); + auto& video_events = GetVideoEvents(); + + video_events.before_present_event.Trigger(present_info); Present(); ProcessFrameDumping(ticks); - AfterPresentEvent::Trigger(present_info); + video_events.after_present_event.Trigger(present_info); } void Presenter::ProcessFrameDumping(u64 ticks) const @@ -925,7 +929,7 @@ void Presenter::DoState(PointerWrap& p) if (p.IsReadMode() && m_last_xfb_stride != 0) { // This technically counts as the end of the frame - AfterFrameEvent::Trigger(Core::System::GetInstance()); + GetVideoEvents().after_frame_event.Trigger(Core::System::GetInstance()); ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height, m_last_xfb_ticks); diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 44868808ddc..fbed022aa5c 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -46,8 +46,8 @@ bool ShaderCache::Initialize() return false; m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); - m_frame_end_handler = AfterFrameEvent::Register([this](Core::System&) { RetrieveAsyncShaders(); }, - "RetrieveAsyncShaders"); + m_frame_end_handler = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { RetrieveAsyncShaders(); }, "RetrieveAsyncShaders"); return true; } diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index db098db5c7f..97c9264d184 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -20,18 +20,8 @@ Statistics g_stats; -static Common::EventHook s_before_frame_event = - BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame"); - -static Common::EventHook s_after_frame_event = AfterFrameEvent::Register( - [](const Core::System& system) { - DolphinAnalytics::Instance().ReportPerformanceInfo({ - .speed_ratio = system.GetSystemTimers().GetEstimatedEmulationPerformance(), - .num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims, - .num_draw_calls = g_stats.this_frame.num_draw_calls, - }); - }, - "Statistics::PerformanceSample"); +static Common::EventHook s_before_frame_event; +static Common::EventHook s_after_frame_event; static bool clear_scissors; @@ -507,3 +497,25 @@ void Statistics::DisplayScissor() ImGui::End(); } + +void Statistics::Init() +{ + s_before_frame_event = GetVideoEvents().before_frame_event.Register([] { g_stats.ResetFrame(); }, + "Statistics::ResetFrame"); + + s_after_frame_event = GetVideoEvents().after_frame_event.Register( + [](const Core::System& system) { + DolphinAnalytics::Instance().ReportPerformanceInfo({ + .speed_ratio = system.GetSystemTimers().GetEstimatedEmulationPerformance(), + .num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims, + .num_draw_calls = g_stats.this_frame.num_draw_calls, + }); + }, + "Statistics::PerformanceSample"); +} + +void Statistics::Shutdown() +{ + s_before_frame_event.reset(); + s_after_frame_event.reset(); +} diff --git a/Source/Core/VideoCommon/Statistics.h b/Source/Core/VideoCommon/Statistics.h index 7d4b367eade..8b80d0f439b 100644 --- a/Source/Core/VideoCommon/Statistics.h +++ b/Source/Core/VideoCommon/Statistics.h @@ -87,6 +87,9 @@ struct Statistics void Display() const; void DisplayProj() const; void DisplayScissor(); + + static void Init(); + static void Shutdown(); }; extern Statistics g_stats; diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index dd6cb46368d..ee27c3e80b9 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -463,8 +463,8 @@ private: void OnFrameEnd(); - Common::EventHook m_frame_event = - AfterFrameEvent::Register([this](Core::System&) { OnFrameEnd(); }, "TextureCache"); + Common::EventHook m_frame_event = GetVideoEvents().after_frame_event.Register( + [this](Core::System&) { OnFrameEnd(); }, "TextureCache"); VideoCommon::TextureUtils::TextureDumper m_texture_dumper; }; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 1797cb559f0..1af9ad8eb4b 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -118,9 +118,11 @@ VertexManagerBase::~VertexManagerBase() = default; bool VertexManagerBase::Initialize() { - m_frame_end_event = - AfterFrameEvent::Register([this](Core::System&) { OnEndFrame(); }, "VertexManagerBase"); - m_after_present_event = AfterPresentEvent::Register( + auto& video_events = GetVideoEvents(); + + m_frame_end_event = video_events.after_frame_event.Register( + [this](Core::System&) { OnEndFrame(); }, "VertexManagerBase"); + m_after_present_event = video_events.after_present_event.Register( [this](const PresentInfo& pi) { m_ticks_elapsed = pi.emulated_timestamp; }, "VertexManagerBase"); m_index_generator.Init(); @@ -442,7 +444,7 @@ void VertexManagerBase::Flush() if (m_draw_counter == 0) { // This is more or less the start of the Frame - BeforeFrameEvent::Trigger(); + GetVideoEvents().before_frame_event.Trigger(); } if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens || diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 0214dab9dde..63d061e6842 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -38,6 +38,7 @@ VideoConfig g_ActiveConfig; BackendInfo g_backend_info; static std::optional s_config_changed_callback_id = std::nullopt; +static Common::EventHook s_check_config_event; static bool IsVSyncActive(bool enabled) { @@ -218,8 +219,16 @@ void VideoConfig::VerifyValidity() } } +void VideoConfig::Init() +{ + s_check_config_event = GetVideoEvents().after_frame_event.Register( + [](Core::System&) { CheckForConfigChanges(); }, "CheckForConfigChanges"); +} + void VideoConfig::Shutdown() { + s_check_config_event.reset(); + if (!s_config_changed_callback_id.has_value()) return; @@ -390,10 +399,7 @@ void CheckForConfigChanges() } // Notify all listeners - ConfigChangedEvent::Trigger(changed_bits); + GetVideoEvents().config_changed_event.Trigger(changed_bits); // TODO: Move everything else to the ConfigChanged event } - -static Common::EventHook s_check_config_event = AfterFrameEvent::Register( - [](Core::System&) { CheckForConfigChanges(); }, "CheckForConfigChanges"); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index a0baaa666c4..e42b8e5d850 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -191,6 +191,7 @@ struct VideoConfig final VideoConfig() = default; void Refresh(); void VerifyValidity(); + static void Init(); static void Shutdown(); // General diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index 9c763dce344..f8f29db589b 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -14,18 +14,6 @@ namespace Core class System; } -// Called when certain video config setting are changed -using ConfigChangedEvent = Common::HookableEvent<"ConfigChanged", u32>; - -// An event called just before the first draw call of a frame -using BeforeFrameEvent = Common::HookableEvent<"BeforeFrame">; - -// An event called after the frame XFB copy begins processing on the host GPU. -// Useful for "once per frame" usecases. -// Note: In a few rare cases, games do multiple XFB copies per frame and join them while presenting. -// If this matters to your usecase, you should use BeforePresent instead. -using AfterFrameEvent = Common::HookableEvent<"AfterFrame", Core::System&>; - struct PresentInfo { enum class PresentReason @@ -78,19 +66,37 @@ struct PresentInfo std::vector xfb_copy_hashes; }; -// An event called just as a frame is queued for presentation. -// The exact timing of this event depends on the "Immediately Present XFB" option. -// -// If enabled, this event will trigger immediately after AfterFrame -// If disabled, this event won't trigger until the emulated interface starts drawing out a new -// frame. -// -// frame_count: The number of frames -using BeforePresentEvent = Common::HookableEvent<"BeforePresent", PresentInfo&>; +struct VideoEvents +{ + // Called when certain video config setting are changed + Common::HookableEvent config_changed_event{"ConfigChanged"}; -// An event that is triggered after a frame is presented. -// The exact timing of this event depends on backend/driver support. -using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>; + // An event called just before the first draw call of a frame + Common::HookableEvent<> before_frame_event{"BeforeFrame"}; -// An end of frame event that runs on the CPU thread -using VIEndFieldEvent = Common::HookableEvent<"VIEndField">; + // An event called after the frame XFB copy begins processing on the host GPU. + // Useful for "once per frame" usecases. + // Note: In a few rare cases, games do multiple XFB copies per frame and join them while + // presenting. + // If this matters to your usecase, you should use BeforePresent instead. + Common::HookableEvent after_frame_event{"AfterFrame"}; + + // An event called just as a frame is queued for presentation. + // The exact timing of this event depends on the "Immediately Present XFB" option. + // + // If enabled, this event will trigger immediately after AfterFrame + // If disabled, this event won't trigger until the emulated interface starts drawing out a new + // frame. + // + // frame_count: The number of frames + Common::HookableEvent before_present_event{"BeforePresent"}; + + // An event that is triggered after a frame is presented. + // The exact timing of this event depends on backend/driver support. + Common::HookableEvent after_present_event{"AfterPresent"}; + + // An end of frame event that runs on the CPU thread + Common::HookableEvent<> vi_end_field_event{"VIEndField"}; +}; + +VideoEvents& GetVideoEvents(); diff --git a/Source/Core/VideoCommon/Widescreen.cpp b/Source/Core/VideoCommon/Widescreen.cpp index 21309f3a983..e980d24d57d 100644 --- a/Source/Core/VideoCommon/Widescreen.cpp +++ b/Source/Core/VideoCommon/Widescreen.cpp @@ -29,7 +29,10 @@ WidescreenManager::WidescreenManager() "Invalid suggested aspect ratio mode: only Auto, 4:3 and 16:9 are supported"); } - m_config_changed = ConfigChangedEvent::Register( + auto& system = Core::System::GetInstance(); + auto& video_events = system.GetVideoEvents(); + + m_config_changed = video_events.config_changed_event.Register( [this](u32 bits) { if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO)) { @@ -45,10 +48,9 @@ WidescreenManager::WidescreenManager() "Widescreen"); // VertexManager doesn't maintain statistics in Wii mode. - auto& system = Core::System::GetInstance(); if (!system.IsWii()) { - m_update_widescreen = AfterFrameEvent::Register( + m_update_widescreen = video_events.after_frame_event.Register( [this](Core::System&) { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } }