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

Commit 5ab2db70 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "SurfaceFlinger: allow switching when layers vote to refresh rate"

parents c7196387 4ccdcb41
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
}

LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
    ATRACE_CALL();
    std::lock_guard lock(mLock);

    partitionLayers(now);
+22 −8
Original line number Diff line number Diff line
@@ -61,21 +61,35 @@ bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
}

bool LayerInfoV2::isFrequent(nsecs_t now) const {
    // Find the first valid frame time
    auto it = mFrameTimes.begin();
    for (; it != mFrameTimes.end(); ++it) {
        if (isFrameTimeValid(*it)) {
            break;
        }
    }

    // If we know nothing about this layer we consider it as frequent as it might be the start
    // of an animation.
    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
    if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
        return true;
    }

    // Layer is frequent if the earliest value in the window of most recent present times is
    // within threshold.
    const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
    if (!isFrameTimeValid(*it)) {
        return true;
    // Find the first active frame
    for (; it != mFrameTimes.end(); ++it) {
        if (it->queueTime >= getActiveLayerThreshold(now)) {
            break;
        }
    }

    const auto numFrames = std::distance(it, mFrameTimes.end()) - 1;
    if (numFrames <= 0) {
        return false;
    }

    const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
    return it->queueTime >= threshold;
    // Layer is considered frequent if the average frame rate is higher than the threshold
    const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
    return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}

bool LayerInfoV2::hasEnoughDataForHeuristic() const {
+3 −1
Original line number Diff line number Diff line
@@ -47,7 +47,9 @@ class LayerInfoV2 {
    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
    // favor of a low refresh rate.
    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
    static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
    static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
            std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;

    friend class LayerHistoryTestV2;

+87 −59
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@
#include <chrono>
#include <cmath>

using namespace std::chrono_literals;

namespace android::scheduler {

using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
@@ -84,14 +82,31 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
    return *bestSoFar;
}

std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
                                                                 nsecs_t displayPeriod) const {
    auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
    if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
        std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
        displayFramesQuot++;
        displayFramesRem = 0;
    }

    return {displayFramesQuot, displayFramesRem};
}

const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
        const std::vector<LayerRequirement>& layers) const {
    constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count();
        const std::vector<LayerRequirement>& layers, bool touchActive) const {
    ATRACE_CALL();
    ALOGV("getRefreshRateForContent %zu layers", layers.size());

    std::lock_guard lock(mLock);

    // For now if the touch is active return the peak refresh rate
    // This should be optimized to consider other layers as well.
    if (touchActive) {
        return *mAvailableRefreshRates.back();
    }

    int noVoteLayers = 0;
    int minVoteLayers = 0;
    int maxVoteLayers = 0;
@@ -115,11 +130,6 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
        return *mAvailableRefreshRates.front();
    }

    // If we have some Max layers and no Explicit we should return Max
    if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
        return *mAvailableRefreshRates.back();
    }

    // Find the best refresh rate based on score
    std::vector<std::pair<const RefreshRate*, float>> scores;
    scores.reserve(mAvailableRefreshRates.size());
