Keep early vsync-offsets for at least two frames after transaction

Imagine the following sequence, in which vsync-sf is usually 4ms
behind vsync-app.
- Vsync-app fires. The app sends a transaction with early wakeup.
- Vsync-sf fires immediately after early wakeup is received,
before the regular 4ms delay.
- Vsync-sf resets itself to vsync-app+4ms as the transaction was
handled.
- Repeat 1, but now the app was a bit delayed and sends the early
wakeup late, such that the time used to do GL comp isn't enough to
avoid jank.

To fix this, we apply a low pass filter for transaction-caused
early wake and keep it early for at least 2 frames.

Test: Open/close apps, inspect systraces and verify wake-up times
Bug: 78611607
Change-Id: I74b0d88a4d95ca5b6d24950e14e3d6a9379f2714
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h
index 3e5800e..d526313 100644
--- a/services/surfaceflinger/VSyncModulator.h
+++ b/services/surfaceflinger/VSyncModulator.h
@@ -28,6 +28,12 @@
  * Modulates the vsync-offsets depending on current SurfaceFlinger state.
  */
 class VSyncModulator {
+private:
+
+    // Number of frames we'll keep the early phase offsets once they are activated. This acts as a
+    // low-pass filter in case the client isn't quick enough in sending new transactions.
+    const int MIN_EARLY_FRAME_COUNT = 2;
+
 public:
 
     enum TransactionStart {
@@ -55,6 +61,11 @@
     }
 
     void setTransactionStart(TransactionStart transactionStart) {
+
+        if (transactionStart == TransactionStart::EARLY) {
+            mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
+        }
+
         // An early transaction stays an early transaction.
         if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
             return;
@@ -69,10 +80,19 @@
         updatePhaseOffsets();
     }
 
-    void setLastFrameUsedRenderEngine(bool re) {
-        if (re == mLastFrameUsedRenderEngine) return;
-        mLastFrameUsedRenderEngine = re;
-        updatePhaseOffsets();
+    void onRefreshed(bool usedRenderEngine) {
+        bool updatePhaseOffsetsNeeded = false;
+        if (mRemainingEarlyFrameCount > 0) {
+            mRemainingEarlyFrameCount--;
+            updatePhaseOffsetsNeeded = true;
+        }
+        if (usedRenderEngine != mLastFrameUsedRenderEngine) {
+            mLastFrameUsedRenderEngine = usedRenderEngine;
+            updatePhaseOffsetsNeeded = true;
+        }
+        if (updatePhaseOffsetsNeeded) {
+            updatePhaseOffsets();
+        }
     }
 
 private:
@@ -82,7 +102,7 @@
         // Do not change phase offsets if disabled.
         if (mEarlyPhaseOffset == mLatePhaseOffset) return;
 
-        if (mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine) {
+        if (shouldUseEarlyOffset()) {
             if (mPhaseOffset != mEarlyPhaseOffset) {
                 if (mEventThread) {
                     mEventThread->setPhaseOffset(mEarlyPhaseOffset);
@@ -99,12 +119,18 @@
         }
     }
 
+    bool shouldUseEarlyOffset() {
+        return mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine
+                || mRemainingEarlyFrameCount > 0;
+    }
+
     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;
+    std::atomic<int> mRemainingEarlyFrameCount = 0;
 };
 
 } // namespace android