SF: pass last vsync to VsyncPredictor
To avoid crossing min frame period on VRR displays, we pass
the last commited vsync and use that as a reference point.
Bug: 313474368
Bug: 316570464
Test: presubmit
Change-Id: I827f5757ab7accb4577b37a5cabb4bd86df82921
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 693a357..725eaf2 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -301,7 +301,7 @@
mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -501,7 +501,7 @@
const auto scheduleResult =
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
} else {
mVsyncRegistration.cancel();
@@ -757,7 +757,7 @@
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
return oldRegistration;
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 18c0a69..cf8b3bf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -125,7 +125,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
return oldRegistration;
}
@@ -143,7 +143,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
void MessageQueue::waitMessage() {
@@ -196,7 +196,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index aa8d54d..d6ec684 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -600,8 +600,10 @@
const Display& display = *displayOpt;
const nsecs_t threshold =
display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2;
- const nsecs_t nextVsyncTime = display.schedulePtr->getTracker().nextAnticipatedVSyncTimeFrom(
- currentExpectedPresentTime.ns() + threshold);
+ const nsecs_t nextVsyncTime =
+ display.schedulePtr->getTracker()
+ .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold,
+ currentExpectedPresentTime.ns());
return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns());
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index c3a952f..f978016 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -84,8 +84,8 @@
* able to provide the ready-by time (deadline) on the callback.
* For internal clients, we don't need to add additional padding, so
* readyDuration will typically be 0.
- * @earliestVsync: The targeted display time. This will be snapped to the closest
- * predicted vsync time after earliestVsync.
+ * @lastVsync: The targeted display time. This will be snapped to the closest
+ * predicted vsync time after lastVsync.
*
* callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
@@ -93,11 +93,11 @@
struct ScheduleTiming {
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
- nsecs_t earliestVsync = 0;
+ nsecs_t lastVsync = 0;
bool operator==(const ScheduleTiming& other) const {
return workDuration == other.workDuration && readyDuration == other.readyDuration &&
- earliestVsync == other.earliestVsync;
+ lastVsync == other.lastVsync;
}
bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
@@ -109,12 +109,12 @@
* The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
*
- * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+ * The caller designates the earliest vsync event that should be targeted by the lastVsync
* parameter.
* The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
- * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ * predictedVsync is the first vsync event time where ( predictedVsync >= lastVsync ).
*
- * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+ * If (workDuration + readyDuration - lastVsync) is in the past, or if a callback has
* already been dispatched for the predictedVsync, an error will be returned.
*
* It is valid to reschedule a callback to a different time.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ef30887..533ccaf 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -45,8 +45,11 @@
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
const VSyncDispatch::ScheduleTiming& timing) {
- const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
- std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ const auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
return getExpectedCallbackTime(nextVsyncTime, timing);
}
@@ -93,8 +96,11 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
- std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
@@ -139,11 +145,13 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
+ *mLastDispatchTime);
}
if (nextVsyncTooClose) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
+ *mLastDispatchTime + currentPeriod);
}
return nextVsyncTime;
@@ -160,11 +168,12 @@
}
const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
- const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+ const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.lastVsync);
const auto nextVsyncTime =
adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
- tracker.nextAnticipatedVSyncTimeFrom(earliestVsync));
+ tracker.nextAnticipatedVSyncTimeFrom(earliestVsync,
+ mScheduleTiming.lastVsync));
const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
@@ -214,10 +223,10 @@
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
- "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
"to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
- (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
+ (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 7379a46..0adfae4 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -247,7 +247,7 @@
}
auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
- const auto vsync = nextAnticipatedVSyncTimeFromLocked(timestamp);
+ const auto vsync = snapToVsync(timestamp);
if (!mLastVsyncSequence) return {vsync, 0};
const auto [slope, _] = getVSyncPredictionModelLocked();
@@ -257,7 +257,7 @@
return {vsync, vsyncSequence};
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
@@ -293,9 +293,29 @@
return prediction;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt) const {
+ ATRACE_CALL();
std::lock_guard lock(mMutex);
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto threshold = currentPeriod / 2;
+ const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto lastFrameMissed =
+ lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold;
+ const nsecs_t baseTime =
+ FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt
+ ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
+ : timePoint;
+ const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime);
+ if (FlagManager::getInstance().vrr_config()) {
+ const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
+ const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
+ mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
+ }
+ return vsyncTime;
+}
+nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
// update the mLastVsyncSequence for reference point
mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
@@ -319,30 +339,12 @@
}();
if (renderRatePhase == 0) {
- const auto vsyncTime = mLastVsyncSequence->vsyncTime;
- if (FlagManager::getInstance().vrr_config()) {
- const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
- ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__,
- ticks<std::milli, float>(vsyncTimePoint - TimePoint::now()));
- const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
- mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
- }
- return vsyncTime;
+ return mLastVsyncSequence->vsyncTime;
}
auto const [slope, intercept] = getVSyncPredictionModelLocked();
const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
- const auto nextAnticipatedVsyncTime =
- nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
- if (FlagManager::getInstance().vrr_config()) {
- const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime);
- ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__,
- ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now()));
- const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
- mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr,
- renderRate);
- }
- return nextAnticipatedVsyncTime;
+ return snapToVsync(approximateNextVsync - slope / 2);
}
/*
@@ -445,6 +447,7 @@
if (mLastVsyncSequence) {
mLastVsyncSequence->vsyncTime += phase.ns();
}
+ mPastExpectedPresentTimes.clear();
}
}
}
@@ -462,23 +465,17 @@
lastConfirmedPresentTime.ns()) /
1e6f);
}
- mPastExpectedPresentTimes.push_back(expectedPresentTime);
-
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
+ mPastExpectedPresentTimes.push_back(expectedPresentTime);
- const auto minFramePeriod = minFramePeriodLocked().ns();
while (!mPastExpectedPresentTimes.empty()) {
const auto front = mPastExpectedPresentTimes.front().ns();
- const bool frontIsLastConfirmed =
- std::abs(front - lastConfirmedPresentTime.ns()) < threshold;
- const bool frontIsBeforeConfirmed =
- front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold;
- if (frontIsLastConfirmed || frontIsBeforeConfirmed) {
+ const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
+ if (frontIsBeforeConfirmed) {
if (CC_UNLIKELY(mTraceOn)) {
ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
- static_cast<float>(lastConfirmedPresentTime.ns() -
- mPastExpectedPresentTimes.front().ns()) /
+ static_cast<float>(lastConfirmedPresentTime.ns() - front) /
1e6f);
}
mPastExpectedPresentTimes.pop_front();
@@ -502,6 +499,7 @@
TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ mLastMissedVsync = expectedPresentTime;
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 72a3431..9191003 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -45,7 +45,9 @@
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt = {}) const final
+ EXCLUDES(mMutex);
nsecs_t currentPeriod() const final EXCLUDES(mMutex);
Period minFramePeriod() const final EXCLUDES(mMutex);
void resetModel() final EXCLUDES(mMutex);
@@ -87,7 +89,8 @@
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex);
bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
Period minFramePeriodLocked() const REQUIRES(mMutex);
void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
@@ -120,6 +123,8 @@
mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
+
+ TimePoint mLastMissedVsync GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 1ed863c..417163f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -57,9 +57,13 @@
* is updated.
*
* \param [in] timePoint The point in time after which to estimate a vsync event.
+ * \param [in] lastVsyncOpt The last vsync time used by the client. If provided, the tracker
+ * should use that as a reference point when generating the new vsync
+ * and avoid crossing the minimal frame period of a VRR display.
* \return A prediction of the timestamp of a vsync event.
*/
- virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(
+ nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0;
/*
* The current period of the vsync signal.