Loading services/surfaceflinger/Scheduler/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ cc_defaults { "libui", "libutils", ], static_libs: ["libsurfaceflinger_common"], } cc_library_headers { Loading services/surfaceflinger/Scheduler/Scheduler.cpp +9 −21 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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()); Loading @@ -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); } } } Loading Loading @@ -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() { Loading Loading @@ -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; } Loading services/surfaceflinger/Scheduler/Scheduler.h +4 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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; Loading services/surfaceflinger/Scheduler/VSyncPredictor.cpp +64 −52 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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; Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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()); Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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; } Loading @@ -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)); } Loading @@ -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, Loading @@ -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; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { mRenderRateOpt = renderRate; } 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; } void VSyncPredictor::setPeriod(nsecs_t period) { ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str()); traceInt64("VSP-setPeriod", period); mDisplayModePtr = modePtr; traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); 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()); Loading @@ -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); } } Loading services/surfaceflinger/Scheduler/VSyncPredictor.h +8 −14 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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. */ Loading @@ -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); Loading @@ -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; Loading @@ -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 Loading @@ -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); }; Loading Loading
services/surfaceflinger/Scheduler/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ cc_defaults { "libui", "libutils", ], static_libs: ["libsurfaceflinger_common"], } cc_library_headers { Loading
services/surfaceflinger/Scheduler/Scheduler.cpp +9 −21 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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()); Loading @@ -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); } } } Loading Loading @@ -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() { Loading Loading @@ -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; } Loading
services/surfaceflinger/Scheduler/Scheduler.h +4 −8 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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; Loading
services/surfaceflinger/Scheduler/VSyncPredictor.cpp +64 −52 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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; Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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()); Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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; } Loading @@ -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)); } Loading @@ -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, Loading @@ -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; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { mRenderRateOpt = renderRate; } 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; } void VSyncPredictor::setPeriod(nsecs_t period) { ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str()); traceInt64("VSP-setPeriod", period); mDisplayModePtr = modePtr; traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); 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()); Loading @@ -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); } } Loading
services/surfaceflinger/Scheduler/VSyncPredictor.h +8 −14 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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. */ Loading @@ -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); Loading @@ -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; Loading @@ -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 Loading @@ -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); }; Loading