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

Commit 5de27f85 authored by Su Hong Koo's avatar Su Hong Koo
Browse files

SF: Migrate pacesetter display designation to display with highest refresh rate

The pacesetter display is the physical display whose vblank signals are
used to schedule global commit/composition/presentation.

Currently, the pacesetter display designation is given to the active
display, which is always a built-in display. Meaning if an external
display with higher refresh rate is connected, it will still be driven
at a slower effective rate corresponding to the pacesetter's refresh
rate.

This CL adds Scheduler::selectNewPacesetterDisplay(), which looks at
all powered-on physical displays and their refresh rates, and chooses
the display with the highest refresh rate as the new pacesetter
display. The selection logic prefers stability, and will prefer to keep the
current pacesetter display if all other displays with the highest
refresh rates are roughly equal to the current pacesetter's refresh
rate.

This CL adds a variant of Scheduler::setPacesetterDisplay() without any
argument, which uses selectNewPacesetterDisplay() to select the
pacesetter. Furthermore, if the pacesetter_selection flag is enabled,
register/unregister display will use the display from
selectNewPacesetterDisplay() over the active display that's passed in.

Flag: com.android.graphics.surfaceflinger.flags.pacesetter_selection
Bug: 389983418, 241286431
Test: Manually tested on comet + more, new unit tests
Change-Id: I0fbd12d769abfdd501551a845cabc155dce6a9f6
parent 9ff64f6a
Loading
Loading
Loading
Loading
+87 −15
Original line number Diff line number Diff line
@@ -110,11 +110,20 @@ void Scheduler::startTimers() {
    }
}

void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
bool Scheduler::designatePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterId) {
    if (FlagManager::getInstance().pacesetter_selection()) {
        pacesetterId = selectPacesetterDisplay();
    }

    // Skip an unnecessary demotion/promotion cycle if there's no work to do.
    if ((ftl::FakeGuard(mDisplayLock), mPacesetterDisplayId == *pacesetterId)) {
        return false;
    }

    constexpr PromotionParams kPromotionParams = {.toggleIdleTimer = true};

    demotePacesetterDisplay(kPromotionParams);
    promotePacesetterDisplay(pacesetterId, kPromotionParams);
    promotePacesetterDisplay(*pacesetterId, kPromotionParams);

    // Cancel the pending refresh rate change, if any, before updating the phase configuration.
    mVsyncModulator->cancelRefreshRateChange();
@@ -124,7 +133,60 @@ void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
        mVsyncConfiguration->reset();
    }

    updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);
    updatePhaseConfiguration(*pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);

    return true;
}

PhysicalDisplayId Scheduler::selectPacesetterDisplay() const {
    std::scoped_lock lock(mDisplayLock);
    return selectPacesetterDisplayLocked();
}

PhysicalDisplayId Scheduler::selectPacesetterDisplayLocked() const {
    // The first display should be the new pacesetter if none of the displays are powered on.
    const auto& [firstDisplayId, firstDisplay] = *mDisplays.begin();
    PhysicalDisplayId newPacesetterId = firstDisplayId;
    // Only assigning the actual refresh rate if the first display is powered on ensures that any
    // other powered-on display will take over the new pacesetter designation regardless of its
    // refresh rate.
    Fps newPacesetterVsyncRate = Fps::fromValue(0);
    if (firstDisplay.powerMode == hal::PowerMode::ON) {
        newPacesetterVsyncRate = firstDisplay.selectorPtr->getActiveMode().modePtr->getVsyncRate();
    }

    // Attempt to set the fastest powered-on display as the pacesetter.
    for (const auto& [id, display] : mDisplays) {
        if (display.powerMode != hal::PowerMode::ON) {
            continue;
        }

        const Fps displayVsyncRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate();
        if (isStrictlyLess(newPacesetterVsyncRate, displayVsyncRate)) {
            newPacesetterId = id;
            newPacesetterVsyncRate = displayVsyncRate;
        }
    }

    // If the current pacesetter display is powered on and its refresh rate is not too far off from
    // the newly selected pacesetter display, prefer to keep the current one to avoid churn.
    if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
        const auto& pacesetter = pacesetterOpt->get();
        if (pacesetter.powerMode == hal::PowerMode::ON) {
            const Fps currentPacesetterVsyncRate =
                    pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate();
            const float rateDiff =
                    newPacesetterVsyncRate.getValue() - currentPacesetterVsyncRate.getValue();
            constexpr float kRefreshRateEpsilon = 0.1f;

            if (rateDiff < kRefreshRateEpsilon) {
                newPacesetterId = pacesetter.displayId;
                newPacesetterVsyncRate = currentPacesetterVsyncRate;
            }
        }
    }

    return newPacesetterId;
}

