[SF] Updates to not send hints without a present being followed
Updates the hint to be sent through a frame scheduling.
Reduce the number of callbacks to notifyExpectedPresentIfRequired
by making calls only through EventThread.
Timeout hint is sent directly without scheduling a frame
to avoid delay in sending the hint.
A hint through setTransactionState for ScheduleOnTx will
be in a follow up CL
BUG: 316615878
Test: atest NotifyExpectedPresentTest
Change-Id: I60f555d69626656901951808353f4a632e9b5e71
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0c640aa..02f8309 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2750,23 +2750,11 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime();
// TODO(b/255601557) Update frameInterval per display
- refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime);
+ refreshArgs.frameInterval =
+ mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime());
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
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();
@@ -2830,6 +2818,7 @@
scheduleComposite(FrameHint::kNone);
}
+ mNotifyExpectedPresentMap[pacesetterId].hintStatus = NotifyExpectedPresentHintStatus::Start;
onCompositionPresented(pacesetterId, frameTargeters, presentTime);
const bool hadGpuComposited =
@@ -4110,8 +4099,9 @@
}
}
-void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime,
- ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) {
+void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime,
+ ftl::NonNull<DisplayModePtr> modePtr,
+ Fps renderRate) {
const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod();
const auto timeoutOpt = [&]() -> std::optional<Period> {
const auto vrrConfig = modePtr->getVrrConfig();
@@ -4132,45 +4122,91 @@
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);
+ auto& data = mNotifyExpectedPresentMap[displayId];
+ const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp;
+ const auto lastFrameInterval = data.lastFrameInterval;
+ data.lastFrameInterval = frameInterval;
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
+ 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 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);
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return;
+ }
- using fps_approx_ops::operator!=;
- if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
- data.lastExpectedPresentTimestamp = expectedPresentTime;
- }
-
- if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ auto hintStatus = data.hintStatus.load();
+ if (!expectedPresentWithinTimeout) {
+ if (hintStatus != NotifyExpectedPresentHintStatus::Sent ||
+ (timeoutOpt && timeoutOpt->ns() == 0)) {
+ // Send the hint immediately if timeout, as the hint gets
+ // delayed otherwise, as the frame is scheduled close
+ // to the actual present.
+ if (data.hintStatus
+ .compare_exchange_strong(hintStatus,
+ NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
+ scheduleNotifyExpectedPresentHint(displayId);
+ }
return;
}
- data.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
+ if (hintStatus != NotifyExpectedPresentHintStatus::Start) {
+ return;
+ }
+ data.hintStatus.store(NotifyExpectedPresentHintStatus::ScheduleOnPresent);
+ mScheduler->scheduleFrame();
+}
+
+void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
+ auto itr = mNotifyExpectedPresentMap.find(displayId);
+ if (itr == mNotifyExpectedPresentMap.end()) {
+ return;
}
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
- const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
- frameInterval);
+ const auto sendHint = [=, this]() {
+ auto& data = mNotifyExpectedPresentMap.at(displayId);
+ data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent);
+ const auto status =
+ getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp,
+ data.lastFrameInterval);
if (status != NO_ERROR) {
ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
displayId.value);
}
- }));
+ };
+
+ if (itr->second.hintStatus == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
+ return static_cast<void>(mScheduler->schedule([=,
+ this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ auto& data = mNotifyExpectedPresentMap.at(displayId);
+ auto scheduleHintOnTx = NotifyExpectedPresentHintStatus::ScheduleOnTx;
+ if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx,
+ NotifyExpectedPresentHintStatus::Sent)) {
+ sendHint();
+ }
+ }));
+ }
+ sendHint();
+}
+
+void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
+ if (auto itr = mNotifyExpectedPresentMap.find(displayId);
+ itr == mNotifyExpectedPresentMap.end() ||
+ itr->second.hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnPresent) {
+ return;
+ }
+ scheduleNotifyExpectedPresentHint(displayId);
}
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
@@ -4212,8 +4248,7 @@
mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this), features,
- getFactory(), activeRefreshRate, *mTimeStats,
- static_cast<IVsyncTrackerCallback&>(*this));
+ getFactory(), activeRefreshRate, *mTimeStats);
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
if (FlagManager::getInstance().vrr_config()) {
mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);