Introduce an Event system to VideoCommon
A lot of the remaining complexity in Renderer is the massive Swap function which tries to handle a bunch of FrameBegin/FrameEnd events. Rather than create a new place for it. This event system will try to distribute it all over the place
This commit is contained in:
parent
d6cd8de1a7
commit
154cb4f722
|
@ -46,6 +46,7 @@ add_library(common
|
||||||
EnumFormatter.h
|
EnumFormatter.h
|
||||||
EnumMap.h
|
EnumMap.h
|
||||||
Event.h
|
Event.h
|
||||||
|
EventHook.h
|
||||||
FatFsUtil.cpp
|
FatFsUtil.cpp
|
||||||
FatFsUtil.h
|
FatFsUtil.h
|
||||||
FileSearch.cpp
|
FileSearch.cpp
|
||||||
|
@ -115,6 +116,7 @@ add_library(common
|
||||||
SocketContext.cpp
|
SocketContext.cpp
|
||||||
SocketContext.h
|
SocketContext.h
|
||||||
SPSCQueue.h
|
SPSCQueue.h
|
||||||
|
StringLiteral.h
|
||||||
StringUtil.cpp
|
StringUtil.cpp
|
||||||
StringUtil.h
|
StringUtil.h
|
||||||
SymbolDB.cpp
|
SymbolDB.cpp
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2023 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/StringLiteral.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// A hookable event system.
|
||||||
|
|
||||||
|
// Define Events in a header as:
|
||||||
|
//
|
||||||
|
// using MyLoveyEvent = Event<"My lovely event", std::string>;
|
||||||
|
//
|
||||||
|
// Register listeners anywhere you need them as:
|
||||||
|
// EventHook myHook = MyLoveyEvent::Register([](std::string foo) {
|
||||||
|
// // Do something
|
||||||
|
// }, "Name of the hook");
|
||||||
|
//
|
||||||
|
// The hook will be automatically unregistered when the EventHook object goes out of scope.
|
||||||
|
// Trigger events by doing:
|
||||||
|
//
|
||||||
|
// MyLoveyEvent::Trigger("Hello world");
|
||||||
|
//
|
||||||
|
|
||||||
|
struct HookBase
|
||||||
|
{
|
||||||
|
virtual ~HookBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
using EventHook = std::unique_ptr<HookBase>;
|
||||||
|
|
||||||
|
template<StringLiteral EventName, typename... CallbackArgs>
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using CallbackType = std::function<void(CallbackArgs...)>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct HookImpl : public HookBase
|
||||||
|
{
|
||||||
|
~HookImpl() override { Event::Remove(this); }
|
||||||
|
HookImpl(CallbackType callback, std::string name) : m_fn(callback), m_name(name){ }
|
||||||
|
CallbackType m_fn;
|
||||||
|
std::string m_name;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Returns a handle that will unregister the listener when destroyed.
|
||||||
|
static EventHook Register(CallbackType callback, std::string name)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value);
|
||||||
|
auto handle = std::make_unique<HookImpl>(callback, name);
|
||||||
|
m_listeners.push_back(handle.get());
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Trigger(CallbackArgs... args)
|
||||||
|
{
|
||||||
|
for (auto& handle : m_listeners)
|
||||||
|
handle->m_fn(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void Remove(HookImpl* handle)
|
||||||
|
{
|
||||||
|
auto it = std::find(m_listeners.begin(), m_listeners.end(), handle);
|
||||||
|
if (it != m_listeners.end())
|
||||||
|
m_listeners.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static std::vector<HookImpl*> m_listeners = {};
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2023 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// A useful template for passing string literals as arguments to templates
|
||||||
|
// from: https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/
|
||||||
|
template<size_t N>
|
||||||
|
struct StringLiteral {
|
||||||
|
consteval StringLiteral(const char (&str)[N]) {
|
||||||
|
std::copy_n(str, N, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
char value[N];
|
||||||
|
};
|
|
@ -46,6 +46,7 @@
|
||||||
<ClInclude Include="Common\EnumFormatter.h" />
|
<ClInclude Include="Common\EnumFormatter.h" />
|
||||||
<ClInclude Include="Common\EnumMap.h" />
|
<ClInclude Include="Common\EnumMap.h" />
|
||||||
<ClInclude Include="Common\Event.h" />
|
<ClInclude Include="Common\Event.h" />
|
||||||
|
<ClInclude Include="Common\EventHook.h" />
|
||||||
<ClInclude Include="Common\FatFsUtil.h" />
|
<ClInclude Include="Common\FatFsUtil.h" />
|
||||||
<ClInclude Include="Common\FileSearch.h" />
|
<ClInclude Include="Common\FileSearch.h" />
|
||||||
<ClInclude Include="Common\FileUtil.h" />
|
<ClInclude Include="Common\FileUtil.h" />
|
||||||
|
@ -145,6 +146,7 @@
|
||||||
<ClInclude Include="Common\SFMLHelper.h" />
|
<ClInclude Include="Common\SFMLHelper.h" />
|
||||||
<ClInclude Include="Common\SocketContext.h" />
|
<ClInclude Include="Common\SocketContext.h" />
|
||||||
<ClInclude Include="Common\SPSCQueue.h" />
|
<ClInclude Include="Common\SPSCQueue.h" />
|
||||||
|
<ClInclude Include="Common\StringLiteral.h" />
|
||||||
<ClInclude Include="Common\StringUtil.h" />
|
<ClInclude Include="Common\StringUtil.h" />
|
||||||
<ClInclude Include="Common\Swap.h" />
|
<ClInclude Include="Common\Swap.h" />
|
||||||
<ClInclude Include="Common\SymbolDB.h" />
|
<ClInclude Include="Common\SymbolDB.h" />
|
||||||
|
@ -713,6 +715,7 @@
|
||||||
<ClInclude Include="VideoCommon\VideoBackendBase.h" />
|
<ClInclude Include="VideoCommon\VideoBackendBase.h" />
|
||||||
<ClInclude Include="VideoCommon\VideoCommon.h" />
|
<ClInclude Include="VideoCommon\VideoCommon.h" />
|
||||||
<ClInclude Include="VideoCommon\VideoConfig.h" />
|
<ClInclude Include="VideoCommon\VideoConfig.h" />
|
||||||
|
<ClInclude Include="VideoCommon\VideoEvents.h" />
|
||||||
<ClInclude Include="VideoCommon\VideoState.h" />
|
<ClInclude Include="VideoCommon\VideoState.h" />
|
||||||
<ClInclude Include="VideoCommon\XFMemory.h" />
|
<ClInclude Include="VideoCommon\XFMemory.h" />
|
||||||
<ClInclude Include="VideoCommon\XFStructs.h" />
|
<ClInclude Include="VideoCommon\XFStructs.h" />
|
||||||
|
|
|
@ -163,6 +163,7 @@ add_library(videocommon
|
||||||
VertexShaderManager.h
|
VertexShaderManager.h
|
||||||
VideoBackendBase.cpp
|
VideoBackendBase.cpp
|
||||||
VideoBackendBase.h
|
VideoBackendBase.h
|
||||||
|
VideoEvents.h
|
||||||
VideoCommon.h
|
VideoCommon.h
|
||||||
VideoConfig.cpp
|
VideoConfig.cpp
|
||||||
VideoConfig.h
|
VideoConfig.h
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright 2023 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EventHook.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Called when certain video config setting are changed
|
||||||
|
using ConfigChangedEvent = Event<"ConfigChanged", u32>;
|
||||||
|
|
||||||
|
// An event called just before the first draw call of a frame
|
||||||
|
using BeforeFrameEvent = 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.
|
||||||
|
using AfterFrameEvent = Event<"AfterFrame">;
|
||||||
|
|
||||||
|
struct PresentInfo
|
||||||
|
{
|
||||||
|
enum class PresentReason
|
||||||
|
{
|
||||||
|
Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy
|
||||||
|
VideoInterface, // VideoInterface has triggered a present with a new frame
|
||||||
|
VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number of (unique) frames since the emulated console booted
|
||||||
|
u64 frame_count;
|
||||||
|
|
||||||
|
|
||||||
|
// The number of presents since the video backend was initialized.
|
||||||
|
// never goes backwards.
|
||||||
|
u64 present_count;
|
||||||
|
|
||||||
|
// The frame is identical to the previous frame
|
||||||
|
PresentReason reason;
|
||||||
|
|
||||||
|
// The exact emulated time of the when real hardware would have presented this frame
|
||||||
|
// FIXME: Immediate should predict the timestamp of this present
|
||||||
|
u64 emulated_timestamp;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// u64 intended_present_time;
|
||||||
|
|
||||||
|
// AfterPresent only: The actual time the frame was presented
|
||||||
|
u64 actual_present_time = 0;
|
||||||
|
|
||||||
|
enum class PresentTimeAccuracy
|
||||||
|
{
|
||||||
|
// The Driver/OS has given us an exact timestamp of when the first line of the frame started
|
||||||
|
// scanning out to the monitor
|
||||||
|
PresentOnScreenExact,
|
||||||
|
|
||||||
|
// An approximate timestamp of scanout.
|
||||||
|
PresentOnScreen,
|
||||||
|
|
||||||
|
// Dolphin doesn't have visibility of the present time. But the present operation has
|
||||||
|
// been queued with the GPU driver and will happen in the near future.
|
||||||
|
PresentInProgress,
|
||||||
|
|
||||||
|
// Not implemented
|
||||||
|
Unimplemented,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accuracy of actual_present_time
|
||||||
|
PresentTimeAccuracy present_time_accuracy = PresentTimeAccuracy::Unimplemented;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 = Event<"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 = Event<"AfterPresent", PresentInfo&>;
|
Loading…
Reference in New Issue