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

Commit d26c9194 authored by Ady Abraham's avatar Ady Abraham Committed by Android (Google) Code Review
Browse files

Merge "Revert "SF: Introduce VsyncTimeline to VsyncPredictor"" into main

parents 8bf4487a 9633f8e8
Loading
Loading
Loading
Loading
+20 −51
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@

#include <android-base/stringprintf.h>
#include <ftl/concat.h>
#include <gui/TraceUtils.h>
#include <utils/Trace.h>
#include <log/log_main.h>

#include <scheduler/TimeKeeper.h>
@@ -44,17 +44,6 @@ ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
            TimePoint::fromNs(nextVsyncTime)};
}

void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
    if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
        return;
    }

    ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
                      ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
                      ns2us(*entry.targetVsync() - now), "us");
    ATRACE_FORMAT_INSTANT(trace.c_str());
}

} // namespace

VSyncDispatch::~VSyncDispatch() = default;
@@ -98,7 +87,6 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {

ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
                                                      VSyncTracker& tracker, nsecs_t now) {
    ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
    auto nextVsyncTime =
            tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
                                                          now + timing.workDuration +
@@ -110,8 +98,6 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim
            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
    bool const wouldSkipAWakeup =
            mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
    ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
                          wouldSkipAVsyncTarget, wouldSkipAWakeup);
    if (FlagManager::getInstance().dont_skip_on_early_ro()) {
        if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
            nextVsyncTime = mArmedInfo->mActualVsyncTime;
@@ -136,7 +122,7 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim
ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
        VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
    mWorkloadUpdateInfo = timing;
    const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo);
    const auto armedInfo = update(tracker, now, timing, mArmedInfo);
    return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
            TimePoint::fromNs(armedInfo.mActualVsyncTime)};
}
@@ -154,13 +140,11 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
    bool const nextVsyncTooClose = mLastDispatchTime &&
            (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
    if (alreadyDispatchedForVsync) {
        ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
        return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
                                                    *mLastDispatchTime);
    }

    if (nextVsyncTooClose) {
        ATRACE_FORMAT_INSTANT("nextVsyncTooClose");
        return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
                                                    *mLastDispatchTime + currentPeriod);
    }
@@ -168,11 +152,9 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
    return nextVsyncTime;
}

auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now,
auto VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now,
                                          VSyncDispatch::ScheduleTiming timing,
                                                std::optional<ArmingInfo> armedInfo) const
        -> ArmingInfo {
    ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
                                          std::optional<ArmingInfo> armedInfo) const -> ArmingInfo {
    const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
    const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);

@@ -183,39 +165,29 @@ auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t n
    const auto nextReadyTime = nextVsyncTime - timing.readyDuration;
    const auto nextWakeupTime = nextReadyTime - timing.workDuration;

    if (FlagManager::getInstance().dont_skip_on_early_ro()) {
    bool const wouldSkipAVsyncTarget =
            armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
    bool const wouldSkipAWakeup =
            armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
        ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
                              wouldSkipAVsyncTarget, wouldSkipAWakeup);
        if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
    if (FlagManager::getInstance().dont_skip_on_early_ro() &&
        (wouldSkipAVsyncTarget || wouldSkipAWakeup)) {
        return *armedInfo;
    }
    }

    return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime};
}

void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
    ATRACE_NAME("VSyncDispatchTimerQueueEntry::update");
    if (!mArmedInfo && !mWorkloadUpdateInfo) {
        return;
    }

    if (mWorkloadUpdateInfo) {
        const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
        const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
        const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
        ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
                              " lastVsyncDelta=%" PRId64,
                              workDelta, readyDelta, lastVsyncDelta);
        mScheduleTiming = *mWorkloadUpdateInfo;
        mWorkloadUpdateInfo.reset();
    }

    mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo);
    mArmedInfo = update(tracker, now, mScheduleTiming, mArmedInfo);
}

