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

Commit 963da1c0 authored by Dominik Laskowski's avatar Dominik Laskowski
Browse files

SF: Match followers' refresh rate to pacesetter's

Multi-display refresh rate selection was flawed: The scheduler runs the
refresh rate selection algorithm for each display, and then filters the
candidate modes of each follower to match the pacesetter's refresh rate.

This means that:

1. The followers incorrectly consider refresh rates that don't match the
   pacesetter. Because the DM-specified constraint is [59, 61] Hz, some
   situations caused selection of a fractional rate (e.g. 59.94) instead
   of 60 on certain external displays. The result would be black screens
   for several seconds due to mode sets if the selection was not stable.

2. The followers incorrectly evaluate heuristics that should only affect
   the pacesetter, e.g. per-surface votes, global signals.

Fix this by teaching RefreshRateSelector about follower displays.

Foldables also benefit from no longer running the algorithm twice.

Fixes: 324188430
Bug: 329111930
Test: No black screen for 4 seconds upon rotating mirrored YouTube.
Test: 60+60 still works on foldables.
Test: RefreshRateSelectorTest.pacesetterConsidered
Change-Id: Ie1b27e81d860a709c85651f068fedb2b496861de
Merged-In: Ie1b27e81d860a709c85651f068fedb2b496861de
parent eb83011e
Loading
Loading
Loading
Loading
+27 −7
Original line number Original line Diff line number Diff line
@@ -474,21 +474,23 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay
}
}


auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
                                              GlobalSignals signals) const -> RankedFrameRates {
                                              GlobalSignals signals, Fps pacesetterFps) const
        -> RankedFrameRates {
    GetRankedFrameRatesCache cache{layers, signals, pacesetterFps};

    std::lock_guard lock(mLock);
    std::lock_guard lock(mLock);


    if (mGetRankedFrameRatesCache &&
    if (mGetRankedFrameRatesCache && mGetRankedFrameRatesCache->matches(cache)) {
        mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
        return mGetRankedFrameRatesCache->result;
        return mGetRankedFrameRatesCache->result;
    }
    }


    const auto result = getRankedFrameRatesLocked(layers, signals);
    cache.result = getRankedFrameRatesLocked(layers, signals, pacesetterFps);
    mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
    mGetRankedFrameRatesCache = std::move(cache);
    return result;
    return mGetRankedFrameRatesCache->result;
}
}


auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
                                                    GlobalSignals signals) const
                                                    GlobalSignals signals, Fps pacesetterFps) const
        -> RankedFrameRates {
        -> RankedFrameRates {
    using namespace fps_approx_ops;
    using namespace fps_approx_ops;
    ATRACE_CALL();
    ATRACE_CALL();
@@ -496,6 +498,24 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi


    const auto& activeMode = *getActiveModeLocked().modePtr;
    const auto& activeMode = *getActiveModeLocked().modePtr;


    if (pacesetterFps.isValid()) {
        ALOGV("Follower display");

        const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending,
                                            std::nullopt, [&](FrameRateMode mode) {
                                                return mode.modePtr->getPeakFps() == pacesetterFps;
                                            });

        if (!ranking.empty()) {
            ATRACE_FORMAT_INSTANT("%s (Follower display)",
                                  to_string(ranking.front().frameRateMode.fps).c_str());

            return {ranking, kNoSignals, pacesetterFps};
        }

        ALOGW("Follower display cannot follow the pacesetter");
    }

    // Keep the display at max frame rate for the duration of powering on the display.
    // Keep the display at max frame rate for the duration of powering on the display.
    if (signals.powerOnImminent) {
    if (signals.powerOnImminent) {
        ALOGV("Power On Imminent");
        ALOGV("Power On Imminent");
+18 −5
Original line number Original line Diff line number Diff line
@@ -233,14 +233,18 @@ public:
    struct RankedFrameRates {
    struct RankedFrameRates {
        FrameRateRanking ranking; // Ordered by descending score.
        FrameRateRanking ranking; // Ordered by descending score.
        GlobalSignals consideredSignals;
        GlobalSignals consideredSignals;
        Fps pacesetterFps;


        bool operator==(const RankedFrameRates& other) const {
        bool operator==(const RankedFrameRates& other) const {
            return ranking == other.ranking && consideredSignals == other.consideredSignals;
            return ranking == other.ranking && consideredSignals == other.consideredSignals &&
                    isApproxEqual(pacesetterFps, other.pacesetterFps);
        }
        }
    };
    };


    RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
    // If valid, `pacesetterFps` (used by follower displays) filters the ranking to modes matching
            EXCLUDES(mLock);
    // that refresh rate.
    RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals,
                                         Fps pacesetterFps = {}) const EXCLUDES(mLock);


    FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
    FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
        std::lock_guard lock(mLock);
        std::lock_guard lock(mLock);
@@ -415,7 +419,8 @@ private:
    const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
    const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);


    RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
    RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
                                               GlobalSignals signals) const REQUIRES(mLock);
                                               GlobalSignals signals, Fps pacesetterFps) const
            REQUIRES(mLock);


    // Returns number of display frames and remainder when dividing the layer refresh period by
    // Returns number of display frames and remainder when dividing the layer refresh period by
    // display refresh period.
    // display refresh period.
