diff options
| author | 2023-10-09 12:42:57 -0700 | |
|---|---|---|
| committer | 2023-11-02 10:06:47 -0700 | |
| commit | f8c0f1072bf50448ef8f02312f0b411783487fa3 (patch) | |
| tree | cda2c4a14bd2e6025bb08cb5e570711507f57cb5 | |
| parent | f9ed17fb846c3ca6af1fd4065e169604d69abbce (diff) | |
[SF] Adds notifyExpectedPresent call for frame rate change
BUG: 296636253
BUG: 284845445
Test: atest HWComposerTest
Change-Id: Id9445a2765fa546eca64d9084eddc06cdbd090eb
4 files changed, 210 insertions, 32 deletions
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 6807c8e5d7..a44e4be1ce 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -148,7 +148,7 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresentIfRequired, - (PhysicalDisplayId, nsecs_t, int32_t, int32_t)); + (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>)); }; } // namespace mock diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index fb6089dfbe..1d9f9ce73a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -78,6 +78,59 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; namespace hal = android::hardware::graphics::composer::hal; +namespace { +bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + android::Fps lastFrameInterval, android::Period timeout, + android::Duration threshold) { + if (lastFrameInterval.getPeriodNsecs() == 0) { + return false; + } + + const auto expectedPresentTimeDeltaNs = + expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); + + if (expectedPresentTimeDeltaNs > timeout.ns()) { + return false; + } + + const auto expectedPresentPeriods = static_cast<nsecs_t>( + std::round(static_cast<float>(expectedPresentTimeDeltaNs) / + static_cast<float>(lastFrameInterval.getPeriodNsecs()))); + const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; + const auto calculatedExpectedPresentTimeNs = + lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; + const auto presentTimeDelta = + std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); + return presentTimeDelta < threshold.ns(); +} + +bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + std::optional<android::Period> timeoutOpt, + android::Duration threshold) { + if (!timeoutOpt) { + // Always within timeout if timeoutOpt is absent and don't send hint + // for the timeout + return true; + } + + if (timeoutOpt->ns() == 0) { + // Always outside timeout if timeoutOpt is 0 and always send + // the hint for the timeout. + return false; + } + + if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { + return true; + } + + // Check if within the threshold as it can be just outside the timeout + return std::abs(expectedPresentTime.ns() - + (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); +} +} // namespace + namespace android { HWComposer::~HWComposer() = default; @@ -485,7 +538,12 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + { + std::scoped_lock lock{displayData.expectedPresentLock}; + displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime); + // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands + } + if (canSkipValidate) { sp<Fence> outPresentFence; uint32_t state = UINT32_MAX; @@ -879,21 +937,44 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId } status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, - nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) { + Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - auto& displayData = mDisplayData[displayId]; - if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp && - expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) { - return NO_ERROR; - } + { + std::scoped_lock lock{displayData.expectedPresentLock}; + const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp; + const auto lastFrameInterval = displayData.lastFrameInterval; + displayData.lastFrameInterval = frameInterval; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 + ? timeoutOpt->ns() + : kOneSecondNs), + threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return NO_ERROR; + } - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + displayData.lastExpectedPresentTimestamp = expectedPresentTime; + } ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__, - expectedPresentTime, frameIntervalNs); + expectedPresentTime, frameInterval.getPeriodNsecs()); const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(), - expectedPresentTime, frameIntervalNs); + expectedPresentTime.ns(), + frameInterval.getPeriodNsecs()); if (error != hal::Error::NONE) { ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 726a8eafbf..51e93197a8 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -301,9 +301,10 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; - virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, - int32_t timeoutNs) = 0; + virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -462,8 +463,9 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; - status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) override; + status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -497,7 +499,10 @@ private: sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires nsecs_t lastPresentTimestamp = 0; - nsecs_t lastExpectedPresentTimestamp = 0; + std::mutex expectedPresentLock; + TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) = + TimePoint::fromNs(0); + Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0); std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 4f545a9ef3..58d7a40fc8 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -79,10 +79,13 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } - void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) { + void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval) { ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end()); auto& displayData = mHwc.mDisplayData.at(displayId); + std::scoped_lock lock{displayData.expectedPresentLock}; displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; + displayData.lastFrameInterval = lastFrameInterval; } }; @@ -322,48 +325,137 @@ TEST_F(HWComposerTest, notifyExpectedPresentTimeout) { ASSERT_TRUE(info); auto expectedPresentTime = systemTime() + ms2ns(10); - const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs(); - static constexpr nsecs_t kTimeoutNs = ms2ns(30); + static constexpr Fps Fps60Hz = 60_Hz; + static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs(); + static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); + static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0)); + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz)); { // Very first ExpectedPresent after idle, no previous timestamp EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { + // Absent timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + /*timeoutOpt*/ std::nullopt); + } + { + // Timeout is 0 + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(HalError::NONE)); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + Period::fromNs(0)); + } + { // ExpectedPresent is after the timeoutNs - expectedPresentTime += ms2ns(50); + expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent has not changed + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is after the last reported ExpectedPresent. - expectedPresentTime += ms2ns(10); + expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. - expectedPresentTime -= ms2ns(20); + expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } } +TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { + constexpr hal::HWDisplayId kHwcDisplayId = 2; + expectHotplugConnect(kHwcDisplayId); + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + const auto now = systemTime(); + auto expectedPresentTime = now; + static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); + + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0))); + static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + + struct FrameRateIntervalTestData { + int32_t frameIntervalNs; + bool callExpectedPresent; + }; + const std::vector<FrameRateIntervalTestData> 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 (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { + { + expectedPresentTime += frameIntervalNs; + if (callExpectedPresent) { + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, + frameIntervalNs)) + .WillOnce(Return(HalError::NONE)); + } else { + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + } + } +} + struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection)); MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId)); |