summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorScott Mansell <phiren@gmail.com>2023-01-30 22:36:25 +1300
committerScott Mansell <phiren@gmail.com>2023-01-31 19:41:24 +1300
commit154cb4f722a9996a4134463e64c85c6bfa25d8bf (patch)
tree7dc4583afd2fb8ec494032839439314ef8ddae3d /Source/Core
parentd6cd8de1a7c28629ee7680ee20082608b5f84a29 (diff)
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
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/CMakeLists.txt2
-rw-r--r--Source/Core/Common/EventHook.h79
-rw-r--r--Source/Core/Common/StringLiteral.h17
-rw-r--r--Source/Core/DolphinLib.props3
-rw-r--r--Source/Core/VideoCommon/CMakeLists.txt1
-rw-r--r--Source/Core/VideoCommon/VideoEvents.h84
6 files changed, 186 insertions, 0 deletions
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 6e49f7b7a6..bab78766da 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -46,6 +46,7 @@ add_library(common
EnumFormatter.h
EnumMap.h
Event.h
+ EventHook.h
FatFsUtil.cpp
FatFsUtil.h
FileSearch.cpp
@@ -115,6 +116,7 @@ add_library(common
SocketContext.cpp
SocketContext.h
SPSCQueue.h
+ StringLiteral.h
StringUtil.cpp
StringUtil.h
SymbolDB.cpp
diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/EventHook.h
new file mode 100644
index 0000000000..265013827a
--- /dev/null
+++ b/Source/Core/Common/EventHook.h
@@ -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 = {};
+};
diff --git a/Source/Core/Common/StringLiteral.h b/Source/Core/Common/StringLiteral.h
new file mode 100644
index 0000000000..6227d48ed6
--- /dev/null
+++ b/Source/Core/Common/StringLiteral.h
@@ -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];
+};
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 9bf63bbb51..470d303e21 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -46,6 +46,7 @@
<ClInclude Include="Common\EnumFormatter.h" />
<ClInclude Include="Common\EnumMap.h" />
<ClInclude Include="Common\Event.h" />
+ <ClInclude Include="Common\EventHook.h" />
<ClInclude Include="Common\FatFsUtil.h" />
<ClInclude Include="Common\FileSearch.h" />
<ClInclude Include="Common\FileUtil.h" />
@@ -145,6 +146,7 @@
<ClInclude Include="Common\SFMLHelper.h" />
<ClInclude Include="Common\SocketContext.h" />
<ClInclude Include="Common\SPSCQueue.h" />
+ <ClInclude Include="Common\StringLiteral.h" />
<ClInclude Include="Common\StringUtil.h" />
<ClInclude Include="Common\Swap.h" />
<ClInclude Include="Common\SymbolDB.h" />
@@ -713,6 +715,7 @@
<ClInclude Include="VideoCommon\VideoBackendBase.h" />
<ClInclude Include="VideoCommon\VideoCommon.h" />
<ClInclude Include="VideoCommon\VideoConfig.h" />
+ <ClInclude Include="VideoCommon\VideoEvents.h" />
<ClInclude Include="VideoCommon\VideoState.h" />
<ClInclude Include="VideoCommon\XFMemory.h" />
<ClInclude Include="VideoCommon\XFStructs.h" />
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index 4507904afd..5428facc55 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -163,6 +163,7 @@ add_library(videocommon
VertexShaderManager.h
VideoBackendBase.cpp
VideoBackendBase.h
+ VideoEvents.h
VideoCommon.h
VideoConfig.cpp
VideoConfig.h
diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h
new file mode 100644
index 0000000000..bb95296ebd
--- /dev/null
+++ b/Source/Core/VideoCommon/VideoEvents.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&>;