mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
Common: Make HookableEvent use non-static data.
Co-authored-by: Dentomologist <dentomologist@gmail.com>
This commit is contained in:
parent
9c28f19e56
commit
f289b06e0d
@ -3,113 +3,138 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/StringLiteral.h"
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(_DEBUG)
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
struct HookBase
|
struct HookBase
|
||||||
{
|
{
|
||||||
virtual ~HookBase() = default;
|
// A pure virtual destructor makes this class abstract to prevent accidental "slicing".
|
||||||
|
virtual ~HookBase() = 0;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
inline HookBase::~HookBase() = default;
|
||||||
|
|
||||||
// EventHook is a handle a registered listener holds.
|
// EventHook is a handle a registered listener holds.
|
||||||
// When the handle is destroyed, the HookableEvent will automatically remove the listener.
|
// 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>;
|
using EventHook = std::unique_ptr<HookBase>;
|
||||||
|
|
||||||
// A hookable event system.
|
// 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:
|
// 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)
|
// fmt::print("I've been triggered with {} and {}", foo, bar)
|
||||||
// }, "NameOfHook");
|
// }, "NameOfHook");
|
||||||
//
|
//
|
||||||
// The hook will be automatically unregistered when the EventHook object goes out of scope.
|
// The hook will be automatically unregistered when the EventHook object goes out of scope.
|
||||||
// Trigger events by calling Trigger as:
|
// 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
|
class HookableEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using CallbackType = std::function<void(CallbackArgs...)>;
|
using CallbackType = std::function<void(CallbackArgs...)>;
|
||||||
|
|
||||||
private:
|
explicit HookableEvent(std::string_view event_name)
|
||||||
struct HookImpl final : public HookBase
|
: 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
|
|
||||||
{
|
|
||||||
std::recursive_mutex m_mutex;
|
|
||||||
std::vector<HookImpl*> 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.
|
// 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();
|
#if defined(_DEBUG)
|
||||||
std::lock_guard lock(storage.m_mutex);
|
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);
|
||||||
|
|
||||||
DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value);
|
std::lock_guard lg(m_storage->listeners_mutex);
|
||||||
auto handle = std::make_unique<HookImpl>(std::move(callback), std::move(name));
|
m_storage->listeners.push_back(handle.get());
|
||||||
storage.m_listeners.push_back(handle.get());
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Trigger(const CallbackArgs&... args)
|
void Trigger(const CallbackArgs&... args)
|
||||||
{
|
{
|
||||||
auto& storage = GetStorage();
|
std::lock_guard lg(m_storage->listeners_mutex);
|
||||||
std::lock_guard lock(storage.m_mutex);
|
for (auto* const handle : m_storage->listeners)
|
||||||
|
std::invoke(handle->callback, args...);
|
||||||
for (const auto& handle : storage.m_listeners)
|
|
||||||
handle->m_fn(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<HookImpl*> listeners;
|
||||||
|
#if defined(_DEBUG)
|
||||||
|
std::string event_name;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HookImpl final : HookBase
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
~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
|
} // namespace Common
|
||||||
|
|||||||
@ -344,7 +344,7 @@ void AchievementManager::DoFrame()
|
|||||||
{
|
{
|
||||||
m_last_rp_time = current_time;
|
m_last_rp_time = current_time;
|
||||||
rc_client_get_rich_presence_message(m_client, m_rich_presence.data(), RP_SIZE);
|
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))
|
if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED))
|
||||||
Discord::UpdateDiscordPresence();
|
Discord::UpdateDiscordPresence();
|
||||||
}
|
}
|
||||||
@ -769,7 +769,7 @@ void AchievementManager::CloseGame()
|
|||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
|
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateEvent::Trigger(UpdatedItems{.all = true});
|
update_event.Trigger(UpdatedItems{.all = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::Logout()
|
void AchievementManager::Logout()
|
||||||
@ -783,7 +783,7 @@ void AchievementManager::Logout()
|
|||||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
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.");
|
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 AchievementManager::LoginCallback(int result, const char* error_message, rc_client_t* client,
|
||||||
void* userdata)
|
void* userdata)
|
||||||
{
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
if (result != RC_OK)
|
if (result != RC_OK)
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.",
|
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to login {} to RetroAchievements server.",
|
||||||
Config::Get(Config::RA_USERNAME));
|
Config::Get(Config::RA_USERNAME));
|
||||||
UpdateEvent::Trigger({.failed_login_code = result});
|
instance.update_event.Trigger({.failed_login_code = result});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rc_client_user_t* user;
|
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);
|
user = rc_client_get_user_info(client);
|
||||||
}
|
}
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to retrieve user information from client.");
|
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;
|
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 {}.",
|
INFO_LOG_FMT(ACHIEVEMENTS, "Attempted to login prior user {}; current user is {}.",
|
||||||
user->username, Config::Get(Config::RA_USERNAME));
|
user->username, Config::Get(Config::RA_USERNAME));
|
||||||
rc_client_logout(client);
|
rc_client_logout(client);
|
||||||
UpdateEvent::Trigger({.failed_login_code = RC_INVALID_STATE});
|
instance.update_event.Trigger({.failed_login_code = RC_INVALID_STATE});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully logged in {} to RetroAchievements server.",
|
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully logged in {} to RetroAchievements server.",
|
||||||
user->username);
|
user->username);
|
||||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
std::lock_guard lg{instance.GetLock()};
|
||||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, user->token);
|
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, user->token);
|
||||||
AchievementManager::GetInstance().FetchPlayerBadge();
|
instance.FetchPlayerBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::FetchBoardInfo(AchievementId leaderboard_id)
|
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_leaderboard_entry_list_t* list,
|
||||||
rc_client_t* client, void* userdata)
|
rc_client_t* client, void* userdata)
|
||||||
{
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
u32* leaderboard_id = static_cast<u32*>(userdata);
|
u32* leaderboard_id = static_cast<u32*>(userdata);
|
||||||
Common::ScopeGuard on_end_scope([&] { delete leaderboard_id; });
|
Common::ScopeGuard on_end_scope([&] { delete leaderboard_id; });
|
||||||
|
|
||||||
@ -991,10 +993,10 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
|
|||||||
return;
|
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++)
|
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];
|
const auto& response_entry = list->entries[ix];
|
||||||
auto& map_entry = leaderboard.entries[response_entry.index];
|
auto& map_entry = leaderboard.entries[response_entry.index];
|
||||||
map_entry.username.assign(response_entry.user);
|
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)
|
if (static_cast<int32_t>(ix) == list->user_index)
|
||||||
leaderboard.player_index = response_entry.rank;
|
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,
|
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);
|
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
|
||||||
instance.FetchGameBadges();
|
instance.FetchGameBadges();
|
||||||
instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release);
|
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
|
// Set this to a value that will immediately trigger RP
|
||||||
instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2};
|
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)
|
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,
|
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
|
||||||
client_event->achievement->points),
|
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 :
|
(rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW :
|
||||||
OSD::Color::CYAN,
|
OSD::Color::CYAN,
|
||||||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
&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
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
switch (rc_client_raintegration_get_achievement_state(instance.m_client,
|
switch (rc_client_raintegration_get_achievement_state(instance.m_client,
|
||||||
client_event->achievement->id))
|
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)
|
void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t* client_event)
|
||||||
{
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
OSD::AddMessage(fmt::format("Scored {} on leaderboard: {}",
|
OSD::AddMessage(fmt::format("Scored {} on leaderboard: {}",
|
||||||
client_event->leaderboard->tracker_value,
|
client_event->leaderboard->tracker_value,
|
||||||
client_event->leaderboard->title),
|
client_event->leaderboard->title),
|
||||||
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
|
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
|
||||||
AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id);
|
instance.FetchBoardInfo(client_event->leaderboard->id);
|
||||||
UpdateEvent::Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
|
instance.update_event.Trigger(UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event)
|
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);
|
const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id);
|
||||||
if (inserted)
|
if (inserted)
|
||||||
instance.m_challenges_updated = true;
|
instance.m_challenges_updated = true;
|
||||||
UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
|
instance.update_event.Trigger(UpdatedItems{.rich_presence = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
|
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
|
||||||
@ -1233,7 +1236,7 @@ void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
|
|||||||
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
|
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
|
||||||
if (removed > 0)
|
if (removed > 0)
|
||||||
instance.m_challenges_updated = true;
|
instance.m_challenges_updated = true;
|
||||||
UpdateEvent::Trigger(UpdatedItems{.rich_presence = true});
|
instance.update_event.Trigger(UpdatedItems{.rich_presence = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
|
void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
|
||||||
@ -1249,7 +1252,7 @@ void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
|
|||||||
OSD::Duration::SHORT, OSD::Color::GREEN,
|
OSD::Duration::SHORT, OSD::Color::GREEN,
|
||||||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
||||||
instance.m_last_progress_message = current_time;
|
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,
|
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())
|
if (!m_client || !HasAPIToken())
|
||||||
{
|
{
|
||||||
UpdateEvent::Trigger(callback_data);
|
update_event.Trigger(callback_data);
|
||||||
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
|
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
|
||||||
DisplayWelcomeMessage();
|
DisplayWelcomeMessage();
|
||||||
return;
|
return;
|
||||||
@ -1434,7 +1437,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
|
|||||||
"RetroAchievements connection failed on image request.\n URL: {}",
|
"RetroAchievements connection failed on image request.\n URL: {}",
|
||||||
api_request.url);
|
api_request.url);
|
||||||
rc_api_destroy_request(&api_request);
|
rc_api_destroy_request(&api_request);
|
||||||
UpdateEvent::Trigger(callback_data);
|
update_event.Trigger(callback_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1467,7 +1470,7 @@ void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_
|
|||||||
}
|
}
|
||||||
|
|
||||||
*badge = std::move(tmp_badge);
|
*badge = std::move(tmp_badge);
|
||||||
UpdateEvent::Trigger(callback_data);
|
update_event.Trigger(callback_data);
|
||||||
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
|
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
|
||||||
m_active_challenges.contains(*callback_data.achievements.begin()))
|
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_event_handler(instance.m_client, RAIntegrationEventHandler);
|
||||||
rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker);
|
rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker);
|
||||||
rc_client_raintegration_set_get_game_name_function(instance.m_client, GameTitleEstimateHandler);
|
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
|
// TODO: hook up menu and dll event handlers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1565,11 +1568,12 @@ void AchievementManager::LoadIntegrationCallback(int result, const char* error_m
|
|||||||
void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
|
void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
|
||||||
rc_client_t* client)
|
rc_client_t* client)
|
||||||
{
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
|
||||||
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
|
||||||
DevMenuUpdateEvent::Trigger();
|
instance.dev_menu_update_event.Trigger();
|
||||||
break;
|
break;
|
||||||
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
|
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
|
||||||
{
|
{
|
||||||
|
|||||||
@ -120,7 +120,7 @@ public:
|
|||||||
bool rich_presence = false;
|
bool rich_presence = false;
|
||||||
int failed_login_code = 0;
|
int failed_login_code = 0;
|
||||||
};
|
};
|
||||||
using UpdateEvent = Common::HookableEvent<"AchievementManagerUpdate", const UpdatedItems&>;
|
Common::HookableEvent<const UpdatedItems&> update_event{"AchievementManagerUpdate"};
|
||||||
|
|
||||||
static AchievementManager& GetInstance();
|
static AchievementManager& GetInstance();
|
||||||
void Init(void* hwnd);
|
void Init(void* hwnd);
|
||||||
@ -174,7 +174,7 @@ public:
|
|||||||
std::vector<std::string> GetActiveLeaderboards() const;
|
std::vector<std::string> GetActiveLeaderboards() const;
|
||||||
|
|
||||||
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
using DevMenuUpdateEvent = Common::HookableEvent<"AchievementManagerDevMenuUpdate">;
|
Common::HookableEvent<> dev_menu_update_event{"AchievementManagerDevMenuUpdate"};
|
||||||
const rc_client_raintegration_menu_t* GetDevelopmentMenu();
|
const rc_client_raintegration_menu_t* GetDevelopmentMenu();
|
||||||
u32 ActivateDevMenuItem(u32 menu_item_id);
|
u32 ActivateDevMenuItem(u32 menu_item_id);
|
||||||
bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); }
|
bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); }
|
||||||
|
|||||||
@ -113,7 +113,7 @@ static std::atomic<State> s_state = State::Uninitialized;
|
|||||||
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Callback_FramePresented(const PresentInfo& present_info);
|
static void Callback_FramePresented(const PresentInfo& present_info);
|
||||||
|
|
||||||
struct HostJob
|
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,
|
static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot,
|
||||||
WindowSystemInfo wsi);
|
WindowSystemInfo wsi);
|
||||||
|
|
||||||
static Common::EventHook s_frame_presented =
|
|
||||||
AfterPresentEvent::Register(&Core::Callback_FramePresented, "Core Frame Presented");
|
|
||||||
|
|
||||||
bool GetIsThrottlerTempDisabled()
|
bool GetIsThrottlerTempDisabled()
|
||||||
{
|
{
|
||||||
return s_is_throttler_temp_disabled;
|
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.
|
// This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block.
|
||||||
system.GetFifo().Prepare();
|
system.GetFifo().Prepare();
|
||||||
|
|
||||||
|
const Common::EventHook frame_presented = GetVideoEvents().after_present_event.Register(
|
||||||
|
&Core::Callback_FramePresented, "Core Frame Presented");
|
||||||
|
|
||||||
// Setup our core
|
// Setup our core
|
||||||
if (Config::Get(Config::MAIN_CPU_CORE) != PowerPC::CPUCore::Interpreter)
|
if (Config::Get(Config::MAIN_CPU_CORE) != PowerPC::CPUCore::Interpreter)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -250,7 +250,7 @@ void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb)
|
|||||||
m_RequestedRecordingEnd = false;
|
m_RequestedRecordingEnd = false;
|
||||||
m_FinishedCb = finishedCb;
|
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) {
|
[this](const Core::System& system) {
|
||||||
const bool was_recording = OpcodeDecoder::g_record_fifo_data;
|
const bool was_recording = OpcodeDecoder::g_record_fifo_data;
|
||||||
OpcodeDecoder::g_record_fifo_data = IsRecording();
|
OpcodeDecoder::g_record_fifo_data = IsRecording();
|
||||||
|
|||||||
@ -894,7 +894,7 @@ void VideoInterfaceManager::EndField(FieldType field, u64 ticks)
|
|||||||
m_system.GetCoreTiming().Throttle(ticks);
|
m_system.GetCoreTiming().Throttle(ticks);
|
||||||
|
|
||||||
g_perf_metrics.CountVBlank();
|
g_perf_metrics.CountVBlank();
|
||||||
VIEndFieldEvent::Trigger();
|
m_system.GetVideoEvents().vi_end_field_event.Trigger();
|
||||||
Core::OnFrameEnd(m_system);
|
Core::OnFrameEnd(m_system);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
std::unique_ptr<SoundStream> m_sound_stream;
|
||||||
bool m_sound_stream_running = false;
|
bool m_sound_stream_running = false;
|
||||||
bool m_audio_dump_started = false;
|
bool m_audio_dump_started = false;
|
||||||
@ -339,4 +342,14 @@ VideoCommon::CustomResourceManager& System::GetCustomResourceManager() const
|
|||||||
{
|
{
|
||||||
return m_impl->m_custom_resource_manager;
|
return m_impl->m_custom_resource_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoEvents& System::GetVideoEvents() const
|
||||||
|
{
|
||||||
|
return m_impl->m_video_events;
|
||||||
|
}
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
VideoEvents& GetVideoEvents()
|
||||||
|
{
|
||||||
|
return Core::System::GetInstance().GetVideoEvents();
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "VideoCommon/VideoEvents.h"
|
||||||
|
|
||||||
class GeometryShaderManager;
|
class GeometryShaderManager;
|
||||||
class Interpreter;
|
class Interpreter;
|
||||||
class JitInterface;
|
class JitInterface;
|
||||||
@ -198,6 +200,7 @@ public:
|
|||||||
XFStateManager& GetXFStateManager() const;
|
XFStateManager& GetXFStateManager() const;
|
||||||
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
|
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
|
||||||
VideoCommon::CustomResourceManager& GetCustomResourceManager() const;
|
VideoCommon::CustomResourceManager& GetCustomResourceManager() const;
|
||||||
|
VideoEvents& GetVideoEvents() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
System();
|
System();
|
||||||
|
|||||||
@ -31,7 +31,7 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
|||||||
CreateMainLayout();
|
CreateMainLayout();
|
||||||
ConnectWidgets();
|
ConnectWidgets();
|
||||||
|
|
||||||
m_event_hook = AchievementManager::UpdateEvent::Register(
|
m_event_hook = AchievementManager::GetInstance().update_event.Register(
|
||||||
[this](AchievementManager::UpdatedItems updated_items) {
|
[this](AchievementManager::UpdatedItems updated_items) {
|
||||||
QueueOnObject(this, [this, updated_items = std::move(updated_items)] {
|
QueueOnObject(this, [this, updated_items = std::move(updated_items)] {
|
||||||
AchievementsWindow::UpdateData(std::move(updated_items));
|
AchievementsWindow::UpdateData(std::move(updated_items));
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "Core/CheatSearch.h"
|
#include "Core/CheatSearch.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
#include "DolphinQt/QtUtils/QtUtils.h"
|
#include "DolphinQt/QtUtils/QtUtils.h"
|
||||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||||
@ -78,7 +79,8 @@ void CheatsManager::UpdateAllCheatSearchWidgetCurrentValues()
|
|||||||
|
|
||||||
void CheatsManager::RegisterAfterFrameEventCallback()
|
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()
|
void CheatsManager::RemoveAfterFrameEventCallback()
|
||||||
|
|||||||
@ -429,7 +429,8 @@ void MemoryWidget::hideEvent(QHideEvent* event)
|
|||||||
|
|
||||||
void MemoryWidget::RegisterAfterFrameEventCallback()
|
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()
|
void MemoryWidget::RemoveAfterFrameEventCallback()
|
||||||
|
|||||||
@ -295,7 +295,7 @@ void MenuBar::AddToolsMenu()
|
|||||||
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
||||||
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
m_achievements_dev_menu = tools_menu->addMenu(tr("RetroAchievements Development"));
|
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");
|
[this] { QueueOnObject(this, [this] { UpdateAchievementDevelopmentMenu(); }); }, "MenuBar");
|
||||||
m_achievements_dev_menu->menuAction()->setVisible(false);
|
m_achievements_dev_menu->menuAction()->setVisible(false);
|
||||||
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|||||||
@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
#include "UICommon/DiscordPresence.h"
|
#include "UICommon/DiscordPresence.h"
|
||||||
|
|
||||||
|
#include "VideoCommon/Statistics.h"
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
#include "VideoCommon/VideoConfig.h"
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
|
||||||
@ -138,9 +139,11 @@ void Init()
|
|||||||
s_config_changed_callback_id = Config::AddConfigChangedCallback(config_changed_callback);
|
s_config_changed_callback_id = Config::AddConfigChangedCallback(config_changed_callback);
|
||||||
Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader());
|
Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader());
|
||||||
SConfig::Init();
|
SConfig::Init();
|
||||||
|
g_Config.Init();
|
||||||
Discord::Init();
|
Discord::Init();
|
||||||
Common::Log::LogManager::Init();
|
Common::Log::LogManager::Init();
|
||||||
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
|
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
|
||||||
|
Statistics::Init();
|
||||||
|
|
||||||
RefreshConfig();
|
RefreshConfig();
|
||||||
}
|
}
|
||||||
@ -149,6 +152,7 @@ void Shutdown()
|
|||||||
{
|
{
|
||||||
Config::RemoveConfigChangedCallback(s_config_changed_callback_id);
|
Config::RemoveConfigChangedCallback(s_config_changed_callback_id);
|
||||||
|
|
||||||
|
Statistics::Shutdown();
|
||||||
GCAdapter::Shutdown();
|
GCAdapter::Shutdown();
|
||||||
WiimoteReal::Shutdown();
|
WiimoteReal::Shutdown();
|
||||||
Common::Log::LogManager::Shutdown();
|
Common::Log::LogManager::Shutdown();
|
||||||
|
|||||||
@ -18,8 +18,8 @@ std::unique_ptr<AbstractGfx> g_gfx;
|
|||||||
|
|
||||||
AbstractGfx::AbstractGfx()
|
AbstractGfx::AbstractGfx()
|
||||||
{
|
{
|
||||||
m_config_changed =
|
m_config_changed = GetVideoEvents().config_changed_event.Register(
|
||||||
ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "AbstractGfx");
|
[this](u32 bits) { OnConfigChanged(bits); }, "AbstractGfx");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbstractGfx::IsHeadless() const
|
bool AbstractGfx::IsHeadless() const
|
||||||
|
|||||||
@ -29,8 +29,8 @@ void CustomResourceManager::Initialize()
|
|||||||
|
|
||||||
m_asset_loader.Initialize();
|
m_asset_loader.Initialize();
|
||||||
|
|
||||||
m_xfb_event =
|
m_xfb_event = GetVideoEvents().after_frame_event.Register(
|
||||||
AfterFrameEvent::Register([this](Core::System&) { XFBTriggered(); }, "CustomResourceManager");
|
[this](Core::System&) { XFBTriggered(); }, "CustomResourceManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomResourceManager::Shutdown()
|
void CustomResourceManager::Shutdown()
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#include "VideoCommon/BPStructs.h"
|
#include "VideoCommon/BPStructs.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#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,
|
false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
|
||||||
bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
|
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"
|
// This is as closest as we have to an "end of the frame"
|
||||||
// It works 99% of the time.
|
// It works 99% of the time.
|
||||||
// But sometimes games want to render an XFB larger than the EFB's 640x528 pixel resolution
|
// 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
|
// render multiple sub-frames and arrange the XFB copies in next to each-other in main memory
|
||||||
// so they form a single completed XFB.
|
// so they form a single completed XFB.
|
||||||
// See https://dolphin-emu.org/blog/2017/11/19/hybridxfb/ for examples and more detail.
|
// 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
|
// 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
|
// 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
|
// Might also clean up some issues with games doing XFB copies they don't intend to
|
||||||
// display.
|
// display.
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
if (g_ActiveConfig.bImmediateXFB)
|
if (g_ActiveConfig.bImmediateXFB)
|
||||||
{
|
{
|
||||||
// TODO: GetTicks is not sane from the GPU thread.
|
// TODO: GetTicks is not sane from the GPU thread.
|
||||||
|
|||||||
@ -29,8 +29,8 @@ static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name)
|
|||||||
|
|
||||||
FrameDumper::FrameDumper()
|
FrameDumper::FrameDumper()
|
||||||
{
|
{
|
||||||
m_frame_end_handle =
|
m_frame_end_handle = GetVideoEvents().after_frame_event.Register(
|
||||||
AfterFrameEvent::Register([this](Core::System&) { FlushFrameDump(); }, "FrameDumper");
|
[this](Core::System&) { FlushFrameDump(); }, "FrameDumper");
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameDumper::~FrameDumper()
|
FrameDumper::~FrameDumper()
|
||||||
|
|||||||
@ -84,8 +84,8 @@ bool FramebufferManager::Initialize()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_end_of_frame_event =
|
m_end_of_frame_event = GetVideoEvents().after_frame_event.Register(
|
||||||
AfterFrameEvent::Register([this](Core::System&) { EndOfFrame(); }, "FramebufferManager");
|
[this](Core::System&) { EndOfFrame(); }, "FramebufferManager");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,8 @@ CustomShaderCache::CustomShaderCache()
|
|||||||
m_async_uber_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
|
m_async_uber_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
|
||||||
m_async_uber_shader_compiler->StartWorkerThreads(1); // TODO
|
m_async_uber_shader_compiler->StartWorkerThreads(1); // TODO
|
||||||
|
|
||||||
m_frame_end_handler = AfterFrameEvent::Register([this](Core::System&) { RetrieveAsyncShaders(); },
|
m_frame_end_handler = GetVideoEvents().after_frame_event.Register(
|
||||||
"RetrieveAsyncShaders");
|
[this](Core::System&) { RetrieveAsyncShaders(); }, "RetrieveAsyncShaders");
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomShaderCache::~CustomShaderCache()
|
CustomShaderCache::~CustomShaderCache()
|
||||||
|
|||||||
@ -95,8 +95,8 @@ bool GraphicsModManager::Initialize()
|
|||||||
g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes);
|
g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes);
|
||||||
g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config);
|
g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config);
|
||||||
|
|
||||||
m_end_of_frame_event =
|
m_end_of_frame_event = GetVideoEvents().after_frame_event.Register(
|
||||||
AfterFrameEvent::Register([this](Core::System&) { EndOfFrame(); }, "ModManager");
|
[this](Core::System&) { EndOfFrame(); }, "ModManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -94,8 +94,8 @@ static void TryToSnapToXFBSize(int& width, int& height, int xfb_width, int xfb_h
|
|||||||
|
|
||||||
Presenter::Presenter()
|
Presenter::Presenter()
|
||||||
{
|
{
|
||||||
m_config_changed =
|
m_config_changed = GetVideoEvents().config_changed_event.Register(
|
||||||
ConfigChangedEvent::Register([this](u32 bits) { ConfigChanged(bits); }, "Presenter");
|
[this](u32 bits) { ConfigChanged(bits); }, "Presenter");
|
||||||
}
|
}
|
||||||
|
|
||||||
Presenter::~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)
|
if (!is_duplicate || !g_ActiveConfig.bSkipPresentingDuplicateXFBs)
|
||||||
{
|
{
|
||||||
Present(presentation_time);
|
Present(presentation_time);
|
||||||
ProcessFrameDumping(ticks);
|
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.reason = PresentInfo::PresentReason::Immediate;
|
||||||
present_info.present_count = m_present_count++;
|
present_info.present_count = m_present_count++;
|
||||||
|
|
||||||
BeforePresentEvent::Trigger(present_info);
|
auto& video_events = GetVideoEvents();
|
||||||
|
|
||||||
|
video_events.before_present_event.Trigger(present_info);
|
||||||
|
|
||||||
Present();
|
Present();
|
||||||
ProcessFrameDumping(ticks);
|
ProcessFrameDumping(ticks);
|
||||||
|
|
||||||
AfterPresentEvent::Trigger(present_info);
|
video_events.after_present_event.Trigger(present_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Presenter::ProcessFrameDumping(u64 ticks) const
|
void Presenter::ProcessFrameDumping(u64 ticks) const
|
||||||
@ -925,7 +929,7 @@ void Presenter::DoState(PointerWrap& p)
|
|||||||
if (p.IsReadMode() && m_last_xfb_stride != 0)
|
if (p.IsReadMode() && m_last_xfb_stride != 0)
|
||||||
{
|
{
|
||||||
// This technically counts as the end of the frame
|
// 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,
|
ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height,
|
||||||
m_last_xfb_ticks);
|
m_last_xfb_ticks);
|
||||||
|
|||||||
@ -46,8 +46,8 @@ bool ShaderCache::Initialize()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
|
m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
|
||||||
m_frame_end_handler = AfterFrameEvent::Register([this](Core::System&) { RetrieveAsyncShaders(); },
|
m_frame_end_handler = GetVideoEvents().after_frame_event.Register(
|
||||||
"RetrieveAsyncShaders");
|
[this](Core::System&) { RetrieveAsyncShaders(); }, "RetrieveAsyncShaders");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,18 +20,8 @@
|
|||||||
|
|
||||||
Statistics g_stats;
|
Statistics g_stats;
|
||||||
|
|
||||||
static Common::EventHook s_before_frame_event =
|
static Common::EventHook s_before_frame_event;
|
||||||
BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame");
|
static Common::EventHook s_after_frame_event;
|
||||||
|
|
||||||
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 bool clear_scissors;
|
static bool clear_scissors;
|
||||||
|
|
||||||
@ -507,3 +497,25 @@ void Statistics::DisplayScissor()
|
|||||||
|
|
||||||
ImGui::End();
|
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();
|
||||||
|
}
|
||||||
|
|||||||
@ -87,6 +87,9 @@ struct Statistics
|
|||||||
void Display() const;
|
void Display() const;
|
||||||
void DisplayProj() const;
|
void DisplayProj() const;
|
||||||
void DisplayScissor();
|
void DisplayScissor();
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
static void Shutdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Statistics g_stats;
|
extern Statistics g_stats;
|
||||||
|
|||||||
@ -463,8 +463,8 @@ private:
|
|||||||
|
|
||||||
void OnFrameEnd();
|
void OnFrameEnd();
|
||||||
|
|
||||||
Common::EventHook m_frame_event =
|
Common::EventHook m_frame_event = GetVideoEvents().after_frame_event.Register(
|
||||||
AfterFrameEvent::Register([this](Core::System&) { OnFrameEnd(); }, "TextureCache");
|
[this](Core::System&) { OnFrameEnd(); }, "TextureCache");
|
||||||
|
|
||||||
VideoCommon::TextureUtils::TextureDumper m_texture_dumper;
|
VideoCommon::TextureUtils::TextureDumper m_texture_dumper;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -118,9 +118,11 @@ VertexManagerBase::~VertexManagerBase() = default;
|
|||||||
|
|
||||||
bool VertexManagerBase::Initialize()
|
bool VertexManagerBase::Initialize()
|
||||||
{
|
{
|
||||||
m_frame_end_event =
|
auto& video_events = GetVideoEvents();
|
||||||
AfterFrameEvent::Register([this](Core::System&) { OnEndFrame(); }, "VertexManagerBase");
|
|
||||||
m_after_present_event = AfterPresentEvent::Register(
|
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; },
|
[this](const PresentInfo& pi) { m_ticks_elapsed = pi.emulated_timestamp; },
|
||||||
"VertexManagerBase");
|
"VertexManagerBase");
|
||||||
m_index_generator.Init();
|
m_index_generator.Init();
|
||||||
@ -442,7 +444,7 @@ void VertexManagerBase::Flush()
|
|||||||
if (m_draw_counter == 0)
|
if (m_draw_counter == 0)
|
||||||
{
|
{
|
||||||
// This is more or less the start of the Frame
|
// This is more or less the start of the Frame
|
||||||
BeforeFrameEvent::Trigger();
|
GetVideoEvents().before_frame_event.Trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens ||
|
if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens ||
|
||||||
|
|||||||
@ -38,6 +38,7 @@ VideoConfig g_ActiveConfig;
|
|||||||
BackendInfo g_backend_info;
|
BackendInfo g_backend_info;
|
||||||
static std::optional<CPUThreadConfigCallback::ConfigChangedCallbackID>
|
static std::optional<CPUThreadConfigCallback::ConfigChangedCallbackID>
|
||||||
s_config_changed_callback_id = std::nullopt;
|
s_config_changed_callback_id = std::nullopt;
|
||||||
|
static Common::EventHook s_check_config_event;
|
||||||
|
|
||||||
static bool IsVSyncActive(bool enabled)
|
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()
|
void VideoConfig::Shutdown()
|
||||||
{
|
{
|
||||||
|
s_check_config_event.reset();
|
||||||
|
|
||||||
if (!s_config_changed_callback_id.has_value())
|
if (!s_config_changed_callback_id.has_value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -390,10 +399,7 @@ void CheckForConfigChanges()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify all listeners
|
// Notify all listeners
|
||||||
ConfigChangedEvent::Trigger(changed_bits);
|
GetVideoEvents().config_changed_event.Trigger(changed_bits);
|
||||||
|
|
||||||
// TODO: Move everything else to the ConfigChanged event
|
// TODO: Move everything else to the ConfigChanged event
|
||||||
}
|
}
|
||||||
|
|
||||||
static Common::EventHook s_check_config_event = AfterFrameEvent::Register(
|
|
||||||
[](Core::System&) { CheckForConfigChanges(); }, "CheckForConfigChanges");
|
|
||||||
|
|||||||
@ -191,6 +191,7 @@ struct VideoConfig final
|
|||||||
VideoConfig() = default;
|
VideoConfig() = default;
|
||||||
void Refresh();
|
void Refresh();
|
||||||
void VerifyValidity();
|
void VerifyValidity();
|
||||||
|
static void Init();
|
||||||
static void Shutdown();
|
static void Shutdown();
|
||||||
|
|
||||||
// General
|
// General
|
||||||
|
|||||||
@ -14,18 +14,6 @@ namespace Core
|
|||||||
class System;
|
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
|
struct PresentInfo
|
||||||
{
|
{
|
||||||
enum class PresentReason
|
enum class PresentReason
|
||||||
@ -78,19 +66,37 @@ struct PresentInfo
|
|||||||
std::vector<std::string_view> xfb_copy_hashes;
|
std::vector<std::string_view> xfb_copy_hashes;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An event called just as a frame is queued for presentation.
|
struct VideoEvents
|
||||||
// The exact timing of this event depends on the "Immediately Present XFB" option.
|
{
|
||||||
//
|
// Called when certain video config setting are changed
|
||||||
// If enabled, this event will trigger immediately after AfterFrame
|
Common::HookableEvent<u32> config_changed_event{"ConfigChanged"};
|
||||||
// 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.
|
// An event called just before the first draw call of a frame
|
||||||
// The exact timing of this event depends on backend/driver support.
|
Common::HookableEvent<> before_frame_event{"BeforeFrame"};
|
||||||
using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>;
|
|
||||||
|
|
||||||
// An end of frame event that runs on the CPU thread
|
// An event called after the frame XFB copy begins processing on the host GPU.
|
||||||
using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;
|
// 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();
|
||||||
|
|||||||
@ -29,7 +29,10 @@ WidescreenManager::WidescreenManager()
|
|||||||
"Invalid suggested aspect ratio mode: only Auto, 4:3 and 16:9 are supported");
|
"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) {
|
[this](u32 bits) {
|
||||||
if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO))
|
if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO))
|
||||||
{
|
{
|
||||||
@ -45,10 +48,9 @@ WidescreenManager::WidescreenManager()
|
|||||||
"Widescreen");
|
"Widescreen");
|
||||||
|
|
||||||
// VertexManager doesn't maintain statistics in Wii mode.
|
// VertexManager doesn't maintain statistics in Wii mode.
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
if (!system.IsWii())
|
if (!system.IsWii())
|
||||||
{
|
{
|
||||||
m_update_widescreen = AfterFrameEvent::Register(
|
m_update_widescreen = video_events.after_frame_event.Register(
|
||||||
[this](Core::System&) { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic");
|
[this](Core::System&) { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user