Loading services/surfaceflinger/Scheduler/Scheduler.cpp +87 −15 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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()); Loading services/surfaceflinger/Scheduler/Scheduler.h +16 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading services/surfaceflinger/SurfaceFlinger.cpp +27 −29 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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()); } Loading Loading @@ -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 Loading @@ -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); Loading services/surfaceflinger/SurfaceFlinger.h +2 −0 Original line number Diff line number Diff line Loading @@ -1214,6 +1214,8 @@ private: const DisplayDevice& newFrontInternalDisplay) REQUIRES(mStateLock, kMainThreadContext); void onNewPacesetterDisplay() REQUIRES(mStateLock, kMainThreadContext); /* * Debugging & dumpsys */ Loading services/surfaceflinger/tests/unittests/SchedulerTest.cpp +115 −5 Original line number Diff line number Diff line Loading @@ -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 = Loading Loading @@ -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); Loading Loading @@ -678,7 +681,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } if (changePacesetter) { scheduler.setPacesetterDisplay(kDisplayId2); scheduler.designatePacesetterDisplay(kDisplayId2); } return committed; Loading Loading @@ -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) { Loading Loading @@ -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
services/surfaceflinger/Scheduler/Scheduler.cpp +87 −15 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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()); Loading
services/surfaceflinger/Scheduler/Scheduler.h +16 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading
services/surfaceflinger/SurfaceFlinger.cpp +27 −29 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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()); } Loading Loading @@ -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 Loading @@ -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); Loading
services/surfaceflinger/SurfaceFlinger.h +2 −0 Original line number Diff line number Diff line Loading @@ -1214,6 +1214,8 @@ private: const DisplayDevice& newFrontInternalDisplay) REQUIRES(mStateLock, kMainThreadContext); void onNewPacesetterDisplay() REQUIRES(mStateLock, kMainThreadContext); /* * Debugging & dumpsys */ Loading
services/surfaceflinger/tests/unittests/SchedulerTest.cpp +115 −5 Original line number Diff line number Diff line Loading @@ -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 = Loading Loading @@ -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); Loading Loading @@ -678,7 +681,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } if (changePacesetter) { scheduler.setPacesetterDisplay(kDisplayId2); scheduler.designatePacesetterDisplay(kDisplayId2); } return committed; Loading Loading @@ -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) { Loading Loading @@ -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