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

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

Merge "SF: Polish Fps class"

parents 4497fa8b 6eab42d7
Loading
Loading
Loading
Loading
+79 −63
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -19,94 +19,110 @@
#include <cmath>
#include <ostream>
#include <string>
#include <type_traits>

#include <android-base/stringprintf.h>
#include <utils/Timers.h>

namespace android {

// Value which represents "frames per second". This class is a wrapper around
// float, providing some useful utilities, such as comparisons with tolerance
// and converting between period duration and frequency.
// Frames per second, stored as floating-point frequency. Provides conversion from/to period in
// nanoseconds, and relational operators with precision threshold.
//
//     const Fps fps = 60_Hz;
//
//     using namespace fps_approx_ops;
//     assert(fps == Fps::fromPeriodNsecs(16'666'667));
//
class Fps {
public:
    static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); }
    constexpr Fps() = default;

    Fps() = default;
    explicit constexpr Fps(float fps)
          : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {}
    static constexpr Fps fromValue(float frequency) {
        return frequency > 0.f ? Fps(frequency, static_cast<nsecs_t>(1e9f / frequency)) : Fps();
    }

    constexpr float getValue() const { return fps; }
    static constexpr Fps fromPeriodNsecs(nsecs_t period) {
        return period > 0 ? Fps(1e9f / period, period) : Fps();
    }

    constexpr nsecs_t getPeriodNsecs() const { return period; }
    constexpr bool isValid() const { return mFrequency > 0.f; }

    bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; }
    constexpr float getValue() const { return mFrequency; }
    int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }

    // DO NOT use for std::sort. Instead use comparesLess().
    bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; }
    constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }

    bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; }
private:
    constexpr Fps(float frequency, nsecs_t period) : mFrequency(frequency), mPeriod(period) {}

    bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); }
    float mFrequency = 0.f;
    nsecs_t mPeriod = 0;
};

    bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); }
static_assert(std::is_trivially_copyable_v<Fps>);

    bool isValid() const { return fps > 0.0f; }
constexpr Fps operator""_Hz(unsigned long long frequency) {
    return Fps::fromValue(static_cast<float>(frequency));
}

    int getIntValue() const { return static_cast<int>(std::round(fps)); }
