Early wake-up for transitions (1/2)

On some devices it's very likely that we fall into GL comp during
app transitions. However, SF offsets are chosen in a way such that
the time to finish a frame is just too tight to be completely jank
free when hitting GL composition in SurfaceFlinger. Thus, we
introduce the concept of a separate early offset, and wakeup
SurfaceFlinger at that time if we think that hitting GL comp is
likely, or we already hit GL comp in the last frame.

Test: Open app, check vsync offsets in systrace
Test: Open many dialogs/apps to fall into GPU comp.
Bug: 75985430
Change-Id: Ie17e30c4575359fa11bb8912f68dcafe3e569ddb
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index bbf681e..63560c4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -101,7 +101,8 @@
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
     mForceSynchronous(other.mForceSynchronous),
     mTransactionNestCount(other.mTransactionNestCount),
-    mAnimation(other.mAnimation) {
+    mAnimation(other.mAnimation),
+    mEarlyWakeup(other.mEarlyWakeup) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
 }
@@ -157,9 +158,13 @@
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
+    if (mEarlyWakeup) {
+        flags |= ISurfaceComposer::eEarlyWakeup;
+    }
 
     mForceSynchronous = false;
     mAnimation = false;
+    mEarlyWakeup = false;
 
     sf->setTransactionState(composerStates, displayStates, flags);
     mStatus = NO_ERROR;
@@ -185,6 +190,10 @@
     mAnimation = true;
 }
 
+void SurfaceComposerClient::Transaction::setEarlyWakeup() {
+    mEarlyWakeup = true;
+}
+
 layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
     if (mComposerStates.count(sc) == 0) {
         // we don't have it, add an initialized layer_state to our list
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3591090..e401572 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -61,6 +61,11 @@
     enum {
         eSynchronous = 0x01,
         eAnimation   = 0x02,
+
+        // Indicates that this transaction will likely result in a lot of layers being composed, and
+        // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
+        // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
+        eEarlyWakeup = 0x04
     };
 
     enum {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ffc22f6..377fe68 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -154,6 +154,7 @@
         uint32_t                    mForceSynchronous = 0;
         uint32_t                    mTransactionNestCount = 0;
         bool                        mAnimation = false;
+        bool                        mEarlyWakeup = false;
 
         int mStatus = NO_ERROR;
 
@@ -273,6 +274,7 @@
                 const Rect& displayRect);
         void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
         void setAnimationTransaction();
+        void setEarlyWakeup();
     };
 
     status_t    destroySurface(const sp<IBinder>& id);
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 1cbd602..b53c73a 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -764,6 +764,7 @@
 }
 
 void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
+    ATRACE_CALL();
     const State& s(getDrawingState());
 
     computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6b4f5db..87333d0 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -356,6 +356,7 @@
 }
 
 status_t BufferLayerConsumer::bindTextureImageLocked() {
+    ATRACE_CALL();
     mRE.checkErrors();
 
     if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 9e01fd0..7acbd11 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -209,6 +209,28 @@
         return BAD_VALUE;
     }
 
+    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
+        Mutex::Autolock lock(mMutex);
+
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            if (mEventListeners[i].mCallback == callback) {
+                EventListener& listener = mEventListeners.editItemAt(i);
+                const nsecs_t oldPhase = listener.mPhase;
+                listener.mPhase = phase;
+
+                // Pretend that the last time this event was handled at the same frame but with the
+                // new offset to allow for a seamless offset change without double-firing or
+                // skipping.
+                listener.mLastEventTime -= (oldPhase - phase);
+                mCond.signal();
+                return NO_ERROR;
+            }
+        }
+
+        return BAD_VALUE;
+    }
+
     // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
     // case.
     bool hasAnyEventListeners() {
@@ -487,6 +509,11 @@
     return mThread->removeEventListener(callback);
 }
 
