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

Commit 8ca643a3 authored by Ady Abraham's avatar Ady Abraham
Browse files

SF: Generalize frame rate override to any frame rate

Add support to be able to override the frame rate of an app to
any frame rate, as long as it is a divisor of the display refresh rate.

Test: SF unit tests
Bug: 241460058
Bug: 241447632
Change-Id: Ibf8fa600127d3d5672a4c2a58d0a93b190854cc1
parent 285f8c16
Loading
Loading
Loading
Loading
+64 −37
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/match.h>
#include <ftl/unit.h>
#include <utils/Trace.h>

#include "../SurfaceFlingerProperties.h"
@@ -105,7 +106,7 @@ std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Fi
    return sortedModes;
}

bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
    for (const auto it1 : sortedModes) {
        const auto& mode1 = it1->second;
        for (const auto it2 : sortedModes) {
@@ -264,7 +265,7 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay

    if (layer.vote == LayerVoteType::ExplicitExact) {
        const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
        if (mSupportsFrameRateOverrideByContent) {
        if (supportsFrameRateOverrideByContent()) {
            // Since we support frame rate override, allow refresh rates which are
            // multiples of the layer's request, as those apps would be throttled
            // down to run at the desired refresh rate.
@@ -579,7 +580,7 @@ auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerReq
    // vote we should not change it if we get a touch event. Only apply touch boost if it will
    // actually increase the refresh rate over the normal selection.
    const bool touchBoostForExplicitExact = [&] {
        if (mSupportsFrameRateOverrideByContent) {
        if (supportsFrameRateOverrideByContent()) {
            // Enable touch boost if there are other layers besides exact
            return explicitExact + noVoteLayers != layers.size();
        } else {
@@ -648,23 +649,43 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme
                                                GlobalSignals globalSignals) const
        -> UidToFrameRateOverride {
    ATRACE_CALL();

    ALOGV("%s: %zu layers", __func__, layers.size());

    std::lock_guard lock(mLock);

    std::vector<RefreshRateScore> scores;
    scores.reserve(mDisplayModes.size());
    // Prepare a set of supported display refresh rates for easy lookup
    constexpr size_t kStaticCapacity = 8;
    ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates;
    if (mConfig.enableFrameRateOverride ==
        Config::FrameRateOverride::EnabledForNativeRefreshRates) {
        for (const auto& [_, modePtr] : mDisplayModes) {
            supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit);
        }
    }

    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
        scores.emplace_back(RefreshRateScore{it, 0.0f});
    const auto* policyPtr = getCurrentPolicyLocked();
    // We don't want to run lower than 30fps
    const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);

    using fps_approx_ops::operator/;
    const unsigned numMultiples = displayRefreshRate / minFrameRate;

    std::vector<std::pair<Fps, float>> scoredFrameRates;
    scoredFrameRates.reserve(numMultiples);

    for (unsigned n = numMultiples; n > 0; n--) {
        const Fps divisor = displayRefreshRate / n;
        if (mConfig.enableFrameRateOverride ==
                    Config::FrameRateOverride::EnabledForNativeRefreshRates &&
            !supportedDisplayRefreshRates.contains(divisor)) {
            continue;
        }

    std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) {
        const auto& mode1 = lhs.modeIt->second;
        const auto& mode2 = rhs.modeIt->second;
        return isStrictlyLess(mode1->getFps(), mode2->getFps());
    });
        if (policyPtr->appRequestRanges.render.includes(divisor)) {
            ALOGV("%s: adding %s as a potential frame rate", __func__, to_string(divisor).c_str());
            scoredFrameRates.emplace_back(divisor, 0);
        }
    }

    const auto layersByUid = groupLayersByUid(layers);
    UidToFrameRateOverride frameRateOverrides;
@@ -680,7 +701,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme
            continue;
        }

        for (auto& [_, score, _1] : scores) {
        for (auto& [_, score] : scoredFrameRates) {
            score = 0;
        }

@@ -692,36 +713,33 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme
            LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
                                layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
                                layer->vote != LayerVoteType::ExplicitExact);
            for (auto& [modeIt, score, _] : scores) {
            for (auto& [fps, score] : scoredFrameRates) {
                constexpr bool isSeamlessSwitch = true;
                const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
                                                                  isSeamlessSwitch);
                const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
                score += layer->weight * layerScore;
            }
        }

        // We just care about the refresh rates which are a divisor of the
        // display refresh rate
        const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) {
            const auto& [id, mode] = *score.modeIt;
            return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0;
        });
        scores.erase(it, scores.end());

        // If we never scored any layers, we don't have a preferred frame rate
        if (std::all_of(scores.begin(), scores.end(),
                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
        if (std::all_of(scoredFrameRates.begin(), scoredFrameRates.end(),
                        [](const auto& scoredFrameRate) {
                            const auto [_, score] = scoredFrameRate;
                            return score == 0;
                        })) {
            continue;
        }

        // Now that we scored all the refresh rates we need to pick the lowest refresh rate
        // that got the highest score.
        const DisplayModePtr& bestRefreshRate =
                std::min_element(scores.begin(), scores.end(),
                                 RefreshRateScoreComparator{.refreshRateOrder =
                                                                    RefreshRateOrder::Ascending})
                        ->modeIt->second;
        frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
        const auto [overrideFps, _] =
                *std::max_element(scoredFrameRates.begin(), scoredFrameRates.end(),
                                  [](const auto& lhsPair, const auto& rhsPair) {
                                      const float lhs = lhsPair.second;
                                      const float rhs = rhsPair.second;
                                      return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs);
                                  });
        ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
        frameRateOverrides.emplace(uid, overrideFps);
    }

    return frameRateOverrides;