@@ -130,67 +140,85 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(

    for (const auto& layer : layers) {
        ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
            layer.vote == LayerVoteType::Max) {
        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
            continue;
        }

        // Adjust the weight in case we have explicit layers. The priority is:
        //  - ExplicitExactOrMultiple
        //  - ExplicitDefault
        //  - Heuristic
        auto weight = layer.weight;
        if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
            if (layer.vote == LayerVoteType::Heuristic) {
                weight /= 2.f;
            }
        }

        if (explicitExactOrMultipleVoteLayers > 0) {
            if (layer.vote == LayerVoteType::Heuristic ||
                layer.vote == LayerVoteType::ExplicitDefault) {
                weight /= 2.f;
            }
        for (auto i = 0u; i < scores.size(); i++) {
            // If the layer wants Max, give higher score to the higher refresh rate
            if (layer.vote == LayerVoteType::Max) {
                const auto ratio = scores[i].first->fps / scores.back().first->fps;
                // use ratio^2 to get a lower score the more we get further from peak
                const auto layerScore = ratio * ratio;
                ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
                      scores[i].first->name.c_str(), layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }

        for (auto& [refreshRate, overallScore] : scores) {
            const auto displayPeriod = refreshRate->vsyncPeriod;
            const auto displayPeriod = scores[i].first->vsyncPeriod;
            const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
            if (layer.vote == LayerVoteType::ExplicitDefault) {
                const auto layerScore = [&]() {
                    const auto [displayFramesQuot, displayFramesRem] =
                            getDisplayFrames(layerPeriod, displayPeriod);
                    if (displayFramesQuot == 0) {
                        // Layer desired refresh rate is higher the display rate.
                        return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod);
                    }

            // Calculate how many display vsyncs we need to present a single frame for this layer
            auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
            if (displayFramesRem <= MARGIN ||
                std::abs(displayFramesRem - displayPeriod) <= MARGIN) {
                displayFramesQuot++;
                displayFramesRem = 0;
                    return 1.0f -
                            (static_cast<float>(displayFramesRem) /
                             static_cast<float>(layerPeriod));
                }();

                ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
                      layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }

            float layerScore;
            static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
                layer.vote == LayerVoteType::Heuristic) {
                const auto layerScore = [&]() {
                    // Calculate how many display vsyncs we need to present a single frame for this
                    // layer
                    const auto [displayFramesQuot, displayFramesRem] =
                            getDisplayFrames(layerPeriod, displayPeriod);
                    static constexpr size_t MAX_FRAMES_TO_FIT =
                            10; // Stop calculating when score < 0.1
                    if (displayFramesRem == 0) {
                        // Layer desired refresh rate matches the display rate.
                layerScore = weight * 1.0f;
            } else if (displayFramesQuot == 0) {
                        return 1.0f;
                    }

                    if (displayFramesQuot == 0) {
                        // Layer desired refresh rate is higher the display rate.
                layerScore = weight *
                        (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
                        return (static_cast<float>(layerPeriod) /
                                static_cast<float>(displayPeriod)) *
                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
            } else {
                // Layer desired refresh rate is lower the display rate. Check how well it fits the
                // cadence
                    }

                    // Layer desired refresh rate is lower the display rate. Check how well it fits
                    // the cadence
                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
                    int iter = 2;
                while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
                        diff = diff - (displayPeriod - diff);
                        iter++;
                    }

                layerScore = weight * (1.0f / iter);
                    return 1.0f / iter;
                }();
                ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f",
                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
                      layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }

            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
                  1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
            overallScore += layerScore;
        }
    }

+15 −4
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "Scheduler/StrongTyping.h"

namespace android::scheduler {
using namespace std::chrono_literals;

enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };

@@ -43,6 +44,10 @@ inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateC
 */
class RefreshRateConfigs {
public:
    // Margin used when matching refresh rates to the content desired ones.
    static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
        std::chrono::nanoseconds(800us).count();

    struct RefreshRate {
        // The tolerance within which we consider FPS approximately equals.
        static constexpr float FPS_EPSILON = 0.001f;
@@ -123,13 +128,15 @@ public:
        bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
    };

    // Returns all available refresh rates according to the current policy.
    // Returns the refresh rate that fits best to the given layers.
    const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
            EXCLUDES(mLock);

    // Returns all available refresh rates according to the current policy.
    const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const
            EXCLUDES(mLock);
    // Returns the refresh rate that fits best to the given layers. This function also gets a
    // boolean flag that indicates whether user touched the screen recently to be factored in when
    // choosing the refresh rate.
    const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers,
                                                  bool touchActive) const EXCLUDES(mLock);

    // Returns all the refresh rates supported by the device. This won't change at runtime.
    const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
@@ -188,6 +195,10 @@ private:
    template <typename Iter>
    const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const;

    // Returns number of display frames and remainder when dividing the layer refresh period by
    // display refresh period.
    std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;

    // The list of refresh rates, indexed by display config ID. This must not change after this
    // object is initialized.
    AllRefreshRatesMapType mRefreshRates;
Loading