+status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
+    Mutex::Autolock lock(mMutex);
+    return mThread->changePhaseOffset(callback, phase);
+}
+
 void DispSync::setPeriod(nsecs_t period) {
     Mutex::Autolock lock(mMutex);
     mPeriod = period;
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 9336f4d..077256a 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -113,6 +113,11 @@
     // DispSync object.
     status_t removeEventListener(Callback* callback);
 
+    // changePhaseOffset changes the phase offset of an already-registered event callback. The
+    // method will make sure that there is no skipping or double-firing on the listener per frame,
+    // even when changing the offsets multiple times.
+    status_t changePhaseOffset(Callback* callback, nsecs_t phase);
+
     // computeNextRefresh computes when the next refresh is expected to begin.
     // The periodOffset value can be used to move forward or backward; an
     // offset of zero is the next refresh, -1 is the previous refresh, 1 is
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 1fc3100..0fb3d28 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -293,6 +293,7 @@
 }
 
 void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
+    ATRACE_CALL();
     if (mesh.getTexCoordsSize()) {
         glEnableVertexAttribArray(Program::texCoords);
         glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 18b2f68..50495ec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -296,6 +296,12 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
+    property_get("debug.sf.early_phase_offset_ns", value, "0");
+    const int earlyWakeupOffsetOffsetNs = atoi(value);
+    ALOGI_IF(earlyWakeupOffsetOffsetNs != 0, "Enabling separate early offset");
+    mVsyncModulator.setPhaseOffsets(sfVsyncPhaseOffsetNs - earlyWakeupOffsetOffsetNs,
+            sfVsyncPhaseOffsetNs);
+
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
@@ -522,19 +528,10 @@
             return;
         }
 
-        // Remove the listener with the old offset
-        status_t err = mDispSync->removeEventListener(
-                static_cast<DispSync::Callback*>(this));
+        status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this),
+                mPhaseOffset);
         if (err != NO_ERROR) {
-            ALOGE("error unregistering vsync callback: %s (%d)",
-                    strerror(-err), err);
-        }
-
-        // Add a listener with the new offset
-        err = mDispSync->addEventListener(mName, mPhaseOffset,
-                static_cast<DispSync::Callback*>(this));
-        if (err != NO_ERROR) {
-            ALOGE("error registering vsync callback: %s (%d)",
+            ALOGE("error changing vsync offset: %s (%d)",
                     strerror(-err), err);
         }
     }
@@ -623,6 +620,7 @@
     mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
                                                          "sfEventThread");
     mEventQueue->setEventThread(mSFEventThread.get());
+    mVsyncModulator.setEventThread(mSFEventThread.get());
 
     // Get a RenderEngine for the given display / config (can't fail)
     getBE().mRenderEngine =
@@ -1498,6 +1496,7 @@
         mHadClientComposition = mHadClientComposition ||
                 getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
+    mVsyncModulator.setLastFrameUsedRenderEngine(mHadClientComposition);
 
     mLayersWithQueuedFrames.clear();
 }
@@ -2122,6 +2121,7 @@
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
 
+    mVsyncModulator.setTransactionStart(VSyncModulator::TransactionStart::NORMAL);
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
 
@@ -3070,7 +3070,13 @@
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
+    return setTransactionFlags(flags, VSyncModulator::TransactionStart::NORMAL);
+}
+
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
+        VSyncModulator::TransactionStart transactionStart) {
     uint32_t old = android_atomic_or(flags, &mTransactionFlags);
+    mVsyncModulator.setTransactionStart(transactionStart);
     if ((old & flags)==0) { // wake the server up
         signalTransaction();
     }
@@ -3159,7 +3165,10 @@
         }
 
         // this triggers the transaction
-        setTransactionFlags(transactionFlags);
+        const auto start = (flags & eEarlyWakeup)
+                ? VSyncModulator::TransactionStart::EARLY
+                : VSyncModulator::TransactionStart::NORMAL;
+        setTransactionFlags(transactionFlags, start);
 
         // if this is a synchronous transaction, wait for it to take effect
         // before returning.