constexpr Fps operator""_Hz(long double frequency) {
    return Fps::fromValue(static_cast<float>(frequency));
}

    // Use this comparator for sorting. Using a comparator with margins can
    // cause std::sort to crash.
    inline static bool comparesLess(const Fps& left, const Fps& right) {
        return left.fps < right.fps;
inline bool isStrictlyLess(Fps lhs, Fps rhs) {
    return lhs.getValue() < rhs.getValue();
}

    // Compares two FPS with margin.
    // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c.
    // DO NOT use with hash maps. Instead use EqualsInBuckets.
    struct EqualsWithMargin {
        bool operator()(const Fps& left, const Fps& right) const {
            return left.equalsWithMargin(right);
// Does not satisfy equivalence relation.
inline bool isApproxEqual(Fps lhs, Fps rhs) {
    // TODO(b/185536303): Replace with ULP distance.
    return std::abs(lhs.getValue() - rhs.getValue()) < 0.001f;
}
    };

    // Equals comparator which can be used with hash maps.
    // It's guaranteed that if two elements are equal, then their hashes are equal.
    struct EqualsInBuckets {
        bool operator()(const Fps& left, const Fps& right) const {
            return left.getBucket() == right.getBucket();
// Does not satisfy strict weak order.
inline bool isApproxLess(Fps lhs, Fps rhs) {
    return isStrictlyLess(lhs, rhs) && !isApproxEqual(lhs, rhs);
}
    };

    inline friend std::string to_string(const Fps& fps) {
        return base::StringPrintf("%.2ffps", fps.fps);
namespace fps_approx_ops {

inline bool operator==(Fps lhs, Fps rhs) {
    return isApproxEqual(lhs, rhs);
}

    inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) {
        return os << to_string(fps);
inline bool operator<(Fps lhs, Fps rhs) {
    return isApproxLess(lhs, rhs);
}

private:
    friend std::hash<android::Fps>;
inline bool operator!=(Fps lhs, Fps rhs) {
    return !isApproxEqual(lhs, rhs);
}

    constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {}
inline bool operator>(Fps lhs, Fps rhs) {
    return isApproxLess(rhs, lhs);
}

    float getBucket() const { return std::round(fps / kMargin); }
inline bool operator<=(Fps lhs, Fps rhs) {
    return !isApproxLess(rhs, lhs);
}

    static constexpr float kMargin = 0.001f;
    float fps = 0;
    nsecs_t period = 0;
};
inline bool operator>=(Fps lhs, Fps rhs) {
    return !isApproxLess(lhs, rhs);
}

static_assert(std::is_trivially_copyable_v<Fps>);
} // namespace fps_approx_ops

} // namespace android
struct FpsApproxEqual {
    bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};

namespace std {
template <>
struct hash<android::Fps> {
    std::size_t operator()(const android::Fps& fps) const {
        return std::hash<float>()(fps.getBucket());
inline std::string to_string(Fps fps) {
    return base::StringPrintf("%.2f Hz", fps.getValue());
}
};
} // namespace std
 No newline at end of file

inline std::ostream& operator<<(std::ostream& stream, Fps fps) {
    return stream << to_string(fps);
}

} // namespace android
+1 −1
Original line number Diff line number Diff line
@@ -1138,7 +1138,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran
    if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
        childrenHaveFrameRate) {
        *transactionNeeded |=
                setFrameRateForLayerTree(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
                setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
    }

    // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
+21 −16
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
      : mName(name),
        mOwnerUid(ownerUid),
        mDefaultVote(defaultVote),
        mLayerVote({defaultVote, Fps(0.0f)}),
        mLayerVote({defaultVote, Fps()}),
        mRefreshRateHistory(name) {}

void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
@@ -93,10 +93,11 @@ bool LayerInfo::isFrequent(nsecs_t now) const {
        return false;
    }

    using fps_approx_ops::operator>=;

    // Layer is considered frequent if the average frame rate is higher than the threshold
    const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1))
            .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) >= kMinFpsForFrequentLayer;
}

bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -191,17 +192,17 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
        return std::nullopt;
    }

    const auto averageFrameTime = calculateAverageFrameTime();
    if (averageFrameTime.has_value()) {
    if (const auto averageFrameTime = calculateAverageFrameTime()) {
        const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
        if (refreshRateConsistent) {
            const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
            // To avoid oscillation, use the last calculated refresh rate if it is
            // close enough
            using fps_approx_ops::operator!=;

            // To avoid oscillation, use the last calculated refresh rate if it is close enough.
            if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
                        MARGIN &&
                !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) {
                mLastRefreshRate.reported != knownRefreshRate) {
                mLastRefreshRate.calculated = refreshRate;
                mLastRefreshRate.reported = knownRefreshRate;
            }
@@ -228,15 +229,15 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref
    if (isAnimating(now)) {
        ALOGV("%s is animating", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
        return {LayerHistory::LayerVoteType::Max, Fps()};
    }

    if (!isFrequent(now)) {
        ALOGV("%s is infrequent", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        // Infrequent layers vote for mininal refresh rate for
        // Infrequent layers vote for minimal refresh rate for
        // battery saving purposes and also to prevent b/135718869.
        return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
        return {LayerHistory::LayerVoteType::Min, Fps()};
    }

    // If the layer was previously tagged as animating or infrequent, we clear
@@ -253,7 +254,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref
    }

    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
    return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
    return {LayerHistory::LayerVoteType::Max, Fps()};
}

const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
@@ -300,9 +301,13 @@ bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
bool LayerInfo::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 =
    const auto [min, max] =
            std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
                                [](const auto& lhs, const auto& rhs) {
                                    return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
                                });

    const bool consistent =
            max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;

    if (CC_UNLIKELY(sTraceEnabled)) {
+12 −20
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ class LayerInfo {
    // 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 kFrequentLayerWindowSize = 3;
    static constexpr Fps kMinFpsForFrequentLayer{10.0f};
    static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
    static constexpr auto kMaxPeriodForFrequentLayerNs =
            std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;

@@ -62,7 +62,7 @@ public:
    // Holds information about the layer vote
    struct LayerVote {
        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
        Fps fps{0.0f};
        Fps fps;
        Seamlessness seamlessness = Seamlessness::Default;
    };

@@ -86,19 +86,17 @@ public:
        using Seamlessness = scheduler::Seamlessness;

        Fps rate;
        FrameRateCompatibility type;
        Seamlessness seamlessness;
        FrameRateCompatibility type = FrameRateCompatibility::Default;
        Seamlessness seamlessness = Seamlessness::Default;

        FrameRate() = default;

        FrameRate()
              : rate(0),
                type(FrameRateCompatibility::Default),
                seamlessness(Seamlessness::Default) {}
        FrameRate(Fps rate, FrameRateCompatibility type,
                  Seamlessness seamlessness = Seamlessness::OnlySeamless)
              : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}

        bool operator==(const FrameRate& other) const {
            return rate.equalsWithMargin(other.rate) && type == other.type &&
            return isApproxEqual(rate, other.rate) && type == other.type &&
                    seamlessness == other.seamlessness;
        }

@@ -151,7 +149,7 @@ public:
    void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }

    // Resets the layer vote to its default.
    void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
    void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }

    std::string getName() const { return mName; }

@@ -201,9 +199,9 @@ private:
    // Holds information about the calculated and reported refresh rate
    struct RefreshRateHeuristicData {
        // Rate calculated on the layer
        Fps calculated{0.0f};
        Fps calculated;
        // Last reported rate for LayerInfo::getRefreshRate()
        Fps reported{0.0f};
        Fps reported;
        // Whether the last reported rate for LayerInfo::getRefreshRate()
        // was due to animation or infrequent updates
        bool animatingOrInfrequent = false;
@@ -229,14 +227,8 @@ private:

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

            bool operator<(const RefreshRateData& other) const {
                // We don't need comparison with margins since we are using
                // this to find the min and max refresh rates.
                return refreshRate.getValue() < other.refreshRate.getValue();
            }
        };

        // Holds tracing strings
@@ -268,7 +260,7 @@ private:

    // Used for sanitizing the heuristic data. If two frames are less than
    // this period apart from each other they'll be considered as duplicates.
    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
    static constexpr nsecs_t kMinPeriodBetweenFrames = (240_Hz).getPeriodNsecs();
    // Used for sanitizing the heuristic data. If two frames are more than
    // this period apart from each other, the interval between them won't be
    // taken into account when calculating average frame rate.
+43 −31
Original line number Diff line number Diff line
@@ -42,19 +42,18 @@ std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, f
}

std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
    std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
    std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
    knownFrameRates.reserve(knownFrameRates.size() + modes.size());

    // Add all supported refresh rates to the set
    // Add all supported refresh rates.
    for (const auto& mode : modes) {
        const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
        knownFrameRates.emplace_back(refreshRate);
        knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
    }

    // Sort and remove duplicates
    std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
    // Sort and remove duplicates.
    std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
                                      Fps::EqualsWithMargin()),
                                      isApproxEqual),
                          knownFrameRates.end());
    return knownFrameRates;
}
@@ -64,6 +63,11 @@ std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;

bool RefreshRate::inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
    using fps_approx_ops::operator<=;
    return minRefreshRate <= getFps() && getFps() <= maxRefreshRate;
}

