[MD] Single refresh rate selection
Selects the single refresh rate for all the
displays.
BUG: 240743471
Test: atest libsurfaceflinger_unittest
Change-Id: Ifa1bf23bc991fe60e67dba1cb31077e42fd5396e
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6d68bac..7f04a4d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -57,6 +57,39 @@
} \
} while (false)
+namespace {
+
+using android::Fps;
+using android::FpsApproxEqual;
+using android::FpsHash;
+using android::scheduler::AggregatedFpsScore;
+using android::scheduler::RefreshRateRankingsAndSignals;
+
+// Returns the aggregated score per Fps for the RefreshRateRankingsAndSignals sourced.
+auto getAggregatedScoresPerFps(
+ const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+ -> std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> {
+ std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps;
+
+ for (const auto& refreshRateRankingsAndSignal : refreshRateRankingsAndSignalsPerDisplay) {
+ const auto& refreshRateRankings = refreshRateRankingsAndSignal.refreshRateRankings;
+
+ std::for_each(refreshRateRankings.begin(), refreshRateRankings.end(), [&](const auto& it) {
+ const auto [score, result] =
+ aggregatedScoresPerFps.try_emplace(it.displayModePtr->getFps(),
+ AggregatedFpsScore{it.score,
+ /* numDisplays */ 1});
+ if (!result) { // update
+ score->second.totalScore += it.score;
+ score->second.numDisplays++;
+ }
+ });
+ }
+ return aggregatedScoresPerFps;
+}
+
+} // namespace
+
namespace android::scheduler {
Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
@@ -662,6 +695,7 @@
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
DisplayModePtr newMode;
GlobalSignals consideredSignals;
+ std::vector<DisplayModeConfig> displayModeConfigs;
bool refreshRateChanged = false;
bool frameRateOverridesChanged;
@@ -674,9 +708,27 @@
if (currentState == newState) return {};
currentState = std::forward<T>(newState);
- const auto [rankings, signals] = getRankedDisplayModes();
- newMode = rankings.front().displayModePtr;
- consideredSignals = signals;
+ displayModeConfigs = getBestDisplayModeConfigs();
+
+ // mPolicy holds the current mode, using the current mode we find out
+ // what display is currently being tracked through the policy and
+ // then find the DisplayModeConfig for that display. So that
+ // later we check if the policy mode has changed for the same display in policy.
+ // If mPolicy mode isn't available then we take the first display from the best display
+ // modes as the candidate for policy changes and frame rate overrides.
+ // TODO(b/240743786) Update the single display based assumptions and make mode changes
+ // and mPolicy per display.
+ const DisplayModeConfig& displayModeConfigForCurrentPolicy = mPolicy.mode
+ ? *std::find_if(displayModeConfigs.begin(), displayModeConfigs.end(),
+ [&](const auto& displayModeConfig) REQUIRES(mPolicyLock) {
+ return displayModeConfig.displayModePtr
+ ->getPhysicalDisplayId() ==
+ mPolicy.mode->getPhysicalDisplayId();
+ })
+ : displayModeConfigs.front();
+
+ newMode = displayModeConfigForCurrentPolicy.displayModePtr;
+ consideredSignals = displayModeConfigForCurrentPolicy.signals;
frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
if (mPolicy.mode == newMode) {
@@ -691,9 +743,7 @@
}
}
if (refreshRateChanged) {
- mSchedulerCallback.requestDisplayMode(std::move(newMode),
- consideredSignals.idle ? DisplayModeEvent::None
- : DisplayModeEvent::Changed);
+ mSchedulerCallback.requestDisplayModes(std::move(displayModeConfigs));
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -701,12 +751,68 @@
return consideredSignals;
}
-auto Scheduler::getRankedDisplayModes()
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+void Scheduler::registerDisplay(const sp<const DisplayDevice>& display) {
+ const bool ok = mDisplays.try_emplace(display->getPhysicalId(), display).second;
+ ALOGE_IF(!ok, "Duplicate display registered");
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+ mDisplays.erase(displayId);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getBestDisplayModeConfigs() const {
ATRACE_CALL();
- const auto configs = holdRefreshRateConfigs();
+ std::vector<RefreshRateRankingsAndSignals> refreshRateRankingsAndSignalsPerDisplay;
+ refreshRateRankingsAndSignalsPerDisplay.reserve(mDisplays.size());
+ const auto displayModeSelectionParams = getDisplayModeSelectionParams();
+
+ std::for_each(mDisplays.begin(), mDisplays.end(), [&](const auto& display) {
+ const auto& [refreshRateRankings, globalSignals] =
+ display.second->holdRefreshRateConfigs()
+ ->getRankedRefreshRates(displayModeSelectionParams.layerRequirements,
+ displayModeSelectionParams.globalSignals);
+ refreshRateRankingsAndSignalsPerDisplay.emplace_back(
+ RefreshRateRankingsAndSignals{refreshRateRankings, globalSignals});
+ });
+
+ // FPS and their Aggregated score.
+ std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps =
+ getAggregatedScoresPerFps(refreshRateRankingsAndSignalsPerDisplay);
+
+ Fps chosenFps = std::max_element(aggregatedScoresPerFps.begin(), aggregatedScoresPerFps.end(),
+ [](const auto& max, const auto& current) {
+ return max.second.totalScore <= current.second.totalScore;
+ })
+ ->first;
+
+ return getDisplayModeConfigsForTheChosenFps(chosenFps, refreshRateRankingsAndSignalsPerDisplay);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getDisplayModeConfigsForTheChosenFps(
+ Fps chosenFps,
+ const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+ const {
+ std::vector<DisplayModeConfig> displayModeConfigs;
+ displayModeConfigs.reserve(mDisplays.size());
+ using fps_approx_ops::operator==;
+ std::for_each(refreshRateRankingsAndSignalsPerDisplay.begin(),
+ refreshRateRankingsAndSignalsPerDisplay.end(),
+ [&](const auto& refreshRateRankingsAndSignal) {
+ for (const auto& ranking : refreshRateRankingsAndSignal.refreshRateRankings) {
+ if (ranking.displayModePtr->getFps() == chosenFps) {
+ displayModeConfigs.emplace_back(
+ DisplayModeConfig{refreshRateRankingsAndSignal.globalSignals,
+ ranking.displayModePtr});
+ break;
+ }
+ }
+ });
+ return displayModeConfigs;
+}
+
+DisplayModeSelectionParams Scheduler::getDisplayModeSelectionParams() const {
const bool powerOnImminent = mDisplayPowerTimer &&
(mPolicy.displayPowerMode != hal::PowerMode::ON ||
mPolicy.displayPowerTimer == TimerState::Reset);
@@ -715,7 +821,18 @@
.idle = mPolicy.idleTimer == TimerState::Expired,
.powerOnImminent = powerOnImminent};
- return configs->getRankedRefreshRates(mPolicy.contentRequirements, signals);
+ return {mPolicy.contentRequirements, signals};
+}
+
+auto Scheduler::getRankedDisplayModes()
+ -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+ ATRACE_CALL();
+
+ const auto configs = holdRefreshRateConfigs();
+
+ const auto displayModeSelectionParams = getDisplayModeSelectionParams();
+ return configs->getRankedRefreshRates(displayModeSelectionParams.layerRequirements,
+ displayModeSelectionParams.globalSignals);
}
DisplayModePtr Scheduler::getPreferredDisplayMode() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f567205..d6f62ca 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -24,6 +24,7 @@
#include <mutex>
#include <optional>
#include <unordered_map>
+#include <utility>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -32,9 +33,11 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <DisplayDevice.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
+#include "Display/DisplayMap.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "LayerHistory.h"
@@ -83,11 +86,22 @@
namespace scheduler {
+using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+// Config representing the DisplayMode and considered signals for the Display.
+struct DisplayModeConfig {
+ const GlobalSignals signals;
+ const DisplayModePtr displayModePtr;
+
+ DisplayModeConfig(GlobalSignals signals, DisplayModePtr displayModePtr)
+ : signals(signals), displayModePtr(std::move(displayModePtr)) {}
+};
+
struct ISchedulerCallback {
using DisplayModeEvent = scheduler::DisplayModeEvent;
virtual void setVsyncEnabled(bool) = 0;
- virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
+ virtual void requestDisplayModes(std::vector<DisplayModeConfig>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
@@ -95,6 +109,25 @@
~ISchedulerCallback() = default;
};
+// Holds the total score of the FPS and
+// number of displays the FPS is found in.
+struct AggregatedFpsScore {
+ float totalScore;
+ size_t numDisplays;
+};
+
+// Represents LayerRequirements and GlobalSignals to be considered for the display mode selection.
+struct DisplayModeSelectionParams {
+ std::vector<RefreshRateConfigs::LayerRequirement> layerRequirements;
+ GlobalSignals globalSignals;
+};
+
+// Represents the RefreshRateRankings and GlobalSignals for the selected RefreshRateRankings.
+struct RefreshRateRankingsAndSignals {
+ std::vector<RefreshRateRanking> refreshRateRankings;
+ GlobalSignals globalSignals;
+};
+
class Scheduler : android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
@@ -237,6 +270,9 @@
return mLayerHistory.getLayerFramerate(now, id);
}
+ void registerDisplay(const sp<const DisplayDevice>&);
+ void unregisterDisplay(PhysicalDisplayId);
+
private:
friend class TestableScheduler;
@@ -260,8 +296,6 @@
void setVsyncPeriod(nsecs_t period);
- using GlobalSignals = RefreshRateConfigs::GlobalSignals;
-
struct Policy;
// Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -274,6 +308,17 @@
std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedDisplayModes()
REQUIRES(mPolicyLock);
+ // Returns the best display mode per display.
+ std::vector<DisplayModeConfig> getBestDisplayModeConfigs() const REQUIRES(mPolicyLock);
+
+ // Returns the list of DisplayModeConfigs per display for the chosenFps.
+ std::vector<DisplayModeConfig> getDisplayModeConfigsForTheChosenFps(
+ Fps chosenFps, const std::vector<RefreshRateRankingsAndSignals>&) const;
+
+ // Returns the DisplayModeSelectionParams to be considered for the
+ // DisplayMode selection based on the current Policy and GlobalSignals.
+ DisplayModeSelectionParams getDisplayModeSelectionParams() const REQUIRES(mPolicyLock);
+
bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
@@ -323,6 +368,10 @@
mutable std::mutex mPolicyLock;
+ // Holds the Physical displays registered through the SurfaceFlinger, used for making
+ // the refresh rate selections.
+ display::PhysicalDisplayMap<PhysicalDisplayId, const sp<const DisplayDevice>> mDisplays;
+
struct Policy {
// Policy for choosing the display mode.
LayerHistory::Summary contentRequirements;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..2c77142 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -138,6 +138,10 @@
bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};
+struct FpsHash {
+ size_t operator()(Fps fps) const { return std::hash<nsecs_t>()(fps.getPeriodNsecs()); }
+};
+
inline std::string to_string(Fps fps) {
return base::StringPrintf("%.2f Hz", fps.getValue());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 450594d..2d29e4c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2938,7 +2938,7 @@
if (display->isPrimary()) {
mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
}
-
+ mScheduler->registerDisplay(display);
dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
}
@@ -2954,6 +2954,7 @@
releaseVirtualDisplay(display->getVirtualId());
} else {
dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
+ mScheduler->unregisterDisplay(display->getPhysicalId());
}
}
@@ -2990,6 +2991,8 @@
display->disconnect();
if (display->isVirtual()) {
releaseVirtualDisplay(display->getVirtualId());
+ } else {
+ mScheduler->unregisterDisplay(display->getPhysicalId());
}
}
@@ -3319,25 +3322,34 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayModes(
+ std::vector<scheduler::DisplayModeConfig> displayModeConfigs) {
+ if (mBootStage != BootStage::FINISHED) {
+ ALOGV("Currently in the boot stage, skipping display mode changes");
+ return;
+ }
+
+ ATRACE_CALL();
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display || mBootStage != BootStage::FINISHED) {
- return;
- }
- ATRACE_CALL();
-
- if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
- ALOGV("Skipping disallowed mode %d", mode->getId().value());
- return;
- }
-
- setDesiredActiveMode({std::move(mode), event});
+ std::for_each(displayModeConfigs.begin(), displayModeConfigs.end(),
+ [&](const auto& config) REQUIRES(mStateLock) {
+ const auto& displayModePtr = config.displayModePtr;
+ if (const auto display =
+ getDisplayDeviceLocked(displayModePtr->getPhysicalDisplayId());
+ display->refreshRateConfigs().isModeAllowed(displayModePtr->getId())) {
+ const auto event = config.signals.idle ? DisplayModeEvent::None
+ : DisplayModeEvent::Changed;
+ setDesiredActiveMode({displayModePtr, event});
+ } else {
+ ALOGV("Skipping disallowed mode %d for display %" PRId64,
+ displayModePtr->getId().value(), display->getPhysicalId().value);
+ }
+ });
}
void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3386,6 +3398,7 @@
mScheduler->createVsyncSchedule(features);
mScheduler->setRefreshRateConfigs(std::move(configs));
+ mScheduler->registerDisplay(display);
}
setVsyncEnabled(false);
mScheduler->startTimers();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 99d3736..426e788 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -631,8 +631,8 @@
// Toggles hardware VSYNC by calling into HWC.
void setVsyncEnabled(bool) override;
- // Sets the desired display mode if allowed by policy.
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
+ // Sets the desired display mode per display if allowed by policy .
+ void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
// Called when the frame rate override list changed to trigger an event.
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 72b6cc9..0b21272 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -781,7 +781,7 @@
private:
void setVsyncEnabled(bool) override {}
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+ void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..81b420c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+class FakeDisplayInjector {
+public:
+ sp<DisplayDevice> injectDefaultInternalDisplay(
+ const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+ TestableSurfaceFlinger& flinger, uint8_t port = 255u) const {
+ constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+ constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+ constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
+
+ const PhysicalDisplayId physicalDisplayId = PhysicalDisplayId::fromPort(port);
+
+ // The DisplayDevice is required to have a framebuffer (behind the
+ // ANativeWindow interface) which uses the actual hardware display
+ // size.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+ auto compositionDisplay = compositionengine::impl::
+ createDisplay(flinger.getCompositionEngine(),
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(physicalDisplayId)
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPowerAdvisor(mPowerAdvisor)
+ .build());
+
+ constexpr bool kIsPrimary = true;
+ auto injector = FakeDisplayDeviceInjector(flinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal,
+ DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
+
+ injector.setNativeWindow(mNativeWindow);
+ if (injectExtra) {
+ injectExtra(injector);
+ }
+
+ auto displayDevice = injector.inject();
+
+ Mock::VerifyAndClear(mNativeWindow.get());
+
+ return displayDevice;
+ }
+
+ sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+ PowerAdvisor* mPowerAdvisor = new PowerAdvisor();
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 53e49eb..8d2130f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -20,6 +20,7 @@
#include <mutex>
+#include "FakeDisplayInjector.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
@@ -40,6 +41,7 @@
using MockEventThread = android::mock::EventThread;
using MockLayer = android::mock::MockLayer;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
@@ -59,11 +61,13 @@
SchedulerTest();
- static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
- static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
+ static inline const DisplayModePtr kMode60_1 = createDisplayMode(DisplayModeId(0), 60_Hz);
+ static inline const DisplayModePtr kMode120_1 = createDisplayMode(DisplayModeId(1), 120_Hz);
+ static inline const DisplayModePtr kMode60_2 = createDisplayMode(DisplayModeId(2), 60_Hz);
+ static inline const DisplayModePtr kMode120_2 = createDisplayMode(DisplayModeId(3), 120_Hz);
std::shared_ptr<RefreshRateConfigs> mConfigs =
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
+ std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1), kMode60_1->getId());
mock::SchedulerCallback mSchedulerCallback;
TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
@@ -71,6 +75,7 @@
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
+ FakeDisplayInjector mFakeDisplayInjector;
TestableSurfaceFlinger mFlinger;
};
@@ -166,7 +171,7 @@
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
mScheduler->chooseRefreshRateForContent();
}
@@ -176,7 +181,8 @@
ASSERT_EQ(1u, mScheduler->layerHistorySize());
mScheduler->setRefreshRateConfigs(
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+ std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1, kMode120_1),
+ kMode60_1->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -215,12 +221,18 @@
}
MATCHER(Is120Hz, "") {
- return isApproxEqual(arg->getFps(), 120_Hz);
+ return isApproxEqual(arg.front().displayModePtr->getFps(), 120_Hz);
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
- mScheduler->setRefreshRateConfigs(
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+ auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ },
+ mFlinger);
+
+ mScheduler->registerDisplay(display);
+ mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -233,12 +245,111 @@
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
mScheduler->chooseRefreshRateForContent();
// No-op if layer requirements have not changed.
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
mScheduler->chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, getBestDisplayMode_singleDisplay) {
+ auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ },
+ mFlinger);
+ mScheduler->registerDisplay(display);
+
+ std::vector<RefreshRateConfigs::LayerRequirement> layers =
+ std::vector<RefreshRateConfigs::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+ mScheduler->setContentRequirements(layers);
+ GlobalSignals globalSignals = {.idle = true};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(1ul, displayModeConfigs.size());
+ EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode60_1);
+ EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+ globalSignals = {.idle = false};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+ displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(1ul, displayModeConfigs.size());
+ EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+ EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+ globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+ displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(1ul, displayModeConfigs.size());
+ EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+ EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+ mScheduler->unregisterDisplay(display->getPhysicalId());
+ EXPECT_TRUE(mScheduler->mutableDisplays().empty());
+}
+
+TEST_F(SchedulerTest, getBestDisplayModes_multipleDisplays) {
+ auto display1 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+ },
+ mFlinger);
+ auto display2 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(makeModes(kMode60_2, kMode120_2), kMode60_2->getId());
+ },
+ mFlinger, /* port */ 253u);
+ mScheduler->registerDisplay(display1);
+ mScheduler->registerDisplay(display2);
+
+ const std::vector<sp<DisplayDevice>>& expectedDisplays = {display1, display2};
+ std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ GlobalSignals globalSignals = {.idle = true};
+ std::vector<DisplayModeConfig> expectedConfigs = {DisplayModeConfig{globalSignals, kMode60_1},
+ DisplayModeConfig{globalSignals, kMode60_2}};
+
+ mScheduler->setContentRequirements(layers);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+ std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(displayModeConfigs.size(), expectedConfigs.size());
+ for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+ EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+ << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+ << " Actual fps "
+ << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ }
+
+ expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode120_1},
+ DisplayModeConfig{globalSignals, kMode120_2}};
+
+ globalSignals = {.idle = false};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+ displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+ for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+ EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+ << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+ << " Actual fps "
+ << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ }
+
+ globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+ displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+ ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+ for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+ EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+ << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+ << " Actual fps "
+ << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+ EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+ }
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index ec7e8a7..4e9f293 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -21,6 +21,7 @@
#include <thread>
#include "DisplayTransactionTestHelpers.h"
+#include "FakeDisplayInjector.h"
#include <android/hardware/power/Boost.h>
@@ -32,6 +33,8 @@
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
using namespace std::chrono_literals;
+ injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
+
mFlinger.scheduler()->replaceTouchTimer(100);
std::this_thread::sleep_for(10ms); // wait for callback to be triggered
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 93e3059..68df987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -68,6 +68,8 @@
auto& mutableLayerHistory() { return mLayerHistory; }
+ auto& mutableDisplays() { return mDisplays; }
+
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
}
@@ -94,6 +96,22 @@
return mPolicy.touch == Scheduler::TouchState::Active;
}
+ void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive;
+ mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
+ }
+
+ void setContentRequirements(std::vector<RefreshRateConfigs::LayerRequirement> layers) {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.contentRequirements = std::move(layers);
+ }
+
+ std::vector<DisplayModeConfig> getBestDisplayModeConfigs() {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ return Scheduler::getBestDisplayModeConfigs();
+ }
+
void dispatchCachedReportedMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
return Scheduler::dispatchCachedReportedMode();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 5338f4a..f5042d3 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -842,6 +842,9 @@
sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
+ if (mFlinger.scheduler()) {
+ mFlinger.scheduler()->registerDisplay(display);
+ }
DisplayDeviceState state;
state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 5267586..8af2dfa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -24,14 +24,14 @@
struct SchedulerCallback final : ISchedulerCallback {
MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
- MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
+ MOCK_METHOD(void, requestDisplayModes, (std::vector<scheduler::DisplayModeConfig>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
void setVsyncEnabled(bool) override {}
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+ void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
};