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

Commit 22f2ead8 authored by ramindani's avatar ramindani
Browse files

[SF] Refresh rate selection based on the pacesetter display

Pacesetter display makes the refresh rate selection.
Other displays follow the refresh rate of the pacesetter.
When no matching rate is found with the pacesetter,
the display selects the best scored refresh rate.

BUG: 270742628
Test: atest SchedulerTest and manual test
Change-Id: I4fded96c83089f7bdc6390eb882ca9d16121ceed
parent e469593a
Loading
Loading
Loading
Loading
+16 −62
Original line number Original line Diff line number Diff line
@@ -830,81 +830,35 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {


    using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
    using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
    display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
    display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;

    // Tallies the score of a refresh rate across `displayCount` displays.
    struct RefreshRateTally {
        explicit RefreshRateTally(float score) : score(score) {}

        float score;
        size_t displayCount = 1;
    };

    // Chosen to exceed a typical number of refresh rates across displays.
    constexpr size_t kStaticCapacity = 8;
    ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies;

    const auto globalSignals = makeGlobalSignals();
    const auto globalSignals = makeGlobalSignals();
    Fps pacesetterFps;


    for (const auto& [id, display] : mDisplays) {
    for (const auto& [id, display] : mDisplays) {
        auto rankedFrameRates =
        auto rankedFrameRates =
                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
                                                         globalSignals);
                                                         globalSignals);

        if (id == *mPacesetterDisplayId) {
        for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
            pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
            const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);

            if (!inserted) {
                auto& tally = it->second;
                tally.score += score;
                tally.displayCount++;
        }
        }
        }

        perDisplayRanking.push_back(std::move(rankedFrameRates));
        perDisplayRanking.push_back(std::move(rankedFrameRates));
    }
    }


    auto maxScoreIt = refreshRateTallies.cbegin();

    // Find the first refresh rate common to all displays.
    while (maxScoreIt != refreshRateTallies.cend() &&
           maxScoreIt->second.displayCount != mDisplays.size()) {
        ++maxScoreIt;
    }

    if (maxScoreIt != refreshRateTallies.cend()) {
        // Choose the highest refresh rate common to all displays, if any.
        for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
            const auto [fps, tally] = *it;

            if (tally.displayCount == mDisplays.size() && tally.score > maxScoreIt->second.score) {
                maxScoreIt = it;
            }
        }
    }

    const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend()
            ? std::make_optional(maxScoreIt->first)
            : std::nullopt;

    DisplayModeChoiceMap modeChoices;
    DisplayModeChoiceMap modeChoices;

    using fps_approx_ops::operator==;
    using fps_approx_ops::operator==;


    for (auto& [ranking, signals] : perDisplayRanking) {
    for (auto& [rankings, signals] : perDisplayRanking) {
        if (!chosenFps) {
        const auto chosenFrameRateMode =
            const auto& [frameRateMode, _] = ranking.front();
                ftl::find_if(rankings,
            modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
                             [&](const auto& ranking) {
                                    DisplayModeChoice{frameRateMode, signals});
                                 return ranking.frameRateMode.fps == pacesetterFps;
            continue;
                             })
        }
                        .transform([](const auto& scoredFrameRate) {
                            return scoredFrameRate.get().frameRateMode;
                        })
                        .value_or(rankings.front().frameRateMode);


        for (auto& [frameRateMode, _] : ranking) {
        modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
            if (frameRateMode.fps == *chosenFps) {
                                DisplayModeChoice{chosenFrameRateMode, signals});
                modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
                                        DisplayModeChoice{frameRateMode, signals});
                break;
            }
        }
    }
    }
    return modeChoices;
    return modeChoices;
}
}
+22 −1
Original line number Original line Diff line number Diff line
@@ -359,7 +359,8 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
        EXPECT_EQ(expectedChoices, actualChoices);
        EXPECT_EQ(expectedChoices, actualChoices);
    }
    }
    {
    {
        // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal.
        // The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
        // Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
        mScheduler
        mScheduler
                ->registerDisplay(kDisplayId3,
                ->registerDisplay(kDisplayId3,
                                  std::make_shared<RefreshRateSelector>(kDisplay3Modes,
                                  std::make_shared<RefreshRateSelector>(kDisplay3Modes,
@@ -369,6 +370,26 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
        mScheduler->replaceTouchTimer(10);
        mScheduler->replaceTouchTimer(10);
        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);


        expectedChoices = ftl::init::map<
                const PhysicalDisplayId&,
                DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
                                   globalSignals)(kDisplayId2,
                                                  FrameRateMode{120_Hz, kDisplay2Mode120},
                                                  globalSignals)(kDisplayId3,
                                                                 FrameRateMode{60_Hz,
                                                                               kDisplay3Mode60},
                                                                 globalSignals);

        const auto actualChoices = mScheduler->chooseDisplayModes();
        EXPECT_EQ(expectedChoices, actualChoices);
    }
    {
        // We should choose 60Hz despite the touch signal as pacesetter only supports 60Hz
        mScheduler->setPacesetterDisplay(kDisplayId3);
        const GlobalSignals globalSignals = {.touch = true};
        mScheduler->replaceTouchTimer(10);
        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);

        expectedChoices = ftl::init::map<
        expectedChoices = ftl::init::map<
                const PhysicalDisplayId&,
                const PhysicalDisplayId&,
                DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
                DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},