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

Commit 32efd54d authored by Ady Abraham's avatar Ady Abraham
Browse files

SurfaceFlinger: frame rate heuristic during config change

Some devices might takes a few frames to change the refresh rate.
Calculating the frame rate should ignore frames sent while changing
the refresh rate as those might result in an inaccurate frame rate
due to frame misses.

Bug: 156530990
Test: Running Hay Day and observing refresh rate.
Change-Id: I99f364cd1816c3ce2ba3c3145331eb618dcaea71
parent c30ebd01
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -52,6 +52,8 @@ public:
    // Sets the display size. Client is responsible for synchronization.
    virtual void setDisplayArea(uint32_t displayArea) = 0;

    virtual void setConfigChangePending(bool pending) = 0;

    // Marks the layer as active, and records the given state to its history.
    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;

@@ -78,6 +80,8 @@ public:

    void setDisplayArea(uint32_t /*displayArea*/) override {}

    void setConfigChangePending(bool /*pending*/) override {}

    // Marks the layer as active, and records the given state to its history.
    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;

@@ -134,6 +138,8 @@ public:
    // Sets the display size. Client is responsible for synchronization.
    void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }

    void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }

    // Marks the layer as active, and records the given state to its history.
    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;

@@ -178,6 +184,9 @@ private:

    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
    const bool mUseFrameRatePriority;

    // Whether a config change is in progress or not
    std::atomic<bool> mConfigChangePending = false;
};

} // namespace impl
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);

    const auto& info = it->second;
    info->setLastPresentTime(presentTime, now);
    info->setLastPresentTime(presentTime, now, mConfigChangePending);

    // Activate layer if inactive.
    if (const auto end = activeLayers().end(); it >= end) {
+46 −22
Original line number Diff line number Diff line
@@ -34,12 +34,15 @@ LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
        mDefaultVote(defaultVote),
        mLayerVote({defaultVote, 0.0f}) {}

void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
                                     bool pendingConfigChange) {
    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));

    mLastUpdatedTime = std::max(lastPresentTime, now);

    FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
    FrameTimeData frameTime = {.presetTime = lastPresentTime,
                               .queueTime = mLastUpdatedTime,
                               .pendingConfigChange = pendingConfigChange};

    mFrameTimes.push_back(frameTime);
    if (mFrameTimes.size() > HISTORY_SIZE) {
@@ -80,21 +83,20 @@ bool LayerInfoV2::hasEnoughDataForHeuristic() const {
    return true;
}

std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
    static constexpr float MARGIN = 1.0f; // 1Hz

    if (!hasEnoughDataForHeuristic()) {
        ALOGV("Not enough data");
        return std::nullopt;
    }

    // Calculate the refresh rate by finding the average delta between frames
std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
    nsecs_t totalPresentTimeDeltas = 0;
    nsecs_t totalQueueTimeDeltas = 0;
    auto missingPresentTime = false;
    bool missingPresentTime = false;
    int numFrames = 0;
    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
        // Ignore frames captured during a config change
        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
            continue;
        }

        totalQueueTimeDeltas +=
                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
        numFrames++;

        if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
            missingPresentTime = true;
@@ -105,11 +107,6 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
    }

    // If there are no presentation timestamps provided we can't calculate the refresh rate
    if (missingPresentTime && mLastReportedRefreshRate == 0) {
        return std::nullopt;
    }

    // Calculate the average frame time based on presentation timestamps. If those
    // doesn't exist, we look at the time the buffer was queued only. We can do that only if
    // we calculated a refresh rate based on presentation timestamps in the past. The reason
@@ -117,13 +114,18 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
    // when implementing render ahead for specific refresh rates. When hwui no longer provides
    // presentation timestamps we look at the queue time to see if the current refresh rate still
    // matches the content.
    const float averageFrameTime =
    const auto averageFrameTime =
            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
            (mFrameTimes.size() - 1);
            numFrames;
    return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
}

    // Now once we calculated the refresh rate we need to make sure that all the frames we captured
    // are evenly distributed and we don't calculate the average across some burst of frames.
bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
        // Ignore frames captured during a config change
        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
            continue;
        }
        const auto presentTimeDeltas = [&] {
            const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
                                                  : (it + 1)->presetTime - it->presetTime;
@@ -131,8 +133,30 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
        }();

        if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
            return false;
        }
    }

    return true;
}

std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
    static constexpr float MARGIN = 1.0f; // 1Hz

    if (!hasEnoughDataForHeuristic()) {
        ALOGV("Not enough data");
        return std::nullopt;
    }

    const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();

    // If there are no presentation timestamps provided we can't calculate the refresh rate
    if (missingPresentTime && mLastReportedRefreshRate == 0) {
        return std::nullopt;
    }

    if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
        return std::nullopt;
    }

    const auto refreshRate = 1e9f / averageFrameTime;
+4 −1
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ public:
    // Records the last requested present time. It also stores information about when
    // the layer was last updated. If the present time is farther in the future than the
    // updated time, the updated time is the present time.
    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);

    // Sets an explicit layer vote. This usually comes directly from the application via
    // ANativeWindow_setFrameRate API
@@ -93,11 +93,14 @@ private:
    struct FrameTimeData {
        nsecs_t presetTime; // desiredPresentTime, if provided
        nsecs_t queueTime;  // buffer queue time
        bool pendingConfigChange;
    };

    bool isFrequent(nsecs_t now) const;
    bool hasEnoughDataForHeuristic() const;
    std::optional<float> calculateRefreshRateIfPossible();
    std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
    bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;

    const std::string mName;

+6 −0
Original line number Diff line number Diff line
@@ -423,6 +423,12 @@ void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
    }
}

void Scheduler::setConfigChangePending(bool pending) {
    if (mLayerHistory) {
        mLayerHistory->setConfigChangePending(pending);
    }
}

void Scheduler::chooseRefreshRateForContent() {
    if (!mLayerHistory) return;

Loading