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

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

Merge changes Ibb632d4a,I95939670

* changes:
  SF: pass a render rate to VsyncTracker instead of a divisor
  SF: vsync divisors should use the same reference point
parents f0bd6f6c 894a6d48
Loading
Loading
Loading
Loading
+1 −5
Original line number Diff line number Diff line
@@ -418,11 +418,7 @@ void Scheduler::setRenderRate(Fps renderFrameRate) {
    ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
          to_string(mode.modePtr->getFps()).c_str());

    const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps);
    LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(),
                        to_string(mode.fps).c_str());

    mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor));
    mVsyncSchedule->getTracker().setRenderRate(renderFrameRate);
}

void Scheduler::resync() {
+42 −47
Original line number Diff line number Diff line
@@ -219,6 +219,17 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
    return true;
}

auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
    const auto vsync = nextAnticipatedVSyncTimeFromLocked(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::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
    auto const [slope, intercept] = getVSyncPredictionModelLocked();

@@ -258,12 +269,30 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
    std::lock_guard lock(mMutex);

    // TODO(b/246164114): This implementation is not efficient at all. Refactor.
    nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint);
    while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) {
        nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1);
    // update the mLastVsyncSequence for reference point
    mLastVsyncSequence = getVsyncSequenceLocked(timePoint);

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

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

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

        return divisor - mod;
    }();

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

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

/*
@@ -289,57 +318,22 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c
    ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint),
                  divisor);

    struct VsyncError {
        nsecs_t vsyncTimestamp;
        float error;

        bool operator<(const VsyncError& other) const { return error < other.error; }
    };

    if (divisor <= 1 || timePoint == 0) {
        return true;
    }

    const nsecs_t period = mRateMap[mIdealPeriod].slope;
    const nsecs_t justBeforeTimePoint = timePoint - period / 2;
    const nsecs_t dividedPeriod = mIdealPeriod / divisor;

    // If this is the first time we have asked about this divisor with the
    // current vsync period, it is considered in phase and we store the closest
    // vsync timestamp
    const auto knownTimestampIter = mRateDivisorKnownTimestampMap.find(dividedPeriod);
    if (knownTimestampIter == mRateDivisorKnownTimestampMap.end()) {
        const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
        mRateDivisorKnownTimestampMap[dividedPeriod] = vsync;
        ATRACE_FORMAT_INSTANT("(first) knownVsync in: %.2f", getTimePointIn(now, vsync));
        return true;
    }

    // Find the next N vsync timestamp where N is the divisor.
    // One of these vsyncs will be in phase. We return the one which is
    // the most aligned with the last known in phase vsync
    std::vector<VsyncError> vsyncs(static_cast<size_t>(divisor));
    const nsecs_t knownVsync = knownTimestampIter->second;
    nsecs_t point = justBeforeTimePoint;
    for (size_t i = 0; i < divisor; i++) {
        const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
        const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divisor);
        const auto error = std::abs(std::round(numPeriods) - numPeriods);
        vsyncs[i] = {vsync, error};
        point = vsync + 1;
    }

    const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
    mRateDivisorKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
    ATRACE_FORMAT_INSTANT("knownVsync in: %.2f",
                          getTimePointIn(now, minVsyncError->vsyncTimestamp));
    return std::abs(minVsyncError->vsyncTimestamp - 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::setDivisor(unsigned divisor) {
    ALOGV("%s: %d", __func__, divisor);
void VSyncPredictor::setRenderRate(Fps fps) {
    ALOGV("%s: %s", __func__, to_string(fps).c_str());
    std::lock_guard lock(mMutex);
    mDivisor = divisor;
    mRenderRate = fps;
}

VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -382,6 +376,7 @@ void VSyncPredictor::clearTimestamps() {
        mTimestamps.clear();
        mLastTimestampIndex = 0;
    }
    mLastVsyncSequence.reset();
}

bool VSyncPredictor::needsMoreSamples() const {
+16 −14
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ public:

    bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);

    void setDivisor(unsigned divisor) final EXCLUDES(mMutex);
    void setRenderRate(Fps) final EXCLUDES(mMutex);

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

@@ -78,35 +78,37 @@ private:

    inline void traceInt64If(const char* name, int64_t value) const;
    inline void traceInt64(const char* name, int64_t value) const;
    bool const mTraceOn;

    size_t const kHistorySize;
    size_t const kMinimumSamplesForPrediction;
    size_t const kOutlierTolerancePercent;

    std::mutex mutable mMutex;
    size_t next(size_t i) const REQUIRES(mMutex);
    bool validate(nsecs_t timestamp) const REQUIRES(mMutex);

    Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);

    nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);

    bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);

    struct VsyncSequence {
        nsecs_t vsyncTime;
        int64_t seq;
    };
    VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);

    bool const mTraceOn;
    size_t const kHistorySize;
    size_t const kMinimumSamplesForPrediction;
    size_t const kOutlierTolerancePercent;
    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
    std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);

    // Map between the divided vsync period and the last known vsync timestamp
    std::unordered_map<nsecs_t, nsecs_t> mutable mRateDivisorKnownTimestampMap GUARDED_BY(mMutex);

    size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
    std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);

    unsigned mDivisor GUARDED_BY(mMutex) = 1;
    std::optional<Fps> mRenderRate GUARDED_BY(mMutex);

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

} // namespace android::scheduler
+5 −4
Original line number Diff line number Diff line
@@ -80,15 +80,16 @@ public:
    virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;

    /*
     * Sets a divisor on the rate (which is a multiplier of the period).
     * 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 divisor set. Setting a divisor is useful
     * 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.
     *
     * \param [in] divisor   The rate divisor the tracker should operate at.
     * \param [in] Fps   The render rate the tracker should operate at.
     */
    virtual void setDivisor(unsigned divisor) = 0;
    virtual void setRenderRate(Fps) = 0;

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

+1 −1
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ public:
        return true;
    }

    void setDivisor(unsigned) override {}
    void setRenderRate(Fps) override {}

    nsecs_t nextVSyncTime(nsecs_t timePoint) const {
        if (timePoint % mPeriod == 0) {
Loading