@@ -894,8 +912,17 @@ void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId a
    mDisplayManagerPolicy = {};
    mDisplayManagerPolicy.defaultMode = activeModeId;

    mSupportsFrameRateOverrideByContent =
            mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes);
    mFrameRateOverrideConfig = [&] {
        switch (mConfig.enableFrameRateOverride) {
            case Config::FrameRateOverride::Disabled:
            case Config::FrameRateOverride::Enabled:
                return mConfig.enableFrameRateOverride;
            case Config::FrameRateOverride::EnabledForNativeRefreshRates:
                return shouldEnableFrameRateOverride(sortedModes)
                        ? Config::FrameRateOverride::EnabledForNativeRefreshRates
                        : Config::FrameRateOverride::Disabled;
        }
    }();

    constructAvailableRefreshRates();
}
@@ -1128,7 +1155,7 @@ void RefreshRateSelector::dump(utils::Dumper& dumper) const {
        dumper.dump("overridePolicy"sv, currentPolicy.toString());
    }

    dumper.dump("supportsFrameRateOverrideByContent"sv, mSupportsFrameRateOverrideByContent);
    dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig));

    std::string idleTimer;
    if (mIdleTimer) {
+24 −8
Original line number Diff line number Diff line
@@ -251,7 +251,20 @@ public:

    // Configuration flags.
    struct Config {
        bool enableFrameRateOverride = false;
        enum class FrameRateOverride {
            // Do not override the frame rate for an app
            Disabled,

            // Override the frame rate for an app to a value which is also
            // a display refresh rate
            EnabledForNativeRefreshRates,

            // Override the frame rate for an app to any value
            Enabled,

            ftl_last = Enabled
        };
        FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled;

        // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
        // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
@@ -266,8 +279,9 @@ public:
        std::optional<KernelIdleTimerController> kernelIdleTimerController;
    };

    RefreshRateSelector(DisplayModes, DisplayModeId activeModeId,
                        Config config = {.enableFrameRateOverride = false,
    RefreshRateSelector(
            DisplayModes, DisplayModeId activeModeId,
            Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
                             .frameRateMultipleThreshold = 0,
                             .idleTimerTimeout = 0ms,
                             .kernelIdleTimerController = {}});
@@ -293,7 +307,9 @@ public:
    // refresh rates.
    KernelIdleTimerAction getIdleTimerAction() const;

    bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
    bool supportsFrameRateOverrideByContent() const {
        return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
    }

    // Return the display refresh rate divisor to match the layer
    // frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -446,7 +462,7 @@ private:
    const std::vector<Fps> mKnownFrameRates;

    const Config mConfig;
    bool mSupportsFrameRateOverrideByContent;
    Config::FrameRateOverride mFrameRateOverrideConfig;

    struct GetRankedRefreshRatesCache {
        std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+8 −0
Original line number Diff line number Diff line
@@ -144,8 +144,16 @@ inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) {
    return !(lhs == rhs);
}

inline unsigned operator/(Fps lhs, Fps rhs) {
    return static_cast<unsigned>(std::ceil(lhs.getValue() / rhs.getValue()));
}

} // namespace fps_approx_ops

constexpr Fps operator/(Fps fps, unsigned divisor) {
    return Fps::fromPeriodNsecs(fps.getPeriodNsecs() * static_cast<nsecs_t>(divisor));
}

inline bool FpsRange::includes(Fps fps) const {
    using fps_approx_ops::operator<=;
    return min <= fps && fps <= max;
+14 −1
Original line number Diff line number Diff line
@@ -2777,8 +2777,21 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
        const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                getKernelIdleTimerProperties(compositionDisplay->getId());

        const auto enableFrameRateOverride = [&] {
            using Config = scheduler::RefreshRateSelector::Config;
            if (!sysprop::enable_frame_rate_override(false)) {
                return Config::FrameRateOverride::Disabled;
            }

            if (sysprop::frame_rate_override_for_native_rates(true)) {
                return Config::FrameRateOverride::EnabledForNativeRefreshRates;
            }

            return Config::FrameRateOverride::Enabled;
        }();

        scheduler::RefreshRateSelector::Config config =
                {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
                {.enableFrameRateOverride = enableFrameRateOverride,
                 .frameRateMultipleThreshold =
                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
                 .idleTimerTimeout = idleTimerTimeoutMs,
+4 −0
Original line number Diff line number Diff line
@@ -367,6 +367,10 @@ bool enable_frame_rate_override(bool defaultValue) {
    return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
}

bool frame_rate_override_for_native_rates(bool defaultValue) {
    return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
}

bool enable_layer_caching(bool defaultValue) {
    return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
}
Loading