PhysicalDisplayId Scheduler::getPacesetterDisplayId() const {
@@ -863,16 +925,7 @@ void Scheduler::onTouchHint() {
    }
}

void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
    const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) {
        ftl::FakeGuard guard(mDisplayLock);
        return id == mPacesetterDisplayId;
    }();
    if (isPacesetter) {
        // TODO (b/255657128): This needs to be handled per display.
        std::lock_guard<std::mutex> lock(mPolicyLock);
        mPolicy.displayPowerMode = powerMode;
    }
bool Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
    {
        std::scoped_lock lock(mDisplayLock);

@@ -883,7 +936,21 @@ void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMo
        display.powerMode = powerMode;
        display.schedulePtr->getController().setDisplayPowerMode(powerMode);
    }
    if (!isPacesetter) return;

    bool didPacesetterChange = false;
    // The power mode needs to be updated before we try to update the pacesetter.
    if (FlagManager::getInstance().pacesetter_selection()) {
        didPacesetterChange = designatePacesetterDisplay();
    }

    const bool isPacesetter = (ftl::FakeGuard(mDisplayLock), mPacesetterDisplayId == id);
    if (!isPacesetter) return didPacesetterChange;

    {
        // TODO: b/371584290 - This needs to be handled per display.
        std::scoped_lock lock(mPolicyLock);
        mPolicy.displayPowerMode = powerMode;
    }

    if (mDisplayPowerTimer) {
        mDisplayPowerTimer->reset();
@@ -892,6 +959,8 @@ void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMo
    // Display Power event will boost the refresh rate to performance.
    // Clear Layer History to get fresh FPS detection
    mLayerHistory.clear();

    return didPacesetterChange;
}

auto Scheduler::getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt) const
@@ -1090,7 +1159,10 @@ void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, Promoti

std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
        PhysicalDisplayId pacesetterId, PromotionParams params) {
    // TODO: b/241286431 - Choose the pacesetter among mDisplays.
    if (FlagManager::getInstance().pacesetter_selection()) {
        pacesetterId = selectPacesetterDisplayLocked();
    }

    mPacesetterDisplayId = pacesetterId;
    ALOGI("Display %s is the pacesetter", to_string(pacesetterId).c_str());

+16 −4
Original line number Diff line number Diff line
@@ -91,9 +91,10 @@ public:

    void startTimers();

    // TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
    void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext)
            EXCLUDES(mDisplayLock, mVsyncConfigLock);
    // Automatically selects a pacesetter display and designates if required. Returns true if a new
    // display was chosen as the pacesetter.
    bool designatePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterId = std::nullopt)
            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);

    PhysicalDisplayId getPacesetterDisplayId() const EXCLUDES(mDisplayLock);

@@ -264,7 +265,8 @@ public:
    // Indicates that touch interaction is taking place.
    void onTouchHint();

    void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext);
    // Returns true if the pacesetter display designation was changed due to power mode change.
    bool setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext);

    // TODO(b/255635821): Track this per display.
    void setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode) REQUIRES(kMainThreadContext);
@@ -499,6 +501,16 @@ private:
    void resync() override EXCLUDES(mDisplayLock);
    void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);

    // Returns the powered-on display with the highest refresh rate in |mDisplays| as the new
    // pacesetter, but does not set the display as pacesetter.
    // NOTE: If displays with highest refresh rates have roughly equal refresh rates,
    // and the current pacesetter is among them, then the current pacesetter will remain the
    // pacesetter.
    PhysicalDisplayId selectPacesetterDisplay() const REQUIRES(kMainThreadContext)
            EXCLUDES(mDisplayLock);
    PhysicalDisplayId selectPacesetterDisplayLocked() const
            REQUIRES(kMainThreadContext, mDisplayLock);

    std::unique_ptr<EventThread> mRenderEventThread;
    std::unique_ptr<EventThread> mLastCompositeEventThread;

+27 −29
Original line number Diff line number Diff line
@@ -1021,11 +1021,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
    if (FlagManager::getInstance().pacesetter_selection()) {
        // No need to trigger update for pacesetter via Scheduler::setPacesetterDisplay() as it is
        // done as part of adding the `display` in initScheduler().

        getRenderEngine().onActiveDisplaySizeChanged(findLargestFramebufferSizeLocked());
        const auto pacesetter = getPacesetterDisplayLocked();
        applyRefreshRateSelectorPolicy(pacesetter->getPhysicalId(),
                                       pacesetter->refreshRateSelector());
        onNewPacesetterDisplay();
    }
    onNewFrontInternalDisplay(nullptr, *display);

