SF: Implement leader display promotion/demotion
Remove Scheduler::mRefreshRateSelector by using mLeaderDisplayId to look
up the leader's RefreshRateSelector. Replace setRefreshRateSelector with
setLeaderDisplay. Add logic to demote/promote the leader display (always
the primary display for now) when displays are added/changed/removed.
This fixes a regression where Scheduler::applyPolicy no longer respected
the active display after Ifa1bf23bc991fe60e67dba1cb31077e42fd5396e.
Bug: 255635821
Bug: 241286431
Test: Fold/unfold
Test: dumpsys SurfaceFlinger --scheduler
Change-Id: Ib1e8eacd2cdbf0d798ff7e8bb9c25607972104f5
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6108d92..f1fcc88 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -70,7 +70,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- setRefreshRateSelector(nullptr);
+ demoteLeaderDisplay();
}
void Scheduler::startTimers() {
@@ -95,40 +95,29 @@
}
}
-void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) {
- // No need to lock for reads on kMainThreadContext.
- if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) {
- unbindIdleTimer(*selectorPtr);
- }
+void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ demoteLeaderDisplay();
- {
- // Clear state that depends on the current RefreshRateSelector.
- std::scoped_lock lock(mPolicyLock);
- mPolicy = {};
- }
-
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector = std::move(newSelectorPtr);
-
- if (mRefreshRateSelector) {
- bindIdleTimer(*mRefreshRateSelector);
- }
+ std::scoped_lock lock(mDisplayLock);
+ promoteLeaderDisplay(leaderIdOpt);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
- if (!mLeaderDisplayId) {
- mLeaderDisplayId = displayId;
- }
+ demoteLeaderDisplay();
+ std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+
+ promoteLeaderDisplay();
}
void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
- if (mLeaderDisplayId == displayId) {
- mLeaderDisplayId.reset();
- }
+ demoteLeaderDisplay();
+ std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.erase(displayId);
+
+ promoteLeaderDisplay();
}
void Scheduler::run() {
@@ -163,7 +152,7 @@
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -178,8 +167,6 @@
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- std::scoped_lock lock(mRefreshRateSelectorLock);
-
return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
};
@@ -187,7 +174,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
@@ -281,7 +268,7 @@
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -322,8 +309,7 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) {
+ if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
return;
}
@@ -416,10 +402,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- const auto refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector->getActiveModePtr()->getFps();
- }();
+ const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
resyncToHardwareVsync(false, refreshRate);
}
}
@@ -478,12 +461,9 @@
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- if (!mRefreshRateSelector->canSwitch()) return;
+ if (leaderSelectorPtr()->canSwitch()) {
+ mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
-
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
void Scheduler::setModeChangePending(bool pending) {
@@ -496,7 +476,7 @@
}
void Scheduler::chooseRefreshRateForContent() {
- const auto selectorPtr = holdRefreshRateSelector();
+ const auto selectorPtr = leaderSelectorPtr();
if (!selectorPtr->canSwitch()) return;
ATRACE_CALL();
@@ -506,16 +486,13 @@
}
void Scheduler::resetIdleTimer() {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false);
+ leaderSelectorPtr()->resetIdleTimer();
}
void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
-
- std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true);
+ leaderSelectorPtr()->resetKernelIdleTimer();
}
}
@@ -535,30 +512,12 @@
mLayerHistory.clear();
}
-void Scheduler::bindIdleTimer(RefreshRateSelector& selector) {
- selector.setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
- selector.startIdleTimer();
-}
-
-void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) {
- selector.stopIdleTimer();
- selector.clearIdleTimerCallbacks();
-}
-
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateSelectorLock);
- return mRefreshRateSelector->getActiveModePtr()->getFps();
- }();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -614,7 +573,11 @@
}
{
utils::Dumper::Section section(dumper, "Policy"sv);
-
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+ }
dumper.dump("layerHistory"sv, mLayerHistory.dump());
dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
@@ -638,17 +601,44 @@
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- // we always update mFrameRateOverridesByContent here
- // supportsFrameRateOverridesByContent will be checked
- // when getting FrameRateOverrides from mFrameRateOverrideMappings
- if (!consideredSignals.idle) {
- const auto frameRateOverrides =
- holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements,
- displayRefreshRate,
- consideredSignals);
- return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+ if (consideredSignals.idle) return false;
+
+ const auto frameRateOverrides =
+ leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
+
+ // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+ // the FrameRateOverrideMappings rather than here.
+ return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ // TODO(b/241286431): Choose the leader display.
+ mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+
+ if (const auto leaderPtr = leaderSelectorPtrLocked()) {
+ leaderPtr->setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired =
+ [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+ leaderPtr->startIdleTimer();
}
- return false;
+}
+
+void Scheduler::demoteLeaderDisplay() {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
+ leaderPtr->stopIdleTimer();
+ leaderPtr->clearIdleTimerCallbacks();
+ }
+
+ // Clear state that depends on the leader's RefreshRateSelector.
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
}
template <typename S, typename T>
@@ -660,23 +650,29 @@
bool frameRateOverridesChanged;
{
- std::lock_guard<std::mutex> lock(mPolicyLock);
+ std::scoped_lock lock(mPolicyLock);
auto& currentState = mPolicy.*statePtr;
if (currentState == newState) return {};
currentState = std::forward<T>(newState);
- auto modeChoices = chooseDisplayModes();
-
- // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go
- // through. Fix this by tracking per-display Scheduler::Policy and timers.
+ DisplayModeChoiceMap modeChoices;
DisplayModePtr modePtr;
- std::tie(modePtr, consideredSignals) =
- modeChoices.get(*mLeaderDisplayId)
- .transform([](const DisplayModeChoice& choice) {
- return std::make_pair(choice.modePtr, choice.consideredSignals);
- })
- .value();
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ modeChoices = chooseDisplayModes();
+
+ // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
+ // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+ std::tie(modePtr, consideredSignals) =
+ modeChoices.get(*mLeaderDisplayId)
+ .transform([](const DisplayModeChoice& choice) {
+ return std::make_pair(choice.modePtr, choice.consideredSignals);
+ })
+ .value();
+ }
modeRequests.reserve(modeChoices.size());
for (auto& [id, choice] : modeChoices) {
@@ -807,7 +803,7 @@
// Make sure the stored mode is up to date.
if (mPolicy.mode) {
const auto ranking =
- holdRefreshRateSelector()
+ leaderSelectorPtr()
->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
.ranking;