Merge pull request #8650 from Kelebek1/vsync
[Coretiming/NVNFlinger] Improve multi-core vsync timing, and core timing accuracy
This commit is contained in:
		| @@ -54,6 +54,10 @@ public: | |||||||
|         is_set = false; |         is_set = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] bool IsSet() { | ||||||
|  |         return is_set; | ||||||
|  |     } | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     std::condition_variable condvar; |     std::condition_variable condvar; | ||||||
|     std::mutex mutex; |     std::mutex mutex; | ||||||
|   | |||||||
| @@ -134,6 +134,7 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | |||||||
|                                       std::chrono::nanoseconds resched_time, |                                       std::chrono::nanoseconds resched_time, | ||||||
|                                       const std::shared_ptr<EventType>& event_type, |                                       const std::shared_ptr<EventType>& event_type, | ||||||
|                                       std::uintptr_t user_data, bool absolute_time) { |                                       std::uintptr_t user_data, bool absolute_time) { | ||||||
|  |     { | ||||||
|         std::scoped_lock scope{basic_lock}; |         std::scoped_lock scope{basic_lock}; | ||||||
|         const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; |         const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; | ||||||
|  |  | ||||||
| @@ -141,6 +142,9 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | |||||||
|             Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); |             Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); | ||||||
|  |  | ||||||
|         std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |         std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     event.Set(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | ||||||
| @@ -229,17 +233,17 @@ std::optional<s64> CoreTiming::Advance() { | |||||||
|             basic_lock.lock(); |             basic_lock.lock(); | ||||||
|  |  | ||||||
|             if (evt.reschedule_time != 0) { |             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() |                 const auto next_schedule_time{new_schedule_time.has_value() | ||||||
|                                                   ? new_schedule_time.value().count() |                                                   ? new_schedule_time.value().count() | ||||||
|                                                   : evt.reschedule_time}; |                                                   : evt.reschedule_time}; | ||||||
|  |  | ||||||
|  |                 // 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 + next_schedule_time}; | ||||||
|  |                 if (evt.time < pause_end_time) { | ||||||
|  |                     next_time = pause_end_time + next_schedule_time; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 event_queue.emplace_back( |                 event_queue.emplace_back( | ||||||
|                     Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); |                     Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); | ||||||
|                 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |                 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||||||
| @@ -250,8 +254,7 @@ std::optional<s64> CoreTiming::Advance() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!event_queue.empty()) { |     if (!event_queue.empty()) { | ||||||
|         const s64 next_time = event_queue.front().time - global_timer; |         return event_queue.front().time; | ||||||
|         return next_time; |  | ||||||
|     } else { |     } else { | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|     } |     } | ||||||
| @@ -264,11 +267,29 @@ void CoreTiming::ThreadLoop() { | |||||||
|             paused_set = false; |             paused_set = false; | ||||||
|             const auto next_time = Advance(); |             const auto next_time = Advance(); | ||||||
|             if (next_time) { |             if (next_time) { | ||||||
|                 if (*next_time > 0) { |                 // There are more events left in the queue, wait until the next event. | ||||||
|                     std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); |                 const auto wait_time = *next_time - GetGlobalTimeNs().count(); | ||||||
|                     event.WaitFor(next_time_ns); |                 if (wait_time > 0) { | ||||||
|  |                     // Assume a timer resolution of 1ms. | ||||||
|  |                     static constexpr s64 TimerResolutionNS = 1000000; | ||||||
|  |  | ||||||
|  |                     // Sleep in discrete intervals of the timer resolution, and spin the rest. | ||||||
|  |                     const auto sleep_time = wait_time - (wait_time % TimerResolutionNS); | ||||||
|  |                     if (sleep_time > 0) { | ||||||
|  |                         event.WaitFor(std::chrono::nanoseconds(sleep_time)); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) { | ||||||
|  |                         // Yield to reduce thread starvation. | ||||||
|  |                         std::this_thread::yield(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (event.IsSet()) { | ||||||
|  |                         event.Reset(); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|  |                 // Queue is empty, wait until another event is scheduled and signals us to continue. | ||||||
|                 wait_set = true; |                 wait_set = true; | ||||||
|                 event.Wait(); |                 event.Wait(); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -38,20 +38,16 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) { | |||||||
|  |  | ||||||
|     Common::SetCurrentThreadName(name.c_str()); |     Common::SetCurrentThreadName(name.c_str()); | ||||||
|     Common::SetCurrentThreadPriority(Common::ThreadPriority::High); |     Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||||||
|     s64 delay = 0; |  | ||||||
|     while (!stop_token.stop_requested()) { |     while (!stop_token.stop_requested()) { | ||||||
|  |         vsync_signal.wait(false); | ||||||
|  |         vsync_signal.store(false); | ||||||
|  |  | ||||||
|         guard->lock(); |         guard->lock(); | ||||||
|         const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); |  | ||||||
|         Compose(); |         Compose(); | ||||||
|         const auto ticks = GetNextTicks(); |  | ||||||
|         const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); |  | ||||||
|         const s64 time_passed = time_end - time_start; |  | ||||||
|         const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); |  | ||||||
|         guard->unlock(); |         guard->unlock(); | ||||||
|         if (next_time > 0) { |  | ||||||
|             std::this_thread::sleep_for(std::chrono::nanoseconds{next_time}); |  | ||||||
|         } |  | ||||||
|         delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -66,27 +62,41 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr | |||||||
|     guard = std::make_shared<std::mutex>(); |     guard = std::make_shared<std::mutex>(); | ||||||
|  |  | ||||||
|     // Schedule the screen composition events |     // Schedule the screen composition events | ||||||
|     composition_event = Core::Timing::CreateEvent( |     multi_composition_event = Core::Timing::CreateEvent( | ||||||
|  |         "ScreenComposition", | ||||||
|  |         [this](std::uintptr_t, s64 time, | ||||||
|  |                std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||||||
|  |             vsync_signal.store(true); | ||||||
|  |             vsync_signal.notify_all(); | ||||||
|  |             return std::chrono::nanoseconds(GetNextTicks()); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     single_composition_event = Core::Timing::CreateEvent( | ||||||
|         "ScreenComposition", |         "ScreenComposition", | ||||||
|         [this](std::uintptr_t, s64 time, |         [this](std::uintptr_t, s64 time, | ||||||
|                std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { |                std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||||||
|             const auto lock_guard = Lock(); |             const auto lock_guard = Lock(); | ||||||
|             Compose(); |             Compose(); | ||||||
|  |  | ||||||
|             return std::max(std::chrono::nanoseconds::zero(), |             return std::chrono::nanoseconds(GetNextTicks()); | ||||||
|                             std::chrono::nanoseconds(GetNextTicks()) - ns_late); |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     if (system.IsMulticore()) { |     if (system.IsMulticore()) { | ||||||
|  |         system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event); | ||||||
|         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().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event); |         system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| NVFlinger::~NVFlinger() { | NVFlinger::~NVFlinger() { | ||||||
|     if (!system.IsMulticore()) { |     if (system.IsMulticore()) { | ||||||
|         system.CoreTiming().UnscheduleEvent(composition_event, 0); |         system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); | ||||||
|  |         vsync_thread.request_stop(); | ||||||
|  |         vsync_signal.store(true); | ||||||
|  |         vsync_signal.notify_all(); | ||||||
|  |     } else { | ||||||
|  |         system.CoreTiming().UnscheduleEvent(single_composition_event, {}); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (auto& display : displays) { |     for (auto& display : displays) { | ||||||
|   | |||||||
| @@ -126,12 +126,15 @@ private: | |||||||
|     u32 swap_interval = 1; |     u32 swap_interval = 1; | ||||||
|  |  | ||||||
|     /// Event that handles screen composition. |     /// Event that handles screen composition. | ||||||
|     std::shared_ptr<Core::Timing::EventType> composition_event; |     std::shared_ptr<Core::Timing::EventType> multi_composition_event; | ||||||
|  |     std::shared_ptr<Core::Timing::EventType> single_composition_event; | ||||||
|  |  | ||||||
|     std::shared_ptr<std::mutex> guard; |     std::shared_ptr<std::mutex> guard; | ||||||
|  |  | ||||||
|     Core::System& system; |     Core::System& system; | ||||||
|  |  | ||||||
|  |     std::atomic<bool> vsync_signal; | ||||||
|  |  | ||||||
|     std::jthread vsync_thread; |     std::jthread vsync_thread; | ||||||
|  |  | ||||||
|     KernelHelpers::ServiceContext service_context; |     KernelHelpers::ServiceContext service_context; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user