Loading services/surfaceflinger/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -165,7 +165,7 @@ filegroup { "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", "Scheduler/VSyncDispatch.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", Loading services/surfaceflinger/Scheduler/VSyncDispatch.h +9 −102 Original line number Diff line number Diff line Loading @@ -16,14 +16,9 @@ #pragma once #include <android-base/thread_annotations.h> #include <utils/Timers.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "StrongTyping.h" Loading @@ -34,68 +29,6 @@ class VSyncTracker; enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error }; enum class CancelResult { Cancelled, TooLate, Error }; namespace impl { // VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch // hoisted to public for unit testing. class VSyncDispatchEntry { public: // This is the state of the entry. There are 3 states, armed, running, disarmed. // Valid transition: disarmed -> armed ( when scheduled ) // Valid transition: armed -> running -> disarmed ( when timer is called) // Valid transition: armed -> disarmed ( when cancelled ) VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn); std::string_view name() const; // Start: functions that are not threadsafe. // Return the last vsync time this callback was invoked. std::optional<nsecs_t> lastExecutedVsyncTarget() const; // This moves the state from disarmed->armed and will calculate the wakeupTime. nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now); // This will update armed entries with the latest vsync information. Entry remains armed. void update(VSyncTracker& tracker, nsecs_t now); // This will return empty if not armed, or the next calculated wakeup time if armed. // It will not update the wakeupTime. std::optional<nsecs_t> wakeupTime() const; // This moves state from armed->disarmed. void disarm(); // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); // End: functions that are not threadsafe. // Invoke the callback with the timestamp, moving the state from running->disarmed. void callback(nsecs_t timestamp); // Block calling thread while the callback is executing. void ensureNotRunning(); private: void arm(VSyncTracker& tracker, nsecs_t now); std::string const mName; std::function<void(nsecs_t)> const mCallback; nsecs_t mWorkDuration; nsecs_t mEarliestVsync; struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::mutex mRunningMutex; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; }; } // namespace impl /* * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events. */ Loading @@ -103,15 +36,7 @@ class VSyncDispatch { public: using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>; /* creates a VsyncDispatch. * \param [in] a timekeeper object for dispatching events. * \param [in] a tracker object that is monitoring expected vsync events. * \param [in] a tunable in nanoseconds that indicates when events that fall close together * should be dispatched in one timer wakeup. */ explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack); ~VSyncDispatch(); virtual ~VSyncDispatch(); /* * Registers a callback that will be called at designated points on the vsync timeline. Loading @@ -126,8 +51,8 @@ public: * invocation of callbackFn. * */ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName); virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) = 0; /* * Unregisters a callback. Loading @@ -135,7 +60,7 @@ public: * \param [in] token The callback to unregister. * */ void unregisterCallback(CallbackToken token); virtual void unregisterCallback(CallbackToken token) = 0; /* * Schedules the registered callback to be dispatched. Loading Loading @@ -164,7 +89,8 @@ public: * if a callback was dispatched for the predictedVsync already. * A ScheduleResult::Error if there was another error. */ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync); virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) = 0; /* Cancels a scheduled callback, if possible. * Loading @@ -173,31 +99,12 @@ public: * A CancelResult::Cancelled if the callback was successfully cancelled. * A CancelResult::Error if there was an pre-condition violation. */ CancelResult cancel(CallbackToken token); virtual CancelResult cancel(CallbackToken token) = 0; private: protected: VSyncDispatch() = default; VSyncDispatch(VSyncDispatch const&) = delete; VSyncDispatch& operator=(VSyncDispatch const&) = delete; using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VSyncTracker& mTracker; nsecs_t const mTimerSlack; std::mutex mutable mMutex; size_t mCallbackToken GUARDED_BY(mMutex) = 0; CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; }; /* Loading services/surfaceflinger/Scheduler/VSyncDispatch.cpp→services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +35 −32 Original line number Diff line number Diff line Loading @@ -19,34 +19,35 @@ #include <vector> #include "TimeKeeper.h" #include "VSyncDispatch.h" #include "VSyncDispatchTimerQueue.h" #include "VSyncTracker.h" namespace android::scheduler { VSyncDispatch::~VSyncDispatch() = default; VSyncTracker::~VSyncTracker() = default; TimeKeeper::~TimeKeeper() = default; impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name, VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& cb) : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {} std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const { std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const { return mLastDispatchTime; } std::string_view impl::VSyncDispatchEntry::name() const { std::string_view VSyncDispatchTimerQueueEntry::name() const { return mName; } std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const { std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const { if (!mArmedInfo) { return {}; } return {mArmedInfo->mActualWakeupTime}; } nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now) { mWorkDuration = workDuration; mEarliestVsync = earliestVsync; Loading @@ -54,30 +55,30 @@ nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earlies return mArmedInfo->mActualWakeupTime; } void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { return; } arm(tracker, now); } void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) { auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; } void impl::VSyncDispatchEntry::disarm() { void VSyncDispatchTimerQueueEntry::disarm() { mArmedInfo.reset(); } nsecs_t impl::VSyncDispatchEntry::executing() { nsecs_t VSyncDispatchTimerQueueEntry::executing() { mLastDispatchTime = mArmedInfo->mActualVsyncTime; disarm(); return *mLastDispatchTime; } void impl::VSyncDispatchEntry::callback(nsecs_t t) { void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) { { std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = true; Loading @@ -90,36 +91,37 @@ void impl::VSyncDispatchEntry::callback(nsecs_t t) { mCv.notify_all(); } void impl::VSyncDispatchEntry::ensureNotRunning() { void VSyncDispatchTimerQueueEntry::ensureNotRunning() { std::unique_lock<std::mutex> lk(mRunningMutex); mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; }); } VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack) VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack) : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {} VSyncDispatch::~VSyncDispatch() { VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() { std::lock_guard<decltype(mMutex)> lk(mMutex); cancelTimer(); } void VSyncDispatch::cancelTimer() { void VSyncDispatchTimerQueue::cancelTimer() { mIntendedWakeupTime = kInvalidTime; mTimeKeeper->alarmCancel(); } void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) { void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) { mIntendedWakeupTime = targetTime; mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now); mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this), targetTime - now); } void VSyncDispatch::rearmTimer(nsecs_t now) { void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { rearmTimerSkippingUpdateFor(now, mCallbacks.end()); } void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { std::optional<nsecs_t> min; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; Loading @@ -143,9 +145,9 @@ void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now, } } void VSyncDispatch::timerCallback() { void VSyncDispatchTimerQueue::timerCallback() { struct Invocation { std::shared_ptr<impl::VSyncDispatchEntry> callback; std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t timestamp; }; std::vector<Invocation> invocations; Loading Loading @@ -174,18 +176,19 @@ void VSyncDispatch::timerCallback() { } } VSyncDispatch::CallbackToken VSyncDispatch::registerCallback( VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback( std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) { std::lock_guard<decltype(mMutex)> lk(mMutex); return CallbackToken{ mCallbacks .emplace(++mCallbackToken, std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn)) std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName, callbackFn)) .first->first}; } void VSyncDispatch::unregisterCallback(CallbackToken token) { std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr; void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); Loading @@ -200,7 +203,7 @@ void VSyncDispatch::unregisterCallback(CallbackToken token) { } } ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration, ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) { auto result = ScheduleResult::Error; { Loading Loading @@ -228,7 +231,7 @@ ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration return result; } CancelResult VSyncDispatch::cancel(CallbackToken token) { CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); Loading services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <android-base/thread_annotations.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "VSyncDispatch.h" namespace android::scheduler { // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in // VSyncDispatchTimerQueue hoisted to public for unit testing. class VSyncDispatchTimerQueueEntry { public: // This is the state of the entry. There are 3 states, armed, running, disarmed. // Valid transition: disarmed -> armed ( when scheduled ) // Valid transition: armed -> running -> disarmed ( when timer is called) // Valid transition: armed -> disarmed ( when cancelled ) VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn); std::string_view name() const; // Start: functions that are not threadsafe. // Return the last vsync time this callback was invoked. std::optional<nsecs_t> lastExecutedVsyncTarget() const; // This moves the state from disarmed->armed and will calculate the wakeupTime. nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now); // This will update armed entries with the latest vsync information. Entry remains armed. void update(VSyncTracker& tracker, nsecs_t now); // This will return empty if not armed, or the next calculated wakeup time if armed. // It will not update the wakeupTime. std::optional<nsecs_t> wakeupTime() const; // This moves state from armed->disarmed. void disarm(); // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); // End: functions that are not threadsafe. // Invoke the callback with the timestamp, moving the state from running->disarmed. void callback(nsecs_t timestamp); // Block calling thread while the callback is executing. void ensureNotRunning(); private: void arm(VSyncTracker& tracker, nsecs_t now); std::string const mName; std::function<void(nsecs_t)> const mCallback; nsecs_t mWorkDuration; nsecs_t mEarliestVsync; struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::mutex mRunningMutex; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; }; /* * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface * using a single timer queue. */ class VSyncDispatchTimerQueue : public VSyncDispatch { public: explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack); ~VSyncDispatchTimerQueue(); CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) final; void unregisterCallback(CallbackToken token) final; ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final; CancelResult cancel(CallbackToken token) final; private: VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete; VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete; using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VSyncTracker& mTracker; nsecs_t const mTimerSlack; std::mutex mutable mMutex; size_t mCallbackToken GUARDED_BY(mMutex) = 0; CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; }; } // namespace android::scheduler services/surfaceflinger/tests/unittests/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ cc_test { "FrameTracerTest.cpp", "TransactionApplicationTest.cpp", "StrongTypingTest.cpp", "VSyncDispatchTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplay.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", Loading Loading
services/surfaceflinger/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -165,7 +165,7 @@ filegroup { "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", "Scheduler/VSyncDispatch.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", Loading
services/surfaceflinger/Scheduler/VSyncDispatch.h +9 −102 Original line number Diff line number Diff line Loading @@ -16,14 +16,9 @@ #pragma once #include <android-base/thread_annotations.h> #include <utils/Timers.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "StrongTyping.h" Loading @@ -34,68 +29,6 @@ class VSyncTracker; enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error }; enum class CancelResult { Cancelled, TooLate, Error }; namespace impl { // VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch // hoisted to public for unit testing. class VSyncDispatchEntry { public: // This is the state of the entry. There are 3 states, armed, running, disarmed. // Valid transition: disarmed -> armed ( when scheduled ) // Valid transition: armed -> running -> disarmed ( when timer is called) // Valid transition: armed -> disarmed ( when cancelled ) VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn); std::string_view name() const; // Start: functions that are not threadsafe. // Return the last vsync time this callback was invoked. std::optional<nsecs_t> lastExecutedVsyncTarget() const; // This moves the state from disarmed->armed and will calculate the wakeupTime. nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now); // This will update armed entries with the latest vsync information. Entry remains armed. void update(VSyncTracker& tracker, nsecs_t now); // This will return empty if not armed, or the next calculated wakeup time if armed. // It will not update the wakeupTime. std::optional<nsecs_t> wakeupTime() const; // This moves state from armed->disarmed. void disarm(); // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); // End: functions that are not threadsafe. // Invoke the callback with the timestamp, moving the state from running->disarmed. void callback(nsecs_t timestamp); // Block calling thread while the callback is executing. void ensureNotRunning(); private: void arm(VSyncTracker& tracker, nsecs_t now); std::string const mName; std::function<void(nsecs_t)> const mCallback; nsecs_t mWorkDuration; nsecs_t mEarliestVsync; struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::mutex mRunningMutex; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; }; } // namespace impl /* * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events. */ Loading @@ -103,15 +36,7 @@ class VSyncDispatch { public: using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>; /* creates a VsyncDispatch. * \param [in] a timekeeper object for dispatching events. * \param [in] a tracker object that is monitoring expected vsync events. * \param [in] a tunable in nanoseconds that indicates when events that fall close together * should be dispatched in one timer wakeup. */ explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack); ~VSyncDispatch(); virtual ~VSyncDispatch(); /* * Registers a callback that will be called at designated points on the vsync timeline. Loading @@ -126,8 +51,8 @@ public: * invocation of callbackFn. * */ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName); virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) = 0; /* * Unregisters a callback. Loading @@ -135,7 +60,7 @@ public: * \param [in] token The callback to unregister. * */ void unregisterCallback(CallbackToken token); virtual void unregisterCallback(CallbackToken token) = 0; /* * Schedules the registered callback to be dispatched. Loading Loading @@ -164,7 +89,8 @@ public: * if a callback was dispatched for the predictedVsync already. * A ScheduleResult::Error if there was another error. */ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync); virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) = 0; /* Cancels a scheduled callback, if possible. * Loading @@ -173,31 +99,12 @@ public: * A CancelResult::Cancelled if the callback was successfully cancelled. * A CancelResult::Error if there was an pre-condition violation. */ CancelResult cancel(CallbackToken token); virtual CancelResult cancel(CallbackToken token) = 0; private: protected: VSyncDispatch() = default; VSyncDispatch(VSyncDispatch const&) = delete; VSyncDispatch& operator=(VSyncDispatch const&) = delete; using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VSyncTracker& mTracker; nsecs_t const mTimerSlack; std::mutex mutable mMutex; size_t mCallbackToken GUARDED_BY(mMutex) = 0; CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; }; /* Loading
services/surfaceflinger/Scheduler/VSyncDispatch.cpp→services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +35 −32 Original line number Diff line number Diff line Loading @@ -19,34 +19,35 @@ #include <vector> #include "TimeKeeper.h" #include "VSyncDispatch.h" #include "VSyncDispatchTimerQueue.h" #include "VSyncTracker.h" namespace android::scheduler { VSyncDispatch::~VSyncDispatch() = default; VSyncTracker::~VSyncTracker() = default; TimeKeeper::~TimeKeeper() = default; impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name, VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& cb) : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {} std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const { std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const { return mLastDispatchTime; } std::string_view impl::VSyncDispatchEntry::name() const { std::string_view VSyncDispatchTimerQueueEntry::name() const { return mName; } std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const { std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const { if (!mArmedInfo) { return {}; } return {mArmedInfo->mActualWakeupTime}; } nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now) { mWorkDuration = workDuration; mEarliestVsync = earliestVsync; Loading @@ -54,30 +55,30 @@ nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earlies return mArmedInfo->mActualWakeupTime; } void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { return; } arm(tracker, now); } void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) { auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; } void impl::VSyncDispatchEntry::disarm() { void VSyncDispatchTimerQueueEntry::disarm() { mArmedInfo.reset(); } nsecs_t impl::VSyncDispatchEntry::executing() { nsecs_t VSyncDispatchTimerQueueEntry::executing() { mLastDispatchTime = mArmedInfo->mActualVsyncTime; disarm(); return *mLastDispatchTime; } void impl::VSyncDispatchEntry::callback(nsecs_t t) { void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) { { std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = true; Loading @@ -90,36 +91,37 @@ void impl::VSyncDispatchEntry::callback(nsecs_t t) { mCv.notify_all(); } void impl::VSyncDispatchEntry::ensureNotRunning() { void VSyncDispatchTimerQueueEntry::ensureNotRunning() { std::unique_lock<std::mutex> lk(mRunningMutex); mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; }); } VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack) VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack) : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {} VSyncDispatch::~VSyncDispatch() { VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() { std::lock_guard<decltype(mMutex)> lk(mMutex); cancelTimer(); } void VSyncDispatch::cancelTimer() { void VSyncDispatchTimerQueue::cancelTimer() { mIntendedWakeupTime = kInvalidTime; mTimeKeeper->alarmCancel(); } void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) { void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) { mIntendedWakeupTime = targetTime; mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now); mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this), targetTime - now); } void VSyncDispatch::rearmTimer(nsecs_t now) { void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { rearmTimerSkippingUpdateFor(now, mCallbacks.end()); } void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { std::optional<nsecs_t> min; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; Loading @@ -143,9 +145,9 @@ void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now, } } void VSyncDispatch::timerCallback() { void VSyncDispatchTimerQueue::timerCallback() { struct Invocation { std::shared_ptr<impl::VSyncDispatchEntry> callback; std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t timestamp; }; std::vector<Invocation> invocations; Loading Loading @@ -174,18 +176,19 @@ void VSyncDispatch::timerCallback() { } } VSyncDispatch::CallbackToken VSyncDispatch::registerCallback( VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback( std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) { std::lock_guard<decltype(mMutex)> lk(mMutex); return CallbackToken{ mCallbacks .emplace(++mCallbackToken, std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn)) std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName, callbackFn)) .first->first}; } void VSyncDispatch::unregisterCallback(CallbackToken token) { std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr; void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); Loading @@ -200,7 +203,7 @@ void VSyncDispatch::unregisterCallback(CallbackToken token) { } } ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration, ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) { auto result = ScheduleResult::Error; { Loading Loading @@ -228,7 +231,7 @@ ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration return result; } CancelResult VSyncDispatch::cancel(CallbackToken token) { CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); Loading
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <android-base/thread_annotations.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "VSyncDispatch.h" namespace android::scheduler { // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in // VSyncDispatchTimerQueue hoisted to public for unit testing. class VSyncDispatchTimerQueueEntry { public: // This is the state of the entry. There are 3 states, armed, running, disarmed. // Valid transition: disarmed -> armed ( when scheduled ) // Valid transition: armed -> running -> disarmed ( when timer is called) // Valid transition: armed -> disarmed ( when cancelled ) VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn); std::string_view name() const; // Start: functions that are not threadsafe. // Return the last vsync time this callback was invoked. std::optional<nsecs_t> lastExecutedVsyncTarget() const; // This moves the state from disarmed->armed and will calculate the wakeupTime. nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now); // This will update armed entries with the latest vsync information. Entry remains armed. void update(VSyncTracker& tracker, nsecs_t now); // This will return empty if not armed, or the next calculated wakeup time if armed. // It will not update the wakeupTime. std::optional<nsecs_t> wakeupTime() const; // This moves state from armed->disarmed. void disarm(); // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); // End: functions that are not threadsafe. // Invoke the callback with the timestamp, moving the state from running->disarmed. void callback(nsecs_t timestamp); // Block calling thread while the callback is executing. void ensureNotRunning(); private: void arm(VSyncTracker& tracker, nsecs_t now); std::string const mName; std::function<void(nsecs_t)> const mCallback; nsecs_t mWorkDuration; nsecs_t mEarliestVsync; struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::mutex mRunningMutex; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; }; /* * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface * using a single timer queue. */ class VSyncDispatchTimerQueue : public VSyncDispatch { public: explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker, nsecs_t timerSlack); ~VSyncDispatchTimerQueue(); CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) final; void unregisterCallback(CallbackToken token) final; ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final; CancelResult cancel(CallbackToken token) final; private: VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete; VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete; using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; VSyncTracker& mTracker; nsecs_t const mTimerSlack; std::mutex mutable mMutex; size_t mCallbackToken GUARDED_BY(mMutex) = 0; CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; }; } // namespace android::scheduler
services/surfaceflinger/tests/unittests/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ cc_test { "FrameTracerTest.cpp", "TransactionApplicationTest.cpp", "StrongTypingTest.cpp", "VSyncDispatchTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplay.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", Loading