[SF] Add a support for N VSyncs in FrameTargeter

Currently supports 5 past vsyncs

Test: atest FrameTargeterTest
BUG: 308858993
Flag: allow_n_vsyncs_in_targeter
Change-Id: Ie240fc5a65728496b7d20d35d14d7e05a0b77817
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index d6a3f62..d37d2dc 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -33,6 +33,7 @@
 // TODO(b/185536303): Pull to FTL.
 #include "../../../TracedOrdinal.h"
 #include "../../../Utils/Dumper.h"
+#include "../../../Utils/RingBuffer.h"
 
 namespace android::scheduler {
 
@@ -61,7 +62,7 @@
     // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
     // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
     // signaled by now (unless that frame missed).
-    const FenceTimePtr& presentFenceForPastVsync(Period minFramePeriod) const;
+    FenceTimePtr presentFenceForPastVsync(Period minFramePeriod) const;
 
     // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
     const FenceTimePtr& presentFenceForPreviousFrame() const {
@@ -84,6 +85,12 @@
         return mExpectedPresentTime - minFramePeriod;
     }
 
+    void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime,
+                  TimePoint expectedPresentTime) {
+        mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime,
+                                       expectedPresentTime};
+    }
+
     VsyncId mVsyncId;
     TimePoint mFrameBeginTime;
     TimePoint mExpectedPresentTime;
@@ -97,8 +104,11 @@
     struct FenceWithFenceTime {
         sp<Fence> fence = Fence::NO_FENCE;
         FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+        TimePoint expectedPresentTime = TimePoint();
     };
     std::array<FenceWithFenceTime, 2> mPresentFences;
+    utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
+
     TimePoint mLastSignaledFrameTime;
 
 private:
@@ -109,6 +119,18 @@
         static_assert(N > 1);
         return expectedFrameDuration() > (N - 1) * minFramePeriod;
     }
+
+    const FenceTimePtr pastVsyncTimePtr() const {
+        auto pastFenceTimePtr = FenceTime::NO_FENCE;
+        for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
+            const auto& [_, fenceTimePtr, expectedPresentTime] = mFenceWithFenceTimes[i];
+            if (expectedPresentTime > mFrameBeginTime) {
+                return pastFenceTimePtr;
+            }
+            pastFenceTimePtr = fenceTimePtr;
+        }
+        return pastFenceTimePtr;
+    }
 };
 
 // Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 8335568..badd21e 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -16,6 +16,7 @@
 
 #include <gui/TraceUtils.h>
 
+#include <common/FlagManager.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/IVsyncSource.h>
 
@@ -33,8 +34,10 @@
     return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
 }
 
-const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
-    // TODO(b/267315508): Generalize to N VSYNCs.
+FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
+    if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+        return pastVsyncTimePtr();
+    }
     const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
     return mPresentFences[i].fenceTime;
 }
@@ -44,7 +47,8 @@
     // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
 
     // TODO(b/267315508): Generalize to N VSYNCs.
-    if (targetsVsyncsAhead<3>(minFramePeriod)) {
+    const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
+    if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
         return true;
     }
 
@@ -144,8 +148,12 @@
 }
 
 FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
-    mPresentFences[1] = mPresentFences[0];
-    mPresentFences[0] = {std::move(presentFence), presentFenceTime};
+    if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+        addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
+    } else {
+        mPresentFences[1] = mPresentFences[0];
+        mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
+    }
     return presentFenceTime;
 }
 
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 29711af..5448eec 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -24,6 +24,7 @@
 
 #include <com_android_graphics_surfaceflinger_flags.h>
 
+using namespace com::android::graphics::surfaceflinger;
 using namespace std::chrono_literals;
 
 namespace android::scheduler {
@@ -168,6 +169,7 @@
 }
 
 TEST_F(FrameTargeterTest, recallsPastVsync) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     VsyncId vsyncId{111};
     TimePoint frameBeginTime(1000ms);
     constexpr Fps kRefreshRate = 60_Hz;
@@ -184,6 +186,7 @@
 }
 
 TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     VsyncId vsyncId{222};
     TimePoint frameBeginTime(2000ms);
     constexpr Fps kRefreshRate = 120_Hz;
