Merge "Rename crop_legacy to just crop" into sc-dev
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index d297d74..08147ed 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -106,6 +106,7 @@
"tests/planner/CachedSetTest.cpp",
"tests/planner/FlattenerTest.cpp",
"tests/planner/LayerStateTest.cpp",
+ "tests/planner/PredictorTest.cpp",
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 00424b2..fa87fb8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -17,15 +17,12 @@
#pragma once
#include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
#include <chrono>
namespace android {
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
namespace compositionengine::impl::planner {
std::string durationString(std::chrono::milliseconds duration);
@@ -63,7 +60,7 @@
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
size_t getAge() const { return mAge; }
- const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
const sp<Fence>& getDrawFence() const { return mDrawFence; }
NonBufferHash getNonBufferHash() const;
@@ -82,7 +79,7 @@
void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
void append(const CachedSet& other) {
- mBuffer = nullptr;
+ mTexture.setBuffer(nullptr, nullptr);
mDrawFence = nullptr;
mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -105,7 +102,32 @@
std::vector<Layer> mLayers;
Rect mBounds = Rect::EMPTY_RECT;
size_t mAge = 0;
- sp<GraphicBuffer> mBuffer;
+
+ class Texture {
+ public:
+ ~Texture() { setBuffer(nullptr, nullptr); }
+
+ void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+ if (mRE && mBuffer) {
+ mRE->unbindExternalTextureBuffer(mBuffer->getId());
+ }
+
+ mBuffer = buffer;
+ mRE = re;
+
+ if (mRE && mBuffer) {
+ mRE->cacheExternalTextureBuffer(mBuffer);
+ }
+ }
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+ private:
+ sp<GraphicBuffer> mBuffer = nullptr;
+ renderengine::RenderEngine* mRE = nullptr;
+ };
+
+ Texture mTexture;
sp<Fence> mDrawFence;
static const bool sDebugHighlighLayers;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index 422af77..fe486d3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -24,16 +24,28 @@
public:
LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
+ // Describes an approximate match between two layer stacks
struct ApproximateMatch {
bool operator==(const ApproximateMatch& other) const {
return differingIndex == other.differingIndex &&
differingFields == other.differingFields;
}
+ // The index of the single differing layer between the two stacks.
+ // This implies that only one layer is allowed to differ in an approximate match.
size_t differingIndex;
+ // Set of fields that differ for the differing layer in the approximate match.
Flags<LayerStateField> differingFields;
};
+ // Returns an approximate match when comparing this layer stack with the provided list of
+ // layers, for the purposes of scoring how closely the two layer stacks will match composition
+ // strategies.
+ //
+ // If the two layer stacks are identical, then an approximate match is still returned, but the
+ // differing fields will be empty to represent an exact match.
+ //
+ // If the two layer stacks differ by too much, then an empty optional is returned.
std::optional<ApproximateMatch> getApproximateMatch(
const std::vector<const LayerState*>& other) const;
@@ -108,6 +120,10 @@
}
friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
+ friend std::ostream& operator<<(std::ostream& os, const Plan& plan) {
+ return os << to_string(plan);
+ }
+
private:
std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
};
@@ -146,6 +162,10 @@
}
}
+ friend std::ostream& operator<<(std::ostream& os, const Type& type) {
+ return os << to_string(type);
+ }
+
Prediction(const std::vector<const LayerState*>& layers, Plan plan)
: mExampleLayerStack(layers), mPlan(std::move(plan)) {}
@@ -205,11 +225,25 @@
NonBufferHash hash;
Plan plan;
Prediction::Type type;
+
+ friend bool operator==(const PredictedPlan& lhs, const PredictedPlan& rhs) {
+ return lhs.hash == rhs.hash && lhs.plan == rhs.plan && lhs.type == rhs.type;
+ }
};
- std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
- NonBufferHash) const;
+ // Retrieves the predicted plan based on a layer stack alongside its hash.
+ //
+ // If the exact layer stack has previously been seen by the predictor, then report the plan used
+ // for that layer stack.
+ //
+ // Otherwise, try to match to the best approximate stack to retireve the most likely plan.
+ std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) const;
+ // Records a comparison between the predicted plan and the resulting plan, alongside the layer
+ // stack we used.
+ //
+ // This method is intended to help with scoring how effective the prediction engine is.
void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
@@ -275,4 +309,13 @@
mutable size_t mMissCount = 0;
};
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(Predictor::PredictedPlan plan, ::std::ostream* os) {
+ *os << "PredictedPlan {";
+ *os << "\n .hash = " << plan.hash;
+ *os << "\n .plan = " << plan.plan;
+ *os << "\n .type = " << plan.type;
+ *os << "\n}";
+}
+
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ab3fe9e..ba03655 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -126,7 +126,7 @@
}
bool CachedSet::hasReadyBuffer() const {
- return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+ return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
}
std::vector<CachedSet> CachedSet::decompose() const {
@@ -209,11 +209,12 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
base::unique_fd drawFence;
+
status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
base::unique_fd(), &drawFence);
if (result == NO_ERROR) {
- mBuffer = buffer;
+ mTexture.setBuffer(buffer, &renderEngine);
mDrawFence = new Fence(drawFence.release());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index ba5e64d..07920b8 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -50,8 +50,8 @@
return std::nullopt;
}
- // If layers are not identical, but we already have a prior approximate match,
- // the LayerStacks differ by too much, so return nothing
+ // If layers are not identical, but we already detected a prior approximate match for a
+ // previous layer, the LayerStacks differ by too much, so return nothing
if (approximateMatch) {
return std::nullopt;
}
@@ -72,6 +72,10 @@
}
}
+ if (approximateMatch) {
+ return approximateMatch;
+ }
+
// If we make it through the layer-by-layer comparison without an approximate match,
// it means that all layers were either identical or had client-composited layers in common,
// which don't affect the composition strategy, so return a successful result with
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 6d1ce4c..c33828f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -310,8 +310,14 @@
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
cachedSet.render(mRenderEngine);
expectReadyBuffer(cachedSet);
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 42bbfcc..c4bd5b3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -55,8 +55,10 @@
// TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
// mPredictor should be mocked and checked for expectations.
Predictor mPredictor;
- std::unique_ptr<Flattener> mFlattener;
+
+ // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
renderengine::mock::RenderEngine mRenderEngine;
+ std::unique_ptr<Flattener> mFlattener;
const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point mTime = kStartTime;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
new file mode 100644
index 0000000..43e119f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include "DisplayHardware/Hal.h"
+#undef LOG_TAG
+#define LOG_TAG "PredictorTest"
+
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const Rect sRectOne = Rect(1, 2, 3, 4);
+const Rect sRectTwo = Rect(4, 3, 2, 1);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const constexpr int32_t sSequenceId = 12345;
+
+void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+ const OutputLayerCompositionState& outputLayerState,
+ const LayerFECompositionState& layerFEState) {
+ EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sSequenceId));
+ EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(sDebugName.c_str()));
+ EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+}
+
+struct LayerStackTest : public testing::Test {
+ LayerStackTest() {
+ 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());
+ }
+
+ ~LayerStackTest() {
+ 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());
+ }
+};
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ mock::OutputLayer outputLayerThree;
+ mock::LayerFE layerFEThree;
+ OutputLayerCompositionState outputLayerCompositionStateThree;
+ LayerFECompositionState layerFECompositionStateThree;
+ setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree,
+ layerFECompositionStateThree);
+ LayerState layerStateThree(&outputLayerThree);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({}));
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateOne, &layerStateThree}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateOne, &layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 1;
+ expectedMatch.differingFields = LayerStateField::SourceCrop;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ .z = sZOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ .z = sZTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = buffer;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = buffer;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .visibleRegion = sRegionOne,
+ .forceClientComposition = true,
+ .displayFrame = sRectOne,
+ .sourceCrop = sFloatRectOne,
+ .dataspace = ui::Dataspace::SRGB,
+ .z = sZOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .visibleRegion = sRegionTwo,
+ .forceClientComposition = true,
+ .displayFrame = sRectTwo,
+ .sourceCrop = sFloatRectTwo,
+ .dataspace = ui::Dataspace::DISPLAY_P3,
+ .z = sZTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ LayerStack stack({&layerStateOne});
+
+ const auto match = stack.getApproximateMatch({&layerStateTwo});
+ EXPECT_TRUE(match);
+ LayerStack::ApproximateMatch expectedMatch;
+ expectedMatch.differingIndex = 0;
+ expectedMatch.differingFields = LayerStateField::None;
+ EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer = new GraphicBuffer();
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ EXPECT_TRUE(LayerStack({&layerStateOne}).getApproximateMatch({&layerStateTwo}));
+
+ LayerStack stack({&layerStateOne, &layerStateOne});
+ EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo}));
+}
+
+struct PredictionTest : public testing::Test {
+ PredictionTest() {
+ 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());
+ }
+
+ ~PredictionTest() {
+ 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());
+ }
+};
+
+TEST_F(PredictionTest, constructPrediction) {
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Prediction prediction({}, plan);
+
+ EXPECT_EQ(plan, prediction.getPlan());
+
+ // check that dump doesn't crash
+ std::string result;
+ prediction.dump(result);
+}
+
+TEST_F(PredictionTest, recordHits) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordHit(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordHit(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getHitCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getHitCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches, prediction.getHitCount(Prediction::Type::Total));
+}
+
+TEST_F(PredictionTest, recordMisses) {
+ Prediction prediction({}, {});
+
+ const constexpr uint32_t kExactMatches = 2;
+ for (uint32_t i = 0; i < kExactMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Exact);
+ }
+
+ const constexpr uint32_t kApproximateMatches = 3;
+ for (uint32_t i = 0; i < kApproximateMatches; i++) {
+ prediction.recordMiss(Prediction::Type::Approximate);
+ }
+
+ EXPECT_EQ(kExactMatches, prediction.getMissCount(Prediction::Type::Exact));
+ EXPECT_EQ(kApproximateMatches, prediction.getMissCount(Prediction::Type::Approximate));
+ EXPECT_EQ(kExactMatches + kApproximateMatches,
+ prediction.getMissCount(Prediction::Type::Total));
+}
+
+struct PredictorTest : public testing::Test {
+ PredictorTest() {
+ 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());
+ }
+
+ ~PredictorTest() {
+ 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());
+ }
+};
+
+TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullopt) {
+ Predictor predictor;
+ EXPECT_FALSE(predictor.getPredictedPlan({}, 0));
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne;
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hash = getNonBufferHash({&layerStateOne});
+
+ predictor.recordResult(std::nullopt, hash, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({}, hash);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hash, plan, Prediction::Type::Exact};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_TRUE(predictedPlan);
+ Predictor::PredictedPlan expectedPlan{hashOne, plan, Prediction::Type::Approximate};
+ EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+ mock::OutputLayer outputLayerOne;
+ mock::LayerFE layerFEOne;
+ OutputLayerCompositionState outputLayerCompositionStateOne{
+ .sourceCrop = sFloatRectOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+ layerFECompositionStateOne);
+ LayerState layerStateOne(&outputLayerOne);
+
+ mock::OutputLayer outputLayerTwo;
+ mock::LayerFE layerFETwo;
+ OutputLayerCompositionState outputLayerCompositionStateTwo{
+ .sourceCrop = sFloatRectTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ LayerState layerStateTwo(&outputLayerTwo);
+
+ Plan plan;
+ plan.addLayerType(hal::Composition::DEVICE);
+
+ Predictor predictor;
+
+ NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+ NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+ predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+ auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ ASSERT_TRUE(predictedPlan);
+ EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
+
+ Plan planTwo;
+ planTwo.addLayerType(hal::Composition::CLIENT);
+ predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
+ // Now trying to retrieve the predicted plan again returns a nullopt instead.
+ // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
+ // One of the implications around this implementation is that if we miss a prediction then we
+ // can never actually correct our mistake if we see the same layer stack again, which doesn't
+ // seem robust.
+ auto predictedPlanTwo = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+ EXPECT_FALSE(predictedPlanTwo);
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a03560b..9da9483 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -914,11 +914,6 @@
}
info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
- if (display->isPrimary()) {
- if (const auto mode = getDesiredActiveMode()) {
- info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value());
- }
- }
const auto& supportedModes = display->getSupportedModes();
info->supportedDisplayModes.clear();
@@ -1879,7 +1874,12 @@
// underestimated.
mFrameStartTime = frameStart;
}
- signalRefresh();
+
+ // Run the refresh immediately after invalidate as there is no point going thru the message
+ // queue again, and to ensure that we actually refresh the screen instead of handling
+ // other messages that were queued us already in the MessageQueue.
+ mRefreshPending = true;
+ onMessageRefresh();
}
}
@@ -3315,7 +3315,8 @@
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers)) {
+ transaction.originUid, transaction.states,
+ pendingBuffers)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
@@ -3342,7 +3343,8 @@
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers) ||
+ transaction.originUid, transaction.states,
+ pendingBuffers) ||
pendingTransactions) {
mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
@@ -3377,14 +3379,21 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+ ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
bool ready = true;
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ ATRACE_NAME("not current");
+ ready = false;
+ }
+
+ if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+ ATRACE_NAME("!isVsyncValid");
ready = false;
}
@@ -3407,15 +3416,12 @@
continue;
}
+ ATRACE_NAME(layer->getName().c_str());
+
const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
ATRACE_NAME("frameIsEarly()");
- return false;
- }
-
- if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
- ATRACE_NAME("!isVsyncValidForUid");
ready = false;
}
@@ -3424,11 +3430,11 @@
// transaction in the queue.
const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
ready = false;
}
pendingBuffers.insert(s.surface);
}
- pendingBuffers.insert(s.surface);
}
return ready;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 529c64b..3787b9d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -837,7 +837,7 @@
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);