[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();