void VSyncDispatchTimerQueueEntry::disarm() {
@@ -310,7 +282,6 @@ void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {

void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
    ATRACE_CALL();
    std::optional<nsecs_t> min;
    std::optional<nsecs_t> targetVsync;
    std::optional<std::string_view> nextWakeupName;
@@ -323,10 +294,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        if (it != skipUpdateIt) {
            callback->update(*mTracker, now);
        }

        traceEntry(*callback, now);

        const auto wakeupTime = *callback->wakeupTime();
        auto const wakeupTime = *callback->wakeupTime();
        if (!min || *min > wakeupTime) {
            nextWakeupName = callback->name();
            min = wakeupTime;
@@ -335,6 +303,11 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
    }

    if (min && min < mIntendedWakeupTime) {
        if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
            ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
                              "us; VSYNC in ", ns2us(*targetVsync - now), "us");
            ATRACE_NAME(trace.c_str());
        }
        setTimer(*min, now);
    } else {
        ATRACE_NAME("cancel timer");
@@ -343,7 +316,6 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
}

void VSyncDispatchTimerQueue::timerCallback() {
    ATRACE_CALL();
    struct Invocation {
        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
        nsecs_t vsyncTimestamp;
@@ -366,9 +338,8 @@ void VSyncDispatchTimerQueue::timerCallback() {
                continue;
            }

            traceEntry(*callback, now);

            auto const readyTime = callback->readyTime();

            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                callback->executing();
@@ -382,8 +353,6 @@ void VSyncDispatchTimerQueue::timerCallback() {
    }

    for (auto const& invocation : invocations) {
        ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
        ATRACE_FORMAT("%s: %s", __func__, trace.c_str());
        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
                                      invocation.deadlineTimestamp);
    }
+2 −2
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ private:
    };

    nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const;
    ArmingInfo getArmedInfo(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
    ArmingInfo update(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
                      std::optional<ArmingInfo>) const;

    const std::string mName;
+84 −214
Original line number Diff line number Diff line
@@ -45,35 +45,11 @@ using base::StringAppendF;

static auto constexpr kMaxPercent = 100u;

namespace {
nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime,
                      std::optional<nsecs_t> lastVsyncOpt) {
    const auto threshold = model.slope / 2;

    if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
        const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
        if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) {
            const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
            ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from prev. "
                                  "adjust by %.2f",
                                  static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
                                  static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
                                  static_cast<float>(vsyncFixup) / 1e6f);
            return vsyncFixup;
        }
    }

    return 0;
}
} // namespace

VSyncPredictor::~VSyncPredictor() = default;

VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
                               size_t historySize, size_t minimumSamplesForPrediction,
                               uint32_t outlierTolerancePercent)
      : mClock(std::move(clock)),
        mId(modePtr->getPhysicalDisplayId()),
VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
                               size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
      : mId(modePtr->getPhysicalDisplayId()),
        mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
        kHistorySize(historySize),
        kMinimumSamplesForPrediction(minimumSamplesForPrediction),
@@ -171,7 +147,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
            mKnownTimestamp = timestamp;
        }
        ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
                              (mClock->now() - *mKnownTimestamp) / 1e6f);
            (systemTime() - *mKnownTimestamp) / 1e6f);
        return false;
    }

@@ -274,6 +250,17 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
    return true;
}

auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
    const auto vsync = snapToVsync(timestamp);
    if (!mLastVsyncSequence) return {vsync, 0};

    const auto [slope, _] = getVSyncPredictionModelLocked();
    const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
    const auto vsyncSequence = lastVsyncSequence +
            static_cast<int64_t>(std::round((vsync - lastVsyncTime) / static_cast<float>(slope)));
    return {vsync, vsyncSequence};
}

nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
    auto const [slope, intercept] = getVSyncPredictionModelLocked();

@@ -311,32 +298,51 @@ nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
}

nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
                                                     std::optional<nsecs_t> lastVsyncOpt) {
                                                     std::optional<nsecs_t> lastVsyncOpt) const {
    ATRACE_CALL();
    std::lock_guard lock(mMutex);
    const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
    const auto threshold = currentPeriod / 2;
    const auto minFramePeriod = minFramePeriodLocked().ns();
    const auto lastFrameMissed =
            lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold;
    const nsecs_t baseTime =
            FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt
            ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
            : timePoint;
    return snapToVsyncAlignedWithRenderRate(baseTime);
}

    const auto now = TimePoint::fromNs(mClock->now());
    purgeTimelines(now);
nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
    // update the mLastVsyncSequence for reference point
    mLastVsyncSequence = getVsyncSequenceLocked(timePoint);

    std::optional<TimePoint> vsyncOpt;
    for (auto& timeline : mTimelines) {
        vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(getVSyncPredictionModelLocked(),
                                                         minFramePeriodLocked(),
                                                         snapToVsync(timePoint), mMissedVsync,
                                                         lastVsyncOpt);
        if (vsyncOpt) {
            break;
        }
    const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
        if (!mRenderRateOpt) return 0;
        const auto divisor =
                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
                                                         *mRenderRateOpt);
        if (divisor <= 1) return 0;

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

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

    if (*vsyncOpt > mLastCommittedVsync) {
        mLastCommittedVsync = *vsyncOpt;
        ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
                              float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
        return divisor - mod;
    }();

    if (renderRatePhase == 0) {
        return mLastVsyncSequence->vsyncTime;
    }

    return vsyncOpt->ns();
    auto const [slope, intercept] = getVSyncPredictionModelLocked();
    const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
    return snapToVsync(approximateNextVsync - slope / 2);
}

/*
@@ -347,28 +353,32 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
 * isVSyncInPhase(33.3, 30) = false
 * isVSyncInPhase(50.0, 30) = true
 */
bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) {
    if (timePoint == 0) {
        return true;
    }

bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
    std::lock_guard lock(mMutex);
    const auto model = getVSyncPredictionModelLocked();
    const nsecs_t period = model.slope;
    const nsecs_t justBeforeTimePoint = timePoint - period / 2;
    const auto now = TimePoint::fromNs(mClock->now());
    const auto vsync = snapToVsync(justBeforeTimePoint);
    const auto divisor =
            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
                                                     frameRate);
    return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
}

    purgeTimelines(now);
bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
    const TimePoint now = TimePoint::now();
    const auto getTimePointIn = [](TimePoint now, nsecs_t timePoint) -> float {
        return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
    };
    ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint),
                  divisor);

    for (auto& timeline : mTimelines) {
        if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
            return timeline.isVSyncInPhase(model, vsync, frameRate);
        }
    if (divisor <= 1 || timePoint == 0) {
        return true;
    }

    // The last timeline should always be valid
    return mTimelines.back().isVSyncInPhase(model, vsync, frameRate);
    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,
                          getTimePointIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq);
    return vsyncSequence.seq % divisor == 0;
}

void VSyncPredictor::setRenderRate(Fps renderRate) {
@@ -376,9 +386,6 @@ void VSyncPredictor::setRenderRate(Fps renderRate) {
    ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
    std::lock_guard lock(mMutex);
    mRenderRateOpt = renderRate;
    mTimelines.back().freeze(TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
    mTimelines.emplace_back(mIdealPeriod, renderRate);
    purgeTimelines(TimePoint::fromNs(mClock->now()));
}

void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
@@ -408,9 +415,8 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
    clearTimestamps();
}

Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
                                                  TimePoint lastConfirmedPresentTime) {
    ATRACE_CALL();
    const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
    const auto threshold = currentPeriod / 2;
    const auto minFramePeriod = minFramePeriodLocked().ns();
@@ -436,20 +442,17 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT
    if (!mPastExpectedPresentTimes.empty()) {
        const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
        if (phase > 0ns) {
            for (auto& timeline : mTimelines) {
                timeline.shiftVsyncSequence(phase);
            if (mLastVsyncSequence) {
                mLastVsyncSequence->vsyncTime += phase.ns();
            }
            mPastExpectedPresentTimes.clear();
            return phase;
        }
    }

    return 0ns;
}

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

    if (!mDisplayModePtr->getVrrConfig()) return;
@@ -479,14 +482,11 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
        }
    }

    const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
    if (phase > 0ns) {
        mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
    }
    ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
}

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

    std::lock_guard lock(mMutex);
    if (!mDisplayModePtr->getVrrConfig()) return;
@@ -496,15 +496,14 @@ void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
    const auto lastConfirmedPresentTime =
            TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);

    const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
    if (phase > 0ns) {
        mMissedVsync = {expectedPresentTime, Duration::fromNs(0)};
    }
    ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
    mLastMissedVsync = expectedPresentTime;
}

VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
    std::lock_guard lock(mMutex);
    return VSyncPredictor::getVSyncPredictionModelLocked();
    const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
    return {model.slope, model.intercept};
}

VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
@@ -525,11 +524,6 @@ void VSyncPredictor::clearTimestamps() {
        mTimestamps.clear();
        mLastTimestampIndex = 0;
    }

    mTimelines.clear();
    mLastCommittedVsync = TimePoint::fromNs(0);
    mIdealPeriod = Period::fromNs(idealPeriod());
    mTimelines.emplace_back(mIdealPeriod, mRenderRateOpt);
}

bool VSyncPredictor::needsMoreSamples() const {
@@ -553,130 +547,6 @@ void VSyncPredictor::dump(std::string& result) const {
                      period / 1e6f, periodInterceptTuple.slope / 1e6f,
                      periodInterceptTuple.intercept);
    }
    StringAppendF(&result, "\tmTimelines.size()=%zu\n", mTimelines.size());
}

void VSyncPredictor::purgeTimelines(android::TimePoint now) {
    while (mTimelines.size() > 1) {
        const auto validUntilOpt = mTimelines.front().validUntil();
        if (validUntilOpt && *validUntilOpt < now) {
            mTimelines.pop_front();
        } else {
            break;
        }
    }
    LOG_ALWAYS_FATAL_IF(mTimelines.empty());
    LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
}

VSyncPredictor::VsyncTimeline::VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt)
      : mIdealPeriod(idealPeriod), mRenderRateOpt(renderRateOpt) {}

void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
    LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
    ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
                          mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
                          float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
    mValidUntil = lastVsync;
}

std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
        Model model, Period minFramePeriod, nsecs_t vsync, MissedVsync missedVsync,
        std::optional<nsecs_t> lastVsyncOpt) {
    ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");

    const auto threshold = model.slope / 2;
    const auto lastFrameMissed =
            lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
    nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
    nsecs_t vsyncFixupTime = 0;
    if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
        vsyncTime += missedVsync.fixup.ns();
        ATRACE_FORMAT_INSTANT("lastFrameMissed");
    } else {
        vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt);
        vsyncTime += vsyncFixupTime;
    }

    ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
    if (mValidUntil && vsyncTime > mValidUntil->ns()) {
        ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
                              static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
        return std::nullopt;
    }

    if (vsyncFixupTime > 0) {
        shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
    }

    return TimePoint::fromNs(vsyncTime);
}

auto VSyncPredictor::VsyncTimeline::getVsyncSequenceLocked(Model model, nsecs_t vsync)
        -> VsyncSequence {
    if (!mLastVsyncSequence) return {vsync, 0};

    const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
    const auto vsyncSequence = lastVsyncSequence +
            static_cast<int64_t>(std::round((vsync - lastVsyncTime) /
                                            static_cast<float>(model.slope)));
    return {vsync, vsyncSequence};
}

nsecs_t VSyncPredictor::VsyncTimeline::snapToVsyncAlignedWithRenderRate(Model model,
                                                                        nsecs_t vsync) {
    // update the mLastVsyncSequence for reference point
    mLastVsyncSequence = getVsyncSequenceLocked(model, vsync);

    const auto renderRatePhase = [&]() -> int {
        if (!mRenderRateOpt) return 0;
        const auto divisor =
                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod.ns()),
                                                         *mRenderRateOpt);
        if (divisor <= 1) return 0;

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

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

        return divisor - mod;
    }();

    if (renderRatePhase == 0) {
        return mLastVsyncSequence->vsyncTime;
    }

    return mLastVsyncSequence->vsyncTime + model.slope * renderRatePhase;
}

bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, Fps frameRate) {
    const auto getVsyncIn = [](TimePoint now, nsecs_t timePoint) -> float {
        return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
    };

    Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns());
    const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
    const auto now = TimePoint::now();

    if (divisor <= 1) {
        return true;
    }
    const auto vsyncSequence = getVsyncSequenceLocked(model, vsync);
    ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
                          getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
    return vsyncSequence.seq % divisor == 0;
}

void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
    if (mLastVsyncSequence) {
        ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
        mLastVsyncSequence->vsyncTime += phase.ns();
    }
}

} // namespace android::scheduler
+15 −45

File changed.

Preview size limit exceeded, changes collapsed.

+3 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading