Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 645b1f7f authored by Andy McFadden's avatar Andy McFadden
Browse files

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
parent 5167ec68
Loading
Loading
Loading
Loading
+22 −27
Original line number Diff line number Diff line
@@ -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,19 +137,8 @@ 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;
            }
        }

        return false;
@@ -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));
+8 −2
Original line number Diff line number Diff line
@@ -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;

+2 −5
Original line number Diff line number Diff line
@@ -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;
            }
        }