Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9883030 authored by Ady Abraham's avatar Ady Abraham
Browse files

SF: recover from sub-frame jank V2

Respect the VrrConfig::minFrameInterval and adjust the vsync timeline
when a frame miss causes a violation of the minFrameInterval
with the next frame scheduled frame(s).

Bug: 296635687
Test: presubmit
Test: adb root && adb shell service call SurfaceFlinger 1045 f 0.9
Change-Id: Ice2128e291ca4890c7be3b24b9938e6faa383a82
parent c94bffc7
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -223,6 +223,19 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
        mPacesetterFrameDurationFractionToSkip = 0.f;
    }

    if (FlagManager::getInstance().vrr_config()) {
        const auto minFramePeriod = pacesetterOpt->get().schedulePtr->minFramePeriod();
        const auto presentFenceForPastVsync =
                pacesetterTargeter.target().presentFenceForPastVsync(minFramePeriod);
        const auto lastConfirmedPresentTime = presentFenceForPastVsync->getSignalTime();
        if (lastConfirmedPresentTime != Fence::SIGNAL_TIME_PENDING &&
            lastConfirmedPresentTime != Fence::SIGNAL_TIME_INVALID) {
            pacesetterOpt->get()
                    .schedulePtr->getTracker()
                    .onFrameBegin(expectedVsyncTime, TimePoint::fromNs(lastConfirmedPresentTime));
        }
    }

    const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
    compositor.sample();

+100 −2
Original line number Diff line number Diff line
@@ -114,6 +114,10 @@ Period VSyncPredictor::minFramePeriod() const {
    }

    std::lock_guard lock(mMutex);
    return minFramePeriodLocked();
}

Period VSyncPredictor::minFramePeriodLocked() const {
    const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs();
    const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
                                                        static_cast<float>(idealPeriod())));
@@ -297,15 +301,20 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {

    const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
        if (!mRenderRateOpt) return 0;

        const auto divisor =
                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
                                                         *mRenderRateOpt);
        if (divisor <= 1) return 0;

        const int mod = mLastVsyncSequence->seq % divisor;
        int mod = mLastVsyncSequence->seq % divisor;
        if (mod == 0) return 0;

        // This is actually a bug fix, but guarded with vrr_config since we found it with this
        // config
        if (FlagManager::getInstance().vrr_config()) {
            if (mod < 0) mod += divisor;
        }

        return divisor - mod;
    }();

@@ -406,6 +415,95 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
    clearTimestamps();
}

void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
                                                  TimePoint lastConfirmedPresentTime) {
    const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
    const auto threshold = currentPeriod / 2;
    const auto minFramePeriod = minFramePeriodLocked().ns();

    auto prev = lastConfirmedPresentTime.ns();
    for (auto& current : mPastExpectedPresentTimes) {
        if (CC_UNLIKELY(mTraceOn)) {
            ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
                                  static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) /
                                          1e6f);
        }

        const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
        if (minPeriodViolation) {
            ATRACE_NAME("minPeriodViolation");
            current = TimePoint::fromNs(prev + minFramePeriod);
            prev = current.ns();
        } else {
            break;
        }
    }

    if (!mPastExpectedPresentTimes.empty()) {
        const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
        if (phase > 0ns) {
            if (mLastVsyncSequence) {
                mLastVsyncSequence->vsyncTime += phase.ns();
            }
        }
    }
}

void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
                                  TimePoint lastConfirmedPresentTime) {
    ATRACE_CALL();
    std::lock_guard lock(mMutex);

    if (!mDisplayModePtr->getVrrConfig()) return;

    if (CC_UNLIKELY(mTraceOn)) {
        ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
                              static_cast<float>(expectedPresentTime.ns() -
                                                 lastConfirmedPresentTime.ns()) /
                                      1e6f);
    }
    mPastExpectedPresentTimes.push_back(expectedPresentTime);

    const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
    const auto threshold = currentPeriod / 2;

    const auto minFramePeriod = minFramePeriodLocked().ns();
    while (!mPastExpectedPresentTimes.empty()) {
        const auto front = mPastExpectedPresentTimes.front().ns();
        const bool frontIsLastConfirmed =
                std::abs(front - lastConfirmedPresentTime.ns()) < threshold;
        const bool frontIsBeforeConfirmed =
                front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold;
        if (frontIsLastConfirmed || frontIsBeforeConfirmed) {
            if (CC_UNLIKELY(mTraceOn)) {
                ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
                                      static_cast<float>(lastConfirmedPresentTime.ns() -
                                                         mPastExpectedPresentTimes.front().ns()) /
                                              1e6f);
            }
            mPastExpectedPresentTimes.pop_front();
        } else {
            break;
        }
    }

    ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
}

void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
    ATRACE_CALL();

    std::lock_guard lock(mMutex);
    if (!mDisplayModePtr->getVrrConfig()) return;

    // We don't know when the frame is going to be presented, so we assume it missed one vsync
    const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
    const auto lastConfirmedPresentTime =
            TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);

    ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
}

VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
    std::lock_guard lock(mMutex);
    const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+9 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#pragma once

#include <deque>
#include <mutex>
#include <unordered_map>
#include <vector>
@@ -67,6 +68,10 @@ public:

    void setRenderRate(Fps) final EXCLUDES(mMutex);

    void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
            EXCLUDES(mMutex);
    void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex);

    void dump(std::string& result) const final EXCLUDES(mMutex);

private:
@@ -84,6 +89,8 @@ private:
    Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
    nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
    bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
    Period minFramePeriodLocked() const REQUIRES(mMutex);
    void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);

    struct VsyncSequence {
        nsecs_t vsyncTime;
@@ -111,6 +118,8 @@ private:
    std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);

    mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);

    std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
};

} // namespace android::scheduler
+5 −0
Original line number Diff line number Diff line
@@ -107,6 +107,11 @@ public:
     */
    virtual void setRenderRate(Fps) = 0;

    virtual void onFrameBegin(TimePoint expectedPresentTime,
                              TimePoint lastConfirmedPresentTime) = 0;

    virtual void onFrameMissed(TimePoint expectedPresentTime) = 0;

    virtual void dump(std::string& result) const = 0;

protected:
+4 −0
Original line number Diff line number Diff line
@@ -2476,6 +2476,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,

    if (pacesetterFrameTarget.isFramePending()) {
        if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
            if (FlagManager::getInstance().vrr_config()) {
                mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
                        pacesetterFrameTarget.expectedPresentTime());
            }
            scheduleCommit(FrameHint::kNone);
            return false;
        }
Loading