std::string RefreshRate::toString() const {
    return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
                              getModeId().value(), mode->getHwcId(), getFps().getValue(),
@@ -110,14 +114,14 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe

bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
                                       const RefreshRate& refreshRate) const {
    using namespace fps_approx_ops;

    switch (layer.vote) {
        case LayerVoteType::ExplicitExactOrMultiple:
        case LayerVoteType::Heuristic:
            if (mConfig.frameRateMultipleThreshold != 0 &&
                refreshRate.getFps().greaterThanOrEqualWithMargin(
                        Fps(mConfig.frameRateMultipleThreshold)) &&
                layer.desiredRefreshRate.lessThanWithMargin(
                        Fps(mConfig.frameRateMultipleThreshold / 2))) {
                refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
                layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
                // Don't vote high refresh rates past the threshold for layers with a low desired
                // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
                // 120 Hz, but desired 60 fps should have a vote.
@@ -247,7 +251,7 @@ struct RefreshRateScore {
};

RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                                   const GlobalSignals& globalSignals,
                                                   GlobalSignals globalSignals,
                                                   GlobalSignals* outSignalsConsidered) const {
    std::lock_guard lock(mLock);

@@ -269,7 +273,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir
}

std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
        GlobalSignals* outSignalsConsidered) const {
    const bool sameAsLastCall = lastBestRefreshRateInvocation &&
            lastBestRefreshRateInvocation->layerRequirements == layers &&
@@ -286,7 +290,7 @@ std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
}

RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
        GlobalSignals* outSignalsConsidered) const {
    ATRACE_CALL();
    ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -370,7 +374,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
    // move out the of range if layers explicitly request a different refresh
    // rate.
    const bool primaryRangeIsSingleRate =
            policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
            isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);

    if (!globalSignals.touch && globalSignals.idle &&
        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
@@ -498,8 +502,11 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
            return explicitExact == 0;
        }
    }();

    using fps_approx_ops::operator<;

    if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
        bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) {
        bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
        setTouchConsidered();
        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
        return touchRefreshRate;
@@ -552,7 +559,8 @@ std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
}

RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
        const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const {
        const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
        GlobalSignals globalSignals) const {
    ATRACE_CALL();
    if (!mSupportsFrameRateOverride) return {};

@@ -570,7 +578,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr
                                return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
                            });

        if (touch && hasExplicitExactOrMultiple) {
        if (globalSignals.touch && hasExplicitExactOrMultiple) {
            continue;
        }

@@ -798,8 +806,10 @@ bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
        ALOGE("Default mode is not in the primary range.");
        return false;
    }
    return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
            policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max);

    using namespace fps_approx_ops;
    return policy.appRequestRange.min <= policy.primaryRange.min &&
            policy.appRequestRange.max >= policy.primaryRange.max;
}

status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
@@ -925,19 +935,21 @@ void RefreshRateConfigs::constructAvailableRefreshRates() {
}

Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
    if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
        return *mKnownFrameRates.begin();
    using namespace fps_approx_ops;

    if (frameRate <= mKnownFrameRates.front()) {
        return mKnownFrameRates.front();
    }

    if (frameRate.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) {
        return *std::prev(mKnownFrameRates.end());
    if (frameRate >= mKnownFrameRates.back()) {
        return mKnownFrameRates.back();
    }

    auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
                                       Fps::comparesLess);
                                       isStrictlyLess);

    const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue()));
    const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue()));
    const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
    const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}

@@ -956,7 +968,7 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction
    }
    if (minByPolicy == maxByPolicy) {
        // when min primary range in display manager policy is below device min turn on the timer.
        if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
        if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
            return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
        }
        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
@@ -982,14 +994,14 @@ int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrame
}

bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
    if (smaller.getValue() > bigger.getValue()) {
    if (isStrictlyLess(bigger, smaller)) {
        return isFractionalPairOrMultiple(bigger, smaller);
    }

    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
    constexpr float kCoef = 1000.f / 1001.f;
    return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) ||
            bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef));
    return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
            isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
}

void RefreshRateConfigs::dump(std::string& result) const {
Loading