Common: Make HookableEvent use non-static data.
This commit is contained in:
parent
d2db9d9590
commit
7d75c3e86d
|
@ -19,36 +19,38 @@ 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;
|
||||
|
||||
protected:
|
||||
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<"My lovely event", std::string, u32> my_lovey_event;
|
||||
//
|
||||
// Register listeners anywhere you need them as:
|
||||
// EventHook myHook = MyLoveyEvent::Register([](std::string foo, u32 bar) {
|
||||
// EventHook myHook = my_lovey_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_lovey_event.Trigger("Hello world", 42);
|
||||
//
|
||||
|
||||
template <StringLiteral EventName, typename... CallbackArgs>
|
||||
class HookableEvent
|
||||
{
|
||||
|
@ -56,60 +58,68 @@ public:
|
|||
using CallbackType = std::function<void(CallbackArgs...)>;
|
||||
|
||||
private:
|
||||
struct HookImpl final : public HookBase
|
||||
struct Storage;
|
||||
|
||||
struct HookImpl final : HookBase
|
||||
{
|
||||
~HookImpl() override { HookableEvent::Remove(this); }
|
||||
HookImpl(CallbackType callback, std::string name)
|
||||
: m_fn(std::move(callback)), m_name(std::move(name))
|
||||
HookImpl(std::weak_ptr<Storage> storage, CallbackType func, std::string name)
|
||||
: m_storage{std::move(storage)}, m_function{std::move(func)}, m_name{std::move(name)}
|
||||
{
|
||||
}
|
||||
CallbackType m_fn;
|
||||
std::string m_name;
|
||||
|
||||
~HookImpl() override
|
||||
{
|
||||
const auto storage = m_storage.lock();
|
||||
if (storage == nullptr)
|
||||
{
|
||||
DEBUG_LOG_FMT(COMMON, "Handler {} outlived event hook {}", m_name, EventName.value);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG_FMT(COMMON, "Removing {} handler at {} event hook", m_name, EventName.value);
|
||||
storage->RemoveHook(this);
|
||||
}
|
||||
|
||||
std::weak_ptr<Storage> m_storage;
|
||||
const CallbackType m_function;
|
||||
const std::string m_name;
|
||||
};
|
||||
|
||||
struct Storage
|
||||
{
|
||||
std::recursive_mutex m_mutex;
|
||||
void RemoveHook(HookImpl* handle)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::erase(m_listeners, handle);
|
||||
}
|
||||
|
||||
std::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.
|
||||
[[nodiscard]] static EventHook Register(CallbackType callback, std::string name)
|
||||
// Note: Attempting to add/remove hooks of the event within the callback itself will NOT end well.
|
||||
[[nodiscard]] EventHook Register(CallbackType callback, std::string name)
|
||||
{
|
||||
auto& storage = GetStorage();
|
||||
std::lock_guard lock(storage.m_mutex);
|
||||
|
||||
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());
|
||||
auto handle = std::make_unique<HookImpl>(m_storage, std::move(callback), std::move(name));
|
||||
|
||||
std::lock_guard lock(m_storage->m_mutex);
|
||||
m_storage->m_listeners.push_back(handle.get());
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void Trigger(const CallbackArgs&... args)
|
||||
void Trigger(const CallbackArgs&... args)
|
||||
{
|
||||
auto& storage = GetStorage();
|
||||
std::lock_guard lock(storage.m_mutex);
|
||||
|
||||
for (const auto& handle : storage.m_listeners)
|
||||
handle->m_fn(args...);
|
||||
std::lock_guard lock(m_storage->m_mutex);
|
||||
for (auto* const handle : m_storage->m_listeners)
|
||||
handle->m_function(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
// shared_ptr storage allows hooks to forget their connection if they outlive the event itself.
|
||||
std::shared_ptr<Storage> m_storage{std::make_shared<Storage>()};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -112,7 +112,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
|
||||
{
|
||||
|
@ -130,8 +130,8 @@ 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");
|
||||
static Common::EventHook s_frame_presented = GetVideoEvents().after_present_event.Register(
|
||||
&Core::Callback_FramePresented, "Core Frame Presented");
|
||||
|
||||
bool GetIsThrottlerTempDisabled()
|
||||
{
|
||||
|
|
|
@ -255,7 +255,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();
|
||||
|
|
|
@ -863,7 +863,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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::CustomAssetLoader& System::GetCustomAssetLoader() const
|
|||
{
|
||||
return m_impl->m_custom_asset_loader;
|
||||
}
|
||||
|
||||
VideoEvents& System::GetVideoEvents() const
|
||||
{
|
||||
return m_impl->m_video_events;
|
||||
}
|
||||
} // namespace Core
|
||||
|
||||
VideoEvents& GetVideoEvents()
|
||||
{
|
||||
return Core::System::GetInstance().GetVideoEvents();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
class GeometryShaderManager;
|
||||
class Interpreter;
|
||||
|
@ -198,6 +199,7 @@ public:
|
|||
XFStateManager& GetXFStateManager() const;
|
||||
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
|
||||
VideoCommon::CustomAssetLoader& GetCustomAssetLoader() const;
|
||||
VideoEvents& GetVideoEvents() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "Core/CheatSearch.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
|
@ -79,7 +80,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()
|
||||
|
|
|
@ -386,7 +386,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()
|
||||
|
|
|
@ -17,8 +17,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
|
||||
|
|
|
@ -339,6 +339,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
|
||||
|
@ -346,7 +348,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(Core::System::GetInstance());
|
||||
|
||||
// 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
|
||||
|
@ -354,7 +356,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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
Statistics g_stats;
|
||||
|
||||
static Common::EventHook s_before_frame_event =
|
||||
BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame");
|
||||
static Common::EventHook s_before_frame_event = GetVideoEvents().before_frame_event.Register(
|
||||
[] { g_stats.ResetFrame(); }, "Statistics::ResetFrame");
|
||||
|
||||
static Common::EventHook s_after_frame_event = AfterFrameEvent::Register(
|
||||
static Common::EventHook s_after_frame_event = GetVideoEvents().after_frame_event.Register(
|
||||
[](const Core::System& system) {
|
||||
DolphinAnalytics::Instance().ReportPerformanceInfo({
|
||||
.speed_ratio = system.GetSystemTimers().GetEstimatedEmulationPerformance(),
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -387,10 +387,10 @@ 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(
|
||||
static Common::EventHook s_check_config_event = GetVideoEvents().after_frame_event.Register(
|
||||
[](Core::System&) { CheckForConfigChanges(); }, "CheckForConfigChanges");
|
||||
|
|
|
@ -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&>;
|
||||
struct VideoEvents
|
||||
{
|
||||
// Called when certain video config setting are changed
|
||||
Common::HookableEvent<"ConfigChanged", u32> config_changed_event;
|
||||
|
||||
// An event that is triggered after a frame is presented.
|
||||
// The exact timing of this event depends on backend/driver support.
|
||||
using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>;
|
||||
// An event called just before the first draw call of a frame
|
||||
Common::HookableEvent<"BeforeFrame"> before_frame_event;
|
||||
|
||||
// An end of frame event that runs on the CPU thread
|
||||
using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;
|
||||
// An event called after the frame XFB copy begins processing on the host GPU.
|
||||
// Useful for "once per frame" usecases.
|
||||
// Note: In a few rare cases, games do multiple XFB copies per frame and join them while
|
||||
// presenting.
|
||||
// If this matters to your usecase, you should use BeforePresent instead.
|
||||
Common::HookableEvent<"AfterFrame", Core::System&> after_frame_event;
|
||||
|
||||
// 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<"BeforePresent", PresentInfo&> before_present_event;
|
||||
|
||||
// An event that is triggered after a frame is presented.
|
||||
// The exact timing of this event depends on backend/driver support.
|
||||
Common::HookableEvent<"AfterPresent", PresentInfo&> after_present_event;
|
||||
|
||||
// An end of frame event that runs on the CPU thread
|
||||
Common::HookableEvent<"VIEndField"> vi_end_field_event;
|
||||
};
|
||||
|
||||
VideoEvents& GetVideoEvents();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue