summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/stream.cpp2
-rw-r--r--src/audio_core/stream.h25
-rw-r--r--src/core/core_timing.cpp53
-rw-r--r--src/core/core_timing.h45
-rw-r--r--src/core/hardware_interrupt_manager.cpp13
-rw-r--r--src/core/hardware_interrupt_manager.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp13
-rw-r--r--src/core/hle/kernel/kernel.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp5
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h2
-rw-r--r--src/core/memory/cheat_engine.cpp2
-rw-r--r--src/core/memory/cheat_engine.h3
-rw-r--r--src/core/tools/freezer.cpp2
-rw-r--r--src/core/tools/freezer.h3
-rw-r--r--src/tests/core/core_timing.cpp84
17 files changed, 103 insertions, 161 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 6a5f53a57..4ca98f8ea 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -37,7 +37,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, 37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { 38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
39 39
40 release_event = core_timing.RegisterEvent( 40 release_event = Core::Timing::CreateEvent(
41 name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
42} 42}
43 43
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 8106cea43..1708a4d98 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -98,18 +98,19 @@ private:
98 /// Gets the number of core cycles when the specified buffer will be released 98 /// Gets the number of core cycles when the specified buffer will be released
99 s64 GetBufferReleaseCycles(const Buffer& buffer) const; 99 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
100 100
101 u32 sample_rate; ///< Sample rate of the stream 101 u32 sample_rate; ///< Sample rate of the stream
102 Format format; ///< Format of the stream 102 Format format; ///< Format of the stream
103 float game_volume = 1.0f; ///< The volume the game currently has set 103 float game_volume = 1.0f; ///< The volume the game currently has set
104 ReleaseCallback release_callback; ///< Buffer release callback for the stream 104 ReleaseCallback release_callback; ///< Buffer release callback for the stream
105 State state{State::Stopped}; ///< Playback state of the stream 105 State state{State::Stopped}; ///< Playback state of the stream
106 Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream 106 std::shared_ptr<Core::Timing::EventType>
107 BufferPtr active_buffer; ///< Actively playing buffer in the stream 107 release_event; ///< Core timing release event for the stream
108 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream 108 BufferPtr active_buffer; ///< Actively playing buffer in the stream
109 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream 109 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
110 SinkStream& sink_stream; ///< Output sink for the stream 110 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
111 Core::Timing::CoreTiming& core_timing; ///< Core timing instance. 111 SinkStream& sink_stream; ///< Output sink for the stream
112 std::string name; ///< Name of the stream, must be unique 112 Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
113 std::string name; ///< Name of the stream, must be unique
113}; 114};
114 115
115using StreamPtr = std::shared_ptr<Stream>; 116using StreamPtr = std::shared_ptr<Stream>;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0e9570685..aa09fa453 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -17,11 +17,15 @@ namespace Core::Timing {
17 17
18constexpr int MAX_SLICE_LENGTH = 10000; 18constexpr int MAX_SLICE_LENGTH = 10000;
19 19
20std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
21 return std::make_shared<EventType>(std::move(callback), std::move(name));
22}
23
20struct CoreTiming::Event { 24struct CoreTiming::Event {
21 s64 time; 25 s64 time;
22 u64 fifo_order; 26 u64 fifo_order;
23 u64 userdata; 27 u64 userdata;
24 const EventType* type; 28 std::weak_ptr<EventType> type;
25 29
26 // Sort by time, unless the times are the same, in which case sort by 30 // Sort by time, unless the times are the same, in which case sort by
27 // the order added to the queue 31 // the order added to the queue
@@ -54,36 +58,15 @@ void CoreTiming::Initialize() {
54 event_fifo_id = 0; 58 event_fifo_id = 0;
55 59
56 const auto empty_timed_callback = [](u64, s64) {}; 60 const auto empty_timed_callback = [](u64, s64) {};
57 ev_lost = RegisterEvent("_lost_event", empty_timed_callback); 61 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
58} 62}
59 63
60void CoreTiming::Shutdown() { 64void CoreTiming::Shutdown() {
61 ClearPendingEvents(); 65 ClearPendingEvents();
62 UnregisterAllEvents();
63}
64
65EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
66 std::lock_guard guard{inner_mutex};
67 // check for existing type with same name.
68 // we want event type names to remain unique so that we can use them for serialization.
69 ASSERT_MSG(event_types.find(name) == event_types.end(),
70 "CoreTiming Event \"{}\" is already registered. Events should only be registered "
71 "during Init to avoid breaking save states.",
72 name.c_str());
73
74 auto info = event_types.emplace(name, EventType{callback, nullptr});
75 EventType* event_type = &info.first->second;
76 event_type->name = &info.first->first;
77 return event_type;
78}
79
80void CoreTiming::UnregisterAllEvents() {
81 ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
82 event_types.clear();
83} 66}
84 67
85void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 68void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
86 ASSERT(event_type != nullptr); 69 u64 userdata) {
87 std::lock_guard guard{inner_mutex}; 70 std::lock_guard guard{inner_mutex};
88 const s64 timeout = GetTicks() + cycles_into_future; 71 const s64 timeout = GetTicks() + cycles_into_future;
89 72
@@ -93,13 +76,15 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
93 } 76 }
94 77
95 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 78 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
79
96 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 80 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
97} 81}
98 82
99void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { 83void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
100 std::lock_guard guard{inner_mutex}; 84 std::lock_guard guard{inner_mutex};
85
101 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 86 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
102 return e.type == event_type && e.userdata == userdata; 87 return e.type.lock().get() == event_type.get() && e.userdata == userdata;
103 }); 88 });
104 89
105 // Removing random items breaks the invariant so we have to re-establish it. 90 // Removing random items breaks the invariant so we have to re-establish it.
@@ -130,10 +115,12 @@ void CoreTiming::ClearPendingEvents() {
130 event_queue.clear(); 115 event_queue.clear();
131} 116}
132 117
133void CoreTiming::RemoveEvent(const EventType* event_type) { 118void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
134 std::lock_guard guard{inner_mutex}; 119 std::lock_guard guard{inner_mutex};
135 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), 120
136 [&](const Event& e) { return e.type == event_type; }); 121 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
122 return e.type.lock().get() == event_type.get();
123 });
137 124
138 // Removing random items breaks the invariant so we have to re-establish it. 125 // Removing random items breaks the invariant so we have to re-establish it.
139 if (itr != event_queue.end()) { 126 if (itr != event_queue.end()) {
@@ -181,7 +168,11 @@ void CoreTiming::Advance() {
181 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 168 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
182 event_queue.pop_back(); 169 event_queue.pop_back();
183 inner_mutex.unlock(); 170 inner_mutex.unlock();
184 evt.type->callback(evt.userdata, global_timer - evt.time); 171
172 if (auto event_type{evt.type.lock()}) {
173 event_type->callback(evt.userdata, global_timer - evt.time);
174 }
175
185 inner_mutex.lock(); 176 inner_mutex.lock();
186 } 177 }
187 178
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 3bb88c810..d50f4eb8a 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -6,11 +6,12 @@
6 6
7#include <chrono> 7#include <chrono>
8#include <functional> 8#include <functional>
9#include <memory>
9#include <mutex> 10#include <mutex>
10#include <optional> 11#include <optional>
11#include <string> 12#include <string>
12#include <unordered_map>
13#include <vector> 13#include <vector>
14
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "common/threadsafe_queue.h" 16#include "common/threadsafe_queue.h"
16 17
@@ -21,10 +22,13 @@ using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
21 22
22/// Contains the characteristics of a particular event. 23/// Contains the characteristics of a particular event.
23struct EventType { 24struct EventType {
25 EventType(TimedCallback&& callback, std::string&& name)
26 : callback{std::move(callback)}, name{std::move(name)} {}
27
24 /// The event's callback function. 28 /// The event's callback function.
25 TimedCallback callback; 29 TimedCallback callback;
26 /// A pointer to the name of the event. 30 /// A pointer to the name of the event.
27 const std::string* name; 31 const std::string name;
28}; 32};
29 33
30/** 34/**
@@ -57,31 +61,17 @@ public:
57 /// Tears down all timing related functionality. 61 /// Tears down all timing related functionality.
58 void Shutdown(); 62 void Shutdown();
59 63
60 /// Registers a core timing event with the given name and callback.
61 ///
62 /// @param name The name of the core timing event to register.
63 /// @param callback The callback to execute for the event.
64 ///
65 /// @returns An EventType instance representing the registered event.
66 ///
67 /// @pre The name of the event being registered must be unique among all
68 /// registered events.
69 ///
70 EventType* RegisterEvent(const std::string& name, TimedCallback callback);
71
72 /// Unregisters all registered events thus far. Note: not thread unsafe
73 void UnregisterAllEvents();
74
75 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an 64 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
76 /// event is scheduled earlier than the current values. 65 /// event is scheduled earlier than the current values.
77 /// 66 ///
78 /// Scheduling from a callback will not update the downcount until the Advance() completes. 67 /// Scheduling from a callback will not update the downcount until the Advance() completes.
79 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); 68 void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
69 u64 userdata = 0);
80 70
81 void UnscheduleEvent(const EventType* event_type, u64 userdata); 71 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata);
82 72
83 /// We only permit one event of each type in the queue at a time. 73 /// We only permit one event of each type in the queue at a time.
84 void RemoveEvent(const EventType* event_type); 74 void RemoveEvent(const std::shared_ptr<EventType>& event_type);
85 75
86 void ForceExceptionCheck(s64 cycles); 76 void ForceExceptionCheck(s64 cycles);
87 77
@@ -148,13 +138,18 @@ private:
148 std::vector<Event> event_queue; 138 std::vector<Event> event_queue;
149 u64 event_fifo_id = 0; 139 u64 event_fifo_id = 0;
150 140
151 // Stores each element separately as a linked list node so pointers to elements 141 std::shared_ptr<EventType> ev_lost;
152 // remain stable regardless of rehashes/resizing.
153 std::unordered_map<std::string, EventType> event_types;
154
155 EventType* ev_lost = nullptr;
156 142
157 std::mutex inner_mutex; 143 std::mutex inner_mutex;
158}; 144};
159 145
146/// Creates a core timing event with the given name and callback.
147///
148/// @param name The name of the core timing event to create.
149/// @param callback The callback to execute for the event.
150///
151/// @returns An EventType instance representing the created event.
152///
153std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback);
154
160} // namespace Core::Timing 155} // namespace Core::Timing
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index c2115db2d..c629d9fa1 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,13 +11,12 @@
11namespace Core::Hardware { 11namespace Core::Hardware {
12 12
13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { 13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
14 gpu_interrupt_event = 14 gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) {
15 system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) { 15 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
16 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); 16 const u32 syncpt = static_cast<u32>(message >> 32);
17 const u32 syncpt = static_cast<u32>(message >> 32); 17 const u32 value = static_cast<u32>(message);
18 const u32 value = static_cast<u32>(message); 18 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
19 nvdrv->SignalGPUInterruptSyncpt(syncpt, value); 19 });
20 });
21} 20}
22 21
23InterruptManager::~InterruptManager() = default; 22InterruptManager::~InterruptManager() = default;
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
index 494db883a..5fa306ae0 100644
--- a/src/core/hardware_interrupt_manager.h
+++ b/src/core/hardware_interrupt_manager.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace Core { 11namespace Core {
@@ -25,7 +27,7 @@ public:
25 27
26private: 28private:
27 Core::System& system; 29 Core::System& system;
28 Core::Timing::EventType* gpu_interrupt_event{}; 30 std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event;
29}; 31};
30 32
31} // namespace Core::Hardware 33} // namespace Core::Hardware
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 63ad07950..a9851113a 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -139,12 +139,12 @@ struct KernelCore::Impl {
139 139
140 void InitializeThreads() { 140 void InitializeThreads() {
141 thread_wakeup_event_type = 141 thread_wakeup_event_type =
142 system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 142 Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback);
143 } 143 }
144 144
145 void InitializePreemption() { 145 void InitializePreemption() {
146 preemption_event = system.CoreTiming().RegisterEvent( 146 preemption_event =
147 "PreemptionCallback", [this](u64 userdata, s64 cycles_late) { 147 Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
148 global_scheduler.PreemptThreads(); 148 global_scheduler.PreemptThreads();
149 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); 149 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
150 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 150 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
@@ -166,8 +166,9 @@ struct KernelCore::Impl {
166 166
167 std::shared_ptr<ResourceLimit> system_resource_limit; 167 std::shared_ptr<ResourceLimit> system_resource_limit;
168 168
169 Core::Timing::EventType* thread_wakeup_event_type = nullptr; 169 std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
170 Core::Timing::EventType* preemption_event = nullptr; 170 std::shared_ptr<Core::Timing::EventType> preemption_event;
171
171 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, 172 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
172 // allowing us to simply use a pool index or similar. 173 // allowing us to simply use a pool index or similar.
173 Kernel::HandleTable thread_wakeup_callback_handle_table; 174 Kernel::HandleTable thread_wakeup_callback_handle_table;
@@ -269,7 +270,7 @@ u64 KernelCore::CreateNewUserProcessID() {
269 return impl->next_user_process_id++; 270 return impl->next_user_process_id++;
270} 271}
271 272
272Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { 273const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const {
273 return impl->thread_wakeup_event_type; 274 return impl->thread_wakeup_event_type;
274} 275}
275 276
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c74b9078f..babb531c6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -113,7 +113,7 @@ private:
113 u64 CreateNewThreadID(); 113 u64 CreateNewThreadID();
114 114
115 /// Retrieves the event type used for thread wakeup callbacks. 115 /// Retrieves the event type used for thread wakeup callbacks.
116 Core::Timing::EventType* ThreadWakeupCallbackEventType() const; 116 const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
117 117
118 /// Provides a reference to the thread wakeup callback handle table. 118 /// Provides a reference to the thread wakeup callback handle table.
119 Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); 119 Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8ef029e0f..89bf8b815 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -77,15 +77,14 @@ IAppletResource::IAppletResource(Core::System& system)
77 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); 77 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
78 78
79 // Register update callbacks 79 // Register update callbacks
80 auto& core_timing = system.CoreTiming();
81 pad_update_event = 80 pad_update_event =
82 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { 81 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
83 UpdateControllers(userdata, cycles_late); 82 UpdateControllers(userdata, cycles_late);
84 }); 83 });
85 84
86 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 85 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
87 86
88 core_timing.ScheduleEvent(pad_update_ticks, pad_update_event); 87 system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event);
89 88
90 ReloadInputDevices(); 89 ReloadInputDevices();
91} 90}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 923762fff..ad20f147c 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -69,7 +69,7 @@ private:
69 69
70 std::shared_ptr<Kernel::SharedMemory> shared_mem; 70 std::shared_ptr<Kernel::SharedMemory> shared_mem;
71 71
72 Core::Timing::EventType* pad_update_event; 72 std::shared_ptr<Core::Timing::EventType> pad_update_event;
73 Core::System& system; 73 Core::System& system;
74 74
75 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 75 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index cd07ab362..52623cf89 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -37,8 +37,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
37 displays.emplace_back(4, "Null", system); 37 displays.emplace_back(4, "Null", system);
38 38
39 // Schedule the screen composition events 39 // Schedule the screen composition events
40 composition_event = system.CoreTiming().RegisterEvent( 40 composition_event =
41 "ScreenComposition", [this](u64 userdata, s64 cycles_late) { 41 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
42 Compose(); 42 Compose();
43 const auto ticks = 43 const auto ticks =
44 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); 44 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 9cc41f2e6..e3cc14bdc 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -103,7 +103,7 @@ private:
103 u32 swap_interval = 1; 103 u32 swap_interval = 1;
104 104
105 /// Event that handles screen composition. 105 /// Event that handles screen composition.
106 Core::Timing::EventType* composition_event; 106 std::shared_ptr<Core::Timing::EventType> composition_event;
107 107
108 Core::System& system; 108 Core::System& system;
109}; 109};
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 10821d452..b73cc9fbd 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -186,7 +186,7 @@ CheatEngine::~CheatEngine() {
186} 186}
187 187
188void CheatEngine::Initialize() { 188void CheatEngine::Initialize() {
189 event = core_timing.RegisterEvent( 189 event = Core::Timing::CreateEvent(
190 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 190 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
191 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 191 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
192 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); 192 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 0f012e9b5..e3db90dac 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <memory>
8#include <vector> 9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/memory/dmnt_cheat_types.h" 11#include "core/memory/dmnt_cheat_types.h"
@@ -78,7 +79,7 @@ private:
78 std::vector<CheatEntry> cheats; 79 std::vector<CheatEntry> cheats;
79 std::atomic_bool is_pending_reload{false}; 80 std::atomic_bool is_pending_reload{false};
80 81
81 Core::Timing::EventType* event{}; 82 std::shared_ptr<Core::Timing::EventType> event;
82 Core::Timing::CoreTiming& core_timing; 83 Core::Timing::CoreTiming& core_timing;
83 Core::System& system; 84 Core::System& system;
84}; 85};
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 17f050068..19b531ecb 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -54,7 +54,7 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
54} // Anonymous namespace 54} // Anonymous namespace
55 55
56Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { 56Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
57 event = core_timing.RegisterEvent( 57 event = Core::Timing::CreateEvent(
58 "MemoryFreezer::FrameCallback", 58 "MemoryFreezer::FrameCallback",
59 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 59 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
60 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); 60 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index b58de5472..90b1a885c 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <memory>
8#include <mutex> 9#include <mutex>
9#include <optional> 10#include <optional>
10#include <vector> 11#include <vector>
@@ -75,7 +76,7 @@ private:
75 mutable std::mutex entries_mutex; 76 mutable std::mutex entries_mutex;
76 std::vector<Entry> entries; 77 std::vector<Entry> entries;
77 78
78 Core::Timing::EventType* event; 79 std::shared_ptr<Core::Timing::EventType> event;
79 Core::Timing::CoreTiming& core_timing; 80 Core::Timing::CoreTiming& core_timing;
80}; 81};
81 82
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 3443bf05e..1e3940801 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -7,7 +7,9 @@
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <cstdlib> 9#include <cstdlib>
10#include <memory>
10#include <string> 11#include <string>
12
11#include "common/file_util.h" 13#include "common/file_util.h"
12#include "core/core.h" 14#include "core/core.h"
13#include "core/core_timing.h" 15#include "core/core_timing.h"
@@ -65,11 +67,16 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
65 ScopeInit guard; 67 ScopeInit guard;
66 auto& core_timing = guard.core_timing; 68 auto& core_timing = guard.core_timing;
67 69
68 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); 70 std::shared_ptr<Core::Timing::EventType> cb_a =
69 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); 71 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>);
70 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); 72 std::shared_ptr<Core::Timing::EventType> cb_b =
71 Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>); 73 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
72 Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>); 74 std::shared_ptr<Core::Timing::EventType> cb_c =
75 Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>);
76 std::shared_ptr<Core::Timing::EventType> cb_d =
77 Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>);
78 std::shared_ptr<Core::Timing::EventType> cb_e =
79 Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>);
73 80
74 // Enter slice 0 81 // Enter slice 0
75 core_timing.ResetRun(); 82 core_timing.ResetRun();
@@ -99,8 +106,8 @@ TEST_CASE("CoreTiming[FairSharing]", "[core]") {
99 ScopeInit guard; 106 ScopeInit guard;
100 auto& core_timing = guard.core_timing; 107 auto& core_timing = guard.core_timing;
101 108
102 Core::Timing::EventType* empty_callback = 109 std::shared_ptr<Core::Timing::EventType> empty_callback =
103 core_timing.RegisterEvent("empty_callback", EmptyCallback); 110 Core::Timing::CreateEvent("empty_callback", EmptyCallback);
104 111
105 callbacks_done = 0; 112 callbacks_done = 0;
106 u64 MAX_CALLBACKS = 10; 113 u64 MAX_CALLBACKS = 10;
@@ -133,8 +140,10 @@ TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
133 ScopeInit guard; 140 ScopeInit guard;
134 auto& core_timing = guard.core_timing; 141 auto& core_timing = guard.core_timing;
135 142
136 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); 143 std::shared_ptr<Core::Timing::EventType> cb_a =
137 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); 144 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>);
145 std::shared_ptr<Core::Timing::EventType> cb_b =
146 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
138 147
139 // Enter slice 0 148 // Enter slice 0
140 core_timing.ResetRun(); 149 core_timing.ResetRun();
@@ -145,60 +154,3 @@ TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
145 AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) 154 AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10)
146 AdvanceAndCheck(core_timing, 1, 1, 50, -50); 155 AdvanceAndCheck(core_timing, 1, 1, 50, -50);
147} 156}
148
149namespace ChainSchedulingTest {
150static int reschedules = 0;
151
152static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata,
153 s64 cycles_late) {
154 --reschedules;
155 REQUIRE(reschedules >= 0);
156 REQUIRE(lateness == cycles_late);
157
158 if (reschedules > 0) {
159 core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
160 userdata);
161 }
162}
163} // namespace ChainSchedulingTest
164
165TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
166 using namespace ChainSchedulingTest;
167
168 ScopeInit guard;
169 auto& core_timing = guard.core_timing;
170
171 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
172 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
173 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
174 Core::Timing::EventType* cb_rs = core_timing.RegisterEvent(
175 "callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) {
176 RescheduleCallback(core_timing, userdata, cycles_late);
177 });
178
179 // Enter slice 0
180 core_timing.ResetRun();
181
182 core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
183 core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
184 core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
185 core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
186 REQUIRE(800 == core_timing.GetDowncount());
187
188 reschedules = 3;
189 AdvanceAndCheck(core_timing, 0, 0); // cb_a
190 AdvanceAndCheck(core_timing, 1, 1); // cb_b, cb_rs
191 REQUIRE(2 == reschedules);
192
193 core_timing.AddTicks(core_timing.GetDowncount());
194 core_timing.Advance(); // cb_rs
195 core_timing.SwitchContext(3);
196 REQUIRE(1 == reschedules);
197 REQUIRE(200 == core_timing.GetDowncount());
198
199 AdvanceAndCheck(core_timing, 2, 3); // cb_c
200
201 core_timing.AddTicks(core_timing.GetDowncount());
202 core_timing.Advance(); // cb_rs
203 REQUIRE(0 == reschedules);
204}