SF: use peak refresh rate for HWC backpressure

The logic we have for selecting which fence to use to detect
backpressure replies on the assumption that continuously presents as
the rate of vsync. This is not true for VRR displays.

Bug: 296635687
Test: adb root && adb shell service call SurfaceFlinger 1045 f 0.9
Change-Id: Iaceb45741fd657b8c551f51b3c76a643df116da8
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index db247aa..d714848 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -62,5 +62,9 @@
         "libgmock",
         "libgtest",
         "libscheduler",
+        "libsurfaceflingerflags_test",
+    ],
+    shared_libs: [
+        "server_configurable_flags",
     ],
 }
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index acb7265..c13c330 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -108,6 +108,19 @@
     return mRateMap.find(idealPeriod())->second.slope;
 }
 
+Period VSyncPredictor::minFramePeriod() const {
+    if (!FlagManager::getInstance().vrr_config()) {
+        return Period::fromNs(currentPeriod());
+    }
+
+    std::lock_guard lock(mMutex);
+    const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs();
+    const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
+                                                        static_cast<float>(idealPeriod())));
+    const auto slope = mRateMap.find(idealPeriod())->second.slope;
+    return Period::fromNs(slope * numPeriods);
+}
+
 bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
     std::lock_guard lock(mMutex);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index fbc1e16..6cb5a67 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -46,6 +46,7 @@
     bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
     nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+    Period minFramePeriod() const final EXCLUDES(mMutex);
     void resetModel() final EXCLUDES(mMutex);
 
     /* Query if the model is in need of more samples to make a prediction.
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2ae5c0b..aa18897 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -68,6 +68,11 @@
      */
     virtual nsecs_t currentPeriod() const = 0;
 
+    /*
+     * The minimal period frames can be displayed.
+     */
+    virtual Period minFramePeriod() const = 0;
+
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 4a8aac6..ff1c9e9 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -81,6 +81,13 @@
     return Period::fromNs(mTracker->currentPeriod());
 }
 
+Period VsyncSchedule::minFramePeriod() const {
+    if (FlagManager::getInstance().vrr_config()) {
+        return mTracker->minFramePeriod();
+    }
+    return period();
+}
+
 TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
     return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
 }
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 60c4105..722ea0b 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -64,6 +64,7 @@
     // IVsyncSource overrides:
     Period period() const override;
     TimePoint vsyncDeadlineAfter(TimePoint) const override;
+    Period minFramePeriod() const override;
 
     // Inform the schedule that the display mode changed the schedule needs to recalibrate
     // itself to the new vsync period. The schedule will end the period transition internally.
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index ae74205..70d4846 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -50,11 +50,11 @@
     TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
 
     // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
-    TimePoint pastVsyncTime(Period vsyncPeriod) const;
+    TimePoint pastVsyncTime(Period minFramePeriod) const;
 
     // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
-    TimePoint previousFrameVsyncTime(Period vsyncPeriod) const {
-        return mExpectedPresentTime - vsyncPeriod;
+    TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
+        return mExpectedPresentTime - minFramePeriod;
     }
 
     // The present fence for the frame that had targeted the most recent VSYNC before this frame.
@@ -62,14 +62,14 @@
     // 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 vsyncPeriod) const;
+    const FenceTimePtr& presentFenceForPastVsync(Period minFramePeriod) const;
 
     // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
     const FenceTimePtr& presentFenceForPreviousFrame() const {
         return mPresentFences.front().fenceTime;
     }
 
-    bool wouldPresentEarly(Period vsyncPeriod) const;
+    bool wouldPresentEarly(Period minFramePeriod) const;
 
     bool isFramePending() const { return mFramePending; }
     bool didMissFrame() const { return mFrameMissed; }
@@ -96,9 +96,9 @@
 
 private:
     template <int N>
-    inline bool targetsVsyncsAhead(Period vsyncPeriod) const {
+    inline bool targetsVsyncsAhead(Period minFramePeriod) const {
         static_assert(N > 1);
-        return expectedFrameDuration() > (N - 1) * vsyncPeriod;
+        return expectedFrameDuration() > (N - 1) * minFramePeriod;
     }
 };
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
index bb2de75..0154060 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -23,6 +23,7 @@
 struct IVsyncSource {
     virtual Period period() const = 0;
     virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
+    virtual Period minFramePeriod() const = 0;
 
 protected:
     ~IVsyncSource() = default;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 7a18654..e80372b 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -27,28 +27,28 @@
         mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
         mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
 
-TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
+TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
     // TODO(b/267315508): Generalize to N VSYNCs.
-    const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
-    return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift);
+    const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod));
+    return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
 }
 
-const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const {
+const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
     // TODO(b/267315508): Generalize to N VSYNCs.
-    const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod));
+    const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
     return mPresentFences[i].fenceTime;
 }
 
-bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const {
+bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
     // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
     // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
 
     // TODO(b/267315508): Generalize to N VSYNCs.
-    if (targetsVsyncsAhead<3>(vsyncPeriod)) {
+    if (targetsVsyncsAhead<3>(minFramePeriod)) {
         return true;
     }
 
-    const auto fence = presentFenceForPastVsync(vsyncPeriod);
+    const auto fence = presentFenceForPastVsync(minFramePeriod);
     return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
 }
 
@@ -68,6 +68,7 @@
     mScheduledPresentTime = args.expectedVsyncTime;
 
     const Period vsyncPeriod = vsyncSource.period();
+    const Period minFramePeriod = vsyncSource.minFramePeriod();
 
     // Calculate the expected present time once and use the cached value throughout this frame to
     // make sure all layers are seeing this same value.
@@ -85,7 +86,7 @@
                   ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
                   mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
 
-    const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod);
+    const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod);
 
     // In cases where the present fence is about to fire, give it a small grace period instead of
     // giving up on the frame.
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 1e038d1..c883385 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -17,23 +17,29 @@
 #include <ftl/optional.h>
 #include <gtest/gtest.h>
 
