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
This commit is contained in:
Andy McFadden 2014-06-10 14:43:32 -07:00
parent 5167ec68fe
commit 645b1f7ffb
3 changed files with 32 additions and 34 deletions

View File

@ -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>& 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));

View File

@ -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<DispSyncThread> mThread;

View File

@ -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;
}
}