SF: Move some Scheduler APIs to VsyncSchedule
Scheduler::getDisplayStatInfo forces callers to query both the deadline
and period, so split it into two VsyncSchedule APIs. Inline Scheduler::
getPreviousVsyncFrom.
Introduce <scheduler/Time.h> for type-safe, self-documenting wrappers
around std::chrono, which will gradually supersede nsecs_t (including
<scheduler/Fps.h> integration).
Clean up SF helpers for previous present fences. Remove the unnecessary
and inaccurate CompositorTiming initialization.
Bug: 185535769
Test: Perfetto timeline is green.
Change-Id: I22d8ad44ae37612e66f9d98fd4e7e1831951eb99
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d313406..65e4b4e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1055,12 +1055,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
- if (!stats) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) {
+ if (!outStats) {
return BAD_VALUE;
}
- *stats = mScheduler->getDisplayStatInfo(systemTime());
+ const auto& schedule = mScheduler->getVsyncSchedule();
+ outStats->vsyncTime = schedule.vsyncDeadlineAfter(scheduler::SchedulerClock::now()).ns();
+ outStats->vsyncPeriod = schedule.period().ns();
return NO_ERROR;
}
@@ -1569,12 +1571,10 @@
status_t SurfaceFlinger::injectVSync(nsecs_t when) {
Mutex::Autolock lock(mStateLock);
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
- const auto expectedPresent = calculateExpectedPresentTime(stats);
- return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
- /*deadlineTimestamp=*/expectedPresent)
- ? NO_ERROR
- : BAD_VALUE;
+ const nsecs_t expectedPresentTime = calculateExpectedPresentTime(when).ns();
+ const nsecs_t deadlineTimestamp = expectedPresentTime;
+ return mScheduler->injectVSync(when, expectedPresentTime, deadlineTimestamp) ? NO_ERROR
+ : BAD_VALUE;
}
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
@@ -1951,18 +1951,15 @@
}));
}
-SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
- const auto now = systemTime();
- const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
- const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
- return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+auto SurfaceFlinger::getPreviousPresentFence(nsecs_t frameTime, Period vsyncPeriod)
+ -> const FenceTimePtr& {
+ const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod.ns();
+ const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
+ return mPreviousPresentFences[i].fenceTime;
}
-bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
+bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
ATRACE_CALL();
- const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
-
if (fence == FenceTime::NO_FENCE) {
return false;
}
@@ -1973,37 +1970,32 @@
return status == -ETIME;
}
-nsecs_t SurfaceFlinger::previousFramePresentTime() {
- const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
+TimePoint SurfaceFlinger::calculateExpectedPresentTime(nsecs_t frameTime) const {
+ const auto& schedule = mScheduler->getVsyncSchedule();
- if (fence == FenceTime::NO_FENCE) {
- return Fence::SIGNAL_TIME_INVALID;
+ const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(frameTime));
+ if (mVsyncModulator->getVsyncConfig().sfOffset > 0) {
+ return vsyncDeadline;
}
- return fence->getSignalTime();
-}
-
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
- // Inflate the expected present time if we're targetting the next vsync.
- return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
- : stats.vsyncTime + stats.vsyncPeriod;
+ // Inflate the expected present time if we're targeting the next vsync.
+ return vsyncDeadline + schedule.period();
}
bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
- // calculate the expected present time once and use the cached
- // value throughout this frame to make sure all layers are
- // seeing this same value.
- if (expectedVsyncTime >= frameTime) {
- mExpectedPresentTime = expectedVsyncTime;
- } else {
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
- mExpectedPresentTime = calculateExpectedPresentTime(stats);
- }
-
+ // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
+ // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
+ // accordingly, but not mScheduledPresentTime.
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVsyncTime;
+ // Calculate the expected present time once and use the cached value throughout this frame to
+ // make sure all layers are seeing this same value.
+ mExpectedPresentTime = expectedVsyncTime >= frameTime
+ ? expectedVsyncTime
+ : calculateExpectedPresentTime(frameTime).ns();
+
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2011,6 +2003,9 @@
ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
+ const Period vsyncPeriod = mScheduler->getVsyncSchedule().period();
+ const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
+
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
@@ -2019,7 +2014,8 @@
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
- previousFramePending(graceTimeForPresentFenceMs)};
+ isFencePending(previousPresentFence,
+ graceTimeForPresentFenceMs)};
// Frame missed counts for metrics tracking.
// A frame is missed if the prior frame is still pending. If no longer pending,
@@ -2029,9 +2025,8 @@
// Add some slop to correct for drift. This should generally be
// smaller than a typical frame duration, but should not be so small
// that it reports reasonable drift as a missed frame.
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
- const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
- const nsecs_t previousPresentTime = previousFramePresentTime();
+ const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+ const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
framePending ||
(previousPresentTime >= 0 &&
@@ -2118,7 +2113,7 @@
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
- mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
+ mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(vsyncPeriod.ns()));
bool needsTraversal = false;
if (clearTransactionFlags(eTransactionFlushNeeded)) {
@@ -2239,7 +2234,9 @@
}
const auto expectedPresentTime = mExpectedPresentTime.load();
- const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
+ const auto prevVsyncTime =
+ TimePoint::fromNs(expectedPresentTime) - mScheduler->getVsyncSchedule().period();
+
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
@@ -2326,11 +2323,11 @@
mLayersPendingRefresh.clear();
}
-void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
- std::shared_ptr<FenceTime>& presentFenceTime) {
+nsecs_t SurfaceFlinger::trackPresentLatency(nsecs_t compositeTime,
+ std::shared_ptr<FenceTime> presentFenceTime) {
// Update queue of past composite+present times and determine the
// most recently known composite to present latency.
- getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
+ getBE().mCompositePresentTimes.push({compositeTime, std::move(presentFenceTime)});
nsecs_t compositeToPresentLatency = -1;
while (!getBE().mCompositePresentTimes.empty()) {
SurfaceFlingerBE::CompositePresentTime& cpt = getBE().mCompositePresentTimes.front();
@@ -2349,13 +2346,13 @@
getBE().mCompositePresentTimes.pop();
}
- setCompositorTimingSnapped(stats, compositeToPresentLatency);
+ return compositeToPresentLatency;
}
-void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
+void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod,
nsecs_t compositeToPresentLatency) {
// Avoid division by 0 by defaulting to 60Hz
- const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs();
+ vsyncPeriod = vsyncPeriod ?: (60_Hz).getPeriodNsecs();
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
@@ -2381,7 +2378,7 @@
(extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency;
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
- getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
+ getBE().mCompositorTiming.deadline = vsyncDeadline - idealLatency;
getBE().mCompositorTiming.interval = vsyncPeriod;
getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
}
@@ -2470,13 +2467,18 @@
mFrameTimeline->setSfPresent(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime,
glCompositionDoneFenceTime);
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
-
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
- // but that should be okay since updateCompositorTiming has snapping logic.
- updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
- mPreviousPresentFences[0].fenceTime);
+ // but that should be okay since setCompositorTimingSnapped has snapping logic.
+ const nsecs_t compositeTime = mCompositionEngine->getLastFrameRefreshTimestamp();
+ const nsecs_t presentLatency =
+ trackPresentLatency(compositeTime, mPreviousPresentFences[0].fenceTime);
+
+ const auto& schedule = mScheduler->getVsyncSchedule();
+ const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(now));
+ const Period vsyncPeriod = schedule.period();
+ setCompositorTimingSnapped(vsyncDeadline.ns(), vsyncPeriod.ns(), presentLatency);
+
CompositorTiming compositorTiming;
{
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2593,7 +2595,7 @@
mHasPoweredOff = false;
} else {
nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
- size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
+ const size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncPeriod.ns());
if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
getBE().mFrameBuckets[numPeriods] += elapsedTime;
} else {
@@ -3470,8 +3472,8 @@
mInterceptor->saveVSyncEvent(timestamp);
});
- mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
- configs.late.sfWorkDuration);
+ mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
+ *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3941,10 +3943,10 @@
bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
// The amount of time SF can delay a frame if it is considered early based
// on the VsyncModulator::VsyncConfig::appWorkDuration
- constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
+ constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
- const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
- const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
+ const nsecs_t currentVsyncPeriod = mScheduler->getVsyncSchedule().period().ns();
+ const nsecs_t earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
if (!prediction.has_value()) {
@@ -4839,10 +4841,6 @@
const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
mActiveDisplayTransformHint = display->getTransformHint();
- // Use phase of 0 since phase is not known.
- // Use latency of 0, which will snap to the ideal latency.
- DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
- setCompositorTimingSnapped(stats, 0);
}
void SurfaceFlinger::initializeDisplays() {