+#include <common/test/FlagUtils.h>
 #include <scheduler/Fps.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/IVsyncSource.h>
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 using namespace std::chrono_literals;
 
 namespace android::scheduler {
 namespace {
 
 struct VsyncSource final : IVsyncSource {
-    VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {}
+    VsyncSource(Period period, Period minFramePeriod, TimePoint deadline)
+          : vsyncPeriod(period), framePeriod(minFramePeriod), vsyncDeadline(deadline) {}
 
     const Period vsyncPeriod;
+    const Period framePeriod;
     const TimePoint vsyncDeadline;
 
     Period period() const override { return vsyncPeriod; }
     TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
+    Period minFramePeriod() const override { return framePeriod; }
 };
 
 } // namespace
@@ -44,10 +50,13 @@
 
     struct Frame {
         Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
-              Duration frameDuration, Fps refreshRate,
+              Duration frameDuration, Fps refreshRate, Fps peakRefreshRate,
               FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
               const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
-              : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) {
+              : testPtr(testPtr),
+                frameBeginTime(frameBeginTime),
+                period(refreshRate.getPeriod()),
+                minFramePeriod(peakRefreshRate.getPeriod()) {
             const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
                                                      .vsyncId = vsyncId,
                                                      .expectedVsyncTime =
@@ -58,7 +67,7 @@
                                           vsyncSourceOpt
                                                   .or_else([&] {
                                                       return std::make_optional(
-                                                              VsyncSource(period,
+                                                              VsyncSource(period, period,
                                                                           args.expectedVsyncTime));
                                                   })
                                                   .value(),
@@ -88,6 +97,7 @@
 
         TimePoint& frameBeginTime;
         const Period period;
+        const Period minFramePeriod;
 
         bool ended = false;
     };
@@ -103,7 +113,7 @@
     VsyncId vsyncId{42};
     {
         TimePoint frameBeginTime(989ms);
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz);
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz, 60_Hz);
 
         EXPECT_EQ(target().vsyncId(), VsyncId{42});
         EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
@@ -112,7 +122,7 @@
     }
     {
         TimePoint frameBeginTime(1100ms);
-        const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz);
+        const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz, 60_Hz);
 
         EXPECT_EQ(target().vsyncId(), VsyncId{43});
         EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
@@ -127,9 +137,10 @@
     TimePoint frameBeginTime(777ms);
 
     constexpr Fps kRefreshRate = 120_Hz;
-    const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms);
+    const VsyncSource vsyncSource(kRefreshRate.getPeriod(), kRefreshRate.getPeriod(),
+                                  frameBeginTime + 5ms);
     const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
-                      Frame::fenceSignaled, vsyncSource);
+                      kRefreshRate, Frame::fenceSignaled, vsyncSource);
 
     EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
 }
@@ -142,7 +153,7 @@
     constexpr Duration kFrameDuration = 13ms;
 
     for (int n = 5; n-- > 0;) {
-        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
         const auto fence = frame.end();
 
         EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
@@ -160,7 +171,31 @@
     FenceTimePtr previousFence = FenceTime::NO_FENCE;
 
     for (int n = 5; n-- > 0;) {
-        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+        const auto fence = frame.end();
+
+        EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
+        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+
+        previousFence = fence;
+    }
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, 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();
 
         EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
@@ -184,12 +219,12 @@
 
     // The target is not early while past present fences are pending.
     for (int n = 3; n-- > 0;) {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
     }
 
     // The target is early if the past present fence was signaled.
-    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
     const auto fence = frame.end();
     fence->signalForTest(frameBeginTime.ns());
 
@@ -204,18 +239,18 @@
 
     // The target is not early while past present fences are pending.
     for (int n = 3; n-- > 0;) {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
     }
 
-    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
     const auto fence = frame.end();
     fence->signalForTest(frameBeginTime.ns());
 
     // The target is two VSYNCs ahead, so the past present fence is still pending.
     EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
 
-    { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); }
+    { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
 
     // The target is early if the past present fence was signaled.
     EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
@@ -226,7 +261,7 @@
     constexpr Fps kRefreshRate = 144_Hz;
     constexpr Period kPeriod = kRefreshRate.getPeriod();
 
-    const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate);
+    const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
 
     // The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
     EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
@@ -243,7 +278,7 @@
     EXPECT_FALSE(target().didMissHwcFrame());
 
     {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().isFramePending());
 
         // The frame did not miss if the past present fence is invalid.
@@ -251,7 +286,8 @@
         EXPECT_FALSE(target().didMissHwcFrame());
     }
     {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
+                    Frame::fencePending);
         EXPECT_TRUE(target().isFramePending());
 
         // The frame missed if the past present fence is pending.
@@ -261,7 +297,8 @@
         frame.end(CompositionCoverage::Gpu);
     }
     {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
+                          Frame::fencePending);
         EXPECT_TRUE(target().isFramePending());
 
         // The GPU frame missed if the past present fence is pending.
@@ -269,7 +306,7 @@
         EXPECT_FALSE(target().didMissHwcFrame());
     }
     {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().isFramePending());
 
         const auto fence = frame.end();
@@ -277,7 +314,7 @@
         fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
     }
     {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().isFramePending());
 
         const auto fence = frame.end();
@@ -289,7 +326,7 @@
         EXPECT_TRUE(target().didMissHwcFrame());
     }
     {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
         EXPECT_FALSE(target().isFramePending());
 
         // The frame did not miss if the past present fence was signaled within slop.