@@ -534,8 +539,16 @@ private:
    Config::FrameRateOverride mFrameRateOverrideConfig;
    Config::FrameRateOverride mFrameRateOverrideConfig;


    struct GetRankedFrameRatesCache {
    struct GetRankedFrameRatesCache {
        std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
        std::vector<LayerRequirement> layers;
        GlobalSignals signals;
        Fps pacesetterFps;

        RankedFrameRates result;
        RankedFrameRates result;

        bool matches(const GetRankedFrameRatesCache& other) const {
            return layers == other.layers && signals == other.signals &&
                    isApproxEqual(pacesetterFps, other.pacesetterFps);
        }
    };
    };
    mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
    mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);


+19 −26
Original line number Original line Diff line number Diff line
@@ -1146,38 +1146,31 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
    ATRACE_CALL();
    ATRACE_CALL();


    using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
    DisplayModeChoiceMap modeChoices;
    ui::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
    const auto globalSignals = makeGlobalSignals();
    const auto globalSignals = makeGlobalSignals();
    Fps pacesetterFps;


    for (const auto& [id, display] : mDisplays) {
    const Fps pacesetterFps = [&]() REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext) {
        auto rankedFrameRates =
        auto rankedFrameRates =
                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
                pacesetterSelectorPtrLocked()->getRankedFrameRates(mPolicy.contentRequirements,
                                                                   globalSignals);
                                                                   globalSignals);
        if (id == *mPacesetterDisplayId) {
            pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
        }
        perDisplayRanking.push_back(std::move(rankedFrameRates));
    }


    DisplayModeChoiceMap modeChoices;
        const Fps pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
    using fps_approx_ops::operator==;


    for (auto& [rankings, signals] : perDisplayRanking) {
        modeChoices.try_emplace(*mPacesetterDisplayId,
        const auto chosenFrameRateMode =
                                DisplayModeChoice::from(std::move(rankedFrameRates)));
                ftl::find_if(rankings,
        return pacesetterFps;
                             [&](const auto& ranking) {
    }();
                                 return ranking.frameRateMode.fps == pacesetterFps;

                             })
    for (const auto& [id, display] : mDisplays) {
                        .transform([](const auto& scoredFrameRate) {
        if (id == *mPacesetterDisplayId) continue;
                            return scoredFrameRate.get().frameRateMode;
                        })
                        .value_or(rankings.front().frameRateMode);


        modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
        auto rankedFrameRates =
                                DisplayModeChoice{chosenFrameRateMode, signals});
                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals,
                                                         pacesetterFps);

        modeChoices.try_emplace(id, DisplayModeChoice::from(std::move(rankedFrameRates)));
    }
    }

    return modeChoices;
    return modeChoices;
}
}


+5 −0
Original line number Original line Diff line number Diff line
@@ -402,6 +402,11 @@ private:
        DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
        DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
              : mode(std::move(mode)), consideredSignals(consideredSignals) {}
              : mode(std::move(mode)), consideredSignals(consideredSignals) {}


        static DisplayModeChoice from(RefreshRateSelector::RankedFrameRates rankedFrameRates) {
            return {rankedFrameRates.ranking.front().frameRateMode,
                    rankedFrameRates.consideredSignals};
        }

        FrameRateMode mode;
        FrameRateMode mode;
        GlobalSignals consideredSignals;
        GlobalSignals consideredSignals;


+46 −19
Original line number Original line Diff line number Diff line
@@ -103,8 +103,9 @@ struct TestableRefreshRateSelector : RefreshRateSelector {
    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }


    auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
    auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
                             GlobalSignals signals = {}) const {
                             GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
        const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
        const auto result =
                RefreshRateSelector::getRankedFrameRates(layers, signals, pacesetterFps);


        EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
        EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
                                   ScoredFrameRate::DescendingScore{}));
                                   ScoredFrameRate::DescendingScore{}));
@@ -114,8 +115,8 @@ struct TestableRefreshRateSelector : RefreshRateSelector {


    auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
    auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
                                     GlobalSignals signals) const {
                                     GlobalSignals signals) const {
        const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
        const auto result = getRankedFrameRates(layers, signals);
        return std::make_pair(ranking, consideredSignals);
        return std::make_pair(result.ranking, result.consideredSignals);
    }
    }


    FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
    FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
@@ -1343,7 +1344,7 @@ TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
    auto selector = createSelector(kModes_60_90, kModeId60);
    auto selector = createSelector(kModes_60_90, kModeId60);


    auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
    auto [refreshRates, signals, _] = selector.getRankedFrameRates({}, {});
    EXPECT_FALSE(signals.powerOnImminent);
    EXPECT_FALSE(signals.powerOnImminent);


    auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
    auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
@@ -1427,10 +1428,32 @@ TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
    }
    }
}
}


