Merge cherrypicks of ['googleplex-android-review.googlesource.com/26441798'] into 24Q2-release.
Change-Id: Ic57892e0cc2ea3421c9985d2bc63f452e71c485e
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 6d6b70d..84ccf8e 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -20,7 +20,7 @@
#include <android-base/stringprintf.h>
#include <ftl/concat.h>
-#include <gui/TraceUtils.h>
+#include <utils/Trace.h>
#include <log/log_main.h>
#include <scheduler/TimeKeeper.h>
@@ -44,17 +44,6 @@
TimePoint::fromNs(nextVsyncTime)};
}
-void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
- if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
- return;
- }
-
- ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
- ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
- ns2us(*entry.targetVsync() - now), "us");
- ATRACE_FORMAT_INSTANT(trace.c_str());
-}
-
} // namespace
VSyncDispatch::~VSyncDispatch() = default;
@@ -98,7 +87,6 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
now + timing.workDuration +
@@ -110,8 +98,6 @@
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
- ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
- wouldSkipAVsyncTarget, wouldSkipAWakeup);
if (FlagManager::getInstance().dont_skip_on_early_ro()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
nextVsyncTime = mArmedInfo->mActualVsyncTime;
@@ -136,7 +122,7 @@
ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
mWorkloadUpdateInfo = timing;
- const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo);
+ const auto armedInfo = update(tracker, now, timing, mArmedInfo);
return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
TimePoint::fromNs(armedInfo.mActualVsyncTime)};
}
@@ -154,13 +140,11 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
- ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
*mLastDispatchTime);
}
if (nextVsyncTooClose) {
- ATRACE_FORMAT_INSTANT("nextVsyncTooClose");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
*mLastDispatchTime + currentPeriod);
}
@@ -168,11 +152,9 @@
return nextVsyncTime;
}
-auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now,
- VSyncDispatch::ScheduleTiming timing,
- std::optional<ArmingInfo> armedInfo) const
- -> ArmingInfo {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
+auto VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now,
+ VSyncDispatch::ScheduleTiming timing,
+ std::optional<ArmingInfo> armedInfo) const -> ArmingInfo {
const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);
@@ -183,39 +165,29 @@
const auto nextReadyTime = nextVsyncTime - timing.readyDuration;
const auto nextWakeupTime = nextReadyTime - timing.workDuration;
- if (FlagManager::getInstance().dont_skip_on_early_ro()) {
- bool const wouldSkipAVsyncTarget =
- armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
- bool const wouldSkipAWakeup =
- armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
- ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
- wouldSkipAVsyncTarget, wouldSkipAWakeup);
- if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
- return *armedInfo;
- }
+ bool const wouldSkipAVsyncTarget =
+ armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
+ bool const wouldSkipAWakeup =
+ armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
+ if (FlagManager::getInstance().dont_skip_on_early_ro() &&
+ (wouldSkipAVsyncTarget || wouldSkipAWakeup)) {
+ return *armedInfo;
}
return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime};
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::update");
if (!mArmedInfo && !mWorkloadUpdateInfo) {
return;
}
if (mWorkloadUpdateInfo) {
- const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
- const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
- const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
- ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
- " lastVsyncDelta=%" PRId64,
- workDelta, readyDelta, lastVsyncDelta);
mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
- mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo);
+ mArmedInfo = update(tracker, now, mScheduleTiming, mArmedInfo);
}
void VSyncDispatchTimerQueueEntry::disarm() {
@@ -310,7 +282,6 @@
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
- ATRACE_CALL();
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
@@ -323,10 +294,7 @@
if (it != skipUpdateIt) {
callback->update(*mTracker, now);
}
-
- traceEntry(*callback, now);
-
- const auto wakeupTime = *callback->wakeupTime();
+ auto const wakeupTime = *callback->wakeupTime();
if (!min || *min > wakeupTime) {
nextWakeupName = callback->name();
min = wakeupTime;
@@ -335,6 +303,11 @@
}
if (min && min < mIntendedWakeupTime) {
+ if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
+ ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
+ "us; VSYNC in ", ns2us(*targetVsync - now), "us");
+ ATRACE_NAME(trace.c_str());
+ }
setTimer(*min, now);
} else {
ATRACE_NAME("cancel timer");
@@ -343,7 +316,6 @@
}
void VSyncDispatchTimerQueue::timerCallback() {
- ATRACE_CALL();
struct Invocation {
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
@@ -366,9 +338,8 @@
continue;
}
- traceEntry(*callback, now);
-
auto const readyTime = callback->readyTime();
+
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
@@ -382,8 +353,6 @@
}
for (auto const& invocation : invocations) {
- ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
- ATRACE_FORMAT("%s: %s", __func__, trace.c_str());
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
invocation.deadlineTimestamp);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index e4ddc03..252c09c 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -91,8 +91,8 @@
};
nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const;
- ArmingInfo getArmedInfo(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
- std::optional<ArmingInfo>) const;
+ ArmingInfo update(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
+ std::optional<ArmingInfo>) const;
const std::string mName;
const VSyncDispatch::Callback mCallback;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 2f9dfea..8697696 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -45,35 +45,11 @@
static auto constexpr kMaxPercent = 100u;
-namespace {
-nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime,
- std::optional<nsecs_t> lastVsyncOpt) {
- const auto threshold = model.slope / 2;
-
- if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
- const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
- if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) {
- const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
- ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from prev. "
- "adjust by %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
- static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
- static_cast<float>(vsyncFixup) / 1e6f);
- return vsyncFixup;
- }
- }
-
- return 0;
-}
-} // namespace
-
VSyncPredictor::~VSyncPredictor() = default;
-VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
- size_t historySize, size_t minimumSamplesForPrediction,
- uint32_t outlierTolerancePercent)
- : mClock(std::move(clock)),
- mId(modePtr->getPhysicalDisplayId()),
+VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+ : mId(modePtr->getPhysicalDisplayId()),
mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
@@ -171,7 +147,7 @@
mKnownTimestamp = timestamp;
}
ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
- (mClock->now() - *mKnownTimestamp) / 1e6f);
+ (systemTime() - *mKnownTimestamp) / 1e6f);
return false;
}
@@ -274,6 +250,17 @@
return true;
}
+auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
+ const auto vsync = snapToVsync(timestamp);
+ if (!mLastVsyncSequence) return {vsync, 0};
+
+ const auto [slope, _] = getVSyncPredictionModelLocked();
+ const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
+ const auto vsyncSequence = lastVsyncSequence +
+ static_cast<int64_t>(std::round((vsync - lastVsyncTime) / static_cast<float>(slope)));
+ return {vsync, vsyncSequence};
+}
+
nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
auto const [slope, intercept] = getVSyncPredictionModelLocked();
@@ -311,32 +298,51 @@
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
- std::optional<nsecs_t> lastVsyncOpt) {
+ 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;
+ return snapToVsyncAlignedWithRenderRate(baseTime);
+}
- const auto now = TimePoint::fromNs(mClock->now());
- purgeTimelines(now);
+nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
+ // update the mLastVsyncSequence for reference point
+ mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
- std::optional<TimePoint> vsyncOpt;
- for (auto& timeline : mTimelines) {
- vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(getVSyncPredictionModelLocked(),
- minFramePeriodLocked(),
- snapToVsync(timePoint), mMissedVsync,
- lastVsyncOpt);
- if (vsyncOpt) {
- break;
+ const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
+ if (!mRenderRateOpt) return 0;
+ const auto divisor =
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+ *mRenderRateOpt);
+ if (divisor <= 1) return 0;
+
+ int mod = mLastVsyncSequence->seq % divisor;
+ if (mod == 0) return 0;
+
+ // This is actually a bug fix, but guarded with vrr_config since we found it with this
+ // config
+ if (FlagManager::getInstance().vrr_config()) {
+ if (mod < 0) mod += divisor;
}
- }
- LOG_ALWAYS_FATAL_IF(!vsyncOpt);
- if (*vsyncOpt > mLastCommittedVsync) {
- mLastCommittedVsync = *vsyncOpt;
- ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
- float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
+ return divisor - mod;
+ }();
+
+ if (renderRatePhase == 0) {
+ return mLastVsyncSequence->vsyncTime;
}
- return vsyncOpt->ns();
+ auto const [slope, intercept] = getVSyncPredictionModelLocked();
+ const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
+ return snapToVsync(approximateNextVsync - slope / 2);
}
/*
@@ -347,28 +353,32 @@
* isVSyncInPhase(33.3, 30) = false
* isVSyncInPhase(50.0, 30) = true
*/
-bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) {
- if (timePoint == 0) {
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
+ std::lock_guard lock(mMutex);
+ const auto divisor =
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+ frameRate);
+ return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
+}
+
+bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
+ const TimePoint now = TimePoint::now();
+ const auto getTimePointIn = [](TimePoint now, nsecs_t timePoint) -> float {
+ return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
+ };
+ ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint),
+ divisor);
+
+ if (divisor <= 1 || timePoint == 0) {
return true;
}
- std::lock_guard lock(mMutex);
- const auto model = getVSyncPredictionModelLocked();
- const nsecs_t period = model.slope;
+ const nsecs_t period = mRateMap[idealPeriod()].slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
- const auto now = TimePoint::fromNs(mClock->now());
- const auto vsync = snapToVsync(justBeforeTimePoint);
-
- purgeTimelines(now);
-
- for (auto& timeline : mTimelines) {
- if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
- return timeline.isVSyncInPhase(model, vsync, frameRate);
- }
- }
-
- // The last timeline should always be valid
- return mTimelines.back().isVSyncInPhase(model, vsync, frameRate);
+ const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint);
+ ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64,
+ getTimePointIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq);
+ return vsyncSequence.seq % divisor == 0;
}
void VSyncPredictor::setRenderRate(Fps renderRate) {
@@ -376,9 +386,6 @@
ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
std::lock_guard lock(mMutex);
mRenderRateOpt = renderRate;
- mTimelines.back().freeze(TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
- mTimelines.emplace_back(mIdealPeriod, renderRate);
- purgeTimelines(TimePoint::fromNs(mClock->now()));
}
void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
@@ -408,9 +415,8 @@
clearTimestamps();
}
-Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
- TimePoint lastConfirmedPresentTime) {
- ATRACE_CALL();
+void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
+ TimePoint lastConfirmedPresentTime) {
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
const auto minFramePeriod = minFramePeriodLocked().ns();
@@ -436,20 +442,17 @@
if (!mPastExpectedPresentTimes.empty()) {
const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
if (phase > 0ns) {
- for (auto& timeline : mTimelines) {
- timeline.shiftVsyncSequence(phase);
+ if (mLastVsyncSequence) {
+ mLastVsyncSequence->vsyncTime += phase.ns();
}
mPastExpectedPresentTimes.clear();
- return phase;
}
}
-
- return 0ns;
}
void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) {
- ATRACE_NAME("VSyncPredictor::onFrameBegin");
+ ATRACE_CALL();
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
@@ -479,14 +482,11 @@
}
}
- const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
- if (phase > 0ns) {
- mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
- }
+ ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
}
void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
- ATRACE_NAME("VSyncPredictor::onFrameMissed");
+ ATRACE_CALL();
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
@@ -496,15 +496,14 @@
const auto lastConfirmedPresentTime =
TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
- const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
- if (phase > 0ns) {
- mMissedVsync = {expectedPresentTime, Duration::fromNs(0)};
- }
+ ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ mLastMissedVsync = expectedPresentTime;
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
std::lock_guard lock(mMutex);
- return VSyncPredictor::getVSyncPredictionModelLocked();
+ const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+ return {model.slope, model.intercept};
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
@@ -525,11 +524,6 @@
mTimestamps.clear();
mLastTimestampIndex = 0;
}
-
- mTimelines.clear();
- mLastCommittedVsync = TimePoint::fromNs(0);
- mIdealPeriod = Period::fromNs(idealPeriod());
- mTimelines.emplace_back(mIdealPeriod, mRenderRateOpt);
}
bool VSyncPredictor::needsMoreSamples() const {
@@ -553,130 +547,6 @@
period / 1e6f, periodInterceptTuple.slope / 1e6f,
periodInterceptTuple.intercept);
}
- StringAppendF(&result, "\tmTimelines.size()=%zu\n", mTimelines.size());
-}
-
-void VSyncPredictor::purgeTimelines(android::TimePoint now) {
- while (mTimelines.size() > 1) {
- const auto validUntilOpt = mTimelines.front().validUntil();
- if (validUntilOpt && *validUntilOpt < now) {
- mTimelines.pop_front();
- } else {
- break;
- }
- }
- LOG_ALWAYS_FATAL_IF(mTimelines.empty());
- LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
-}
-
-VSyncPredictor::VsyncTimeline::VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt)
- : mIdealPeriod(idealPeriod), mRenderRateOpt(renderRateOpt) {}
-
-void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
- LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
- ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
- mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
- float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
- mValidUntil = lastVsync;
-}
-
-std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
- Model model, Period minFramePeriod, nsecs_t vsync, MissedVsync missedVsync,
- std::optional<nsecs_t> lastVsyncOpt) {
- ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
-
- const auto threshold = model.slope / 2;
- const auto lastFrameMissed =
- lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
- nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
- nsecs_t vsyncFixupTime = 0;
- if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
- vsyncTime += missedVsync.fixup.ns();
- ATRACE_FORMAT_INSTANT("lastFrameMissed");
- } else {
- vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt);
- vsyncTime += vsyncFixupTime;
- }
-
- ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
- if (mValidUntil && vsyncTime > mValidUntil->ns()) {
- ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
- return std::nullopt;
- }
-
- if (vsyncFixupTime > 0) {
- shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
- }
-
- return TimePoint::fromNs(vsyncTime);
-}
-
-auto VSyncPredictor::VsyncTimeline::getVsyncSequenceLocked(Model model, nsecs_t vsync)
- -> VsyncSequence {
- if (!mLastVsyncSequence) return {vsync, 0};
-
- const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
- const auto vsyncSequence = lastVsyncSequence +
- static_cast<int64_t>(std::round((vsync - lastVsyncTime) /
- static_cast<float>(model.slope)));
- return {vsync, vsyncSequence};
-}
-
-nsecs_t VSyncPredictor::VsyncTimeline::snapToVsyncAlignedWithRenderRate(Model model,
- nsecs_t vsync) {
- // update the mLastVsyncSequence for reference point
- mLastVsyncSequence = getVsyncSequenceLocked(model, vsync);
-
- const auto renderRatePhase = [&]() -> int {
- if (!mRenderRateOpt) return 0;
- const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod.ns()),
- *mRenderRateOpt);
- if (divisor <= 1) return 0;
-
- int mod = mLastVsyncSequence->seq % divisor;
- if (mod == 0) return 0;
-
- // This is actually a bug fix, but guarded with vrr_config since we found it with this
- // config
- if (FlagManager::getInstance().vrr_config()) {
- if (mod < 0) mod += divisor;
- }
-
- return divisor - mod;
- }();
-
- if (renderRatePhase == 0) {
- return mLastVsyncSequence->vsyncTime;
- }
-
- return mLastVsyncSequence->vsyncTime + model.slope * renderRatePhase;
-}
-
-bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, Fps frameRate) {
- const auto getVsyncIn = [](TimePoint now, nsecs_t timePoint) -> float {
- return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
- };
-
- Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns());
- const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
- const auto now = TimePoint::now();
-
- if (divisor <= 1) {
- return true;
- }
- const auto vsyncSequence = getVsyncSequenceLocked(model, vsync);
- ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
- getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
- return vsyncSequence.seq % divisor == 0;
-}
-
-void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
- if (mLastVsyncSequence) {
- ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
- mLastVsyncSequence->vsyncTime += phase.ns();
- }
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c175765..8fd7e60 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -22,7 +22,6 @@
#include <vector>
#include <android-base/thread_annotations.h>
-#include <scheduler/TimeKeeper.h>
#include <ui/DisplayId.h>
#include "VSyncTracker.h"
@@ -32,7 +31,6 @@
class VSyncPredictor : public VSyncTracker {
public:
/*
- * \param [in] Clock The clock abstraction. Useful for unit tests.
* \param [in] PhysicalDisplayid The display this corresponds to.
* \param [in] modePtr The initial display mode
* \param [in] historySize The internal amount of entries to store in the model.
@@ -40,13 +38,13 @@
* predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
* samples that fall outlierTolerancePercent from an anticipated vsync event.
*/
- VSyncPredictor(std::unique_ptr<Clock>, ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
+ VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
- std::optional<nsecs_t> lastVsyncOpt = {}) final
+ std::optional<nsecs_t> lastVsyncOpt = {}) const final
EXCLUDES(mMutex);
nsecs_t currentPeriod() const final EXCLUDES(mMutex);
Period minFramePeriod() const final EXCLUDES(mMutex);
@@ -64,7 +62,7 @@
VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
- bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) final EXCLUDES(mMutex);
+ bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
@@ -77,42 +75,10 @@
void dump(std::string& result) const final EXCLUDES(mMutex);
private:
- struct VsyncSequence {
- nsecs_t vsyncTime;
- int64_t seq;
- };
-
- struct MissedVsync {
- TimePoint vsync;
- Duration fixup = Duration::fromNs(0);
- };
-
- class VsyncTimeline {
- public:
- VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt);
- std::optional<TimePoint> nextAnticipatedVSyncTimeFrom(
- Model model, Period minFramePeriod, nsecs_t vsyncTime, MissedVsync lastMissedVsync,
- std::optional<nsecs_t> lastVsyncOpt = {});
- void freeze(TimePoint lastVsync);
- std::optional<TimePoint> validUntil() const { return mValidUntil; }
- bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
- void shiftVsyncSequence(Duration phase);
-
- private:
- nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
- VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
-
- const Period mIdealPeriod = Duration::fromNs(0);
- const std::optional<Fps> mRenderRateOpt;
- std::optional<TimePoint> mValidUntil;
- std::optional<VsyncSequence> mLastVsyncSequence;
- };
-
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
void clearTimestamps() REQUIRES(mMutex);
- const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
inline void traceInt64If(const char* name, int64_t value) const;
@@ -122,10 +88,16 @@
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
Model getVSyncPredictionModelLocked() 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);
- Duration ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
- void purgeTimelines(android::TimePoint now) REQUIRES(mMutex);
+ void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
+ struct VsyncSequence {
+ nsecs_t vsyncTime;
+ int64_t seq;
+ };
+ VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);
nsecs_t idealPeriod() const REQUIRES(mMutex);
bool const mTraceOn;
@@ -143,15 +115,13 @@
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
+ std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
+
+ mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
- MissedVsync mMissedVsync GUARDED_BY(mMutex);
-
- std::deque<VsyncTimeline> mTimelines GUARDED_BY(mMutex);
- TimePoint mLastCommittedVsync GUARDED_BY(mMutex) = TimePoint::fromNs(0);
- Period mIdealPeriod GUARDED_BY(mMutex) = Duration::fromNs(0);
- std::optional<Fps> mRenderRateOpt 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 1e55a87..37bd4b4 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -56,8 +56,8 @@
* 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,
- std::optional<nsecs_t> lastVsyncOpt = {}) = 0;
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(
+ nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0;
/*
* The current period of the vsync signal.
@@ -82,7 +82,7 @@
* \param [in] timePoint A vsync timestamp
* \param [in] frameRate The frame rate to check for
*/
- virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) = 0;
+ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
/*
* Sets the active mode of the display which includes the vsync period and other VRR attributes.
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 2fa3318..001938c 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -120,8 +120,8 @@
constexpr size_t kMinSamplesForPrediction = 6;
constexpr uint32_t kDiscardOutlierPercent = 20;
- return std::make_unique<VSyncPredictor>(std::make_unique<SystemClock>(), modePtr, kHistorySize,
- kMinSamplesForPrediction, kDiscardOutlierPercent);
+ return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction,
+ kDiscardOutlierPercent);
}
VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 881d678..85cd3e7 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -81,7 +81,7 @@
bool addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod);
// TODO(b/185535769): Hide behind API.
- VsyncTracker& getTracker() const { return *mTracker; }
+ const VsyncTracker& getTracker() const { return *mTracker; }
VsyncTracker& getTracker() { return *mTracker; }
VsyncController& getController() { return *mController; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9e13144..cf5f55d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4702,14 +4702,7 @@
return TransactionReadiness::NotReady;
}
- const auto vsyncId = VsyncId{transaction.frameTimelineInfo.vsyncId};
-
- // Transactions with VsyncId are already throttled by the vsyncId (i.e. Choreographer issued
- // the vsyncId according to the frame rate override cadence) so we shouldn't throttle again
- // when applying the transaction. Otherwise we might throttle older transactions
- // incorrectly as the frame rate of SF changed before it drained the older transactions.
- if (ftl::to_underlying(vsyncId) == FrameTimelineInfo::INVALID_VSYNC_ID &&
- !mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
+ if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
transaction.originUid);
return TransactionReadiness::NotReady;
@@ -4717,7 +4710,8 @@
// If the client didn't specify desiredPresentTime, use the vsyncId to determine the
// expected present time of this transaction.
- if (transaction.isAutoTimestamp && frameIsEarly(expectedPresentTime, vsyncId)) {
+ if (transaction.isAutoTimestamp &&
+ frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
return TransactionReadiness::NotReady;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 049b092..10e2220 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -25,7 +25,6 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncPredictor.h"
-#include "Scheduler/VSyncReactor.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -564,8 +563,7 @@
hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>(
frameRate.getPeriodNsecs())}));
std::shared_ptr<VSyncPredictor> vrrTracker =
- std::make_shared<VSyncPredictor>(std::make_unique<SystemClock>(), kMode, kHistorySize,
- kMinimumSamplesForPrediction,
+ std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction,
kOutlierTolerancePercent);
std::shared_ptr<RefreshRateSelector> vrrSelectorPtr =
std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId());
@@ -580,8 +578,6 @@
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
vrrTracker->addVsyncTimestamp(0);
- // Set 1000 as vsync seq #0
- vrrTracker->nextAnticipatedVSyncTimeFrom(700);
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
@@ -591,7 +587,7 @@
TimePoint::fromNs(2000)));
// Not crossing the min frame period
- EXPECT_EQ(Fps::fromPeriodNsecs(1000),
+ EXPECT_EQ(Fps::fromPeriodNsecs(1500),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2500)));
// Change render rate
@@ -599,9 +595,6 @@
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
- // Set 2000 as vsync seq #0
- vrrTracker->nextAnticipatedVSyncTimeFrom(1700);
-
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2000)));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c22deab..d891008 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -48,7 +48,7 @@
Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
void setRenderRate(Fps) final {}
void onFrameBegin(TimePoint, TimePoint) final {}
@@ -64,7 +64,7 @@
public:
FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
return timePoint;
@@ -77,7 +77,7 @@
public:
VRRStubTracker(nsecs_t period) : StubTracker(period) {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final {
std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 6b9ea56..b9f3d70 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -75,28 +75,6 @@
return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution,
DEFAULT_DISPLAY_ID));
}
-
-class TestClock : public Clock {
-public:
- TestClock() = default;
-
- nsecs_t now() const override { return mNow; }
- void setNow(nsecs_t now) { mNow = now; }
-
-private:
- nsecs_t mNow = 0;
-};
-
-class ClockWrapper : public Clock {
-public:
- ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
-
- nsecs_t now() const { return mClock->now(); }
-
-private:
- std::shared_ptr<Clock> const mClock;
-};
-
} // namespace
struct VSyncPredictorTest : testing::Test {
@@ -108,10 +86,8 @@
static constexpr size_t kOutlierTolerancePercent = 25;
static constexpr nsecs_t mMaxRoundingError = 100;
- std::shared_ptr<TestClock> mClock{std::make_shared<TestClock>()};
-
- VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mMode, kHistorySize,
- kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent};
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
@@ -432,8 +408,7 @@
// See b/151146131
TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
const auto mode = displayMode(mPeriod);
- VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mode, 20,
- kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
840923581635, 840940161584, 840956868096,
840973702473, 840990256277, 841007116851,
@@ -631,6 +606,35 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
}
+TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) {
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
+
+ tracker.setRenderRate(refreshRate / 4);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod));
+
+ tracker.setRenderRate(refreshRate / 2);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5 * mPeriod), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod));
+
+ tracker.setRenderRate(refreshRate / 6);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod));
+}
+
TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
auto last = mNow;
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
@@ -666,8 +670,8 @@
.setVrrConfig(std::move(vrrConfig))
.build());
- VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
- kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent};
vrrTracker.setRenderRate(minFrameRate);
vrrTracker.addVsyncTimestamp(0);
@@ -683,44 +687,7 @@
vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
-
- vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500));
- EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
-
-TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
- tracker.addVsyncTimestamp(1000);
-
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
-
- tracker.setRenderRate(Fps::fromPeriodNsecs(2000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
-
- tracker.setRenderRate(Fps::fromPeriodNsecs(3000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(11001), Eq(14000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(12001), Eq(14000));
-
- // Check the purge logic works
- mClock->setNow(20000);
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(2000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(8000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000));
-}
-
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 6d10a5c..3870983 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -29,12 +29,12 @@
MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override));
MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>),
- (override));
+ (const, override));
MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override));
MOCK_METHOD(Period, minFramePeriod, (), (const, override));
MOCK_METHOD(void, resetModel, (), (override));
MOCK_METHOD(bool, needsMoreSamples, (), (const, override));
- MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override));
+ MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override));
MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
MOCK_METHOD(void, setRenderRate, (Fps), (override));
MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));