SurfaceFlinger: no app vsyncs during config switch

Do not dipatch vsync callbacks to applications during a config
switch as applications will get choreographer callbacks at uneven
times and will stuff the queue to surface flinger.

Test: test google/perf/jank/UIBench/UIBench-Trace:com.android.uibench.janktests.UiBenchJankTests#testClippedListView -v
Change-Id: I0537933c27af83a9186bcff54c3596d612c748ea
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3bec6dc..e1e3dfb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -949,12 +949,18 @@
 
     // Don't check against the current mode yet. Worst case we set the desired
     // config twice.
-    {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+    std::lock_guard<std::mutex> lock(mActiveConfigLock);
+    mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+
+    if (!mDesiredActiveConfigChanged) {
+        // This is the first time we set the desired
+        mScheduler->pauseVsyncCallback(mAppConnectionHandle, true);
+
+        // This will trigger HWC refresh without resetting the idle timer.
+        repaintEverythingForHWC();
     }
-    // This will trigger HWC refresh without resetting the idle timer.
-    repaintEverythingForHWC();
+    mDesiredActiveConfigChanged = true;
+    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 }
 
 status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
@@ -964,23 +970,6 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::setActiveConfigInHWC() {
-    ATRACE_CALL();
-
-    const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
-    if (!display) {
-        return;
-    }
-
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-
-    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
-    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
-    mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
-    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
-}
-
 void SurfaceFlinger::setActiveConfigInternal() {
     ATRACE_CALL();
 
@@ -990,51 +979,68 @@
     const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
-    mSetActiveConfigState = SetActiveConfigState::NONE;
-    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
-
     mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
     ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
 }
 
-bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
+    // we may be in the process of changing the active state
+    if (mWaitForNextInvalidate) {
+        mWaitForNextInvalidate = false;
+
+        repaintEverythingForHWC();
+        // We do not want to give another frame to HWC while we are transitioning.
+        return true;
+    }
+
+    if (mCheckPendingFence) {
+        if (mPreviousPresentFence != Fence::NO_FENCE &&
+            (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
+            // fence has not signaled yet. wait for the next invalidate
+            repaintEverythingForHWC();
+            return true;
+        }
+
+        // We received the present fence from the HWC, so we assume it successfully updated
+        // the config, hence we update SF.
+        mCheckPendingFence = false;
+        setActiveConfigInternal();
+    }
+
     // Store the local variable to release the lock.
     ActiveConfigInfo desiredActiveConfig;
     {
         std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        if (!mDesiredActiveConfigChanged) {
+            return false;
+        }
         desiredActiveConfig = mDesiredActiveConfig;
     }
 
     const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
-    if (display) {
-        if (mSetActiveConfigState == SetActiveConfigState::NONE &&
-            display->getActiveConfig() != desiredActiveConfig.configId) {
-            // Step 1) Desired active config was set, it is different than the
-            // config currently in use. Notify HWC.
-            mUpcomingActiveConfig = desiredActiveConfig;
-            setActiveConfigInHWC();
-        } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
-            // Step 2) HWC was notified and we received refresh from it.
-            mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
-            ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
-            repaintEverythingForHWC();
-            // We do not want to give another frame to HWC while we are transitioning.
-            return false;
-        } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
-                   !(mPreviousPresentFence != Fence::NO_FENCE &&
-                     (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
-            // Step 3) We received the present fence from the HWC, so we assume it
-            // successfully updated the config, hence we update SF.
-            setActiveConfigInternal();
-            // If the config changed again while we were transitioning, restart the
-            // process.
-            if (desiredActiveConfig != mUpcomingActiveConfig) {
-                mUpcomingActiveConfig = desiredActiveConfig;
-                setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
-            }
-        }
+    if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
+        // display is not valid or we are already in the requested mode
+        // on both cases there is nothing left to do
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        mScheduler->pauseVsyncCallback(mAppConnectionHandle, false);
+        mDesiredActiveConfigChanged = false;
+        ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+        return false;
     }
-    return true;
+
+    // Desired active config was set, it is different than the config currently in use. Notify HWC.
+    mUpcomingActiveConfig = desiredActiveConfig;
+    const auto displayId = display->getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
+
+    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+
+    // we need to submit an empty frame to HWC to start the process
+    mWaitForNextInvalidate = true;
+    mCheckPendingFence = true;
+
+    return false;
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1575,7 +1581,7 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            if (!updateSetActiveConfigStateMachine()) {
+            if (performSetActiveConfig()) {
                 break;
             }