TEST_P(RefreshRateSelectorTest, pacesetterConsidered) {
    auto selector = createSelector(kModes_60_90, kModeId60);
    constexpr RefreshRateSelector::GlobalSignals kNoSignals;

    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
    layers[0].vote = LayerVoteType::Min;

    // The pacesetterFps takes precedence over the LayerRequirement.
    {
        const auto result = selector.getRankedFrameRates(layers, {}, 90_Hz);
        EXPECT_EQ(kMode90, result.ranking.front().frameRateMode.modePtr);
        EXPECT_EQ(kNoSignals, result.consideredSignals);
    }

    // The pacesetterFps takes precedence over GlobalSignals.
    {
        const auto result = selector.getRankedFrameRates(layers, {.touch = true}, 60_Hz);
        EXPECT_EQ(kMode60, result.ranking.front().frameRateMode.modePtr);
        EXPECT_EQ(kNoSignals, result.consideredSignals);
    }
}

TEST_P(RefreshRateSelectorTest, touchConsidered) {
TEST_P(RefreshRateSelectorTest, touchConsidered) {
    auto selector = createSelector(kModes_60_90, kModeId60);
    auto selector = createSelector(kModes_60_90, kModeId60);


    auto [_, signals] = selector.getRankedFrameRates({}, {});
    auto signals = selector.getRankedFrameRates({}, {}).consideredSignals;
    EXPECT_FALSE(signals.touch);
    EXPECT_FALSE(signals.touch);


    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -1964,7 +1987,7 @@ TEST_P(RefreshRateSelectorTest,
    lr.name = "60Hz ExplicitDefault";
    lr.name = "60Hz ExplicitDefault";
    lr.focused = true;
    lr.focused = true;


    const auto [rankedFrameRate, signals] =
    const auto [rankedFrameRate, signals, _] =
            selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
            selector.getRankedFrameRates(layers, {.touch = true, .idle = true});


    EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
    EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
@@ -2188,7 +2211,7 @@ TEST_P(RefreshRateSelectorTest,
    EXPECT_EQ(SetPolicyResult::Changed,
    EXPECT_EQ(SetPolicyResult::Changed,
              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));


    const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
    const auto [ranking, signals, _] = selector.getRankedFrameRates({}, {});
    EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
    EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
    EXPECT_FALSE(signals.touch);
    EXPECT_FALSE(signals.touch);


@@ -2572,7 +2595,7 @@ TEST_P(RefreshRateSelectorTest, idle) {
        layers[0].vote = voteType;
        layers[0].vote = voteType;
        layers[0].desiredRefreshRate = 90_Hz;
        layers[0].desiredRefreshRate = 90_Hz;


        const auto [ranking, signals] =
        const auto [ranking, signals, _] =
                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});


        // Refresh rate will be chosen by either touch state or idle state.
        // Refresh rate will be chosen by either touch state or idle state.
@@ -2722,16 +2745,17 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);


    using GlobalSignals = RefreshRateSelector::GlobalSignals;
    using GlobalSignals = RefreshRateSelector::GlobalSignals;
    const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                     GlobalSignals{.touch = true, .idle = true});

    const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
    const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
                                                                  {90_Hz, kMode90}}},
                                                                  {90_Hz, kMode90}}},
                                                          GlobalSignals{.touch = true}};
                                                          GlobalSignals{.touch = true}};


    selector.mutableGetRankedRefreshRatesCache() = {args, result};
    selector.mutableGetRankedRefreshRatesCache() = {.layers = std::vector<LayerRequirement>{},
                                                    .signals = GlobalSignals{.touch = true,
                                                                             .idle = true},
                                                    .result = result};


    EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
    const auto& cache = *selector.mutableGetRankedRefreshRatesCache();
    EXPECT_EQ(result, selector.getRankedFrameRates(cache.layers, cache.signals));
}
}


TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
@@ -2739,15 +2763,18 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {


    EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
    EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());


    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
    const std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
    RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
    const RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
    const Fps pacesetterFps = 60_Hz;


    const auto result = selector.getRankedFrameRates(layers, globalSignals);
    const auto result = selector.getRankedFrameRates(layers, globalSignals, pacesetterFps);


    const auto& cache = selector.mutableGetRankedRefreshRatesCache();
    const auto& cache = selector.mutableGetRankedRefreshRatesCache();
    ASSERT_TRUE(cache);
    ASSERT_TRUE(cache);


    EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
    EXPECT_EQ(cache->layers, layers);
    EXPECT_EQ(cache->signals, globalSignals);
    EXPECT_EQ(cache->pacesetterFps, pacesetterFps);
    EXPECT_EQ(cache->result, result);
    EXPECT_EQ(cache->result, result);
}
}


@@ -3674,7 +3701,7 @@ TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
        layers[0].vote = voteType;
        layers[0].vote = voteType;
        layers[0].desiredRefreshRate = 90_Hz;
        layers[0].desiredRefreshRate = 90_Hz;


        const auto [ranking, signals] =
        const auto [ranking, signals, _] =
                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});


        // Refresh rate will be chosen by either touch state or idle state.
        // Refresh rate will be chosen by either touch state or idle state.
Loading