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

Commit 4c7831e4 authored by Marin Shalamanov's avatar Marin Shalamanov
Browse files

SF: Move mode caching from Scheduler to RefreshRateConfigs

In the existing behavior Scheduler checks if the
layerReuquirements have changes since the last time
and only then calls into RefreshRateConfigs to
getBestRefreshRate. There are two problems with that

1. on the first iteration of the algorithm
   mFeatures.contentRequirements is empty. If we happen
   to have an empty list of current content requirements
   (for example if all layers have NoVote), we
   won't execute the refresh rate selection algorithm
   and we'll end up with a wrong initial refresh rate.

2. the cached value needs to be invalided when one of
   these change
    - globalSignals
    - supported display modes (happens on TV)
    - display manager policy

Bug: 188872896
Test: atest SchedulerTest RefreshRateConfigsTest
Change-Id: I101f401522fae8358752e283d8375caa93957b6a
parent 2cde1001
Loading
Loading
Loading
Loading
+51 −2
Original line number Diff line number Diff line
@@ -190,6 +190,45 @@ struct RefreshRateScore {
RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                                   const GlobalSignals& globalSignals,
                                                   GlobalSignals* outSignalsConsidered) const {
    std::lock_guard lock(mLock);

    if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
        return *cached;
    }

    GlobalSignals signalsConsidered;
    RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
    lastBestRefreshRateInvocation.emplace(
            GetBestRefreshRateInvocation{.layerRequirements = layers,
                                         .globalSignals = globalSignals,
                                         .outSignalsConsidered = signalsConsidered,
                                         .resultingBestRefreshRate = result});
    if (outSignalsConsidered) {
        *outSignalsConsidered = signalsConsidered;
    }
    return result;
}

std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
        GlobalSignals* outSignalsConsidered) const {
    const bool sameAsLastCall = lastBestRefreshRateInvocation &&
            lastBestRefreshRateInvocation->layerRequirements == layers &&
            lastBestRefreshRateInvocation->globalSignals == globalSignals;

    if (sameAsLastCall) {
        if (outSignalsConsidered) {
            *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
        }
        return lastBestRefreshRateInvocation->resultingBestRefreshRate;
    }

    return {};
}

RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
        GlobalSignals* outSignalsConsidered) const {
    ATRACE_CALL();
    ALOGV("getBestRefreshRate %zu layers", layers.size());

@@ -206,8 +245,6 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir
        }
    };

    std::lock_guard lock(mLock);

    int noVoteLayers = 0;
    int minVoteLayers = 0;
    int maxVoteLayers = 0;
@@ -592,6 +629,11 @@ const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() con

void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
    std::lock_guard lock(mLock);

    // Invalidate the cached invocation to getBestRefreshRate. This forces
    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
    lastBestRefreshRateInvocation.reset();

    mCurrentRefreshRate = mRefreshRates.at(modeId).get();
}

@@ -605,11 +647,16 @@ RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
                                            DisplayModeId currentModeId) {
    std::lock_guard lock(mLock);

    // The current mode should be supported
    LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
        return mode->getId() == currentModeId;
    }));

    // Invalidate the cached invocation to getBestRefreshRate. This forces
    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
    lastBestRefreshRateInvocation.reset();

    mRefreshRates.clear();
    for (const auto& mode : modes) {
        const auto modeId = mode->getId();
@@ -666,6 +713,7 @@ status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
        ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
        return BAD_VALUE;
    }
    lastBestRefreshRateInvocation.reset();
    Policy previousPolicy = *getCurrentPolicyLocked();
    mDisplayManagerPolicy = policy;
    if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -680,6 +728,7 @@ status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& poli
    if (policy && !isPolicyValidLocked(*policy)) {
        return BAD_VALUE;
    }
    lastBestRefreshRateInvocation.reset();
    Policy previousPolicy = *getCurrentPolicyLocked();
    mOverridePolicy = policy;
    if (*getCurrentPolicyLocked() == previousPolicy) {
+22 −0
Original line number Diff line number Diff line
@@ -250,6 +250,10 @@ public:
        bool touch = false;
        // True if the system hasn't seen any buffers posted to layers recently.
        bool idle = false;

        bool operator==(const GlobalSignals& other) const {
            return touch == other.touch && idle == other.idle;
        }
    };

    // Returns the refresh rate that fits best to the given layers.
@@ -350,6 +354,15 @@ private:
            const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
            std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);

    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                                        const GlobalSignals& globalSignals,
                                                        GlobalSignals* outSignalsConsidered) const
            REQUIRES(mLock);

    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
                                         const GlobalSignals& globalSignals,
                                         GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);

    // Returns the refresh rate with the highest score in the collection specified from begin
    // to end. If there are more than one with the same highest refresh rate, the first one is
    // returned.