@@ -203,8 +206,32 @@
     }
 }
 
+TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+    VsyncId vsyncId{222};
+    TimePoint frameBeginTime(2000ms);
+    constexpr Fps kRefreshRate = 120_Hz;
+    constexpr Period kPeriod = kRefreshRate.getPeriod();
+    constexpr Duration kFrameDuration = 10ms;
+
+    FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+    for (int n = 5; n-- > 0;) {
+        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+        const auto fence = frame.end();
+
+        const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
+        EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
+        EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+
+        frameBeginTime += kPeriod;
+        previousFence = fence;
+    }
+}
+
 TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
 
     VsyncId vsyncId{222};
     TimePoint frameBeginTime(2000ms);
@@ -227,6 +254,33 @@
     }
 }
 
+TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+
+    VsyncId vsyncId{222};
+    TimePoint frameBeginTime(2000ms);
+    constexpr Fps kRefreshRate = 120_Hz;
+    constexpr Fps kPeakRefreshRate = 240_Hz;
+    constexpr Period kPeriod = kRefreshRate.getPeriod();
+    constexpr Duration kFrameDuration = 10ms;
+
+    FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+    for (int n = 5; n-- > 0;) {
+        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+                    kPeakRefreshRate);
+        const auto fence = frame.end();
+
+        const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
+        EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
+        EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+
+        frameBeginTime += kPeriod;
+        previousFence = fence;
+    }
+}
+
 TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
     constexpr Period kPeriod = (60_Hz).getPeriod();
     EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
@@ -234,6 +288,7 @@
 }
 
 TEST_F(FrameTargeterTest, detectsEarlyPresent) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     VsyncId vsyncId{333};
     TimePoint frameBeginTime(3000ms);
     constexpr Fps kRefreshRate = 60_Hz;
@@ -263,6 +318,7 @@
 // Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
 // when there is expected present time support.
 TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     VsyncId vsyncId{333};
     TimePoint frameBeginTime(3000ms);
     constexpr Fps kRefreshRate = 60_Hz;
@@ -289,6 +345,7 @@
 }
 
 TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     VsyncId vsyncId{444};
     TimePoint frameBeginTime(4000ms);
     constexpr Fps kRefreshRate = 120_Hz;
@@ -320,7 +377,52 @@
               target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
 }
 
+TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+    VsyncId vsyncId{444};
+    TimePoint frameBeginTime(4000ms);
+    Fps refreshRate = 120_Hz;
+    Period period = refreshRate.getPeriod();
+
+    // The target is not early while past present fences are pending.
+    for (int n = 5; n-- > 0;) {
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+        EXPECT_FALSE(wouldPresentEarly(period));
+        EXPECT_FALSE(target().earliestPresentTime());
+    }
+
+    Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+    auto fence = frame.end();
+    frameBeginTime += period;
+    fence->signalForTest(frameBeginTime.ns());
+
+    // The target is two VSYNCs ahead, so the past present fence is still pending.
+    EXPECT_FALSE(wouldPresentEarly(period));
+    EXPECT_FALSE(target().earliestPresentTime());
+
+    { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); }
+
+    Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+    // The target is early if the past present fence was signaled.
+    EXPECT_TRUE(wouldPresentEarly(period));
+    ASSERT_NE(std::nullopt, target().earliestPresentTime());
+    EXPECT_EQ(*target().earliestPresentTime(),
+              target().expectedPresentTime() - period - kHwcMinWorkDuration);
+
+    fence = oneEarlyPresentFrame.end();
+    frameBeginTime += period;
+    fence->signalForTest(frameBeginTime.ns());
+
+    // Change rate to track frame more than 2 vsyncs ahead
+    refreshRate = 144_Hz;
+    period = refreshRate.getPeriod();
+    Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate);
+    // The target is not early as last frame as the past frame is tracked for pending.
+    EXPECT_FALSE(wouldPresentEarly(period));
+}
+
 TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
+    SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
     TimePoint frameBeginTime(5000ms);
     constexpr Fps kRefreshRate = 144_Hz;
     constexpr Period kPeriod = kRefreshRate.getPeriod();