From 645b1f7ffb41d21a60765d1ec54ba82f14a36a59 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 10 Jun 2014 14:43:32 -0700 Subject: [PATCH] Replace "lower power mode" experiment This replaces the previous low-power mode experiment, which discarded refresh events, with a new experiment that alters the refresh period. (see also I2849e5ea335c0d2509fea1c315392bce7f20451d ) The feature is enabled by specifying a nonzero value for the "refresh skip count", which indicates the number of periods to skip. For example, the command: adb shell service call SurfaceFlinger 1016 i32 1 sets a skip count of '1', yielding a 30Hz refresh rate on a device with a 60Hz display. Changing the last value to '2' would set the refresh to 20Hz. '0' returns to the default behavior. Bug 15523257 Change-Id: I00039c22a55750e74035644c63800e4bee1c774a --- services/surfaceflinger/DispSync.cpp | 49 ++++++++++------------ services/surfaceflinger/DispSync.h | 10 ++++- services/surfaceflinger/SurfaceFlinger.cpp | 7 +--- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp index c60834ea2..12da9a5cb 100644 --- a/services/surfaceflinger/DispSync.cpp +++ b/services/surfaceflinger/DispSync.cpp @@ -53,10 +53,7 @@ class DispSyncThread: public Thread { public: DispSyncThread(): - mLowPowerMode(false), mStop(false), - mLastVsyncSent(false), - mLastBufferFull(false), mPeriod(0), mPhase(0), mWakeupLatency(0) { @@ -140,18 +137,7 @@ public: } if (callbackInvocations.size() > 0) { - if (mLowPowerMode) { - if (!mLastVsyncSent || !mLastBufferFull) { - fireCallbackInvocations(callbackInvocations); - mLastVsyncSent = true; - } else - mLastVsyncSent = false; - } else { - fireCallbackInvocations(callbackInvocations); - } - mLastBufferFull = true; - } else { - mLastBufferFull = false; + fireCallbackInvocations(callbackInvocations); } } @@ -205,7 +191,6 @@ public: return !mEventListeners.empty(); } - bool mLowPowerMode; private: struct EventListener { @@ -278,8 +263,6 @@ private: } bool mStop; - bool mLastVsyncSent; - bool mLastBufferFull; nsecs_t mPeriod; nsecs_t mPhase; @@ -304,8 +287,10 @@ private: bool mParity; }; -DispSync::DispSync() { - mThread = new DispSyncThread(); +DispSync::DispSync() : + mRefreshSkipCount(0), + mThread(new DispSyncThread()) { + mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); reset(); @@ -404,8 +389,11 @@ status_t DispSync::addEventListener(nsecs_t phase, return mThread->addEventListener(phase, callback); } -void DispSync::setLowPowerMode(bool enabled) { - mThread->mLowPowerMode = enabled; +void DispSync::setRefreshSkipCount(int count) { + Mutex::Autolock lock(mMutex); + ALOGD("setRefreshSkipCount(%d)", count); + mRefreshSkipCount = count; + updateModelLocked(); } status_t DispSync::removeEventListener(const sp& callback) { @@ -456,6 +444,9 @@ void DispSync::updateModelLocked() { ATRACE_INT64("DispSync:Phase", mPhase); } + // Artificially inflate the period if requested. + mPeriod += mPeriod * mRefreshSkipCount; + mThread->updateModel(mPeriod, mPhase); } } @@ -465,15 +456,19 @@ void DispSync::updateErrorLocked() { return; } + // Need to compare present fences against the un-adjusted refresh period, + // since they might arrive between two events. + nsecs_t period = mPeriod / (1 + mRefreshSkipCount); + int numErrSamples = 0; nsecs_t sqErrSum = 0; for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { nsecs_t sample = mPresentTimes[i]; if (sample > mPhase) { - nsecs_t sampleErr = (sample - mPhase) % mPeriod; - if (sampleErr > mPeriod / 2) { - sampleErr -= mPeriod; + nsecs_t sampleErr = (sample - mPhase) % period; + if (sampleErr > period / 2) { + sampleErr -= period; } sqErrSum += sampleErr * sampleErr; numErrSamples++; @@ -510,8 +505,8 @@ void DispSync::dump(String8& result) const { Mutex::Autolock lock(mMutex); result.appendFormat("present fences are %s\n", kIgnorePresentFences ? "ignored" : "used"); - result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps)\n", - mPeriod, 1000000000.0 / mPeriod); + result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", + mPeriod, 1000000000.0 / mPeriod, mRefreshSkipCount); result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase); result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError)); diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h index 5106bc89d..7a26df33f 100644 --- a/services/surfaceflinger/DispSync.h +++ b/services/surfaceflinger/DispSync.h @@ -67,6 +67,7 @@ public: DispSync(); ~DispSync(); + // reset clears the resync samples and error value. void reset(); // addPresentFence adds a fence for use in validating the current vsync @@ -100,8 +101,11 @@ public: // turned on. It should NOT be used after that. void setPeriod(nsecs_t period); - // Setting the low power mode reduces the frame rate to half of the default - void setLowPowerMode(bool enabled); + // setRefreshSkipCount specifies an additional number of refresh + // cycles to skip. For example, on a 60Hz display, a skip count of 1 + // will result in events happening at 30Hz. Default is zero. The idea + // is to sacrifice smoothness for battery life. + void setRefreshSkipCount(int count); // addEventListener registers a callback to be called repeatedly at the // given phase offset from the hardware vsync events. The callback is @@ -161,6 +165,8 @@ private: nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES]; size_t mPresentSampleOffset; + int mRefreshSkipCount; + // mThread is the thread from which all the callbacks are called. sp mThread; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3bdbb4fd9..f06db5cb1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2682,11 +2682,8 @@ status_t SurfaceFlinger::onTransact( // This is an experimental interface // Needs to be shifted to proper binder interface when we productize case 1016: { - mPrimaryDispSync.setLowPowerMode(true); - return NO_ERROR; - } - case 1017: { - mPrimaryDispSync.setLowPowerMode(false); + n = data.readInt32(); + mPrimaryDispSync.setRefreshSkipCount(n); return NO_ERROR; } }