summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2025-05-03 02:48:44 -0500
committerJordan Woyak <jordan.woyak@gmail.com>2025-11-02 17:30:43 -0600
commitf289b06e0db8bba6a3491d6e3cf0c2dd1b7a29fa (patch)
tree4e95a04a656f571e4dc0a7024348d22c5a1c662e /Source/Core
parent9c28f19e56307ac4de750ffa6ffb8c4e15e55def (diff)
Common: Make HookableEvent use non-static data.
Co-authored-by: Dentomologist <dentomologist@gmail.com>
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/HookableEvent.h145
-rw-r--r--Source/Core/Core/AchievementManager.cpp54
-rw-r--r--Source/Core/Core/AchievementManager.h4
-rw-r--r--Source/Core/Core/Core.cpp8
-rw-r--r--Source/Core/Core/FifoPlayer/FifoRecorder.cpp2
-rw-r--r--Source/Core/Core/HW/VideoInterface.cpp2
-rw-r--r--Source/Core/Core/System.cpp13
-rw-r--r--Source/Core/Core/System.h3
-rw-r--r--Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp2
-rw-r--r--Source/Core/DolphinQt/CheatsManager.cpp4
-rw-r--r--Source/Core/DolphinQt/Debugger/MemoryWidget.cpp3
-rw-r--r--Source/Core/DolphinQt/MenuBar.cpp2
-rw-r--r--Source/Core/UICommon/UICommon.cpp4
-rw-r--r--Source/Core/VideoCommon/AbstractGfx.cpp4
-rw-r--r--Source/Core/VideoCommon/Assets/CustomResourceManager.cpp4
-rw-r--r--Source/Core/VideoCommon/BPStructs.cpp6
-rw-r--r--Source/Core/VideoCommon/FrameDumper.cpp4
-rw-r--r--Source/Core/VideoCommon/FramebufferManager.cpp4
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.cpp4
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp4
-rw-r--r--Source/Core/VideoCommon/Present.cpp18
-rw-r--r--Source/Core/VideoCommon/ShaderCache.cpp4
-rw-r--r--Source/Core/VideoCommon/Statistics.cpp36
-rw-r--r--Source/Core/VideoCommon/Statistics.h3
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.h4
-rw-r--r--Source/Core/VideoCommon/VertexManagerBase.cpp10
-rw-r--r--Source/Core/VideoCommon/VideoConfig.cpp14
-rw-r--r--Source/Core/VideoCommon/VideoConfig.h1
-rw-r--r--Source/Core/VideoCommon/VideoEvents.h62
-rw-r--r--Source/Core/VideoCommon/Widescreen.cpp8
30 files changed, 262 insertions, 174 deletions
diff --git a/Source/Core/Common/HookableEvent.h b/Source/Core/Common/HookableEvent.h
index 987880de98..e2145e57e1 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 <functional>
#include <memory>
#include <mutex>
-#include <string>
#include <string_view>
#include <vector>
+#if defined(_DEBUG)
+#include <string>
+
+#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<HookBase>;
// A hookable event system.
//
-// Define Events in a header as:
+// Define Events as:
//
-// using MyLoveyEvent = HookableEvent<"My lovely event", std::string, u32>;
+// HookableEvent<std::string, u32> 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 <StringLiteral EventName, typename... CallbackArgs>
+
+template <typename... CallbackArgs>
class HookableEvent
{
public:
using CallbackType = std::function<void(CallbackArgs...)>;
-private:
- struct HookImpl final : public HookBase
+ explicit HookableEvent(std::string_view event_name)
+ : m_storage{std::make_shared<Storage>(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
+ // Returns a handle that will unregister the listener when destroyed.
+ // 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)
{
- std::recursive_mutex m_mutex;
- std::vector<HookImpl*> m_listeners;
- };
+#if defined(_DEBUG)
+ DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, m_storage->event_name);
+#endif
+ auto handle = std::make_unique<HookImpl>(m_storage, std::move(callback), name);
- // 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;
+ std::lock_guard lg(m_storage->listeners_mutex);
+ m_storage->listeners.push_back(handle.get());
+ return handle;
}
- static void Remove(HookImpl* handle)
+ void Trigger(const CallbackArgs&... args)
{
- auto& storage = GetStorage();
- std::lock_guard lock(storage.m_mutex);
-
- std::erase(storage.m_listeners, handle);
+ std::lock_guard lg(m_storage->listeners_mutex);
+ for (auto* const handle : m_storage->listeners)
+ std::invoke(handle->callback, args...);
}
-public:
- // Returns a handle that will unregister the listener when destroyed.
- [[nodiscard]] static EventHook Register(CallbackType callback, std::string name)
+private:
+ struct HookImpl;
+
+ struct Storage
{
- auto& storage = GetStorage();
- std::lock_guard lock(storage.m_mutex);
+ explicit Storage(std::string_view name [[maybe_unused]])
+ {
+#if defined(_DEBUG)
+ event_name = std::string(name);
+#endif
+ }
- DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value);
- auto handle = std::make_unique<HookImpl>(std::move(callback), std::move(name));
- storage.m_listeners.push_back(handle.get());
- return handle;
- }
+ std::mutex listeners_mutex;
+ std::vector<HookImpl*> listeners;
+#if defined(_DEBUG)
+ std::string event_name;
+#endif
+ };
- static void Trigger(const CallbackArgs&... args)
+ struct HookImpl final : HookBase
{
- auto& storage = GetStorage();
- std::lock_guard lock(storage.m_mutex);
+ HookImpl(const std::shared_ptr<Storage> 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
+ }
- for (const auto& handle : storage.m_listeners)
- handle->m_fn(args...);
- }
+ ~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<Storage> 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<Storage> m_storage;
};
} // namespace Common
diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp
index 6f093d8181..e08593e696 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<u32*>(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<int32_t>(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 625a4fb718..c1734cbcd3 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<const UpdatedItems&> update_event{"AchievementManagerUpdate"};
static AchievementManager& GetInstance();
void Init(void* hwnd);
@@ -174,7 +174,7 @@ public:
std::vector<std::string> 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 61664e2202..1892e0b3ea 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -113,7 +113,7 @@ static std::atomic<State> s_state = State::Uninitialized;
static std::unique_ptr<MemoryWatcher> 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<BootParameters> 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<BootParameters> 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 04236c4651..7cc22f42be 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 788d5295cc..c0f46bed35 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 0170c5fd11..22681cac43 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<SoundStream> 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 348f4fcb4a..e9fdec092d 100644
--- a/Source/Core/Core/System.h
+++ b/Source/Core/Core/System.h
@@ -5,6 +5,8 @@
#include <memory>
+#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 1aadb12020..43218c47fd 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 ecaf4fe2f4..e3dd639293 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 212bc49185..b088eeda02 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 152bfa648c..f4d69f41ce 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 40f31a231d..60077bfcd5 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 90b85ddfff..ca6ad881b1 100644
--- a/Source/Core/VideoCommon/AbstractGfx.cpp
+++ b/Source/Core/VideoCommon/AbstractGfx.cpp
@@ -18,8 +18,8 @@ std::unique_ptr<AbstractGfx> 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 f2af4ccbea..ac8199ef86 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 dd0cd59570..1ece9f278e 100644
--- a/Source/Core/VideoCommon/BPStructs.cpp
+++ b/Source/Core/VideoCommon/BPStructs.cpp
@@ -4,7 +4,6 @@
#include "VideoCommon/BPStructs.h"
#include <algorithm>
-#include <cmath>
#include <cstring>
#include <string>
@@ -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 c98141e8eb..f39424805c 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 5a3c768311..fc514811d5 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 a0dca15d38..0bf8e844ec 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 70d3a9a5ca..6a523c61a2 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 43f1c8140f..24c767cc69 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 44868808dd..fbed022aa5 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 db098db5c7..97c9264d18 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 7d4b367ead..8b80d0f439 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 dd6cb46368..ee27c3e80b 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 1797cb559f..1af9ad8eb4 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 0214dab9dd..63d061e684 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<CPUThreadConfigCallback::ConfigChangedCallbackID>
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 a0baaa666c..e42b8e5d85 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 9c763dce34..f8f29db589 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<std::string_view> 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&>;
-
-// 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 end of frame event that runs on the CPU thread
-using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;
+struct VideoEvents
+{
+ // Called when certain video config setting are changed
+ Common::HookableEvent<u32> config_changed_event{"ConfigChanged"};
+
+ // An event called just before the first draw call of a frame
+ Common::HookableEvent<> before_frame_event{"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.
+ Common::HookableEvent<Core::System&> 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<PresentInfo&> 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<PresentInfo&> 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 21309f3a98..e980d24d57 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");
}
}