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

Commit 2dcdc109 authored by Ady Abraham's avatar Ady Abraham Committed by Automerger Merge Worker
Browse files

Merge changes If3b52582,I97a24b74 into rvc-dev am: 3c17badb am: 9e182ed4

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/11766433

Change-Id: I6b223b804ca2cd26a5ced10a94ce7f93efbab39d
parents 7a402f05 9e182ed4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ private:

class LayerHistoryV2 : public android::scheduler::LayerHistory {
public:
    LayerHistoryV2();
    LayerHistoryV2(const scheduler::RefreshRateConfigs&);
    virtual ~LayerHistoryV2();

    // Layers are unregistered when the weak reference expires.
+19 −23
Original line number Diff line number Diff line
@@ -58,37 +58,32 @@ bool useFrameRatePriority() {
    return atoi(value);
}

void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
           int fps) {
    const auto layer = weak.promote();
    if (!layer) return;

    const auto makeTag = [layer](LayerHistory::LayerVoteType vote) {
        return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName();
    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
    };

    const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote);
    const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic);
    const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault);
    const auto explicitExactOrMultipleVoteTag =
            makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple);
    const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min);
    const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max);

    ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
    ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
    ATRACE_INT(explicitDefaultVoteTag.c_str(),
               type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
    ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
               type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
    ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
    ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
    traceType(LayerHistory::LayerVoteType::NoVote, 1);
    traceType(LayerHistory::LayerVoteType::Heuristic, fps);
    traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
    traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
    traceType(LayerHistory::LayerVoteType::Min, 1);
    traceType(LayerHistory::LayerVoteType::Max, 1);

    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
}
} // namespace

LayerHistoryV2::LayerHistoryV2()
      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
    LayerInfoV2::setTraceEnabled(mTraceEnabled);
    LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
}

LayerHistoryV2::~LayerHistoryV2() = default;

void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
@@ -151,7 +146,7 @@ LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
        summary.push_back({strong->getName(), type, refreshRate, weight});

        if (CC_UNLIKELY(mTraceEnabled)) {
            trace(layer, type, static_cast<int>(std::round(refreshRate)));
            trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
        }
    }

@@ -190,7 +185,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) {
        }

        if (CC_UNLIKELY(mTraceEnabled)) {
            trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
        }

        info->onLayerInactive(now);
@@ -217,4 +212,5 @@ void LayerHistoryV2::clear() {
        info->clearHistory(systemTime());
    }
}

} // namespace android::scheduler::impl
+3 −3
Original line number Diff line number Diff line
@@ -98,9 +98,9 @@ class LayerInfo {
                return false;
            }

            // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
            // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
            if (mElements.size() < HISTORY_SIZE &&
                mElements.back() - mElements.front() < HISTORY_TIME.count()) {
                mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
                return false;
            }

@@ -124,7 +124,7 @@ class LayerInfo {

    private:
        std::deque<nsecs_t> mElements;
        static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
    };

    friend class LayerHistoryTest;
+121 −47
Original line number Diff line number Diff line
@@ -15,24 +15,31 @@
 */

// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS

#include "LayerInfoV2.h"

#include <algorithm>
#include <utility>

#include <cutils/compiler.h>
#include <cutils/trace.h>

#undef LOG_TAG
#define LOG_TAG "LayerInfoV2"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS

namespace android::scheduler {

const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
bool LayerInfoV2::sTraceEnabled = false;

LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
                         LayerHistory::LayerVoteType defaultVote)
      : mName(name),
        mHighRefreshRatePeriod(highRefreshRatePeriod),
        mDefaultVote(defaultVote),
        mLayerVote({defaultVote, 0.0f}) {}
        mLayerVote({defaultVote, 0.0f}),
        mRefreshRateHistory(name) {}

void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
                                     LayerUpdateType updateType, bool pendingConfigChange) {
@@ -92,24 +99,28 @@ bool LayerInfoV2::isAnimating(nsecs_t now) const {
}

bool LayerInfoV2::hasEnoughDataForHeuristic() const {
    // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
    // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
    if (mFrameTimes.size() < 2) {
        ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
        return false;
    }

    if (!isFrameTimeValid(mFrameTimes.front())) {
        ALOGV("stale frames still captured");
        return false;
    }

    if (mFrameTimes.size() < HISTORY_SIZE &&
        mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
    const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
    if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
        ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
              totalDuration / 1e9f);
        return false;
    }

    return true;
}

