Rework CoreTiming
This commit is contained in:
		| @@ -2,6 +2,7 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-or-later | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
| #include <limits> | #include <limits> | ||||||
|  | #include <optional> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "audio_core/audio_out.h" | #include "audio_core/audio_out.h" | ||||||
| @@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor | |||||||
|     stream = audio_out->OpenStream( |     stream = audio_out->OpenStream( | ||||||
|         core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |         core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | ||||||
|         fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); |         fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); | ||||||
|     process_event = Core::Timing::CreateEvent( |     process_event = | ||||||
|         fmt::format("AudioRenderer-Instance{}-Process", instance_number), |         Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number), | ||||||
|         [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); |                                   [this](std::uintptr_t, s64, std::chrono::nanoseconds) { | ||||||
|  |                                       ReleaseAndQueueBuffers(); | ||||||
|  |                                       return std::nullopt; | ||||||
|  |                                   }); | ||||||
|     for (s32 i = 0; i < NUM_BUFFERS; ++i) { |     for (s32 i = 0; i < NUM_BUFFERS; ++i) { | ||||||
|         QueueMixedBuffer(i); |         QueueMixedBuffer(i); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -34,9 +34,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format | |||||||
|                ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) |                ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) | ||||||
|     : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, |     : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, | ||||||
|       sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { |       sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { | ||||||
|     release_event = |     release_event = Core::Timing::CreateEvent( | ||||||
|         Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { |         name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) { | ||||||
|             ReleaseActiveBuffer(ns_late); |             ReleaseActiveBuffer(ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac | |||||||
| } | } | ||||||
|  |  | ||||||
| struct CoreTiming::Event { | struct CoreTiming::Event { | ||||||
|     u64 time; |     s64 time; | ||||||
|     u64 fifo_order; |     u64 fifo_order; | ||||||
|     std::uintptr_t user_data; |     std::uintptr_t user_data; | ||||||
|     std::weak_ptr<EventType> type; |     std::weak_ptr<EventType> type; | ||||||
|  |     s64 reschedule_time; | ||||||
|  |  | ||||||
|     // Sort by time, unless the times are the same, in which case sort by |     // Sort by time, unless the times are the same, in which case sort by | ||||||
|     // the order added to the queue |     // the order added to the queue | ||||||
| @@ -58,7 +59,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { | |||||||
|     event_fifo_id = 0; |     event_fifo_id = 0; | ||||||
|     shutting_down = false; |     shutting_down = false; | ||||||
|     ticks = 0; |     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); |     ev_lost = CreateEvent("_lost_event", empty_timed_callback); | ||||||
|     if (is_multicore) { |     if (is_multicore) { | ||||||
|         worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); |         worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); | ||||||
| @@ -76,6 +78,7 @@ void CoreTiming::Shutdown() { | |||||||
|         thread.join(); |         thread.join(); | ||||||
|     } |     } | ||||||
|     worker_threads.clear(); |     worker_threads.clear(); | ||||||
|  |     pause_callbacks.clear(); | ||||||
|     ClearPendingEvents(); |     ClearPendingEvents(); | ||||||
|     has_started = false; |     has_started = false; | ||||||
| } | } | ||||||
| @@ -93,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     paused_state.store(is_paused_, std::memory_order_relaxed); |     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_) { | 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; }); |             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 { | bool CoreTiming::IsRunning() const { | ||||||
| @@ -129,12 +148,30 @@ bool CoreTiming::HasPendingEvents() const { | |||||||
|  |  | ||||||
| void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, | ||||||
|                                const std::shared_ptr<EventType>& event_type, |                                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); |     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); |     pending_events.fetch_add(1, std::memory_order_relaxed); | ||||||
|  |  | ||||||
|     std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |     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() { | std::optional<s64> CoreTiming::Advance() { | ||||||
|     global_timer = GetGlobalTimeNs().count(); |     global_timer = GetGlobalTimeNs().count(); | ||||||
|  |  | ||||||
| @@ -223,14 +265,31 @@ std::optional<s64> CoreTiming::Advance() { | |||||||
|         event_queue.pop_back(); |         event_queue.pop_back(); | ||||||
|  |  | ||||||
|         if (const auto event_type{evt.type.lock()}) { |         if (const auto event_type{evt.type.lock()}) { | ||||||
|  |  | ||||||
|             event_mutex.unlock(); |             event_mutex.unlock(); | ||||||
|  |  | ||||||
|             const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time); |             const auto new_schedule_time{event_type->callback( | ||||||
|             event_type->callback(evt.user_data, std::chrono::nanoseconds{delay}); |                 evt.user_data, evt.time, | ||||||
|  |                 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; | ||||||
|  |  | ||||||
|             event_mutex.lock(); |             event_mutex.lock(); | ||||||
|             pending_events.fetch_sub(1, std::memory_order_relaxed); |             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(); |         global_timer = GetGlobalTimeNs().count(); | ||||||
|   | |||||||
| @@ -20,8 +20,9 @@ | |||||||
| namespace Core::Timing { | namespace Core::Timing { | ||||||
|  |  | ||||||
| /// A callback that may be scheduled for a particular core timing event. | /// A callback that may be scheduled for a particular core timing event. | ||||||
| using TimedCallback = | using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( | ||||||
|     std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; |     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. | /// Contains the characteristics of a particular event. | ||||||
| struct EventType { | struct EventType { | ||||||
| @@ -93,7 +94,15 @@ public: | |||||||
|  |  | ||||||
|     /// Schedules an event in core timing |     /// Schedules an event in core timing | ||||||
|     void ScheduleEvent(std::chrono::nanoseconds ns_into_future, |     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); |     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. |     /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. | ||||||
|     std::optional<s64> Advance(); |     std::optional<s64> Advance(); | ||||||
|  |  | ||||||
|  |     /// Register a callback function to be called when coretiming pauses. | ||||||
|  |     void RegisterPauseCallback(PauseCallback&& callback); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     struct Event; |     struct Event; | ||||||
|  |  | ||||||
| @@ -136,7 +148,7 @@ private: | |||||||
|  |  | ||||||
|     std::unique_ptr<Common::WallClock> clock; |     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. |     // 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 |     // 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 shutting_down{}; | ||||||
|     bool is_multicore{}; |     bool is_multicore{}; | ||||||
|     size_t pause_count{}; |     size_t pause_count{}; | ||||||
|  |     s64 pause_end_time{}; | ||||||
|  |  | ||||||
|     /// Cycle timing |     /// Cycle timing | ||||||
|     u64 ticks{}; |     u64 ticks{}; | ||||||
|     s64 downcount{}; |     s64 downcount{}; | ||||||
|  |  | ||||||
|  |     std::vector<PauseCallback> pause_callbacks{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Creates a core timing event with the given name and callback. | /// Creates a core timing event with the given name and callback. | ||||||
|   | |||||||
| @@ -11,11 +11,14 @@ namespace Core::Hardware { | |||||||
|  |  | ||||||
| InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | ||||||
|     gpu_interrupt_event = Core::Timing::CreateEvent( |     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"); |             auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | ||||||
|             const u32 syncpt = static_cast<u32>(message >> 32); |             const u32 syncpt = static_cast<u32>(message >> 32); | ||||||
|             const u32 value = static_cast<u32>(message); |             const u32 value = static_cast<u32>(message); | ||||||
|             nvdrv->SignalGPUInterruptSyncpt(syncpt, value); |             nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -234,17 +234,19 @@ struct KernelCore::Impl { | |||||||
|  |  | ||||||
|     void InitializePreemption(KernelCore& kernel) { |     void InitializePreemption(KernelCore& kernel) { | ||||||
|         preemption_event = Core::Timing::CreateEvent( |         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); |                     KScopedSchedulerLock lock(kernel); | ||||||
|                     global_scheduler_context->PreemptThreads(); |                     global_scheduler_context->PreemptThreads(); | ||||||
|                 } |                 } | ||||||
|                 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; |                 return std::nullopt; | ||||||
|                 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |  | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|         const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; |         const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; | ||||||
|         system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |         system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), time_interval, | ||||||
|  |                                                  preemption_event); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void InitializeShutdownThreads() { |     void InitializeShutdownThreads() { | ||||||
|   | |||||||
| @@ -11,15 +11,17 @@ | |||||||
| namespace Kernel { | namespace Kernel { | ||||||
|  |  | ||||||
| TimeManager::TimeManager(Core::System& system_) : system{system_} { | TimeManager::TimeManager(Core::System& system_) : system{system_} { | ||||||
|     time_manager_event_type = |     time_manager_event_type = Core::Timing::CreateEvent( | ||||||
|         Core::Timing::CreateEvent("Kernel::TimeManagerCallback", |         "Kernel::TimeManagerCallback", | ||||||
|                                   [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { |         [this](std::uintptr_t thread_handle, s64 time, | ||||||
|                                       KThread* thread = reinterpret_cast<KThread*>(thread_handle); |                std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { | ||||||
|                                       { |             KThread* thread = reinterpret_cast<KThread*>(thread_handle); | ||||||
|                                           KScopedSchedulerLock sl(system.Kernel()); |             { | ||||||
|                                           thread->OnTimer(); |                 KScopedSchedulerLock sl(system.Kernel()); | ||||||
|                                       } |                 thread->OnTimer(); | ||||||
|                                   }); |             } | ||||||
|  |             return std::nullopt; | ||||||
|  |         }); | ||||||
| } | } | ||||||
|  |  | ||||||
| void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { | void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { | ||||||
|   | |||||||
| @@ -74,26 +74,35 @@ IAppletResource::IAppletResource(Core::System& system_, | |||||||
|     // Register update callbacks |     // Register update callbacks | ||||||
|     pad_update_event = Core::Timing::CreateEvent( |     pad_update_event = Core::Timing::CreateEvent( | ||||||
|         "HID::UpdatePadCallback", |         "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(); |             const auto guard = LockService(); | ||||||
|             UpdateControllers(user_data, ns_late); |             UpdateControllers(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|     mouse_keyboard_update_event = Core::Timing::CreateEvent( |     mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||||||
|         "HID::UpdateMouseKeyboardCallback", |         "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(); |             const auto guard = LockService(); | ||||||
|             UpdateMouseKeyboard(user_data, ns_late); |             UpdateMouseKeyboard(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|     motion_update_event = Core::Timing::CreateEvent( |     motion_update_event = Core::Timing::CreateEvent( | ||||||
|         "HID::UpdateMotionCallback", |         "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(); |             const auto guard = LockService(); | ||||||
|             UpdateMotion(user_data, ns_late); |             UpdateMotion(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); |     system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), pad_update_ns, | ||||||
|     system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); |                                              pad_update_event); | ||||||
|     system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); |     system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), mouse_keyboard_update_ns, | ||||||
|  |                                              mouse_keyboard_update_event); | ||||||
|  |     system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), motion_update_ns, | ||||||
|  |                                              motion_update_event); | ||||||
|  |  | ||||||
|     system.HIDCore().ReloadInputDevices(); |     system.HIDCore().ReloadInputDevices(); | ||||||
| } | } | ||||||
| @@ -135,13 +144,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, | |||||||
|         } |         } | ||||||
|         controller->OnUpdate(core_timing); |         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, | void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, | ||||||
| @@ -150,26 +152,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::Mouse)]->OnUpdate(core_timing); | ||||||
|     controllers[static_cast<size_t>(HidController::Keyboard)]->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) { | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||||||
|     auto& core_timing = system.CoreTiming(); |     auto& core_timing = system.CoreTiming(); | ||||||
|  |  | ||||||
|     controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); |     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> { | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | ||||||
|   | |||||||
| @@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_) | |||||||
|     // Register update callbacks |     // Register update callbacks | ||||||
|     hidbus_update_event = Core::Timing::CreateEvent( |     hidbus_update_event = Core::Timing::CreateEvent( | ||||||
|         "Hidbus::UpdateCallback", |         "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(); |             const auto guard = LockService(); | ||||||
|             UpdateHidbus(user_data, ns_late); |             UpdateHidbus(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); |     system_.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), hidbus_update_ns, | ||||||
|  |                                               hidbus_update_event); | ||||||
| } | } | ||||||
|  |  | ||||||
| HidBus::~HidBus() { | HidBus::~HidBus() { | ||||||
| @@ -63,8 +66,6 @@ HidBus::~HidBus() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||||||
|     auto& core_timing = system.CoreTiming(); |  | ||||||
|  |  | ||||||
|     if (is_hidbus_enabled) { |     if (is_hidbus_enabled) { | ||||||
|         for (std::size_t i = 0; i < devices.size(); ++i) { |         for (std::size_t i = 0; i < devices.size(); ++i) { | ||||||
|             if (!devices[i].is_device_initializated) { |             if (!devices[i].is_device_initializated) { | ||||||
| @@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_ | |||||||
|                         sizeof(HidbusStatusManagerEntry)); |                         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 { | std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { | ||||||
|   | |||||||
| @@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr | |||||||
|  |  | ||||||
|     // Schedule the screen composition events |     // Schedule the screen composition events | ||||||
|     composition_event = Core::Timing::CreateEvent( |     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(); |             const auto lock_guard = Lock(); | ||||||
|             Compose(); |             Compose(); | ||||||
|  |  | ||||||
|             const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; |             return std::chrono::nanoseconds(GetNextTicks()) - ns_late; | ||||||
|             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); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     if (system.IsMulticore()) { |     if (system.IsMulticore()) { | ||||||
|         vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); |         vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); | ||||||
|     } else { |     } else { | ||||||
|         system.CoreTiming().ScheduleEvent(frame_ns, composition_event); |         system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), frame_ns, | ||||||
|  |                                                  composition_event); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() { | |||||||
| void CheatEngine::Initialize() { | void CheatEngine::Initialize() { | ||||||
|     event = Core::Timing::CreateEvent( |     event = Core::Timing::CreateEvent( | ||||||
|         "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |         "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); |             FrameCallback(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|     core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); |     core_timing.ScheduleLoopingEvent(std::chrono::nanoseconds(0), CHEAT_ENGINE_NS, event); | ||||||
|  |  | ||||||
|     metadata.process_id = system.CurrentProcess()->GetProcessID(); |     metadata.process_id = system.CurrentProcess()->GetProcessID(); | ||||||
|     metadata.title_id = system.GetCurrentProcessProgramID(); |     metadata.title_id = system.GetCurrentProcessProgramID(); | ||||||
| @@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late | |||||||
|     MICROPROFILE_SCOPE(Cheat_Engine); |     MICROPROFILE_SCOPE(Cheat_Engine); | ||||||
|  |  | ||||||
|     vm.Execute(metadata); |     vm.Execute(metadata); | ||||||
|  |  | ||||||
|     core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Core::Memory | } // namespace Core::Memory | ||||||
|   | |||||||
| @@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||||||
|     : core_timing{core_timing_}, memory{memory_} { |     : core_timing{core_timing_}, memory{memory_} { | ||||||
|     event = Core::Timing::CreateEvent( |     event = Core::Timing::CreateEvent( | ||||||
|         "MemoryFreezer::FrameCallback", |         "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); |             FrameCallback(user_data, ns_late); | ||||||
|  |             return std::nullopt; | ||||||
|         }); |         }); | ||||||
|     core_timing.ScheduleEvent(memory_freezer_ns, event); |     core_timing.ScheduleEvent(memory_freezer_ns, event); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | #include <optional> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| @@ -25,13 +26,15 @@ u64 expected_callback = 0; | |||||||
| std::mutex control_mutex; | std::mutex control_mutex; | ||||||
|  |  | ||||||
| template <unsigned int IDX> | 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); |     std::unique_lock<std::mutex> lk(control_mutex); | ||||||
|     static_assert(IDX < CB_IDS.size(), "IDX out of range"); |     static_assert(IDX < CB_IDS.size(), "IDX out of range"); | ||||||
|     callbacks_ran_flags.set(IDX); |     callbacks_ran_flags.set(IDX); | ||||||
|     REQUIRE(CB_IDS[IDX] == user_data); |     REQUIRE(CB_IDS[IDX] == user_data); | ||||||
|     delays[IDX] = ns_late.count(); |     delays[IDX] = ns_late.count(); | ||||||
|     ++expected_callback; |     ++expected_callback; | ||||||
|  |     return std::nullopt; | ||||||
| } | } | ||||||
|  |  | ||||||
| struct ScopeInit final { | struct ScopeInit final { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user