@@ -1529,6 +1525,12 @@ bool SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
    mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
                                              activeMode.modePtr->getVsyncRate(), activeMode.fps);

    if (FlagManager::getInstance().pacesetter_selection()) {
        if (mScheduler->designatePacesetterDisplay()) {
            onNewPacesetterDisplay();
        }
    }

    mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);

    // Skip for resolution changes, since the event was already emitted on setting the desired mode.
@@ -4202,13 +4204,9 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,

            if (display->getPhysicalId() == mFrontInternalDisplayId) {
                if (FlagManager::getInstance().pacesetter_selection()) {
                    mScheduler->setPacesetterDisplay(mFrontInternalDisplayId);

                    getRenderEngine().onActiveDisplaySizeChanged(
                            findLargestFramebufferSizeLocked());
                    const auto pacesetter = getPacesetterDisplayLocked();
                    applyRefreshRateSelectorPolicy(pacesetter->getPhysicalId(),
                                                   pacesetter->refreshRateSelector());
                    if (mScheduler->designatePacesetterDisplay()) {
                        onNewPacesetterDisplay();
                    }
                }
                onNewFrontInternalDisplay(nullptr, *display);
            }
@@ -5789,6 +5787,10 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
        applyOptimizationPolicy(__func__);
    }

    if (mScheduler->setDisplayPowerMode(displayId, mode)) {
        onNewPacesetterDisplay();
    }

    const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
    using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
    if (currentMode == hal::PowerMode::OFF) {
@@ -5896,22 +5898,6 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
        mScheduler->setActiveDisplayPowerModeForRefreshRateStats(mode);
    }

    mScheduler->setDisplayPowerMode(displayId, mode);
    if (FlagManager::getInstance().pacesetter_selection() &&
        mScheduler->getPacesetterDisplayId() != mFrontInternalDisplayId) {
        // TODO: b/389983418 - Update pacesetter designation inside
        // Scheduler::setDisplayPowerMode().
        mScheduler->setPacesetterDisplay(mFrontInternalDisplayId);

        // Whether or not the policy of the new pacesetter display changed while it was powered off
        // (in which case its preferred mode has already been propagated to HWC via setDesiredMode),
        // the Scheduler's emittedModeOpt must be initialized to the newly active mode, and the
        // kernel idle timer of the pacesetter display must be toggled.
        const auto pacesetter = getPacesetterDisplayLocked();
        applyRefreshRateSelectorPolicy(pacesetter->getPhysicalId(),
                                       pacesetter->refreshRateSelector());
    }

    ALOGD("Finished setting power mode %d on physical display %s", mode,
          to_string(displayId).c_str());
}
@@ -8611,7 +8597,7 @@ void SurfaceFlinger::onNewFrontInternalDisplay(const DisplayDevice* oldFrontInte

        newFrontInternalDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);

        mScheduler->setPacesetterDisplay(mFrontInternalDisplayId);
        mScheduler->designatePacesetterDisplay(mFrontInternalDisplayId);

        // Whether or not the policy of the new front internal display changed while it was powered
        // off (in which case its preferred mode has already been propagated to HWC via
@@ -8622,6 +8608,18 @@ void SurfaceFlinger::onNewFrontInternalDisplay(const DisplayDevice* oldFrontInte
    }
}

void SurfaceFlinger::onNewPacesetterDisplay() {
    SFTRACE_CALL();

    // Whether or not the policy of the new pacesetter display changed while it was powered off in
    // which case its preferred mode has already been propagated to HWC via setDesiredMode), the
    // Scheduler's emittedModeOpt must be initialized to the newly active mode, and the kernel idle
    // timer of the pacesetter display must be toggled.
    getRenderEngine().onActiveDisplaySizeChanged(findLargestFramebufferSizeLocked());
    const auto pacesetter = getPacesetterDisplayLocked();
    applyRefreshRateSelectorPolicy(pacesetter->getPhysicalId(), pacesetter->refreshRateSelector());
}

status_t SurfaceFlinger::addWindowInfosListener(const sp<IWindowInfosListener>& windowInfosListener,
                                                gui::WindowInfosListenerInfo* outInfo) {
    mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener, outInfo);
+2 −0
Original line number Diff line number Diff line
@@ -1219,6 +1219,8 @@ private:
                                   const DisplayDevice& newFrontInternalDisplay)
            REQUIRES(mStateLock, kMainThreadContext);

    void onNewPacesetterDisplay() REQUIRES(mStateLock, kMainThreadContext);

    /*
     * Debugging & dumpsys
     */
+115 −5
Original line number Diff line number Diff line
@@ -95,7 +95,10 @@ protected:
            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
    static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60point01 =
            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(2), 60.01_Hz));
    static inline const DisplayModes kDisplay2Modes =
            makeModes(kDisplay2Mode60, kDisplay2Mode120, kDisplay2Mode60point01);

    static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
    static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
