Loading services/surfaceflinger/Scheduler/DispSync.cpp +33 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ public: DispSyncThread(const char* name, bool showTraceDetailedInfo) : mName(name), mStop(false), mModelLocked(false), mPeriod(0), mPhase(0), mReferenceTime(0), Loading @@ -78,6 +79,11 @@ public: Mutex::Autolock lock(mMutex); mPhase = phase; if (mReferenceTime != referenceTime) { for (auto& eventListener : mEventListeners) { eventListener.mHasFired = false; } } mReferenceTime = referenceTime; if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) { // Inflate the reference time to be the most recent predicted Loading Loading @@ -106,6 +112,16 @@ public: mCond.signal(); } void lockModel() { Mutex::Autolock lock(mMutex); mModelLocked = true; } void unlockModel() { Mutex::Autolock lock(mMutex); mModelLocked = false; } virtual bool threadLoop() { status_t err; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); Loading Loading @@ -288,6 +304,7 @@ private: nsecs_t mLastEventTime; nsecs_t mLastCallbackTime; DispSync::Callback* mCallback; bool mHasFired = false; }; struct CallbackInvocation { Loading Loading @@ -335,6 +352,12 @@ private: eventListener.mName); continue; } if (eventListener.mHasFired && !mModelLocked) { eventListener.mLastEventTime = t; ALOGV("[%s] [%s] Skipping event due to already firing", mName, eventListener.mName); continue; } CallbackInvocation ci; ci.mCallback = eventListener.mCallback; ci.mEventTime = t; Loading @@ -343,6 +366,7 @@ private: callbackInvocations.push_back(ci); eventListener.mLastEventTime = t; eventListener.mLastCallbackTime = now; eventListener.mHasFired = true; } } Loading Loading @@ -410,6 +434,7 @@ private: const char* const mName; bool mStop; bool mModelLocked; nsecs_t mPeriod; nsecs_t mPhase; Loading Loading @@ -497,6 +522,7 @@ void DispSync::resetLocked() { mNumResyncSamples = 0; mFirstResyncSample = 0; mNumResyncSamplesSincePresent = 0; mThread->unlockModel(); resetErrorLocked(); } Loading @@ -519,6 +545,7 @@ bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { void DispSync::beginResync() { Mutex::Autolock lock(mMutex); ALOGV("[%s] beginResync", mName); mThread->unlockModel(); mModelUpdated = false; mNumResyncSamples = 0; } Loading Loading @@ -581,10 +608,15 @@ bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) { // resync again bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0; ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); if (modelLocked) { mThread->lockModel(); } return !modelLocked; } void DispSync::endResync() {} void DispSync::endResync() { mThread->lockModel(); } status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback, nsecs_t lastCallbackTime) { Loading services/surfaceflinger/Scheduler/IdleTimer.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -84,7 +84,10 @@ void IdleTimer::loop() { constexpr auto zero = std::chrono::steady_clock::duration::zero(); auto waitTime = triggerTime - std::chrono::steady_clock::now(); if (waitTime > zero) mCondition.wait_for(mMutex, waitTime); if (mState == TimerState::WAITING && if (mState == TimerState::RESET) { triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; } else if (mState == TimerState::WAITING && (triggerTime - std::chrono::steady_clock::now()) <= zero) { triggerTimeout = true; mState = TimerState::IDLE; Loading services/surfaceflinger/Scheduler/IdleTimer.h +20 −1 Original line number Diff line number Diff line Loading @@ -39,13 +39,31 @@ public: const TimeoutCallback& timeoutCallback); ~IdleTimer(); // Initializes and turns on the idle timer. void start(); // Stops the idle timer and any held resources. void stop(); // Resets the wakeup time and fires the reset callback. void reset(); private: // Enum to track in what state is the timer. enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 }; enum class TimerState { // The internal timer thread has been destroyed, and no state is // tracked. // Possible state transitions: RESET STOPPED = 0, // An external thread has just reset this timer. // If there is a reset callback, then that callback is fired. // Possible state transitions: STOPPED, WAITING RESET = 1, // This timer is waiting for the timeout interval to expire. // Possible state transaitions: STOPPED, RESET, IDLE WAITING = 2, // The timeout interval has expired, so we are sleeping now. // Possible state transaitions: STOPPED, RESET IDLE = 3 }; // Function that loops until the condition for stopping is met. void loop(); Loading @@ -59,6 +77,7 @@ private: // Lock used for synchronizing the waiting thread with the application thread. std::mutex mMutex; // Current timer state TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; // Interval after which timer expires. Loading services/surfaceflinger/Scheduler/Scheduler.cpp +35 −6 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, mEventControlThread = std::make_unique<impl::EventControlThread>(function); mSetIdleTimerMs = set_idle_timer_ms(0); mSupportKernelTimer = support_kernel_idle_timer(false); char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.set_idle_timer_ms", value, "0"); Loading @@ -82,10 +83,20 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, } if (mSetIdleTimerMs > 0) { if (mSupportKernelTimer) { mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs), std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( mSetIdleTimerMs), [this] { resetKernelTimerCallback(); }, [this] { expiredKernelTimerCallback(); }); } else { mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( mSetIdleTimerMs), [this] { resetTimerCallback(); }, [this] { expiredTimerCallback(); }); } mIdleTimer->start(); } } Loading Loading @@ -354,6 +365,11 @@ void Scheduler::setChangeRefreshRateCallback( mChangeRefreshRateCallback = changeRefreshRateCallback; } void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) { std::lock_guard<std::mutex> lock(mCallbackLock); mGetVsyncPeriod = getVsyncPeriod; } void Scheduler::updateFrameSkipping(const int64_t skipCount) { ATRACE_INT("FrameSkipCount", skipCount); if (mSkipCount != skipCount) { Loading @@ -370,17 +386,30 @@ void Scheduler::resetIdleTimer() { } void Scheduler::resetTimerCallback() { // We do not notify the applications about config changes when idle timer is reset. timerChangeRefreshRate(IdleTimerState::RESET); ATRACE_INT("ExpiredIdleTimer", 0); } void Scheduler::resetKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 0); std::lock_guard<std::mutex> lock(mCallbackLock); if (mGetVsyncPeriod) { resyncToHardwareVsync(false, mGetVsyncPeriod()); } } void Scheduler::expiredTimerCallback() { // We do not notify the applications about config changes when idle timer expires. timerChangeRefreshRate(IdleTimerState::EXPIRED); ATRACE_INT("ExpiredIdleTimer", 1); } void Scheduler::expiredKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 1); // Disable HW Vsync if the timer expired, as we don't need it // enabled if we're not pushing frames. disableHardwareVsync(false); } std::string Scheduler::doDump() { std::ostringstream stream; stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl; Loading services/surfaceflinger/Scheduler/Scheduler.h +13 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,7 @@ public: void updateFpsBasedOnContent(); // Callback that gets invoked when Scheduler wants to change the refresh rate. void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback); void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod); // Returns whether idle timer is enabled or not bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; } Loading Loading @@ -194,6 +195,14 @@ private: void resetTimerCallback(); // Function that is called when the timer expires. void expiredTimerCallback(); // Function that is called when the timer resets when paired with a display // driver timeout in the kernel. This enables hardware vsync when we move // out from idle. void resetKernelTimerCallback(); // Function that is called when the timer expires when paired with a display // driver timeout in the kernel. This disables hardware vsync when we move // into idle. void expiredKernelTimerCallback(); // Sets vsync period. void setVsyncPeriod(const nsecs_t period); // Idle timer feature's function to change the refresh rate. Loading Loading @@ -245,9 +254,13 @@ private: // interval, a callback is fired. Set this variable to >0 to use this feature. int64_t mSetIdleTimerMs = 0; std::unique_ptr<scheduler::IdleTimer> mIdleTimer; // Enables whether to use idle timer callbacks that support the kernel // timer. bool mSupportKernelTimer; std::mutex mCallbackLock; ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock); GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock); // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. Loading Loading
services/surfaceflinger/Scheduler/DispSync.cpp +33 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ public: DispSyncThread(const char* name, bool showTraceDetailedInfo) : mName(name), mStop(false), mModelLocked(false), mPeriod(0), mPhase(0), mReferenceTime(0), Loading @@ -78,6 +79,11 @@ public: Mutex::Autolock lock(mMutex); mPhase = phase; if (mReferenceTime != referenceTime) { for (auto& eventListener : mEventListeners) { eventListener.mHasFired = false; } } mReferenceTime = referenceTime; if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) { // Inflate the reference time to be the most recent predicted Loading Loading @@ -106,6 +112,16 @@ public: mCond.signal(); } void lockModel() { Mutex::Autolock lock(mMutex); mModelLocked = true; } void unlockModel() { Mutex::Autolock lock(mMutex); mModelLocked = false; } virtual bool threadLoop() { status_t err; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); Loading Loading @@ -288,6 +304,7 @@ private: nsecs_t mLastEventTime; nsecs_t mLastCallbackTime; DispSync::Callback* mCallback; bool mHasFired = false; }; struct CallbackInvocation { Loading Loading @@ -335,6 +352,12 @@ private: eventListener.mName); continue; } if (eventListener.mHasFired && !mModelLocked) { eventListener.mLastEventTime = t; ALOGV("[%s] [%s] Skipping event due to already firing", mName, eventListener.mName); continue; } CallbackInvocation ci; ci.mCallback = eventListener.mCallback; ci.mEventTime = t; Loading @@ -343,6 +366,7 @@ private: callbackInvocations.push_back(ci); eventListener.mLastEventTime = t; eventListener.mLastCallbackTime = now; eventListener.mHasFired = true; } } Loading Loading @@ -410,6 +434,7 @@ private: const char* const mName; bool mStop; bool mModelLocked; nsecs_t mPeriod; nsecs_t mPhase; Loading Loading @@ -497,6 +522,7 @@ void DispSync::resetLocked() { mNumResyncSamples = 0; mFirstResyncSample = 0; mNumResyncSamplesSincePresent = 0; mThread->unlockModel(); resetErrorLocked(); } Loading @@ -519,6 +545,7 @@ bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { void DispSync::beginResync() { Mutex::Autolock lock(mMutex); ALOGV("[%s] beginResync", mName); mThread->unlockModel(); mModelUpdated = false; mNumResyncSamples = 0; } Loading Loading @@ -581,10 +608,15 @@ bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) { // resync again bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0; ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); if (modelLocked) { mThread->lockModel(); } return !modelLocked; } void DispSync::endResync() {} void DispSync::endResync() { mThread->lockModel(); } status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback, nsecs_t lastCallbackTime) { Loading
services/surfaceflinger/Scheduler/IdleTimer.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -84,7 +84,10 @@ void IdleTimer::loop() { constexpr auto zero = std::chrono::steady_clock::duration::zero(); auto waitTime = triggerTime - std::chrono::steady_clock::now(); if (waitTime > zero) mCondition.wait_for(mMutex, waitTime); if (mState == TimerState::WAITING && if (mState == TimerState::RESET) { triggerTime = std::chrono::steady_clock::now() + mInterval; mState = TimerState::WAITING; } else if (mState == TimerState::WAITING && (triggerTime - std::chrono::steady_clock::now()) <= zero) { triggerTimeout = true; mState = TimerState::IDLE; Loading
services/surfaceflinger/Scheduler/IdleTimer.h +20 −1 Original line number Diff line number Diff line Loading @@ -39,13 +39,31 @@ public: const TimeoutCallback& timeoutCallback); ~IdleTimer(); // Initializes and turns on the idle timer. void start(); // Stops the idle timer and any held resources. void stop(); // Resets the wakeup time and fires the reset callback. void reset(); private: // Enum to track in what state is the timer. enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 }; enum class TimerState { // The internal timer thread has been destroyed, and no state is // tracked. // Possible state transitions: RESET STOPPED = 0, // An external thread has just reset this timer. // If there is a reset callback, then that callback is fired. // Possible state transitions: STOPPED, WAITING RESET = 1, // This timer is waiting for the timeout interval to expire. // Possible state transaitions: STOPPED, RESET, IDLE WAITING = 2, // The timeout interval has expired, so we are sleeping now. // Possible state transaitions: STOPPED, RESET IDLE = 3 }; // Function that loops until the condition for stopping is met. void loop(); Loading @@ -59,6 +77,7 @@ private: // Lock used for synchronizing the waiting thread with the application thread. std::mutex mMutex; // Current timer state TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; // Interval after which timer expires. Loading
services/surfaceflinger/Scheduler/Scheduler.cpp +35 −6 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, mEventControlThread = std::make_unique<impl::EventControlThread>(function); mSetIdleTimerMs = set_idle_timer_ms(0); mSupportKernelTimer = support_kernel_idle_timer(false); char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.set_idle_timer_ms", value, "0"); Loading @@ -82,10 +83,20 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, } if (mSetIdleTimerMs > 0) { if (mSupportKernelTimer) { mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs), std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( mSetIdleTimerMs), [this] { resetKernelTimerCallback(); }, [this] { expiredKernelTimerCallback(); }); } else { mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( mSetIdleTimerMs), [this] { resetTimerCallback(); }, [this] { expiredTimerCallback(); }); } mIdleTimer->start(); } } Loading Loading @@ -354,6 +365,11 @@ void Scheduler::setChangeRefreshRateCallback( mChangeRefreshRateCallback = changeRefreshRateCallback; } void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) { std::lock_guard<std::mutex> lock(mCallbackLock); mGetVsyncPeriod = getVsyncPeriod; } void Scheduler::updateFrameSkipping(const int64_t skipCount) { ATRACE_INT("FrameSkipCount", skipCount); if (mSkipCount != skipCount) { Loading @@ -370,17 +386,30 @@ void Scheduler::resetIdleTimer() { } void Scheduler::resetTimerCallback() { // We do not notify the applications about config changes when idle timer is reset. timerChangeRefreshRate(IdleTimerState::RESET); ATRACE_INT("ExpiredIdleTimer", 0); } void Scheduler::resetKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 0); std::lock_guard<std::mutex> lock(mCallbackLock); if (mGetVsyncPeriod) { resyncToHardwareVsync(false, mGetVsyncPeriod()); } } void Scheduler::expiredTimerCallback() { // We do not notify the applications about config changes when idle timer expires. timerChangeRefreshRate(IdleTimerState::EXPIRED); ATRACE_INT("ExpiredIdleTimer", 1); } void Scheduler::expiredKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 1); // Disable HW Vsync if the timer expired, as we don't need it // enabled if we're not pushing frames. disableHardwareVsync(false); } std::string Scheduler::doDump() { std::ostringstream stream; stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl; Loading
services/surfaceflinger/Scheduler/Scheduler.h +13 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,7 @@ public: void updateFpsBasedOnContent(); // Callback that gets invoked when Scheduler wants to change the refresh rate. void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback); void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod); // Returns whether idle timer is enabled or not bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; } Loading Loading @@ -194,6 +195,14 @@ private: void resetTimerCallback(); // Function that is called when the timer expires. void expiredTimerCallback(); // Function that is called when the timer resets when paired with a display // driver timeout in the kernel. This enables hardware vsync when we move // out from idle. void resetKernelTimerCallback(); // Function that is called when the timer expires when paired with a display // driver timeout in the kernel. This disables hardware vsync when we move // into idle. void expiredKernelTimerCallback(); // Sets vsync period. void setVsyncPeriod(const nsecs_t period); // Idle timer feature's function to change the refresh rate. Loading Loading @@ -245,9 +254,13 @@ private: // interval, a callback is fired. Set this variable to >0 to use this feature. int64_t mSetIdleTimerMs = 0; std::unique_ptr<scheduler::IdleTimer> mIdleTimer; // Enables whether to use idle timer callbacks that support the kernel // timer. bool mSupportKernelTimer; std::mutex mCallbackLock; ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock); GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock); // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. Loading