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

Commit 2810984f authored by Su Hong Koo's avatar Su Hong Koo Committed by Android (Google) Code Review
Browse files

Merge "SF: Migrate pacesetter display designation to display with highest refresh rate" into main

parents 7c87a532 5de27f85
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
@@ -1022,11 +1022,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);

@@ -1530,6 +1526,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.
@@ -4213,13 +4215,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);
            }
@@ -5802,6 +5800,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) {
@@ -5909,22 +5911,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());
}
@@ -8624,7 +8610,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
@@ -8635,6 +8621,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
@@ -1214,6 +1214,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