std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
    nsecs_t totalPresentTimeDeltas = 0;
    nsecs_t totalQueueTimeDeltas = 0;
    bool missingPresentTime = false;
@@ -117,15 +128,20 @@ std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() 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;
            return std::nullopt;
        }

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

        if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
        if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
            missingPresentTime = true;
            // If there are no presentation timestamps and we haven't calculated
            // one in the past then we can't calculate the refresh rate
            if (mLastRefreshRate.reported == 0) {
                return std::nullopt;
            }
            continue;
        }

@@ -140,58 +156,46 @@ std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
    // 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 auto averageFrameTime =
            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
            numFrames;
    return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
}

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;
    return static_cast<nsecs_t>(averageFrameTime);
}
        const auto presentTimeDeltas = [&] {
            const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
                                                  : (it + 1)->presetTime - it->presetTime;
            return std::max(delta, mHighRefreshRatePeriod);
        }();

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

    return true;
}

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

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

    const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
    const auto averageFrameTime = calculateAverageFrameTime();
    if (averageFrameTime.has_value()) {
        const auto refreshRate = 1e9f / *averageFrameTime;
        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
        if (refreshRateConsistent) {
            const auto knownRefreshRate =
                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);

    // If there are no presentation timestamps provided we can't calculate the refresh rate
    if (missingPresentTime && mLastReportedRefreshRate == 0) {
        return std::nullopt;
            // To avoid oscillation, use the last calculated refresh rate if it is
            // close enough
            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
                mLastRefreshRate.reported != knownRefreshRate) {
                mLastRefreshRate.calculated = refreshRate;
                mLastRefreshRate.reported = knownRefreshRate;
            }

    if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
        return std::nullopt;
            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
                  refreshRate, mLastRefreshRate.reported);
        } else {
            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
                  refreshRate, mLastRefreshRate.reported);
        }

    const auto refreshRate = 1e9f / averageFrameTime;
    if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
        mLastReportedRefreshRate = refreshRate;
    }

    ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
    return mLastReportedRefreshRate;
    return mLastRefreshRate.reported == 0 ? std::nullopt
                                          : std::make_optional(mLastRefreshRate.reported);
}

std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
@@ -202,15 +206,24 @@ std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_

    if (isAnimating(now)) {
        ALOGV("%s is animating", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        return {LayerHistory::LayerVoteType::Max, 0};
    }

    if (!isFrequent(now)) {
        ALOGV("%s is infrequent", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        return {LayerHistory::LayerVoteType::Min, 0};
    }

    auto refreshRate = calculateRefreshRateIfPossible();
    // If the layer was previously tagged as animating or infrequent, we clear
    // the history as it is likely the layer just changed its behavior
    // and we should not look at stale data
    if (mLastRefreshRate.animatingOrInfrequent) {
        clearHistory(now);
    }

    auto refreshRate = calculateRefreshRateIfPossible(now);
    if (refreshRate.has_value()) {
        ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
@@ -220,4 +233,65 @@ std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_
    return {LayerHistory::LayerVoteType::Max, 0};
}

const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
    if (mTraceTags.count(type) == 0) {
        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
        mTraceTags.emplace(type, tag);
    }

    return mTraceTags.at(type).c_str();
}

LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
    const std::string prefix = "LFPS ";
    const std::string suffix = "Heuristic ";
    return {.min = prefix + mName + suffix + "min",
            .max = prefix + mName + suffix + "max",
            .consistent = prefix + mName + suffix + "consistent",
            .average = prefix + mName + suffix + "average"};
}

void LayerInfoV2::RefreshRateHistory::clear() {
    mRefreshRates.clear();
}

bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
    mRefreshRates.push_back({refreshRate, now});
    while (mRefreshRates.size() >= HISTORY_SIZE ||
           now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
        mRefreshRates.pop_front();
    }

    if (CC_UNLIKELY(sTraceEnabled)) {
        if (!mHeuristicTraceTagData.has_value()) {
            mHeuristicTraceTagData = makeHeuristicTraceTagData();
        }

        ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
    }

    return isConsistent();
}

bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
    if (mRefreshRates.empty()) return true;

    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
    const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;

    if (CC_UNLIKELY(sTraceEnabled)) {
        if (!mHeuristicTraceTagData.has_value()) {
            mHeuristicTraceTagData = makeHeuristicTraceTagData();
        }

        ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
        ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
        ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
    }

    return consistent;
}

} // namespace android::scheduler
+88 −13
Original line number Diff line number Diff line
@@ -56,6 +56,12 @@ class LayerInfoV2 {
    friend class LayerHistoryTestV2;

public:
    static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }

    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
        sRefreshRateConfigs = &refreshRateConfigs;
    }

    LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
                LayerHistory::LayerVoteType defaultVote);

@@ -86,6 +92,9 @@ public:
    // updated time, the updated time is the present time.
    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }

    // Returns a C string for tracing a vote
    const char* getTraceTag(LayerHistory::LayerVoteType type) const;

    void onLayerInactive(nsecs_t now) {
        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
        // We are not deleting the old frame to keep track of whether we should treat the first
@@ -93,7 +102,8 @@ public:
        // posting infrequent updates.
        const auto timePoint = std::chrono::nanoseconds(now);
        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
        mLastReportedRefreshRate = 0.0f;
        mLastRefreshRate = {};
        mRefreshRateHistory.clear();
    }

    void clearHistory(nsecs_t now) {
@@ -109,12 +119,73 @@ private:
        bool pendingConfigChange;
    };

    // Holds information about the calculated and reported refresh rate
    struct RefreshRateHeuristicData {
        // Rate calculated on the layer
        float calculated = 0.0f;
        // Last reported rate for LayerInfoV2::getRefreshRate()
        float reported = 0.0f;
        // Whether the last reported rate for LayerInfoV2::getRefreshRate()
        // was due to animation or infrequent updates
        bool animatingOrInfrequent = false;
    };

    // Holds information about the layer vote
    struct LayerVote {
        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
        float fps = 0.0f;
    };

    // Class to store past calculated refresh rate and determine whether
    // the refresh rate calculated is consistent with past values
    class RefreshRateHistory {
    public:
        static constexpr auto HISTORY_SIZE = 90;
        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;

        RefreshRateHistory(const std::string& name) : mName(name) {}

        // Clears History
        void clear();

        // Adds a new refresh rate and returns true if it is consistent
        bool add(float refreshRate, nsecs_t now);

    private:
        friend class LayerHistoryTestV2;

        // Holds the refresh rate when it was calculated
        struct RefreshRateData {
            float refreshRate = 0.0f;
            nsecs_t timestamp = 0;

            bool operator<(const RefreshRateData& other) const {
                return refreshRate < other.refreshRate;
            }
        };

        // Holds tracing strings
        struct HeuristicTraceTagData {
            std::string min;
            std::string max;
            std::string consistent;
            std::string average;
        };

        bool isConsistent() const;
        HeuristicTraceTagData makeHeuristicTraceTagData() const;

        const std::string mName;
        mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
        std::deque<RefreshRateData> mRefreshRates;
        static constexpr float MARGIN_FPS = 1.0;
    };

    bool isFrequent(nsecs_t now) const;
    bool isAnimating(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;
    std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
    std::optional<nsecs_t> calculateAverageFrameTime() const;
    bool isFrameTimeValid(const FrameTimeData&) const;

    const std::string mName;
@@ -123,23 +194,27 @@ private:
    const nsecs_t mHighRefreshRatePeriod;
    LayerHistory::LayerVoteType mDefaultVote;

    LayerVote mLayerVote;

    nsecs_t mLastUpdatedTime = 0;

    nsecs_t mLastAnimationTime = 0;

    float mLastReportedRefreshRate = 0.0f;

    // Holds information about the layer vote
    struct {
        LayerHistory::LayerVoteType type;
        float fps;
    } mLayerVote;
    RefreshRateHeuristicData mLastRefreshRate;

    std::deque<FrameTimeData> mFrameTimes;
    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
            std::chrono::steady_clock::now();
    static constexpr size_t HISTORY_SIZE = 90;
    static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
    static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
    static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;

    RefreshRateHistory mRefreshRateHistory;

    mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;

    // Shared for all LayerInfo instances
    static const RefreshRateConfigs* sRefreshRateConfigs;
    static bool sTraceEnabled;
};

} // namespace scheduler
Loading