@@ -613,7 +616,7 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
    }
    {
        // We should choose 60Hz despite the touch signal as pacesetter only supports 60Hz
        mScheduler->setPacesetterDisplay(kDisplayId3);
        mScheduler->designatePacesetterDisplay(kDisplayId3);
        const GlobalSignals globalSignals = {.touch = true};
        mScheduler->replaceTouchTimer(10);
        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -678,7 +681,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
            }

            if (changePacesetter) {
                scheduler.setPacesetterDisplay(kDisplayId2);
                scheduler.designatePacesetterDisplay(kDisplayId2);
            }

            return committed;
@@ -860,14 +863,15 @@ TEST_F(SchedulerTest, enablesLayerCachingTexturePoolForPacesetter) {
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
    mScheduler->registerDisplay(kDisplayId2,
                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
                                                                      kDisplay2Mode60->getId()));
                                                                      kDisplay2Mode120->getId()));
    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);

    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId2);

    EXPECT_CALL(mSchedulerCallback, enableLayerCachingTexturePool(kDisplayId1, true));
    EXPECT_CALL(mSchedulerCallback, enableLayerCachingTexturePool(kDisplayId2, false));
    mScheduler->setPacesetterDisplay(kDisplayId1);
    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::OFF);
    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);
}

TEST_F(SchedulerTest, pendingModeChangeSingleDisplay) {
@@ -1258,4 +1262,110 @@ TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) {
    EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
}

class SelectPacesetterDisplayTest : public SchedulerTest {};

TEST_F(SelectPacesetterDisplayTest, SingleDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
    SET_FLAG_FOR_TEST(flags::pacesetter_selection, true);

    constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
    mScheduler->registerDisplay(kDisplayId1,
                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
                                                                      kDisplay1Mode60->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
    mScheduler->designatePacesetterDisplay();

    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);
}

TEST_F(SelectPacesetterDisplayTest, TwoDisplaysDifferentRefreshRates)
FTL_FAKE_GUARD(kMainThreadContext) {
    SET_FLAG_FOR_TEST(flags::pacesetter_selection, true);

    constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
    mScheduler->registerDisplay(kDisplayId1,
                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
                                                                      kDisplay1Mode60->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);

    mScheduler->registerDisplay(kDisplayId2,
                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
                                                                      kDisplay2Mode120->getId()),
                                kActiveDisplayId);
    // setDisplayPowerMode() should trigger pacesetter migration to display 2.
    EXPECT_TRUE(mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON));

    // Display2 has the higher refresh rate so should be the pacesetter.
    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId2);
}

TEST_F(SelectPacesetterDisplayTest, TwoDisplaysHigherIgnoredPowerOff)
FTL_FAKE_GUARD(kMainThreadContext) {
    SET_FLAG_FOR_TEST(flags::pacesetter_selection, true);

    constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
    mScheduler->registerDisplay(kDisplayId1,
                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
                                                                      kDisplay1Mode60->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);

    mScheduler->registerDisplay(kDisplayId2,
                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
                                                                      kDisplay2Mode120->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::OFF);

    mScheduler->designatePacesetterDisplay();

    // Display2 has the higher refresh rate but is off so should not be considered.
    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);
}

TEST_F(SelectPacesetterDisplayTest, TwoDisplaysAllOffFirstUsed) FTL_FAKE_GUARD(kMainThreadContext) {
    SET_FLAG_FOR_TEST(flags::pacesetter_selection, true);

    constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
    mScheduler->registerDisplay(kDisplayId1,
                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
                                                                      kDisplay1Mode60->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::OFF);

    mScheduler->registerDisplay(kDisplayId2,
                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
                                                                      kDisplay2Mode120->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::OFF);

    mScheduler->designatePacesetterDisplay();

    // When all displays are off just use the first display as pacesetter.
    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);
}

TEST_F(SelectPacesetterDisplayTest, TwoDisplaysWithinEpsilon) FTL_FAKE_GUARD(kMainThreadContext) {
    SET_FLAG_FOR_TEST(flags::pacesetter_selection, true);

    constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
    mScheduler->registerDisplay(kDisplayId1,
                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
                                                                      kDisplay1Mode60->getId()),
                                kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);

    auto selector2 =
            std::make_shared<RefreshRateSelector>(kDisplay2Modes, kDisplay2Mode60->getId());
    mScheduler->registerDisplay(kDisplayId2, selector2, kActiveDisplayId);
    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);

    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);

    // If the highest refresh rate is within a small epsilon of the current pacesetter display's
    // refresh rate, let the current pacesetter stay.
    selector2->setActiveMode(kDisplay2Mode60point01->getId(), 60.01_Hz);
    EXPECT_EQ(mScheduler->pacesetterDisplayId(), kDisplayId1);
}

} // namespace android::scheduler
Loading