SF: Short-circuit scheduler content detection
Avoid registering, recording, partitioning, and transforming layers
for content detection in the scheduler if there is a single refresh
rate config.
Dump content detection version, and layer history stats.
Bug: 158780872
Test: No layer history on crosshatch, ARC, etc.
Test: dumpsys SurfaceFlinger --vsync
Test: libsurfaceflinger_unittest
Change-Id: Ia23a35cd6aa0cb117b19c98f5d0af8c14836430e
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 456891e..c73239b 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -149,9 +149,14 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ constexpr bool kHasMultipleConfigs = true;
mFlinger.setupScheduler(std::move(primaryDispSync),
std::make_unique<mock::EventControlThread>(),
- std::move(eventThread), std::move(sfEventThread));
+ std::move(eventThread), std::move(sfEventThread),
+ kHasMultipleConfigs);
+
+ // Layer history should be created if there are multiple configs.
+ ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cae317b..3ab22d3 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -49,6 +49,8 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index afd2b71..1705835 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -50,6 +50,8 @@
LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..382199d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -31,8 +31,10 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
using testing::_;
using testing::Return;
@@ -58,13 +60,18 @@
SchedulerTest();
~SchedulerTest() override;
- std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
- std::unique_ptr<TestableScheduler> mScheduler;
+ Hwc2::mock::Display mDisplay;
+ const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ .setVsyncPeriod(16'666'667)
+ .setConfigGroup(0)
+ .build()},
+ HwcConfigIndexType(0)};
+
+ TestableScheduler mScheduler{mConfigs, false};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- Hwc2::mock::Display mDisplay;
};
SchedulerTest::SchedulerTest() {
@@ -72,16 +79,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -93,7 +90,7 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
}
@@ -103,7 +100,6 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
@@ -111,67 +107,84 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(handle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(handle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler->getEventConnection(handle));
+ EXPECT_FALSE(mScheduler.getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+ mScheduler.onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+ mScheduler.onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+ mScheduler.dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+ mScheduler.setPhaseOffset(handle, 10);
}
TEST_F(SchedulerTest, validConnectionHandle) {
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(mConnectionHandle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+ mScheduler.onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+ mScheduler.onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+ mScheduler.dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+ mScheduler.setPhaseOffset(mConnectionHandle, 10);
static constexpr size_t kEventConnections = 5;
ON_CALL(*mEventThread, getEventThreadConnectionCount())
.WillByDefault(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
}
-} // namespace
+TEST_F(SchedulerTest, noLayerHistory) {
+ // Layer history should not be created if there is a single config.
+ ASSERT_FALSE(mScheduler.hasLayerHistory());
+
+ TestableSurfaceFlinger flinger;
+ mock::MockLayer layer(flinger.flinger());
+
+ // Content detection should be no-op.
+ mScheduler.registerLayer(&layer);
+ mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler.setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ mScheduler.chooseRefreshRateForContent();
+ EXPECT_EQ(0, mScheduler.refreshRateChangeCount());
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index b7067a6..c463294 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,41 +29,20 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ createLayerHistory(configs, useContentDetectionV2), useContentDetectionV2,
+ true) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
- size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- if (mUseContentDetectionV2) {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
- ->mLayerInfos.size();
- } else {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
- ->mLayerInfos.size();
- }
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -73,14 +52,26 @@
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto mutableLayerHistory() {
+ size_t refreshRateChangeCount() const { return mRefreshRateChangeCount; }
+
+ bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+ auto* mutableLayerHistory() {
+ LOG_ALWAYS_FATAL_IF(mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
- auto mutableLayerHistoryV2() {
+ auto* mutableLayerHistoryV2() {
+ LOG_ALWAYS_FATAL_IF(!mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
}
+ size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mUseContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
+ : mutableLayerHistory()->mLayerInfos.size();
+ }
+
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
mTouchTimer.reset();
@@ -108,9 +99,12 @@
}
private:
- void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+ void changeRefreshRate(const RefreshRate&, ConfigEvent) override { mRefreshRateChangeCount++; }
+
void repaintEverythingForHWC() override {}
void kernelTimerChanged(bool /*expired*/) override {}
+
+ size_t mRefreshRateChangeCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f630103..256e048 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -202,13 +202,20 @@
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- bool useContentDetectionV2 = false) {
+ bool hasMultipleConfigs = false) {
std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
+ .setVsyncPeriod(16'666'667)
.setConfigGroup(0)
.build()};
+ if (hasMultipleConfigs) {
+ configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
+ .setVsyncPeriod(11'111'111)
+ .setConfigGroup(0)
+ .build());
+ }
+
mFlinger->mRefreshRateConfigs = std::make_unique<
scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mFlinger->mRefreshRateStats = std::make_unique<
@@ -218,9 +225,10 @@
mFlinger->mPhaseConfiguration =
mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ constexpr bool kUseContentDetectionV2 = false;
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+ *mFlinger->mRefreshRateConfigs, kUseContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));