summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
authorramindani <ramindani@google.com>2023-11-28 14:58:47 -0800
committerramindani <ramindani@google.com>2023-12-13 15:14:06 -0800
commitc67d22c8b485dcdd95714fb3e1fed6a3b0d29672 (patch)
tree49ad93cb04eb662b059444fe8954072ba4d9391b /services
parent9a296f97bcf9c3b28b7e4469e29c6279bcd1c1b8 (diff)
downloadnative-c67d22c8b485dcdd95714fb3e1fed6a3b0d29672.tar.gz
[SF] Move the notifyExpectedPresentHint call to SF
This moves the notifyExpectedPresent call off the HWComposer, HWComposer should only be access with mStateLock or from the main thread, and moving this to SF achieves that. Schedule the HWComposer::notifyExpectedPresent call on the main thread once the decision to send the hint is made BUG: 311300327 Test: atest Change-Id: Ia5f92546028ce104e391364c6696415c29760232
Diffstat (limited to 'services')
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h3
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp113
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.h17
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp116
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h13
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp1
-rw-r--r--services/surfaceflinger/tests/unittests/HWComposerTest.cpp147
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp180
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h15
9 files changed, 328 insertions, 277 deletions
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index b2491d894c..3acc402b76 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -147,8 +147,7 @@ public:
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
- MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
- (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>));
+ MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
};
} // namespace mock
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 10df216b02..9d93b30f0a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -15,6 +15,7 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <chrono>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -78,59 +79,6 @@ 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;
@@ -538,13 +486,6 @@ status_t HWComposer::getDeviceCompositionChanges(
}();
displayData.validateWasSkipped = false;
- {
- std::scoped_lock lock{displayData.expectedPresentLock};
- if (expectedPresentTime > displayData.lastExpectedPresentTimestamp.ns()) {
- displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
- }
- }
-
ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue());
if (canSkipValidate) {
sp<Fence> outPresentFence = Fence::NO_FENCE;
@@ -939,55 +880,15 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId
return NO_ERROR;
}
-status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
- Period vsyncPeriod,
- TimePoint expectedPresentTime,
- Fps frameInterval,
- std::optional<Period> timeoutOpt) {
+status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId,
+ TimePoint expectedPresentTime, Fps frameInterval) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- auto& displayData = mDisplayData[displayId];
- if (!displayData.hwcDisplay) {
- // Display setup has not completed yet
- return BAD_INDEX;
- }
- {
- 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);
-
- using fps_approx_ops::operator!=;
- if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
- }
-
- if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
- return NO_ERROR;
- }
-
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
- }
- ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
- expectedPresentTime, frameInterval.getPeriodNsecs());
- const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
+ ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__,
+ ticks<std::milli, float>(expectedPresentTime - TimePoint::now()),
+ ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs())));
+ const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(),
expectedPresentTime.ns(),
frameInterval.getPeriodNsecs());
-
if (error != hal::Error::NONE) {
ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
return INVALID_OPERATION;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5846c07f87..3d18e9a8a4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -302,10 +302,8 @@ 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, Period vsyncPeriod,
- TimePoint expectedPresentTime,
- Fps frameInterval,
- std::optional<Period> timeoutOpt) = 0;
+ virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -464,9 +462,8 @@ 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, Period vsyncPeriod,
- TimePoint expectedPresentTime, Fps frameInterval,
- std::optional<Period> timeoutOpt) override;
+ status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -492,7 +489,6 @@ public:
private:
// For unit tests
friend TestableSurfaceFlinger;
- friend HWComposerTest;
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -500,11 +496,6 @@ private:
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 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;
bool validateWasSkipped;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 85c16b716d..4262db91fd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -318,6 +318,53 @@ bool fileNewerThan(const std::string& path, std::chrono::minutes duration) {
return duration > (Clock::now() - updateTime);
}
+bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval, Period timeout, 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(TimePoint expectedPresentTime,
+ TimePoint lastExpectedPresentTimestamp,
+ std::optional<Period> timeoutOpt, 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 anonymous
// ---------------------------------------------------------------------------
@@ -2702,7 +2749,18 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
refreshArgs.expectedPresentTime = expectedPresentTime.ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
-
+ {
+ auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId];
+ auto lastExpectedPresentTimestamp = TimePoint::fromNs(
+ notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns());
+ if (expectedPresentTime > lastExpectedPresentTimestamp) {
+ // If the values are not same, then hint is sent with newer value.
+ // And because composition always follows the notifyExpectedPresentIfRequired, we can
+ // skip updating the lastExpectedPresentTimestamp in this case.
+ notifyExpectedPresentData.lastExpectedPresentTimestamp
+ .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime);
+ }
+ }
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
const auto presentTime = systemTime();
@@ -4053,7 +4111,7 @@ void SurfaceFlinger::onChoreographerAttached() {
void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime,
ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) {
const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod();
- const auto timeout = [&]() -> std::optional<Period> {
+ const auto timeoutOpt = [&]() -> std::optional<Period> {
const auto vrrConfig = modePtr->getVrrConfig();
if (!vrrConfig) return std::nullopt;
@@ -4063,14 +4121,54 @@ void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime,
return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
}();
- const auto displayId = modePtr->getPhysicalDisplayId();
- const auto status = getHwComposer().notifyExpectedPresentIfRequired(displayId, vsyncPeriod,
- expectedPresentTime,
- renderRate, timeout);
- if (status != NO_ERROR) {
- ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__,
- displayId.value);
+ notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod,
+ expectedPresentTime, renderRate, timeoutOpt);
+}
+
+void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
+ Period vsyncPeriod,
+ TimePoint expectedPresentTime,
+ Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ {
+ auto& data = mNotifyExpectedPresentMap[displayId];
+ const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load();
+ const auto lastFrameInterval = data.lastFrameInterval;
+ data.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 auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns()
+ : kOneSecondNs);
+ const bool frameIntervalIsOnCadence =
+ isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
+ lastFrameInterval, timeout, threshold);
+
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+
+ using fps_approx_ops::operator!=;
+ if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return;
+ }
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
}
+
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+ frameInterval);
+ if (status != NO_ERROR) {
+ ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
+ displayId.value);
+ }
+ }));
}
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 788fe73519..b14c9be88d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1460,6 +1460,19 @@ private:
// Map of displayid to mirrorRoot
ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
+ // NotifyExpectedPresentHint
+ struct NotifyExpectedPresentData {
+ // lastExpectedPresentTimestamp is read and write from multiple threads such as
+ // main thread, EventThread, MessageQueue. And is atomic for that reason.
+ std::atomic<TimePoint> lastExpectedPresentTimestamp{};
+ Fps lastFrameInterval{};
+ };
+ std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
+
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt);
+
void sfdo_enableRefreshRateOverlay(bool active);
void sfdo_setDebugFlash(int delay);
void sfdo_scheduleComposite();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index dea019431b..c75f90222a 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -106,6 +106,7 @@ cc_test {
"SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_InitializeDisplaysTest.cpp",
+ "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_PowerHintTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index d3ce4f2b90..c9edb84fcb 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -82,15 +82,6 @@ struct HWComposerTest : testing::Test {
EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
-
- 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;
- }
};
TEST_F(HWComposerTest, isHeadless) {
@@ -417,144 +408,6 @@ TEST_F(HWComposerTest, onVsyncInvalid) {
EXPECT_FALSE(displayIdOpt);
}
-TEST_F(HWComposerTest, notifyExpectedPresentTimeout) {
- constexpr hal::HWDisplayId kHwcDisplayId = 2;
- expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
- ASSERT_TRUE(info);
-
- auto expectedPresentTime = systemTime() + ms2ns(10);
- 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, kLastExpectedPresentTimestamp, Fps60Hz));
-
- {
- // Very first ExpectedPresent after idle, no previous timestamp
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- 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 += 2 * kFrameInterval5HzNs;
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- 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 += kFrameInterval60HzNs;
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- 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 -= kFrameInterval120HzNs;
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
- .WillOnce(Return(HalError::NONE));
- 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_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent),
(override));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
new file mode 100644
index 0000000000..7206e2977d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class NotifyExpectedPresentTest : public DisplayTransactionTest {
+public:
+ void SetUp() override {
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject();
+ FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary)
+ .setPowerMode(hal::PowerMode::ON)
+ .inject(&mFlinger, mComposer);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+ static constexpr bool kIsPrimary = true;
+ static constexpr hal::HWDisplayId HWC_DISPLAY_ID =
+ FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+};
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ auto expectedPresentTime = systemTime() + ms2ns(10);
+ static constexpr Fps kFps60Hz = 60_Hz;
+ static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.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(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ kLastExpectedPresentTimestamp,
+ kFps60Hz));
+
+ {
+ // Very first ExpectedPresent after idle, no previous timestamp
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // Absent timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ /*timeoutOpt*/ std::nullopt);
+ }
+ {
+ // Timeout is 0
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ Period::fromNs(0));
+ }
+ {
+ // ExpectedPresent is after the timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent has not changed
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is after the last reported ExpectedPresent.
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ 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 -= kFrameInterval120HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+}
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ const auto now = systemTime();
+ auto expectedPresentTime = now;
+ static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
+
+ ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ 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(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ frameIntervalNs))
+ .WillOnce(Return(Error::NONE));
+ } else {
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ }
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime),
+ Fps::fromPeriodNsecs(frameIntervalNs),
+ kTimeoutNs);
+ }
+ }
+}
+} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 0909178777..fd578a227f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -694,6 +694,21 @@ public:
mFlinger->mLegacyFrontEndEnabled = false;
}
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ mFlinger->notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime,
+ frameInterval, timeoutOpt);
+ }
+
+ void setNotifyExpectedPresentData(PhysicalDisplayId displayId,
+ TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval) {
+ auto& displayData = mFlinger->mNotifyExpectedPresentMap[displayId];
+ displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+ displayData.lastFrameInterval = lastFrameInterval;
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may