/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #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 com::android::graphics::surfaceflinger; using namespace std::chrono_literals; namespace android::scheduler { namespace { struct VsyncSource final : IVsyncSource { 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, ftl::Optional<TimePoint> = {}) const override { return vsyncDeadline; } Period minFramePeriod() const override { return framePeriod; } }; } // namespace class FrameTargeterTestBase : public testing::Test { public: FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {} const auto& target() const { return mTargeter.target(); } bool wouldPresentEarly(Period minFramePeriod) const { return target().wouldPresentEarly(minFramePeriod); } struct Frame { Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, 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()), minFramePeriod(peakRefreshRate.getPeriod()) { const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime, .vsyncId = vsyncId, .expectedVsyncTime = frameBeginTime + frameDuration, .sfWorkDuration = 10ms, .hwcMinWorkDuration = kHwcMinWorkDuration}; testPtr->mTargeter.beginFrame(args, vsyncSourceOpt .or_else([&] { return std::make_optional( VsyncSource(period, period, args.expectedVsyncTime)); }) .value(), isFencePendingFuncPtr); } FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) { if (ended) return nullptr; ended = true; auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest(); testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime); testPtr->mTargeter.endFrame({.compositionCoverage = coverage}); return fenceTime; } ~Frame() { end(); frameBeginTime += period; } static bool fencePending(const FenceTimePtr&, int) { return true; } static bool fenceSignaled(const FenceTimePtr&, int) { return false; } FrameTargeterTestBase* const testPtr; TimePoint& frameBeginTime; const Period period; const Period minFramePeriod; bool ended = false; }; static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns); private: FenceToFenceTimeMap mFenceMap; FrameTargeter mTargeter; }; class FrameTargeterTest : public FrameTargeterTestBase { public: FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {} }; class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase { public: FrameTargeterWithExpectedPresentSupportTest() : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) | Feature::kExpectedPresentTime) {} }; TEST_F(FrameTargeterTest, targetsFrames) { VsyncId vsyncId{42}; { TimePoint frameBeginTime(989ms); const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz, 60_Hz); EXPECT_EQ(target().vsyncId(), VsyncId{42}); EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms)); EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms)); EXPECT_EQ(target().expectedFrameDuration(), 10ms); } { TimePoint frameBeginTime(1100ms); const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz, 60_Hz); EXPECT_EQ(target().vsyncId(), VsyncId{43}); EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms)); EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms)); EXPECT_EQ(target().expectedFrameDuration(), 11ms); } } TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) { // Negative such that `expectedVsyncTime` is in the past. constexpr Duration kFrameDuration = -3ms; TimePoint frameBeginTime(777ms); constexpr Fps kRefreshRate = 120_Hz; const VsyncSource vsyncSource(kRefreshRate.getPeriod(), kRefreshRate.getPeriod(), frameBeginTime + 5ms); const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate, Frame::fenceSignaled, vsyncSource); EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod); } 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; constexpr Period kPeriod = kRefreshRate.getPeriod(); constexpr Duration kFrameDuration = 13ms; for (int n = 5; n-- > 0;) { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); const auto fence = frame.end(); EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod); EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence); } } 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; 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(); EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); previousFence = fence; } } 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(flags::vrr_config, true); SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false); 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); EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); previousFence = fence; } } 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); EXPECT_FALSE(wouldPresentEarly(kPeriod)); } 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; constexpr Period kPeriod = kRefreshRate.getPeriod(); // 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, kRefreshRate); EXPECT_FALSE(wouldPresentEarly(kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // `finalFrame` would present early, so it has an earliest present time. EXPECT_TRUE(wouldPresentEarly(kPeriod)); ASSERT_NE(std::nullopt, target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } // 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; constexpr Period kPeriod = kRefreshRate.getPeriod(); // 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, kRefreshRate); EXPECT_FALSE(wouldPresentEarly(kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // `finalFrame` would present early, but we have expected present time support, so it has no // earliest present time. EXPECT_TRUE(wouldPresentEarly(kPeriod)); ASSERT_EQ(std::nullopt, target().earliestPresentTime()); } 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; constexpr Period kPeriod = kRefreshRate.getPeriod(); // 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, kRefreshRate); EXPECT_FALSE(wouldPresentEarly(kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); } 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(wouldPresentEarly(kPeriod)); EXPECT_FALSE(target().earliestPresentTime()); { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); } Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); // The target is early if the past present fence was signaled. EXPECT_TRUE(wouldPresentEarly(kPeriod)); ASSERT_NE(std::nullopt, target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), 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(); 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(wouldPresentEarly(kPeriod)); EXPECT_TRUE(target().earliestPresentTime()); EXPECT_EQ(*target().earliestPresentTime(), target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } TEST_F(FrameTargeterTest, detectsMissedFrames) { VsyncId vsyncId{555}; TimePoint frameBeginTime(5000ms); constexpr Fps kRefreshRate = 60_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); EXPECT_FALSE(target().isFramePending()); EXPECT_FALSE(target().didMissFrame()); EXPECT_FALSE(target().didMissHwcFrame()); { 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. EXPECT_FALSE(target().didMissFrame()); EXPECT_FALSE(target().didMissHwcFrame()); } { Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate, Frame::fencePending); EXPECT_TRUE(target().isFramePending()); // The frame missed if the past present fence is pending. EXPECT_TRUE(target().didMissFrame()); EXPECT_TRUE(target().didMissHwcFrame()); frame.end(CompositionCoverage::Gpu); } { 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. EXPECT_TRUE(target().didMissFrame()); EXPECT_FALSE(target().didMissHwcFrame()); } { Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); const auto fence = frame.end(); const auto expectedPresentTime = target().expectedPresentTime(); fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1); } { Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); const auto fence = frame.end(); const auto expectedPresentTime = target().expectedPresentTime(); fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2); // The frame missed if the past present fence was signaled but not within slop. EXPECT_TRUE(target().didMissFrame()); EXPECT_TRUE(target().didMissHwcFrame()); } { 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. EXPECT_FALSE(target().didMissFrame()); EXPECT_FALSE(target().didMissHwcFrame()); } } } // namespace android::scheduler