SF Generalization of Refresh Rates: Adding layer priority hint
Test: Device boots. Observe logs for errors.
Test: Open Swappy and Chrome in split screen, tap between apps,
SF prints the correct priority.
Test: Open Chrome, play video. SF prints correct priority.
Test: SF prints the correct priority received.
Bug: 142507166
Change-Id: I5fa8a857c950db01f42a380a72d098039badc289
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index a7c4f46..8b448ff 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -110,6 +110,9 @@
}
}
output.writeFloat(shadowRadius);
+
+ output.writeInt32(frameRateSelectionPriority);
+
return NO_ERROR;
}
@@ -188,6 +191,9 @@
listeners.emplace_back(listener, callbackIds);
}
shadowRadius = input.readFloat();
+
+ frameRateSelectionPriority = input.readInt32();
+
return NO_ERROR;
}
@@ -406,12 +412,14 @@
what |= eMetadataChanged;
metadata.merge(other.metadata);
}
-
if (other.what & eShadowRadiusChanged) {
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
-
+ if (other.what & eFrameRateSelectionPriority) {
+ what |= eFrameRateSelectionPriority;
+ frameRateSelectionPriority = other.frameRateSelectionPriority;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5e259e2..2ef33cf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1192,6 +1192,22 @@
}
SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const sp<SurfaceControl>& sc,
+ int32_t priority) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eFrameRateSelectionPriority;
+ s->frameRateSelectionPriority = priority;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
auto listener = TransactionCompletedListener::getInstance();
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index fb18639..03e91c4 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -99,6 +99,7 @@
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
eColorSpaceAgnosticChanged = 0x10'00000000,
+ eFrameRateSelectionPriority = 0x20'00000000,
};
layer_state_t()
@@ -128,7 +129,8 @@
bgColorAlpha(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
colorSpaceAgnostic(false),
- shadowRadius(0.0f) {
+ shadowRadius(0.0f),
+ frameRateSelectionPriority(-1) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -209,6 +211,9 @@
// Draws a shadow around the surface.
float shadowRadius;
+
+ // Priority of the layer assigned by Window Manager.
+ int32_t frameRateSelectionPriority;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 44f29ea..6a3f452 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -483,6 +483,9 @@
Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic);
+ // Sets information about the priority of the frame.
+ Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
+
Transaction& addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 37714f5..4b9f555 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -108,6 +108,7 @@
mCurrentState.api = -1;
mCurrentState.hasColorTransform = false;
mCurrentState.colorSpaceAgnostic = false;
+ mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
@@ -1120,6 +1121,7 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
+
bool Layer::setFlags(uint8_t flags, uint8_t mask) {
const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
if (mCurrentState.flags == newFlags) return false;
@@ -1176,6 +1178,29 @@
return true;
}
+bool Layer::setFrameRateSelectionPriority(int32_t priority) {
+ if (mCurrentState.frameRateSelectionPriority == priority) return false;
+ mCurrentState.frameRateSelectionPriority = priority;
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+int32_t Layer::getFrameRateSelectionPriority() const {
+ // Check if layer has priority set.
+ if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
+ return mDrawingState.frameRateSelectionPriority;
+ }
+ // If not, search whether its parents have it set.
+ sp<Layer> parent = getParent();
+ if (parent != nullptr) {
+ return parent->getFrameRateSelectionPriority();
+ }
+
+ return Layer::PRIORITY_UNSET;
+}
+
uint32_t Layer::getLayerStack() const {
auto p = mDrawingParent.promote();
if (p == nullptr) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 76edfa5..58a360a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -96,6 +96,7 @@
class Layer : public compositionengine::LayerFE {
static std::atomic<int32_t> sSequence;
+ static constexpr int32_t PRIORITY_UNSET = -1;
public:
mutable bool contentDirty{false};
@@ -220,6 +221,9 @@
// Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
// be rendered around the layer.
float shadowRadius;
+
+ // Priority of the layer assigned by Window Manager.
+ int32_t frameRateSelectionPriority;
};
explicit Layer(const LayerCreationArgs& args);
@@ -337,6 +341,10 @@
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
bool setShadowRadius(float shadowRadius);
+ virtual bool setFrameRateSelectionPriority(int32_t priority);
+ // If the variable is not set on the layer, it traverses up the tree to inherit the frame
+ // rate priority from its parent.
+ virtual int32_t getFrameRateSelectionPriority() const;
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
@@ -755,6 +763,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
+ friend class RefreshRateSelectionTest;
virtual void commitTransaction(const State& stateToCommit);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index cf79d9f..aaac1ec 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -48,6 +48,12 @@
return atoi(value);
}
+bool useFrameRatePriority() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.use_frame_rate_priority", value, "1");
+ return atoi(value);
+}
+
void trace(const wp<Layer>& weak, int fps) {
const auto layer = weak.promote();
if (!layer) return;
@@ -60,7 +66,8 @@
} // namespace
-LayerHistory::LayerHistory() : mTraceEnabled(traceEnabled()) {}
+LayerHistory::LayerHistory()
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
LayerHistory::~LayerHistory() = default;
void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate) {
@@ -94,16 +101,23 @@
partitionLayers(now);
// Find the maximum refresh rate among recently active layers.
- for (const auto& [layer, info] : activeLayers()) {
+ for (const auto& [activeLayer, info] : activeLayers()) {
const bool recent = info->isRecentlyActive(now);
+
if (recent || CC_UNLIKELY(mTraceEnabled)) {
const float refreshRate = info->getRefreshRate(now);
if (recent && refreshRate > maxRefreshRate) {
+ if (const auto layer = activeLayer.promote(); layer) {
+ const int32_t priority = layer->getFrameRateSelectionPriority();
+ // TODO(b/142507166): This is where the scoring algorithm should live.
+ // Layers should be organized by priority
+ ALOGD("Layer has priority: %d", priority);
+ }
maxRefreshRate = refreshRate;
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, std::round(refreshRate));
+ trace(activeLayer, std::round(refreshRate));
}
}
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index d92e5c3..188fa64 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -106,6 +106,9 @@
// Whether to emit systrace output and debug logs.
const bool mTraceEnabled;
+
+ // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+ const bool mUseFrameRatePriority;
};
} // namespace impl
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 65d02ad..46cfb21 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1747,12 +1747,6 @@
mGpuFrameMissedCount++;
}
- mScheduler->chooseRefreshRateForContent();
-
- if (performSetActiveConfig()) {
- break;
- }
-
if (frameMissed && mPropagateBackpressure) {
if ((hwcFrameMissed && !gpuFrameMissed) ||
mPropagateBackpressureClientComposition) {
@@ -1769,6 +1763,13 @@
bool refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
+ // Layers need to get updated (in the previous line) before we can use them for
+ // choosing the refresh rate.
+ mScheduler->chooseRefreshRateForContent();
+ if (performSetActiveConfig()) {
+ break;
+ }
+
updateCursorAsync();
updateInputFlinger();
@@ -3474,6 +3475,11 @@
if (what & layer_state_t::eShadowRadiusChanged) {
if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eFrameRateSelectionPriority) {
+ if (privileged && layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
+ flags |= eTraversalNeeded;
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 68adbfc..23fa940 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -49,6 +49,7 @@
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"RefreshRateConfigsTest.cpp",
+ "RefreshRateSelectionTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index f055fe7..b584838 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -64,6 +64,7 @@
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -90,6 +91,7 @@
TEST_F(LayerHistoryTest, oneHDRLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -109,6 +111,7 @@
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -130,8 +133,13 @@
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
nsecs_t time = mTime;
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
new file mode 100644
index 0000000..c23a660
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class RefreshRateSelectionTest : public testing::Test {
+public:
+ RefreshRateSelectionTest();
+ ~RefreshRateSelectionTest() override;
+
+protected:
+ static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+ static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr int32_t PRIORITY_UNSET = -1;
+
+ void setupScheduler();
+ void setupComposer(int virtualDisplayCount);
+ sp<BufferQueueLayer> createBufferQueueLayer();
+ sp<BufferStateLayer> createBufferStateLayer();
+ sp<ColorLayer> createColorLayer();
+
+ void setParent(Layer* child, Layer* parent);
+ void commitTransaction(Layer* layer);
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+
+ sp<Client> mClient;
+ sp<Layer> mParent;
+ sp<Layer> mChild;
+ sp<Layer> mGrandChild;
+};
+
+RefreshRateSelectionTest::RefreshRateSelectionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ setupScheduler();
+ setupComposer(0);
+}
+
+RefreshRateSelectionTest::~RefreshRateSelectionTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferQueueLayer(args);
+}
+
+sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+}
+
+sp<ColorLayer> RefreshRateSelectionTest::createColorLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+ LayerMetadata());
+ return new ColorLayer(args);
+}
+
+void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
+ child->setParent(parent);
+}
+
+void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
+ layer->commitTransaction(layer->getCurrentState());
+}
+
+void RefreshRateSelectionTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+ EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*primaryDispSync, getPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(primaryDispSync),
+ std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+ std::move(sfEventThread));
+}
+
+void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getCapabilities())
+ .WillOnce(Return(std::vector<IComposer::Capability>()));
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) {
+ mParent = createBufferQueueLayer();
+ mChild = createBufferQueueLayer();
+ setParent(mChild.get(), mParent.get());
+ mGrandChild = createBufferQueueLayer();
+ setParent(mGrandChild.get(), mChild.get());
+
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child has its own priority.
+ mGrandChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child inherits from his parent.
+ mChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Grandchild inherits from his grand parent.
+ mParent->setFrameRateSelectionPriority(1);
+ commitTransaction(mParent.get());
+ mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
+ mParent = createBufferStateLayer();
+ mChild = createBufferStateLayer();
+ setParent(mChild.get(), mParent.get());
+ mGrandChild = createBufferStateLayer();
+ setParent(mGrandChild.get(), mChild.get());
+
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child has its own priority.
+ mGrandChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child inherits from his parent.
+ mChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Grandchild inherits from his grand parent.
+ mParent->setFrameRateSelectionPriority(1);
+ commitTransaction(mParent.get());
+ mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnColorLayers) {
+ mParent = createColorLayer();
+ mChild = createColorLayer();
+ setParent(mChild.get(), mParent.get());
+ mGrandChild = createColorLayer();
+ setParent(mGrandChild.get(), mChild.get());
+
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child has its own priority.
+ mGrandChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Child inherits from his parent.
+ mChild->setFrameRateSelectionPriority(1);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+ // Grandchild inherits from his grand parent.
+ mParent->setFrameRateSelectionPriority(1);
+ commitTransaction(mParent.get());
+ mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mChild.get());
+ mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+ commitTransaction(mGrandChild.get());
+ ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+ ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index f375e23..1fd0e61 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -28,6 +28,7 @@
: Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
MOCK_CONST_METHOD0(getType, const char*());
+ MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
};