@@ -414,6 +427,15 @@ private:

    const bool mEnableFrameRateOverride;
    bool mSupportsFrameRateOverride;

    struct GetBestRefreshRateInvocation {
        std::vector<LayerRequirement> layerRequirements;
        GlobalSignals globalSignals;
        GlobalSignals outSignalsConsidered;
        RefreshRate resultingBestRefreshRate;
    };
    mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
            GUARDED_BY(mLock);
};

} // namespace android::scheduler
+0 −3
Original line number Diff line number Diff line
@@ -622,9 +622,6 @@ void Scheduler::chooseRefreshRateForContent() {
    bool frameRateOverridesChanged;
    {
        std::lock_guard<std::mutex> lock(mFeatureStateLock);
        if (mFeatures.contentRequirements == summary) {
            return;
        }
        mFeatures.contentRequirements = summary;

        newModeId = calculateRefreshRateModeId(&consideredSignals);
+92 −0
Original line number Diff line number Diff line
@@ -45,9 +45,16 @@ using LayerRequirement = RefreshRateConfigs::LayerRequirement;

class RefreshRateConfigsTest : public testing::Test {
protected:
    using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;

    RefreshRateConfigsTest();
    ~RefreshRateConfigsTest();

    RefreshRate createRefreshRate(DisplayModePtr displayMode) {
        return {displayMode->getId(), displayMode, displayMode->getFps(),
                RefreshRate::ConstructorTag(0)};
    }

    Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
        return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
    }
@@ -71,6 +78,19 @@ protected:
        return *refreshRateConfigs.mMaxSupportedRefreshRate;
    }

    void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
                                          const GetBestRefreshRateInvocation& invocation) {
        std::lock_guard lock(refreshRateConfigs.mLock);
        refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
                GetBestRefreshRateInvocation(invocation));
    }

    std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
            const RefreshRateConfigs& refreshRateConfigs) {
        std::lock_guard lock(refreshRateConfigs.mLock);
        return refreshRateConfigs.lastBestRefreshRateInvocation;
    }

    // Test config IDs
    static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
    static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
@@ -1752,6 +1772,78 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOv
              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}

TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
    using GlobalSignals = RefreshRateConfigs::GlobalSignals;

    auto refreshRateConfigs =
            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);

    setLastBestRefreshRateInvocation(*refreshRateConfigs,
                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
                                                                          LayerRequirement>(),
                                                                  .globalSignals = {.touch = true,
                                                                                    .idle = true},
                                                                  .outSignalsConsidered =
                                                                          {.touch = true,
                                                                           .idle = false},
                                                                  .resultingBestRefreshRate =
                                                                          createRefreshRate(
                                                                                  mConfig90)});

    EXPECT_EQ(createRefreshRate(mConfig90),
              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
                                                     {.touch = true, .idle = true}));

    const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
    setLastBestRefreshRateInvocation(*refreshRateConfigs,
                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
                                                                          LayerRequirement>(),
                                                                  .globalSignals = {.touch = true,
                                                                                    .idle = true},
                                                                  .outSignalsConsidered =
                                                                          cachedSignalsConsidered,
                                                                  .resultingBestRefreshRate =
                                                                          createRefreshRate(
                                                                                  mConfig30)});

    GlobalSignals signalsConsidered;
    EXPECT_EQ(createRefreshRate(mConfig30),
              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
                                                     {.touch = true, .idle = true},
                                                     &signalsConsidered));

    EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
}

TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
    using GlobalSignals = RefreshRateConfigs::GlobalSignals;

    auto refreshRateConfigs =
            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
    ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());

    GlobalSignals globalSignals{.touch = true, .idle = true};
    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                LayerRequirement{.weight = 0.5f}};
    const auto lastResult =
            refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
                                                   /* outSignalsConsidered */ nullptr);

    const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);

    ASSERT_TRUE(lastInvocation.has_value());
    ASSERT_EQ(layers, lastInvocation->layerRequirements);
    ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
    ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);

    // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
    // to getBestRefreshRate()
    GlobalSignals detaultSignals;
    ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
}

TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
    EXPECT_TRUE(mExpected60Config < mExpected90Config);
    EXPECT_FALSE(mExpected60Config < mExpected60Config);
+21 −0
Original line number Diff line number Diff line
@@ -220,4 +220,25 @@ TEST_F(SchedulerTest, calculateExtraBufferCount) {
    EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
}

MATCHER(Is120Hz, "") {
    return arg.getFps().equalsWithMargin(Fps(120.f));
}

TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());

    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());

    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);

    constexpr bool kPowerStateNormal = true;
    mScheduler->setDisplayPowerState(kPowerStateNormal);

    constexpr uint32_t kDisplayArea = 999'999;
    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);

    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
    mScheduler->chooseRefreshRateForContent();
}

} // namespace android