/* * 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. */ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" #include #include "DisplayHardware/Hal.h" #include "DisplayTransactionTestHelpers.h" namespace android { using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; using android::hardware::graphics::composer::hal::Error; class NotifyExpectedPresentTest : public DisplayTransactionTest { public: void SetUp() override { const auto display = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); mPhysicalDisplayId = display->getPhysicalId(); FakeHwcDisplayInjector(mPhysicalDisplayId, hal::DisplayType::PHYSICAL, /*isPrimary=*/true) .setPowerMode(hal::PowerMode::ON) .inject(&mFlinger, mComposer); ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(0), kFps60Hz)); mCompositor = std::make_unique(mPhysicalDisplayId, mFlinger); } protected: void setTransactionState() { ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty()); TransactionInfo transaction; mFlinger.setTransactionState(std::move(transaction)); } struct TransactionInfo : public TransactionState { TransactionInfo() { mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); mIsAutoTimestamp = false; mId = static_cast(-1); } }; struct Compositor final : ICompositor { explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger) : displayId(displayId), surfaceFlinger(surfaceFlinger) {} void sendNotifyExpectedPresentHint(PhysicalDisplayId id) override { surfaceFlinger.sendNotifyExpectedPresentHint(id); } bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return committed; } CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& targeters) override { pacesetterIds.composite = pacesetterId; CompositeResultsPerDisplay results; for (const auto& [id, targeter] : targeters) { vsyncIds.composite.emplace_back(id, targeter->target().vsyncId()); surfaceFlinger.resetNotifyExpectedPresentHintState(pacesetterId); results.try_emplace(id, CompositeResult{.compositionCoverage = CompositionCoverage::Hwc}); } return results; } void sample() override {} void configure() override {} struct { PhysicalDisplayId commit; PhysicalDisplayId composite; } pacesetterIds; using VsyncIds = std::vector>; struct { VsyncIds commit; VsyncIds composite; } vsyncIds; bool committed = true; PhysicalDisplayId displayId; TestableSurfaceFlinger& surfaceFlinger; }; PhysicalDisplayId mPhysicalDisplayId; std::unique_ptr mCompositor; static constexpr hal::HWDisplayId kHwcDisplayId = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; static constexpr Fps kFps60Hz = 60_Hz; static constexpr int32_t kFrameInterval5HzNs = static_cast(5_Hz).getPeriodNsecs(); static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); static constexpr int32_t kFrameInterval120HzNs = static_cast(120_Hz).getPeriodNsecs(); static constexpr Period kVsyncPeriod = Period::fromNs(static_cast(240_Hz).getPeriodNsecs()); static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); }; TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) { auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); ASSERT_NO_FATAL_FAILURE( mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(expectedPresentTime), kFps60Hz)); EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); for (int i = 0; i < 5; i++) { expectedPresentTime += 2 * kFrameInterval5HzNs; mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, /*timeoutOpt*/ std::nullopt); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) { auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); { // Very first ExpectedPresent after idle, no previous timestamp. EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); // Present frame mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); // Present happens and NotifyExpectedPresentHintStatus is start. ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { mCompositor->committed = false; expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(static_cast( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(expectedPresentTime)); EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, Period::fromNs(0)); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); // Hint sent ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(static_cast( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(expectedPresentTime)); EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, Period::fromNs(0)); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); // Hint is executed ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10); { // Very first ExpectedPresent after idle, no previous timestamp mCompositor->committed = false; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); expectedPresentTime += 2 * kFrameInterval5HzNs; mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); { EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); // Hint sent with the setTransactionState setTransactionState(); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } { // ExpectedPresentTime is after the timeoutNs mCompositor->committed = true; expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); // Present happens notifyExpectedPresentHintStatus is Start ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); // Another expectedPresent after timeout expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent has not changed EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent is after the last reported ExpectedPresent and within timeout. expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. mCompositor->committed = false; expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)); mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { const auto now = TimePoint::now().ns(); auto expectedPresentTime = now; static constexpr Period kTimeoutNs = Period::fromNs(static_cast(1_Hz).getPeriodNsecs()); ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(now), Fps::fromValue(0))); static constexpr int32_t kFrameIntervalNs120Hz = static_cast(120_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs96Hz = static_cast(96_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs80Hz = static_cast(80_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs60Hz = static_cast(60_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs40Hz = static_cast(40_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs30Hz = static_cast(30_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs24Hz = static_cast(24_Hz).getPeriodNsecs(); static constexpr int32_t kFrameIntervalNs20Hz = static_cast(20_Hz).getPeriodNsecs(); static constexpr Period kVsyncPeriod = Period::fromNs(static_cast(240_Hz).getPeriodNsecs()); struct FrameRateIntervalTestData { int32_t frameIntervalNs; bool callNotifyExpectedPresentHint; }; const std::vector frameIntervals = { {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, }; for (size_t i = 0; i < frameIntervals.size(); i++) { const auto& [frameIntervalNs, callNotifyExpectedPresentHint] = frameIntervals[i]; expectedPresentTime += frameIntervalNs; mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); EXPECT_CALL(static_cast( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(expectedPresentTime)); if (callNotifyExpectedPresentHint) { mCompositor->committed = false; ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)) << "Hint not scheduled for frameInterval " << frameIntervalNs << " at index " << i; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) .WillOnce(Return(Error::NONE)); } else { // Only lastExpectedPresentTime is updated EXPECT_TRUE( mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)) << "LastExpectedPresentTime for frameInterval " << frameIntervalNs << "at index " << i << " did not match for frameInterval " << frameIntervalNs; EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); } mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); if (callNotifyExpectedPresentHint) { // Present resumes the calls to the notifyExpectedPresentHint. mCompositor->committed = true; mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); } } } } // namespace android