Merge "SF: VSyncReactor change offsets at correct time"
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index c471e49..a652053 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -154,7 +154,7 @@
         mTracker->addVsyncTimestamp(signalTime);
     }
 
-    return false; // TODO(b/144707443): add policy for turning on HWVsync.
+    return mMoreSamplesNeeded;
 }
 
 void VSyncReactor::setIgnorePresentFences(bool ignoration) {
@@ -176,14 +176,9 @@
 }
 
 void VSyncReactor::setPeriod(nsecs_t period) {
-    mTracker->setPeriod(period);
-    {
-        std::lock_guard<std::mutex> lk(mMutex);
-        mPeriodChangeInProgress = true;
-        for (auto& entry : mCallbacks) {
-            entry.second->setPeriod(period);
-        }
-    }
+    std::lock_guard lk(mMutex);
+    mLastHwVsync.reset();
+    mPeriodTransitioningTo = period;
 }
 
 nsecs_t VSyncReactor::getPeriod() {
@@ -194,15 +189,40 @@
 
 void VSyncReactor::endResync() {}
 
+bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
+    if (!mLastHwVsync || !mPeriodTransitioningTo) {
+        return false;
+    }
+    auto const distance = vsync_timestamp - *mLastHwVsync;
+    return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+}
+
 bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
     assert(periodFlushed);
-    mTracker->addVsyncTimestamp(timestamp);
-    {
-        std::lock_guard<std::mutex> lk(mMutex);
-        *periodFlushed = mPeriodChangeInProgress;
-        mPeriodChangeInProgress = false;
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (periodChangeDetected(timestamp)) {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = true;
+
+        mTracker->setPeriod(*mPeriodTransitioningTo);
+        for (auto& entry : mCallbacks) {
+            entry.second->setPeriod(*mPeriodTransitioningTo);
+        }
+
+        mPeriodTransitioningTo.reset();
+        mLastHwVsync.reset();
+    } else if (mPeriodTransitioningTo) {
+        mLastHwVsync = timestamp;
+        mMoreSamplesNeeded = true;
+        *periodFlushed = false;
+    } else {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = false;
     }
-    return false;
+
+    mTracker->addVsyncTimestamp(timestamp);
+    return mMoreSamplesNeeded;
 }
 
 status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 29a0a11..73a5e37 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -61,6 +61,8 @@
     void reset() final;
 
 private:
+    bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+
     std::unique_ptr<Clock> const mClock;
     std::unique_ptr<VSyncTracker> const mTracker;
     std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -69,7 +71,11 @@
     std::mutex mMutex;
     bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
-    bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+
+    bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+    std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+    std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+
     std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
             GUARDED_BY(mMutex);
 };
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 537cc80..72f8bb9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -159,7 +159,6 @@
     static constexpr nsecs_t mPhase = 3000;
     static constexpr nsecs_t mAnotherPhase = 5200;
     static constexpr nsecs_t period = 10000;
-    static constexpr nsecs_t mAnotherPeriod = 23333;
     static constexpr nsecs_t mFakeCbTime = 2093;
     static constexpr nsecs_t mFakeNow = 2214;
     static constexpr const char mName[] = "callbacky";
@@ -274,10 +273,38 @@
     EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
 }
 
-TEST_F(VSyncReactorTest, setPeriod) {
-    nsecs_t const fakePeriod = 4098;
-    EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
-    mReactor.setPeriod(fakePeriod);
+TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(10000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(20000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+
+    EXPECT_FALSE(mReactor.addResyncSample(25000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(5000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+    EXPECT_FALSE(mReactor.addResyncSample(10000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
 }
 
 TEST_F(VSyncReactorTest, addResyncSampleTypical) {
@@ -291,12 +318,43 @@
 
 TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
     bool periodFlushed = false;
-    nsecs_t const fakeTimestamp = 4398;
-    nsecs_t const newPeriod = 3490;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    nsecs_t const newPeriod = 4000;
+
     mReactor.setPeriod(newPeriod);
-    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+
+    auto time = 0;
+    auto constexpr numTimestampSubmissions = 10;
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += period;
+        EXPECT_TRUE(mReactor.addResyncSample(time, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+
+    time += newPeriod;
+    EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
+
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += newPeriod;
+        EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+}
+
+TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
+    auto time = 0;
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+    mReactor.setPeriod(newPeriod);
+
+    time += period;
+    mReactor.addResyncSample(time, &periodFlushed);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    time += newPeriod;
+    mReactor.addResyncSample(time, &periodFlushed);
+
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
 static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
@@ -399,20 +457,26 @@
     mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
 }
 
-TEST_F(VSyncReactorTest, changingPeriodChangesOfsetsOnNextCb) {
+TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
+    static constexpr nsecs_t anotherPeriod = 23333;
     Sequence seq;
     EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
             .InSequence(seq)
             .WillOnce(Return(mFakeToken));
     EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
             .InSequence(seq);
-    EXPECT_CALL(*mMockTracker, setPeriod(mAnotherPeriod));
+    EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
     EXPECT_CALL(*mMockDispatch,
-                schedule(mFakeToken, computeWorkload(mAnotherPeriod, mPhase), mFakeNow))
+                schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
             .InSequence(seq);
 
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-    mReactor.setPeriod(mAnotherPeriod);
+
+    bool periodFlushed = false;
+    mReactor.setPeriod(anotherPeriod);
+    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, &periodFlushed));
+
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
 }