summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Fernando S2022-07-10 10:29:56 +0200
committerGravatar GitHub2022-07-10 10:29:56 +0200
commit25e47738f71e6a6469ff251dd84e28289133dad1 (patch)
treec431a3b1ee04fb4da60e08ab21d78a5813e9b528
parentMerge pull request #8531 from FernandoS27/core-timing-fix-reg (diff)
parentPR (diff)
downloadyuzu-25e47738f71e6a6469ff251dd84e28289133dad1.tar.gz
yuzu-25e47738f71e6a6469ff251dd84e28289133dad1.tar.xz
yuzu-25e47738f71e6a6469ff251dd84e28289133dad1.zip
Merge pull request #8561 from Kelebek1/Audio-CoreTiming
Rework CoreTiming events
Diffstat (limited to '')
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/stream.cpp5
-rw-r--r--src/core/core_timing.cpp75
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/hardware_interrupt_manager.cpp5
-rw-r--r--src/core/hle/kernel/kernel.cpp9
-rw-r--r--src/core/hle/kernel/time_manager.cpp20
-rw-r--r--src/core/hle/service/hid/hid.cpp41
-rw-r--r--src/core/hle/service/hid/hidbus.cpp16
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/tools/freezer.cpp4
-rw-r--r--src/tests/core/core_timing.cpp5
13 files changed, 152 insertions, 82 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 2ee0a96ed..9191ca093 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <limits> 4#include <limits>
5#include <optional>
5#include <vector> 6#include <vector>
6 7
7#include "audio_core/audio_out.h" 8#include "audio_core/audio_out.h"
@@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor
88 stream = audio_out->OpenStream( 89 stream = audio_out->OpenStream(
89 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, 90 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
90 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); 91 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
91 process_event = Core::Timing::CreateEvent( 92 process_event =
92 fmt::format("AudioRenderer-Instance{}-Process", instance_number), 93 Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number),
93 [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); 94 [this](std::uintptr_t, s64, std::chrono::nanoseconds) {
95 ReleaseAndQueueBuffers();
96 return std::nullopt;
97 });
94 for (s32 i = 0; i < NUM_BUFFERS; ++i) { 98 for (s32 i = 0; i < NUM_BUFFERS; ++i) {
95 QueueMixedBuffer(i); 99 QueueMixedBuffer(i);
96 } 100 }
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index f8034b04b..cf3d94c53 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -34,9 +34,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format
34 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) 34 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
35 : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, 35 : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
36 sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { 36 sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
37 release_event = 37 release_event = Core::Timing::CreateEvent(
38 Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { 38 name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) {
39 ReleaseActiveBuffer(ns_late); 39 ReleaseActiveBuffer(ns_late);
40 return std::nullopt;
40 }); 41 });
41} 42}
42 43
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 07b8e356b..5425637f5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -22,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
22} 22}
23 23
24struct CoreTiming::Event { 24struct CoreTiming::Event {
25 u64 time; 25 s64 time;
26 u64 fifo_order; 26 u64 fifo_order;
27 std::uintptr_t user_data; 27 std::uintptr_t user_data;
28 std::weak_ptr<EventType> type; 28 std::weak_ptr<EventType> type;
29 s64 reschedule_time;
29 30
30 // Sort by time, unless the times are the same, in which case sort by 31 // Sort by time, unless the times are the same, in which case sort by
31 // the order added to the queue 32 // the order added to the queue
@@ -58,7 +59,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
58 event_fifo_id = 0; 59 event_fifo_id = 0;
59 shutting_down = false; 60 shutting_down = false;
60 ticks = 0; 61 ticks = 0;
61 const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; 62 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
63 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 64 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
63 if (is_multicore) { 65 if (is_multicore) {
64 worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); 66 worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
@@ -76,6 +78,7 @@ void CoreTiming::Shutdown() {
76 thread.join(); 78 thread.join();
77 } 79 }
78 worker_threads.clear(); 80 worker_threads.clear();
81 pause_callbacks.clear();
79 ClearPendingEvents(); 82 ClearPendingEvents();
80 has_started = false; 83 has_started = false;
81} 84}
@@ -93,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) {
93 } 96 }
94 } 97 }
95 paused_state.store(is_paused_, std::memory_order_relaxed); 98 paused_state.store(is_paused_, std::memory_order_relaxed);
99
100 if (!is_paused_) {
101 pause_end_time = GetGlobalTimeNs().count();
102 }
103
104 for (auto& cb : pause_callbacks) {
105 cb(is_paused_);
106 }
96} 107}
97 108
98void CoreTiming::SyncPause(bool is_paused_) { 109void CoreTiming::SyncPause(bool is_paused_) {
@@ -116,6 +127,14 @@ void CoreTiming::SyncPause(bool is_paused_) {
116 wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); 127 wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
117 } 128 }
118 } 129 }
130
131 if (!is_paused_) {
132 pause_end_time = GetGlobalTimeNs().count();
133 }
134
135 for (auto& cb : pause_callbacks) {
136 cb(is_paused_);
137 }
119} 138}
120 139
121bool CoreTiming::IsRunning() const { 140bool CoreTiming::IsRunning() const {
@@ -129,12 +148,30 @@ bool CoreTiming::HasPendingEvents() const {
129 148
130void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, 149void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
131 const std::shared_ptr<EventType>& event_type, 150 const std::shared_ptr<EventType>& event_type,
132 std::uintptr_t user_data) { 151 std::uintptr_t user_data, bool absolute_time) {
133 152
134 std::unique_lock main_lock(event_mutex); 153 std::unique_lock main_lock(event_mutex);
135 const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); 154 const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
136 155
137 event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); 156 event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
157 pending_events.fetch_add(1, std::memory_order_relaxed);
158
159 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
160
161 if (is_multicore) {
162 event_cv.notify_one();
163 }
164}
165
166void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
167 std::chrono::nanoseconds resched_time,
168 const std::shared_ptr<EventType>& event_type,
169 std::uintptr_t user_data, bool absolute_time) {
170 std::unique_lock main_lock(event_mutex);
171 const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
172
173 event_queue.emplace_back(
174 Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
138 pending_events.fetch_add(1, std::memory_order_relaxed); 175 pending_events.fetch_add(1, std::memory_order_relaxed);
139 176
140 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 177 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
@@ -213,6 +250,11 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
213 } 250 }
214} 251}
215 252
253void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
254 std::unique_lock main_lock(event_mutex);
255 pause_callbacks.emplace_back(std::move(callback));
256}
257
216std::optional<s64> CoreTiming::Advance() { 258std::optional<s64> CoreTiming::Advance() {
217 global_timer = GetGlobalTimeNs().count(); 259 global_timer = GetGlobalTimeNs().count();
218 260
@@ -223,14 +265,31 @@ std::optional<s64> CoreTiming::Advance() {
223 event_queue.pop_back(); 265 event_queue.pop_back();
224 266
225 if (const auto event_type{evt.type.lock()}) { 267 if (const auto event_type{evt.type.lock()}) {
226
227 event_mutex.unlock(); 268 event_mutex.unlock();
228 269
229 const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time); 270 const auto new_schedule_time{event_type->callback(
230 event_type->callback(evt.user_data, std::chrono::nanoseconds{delay}); 271 evt.user_data, evt.time,
272 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
231 273
232 event_mutex.lock(); 274 event_mutex.lock();
233 pending_events.fetch_sub(1, std::memory_order_relaxed); 275 pending_events.fetch_sub(1, std::memory_order_relaxed);
276
277 if (evt.reschedule_time != 0) {
278 // If this event was scheduled into a pause, its time now is going to be way behind.
279 // Re-set this event to continue from the end of the pause.
280 auto next_time{evt.time + evt.reschedule_time};
281 if (evt.time < pause_end_time) {
282 next_time = pause_end_time + evt.reschedule_time;
283 }
284
285 const auto next_schedule_time{new_schedule_time.has_value()
286 ? new_schedule_time.value().count()
287 : evt.reschedule_time};
288 event_queue.emplace_back(
289 Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
290 pending_events.fetch_add(1, std::memory_order_relaxed);
291 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
292 }
234 } 293 }
235 294
236 global_timer = GetGlobalTimeNs().count(); 295 global_timer = GetGlobalTimeNs().count();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index c52bffb3b..09b6ed81a 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -20,8 +20,9 @@
20namespace Core::Timing { 20namespace Core::Timing {
21 21
22/// A callback that may be scheduled for a particular core timing event. 22/// A callback that may be scheduled for a particular core timing event.
23using TimedCallback = 23using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
24 std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; 24 std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
25using PauseCallback = std::function<void(bool paused)>;
25 26
26/// Contains the characteristics of a particular event. 27/// Contains the characteristics of a particular event.
27struct EventType { 28struct EventType {
@@ -93,7 +94,15 @@ public:
93 94
94 /// Schedules an event in core timing 95 /// Schedules an event in core timing
95 void ScheduleEvent(std::chrono::nanoseconds ns_into_future, 96 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
96 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); 97 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
98 bool absolute_time = false);
99
100 /// Schedules an event which will automatically re-schedule itself with the given time, until
101 /// unscheduled
102 void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
103 std::chrono::nanoseconds resched_time,
104 const std::shared_ptr<EventType>& event_type,
105 std::uintptr_t user_data = 0, bool absolute_time = false);
97 106
98 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); 107 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
99 108
@@ -125,6 +134,9 @@ public:
125 /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. 134 /// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
126 std::optional<s64> Advance(); 135 std::optional<s64> Advance();
127 136
137 /// Register a callback function to be called when coretiming pauses.
138 void RegisterPauseCallback(PauseCallback&& callback);
139
128private: 140private:
129 struct Event; 141 struct Event;
130 142
@@ -136,7 +148,7 @@ private:
136 148
137 std::unique_ptr<Common::WallClock> clock; 149 std::unique_ptr<Common::WallClock> clock;
138 150
139 u64 global_timer = 0; 151 s64 global_timer = 0;
140 152
141 // The queue is a min-heap using std::make_heap/push_heap/pop_heap. 153 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
142 // We don't use std::priority_queue because we need to be able to serialize, unserialize and 154 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -162,10 +174,13 @@ private:
162 bool shutting_down{}; 174 bool shutting_down{};
163 bool is_multicore{}; 175 bool is_multicore{};
164 size_t pause_count{}; 176 size_t pause_count{};
177 s64 pause_end_time{};
165 178
166 /// Cycle timing 179 /// Cycle timing
167 u64 ticks{}; 180 u64 ticks{};
168 s64 downcount{}; 181 s64 downcount{};
182
183 std::vector<PauseCallback> pause_callbacks{};
169}; 184};
170 185
171/// Creates a core timing event with the given name and callback. 186/// Creates a core timing event with the given name and callback.
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index d2d968a76..d08cc3315 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,11 +11,14 @@ namespace Core::Hardware {
11 11
12InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { 12InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
13 gpu_interrupt_event = Core::Timing::CreateEvent( 13 gpu_interrupt_event = Core::Timing::CreateEvent(
14 "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { 14 "GPUInterrupt",
15 [this](std::uintptr_t message, u64 time,
16 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
15 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); 17 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
16 const u32 syncpt = static_cast<u32>(message >> 32); 18 const u32 syncpt = static_cast<u32>(message >> 32);
17 const u32 value = static_cast<u32>(message); 19 const u32 value = static_cast<u32>(message);
18 nvdrv->SignalGPUInterruptSyncpt(syncpt, value); 20 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
21 return std::nullopt;
19 }); 22 });
20} 23}
21 24
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 0009193be..7307cf262 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -234,17 +234,18 @@ struct KernelCore::Impl {
234 234
235 void InitializePreemption(KernelCore& kernel) { 235 void InitializePreemption(KernelCore& kernel) {
236 preemption_event = Core::Timing::CreateEvent( 236 preemption_event = Core::Timing::CreateEvent(
237 "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { 237 "PreemptionCallback",
238 [this, &kernel](std::uintptr_t, s64 time,
239 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
238 { 240 {
239 KScopedSchedulerLock lock(kernel); 241 KScopedSchedulerLock lock(kernel);
240 global_scheduler_context->PreemptThreads(); 242 global_scheduler_context->PreemptThreads();
241 } 243 }
242 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; 244 return std::nullopt;
243 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
244 }); 245 });
245 246
246 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; 247 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
247 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 248 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
248 } 249 }
249 250
250 void InitializeShutdownThreads() { 251 void InitializeShutdownThreads() {
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 2724c3782..5ee72c432 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -11,15 +11,17 @@
11namespace Kernel { 11namespace Kernel {
12 12
13TimeManager::TimeManager(Core::System& system_) : system{system_} { 13TimeManager::TimeManager(Core::System& system_) : system{system_} {
14 time_manager_event_type = 14 time_manager_event_type = Core::Timing::CreateEvent(
15 Core::Timing::CreateEvent("Kernel::TimeManagerCallback", 15 "Kernel::TimeManagerCallback",
16 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { 16 [this](std::uintptr_t thread_handle, s64 time,
17 KThread* thread = reinterpret_cast<KThread*>(thread_handle); 17 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
18 { 18 KThread* thread = reinterpret_cast<KThread*>(thread_handle);
19 KScopedSchedulerLock sl(system.Kernel()); 19 {
20 thread->OnTimer(); 20 KScopedSchedulerLock sl(system.Kernel());
21 } 21 thread->OnTimer();
22 }); 22 }
23 return std::nullopt;
24 });
23} 25}
24 26
25void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { 27void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 78efffc50..89bb12442 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_,
74 // Register update callbacks 74 // Register update callbacks
75 pad_update_event = Core::Timing::CreateEvent( 75 pad_update_event = Core::Timing::CreateEvent(
76 "HID::UpdatePadCallback", 76 "HID::UpdatePadCallback",
77 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 77 [this](std::uintptr_t user_data, s64 time,
78 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
78 const auto guard = LockService(); 79 const auto guard = LockService();
79 UpdateControllers(user_data, ns_late); 80 UpdateControllers(user_data, ns_late);
81 return std::nullopt;
80 }); 82 });
81 mouse_keyboard_update_event = Core::Timing::CreateEvent( 83 mouse_keyboard_update_event = Core::Timing::CreateEvent(
82 "HID::UpdateMouseKeyboardCallback", 84 "HID::UpdateMouseKeyboardCallback",
83 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 85 [this](std::uintptr_t user_data, s64 time,
86 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
84 const auto guard = LockService(); 87 const auto guard = LockService();
85 UpdateMouseKeyboard(user_data, ns_late); 88 UpdateMouseKeyboard(user_data, ns_late);
89 return std::nullopt;
86 }); 90 });
87 motion_update_event = Core::Timing::CreateEvent( 91 motion_update_event = Core::Timing::CreateEvent(
88 "HID::UpdateMotionCallback", 92 "HID::UpdateMotionCallback",
89 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 93 [this](std::uintptr_t user_data, s64 time,
94 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
90 const auto guard = LockService(); 95 const auto guard = LockService();
91 UpdateMotion(user_data, ns_late); 96 UpdateMotion(user_data, ns_late);
97 return std::nullopt;
92 }); 98 });
93 99
94 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); 100 system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
95 system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); 101 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
96 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); 102 mouse_keyboard_update_event);
103 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
104 motion_update_event);
97 105
98 system.HIDCore().ReloadInputDevices(); 106 system.HIDCore().ReloadInputDevices();
99} 107}
@@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
135 } 143 }
136 controller->OnUpdate(core_timing); 144 controller->OnUpdate(core_timing);
137 } 145 }
138
139 // If ns_late is higher than the update rate ignore the delay
140 if (ns_late > pad_update_ns) {
141 ns_late = {};
142 }
143
144 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
145} 146}
146 147
147void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, 148void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
@@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
150 151
151 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); 152 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
152 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); 153 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
153
154 // If ns_late is higher than the update rate ignore the delay
155 if (ns_late > mouse_keyboard_update_ns) {
156 ns_late = {};
157 }
158
159 core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
160} 154}
161 155
162void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 156void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
163 auto& core_timing = system.CoreTiming(); 157 auto& core_timing = system.CoreTiming();
164 158
165 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); 159 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
166
167 // If ns_late is higher than the update rate ignore the delay
168 if (ns_late > motion_update_ns) {
169 ns_late = {};
170 }
171
172 core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
173} 160}
174 161
175class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 162class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index fa6153b4c..e5e50845f 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_)
50 // Register update callbacks 50 // Register update callbacks
51 hidbus_update_event = Core::Timing::CreateEvent( 51 hidbus_update_event = Core::Timing::CreateEvent(
52 "Hidbus::UpdateCallback", 52 "Hidbus::UpdateCallback",
53 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 53 [this](std::uintptr_t user_data, s64 time,
54 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
54 const auto guard = LockService(); 55 const auto guard = LockService();
55 UpdateHidbus(user_data, ns_late); 56 UpdateHidbus(user_data, ns_late);
57 return std::nullopt;
56 }); 58 });
57 59
58 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); 60 system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
61 hidbus_update_event);
59} 62}
60 63
61HidBus::~HidBus() { 64HidBus::~HidBus() {
@@ -63,8 +66,6 @@ HidBus::~HidBus() {
63} 66}
64 67
65void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 68void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
66 auto& core_timing = system.CoreTiming();
67
68 if (is_hidbus_enabled) { 69 if (is_hidbus_enabled) {
69 for (std::size_t i = 0; i < devices.size(); ++i) { 70 for (std::size_t i = 0; i < devices.size(); ++i) {
70 if (!devices[i].is_device_initializated) { 71 if (!devices[i].is_device_initializated) {
@@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_
82 sizeof(HidbusStatusManagerEntry)); 83 sizeof(HidbusStatusManagerEntry));
83 } 84 }
84 } 85 }
85
86 // If ns_late is higher than the update rate ignore the delay
87 if (ns_late > hidbus_update_ns) {
88 ns_late = {};
89 }
90
91 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
92} 86}
93 87
94std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { 88std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2b2985a2d..5f69c8c2c 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr
67 67
68 // Schedule the screen composition events 68 // Schedule the screen composition events
69 composition_event = Core::Timing::CreateEvent( 69 composition_event = Core::Timing::CreateEvent(
70 "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { 70 "ScreenComposition",
71 [this](std::uintptr_t, s64 time,
72 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
71 const auto lock_guard = Lock(); 73 const auto lock_guard = Lock();
72 Compose(); 74 Compose();
73 75
74 const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; 76 return std::max(std::chrono::nanoseconds::zero(),
75 const auto ticks_delta = ticks - ns_late; 77 std::chrono::nanoseconds(GetNextTicks()) - ns_late);
76 const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
77
78 this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
79 }); 78 });
80 79
81 if (system.IsMulticore()) { 80 if (system.IsMulticore()) {
82 vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); 81 vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
83 } else { 82 } else {
84 system.CoreTiming().ScheduleEvent(frame_ns, composition_event); 83 system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);
85 } 84 }
86} 85}
87 86
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 5f71f0ff5..ffdbacc18 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() {
184void CheatEngine::Initialize() { 184void CheatEngine::Initialize() {
185 event = Core::Timing::CreateEvent( 185 event = Core::Timing::CreateEvent(
186 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 186 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
187 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 187 [this](std::uintptr_t user_data, s64 time,
188 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
188 FrameCallback(user_data, ns_late); 189 FrameCallback(user_data, ns_late);
190 return std::nullopt;
189 }); 191 });
190 core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); 192 core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
191 193
192 metadata.process_id = system.CurrentProcess()->GetProcessID(); 194 metadata.process_id = system.CurrentProcess()->GetProcessID();
193 metadata.title_id = system.GetCurrentProcessProgramID(); 195 metadata.title_id = system.GetCurrentProcessProgramID();
@@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late
237 MICROPROFILE_SCOPE(Cheat_Engine); 239 MICROPROFILE_SCOPE(Cheat_Engine);
238 240
239 vm.Execute(metadata); 241 vm.Execute(metadata);
240
241 core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
242} 242}
243 243
244} // namespace Core::Memory 244} // namespace Core::Memory
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 5cc99fbe4..98ebbbf32 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
53 : core_timing{core_timing_}, memory{memory_} { 53 : core_timing{core_timing_}, memory{memory_} {
54 event = Core::Timing::CreateEvent( 54 event = Core::Timing::CreateEvent(
55 "MemoryFreezer::FrameCallback", 55 "MemoryFreezer::FrameCallback",
56 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 56 [this](std::uintptr_t user_data, s64 time,
57 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
57 FrameCallback(user_data, ns_late); 58 FrameCallback(user_data, ns_late);
59 return std::nullopt;
58 }); 60 });
59 core_timing.ScheduleEvent(memory_freezer_ns, event); 61 core_timing.ScheduleEvent(memory_freezer_ns, event);
60} 62}
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index e687416a8..894975e6f 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -9,6 +9,7 @@
9#include <cstdlib> 9#include <cstdlib>
10#include <memory> 10#include <memory>
11#include <mutex> 11#include <mutex>
12#include <optional>
12#include <string> 13#include <string>
13 14
14#include "core/core.h" 15#include "core/core.h"
@@ -25,13 +26,15 @@ u64 expected_callback = 0;
25std::mutex control_mutex; 26std::mutex control_mutex;
26 27
27template <unsigned int IDX> 28template <unsigned int IDX>
28void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 29std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
30 std::chrono::nanoseconds ns_late) {
29 std::unique_lock<std::mutex> lk(control_mutex); 31 std::unique_lock<std::mutex> lk(control_mutex);
30 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 32 static_assert(IDX < CB_IDS.size(), "IDX out of range");
31 callbacks_ran_flags.set(IDX); 33 callbacks_ran_flags.set(IDX);
32 REQUIRE(CB_IDS[IDX] == user_data); 34 REQUIRE(CB_IDS[IDX] == user_data);
33 delays[IDX] = ns_late.count(); 35 delays[IDX] = ns_late.count();
34 ++expected_callback; 36 ++expected_callback;
37 return std::nullopt;
35} 38}
36 39
37struct ScopeInit final { 40struct ScopeInit final {