Merge "Allow single inactive layers to be cached if they would be used for a hole punch."
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 8ec15ed..7c2700c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -98,8 +98,8 @@
std::chrono::steady_clock::time_point now);
// A Run is a sequence of CachedSets, which is a candidate for flattening into a single
- // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
- // CachedSet
+ // CachedSet. Because it is wasteful to flatten 1 CachedSet, a run must contain more than
+ // 1 CachedSet or be used for a hole punch.
class Run {
public:
// A builder for a Run, to aid in construction
@@ -133,7 +133,13 @@
// Builds a Run instance, if a valid Run may be built.
std::optional<Run> validateAndBuild() {
- if (mLengths.size() <= 1) {
+ if (mLengths.size() == 0) {
+ return std::nullopt;
+ }
+ // Runs of length 1 which are hole punch candidates are allowed if the candidate is
+ // going to be used.
+ if (mLengths.size() == 1 &&
+ (!mHolePunchCandidate || !(mHolePunchCandidate->requiresHolePunch()))) {
return std::nullopt;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index f5cfd2f..fc9d0dd 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -659,6 +659,73 @@
EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
}
+// A test that verifies the hole puch optimization can be done on a single layer.
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+
+ // An opaque static background
+ auto& layerState0 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // a rounded updating layer
+ auto& layerState1 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState0.get(),
+ layerState1.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+ initializeOverrideBuffer(layers);
+ // Expect no cache invalidation the first time (there's no cache yet)
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
+ // exception that there would be a hole punch above it.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer0);
+
+ // This time we merge the CachedSet in and we should still have only two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+ EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+ // expect 0's peek though layer to be 1's output layer
+ const auto* peekThroughLayer0 =
+ layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+ EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
auto& layerState1 = mTestLayers[0]->layerState;