@@ -4099,9 +4108,9 @@
     colorizer.bold(result);
     result.append("DispSync configuration: ");
     colorizer.reset(result);
-    result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
-            "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
-        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs,
+    result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, early sf phase %" PRId64
+        " ns, present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
+        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, mVsyncModulator.getEarlyPhaseOffset(),
         dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
     result.append("\n");
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 477ace5..01f1ac3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,6 +33,7 @@
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
+#include <utils/Trace.h>
 
 #include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
@@ -55,6 +56,7 @@
 #include "Barrier.h"
 #include "DisplayDevice.h"
 #include "DispSync.h"
+#include "EventThread.h"
 #include "FrameTracker.h"
 #include "LayerStats.h"
 #include "LayerVector.h"
@@ -63,6 +65,7 @@
 #include "SurfaceTracing.h"
 #include "StartPropertySetThread.h"
 #include "LayerBE.h"
+#include "VSyncModulator.h"
 
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
@@ -494,6 +497,7 @@
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
+    uint32_t setTransactionFlags(uint32_t flags, VSyncModulator::TransactionStart transactionStart);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
@@ -767,6 +771,8 @@
     std::unique_ptr<EventControlThread> mEventControlThread;
     sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
 
+    VSyncModulator mVsyncModulator;
+
     // Can only accessed from the main thread, these members
     // don't need synchronization
     State mDrawingState{LayerVector::StateSet::Drawing};
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h
new file mode 100644
index 0000000..3126deb
--- /dev/null
+++ b/services/surfaceflinger/VSyncModulator.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 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/Errors.h>
+
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+/*
+ * Modulates the vsync-offsets depending on current SurfaceFlinger state.
+ */
+class VSyncModulator {
+public:
+
+    enum TransactionStart {
+        EARLY,
+        NORMAL
+    };
+
+    // Sets the phase offsets
+    //
+    // early: the phase offset when waking up early. May be the same as late, in which case we don't
+    //        shift offsets.
+    // late: the regular sf phase offset.
+    void setPhaseOffsets(nsecs_t early, nsecs_t late) {
+        mEarlyPhaseOffset = early;
+        mLatePhaseOffset = late;
+        mPhaseOffset = late;
+    }
+
+    nsecs_t getEarlyPhaseOffset() const {
+        return mEarlyPhaseOffset;
+    }
+
+    void setEventThread(EventThread* eventThread) {
+        mEventThread = eventThread;
+    }
+
+    void setTransactionStart(TransactionStart transactionStart) {
+        if (transactionStart == mTransactionStart) return;
+        mTransactionStart = transactionStart;
+        updatePhaseOffsets();
+    }
+
+    void setLastFrameUsedRenderEngine(bool re) {
+        if (re == mLastFrameUsedRenderEngine) return;
+        mLastFrameUsedRenderEngine = re;
+        updatePhaseOffsets();
+    }
+
+private:
+
+    void updatePhaseOffsets() {
+
+        // Do not change phase offsets if disabled.
+        if (mEarlyPhaseOffset == mLatePhaseOffset) return;
+
+        if (mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine) {
+            if (mPhaseOffset != mEarlyPhaseOffset) {
+                if (mEventThread) {
+                    mEventThread->setPhaseOffset(mEarlyPhaseOffset);
+                }
+                mPhaseOffset = mEarlyPhaseOffset;
+            }
+        } else {
+            if (mPhaseOffset != mLatePhaseOffset) {
+                if (mEventThread) {
+                    mEventThread->setPhaseOffset(mLatePhaseOffset);
+                }
+                mPhaseOffset = mLatePhaseOffset;
+            }
+        }
+    }
+
+    nsecs_t mLatePhaseOffset = 0;
+    nsecs_t mEarlyPhaseOffset = 0;
+    EventThread* mEventThread = nullptr;
+    std::atomic<nsecs_t> mPhaseOffset = 0;
+    std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
+    std::atomic<bool> mLastFrameUsedRenderEngine = false;
+};
+
+} // namespace android