Loading services/surfaceflinger/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ filegroup { "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", "Scheduler/VSyncDispatch.cpp", "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", Loading services/surfaceflinger/Scheduler/TimeKeeper.h 0 → 100644 +53 −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 <utils/Timers.h> #include <functional> namespace android::scheduler { /* * TimeKeeper is the interface for a single-shot timer primitive. */ class TimeKeeper { public: virtual ~TimeKeeper(); /* * Arms callback to fired in time nanoseconds. * There is only one timer, and subsequent calls will reset the callback function and the time. */ virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0; /* * Cancels an existing pending callback */ virtual void alarmCancel() = 0; /* * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time. */ virtual nsecs_t now() const = 0; protected: TimeKeeper(TimeKeeper const&) = delete; TimeKeeper& operator=(TimeKeeper const&) = delete; TimeKeeper() = default; }; } // namespace android::scheduler services/surfaceflinger/Scheduler/VSyncDispatch.cpp 0 → 100644 +285 −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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Trace.h> #include <vector> #include "TimeKeeper.h" #include "VSyncDispatch.h" #include "VSyncTracker.h" namespace android::scheduler { VSyncTracker::~VSyncTracker() = default; TimeKeeper::~TimeKeeper() = default; impl::VSyncDispatchEntry::VSyncDispatchEntry(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 { return mLastDispatchTime; } std::string_view impl::VSyncDispatchEntry::name() const { return mName; } std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const { if (!mArmedInfo) { return {}; } return {mArmedInfo->mActualWakeupTime}; } nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now) { mWorkDuration = workDuration; mEarliestVsync = earliestVsync; arm(tracker, now); return mArmedInfo->mActualWakeupTime; } void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { return; } arm(tracker, now); } void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) { auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; } void impl::VSyncDispatchEntry::disarm() { mArmedInfo.reset(); } nsecs_t impl::VSyncDispatchEntry::executing() { mLastDispatchTime = mArmedInfo->mActualVsyncTime; disarm(); return *mLastDispatchTime; } void impl::VSyncDispatchEntry::callback(nsecs_t t) { { std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = true; } mCallback(t); std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = false; mCv.notify_all(); } void impl::VSyncDispatchEntry::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) : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {} VSyncDispatch::~VSyncDispatch() { std::lock_guard<decltype(mMutex)> lk(mMutex); cancelTimer(); } void VSyncDispatch::cancelTimer() { mIntendedWakeupTime = kInvalidTime; mTimeKeeper->alarmCancel(); } void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) { mIntendedWakeupTime = targetTime; mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now); } void VSyncDispatch::rearmTimer(nsecs_t now) { rearmTimerSkippingUpdateFor(now, mCallbacks.end()); } void VSyncDispatch::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; if (!callback->wakeupTime()) { continue; } if (it != skipUpdateIt) { callback->update(mTracker, now); } auto const wakeupTime = *callback->wakeupTime(); if (!min || (min && *min > wakeupTime)) { min = wakeupTime; } } if (min && (min < mIntendedWakeupTime)) { setTimer(*min, now); } else { cancelTimer(); } } void VSyncDispatch::timerCallback() { struct Invocation { std::shared_ptr<impl::VSyncDispatchEntry> callback; nsecs_t timestamp; }; std::vector<Invocation> invocations; { std::lock_guard<decltype(mMutex)> lk(mMutex); for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto const wakeupTime = callback->wakeupTime(); if (!wakeupTime) { continue; } if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) { callback->executing(); invocations.emplace_back( Invocation{callback, *callback->lastExecutedVsyncTarget()}); } } mIntendedWakeupTime = kInvalidTime; rearmTimer(mTimeKeeper->now()); } for (auto const& invocation : invocations) { invocation.callback->callback(invocation.timestamp); } } VSyncDispatch::CallbackToken VSyncDispatch::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)) .first->first}; } void VSyncDispatch::unregisterCallback(CallbackToken token) { std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it != mCallbacks.end()) { entry = it->second; mCallbacks.erase(it); } } if (entry) { entry->ensureNotRunning(); } } ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) { auto result = ScheduleResult::Error; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { return result; } auto& callback = it->second; result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled; auto const now = mTimeKeeper->now(); auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now); if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) { return ScheduleResult::CannotSchedule; } if (wakeupTime < mIntendedWakeupTime - mTimerSlack) { rearmTimerSkippingUpdateFor(now, it); } } return result; } CancelResult VSyncDispatch::cancel(CallbackToken token) { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { return CancelResult::Error; } auto& callback = it->second; if (callback->wakeupTime()) { callback->disarm(); mIntendedWakeupTime = kInvalidTime; rearmTimer(mTimeKeeper->now()); return CancelResult::Cancelled; } return CancelResult::TooLate; } VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch, std::function<void(nsecs_t)> const& callbackFn, std::string const& callbackName) : mDispatch(dispatch), mToken(dispatch.registerCallback(callbackFn, callbackName)), mValidToken(true) {} VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other) : mDispatch(other.mDispatch), mToken(std::move(other.mToken)), mValidToken(std::move(other.mValidToken)) { other.mValidToken = false; } VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) { mDispatch = std::move(other.mDispatch); mToken = std::move(other.mToken); mValidToken = std::move(other.mValidToken); other.mValidToken = false; return *this; } VSyncCallbackRegistration::~VSyncCallbackRegistration() { if (mValidToken) mDispatch.get().unregisterCallback(mToken); } ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) { if (!mValidToken) return ScheduleResult::Error; return mDispatch.get().schedule(mToken, workDuration, earliestVsync); } CancelResult VSyncCallbackRegistration::cancel() { if (!mValidToken) return CancelResult::Error; return mDispatch.get().cancel(mToken); } } // namespace android::scheduler services/surfaceflinger/Scheduler/VSyncDispatch.h 0 → 100644 +230 −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 <utils/Timers.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "StrongTyping.h" namespace android::scheduler { class TimeKeeper; 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. */ 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(); /* * Registers a callback that will be called at designated points on the vsync timeline. * The callback can be scheduled, rescheduled targeting vsync times, or cancelled. * The token returned must be cleaned up via unregisterCallback. * * \param [in] callbackFn A function to schedule for callback. The resources needed to invoke * callbackFn must have lifetimes encompassing the lifetime of the * CallbackToken returned. * \param [in] callbackName A human-readable, unique name to identify the callback. * \return A token that can be used to schedule, reschedule, or cancel the * invocation of callbackFn. * */ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName); /* * Unregisters a callback. * * \param [in] token The callback to unregister. * */ void unregisterCallback(CallbackToken token); /* * Schedules the registered callback to be dispatched. * * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event. * * The caller designates the earliest vsync event that should be targeted by the earliestVsync * parameter. * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync * is the first vsync event time where ( predictedVsync >= earliestVsync ). * * If (workDuration - earliestVsync) is in the past, or if a callback has already been * dispatched for the predictedVsync, an error will be returned. * * It is valid to reschedule a callback to a different time. * * \param [in] token The callback to schedule. * \param [in] workDuration The time before the actual vsync time to invoke the callback * associated with token. * \param [in] earliestVsync The targeted display time. This will be snapped to the closest * predicted vsync time after earliestVsync. * \return A ScheduleResult::Scheduled if callback was scheduled. * A ScheduleResult::ReScheduled if callback was rescheduled. * A ScheduleResult::CannotSchedule * if (workDuration - earliestVsync) is in the past, or * 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); /* Cancels a scheduled callback, if possible. * * \param [in] token The callback to cancel. * \return A CancelResult::TooLate if the callback was already dispatched. * A CancelResult::Cancelled if the callback was successfully cancelled. * A CancelResult::Error if there was an pre-condition violation. */ CancelResult cancel(CallbackToken token); private: 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; }; /* * Helper class to operate on registered callbacks. It is up to user of the class to ensure * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation. */ class VSyncCallbackRegistration { public: VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn, std::string const& callbackName); VSyncCallbackRegistration(VSyncCallbackRegistration&&); VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&); ~VSyncCallbackRegistration(); // See documentation for VSyncDispatch::schedule. ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync); // See documentation for VSyncDispatch::cancel. CancelResult cancel(); private: VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete; VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete; std::reference_wrapper<VSyncDispatch> mDispatch; VSyncDispatch::CallbackToken mToken; bool mValidToken; }; } // namespace android::scheduler services/surfaceflinger/Scheduler/VSyncTracker.h 0 → 100644 +56 −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 <utils/Timers.h> #include "VSyncDispatch.h" namespace android::scheduler { /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. */ class VSyncTracker { public: virtual ~VSyncTracker(); /* * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence) * to the model. * * \param [in] timestamp The timestamp when the vsync signal was. */ virtual void addVsyncTimestamp(nsecs_t timestamp) = 0; /* * Access the next anticipated vsync time such that the anticipated time >= timePoint. * This will always give the best accurate at the time of calling; multiple * calls with the same timePoint might give differing values if the internal model * is updated. * * \param [in] timePoint The point in time after which to estimate a vsync event. * \return A prediction of the timestamp of a vsync event. */ virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0; protected: VSyncTracker(VSyncTracker const&) = delete; VSyncTracker& operator=(VSyncTracker const&) = delete; VSyncTracker() = default; }; } // namespace android::scheduler Loading
services/surfaceflinger/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ filegroup { "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", "Scheduler/VSyncDispatch.cpp", "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", Loading
services/surfaceflinger/Scheduler/TimeKeeper.h 0 → 100644 +53 −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 <utils/Timers.h> #include <functional> namespace android::scheduler { /* * TimeKeeper is the interface for a single-shot timer primitive. */ class TimeKeeper { public: virtual ~TimeKeeper(); /* * Arms callback to fired in time nanoseconds. * There is only one timer, and subsequent calls will reset the callback function and the time. */ virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0; /* * Cancels an existing pending callback */ virtual void alarmCancel() = 0; /* * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time. */ virtual nsecs_t now() const = 0; protected: TimeKeeper(TimeKeeper const&) = delete; TimeKeeper& operator=(TimeKeeper const&) = delete; TimeKeeper() = default; }; } // namespace android::scheduler
services/surfaceflinger/Scheduler/VSyncDispatch.cpp 0 → 100644 +285 −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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <utils/Trace.h> #include <vector> #include "TimeKeeper.h" #include "VSyncDispatch.h" #include "VSyncTracker.h" namespace android::scheduler { VSyncTracker::~VSyncTracker() = default; TimeKeeper::~TimeKeeper() = default; impl::VSyncDispatchEntry::VSyncDispatchEntry(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 { return mLastDispatchTime; } std::string_view impl::VSyncDispatchEntry::name() const { return mName; } std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const { if (!mArmedInfo) { return {}; } return {mArmedInfo->mActualWakeupTime}; } nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, nsecs_t now) { mWorkDuration = workDuration; mEarliestVsync = earliestVsync; arm(tracker, now); return mArmedInfo->mActualWakeupTime; } void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { return; } arm(tracker, now); } void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) { auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; } void impl::VSyncDispatchEntry::disarm() { mArmedInfo.reset(); } nsecs_t impl::VSyncDispatchEntry::executing() { mLastDispatchTime = mArmedInfo->mActualVsyncTime; disarm(); return *mLastDispatchTime; } void impl::VSyncDispatchEntry::callback(nsecs_t t) { { std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = true; } mCallback(t); std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = false; mCv.notify_all(); } void impl::VSyncDispatchEntry::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) : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {} VSyncDispatch::~VSyncDispatch() { std::lock_guard<decltype(mMutex)> lk(mMutex); cancelTimer(); } void VSyncDispatch::cancelTimer() { mIntendedWakeupTime = kInvalidTime; mTimeKeeper->alarmCancel(); } void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) { mIntendedWakeupTime = targetTime; mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now); } void VSyncDispatch::rearmTimer(nsecs_t now) { rearmTimerSkippingUpdateFor(now, mCallbacks.end()); } void VSyncDispatch::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; if (!callback->wakeupTime()) { continue; } if (it != skipUpdateIt) { callback->update(mTracker, now); } auto const wakeupTime = *callback->wakeupTime(); if (!min || (min && *min > wakeupTime)) { min = wakeupTime; } } if (min && (min < mIntendedWakeupTime)) { setTimer(*min, now); } else { cancelTimer(); } } void VSyncDispatch::timerCallback() { struct Invocation { std::shared_ptr<impl::VSyncDispatchEntry> callback; nsecs_t timestamp; }; std::vector<Invocation> invocations; { std::lock_guard<decltype(mMutex)> lk(mMutex); for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto const wakeupTime = callback->wakeupTime(); if (!wakeupTime) { continue; } if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) { callback->executing(); invocations.emplace_back( Invocation{callback, *callback->lastExecutedVsyncTarget()}); } } mIntendedWakeupTime = kInvalidTime; rearmTimer(mTimeKeeper->now()); } for (auto const& invocation : invocations) { invocation.callback->callback(invocation.timestamp); } } VSyncDispatch::CallbackToken VSyncDispatch::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)) .first->first}; } void VSyncDispatch::unregisterCallback(CallbackToken token) { std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it != mCallbacks.end()) { entry = it->second; mCallbacks.erase(it); } } if (entry) { entry->ensureNotRunning(); } } ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) { auto result = ScheduleResult::Error; { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { return result; } auto& callback = it->second; result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled; auto const now = mTimeKeeper->now(); auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now); if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) { return ScheduleResult::CannotSchedule; } if (wakeupTime < mIntendedWakeupTime - mTimerSlack) { rearmTimerSkippingUpdateFor(now, it); } } return result; } CancelResult VSyncDispatch::cancel(CallbackToken token) { std::lock_guard<decltype(mMutex)> lk(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { return CancelResult::Error; } auto& callback = it->second; if (callback->wakeupTime()) { callback->disarm(); mIntendedWakeupTime = kInvalidTime; rearmTimer(mTimeKeeper->now()); return CancelResult::Cancelled; } return CancelResult::TooLate; } VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch, std::function<void(nsecs_t)> const& callbackFn, std::string const& callbackName) : mDispatch(dispatch), mToken(dispatch.registerCallback(callbackFn, callbackName)), mValidToken(true) {} VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other) : mDispatch(other.mDispatch), mToken(std::move(other.mToken)), mValidToken(std::move(other.mValidToken)) { other.mValidToken = false; } VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) { mDispatch = std::move(other.mDispatch); mToken = std::move(other.mToken); mValidToken = std::move(other.mValidToken); other.mValidToken = false; return *this; } VSyncCallbackRegistration::~VSyncCallbackRegistration() { if (mValidToken) mDispatch.get().unregisterCallback(mToken); } ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) { if (!mValidToken) return ScheduleResult::Error; return mDispatch.get().schedule(mToken, workDuration, earliestVsync); } CancelResult VSyncCallbackRegistration::cancel() { if (!mValidToken) return CancelResult::Error; return mDispatch.get().cancel(mToken); } } // namespace android::scheduler
services/surfaceflinger/Scheduler/VSyncDispatch.h 0 → 100644 +230 −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 <utils/Timers.h> #include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> #include <unordered_map> #include "StrongTyping.h" namespace android::scheduler { class TimeKeeper; 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. */ 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(); /* * Registers a callback that will be called at designated points on the vsync timeline. * The callback can be scheduled, rescheduled targeting vsync times, or cancelled. * The token returned must be cleaned up via unregisterCallback. * * \param [in] callbackFn A function to schedule for callback. The resources needed to invoke * callbackFn must have lifetimes encompassing the lifetime of the * CallbackToken returned. * \param [in] callbackName A human-readable, unique name to identify the callback. * \return A token that can be used to schedule, reschedule, or cancel the * invocation of callbackFn. * */ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn, std::string callbackName); /* * Unregisters a callback. * * \param [in] token The callback to unregister. * */ void unregisterCallback(CallbackToken token); /* * Schedules the registered callback to be dispatched. * * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event. * * The caller designates the earliest vsync event that should be targeted by the earliestVsync * parameter. * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync * is the first vsync event time where ( predictedVsync >= earliestVsync ). * * If (workDuration - earliestVsync) is in the past, or if a callback has already been * dispatched for the predictedVsync, an error will be returned. * * It is valid to reschedule a callback to a different time. * * \param [in] token The callback to schedule. * \param [in] workDuration The time before the actual vsync time to invoke the callback * associated with token. * \param [in] earliestVsync The targeted display time. This will be snapped to the closest * predicted vsync time after earliestVsync. * \return A ScheduleResult::Scheduled if callback was scheduled. * A ScheduleResult::ReScheduled if callback was rescheduled. * A ScheduleResult::CannotSchedule * if (workDuration - earliestVsync) is in the past, or * 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); /* Cancels a scheduled callback, if possible. * * \param [in] token The callback to cancel. * \return A CancelResult::TooLate if the callback was already dispatched. * A CancelResult::Cancelled if the callback was successfully cancelled. * A CancelResult::Error if there was an pre-condition violation. */ CancelResult cancel(CallbackToken token); private: 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; }; /* * Helper class to operate on registered callbacks. It is up to user of the class to ensure * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation. */ class VSyncCallbackRegistration { public: VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn, std::string const& callbackName); VSyncCallbackRegistration(VSyncCallbackRegistration&&); VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&); ~VSyncCallbackRegistration(); // See documentation for VSyncDispatch::schedule. ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync); // See documentation for VSyncDispatch::cancel. CancelResult cancel(); private: VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete; VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete; std::reference_wrapper<VSyncDispatch> mDispatch; VSyncDispatch::CallbackToken mToken; bool mValidToken; }; } // namespace android::scheduler
services/surfaceflinger/Scheduler/VSyncTracker.h 0 → 100644 +56 −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 <utils/Timers.h> #include "VSyncDispatch.h" namespace android::scheduler { /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. */ class VSyncTracker { public: virtual ~VSyncTracker(); /* * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence) * to the model. * * \param [in] timestamp The timestamp when the vsync signal was. */ virtual void addVsyncTimestamp(nsecs_t timestamp) = 0; /* * Access the next anticipated vsync time such that the anticipated time >= timePoint. * This will always give the best accurate at the time of calling; multiple * calls with the same timePoint might give differing values if the internal model * is updated. * * \param [in] timePoint The point in time after which to estimate a vsync event. * \return A prediction of the timestamp of a vsync event. */ virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0; protected: VSyncTracker(VSyncTracker const&) = delete; VSyncTracker& operator=(VSyncTracker const&) = delete; VSyncTracker() = default; }; } // namespace android::scheduler