diff options
author | Ady Abraham <adyabr@google.com> | 2023-11-15 18:41:35 -0800 |
---|---|---|
committer | Ady Abraham <adyabr@google.com> | 2023-11-21 15:22:34 -0800 |
commit | c585dbac703f85d2febc9b7f31dd797bc2b04d81 (patch) | |
tree | 5f0bb60ce1e08b48e1f10985493586976b28a099 | |
parent | 43881b0fa27a23422041337c89d95098a2d4b618 (diff) | |
download | native-c585dbac703f85d2febc9b7f31dd797bc2b04d81.tar.gz |
SF: pass DisplayMode to VsyncTracker
This will be used later to get the peak refresh rate from the predictor
for sub-frame jank recover in VRR case.
Bug: 296635687
Test: presubmit
Change-Id: I1e108223b6ae4872bb48a38e4af743da565749cd
26 files changed, 341 insertions, 313 deletions
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp index 6d2586ae9c..db247aaebc 100644 --- a/services/surfaceflinger/Scheduler/Android.bp +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -21,6 +21,7 @@ cc_defaults { "libui", "libutils", ], + static_libs: ["libsurfaceflinger_common"], } cc_library_headers { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index b54f33451b..29b1d62956 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -118,7 +118,7 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { auto schedulePtr = std::make_shared<VsyncSchedule>( - displayId, mFeatures, + selectorPtr->getActiveMode().modePtr, mFeatures, [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, mVsyncTrackerCallback); @@ -503,7 +503,7 @@ void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) { } void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable, - std::optional<Fps> refreshRate) { + DisplayModePtr modePtr) { const auto displayOpt = mDisplays.get(id); if (!displayOpt) { ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str()); @@ -512,12 +512,12 @@ void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEn const Display& display = *displayOpt; if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) { - if (!refreshRate) { - refreshRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate(); + if (!modePtr) { + modePtr = display.selectorPtr->getActiveMode().modePtr.get(); } - if (refreshRate->isValid()) { + if (modePtr->getVsyncRate().isValid()) { constexpr bool kForce = false; - display.schedulePtr->startPeriodTransition(refreshRate->getPeriod(), kForce); + display.schedulePtr->onDisplayModeChanged(ftl::as_non_null(modePtr), kForce); } } } @@ -563,19 +563,7 @@ void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), to_string(mode.modePtr->getVsyncRate()).c_str()); - display.schedulePtr->getTracker().setDisplayModeData( - {.renderRate = renderFrameRate, - .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)}); -} - -std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) { - if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) { - return Period::fromNs( - mode.modePtr->getVrrConfig() - ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); - } else { - return std::nullopt; - } + display.schedulePtr->getTracker().setRenderRate(renderFrameRate); } void Scheduler::resync() { @@ -913,9 +901,9 @@ std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked( newVsyncSchedulePtr = pacesetter.schedulePtr; - const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate(); constexpr bool kForce = true; - newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce); + newVsyncSchedulePtr->onDisplayModeChanged(pacesetter.selectorPtr->getActiveMode().modePtr, + kForce); } return newVsyncSchedulePtr; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index c78051a278..0615b31c10 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -210,13 +210,12 @@ public: // If allowToEnable is true, then hardware vsync will be turned on. // Otherwise, if hardware vsync is not already enabled then this method will // no-op. - // If refreshRate is nullopt, use the existing refresh rate of the display. + // If modePtr is nullopt, use the active display mode. void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable, - std::optional<Fps> refreshRate = std::nullopt) - EXCLUDES(mDisplayLock) { + DisplayModePtr modePtr = nullptr) EXCLUDES(mDisplayLock) { std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); - resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate); + resyncToHardwareVsyncLocked(id, allowToEnable, modePtr); } void forceNextResync() { mLastResyncTime = 0; } @@ -354,7 +353,7 @@ private: void onHardwareVsyncRequest(PhysicalDisplayId, bool enable); void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable, - std::optional<Fps> refreshRate = std::nullopt) + DisplayModePtr modePtr = nullptr) REQUIRES(kMainThreadContext, mDisplayLock); void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock); void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod); @@ -431,9 +430,6 @@ private: Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); - std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&) - REQUIRES(mDisplayLock); - // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index f5f93ce2f1..acb7265fc5 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -47,16 +47,16 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; -VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize, +VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, IVsyncTrackerCallback& callback) - : mId(id), + : mId(modePtr->getPhysicalDisplayId()), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), mVsyncTrackerCallback(callback), - mIdealPeriod(idealPeriod) { + mDisplayModePtr(modePtr) { resetModel(); } @@ -74,13 +74,18 @@ inline size_t VSyncPredictor::next(size_t i) const { return (i + 1) % mTimestamps.size(); } +nsecs_t VSyncPredictor::idealPeriod() const { + return mDisplayModePtr->getVsyncRate().getPeriodNsecs(); +} + bool VSyncPredictor::validate(nsecs_t timestamp) const { if (mLastTimestampIndex < 0 || mTimestamps.empty()) { return true; } - auto const aValidTimestamp = mTimestamps[mLastTimestampIndex]; - auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod; + const auto aValidTimestamp = mTimestamps[mLastTimestampIndex]; + const auto percent = + (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { return false; @@ -90,7 +95,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { [timestamp](nsecs_t a, nsecs_t b) { return std::abs(timestamp - a) < std::abs(timestamp - b); }); - const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod; + const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod(); if (distancePercent < kOutlierTolerancePercent) { // duplicate timestamp return false; @@ -100,7 +105,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { nsecs_t VSyncPredictor::currentPeriod() const { std::lock_guard lock(mMutex); - return mRateMap.find(mIdealPeriod)->second.slope; + return mRateMap.find(idealPeriod())->second.slope; } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { @@ -137,7 +142,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { const size_t numSamples = mTimestamps.size(); if (numSamples < kMinimumSamplesForPrediction) { - mRateMap[mIdealPeriod] = {mIdealPeriod, 0}; + mRateMap[idealPeriod()] = {idealPeriod(), 0}; return true; } @@ -161,7 +166,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // Normalizing to the oldest timestamp cuts down on error in calculating the intercept. const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end()); - auto it = mRateMap.find(mIdealPeriod); + auto it = mRateMap.find(idealPeriod()); auto const currentPeriod = it->second.slope; // The mean of the ordinals must be precise for the intercept calculation, so scale them up for @@ -199,7 +204,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { } if (CC_UNLIKELY(bottom == 0)) { - it->second = {mIdealPeriod, 0}; + it->second = {idealPeriod(), 0}; clearTimestamps(); return false; } @@ -207,9 +212,9 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom; nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor); - auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod; + auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent) { - it->second = {mIdealPeriod, 0}; + it->second = {idealPeriod(), 0}; clearTimestamps(); return false; } @@ -241,8 +246,8 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co if (mTimestamps.empty()) { traceInt64("VSP-mode", 1); auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint; - auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1; - return knownTimestamp + numPeriodsOut * mIdealPeriod; + auto const numPeriodsOut = ((timePoint - knownTimestamp) / idealPeriod()) + 1; + return knownTimestamp + numPeriodsOut * idealPeriod(); } auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end()); @@ -278,11 +283,11 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { mLastVsyncSequence = getVsyncSequenceLocked(timePoint); const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mDisplayModeDataOpt) return 0; + if (!mRenderRateOpt) return 0; const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), - mDisplayModeDataOpt->renderRate); + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), + *mRenderRateOpt); if (divisor <= 1) return 0; const int mod = mLastVsyncSequence->seq % divisor; @@ -293,12 +298,12 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { if (renderRatePhase == 0) { const auto vsyncTime = mLastVsyncSequence->vsyncTime; - if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + 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())); - mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt, - Period::fromNs(mIdealPeriod)); + const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); + mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); } return vsyncTime; } @@ -307,12 +312,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; const auto nextAnticipatedVsyncTime = nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); - if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + 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())); - mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint, - *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod)); + const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); + mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr, + renderRate); } return nextAnticipatedVsyncTime; } @@ -328,7 +334,8 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { std::lock_guard lock(mMutex); const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), + frameRate); return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor)); } @@ -344,7 +351,7 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return true; } - const nsecs_t period = mRateMap[mIdealPeriod].slope; + const nsecs_t period = mRateMap[idealPeriod()].slope; const nsecs_t justBeforeTimePoint = timePoint - period / 2; const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint); ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64, @@ -352,45 +359,50 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) { - ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), - to_string(displayModeData.renderRate).c_str(), - displayModeData.notifyExpectedPresentTimeoutOpt - ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str() - : "N/A"); +void VSyncPredictor::setRenderRate(Fps renderRate) { + ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str()); + ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str()); std::lock_guard lock(mMutex); - mDisplayModeDataOpt = displayModeData; + mRenderRateOpt = renderRate; } -VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { +void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { + LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(), + "mode does not belong to the display"); + ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str()); + const auto timeout = modePtr->getVrrConfig() + ? modePtr->getVrrConfig()->notifyExpectedPresentConfig + : std::nullopt; + ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), + to_string(*modePtr).c_str(), + timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); - const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); - return {model.slope, model.intercept}; -} -VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { - return mRateMap.find(mIdealPeriod)->second; -} + mDisplayModePtr = modePtr; + traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); -void VSyncPredictor::setPeriod(nsecs_t period) { - ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str()); - traceInt64("VSP-setPeriod", period); - - std::lock_guard lock(mMutex); static constexpr size_t kSizeLimit = 30; if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) { mRateMap.erase(mRateMap.begin()); } - // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData - mIdealPeriod = period; - if (mRateMap.find(period) == mRateMap.end()) { - mRateMap[mIdealPeriod] = {period, 0}; + if (mRateMap.find(idealPeriod()) == mRateMap.end()) { + mRateMap[idealPeriod()] = {idealPeriod(), 0}; } clearTimestamps(); } +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { + std::lock_guard lock(mMutex); + const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); + return {model.slope, model.intercept}; +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { + return mRateMap.find(idealPeriod())->second; +} + void VSyncPredictor::clearTimestamps() { if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); @@ -412,18 +424,18 @@ bool VSyncPredictor::needsMoreSamples() const { void VSyncPredictor::resetModel() { std::lock_guard lock(mMutex); - mRateMap[mIdealPeriod] = {mIdealPeriod, 0}; + mRateMap[idealPeriod()] = {idealPeriod(), 0}; clearTimestamps(); } void VSyncPredictor::dump(std::string& result) const { std::lock_guard lock(mMutex); - StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f); + StringAppendF(&result, "\tmDisplayModePtr=%s\n", to_string(*mDisplayModePtr).c_str()); StringAppendF(&result, "\tRefresh Rate Map:\n"); - for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) { + for (const auto& [period, periodInterceptTuple] : mRateMap) { StringAppendF(&result, "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n", - idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f, + period / 1e6f, periodInterceptTuple.slope / 1e6f, periodInterceptTuple.intercept); } } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index c271eb738e..fbc1e1665e 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -31,14 +31,14 @@ class VSyncPredictor : public VSyncTracker { public: /* * \param [in] PhysicalDisplayid The display this corresponds to. - * \param [in] idealPeriod The initial ideal period to use. + * \param [in] modePtr The initial display mode * \param [in] historySize The internal amount of entries to store in the model. * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ - VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize, + VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, IVsyncTrackerCallback&); ~VSyncPredictor(); @@ -48,15 +48,6 @@ public: nsecs_t currentPeriod() const final EXCLUDES(mMutex); void resetModel() final EXCLUDES(mMutex); - /* - * Inform the model that the period is anticipated to change to a new value. - * model will use the period parameter to predict vsync events until enough - * timestamps with the new period have been collected. - * - * \param [in] period The new period that should be used. - */ - void setPeriod(nsecs_t period) final EXCLUDES(mMutex); - /* Query if the model is in need of more samples to make a prediction. * \return True, if model would benefit from more samples, False if not. */ @@ -71,7 +62,9 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); - void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex); + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex); + + void setRenderRate(Fps) final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); @@ -96,6 +89,7 @@ private: int64_t seq; }; VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex); + nsecs_t idealPeriod() const REQUIRES(mMutex); bool const mTraceOn; size_t const kHistorySize; @@ -104,7 +98,6 @@ private: IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; - nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); // Map between ideal vsync period and the calculated model @@ -113,7 +106,8 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); - std::optional<DisplayModeData> mDisplayModeDataOpt 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); }; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 2938aa3fb3..24737e4fb2 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -116,32 +116,34 @@ void VSyncReactor::updateIgnorePresentFencesInternal() { } } -void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { +void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodConfirmationInProgress = true; - mPeriodTransitioningTo = newPeriod; + mModePtrTransitioningTo = modePtr.get(); mMoreSamplesNeeded = true; setIgnorePresentFencesInternal(true); } void VSyncReactor::endPeriodTransition() { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); - mPeriodTransitioningTo.reset(); + mModePtrTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); } -void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) { - ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period); +void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) { + ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), + modePtr->getVsyncRate().getPeriodNsecs()); std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) { + if (!mSupportKernelIdleTimer && + modePtr->getVsyncRate().getPeriodNsecs() == mTracker.currentPeriod() && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; } else { - startPeriodTransitionInternal(period); + startPeriodTransitionInternal(modePtr); } } @@ -159,14 +161,16 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return false; } - const bool periodIsChanging = - mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod()); + const std::optional<Period> newPeriod = mModePtrTransitioningTo + ? mModePtrTransitioningTo->getVsyncRate().getPeriod() + : std::optional<Period>{}; + const bool periodIsChanging = newPeriod && (newPeriod->ns() != mTracker.currentPeriod()); if (mSupportKernelIdleTimer && !periodIsChanging) { // Clear out the Composer-provided period and use the allowance logic below HwcVsyncPeriod = {}; } - auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod(); + auto const period = newPeriod ? newPeriod->ns() : mTracker.currentPeriod(); static constexpr int allowancePercent = 10; static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio; auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den; @@ -185,8 +189,8 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> std::lock_guard lock(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); - if (mPeriodTransitioningTo) { - mTracker.setPeriod(*mPeriodTransitioningTo); + if (mModePtrTransitioningTo) { + mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo)); *periodFlushed = true; } @@ -228,10 +232,11 @@ void VSyncReactor::dump(std::string& result) const { mInternalIgnoreFences, mExternalIgnoreFences); StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n", mMoreSamplesNeeded, mPeriodConfirmationInProgress); - if (mPeriodTransitioningTo) { - StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo); + if (mModePtrTransitioningTo) { + StringAppendF(&result, "mModePtrTransitioningTo=%s\n", + to_string(*mModePtrTransitioningTo).c_str()); } else { - StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n"); + StringAppendF(&result, "mModePtrTransitioningTo=nullptr\n"); } if (mLastHwVsync) { diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index f2302422ad..2415a66a64 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -27,6 +27,7 @@ #include <scheduler/TimeKeeper.h> +#include "VSyncTracker.h" #include "VsyncController.h" namespace android::scheduler { @@ -45,7 +46,7 @@ public: bool addPresentFence(std::shared_ptr<FenceTime>) final; void setIgnorePresentFences(bool ignore) final; - void startPeriodTransition(nsecs_t period, bool force) final; + void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) final; bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed) final; @@ -57,7 +58,7 @@ public: private: void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex); void updateIgnorePresentFencesInternal() REQUIRES(mMutex); - void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex); + void startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr>) REQUIRES(mMutex); void endPeriodTransition() REQUIRES(mMutex); bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod) REQUIRES(mMutex); @@ -74,7 +75,7 @@ private: bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false; bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false; - std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex); + DisplayModePtr mModePtrTransitioningTo GUARDED_BY(mMutex); std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex); hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 7eedc312e2..2ae5c0b5ce 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -20,25 +20,16 @@ #include <utils/Timers.h> #include <scheduler/Fps.h> +#include <scheduler/FrameRateMode.h> #include "VSyncDispatch.h" namespace android::scheduler { -struct DisplayModeData { - Fps renderRate; - std::optional<Period> notifyExpectedPresentTimeoutOpt; - - bool operator==(const DisplayModeData& other) const { - return isApproxEqual(renderRate, other.renderRate) && - notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt; - } -}; - struct IVsyncTrackerCallback { virtual ~IVsyncTrackerCallback() = default; - virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, - const DisplayModeData&, Period vsyncPeriod) = 0; + virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) = 0; }; /* @@ -77,13 +68,6 @@ public: */ virtual nsecs_t currentPeriod() const = 0; - /* - * Inform the tracker that the period is changing and the tracker needs to recalibrate itself. - * - * \param [in] period The period that the system is changing into. - */ - virtual void setPeriod(nsecs_t period) = 0; - /* Inform the tracker that the samples it has are not accurate for prediction. */ virtual void resetModel() = 0; @@ -98,20 +82,25 @@ public: virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; /* - * Sets the metadata about the currently active display mode such as VRR - * timeout period, vsyncPeriod and framework property such as render rate. - * If the render rate is not a divisor of the period, the render rate is - * ignored until the period changes. + * Sets the active mode of the display which includes the vsync period and other VRR attributes. + * This will inform the tracker that the period is changing and the tracker needs to recalibrate + * itself. + * + * \param [in] DisplayModePtr The display mode the tracker will use. + */ + virtual void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) = 0; + + /* + * Sets a render rate on the tracker. If the render rate is not a divisor + * of the period, the render rate is ignored until the period changes. * The tracker will continue to track the vsync timeline and expect it * to match the current period, however, nextAnticipatedVSyncTimeFrom will * return vsyncs according to the render rate set. Setting a render rate is useful * when a display is running at 120Hz but the render frame rate is 60Hz. - * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along - * the vsyncPeriod, render rate and timeoutNs. * - * \param [in] DisplayModeData The DisplayModeData the tracker will use. + * \param [in] Fps The render rate the tracker should operate at. */ - virtual void setDisplayModeData(const DisplayModeData&) = 0; + virtual void setRenderRate(Fps) = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 917789934a..807a7fba53 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -22,6 +22,7 @@ #include <DisplayHardware/HWComposer.h> #include <DisplayHardware/Hal.h> +#include <scheduler/FrameRateMode.h> #include <ui/FenceTime.h> #include <utils/Mutex.h> #include <utils/RefBase.h> @@ -59,13 +60,14 @@ public: bool* periodFlushed) = 0; /* - * Inform the controller that the period is changing and the controller needs to recalibrate - * itself. The controller will end the period transition internally. + * Inform the controller that the display mode is changing and the controller needs to + * recalibrate itself to the new vsync period. The controller will end the period transition + * internally. * - * \param [in] period The period that the system is changing into. + * \param [in] DisplayModePtr The new mode the display is changing to. * \param [in] force True to recalibrate even if period matches the existing period. */ - virtual void startPeriodTransition(nsecs_t period, bool force) = 0; + virtual void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) = 0; /* * Tells the tracker to stop using present fences to get a vsync signal. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 5fb53f9e20..4a8aac606b 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -16,6 +16,8 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/FlagManager.h> + #include <ftl/fake_guard.h> #include <scheduler/Fps.h> #include <scheduler/Timer.h> @@ -53,14 +55,14 @@ private: VSyncCallbackRegistration mRegistration; }; -VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features, +VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features, RequestHardwareVsync requestHardwareVsync, IVsyncTrackerCallback& callback) - : mId(id), + : mId(modePtr->getPhysicalDisplayId()), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(id, callback)), + mTracker(createTracker(modePtr, callback)), mDispatch(createDispatch(mTracker)), - mController(createController(id, *mTracker, features)), + mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) ? std::make_unique<PredictedVsyncTracer>(mDispatch) : nullptr) {} @@ -101,17 +103,15 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id, +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback& callback) { // TODO(b/144707443): Tune constants. - constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs(); constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; - return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize, - kMinSamplesForPrediction, kDiscardOutlierPercent, - callback); + return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, + kDiscardOutlierPercent, callback); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { @@ -140,9 +140,9 @@ VsyncSchedule::ControllerPtr VsyncSchedule::createController(PhysicalDisplayId i return reactor; } -void VsyncSchedule::startPeriodTransition(Period period, bool force) { +void VsyncSchedule::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) { std::lock_guard<std::mutex> lock(mHwVsyncLock); - mController->startPeriodTransition(period.ns(), force); + mController->onDisplayModeChanged(modePtr, force); enableHardwareVsyncLocked(); } diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index ca61f875c3..60c4105f51 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -57,21 +57,22 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&); + VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync, + IVsyncTrackerCallback&); ~VsyncSchedule(); // IVsyncSource overrides: Period period() const override; TimePoint vsyncDeadlineAfter(TimePoint) const override; - // Inform the schedule that the period is changing and the schedule needs to recalibrate - // itself. The schedule will end the period transition internally. This will - // enable hardware VSYNCs in order to calibrate. + // Inform the schedule that the display mode changed the schedule needs to recalibrate + // itself to the new vsync period. The schedule will end the period transition internally. + // This will enable hardware VSYNCs in order to calibrate. // - // \param [in] period The period that the system is changing into. + // \param [in] DisplayModePtr The mode that the display is changing to. // \param [in] force True to force a transition even if it is not a // change. - void startPeriodTransition(Period period, bool force); + void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force); // Pass a VSYNC sample to VsyncController. Return true if // VsyncController detected that the VSYNC period changed. Enable or disable @@ -125,7 +126,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&); + static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c15e74f107..2e70b22ee1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1202,7 +1202,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool // Start receiving vsync samples now, so that we can detect a period // switch. mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, - mode.modePtr->getVsyncRate()); + mode.modePtr.get()); // As we called to set period, we will call to onRefreshRateChangeCompleted once // VsyncController model is locked. @@ -1332,10 +1332,9 @@ void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { const auto desiredModeOpt = display->getDesiredMode(); const auto& modeOpt = desiredModeOpt->modeOpt; const auto displayId = modeOpt->modePtr->getPhysicalDisplayId(); - const auto vsyncRate = modeOpt->modePtr->getVsyncRate(); const auto renderFps = modeOpt->fps; dropModeRequest(display); - mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, vsyncRate); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, modeOpt->modePtr.get()); mScheduler->setRenderRate(displayId, renderFps); if (displayId == mActiveDisplayId) { @@ -4035,15 +4034,23 @@ void SurfaceFlinger::onChoreographerAttached() { } } -void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime, - const scheduler::DisplayModeData& displayModeData, - Period vsyncPeriod) { - const auto status = - getHwComposer() - .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, - displayModeData.renderRate, - displayModeData - .notifyExpectedPresentTimeoutOpt); +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 vrrConfig = modePtr->getVrrConfig(); + if (!vrrConfig) return std::nullopt; + + const auto notifyExpectedPresentConfig = + modePtr->getVrrConfig()->notifyExpectedPresentConfig; + if (!notifyExpectedPresentConfig) return std::nullopt; + 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); @@ -5765,7 +5772,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); - const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getVsyncRate(); + const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { // Turn on the display @@ -5802,7 +5809,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mScheduler->enableSyntheticVsync(false); constexpr bool kAllowToEnable = true; - mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, refreshRate); + mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get()); } mVisibleRegionsDirty = true; @@ -5844,7 +5851,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mVisibleRegionsDirty = true; scheduleRepaint(); mScheduler->enableSyntheticVsync(false); - mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, + activeMode.get()); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 75fd25a200..e90f8feb6e 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -658,8 +658,8 @@ private: void notifyCpuLoadUp() override; // IVsyncTrackerCallback overrides - void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, - const scheduler::DisplayModeData&, Period vsyncPeriod) override; + void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) override; // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 9b2d4536cd..4fc39cc912 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -806,8 +806,7 @@ private: void onChoreographerAttached() override {} // IVsyncTrackerCallback overrides - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override {} + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 8fcfd8131a..101f04fa73 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -28,6 +28,7 @@ #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" @@ -179,8 +180,7 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { } struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override {} + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} }; void SchedulerFuzzer::fuzzVSyncPredictor() { @@ -189,14 +189,14 @@ void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); VsyncTrackerCallback callback; - scheduler::VSyncPredictor tracker{kDisplayId, - idealPeriod, - historySize, - minimumSamplesForPrediction, + const auto mode = ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod))); + scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction, mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, callback}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); - tracker.setPeriod(period); + tracker.setDisplayModePtr(ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period)))); for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { if (!tracker.needsMoreSamples()) { break; @@ -271,7 +271,10 @@ void SchedulerFuzzer::fuzzVSyncReactor() { *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/, false); - reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool()); + const auto mode = ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), + Fps::fromPeriodNsecs(mFdp.ConsumeIntegral<nsecs_t>()))); + reactor.onDisplayModeChanged(mode, mFdp.ConsumeBool()); bool periodFlushed = false; // Value does not matter, since this is an out // param from addHwVsyncTimestamp. reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 728708f05c..165de1d7fa 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -90,8 +90,6 @@ public: nsecs_t currentPeriod() const override { return 1; } - void setPeriod(nsecs_t /* period */) override {} - void resetModel() override {} bool needsMoreSamples() const override { return true; } @@ -100,7 +98,7 @@ public: return true; } - void setDisplayModeData(const scheduler::DisplayModeData&) override {} + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) override {} nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { @@ -109,6 +107,8 @@ public: return (timePoint - (timePoint % mPeriod) + mPeriod); } + void setRenderRate(Fps) override {} + void dump(std::string& /* result */) const override {} protected: diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index cf3fab3aa3..31e13305a4 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -25,6 +25,11 @@ namespace android { namespace { +MATCHER_P(DisplayModeFps, value, "equals") { + using fps_approx_ops::operator==; + return arg->getVsyncRate() == value; +} + // Used when we simulate a display that supports doze. template <typename Display> struct DozeIsSupportedVariant { @@ -94,7 +99,8 @@ struct DispSyncIsSupportedVariant { static void setupResetModelCallExpectations(DisplayTransactionTest* test) { auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule(); EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()), - startPeriodTransition(DEFAULT_VSYNC_PERIOD, false)) + onDisplayModeChanged(DisplayModeFps(Fps::fromPeriodNsecs(DEFAULT_VSYNC_PERIOD)), + false)) .Times(1); EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel()) .Times(1); diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 4be07a1ddb..21888318df 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -50,11 +50,11 @@ public: nsecs_t currentPeriod() const final { return mPeriod; } - void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setDisplayModeData(const DisplayModeData&) final {} + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {} + void setRenderRate(Fps) final {} void dump(std::string&) const final {} private: @@ -88,11 +88,11 @@ public: return mPeriod; } - void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setDisplayModeData(const DisplayModeData&) final {} + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {} + void setRenderRate(Fps) final {} void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 83108662b9..b97ebba48e 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -33,6 +33,7 @@ #include "FlagUtils.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncTracker.h" +#include "mock/MockVSyncTracker.h" #include <com_android_graphics_surfaceflinger_flags.h> @@ -42,7 +43,7 @@ using namespace std::literals; namespace android::scheduler { using namespace com::android::graphics::surfaceflinger; -class MockVSyncTracker : public VSyncTracker { +class MockVSyncTracker : public mock::VSyncTracker { public: MockVSyncTracker(nsecs_t period) : mPeriod{period} { ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) @@ -52,16 +53,6 @@ public: .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod)); } - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); - nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { return timePoint; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 30a2855955..01f5af4fd1 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -25,6 +25,7 @@ #include "FlagUtils.h" #include "Scheduler/VSyncPredictor.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> @@ -39,12 +40,24 @@ using namespace testing; using namespace std::literals; using namespace com::android::graphics::surfaceflinger; +using NotifyExpectedPresentConfig = + ::aidl::android::hardware::graphics::composer3::VrrConfig::NotifyExpectedPresentConfig; + +using android::mock::createDisplayMode; +using android::mock::createVrrDisplayMode; + namespace android::scheduler { +namespace { MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") { return arg <= value + tolerance && arg >= value - tolerance; } +MATCHER_P(FpsMatcher, value, "equals") { + using fps_approx_ops::operator==; + return arg == value; +} + std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) { std::vector<nsecs_t> vsyncs(count); std::generate(vsyncs.begin(), vsyncs.end(), @@ -54,21 +67,27 @@ std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); +ftl::NonNull<DisplayModePtr> displayMode(nsecs_t period) { + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(period); + return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution, + DEFAULT_DISPLAY_ID)); +} +} // namespace + struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; + ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod); scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, - mPeriod, - kHistorySize, - kMinimumSamplesForPrediction, - kOutlierTolerancePercent, - mVsyncTrackerCallback}; + VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, + kOutlierTolerancePercent, mVsyncTrackerCallback}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -78,7 +97,7 @@ TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { EXPECT_THAT(model.intercept, Eq(0)); auto const changedPeriod = 2000; - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); model = tracker.getVSyncPredictionModel(); EXPECT_THAT(model.slope, Eq(changedPeriod)); EXPECT_THAT(model.intercept, Eq(0)); @@ -99,7 +118,7 @@ TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) { EXPECT_FALSE(tracker.needsMoreSamples()); auto const changedPeriod = mPeriod * 2; - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); EXPECT_TRUE(tracker.needsMoreSamples()); for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { @@ -133,7 +152,7 @@ TEST_F(VSyncPredictorTest, uponNotifiedOfInaccuracyUsesSynthetic) { } EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod)); - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod)); } @@ -179,7 +198,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) { auto constexpr expectedPeriod = 16639242; auto constexpr expectedIntercept = 1049341; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -198,7 +217,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_90hzLowVariance) { auto expectedPeriod = 11089413; auto expectedIntercept = 94421; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -225,7 +244,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelinesDiscontinuous_22hzLowVariance) auto expectedPeriod = 45450152; auto expectedIntercept = 469647; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -251,7 +270,7 @@ TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) { auto expectedPeriod = 1999892; auto expectedIntercept = 86342; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -271,7 +290,7 @@ TEST_F(VSyncPredictorTest, handlesVsyncChange) { auto const simulatedVsyncsSlow = generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase); - tracker.setPeriod(fastPeriod); + tracker.setDisplayModePtr(displayMode(fastPeriod)); for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } @@ -281,7 +300,7 @@ TEST_F(VSyncPredictorTest, handlesVsyncChange) { EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError)); EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); - tracker.setPeriod(slowPeriod); + tracker.setDisplayModePtr(displayMode(slowPeriod)); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } @@ -305,7 +324,7 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase); auto idealPeriod = 100000; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } @@ -313,14 +332,14 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { EXPECT_THAT(model.slope, Eq(fastPeriod)); EXPECT_THAT(model.intercept, Eq(0)); - tracker.setPeriod(slowPeriod); + tracker.setDisplayModePtr(displayMode(slowPeriod)); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } // we had a model for 100ns mPeriod before, use that until the new samples are // sufficiently built up - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); model = tracker.getVSyncPredictionModel(); EXPECT_THAT(model.slope, Eq(fastPeriod)); EXPECT_THAT(model.intercept, Eq(0)); @@ -369,7 +388,7 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { auto const expectedPeriod = 11113919; auto const expectedIntercept = -1195945; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -388,11 +407,8 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, - mPeriod, - 20, - kMinimumSamplesForPrediction, - kOutlierTolerancePercent, + const auto mode = displayMode(mPeriod); + VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent, mVsyncTrackerCallback}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, @@ -407,7 +423,7 @@ TEST_F(VSyncPredictorTest, hasEnoughPrecision) { auto const expectedPeriod = 16698426; auto const expectedIntercept = 58055; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -420,7 +436,7 @@ TEST_F(VSyncPredictorTest, hasEnoughPrecision) { TEST_F(VSyncPredictorTest, resetsWhenInstructed) { auto const idealPeriod = 10000; auto const realPeriod = 10500; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto i = 0; i < kMinimumSamplesForPrediction; i++) { tracker.addVsyncTimestamp(i * realPeriod); } @@ -562,7 +578,7 @@ TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) { auto constexpr expectedPeriod = 16'644'742; auto constexpr expectedIntercept = 125'626; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -580,7 +596,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)}); + tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -602,12 +618,12 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - tracker.setDisplayModeData({.renderRate = refreshRate / 4}); + 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.setDisplayModeData({.renderRate = refreshRate / 2}); + 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)); @@ -615,7 +631,7 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setDisplayModeData({.renderRate = refreshRate / 6}); + tracker.setRenderRate(refreshRate / 6); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); } @@ -629,7 +645,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { tracker.addVsyncTimestamp(mNow); } - tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)}); + tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -642,16 +658,27 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { SET_FLAG_FOR_TEST(flags::vrr_config, true); + const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - DisplayModeData displayModeData = - DisplayModeData{.renderRate = refreshRate, - .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; - tracker.setDisplayModeData(displayModeData); + NotifyExpectedPresentConfig notifyExpectedPresentConfig; + notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = Period::fromNs(30).ns(); + + hal::VrrConfig vrrConfig; + vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; + vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs(); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto mode = + ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup, + kResolution, DEFAULT_DISPLAY_ID)); + + tracker.setDisplayModePtr(mode); auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod), - displayModeData, Period::fromNs(mPeriod))) + onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode, + FpsMatcher(refreshRate))) .Times(1); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); mNow += mPeriod; @@ -659,14 +686,12 @@ TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { tracker.addVsyncTimestamp(mNow); } - displayModeData = DisplayModeData{.renderRate = refreshRate / 2, - .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; - tracker.setDisplayModeData(displayModeData); + tracker.setRenderRate(refreshRate / 2); { // out of render rate phase EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod), - displayModeData, Period::fromNs(mPeriod))) + onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode, + FpsMatcher(refreshRate / 2))) .Times(1); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index aca3ccca6d..8d9623de1c 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -31,6 +31,9 @@ #include <scheduler/TimeKeeper.h> +#include "mock/DisplayHardware/MockDisplayMode.h" +#include "mock/MockVSyncTracker.h" + #include "Scheduler/VSyncDispatch.h" #include "Scheduler/VSyncReactor.h" #include "Scheduler/VSyncTracker.h" @@ -40,20 +43,7 @@ using namespace std::literals; namespace android::scheduler { -class MockVSyncTracker : public VSyncTracker { -public: - MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); } - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); -}; - +namespace { class MockClock : public Clock { public: MOCK_CONST_METHOD0(now, nsecs_t()); @@ -93,18 +83,33 @@ std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); +ftl::NonNull<DisplayModePtr> displayMode(nsecs_t vsyncPeriod) { + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(vsyncPeriod); + return ftl::as_non_null(mock::createDisplayMode(DisplayModeId(0), refreshRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID)); +} + +MATCHER_P(DisplayModeMatcher, value, "display mode equals") { + return arg->getId() == value->getId() && equalsExceptDisplayModeId(*arg, *value); +} + +} // namespace + class VSyncReactorTest : public testing::Test { protected: VSyncReactorTest() - : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()), + : mMockTracker(std::make_shared<NiceMock<mock::VSyncTracker>>()), mMockClock(std::make_shared<NiceMock<MockClock>>()), mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit, false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); + ON_CALL(*mMockTracker, addVsyncTimestamp(_)).WillByDefault(Return(true)); } - std::shared_ptr<MockVSyncTracker> mMockTracker; + std::shared_ptr<mock::VSyncTracker> mMockTracker; std::shared_ptr<MockClock> mMockClock; static constexpr size_t kPendingLimit = 3; static constexpr nsecs_t mDummyTime = 47; @@ -194,7 +199,8 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -206,8 +212,8 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { nsecs_t const newPeriod = 5000; - EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod, false); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); @@ -217,7 +223,7 @@ TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); - EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(/*displayMode(newPeriod)*/ _)).Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); @@ -226,7 +232,7 @@ TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { nsecs_t sampleTime = 0; nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -234,7 +240,7 @@ TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(period, false); + mReactor.onDisplayModeChanged(displayMode(period), false); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -244,13 +250,13 @@ TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) { nsecs_t const secondPeriod = 5000; nsecs_t const thirdPeriod = 2000; - mReactor.startPeriodTransition(secondPeriod, false); + mReactor.onDisplayModeChanged(displayMode(secondPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(thirdPeriod, false); + mReactor.onDisplayModeChanged(displayMode(thirdPeriod), false); EXPECT_TRUE( mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -291,21 +297,22 @@ TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncP TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) { nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) { nsecs_t const newPeriod = 5000; - EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod, false); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); - EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(DisplayModeMatcher(displayMode(newPeriod)))) + .Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } @@ -323,7 +330,7 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); auto time = 0; auto constexpr numTimestampSubmissions = 10; @@ -348,7 +355,7 @@ TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); auto time = 0; // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps. @@ -365,7 +372,7 @@ TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsH auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); time += period; mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); @@ -381,7 +388,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); static auto constexpr numSamplesWithNewPeriod = 4; Sequence seq; @@ -408,7 +415,7 @@ TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -428,7 +435,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { nsecs_t const newPeriod1 = 4000; nsecs_t const newPeriod2 = 7000; - mReactor.startPeriodTransition(newPeriod1, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod1), false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -447,7 +454,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); - mReactor.startPeriodTransition(newPeriod2, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod2), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); @@ -460,7 +467,7 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -484,7 +491,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // First, set the same period, which should only be confirmed when we receive two // matching callbacks - idleReactor.startPeriodTransition(10000, false); + idleReactor.onDisplayModeChanged(displayMode(10000), false); EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period but incorrect timestamp delta @@ -497,7 +504,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Then, set a new period, which should be confirmed as soon as we receive a callback // reporting the new period nsecs_t const newPeriod = 5000; - idleReactor.startPeriodTransition(newPeriod, false); + idleReactor.onDisplayModeChanged(displayMode(newPeriod), false); // Incorrect timestamp delta and period EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp index a8a3cd0293..bfdd5963f3 100644 --- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp +++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp @@ -25,10 +25,12 @@ #include <scheduler/Fps.h> #include "Scheduler/VsyncSchedule.h" #include "ThreadContext.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +using android::mock::createDisplayMode; using testing::_; namespace android { @@ -157,35 +159,35 @@ TEST_F(VsyncScheduleTest, StartPeriodTransition) { // allowed. ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true)); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, false)); - mVsyncSchedule->startPeriodTransition(period, false); + mVsyncSchedule->onDisplayModeChanged(mode, false); } TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) { ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); mVsyncSchedule->enableHardwareVsync(); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, false)); - mVsyncSchedule->startPeriodTransition(period, false); + mVsyncSchedule->onDisplayModeChanged(mode, false); } TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) { ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true)); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, true)); - mVsyncSchedule->startPeriodTransition(period, true); + mVsyncSchedule->onDisplayModeChanged(mode, true); } TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h index cb05c00699..5bcce501e6 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h @@ -52,6 +52,7 @@ inline DisplayModePtr createVrrDisplayMode( .setVrrConfig(std::move(vrrConfig)) .build(); } + inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) { return DisplayMode::Builder(modePtr->getHwcId()) .setId(modePtr->getId()) diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 31eb86e4c5..ec22b70296 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -27,15 +27,15 @@ public: VSyncTracker(); ~VSyncTracker() override; - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); + MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override)); + MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t), (const, override)); + MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override)); + MOCK_METHOD(void, resetModel, (), (override)); + MOCK_METHOD(bool, needsMoreSamples, (), (const, 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, dump, (std::string&), (const, override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 69ec60acd4..f7433908b0 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -29,7 +29,7 @@ public: MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override)); MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override)); - MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override)); + MOCK_METHOD(void, onDisplayModeChanged, (ftl::NonNull<DisplayModePtr>, bool), (override)); MOCK_METHOD(void, setIgnorePresentFences, (bool), (override)); MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h index b8e24e0593..b48529f4ac 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h @@ -23,13 +23,10 @@ namespace android::scheduler::mock { struct VsyncTrackerCallback final : IVsyncTrackerCallback { - MOCK_METHOD(void, onVsyncGenerated, - (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period), - (override)); + MOCK_METHOD(void, onVsyncGenerated, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); }; struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override{}; + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{}; }; } // namespace android::scheduler::mock |