diff options
| author | Fernando S <fsahmkow27@gmail.com> | 2022-07-10 10:29:56 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-10 10:29:56 +0200 | 
| commit | 25e47738f71e6a6469ff251dd84e28289133dad1 (patch) | |
| tree | c431a3b1ee04fb4da60e08ab21d78a5813e9b528 | |
| parent | c765d5be0bf77af65b254db09997d47a9bddd46e (diff) | |
| parent | b23c6b456c3fd09a4dd04c4174f784f73b7513bc (diff) | |
Merge pull request #8561 from Kelebek1/Audio-CoreTiming
Rework CoreTiming events
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 10 | ||||
| -rw-r--r-- | src/audio_core/stream.cpp | 5 | ||||
| -rw-r--r-- | src/core/core_timing.cpp | 75 | ||||
| -rw-r--r-- | src/core/core_timing.h | 23 | ||||
| -rw-r--r-- | src/core/hardware_interrupt_manager.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/kernel/time_manager.cpp | 20 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hid.cpp | 41 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hidbus.cpp | 16 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/nvflinger.cpp | 13 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.cpp | 8 | ||||
| -rw-r--r-- | src/core/tools/freezer.cpp | 4 | ||||
| -rw-r--r-- | src/tests/core/core_timing.cpp | 5 | 
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 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include <limits> +#include <optional>  #include <vector>  #include "audio_core/audio_out.h" @@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor      stream = audio_out->OpenStream(          core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,          fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); -    process_event = Core::Timing::CreateEvent( -        fmt::format("AudioRenderer-Instance{}-Process", instance_number), -        [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); +    process_event = +        Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number), +                                  [this](std::uintptr_t, s64, std::chrono::nanoseconds) { +                                      ReleaseAndQueueBuffers(); +                                      return std::nullopt; +                                  });      for (s32 i = 0; i < NUM_BUFFERS; ++i) {          QueueMixedBuffer(i);      } 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                 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)      : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},        sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { -    release_event = -        Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { +    release_event = Core::Timing::CreateEvent( +        name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) {              ReleaseActiveBuffer(ns_late); +            return std::nullopt;          });  } 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  }  struct CoreTiming::Event { -    u64 time; +    s64 time;      u64 fifo_order;      std::uintptr_t user_data;      std::weak_ptr<EventType> type; +    s64 reschedule_time;      // Sort by time, unless the times are the same, in which case sort by      // the order added to the queue @@ -58,7 +59,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {      event_fifo_id = 0;      shutting_down = false;      ticks = 0; -    const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; +    const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) +        -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };      ev_lost = CreateEvent("_lost_event", empty_timed_callback);      if (is_multicore) {          worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); @@ -76,6 +78,7 @@ void CoreTiming::Shutdown() {          thread.join();      }      worker_threads.clear(); +    pause_callbacks.clear();      ClearPendingEvents();      has_started = false;  } @@ -93,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) {          }      }      paused_state.store(is_paused_, std::memory_order_relaxed); + +    if (!is_paused_) { +        pause_end_time = GetGlobalTimeNs().count(); +    } + +    for (auto& cb : pause_callbacks) { +        cb(is_paused_); +    }  }  void CoreTiming::SyncPause(bool is_paused_) { @@ -116,6 +127,14 @@ void CoreTiming::SyncPause(bool is_paused_) {              wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });          }      } + +    if (!is_paused_) { +        pause_end_time = GetGlobalTimeNs().count(); +    } + +    for (auto& cb : pause_callbacks) { +        cb(is_paused_); +    }  }  bool CoreTiming::IsRunning() const { @@ -129,12 +148,30 @@ bool CoreTiming::HasPendingEvents() const {  void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,                                 const std::shared_ptr<EventType>& event_type, -                               std::uintptr_t user_data) { +                               std::uintptr_t user_data, bool absolute_time) {      std::unique_lock main_lock(event_mutex); -    const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); +    const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; -    event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); +    event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); +    pending_events.fetch_add(1, std::memory_order_relaxed); + +    std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + +    if (is_multicore) { +        event_cv.notify_one(); +    } +} + +void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, +                                      std::chrono::nanoseconds resched_time, +                                      const std::shared_ptr<EventType>& event_type, +                                      std::uintptr_t user_data, bool absolute_time) { +    std::unique_lock main_lock(event_mutex); +    const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; + +    event_queue.emplace_back( +        Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});      pending_events.fetch_add(1, std::memory_order_relaxed);      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) {      }  } +void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { +    std::unique_lock main_lock(event_mutex); +    pause_callbacks.emplace_back(std::move(callback)); +} +  std::optional<s64> CoreTiming::Advance() {      global_timer = GetGlobalTimeNs().count(); @@ -223,14 +265,31 @@ std::optional<s64> CoreTiming::Advance() {          event_queue.pop_back();          if (const auto event_type{evt.type.lock()}) { -              event_mutex.unlock(); -            const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time); -            event_type->callback(evt.user_data, std::chrono::nanoseconds{delay}); +            const auto new_schedule_time{event_type->callback( +                evt.user_data, evt.time, +                std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};              event_mutex.lock();              pending_events.fetch_sub(1, std::memory_order_relaxed); + +            if (evt.reschedule_time != 0) { +                // If this event was scheduled into a pause, its time now is going to be way behind. +                // Re-set this event to continue from the end of the pause. +                auto next_time{evt.time + evt.reschedule_time}; +                if (evt.time < pause_end_time) { +                    next_time = pause_end_time + evt.reschedule_time; +                } + +                const auto next_schedule_time{new_schedule_time.has_value() +                                                  ? new_schedule_time.value().count() +                                                  : evt.reschedule_time}; +                event_queue.emplace_back( +                    Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); +                pending_events.fetch_add(1, std::memory_order_relaxed); +                std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); +            }          }          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 @@  namespace Core::Timing {  /// A callback that may be scheduled for a particular core timing event. -using TimedCallback = -    std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; +using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( +    std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; +using PauseCallback = std::function<void(bool paused)>;  /// Contains the characteristics of a particular event.  struct EventType { @@ -93,7 +94,15 @@ public:      /// Schedules an event in core timing      void ScheduleEvent(std::chrono::nanoseconds ns_into_future, -                       const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); +                       const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, +                       bool absolute_time = false); + +    /// Schedules an event which will automatically re-schedule itself with the given time, until +    /// unscheduled +    void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, +                              std::chrono::nanoseconds resched_time, +                              const std::shared_ptr<EventType>& event_type, +                              std::uintptr_t user_data = 0, bool absolute_time = false);      void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); @@ -125,6 +134,9 @@ public:      /// Checks for events manually and returns time in nanoseconds for next event, threadsafe.      std::optional<s64> Advance(); +    /// Register a callback function to be called when coretiming pauses. +    void RegisterPauseCallback(PauseCallback&& callback); +  private:      struct Event; @@ -136,7 +148,7 @@ private:      std::unique_ptr<Common::WallClock> clock; -    u64 global_timer = 0; +    s64 global_timer = 0;      // The queue is a min-heap using std::make_heap/push_heap/pop_heap.      // We don't use std::priority_queue because we need to be able to serialize, unserialize and @@ -162,10 +174,13 @@ private:      bool shutting_down{};      bool is_multicore{};      size_t pause_count{}; +    s64 pause_end_time{};      /// Cycle timing      u64 ticks{};      s64 downcount{}; + +    std::vector<PauseCallback> pause_callbacks{};  };  /// 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 {  InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {      gpu_interrupt_event = Core::Timing::CreateEvent( -        "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { +        "GPUInterrupt", +        [this](std::uintptr_t message, u64 time, +               std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {              auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");              const u32 syncpt = static_cast<u32>(message >> 32);              const u32 value = static_cast<u32>(message);              nvdrv->SignalGPUInterruptSyncpt(syncpt, value); +            return std::nullopt;          });  } 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 {      void InitializePreemption(KernelCore& kernel) {          preemption_event = Core::Timing::CreateEvent( -            "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { +            "PreemptionCallback", +            [this, &kernel](std::uintptr_t, s64 time, +                            std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {                  {                      KScopedSchedulerLock lock(kernel);                      global_scheduler_context->PreemptThreads();                  } -                const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; -                system.CoreTiming().ScheduleEvent(time_interval, preemption_event); +                return std::nullopt;              });          const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; -        system.CoreTiming().ScheduleEvent(time_interval, preemption_event); +        system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);      }      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 @@  namespace Kernel {  TimeManager::TimeManager(Core::System& system_) : system{system_} { -    time_manager_event_type = -        Core::Timing::CreateEvent("Kernel::TimeManagerCallback", -                                  [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { -                                      KThread* thread = reinterpret_cast<KThread*>(thread_handle); -                                      { -                                          KScopedSchedulerLock sl(system.Kernel()); -                                          thread->OnTimer(); -                                      } -                                  }); +    time_manager_event_type = Core::Timing::CreateEvent( +        "Kernel::TimeManagerCallback", +        [this](std::uintptr_t thread_handle, s64 time, +               std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { +            KThread* thread = reinterpret_cast<KThread*>(thread_handle); +            { +                KScopedSchedulerLock sl(system.Kernel()); +                thread->OnTimer(); +            } +            return std::nullopt; +        });  }  void 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_,      // Register update callbacks      pad_update_event = Core::Timing::CreateEvent(          "HID::UpdatePadCallback", -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              const auto guard = LockService();              UpdateControllers(user_data, ns_late); +            return std::nullopt;          });      mouse_keyboard_update_event = Core::Timing::CreateEvent(          "HID::UpdateMouseKeyboardCallback", -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              const auto guard = LockService();              UpdateMouseKeyboard(user_data, ns_late); +            return std::nullopt;          });      motion_update_event = Core::Timing::CreateEvent(          "HID::UpdateMotionCallback", -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              const auto guard = LockService();              UpdateMotion(user_data, ns_late); +            return std::nullopt;          }); -    system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); -    system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); -    system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); +    system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); +    system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, +                                             mouse_keyboard_update_event); +    system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, +                                             motion_update_event);      system.HIDCore().ReloadInputDevices();  } @@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,          }          controller->OnUpdate(core_timing);      } - -    // If ns_late is higher than the update rate ignore the delay -    if (ns_late > pad_update_ns) { -        ns_late = {}; -    } - -    core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);  }  void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, @@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,      controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);      controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); - -    // If ns_late is higher than the update rate ignore the delay -    if (ns_late > mouse_keyboard_update_ns) { -        ns_late = {}; -    } - -    core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);  }  void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {      auto& core_timing = system.CoreTiming();      controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); - -    // If ns_late is higher than the update rate ignore the delay -    if (ns_late > motion_update_ns) { -        ns_late = {}; -    } - -    core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);  }  class 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_)      // Register update callbacks      hidbus_update_event = Core::Timing::CreateEvent(          "Hidbus::UpdateCallback", -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              const auto guard = LockService();              UpdateHidbus(user_data, ns_late); +            return std::nullopt;          }); -    system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); +    system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, +                                              hidbus_update_event);  }  HidBus::~HidBus() { @@ -63,8 +66,6 @@ HidBus::~HidBus() {  }  void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { -    auto& core_timing = system.CoreTiming(); -      if (is_hidbus_enabled) {          for (std::size_t i = 0; i < devices.size(); ++i) {              if (!devices[i].is_device_initializated) { @@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_                          sizeof(HidbusStatusManagerEntry));          }      } - -    // If ns_late is higher than the update rate ignore the delay -    if (ns_late > hidbus_update_ns) { -        ns_late = {}; -    } - -    core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);  }  std::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      // Schedule the screen composition events      composition_event = Core::Timing::CreateEvent( -        "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { +        "ScreenComposition", +        [this](std::uintptr_t, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              const auto lock_guard = Lock();              Compose(); -            const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; -            const auto ticks_delta = ticks - ns_late; -            const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); - -            this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); +            return std::max(std::chrono::nanoseconds::zero(), +                            std::chrono::nanoseconds(GetNextTicks()) - ns_late);          });      if (system.IsMulticore()) {          vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });      } else { -        system.CoreTiming().ScheduleEvent(frame_ns, composition_event); +        system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);      }  } 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() {  void CheatEngine::Initialize() {      event = Core::Timing::CreateEvent(          "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              FrameCallback(user_data, ns_late); +            return std::nullopt;          }); -    core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); +    core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);      metadata.process_id = system.CurrentProcess()->GetProcessID();      metadata.title_id = system.GetCurrentProcessProgramID(); @@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late      MICROPROFILE_SCOPE(Cheat_Engine);      vm.Execute(metadata); - -    core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);  }  } // 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      : core_timing{core_timing_}, memory{memory_} {      event = Core::Timing::CreateEvent(          "MemoryFreezer::FrameCallback", -        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +        [this](std::uintptr_t user_data, s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {              FrameCallback(user_data, ns_late); +            return std::nullopt;          });      core_timing.ScheduleEvent(memory_freezer_ns, event);  } 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 @@  #include <cstdlib>  #include <memory>  #include <mutex> +#include <optional>  #include <string>  #include "core/core.h" @@ -25,13 +26,15 @@ u64 expected_callback = 0;  std::mutex control_mutex;  template <unsigned int IDX> -void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time, +                                                             std::chrono::nanoseconds ns_late) {      std::unique_lock<std::mutex> lk(control_mutex);      static_assert(IDX < CB_IDS.size(), "IDX out of range");      callbacks_ran_flags.set(IDX);      REQUIRE(CB_IDS[IDX] == user_data);      delays[IDX] = ns_late.count();      ++expected_callback; +    return std::nullopt;  }  struct ScopeInit final { | 
