SF: Updating content FPS tracking
1) Each time SF creates a layer, register it with Scheduler and return handle
2) BufferQueueLayer and BufferStateLayer can now send information about buffers
for given layers via layer handle.
Algorithm for detecting content fps:
1) Keep the refresh rate per layer (explicit timestamp, or 0).
2) Keep information about last 10 present or update timestamps. This will be an
indicator for precedence.
3) Choose the MAX refresh rate among last updated layers.
For more info see go/surface-flinger-scheduler and
go/content-fps-detection-in-scheduler
Test: Updating unit tests. Systrace.
Change-Id: I988a7a79e9a9f0f61674c9b637c5142db3336177
Bug: 127727337
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ef3dfef..bebfa6c 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -125,7 +125,7 @@
}
void setupScheduler() {
- mScheduler = new TestableScheduler();
+ mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9bf29a2..d83f1bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -179,7 +179,7 @@
}
void DisplayTransactionTest::setupScheduler() {
- mScheduler = new TestableScheduler();
+ mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 3fb1a6e..e51121f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -7,6 +7,7 @@
#include <log/log.h>
#include <mutex>
+#include <thread>
#include "Scheduler/LayerHistory.h"
@@ -14,6 +15,7 @@
using testing::Return;
namespace android {
+namespace scheduler {
class LayerHistoryTest : public testing::Test {
public:
@@ -22,6 +24,8 @@
protected:
std::unique_ptr<LayerHistory> mLayerHistory;
+
+ static constexpr float MAX_REFRESH_RATE = 90.f;
};
LayerHistoryTest::LayerHistoryTest() {
@@ -30,145 +34,79 @@
LayerHistoryTest::~LayerHistoryTest() {}
namespace {
-TEST_F(LayerHistoryTest, simpleInsertAndGet) {
- mLayerHistory->insert("TestLayer", 0);
+TEST_F(LayerHistoryTest, oneLayer) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
- const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap.size());
- auto element = testMap.find("TestLayer");
- EXPECT_EQ("TestLayer", element->first);
- EXPECT_EQ(0, element->second);
+ mLayerHistory->insert(testLayer, 0);
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRate());
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
- EXPECT_EQ(0, emptyMap.size());
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ // This is still 0, because the layer is not considered recently active if it
+ // has been present in less than 10 frames.
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRate());
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ // This should be MAX_REFRESH_RATE as we have more than 10 samples
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
}
-TEST_F(LayerHistoryTest, multipleInserts) {
- mLayerHistory->insert("TestLayer0", 0);
- mLayerHistory->insert("TestLayer1", 1);
- mLayerHistory->insert("TestLayer2", 2);
- mLayerHistory->insert("TestLayer3", 3);
+TEST_F(LayerHistoryTest, explicitTimestamp) {
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
- const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
- // Because the counter was not incremented, all elements were inserted into the first
- // container.
- EXPECT_EQ(4, testMap.size());
- auto element = testMap.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- element = testMap.find("TestLayer1");
- EXPECT_EQ("TestLayer1", element->first);
- EXPECT_EQ(1, element->second);
-
- element = testMap.find("TestLayer2");
- EXPECT_EQ("TestLayer2", element->first);
- EXPECT_EQ(2, element->second);
-
- element = testMap.find("TestLayer3");
- EXPECT_EQ("TestLayer3", element->first);
- EXPECT_EQ(3, element->second);
-
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
- EXPECT_EQ(0, emptyMap.size());
-}
-
-TEST_F(LayerHistoryTest, incrementingCounter) {
- mLayerHistory->insert("TestLayer0", 0);
- mLayerHistory->incrementCounter();
- mLayerHistory->insert("TestLayer1", 1);
- mLayerHistory->insert("TestLayer2", 2);
- mLayerHistory->incrementCounter();
- mLayerHistory->insert("TestLayer3", 3);
-
- // Because the counter was incremented, the elements were inserted into different
- // containers.
- // We expect the get method to access the slot at the current counter of the index
- // is 0.
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap1.size());
- auto element = testMap1.find("TestLayer3");
- EXPECT_EQ("TestLayer3", element->first);
- EXPECT_EQ(3, element->second);
-
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
- EXPECT_EQ(2, testMap2.size());
- element = testMap2.find("TestLayer1");
- EXPECT_EQ("TestLayer1", element->first);
- EXPECT_EQ(1, element->second);
- element = testMap2.find("TestLayer2");
- EXPECT_EQ("TestLayer2", element->first);
- EXPECT_EQ(2, element->second);
-
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
- EXPECT_EQ(1, testMap3.size());
- element = testMap3.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
- EXPECT_EQ(0, emptyMap.size());
-}
-
-TEST_F(LayerHistoryTest, clearTheMap) {
- mLayerHistory->insert("TestLayer0", 0);
-
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap1.size());
- auto element = testMap1.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- mLayerHistory->incrementCounter();
- // The array currently contains 30 elements.
- for (int i = 1; i < 30; ++i) {
- mLayerHistory->insert("TestLayer0", i);
- mLayerHistory->incrementCounter();
- }
- // Expect the map to be cleared.
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0);
- EXPECT_EQ(0, testMap2.size());
-
- mLayerHistory->insert("TestLayer30", 30);
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0);
- element = testMap3.find("TestLayer30");
- EXPECT_EQ("TestLayer30", element->first);
- EXPECT_EQ(30, element->second);
- // The original element in this location does not exist anymore.
- element = testMap3.find("TestLayer0");
- EXPECT_EQ(testMap3.end(), element);
-}
-
-TEST_F(LayerHistoryTest, testingGet) {
- // The array currently contains 30 elements.
- for (int i = 0; i < 30; ++i) {
- const auto name = "TestLayer" + std::to_string(i);
- mLayerHistory->insert(name, i);
- mLayerHistory->incrementCounter();
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 31; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
}
- // The counter should be set to 0, and the map at 0 should be cleared.
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(0, testMap1.size());
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRate());
+}
- // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
- EXPECT_EQ(1, testMap2.size());
- auto element = testMap2.find("TestLayer27");
- EXPECT_EQ("TestLayer27", element->first);
- EXPECT_EQ(27, element->second);
+TEST_F(LayerHistoryTest, multipleLayers) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
+ mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE);
- // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
- // so requesting element 40 would be the same as requesting element 10.
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
- EXPECT_EQ(1, testMap3.size());
- element = testMap3.find("TestLayer20");
- EXPECT_EQ("TestLayer20", element->first);
- EXPECT_EQ(20, element->second);
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(testLayer, 0);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ for (int i = 10; i < 30; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ // This frame is only around for 9 occurrences, so it doesn't throw
+ // anything off.
+ for (int i = 0; i < 9; i++) {
+ mLayerHistory->insert(testLayer2, 0);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+ // After 100 ms frames become obsolete.
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ // Insert the 31st frame.
+ mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333));
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRate());
}
} // namespace
+} // namespace scheduler
} // 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 ec76538..b960bdf 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -33,14 +33,17 @@
MOCK_METHOD0(requestNextVsync, void());
};
+ scheduler::RefreshRateConfigs mRefreshRateConfigs;
+
/**
* This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
* the same.
*/
class MockScheduler : public android::Scheduler {
public:
- MockScheduler(std::unique_ptr<EventThread> eventThread)
- : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
+ MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs,
+ std::unique_ptr<EventThread> eventThread)
+ : Scheduler([](bool) {}, refreshRateConfigs), mEventThread(std::move(eventThread)) {}
std::unique_ptr<EventThread> makeEventThread(
const char* /* connectionName */, DispSync* /* dispSync */,
@@ -71,7 +74,7 @@
std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
- mScheduler = std::make_unique<MockScheduler>(std::move(eventThread));
+ mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread));
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
mEventThreadConnection = new MockEventThreadConnection(mEventThread);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index dcbf973..1c397d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,13 +19,15 @@
#include <gmock/gmock.h>
#include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/Scheduler.h"
namespace android {
class TestableScheduler : public Scheduler {
public:
- TestableScheduler() : Scheduler([](bool) {}) {}
+ TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig)
+ : Scheduler([](bool) {}, refreshRateConfig) {}
// Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection
// and adds it to the list of connectins. Returns the ConnectionHandle for the
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 81235ba..74a380a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -86,7 +86,8 @@
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
+ std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
+ const scheduler::RefreshRateConfigs&) override {
// TODO: Use test-fixture controlled factory
return nullptr;
}
@@ -339,6 +340,7 @@
auto& mutableScheduler() { return mFlinger->mScheduler; }
auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
+ auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does