| /* |
| * 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 <compositionengine/impl/OutputCompositionState.h> |
| #include <compositionengine/impl/planner/CachedSet.h> |
| #include <compositionengine/impl/planner/LayerState.h> |
| #include <compositionengine/mock/LayerFE.h> |
| #include <compositionengine/mock/OutputLayer.h> |
| #include <gmock/gmock-actions.h> |
| #include <gtest/gtest.h> |
| #include <renderengine/ExternalTexture.h> |
| #include <renderengine/mock/FakeExternalTexture.h> |
| #include <renderengine/mock/RenderEngine.h> |
| #include <ui/GraphicTypes.h> |
| #include <utils/Errors.h> |
| #include <memory> |
| |
| namespace android::compositionengine { |
| using namespace std::chrono_literals; |
| |
| using testing::_; |
| using testing::DoAll; |
| using testing::Invoke; |
| using testing::Return; |
| using testing::ReturnRef; |
| using testing::SetArgPointee; |
| |
| using impl::planner::CachedSet; |
| using impl::planner::LayerState; |
| using impl::planner::LayerStateField; |
| using impl::planner::TexturePool; |
| |
| namespace { |
| |
| MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "") { |
| *result_listener << "ClientCompositionTargetSettings' BlurSettings aren't equal \n"; |
| *result_listener << "expected " << expectedBlurSetting << "\n"; |
| *result_listener << "actual " << arg.blurSetting << "\n"; |
| |
| return expectedBlurSetting == arg.blurSetting; |
| } |
| |
| MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") { |
| *result_listener << "ClientCompositionTargetSettings' isSecure bits aren't equal \n"; |
| *result_listener << "expected " << expectedSecureSetting << "\n"; |
| *result_listener << "actual " << arg.isSecure << "\n"; |
| |
| return expectedSecureSetting == arg.isSecure; |
| } |
| |
| MATCHER_P(ClientCompositionTargetSettingsWhitePointEq, expectedWhitePoint, "") { |
| *result_listener << "ClientCompositionTargetSettings' white points aren't equal \n"; |
| *result_listener << "expected " << expectedWhitePoint << "\n"; |
| *result_listener << "actual " << arg.whitePointNits << "\n"; |
| |
| return expectedWhitePoint == arg.whitePointNits; |
| } |
| |
| static const ui::Size kOutputSize = ui::Size(1, 1); |
| |
| class CachedSetTest : public testing::Test { |
| public: |
| CachedSetTest() = default; |
| void SetUp() override; |
| void TearDown() override; |
| |
| protected: |
| const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now(); |
| |
| struct TestLayer { |
| mock::OutputLayer outputLayer; |
| impl::OutputLayerCompositionState outputLayerCompositionState; |
| // LayerFE inherits from RefBase and must be held by an sp<> |
| sp<mock::LayerFE> layerFE; |
| LayerFECompositionState layerFECompositionState; |
| |
| std::unique_ptr<LayerState> layerState; |
| std::unique_ptr<CachedSet::Layer> cachedSetLayer; |
| }; |
| |
| static constexpr size_t kNumLayers = 5; |
| std::vector<std::unique_ptr<TestLayer>> mTestLayers; |
| impl::OutputCompositionState mOutputState; |
| |
| android::renderengine::mock::RenderEngine mRenderEngine; |
| TexturePool mTexturePool = TexturePool(mRenderEngine); |
| }; |
| |
| void CachedSetTest::SetUp() { |
| mTexturePool.setDisplaySize(kOutputSize); |
| for (size_t i = 0; i < kNumLayers; i++) { |
| auto testLayer = std::make_unique<TestLayer>(); |
| auto pos = static_cast<int32_t>(i); |
| testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1); |
| testLayer->outputLayerCompositionState.visibleRegion = |
| Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2)); |
| |
| testLayer->layerFE = sp<mock::LayerFE>::make(); |
| |
| EXPECT_CALL(*testLayer->layerFE, getSequence) |
| .WillRepeatedly(Return(static_cast<int32_t>(i))); |
| EXPECT_CALL(*testLayer->layerFE, getDebugName).WillRepeatedly(Return("testLayer")); |
| EXPECT_CALL(*testLayer->layerFE, getCompositionState) |
| .WillRepeatedly(Return(&testLayer->layerFECompositionState)); |
| EXPECT_CALL(testLayer->outputLayer, getLayerFE) |
| .WillRepeatedly(ReturnRef(*testLayer->layerFE)); |
| EXPECT_CALL(testLayer->outputLayer, getState) |
| .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState)); |
| |
| testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer); |
| testLayer->layerState->incrementFramesSinceBufferUpdate(); |
| testLayer->cachedSetLayer = |
| std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime); |
| |
| mTestLayers.emplace_back(std::move(testLayer)); |
| |
| // set up minimium params needed for rendering |
| mOutputState.dataspace = ui::Dataspace::SRGB; |
| mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5)); |
| mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90); |
| mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10)); |
| } |
| } |
| |
| void CachedSetTest::TearDown() { |
| mTestLayers.clear(); |
| } |
| |
| void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) { |
| EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate()); |
| EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds()); |
| EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion())); |
| EXPECT_EQ(1u, cachedSet.getLayerCount()); |
| EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState()); |
| EXPECT_EQ(0u, cachedSet.getAge()); |
| EXPECT_EQ(layer.getHash(), cachedSet.getNonBufferHash()); |
| } |
| |
| void expectEqual(const CachedSet& cachedSet, const LayerState& layerState, |
| std::chrono::steady_clock::time_point lastUpdate) { |
| CachedSet::Layer layer(&layerState, lastUpdate); |
| expectEqual(cachedSet, layer); |
| } |
| |
| void expectNoBuffer(const CachedSet& cachedSet) { |
| EXPECT_EQ(nullptr, cachedSet.getBuffer()); |
| EXPECT_EQ(nullptr, cachedSet.getDrawFence()); |
| EXPECT_FALSE(cachedSet.hasReadyBuffer()); |
| } |
| |
| void expectReadyBuffer(const CachedSet& cachedSet) { |
| EXPECT_NE(nullptr, cachedSet.getBuffer()); |
| EXPECT_NE(nullptr, cachedSet.getDrawFence()); |
| EXPECT_TRUE(cachedSet.hasReadyBuffer()); |
| EXPECT_TRUE(cachedSet.hasRenderedBuffer()); |
| } |
| |
| TEST_F(CachedSetTest, createFromLayer) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet cachedSet(layer); |
| expectEqual(cachedSet, layer); |
| expectNoBuffer(cachedSet); |
| } |
| |
| TEST_F(CachedSetTest, createFromLayerState) { |
| LayerState& layerState = *mTestLayers[0]->layerState.get(); |
| CachedSet cachedSet(&layerState, kStartTime); |
| expectEqual(cachedSet, layerState, kStartTime); |
| expectNoBuffer(cachedSet); |
| } |
| |
| TEST_F(CachedSetTest, addLayer) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); |
| EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds()); |
| Region expectedRegion; |
| expectedRegion.orSelf(Rect(1, 1, 2, 2)); |
| expectedRegion.orSelf(Rect(2, 2, 3, 3)); |
| EXPECT_TRUE(cachedSet.getVisibleRegion().hasSameRects(expectedRegion)); |
| EXPECT_EQ(2u, cachedSet.getLayerCount()); |
| EXPECT_EQ(0u, cachedSet.getAge()); |
| expectNoBuffer(cachedSet); |
| // TODO(b/181192080): check that getNonBufferHash returns the correct hash value |
| // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()), |
| // cachedSet.getNonBufferHash()); |
| } |
| |
| TEST_F(CachedSetTest, decompose) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); |
| |
| std::vector<CachedSet> decomposed = cachedSet.decompose(); |
| EXPECT_EQ(3u, decomposed.size()); |
| expectEqual(decomposed[0], *layer1.getState(), kStartTime); |
| expectNoBuffer(decomposed[0]); |
| |
| expectEqual(decomposed[1], *layer2.getState(), kStartTime + 10ms); |
| expectNoBuffer(decomposed[1]); |
| |
| expectEqual(decomposed[2], *layer3.getState(), kStartTime + 20ms); |
| expectNoBuffer(decomposed[2]); |
| } |
| |
| TEST_F(CachedSetTest, setLastUpdate) { |
| LayerState& layerState = *mTestLayers[0]->layerState.get(); |
| CachedSet cachedSet(&layerState, kStartTime); |
| cachedSet.setLastUpdate(kStartTime + 10ms); |
| expectEqual(cachedSet, layerState, kStartTime + 10ms); |
| } |
| |
| TEST_F(CachedSetTest, incrementAge) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet cachedSet(layer); |
| EXPECT_EQ(0u, cachedSet.getAge()); |
| cachedSet.incrementAge(); |
| EXPECT_EQ(1u, cachedSet.getAge()); |
| cachedSet.incrementAge(); |
| EXPECT_EQ(2u, cachedSet.getAge()); |
| } |
| |
| TEST_F(CachedSetTest, incrementSkipCount) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet cachedSet(layer); |
| EXPECT_EQ(0u, cachedSet.getSkipCount()); |
| cachedSet.incrementSkipCount(); |
| EXPECT_EQ(1u, cachedSet.getSkipCount()); |
| cachedSet.incrementSkipCount(); |
| EXPECT_EQ(2u, cachedSet.getSkipCount()); |
| } |
| |
| TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); |
| |
| EXPECT_FALSE(cachedSet.hasBufferUpdate()); |
| } |
| |
| TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); |
| |
| mTestLayers[1]->layerState->resetFramesSinceBufferUpdate(); |
| |
| EXPECT_TRUE(cachedSet.hasBufferUpdate()); |
| } |
| |
| TEST_F(CachedSetTest, append) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet1(layer1); |
| CachedSet cachedSet2(layer2); |
| cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms); |
| cachedSet1.incrementSkipCount(); |
| EXPECT_EQ(1u, cachedSet1.getSkipCount()); |
| cachedSet1.append(cachedSet2); |
| |
| EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate()); |
| EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds()); |
| Region expectedRegion; |
| expectedRegion.orSelf(Rect(1, 1, 2, 2)); |
| expectedRegion.orSelf(Rect(2, 2, 3, 3)); |
| expectedRegion.orSelf(Rect(3, 3, 4, 4)); |
| EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion)); |
| EXPECT_EQ(3u, cachedSet1.getLayerCount()); |
| EXPECT_EQ(0u, cachedSet1.getAge()); |
| EXPECT_EQ(0u, cachedSet1.getSkipCount()); |
| |
| expectNoBuffer(cachedSet1); |
| // TODO(b/181192080): check that getNonBufferHash returns the correct hash value |
| // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()), |
| // cachedSet1.getNonBufferHash()); |
| } |
| |
| TEST_F(CachedSetTest, updateAge_NoUpdate) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer); |
| cachedSet.incrementAge(); |
| EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); |
| EXPECT_EQ(1u, cachedSet.getAge()); |
| |
| cachedSet.updateAge(kStartTime + 10ms); |
| EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); |
| EXPECT_EQ(1u, cachedSet.getAge()); |
| } |
| |
| TEST_F(CachedSetTest, updateAge_BufferUpdate) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| mTestLayers[0]->layerState->resetFramesSinceBufferUpdate(); |
| |
| CachedSet cachedSet(layer); |
| cachedSet.incrementAge(); |
| EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); |
| EXPECT_EQ(1u, cachedSet.getAge()); |
| |
| cachedSet.updateAge(kStartTime + 10ms); |
| EXPECT_EQ(kStartTime + 10ms, cachedSet.getLastUpdate()); |
| EXPECT_EQ(0u, cachedSet.getAge()); |
| } |
| |
| TEST_F(CachedSetTest, renderUnsecureOutput) { |
| // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) |
| CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; |
| CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(CachedSet(layer2)); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| clientComp1->alpha = 0.5f; |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| clientComp2->alpha = 0.75f; |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); |
| EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); |
| EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), |
| displaySettings.orientation); |
| EXPECT_EQ(0.5f, layers[0].alpha); |
| EXPECT_EQ(0.75f, layers[1].alpha); |
| EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false))) |
| .WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false))) |
| .WillOnce(Return(clientComp2)); |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| mOutputState.isSecure = false; |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| expectReadyBuffer(cachedSet); |
| |
| EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); |
| EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds()); |
| |
| // Now check that appending a new cached set properly cleans up RenderEngine resources. |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| cachedSet.append(CachedSet(layer3)); |
| } |
| |
| TEST_F(CachedSetTest, renderSecureOutput) { |
| // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) |
| CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; |
| CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(CachedSet(layer2)); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| clientComp1->alpha = 0.5f; |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| clientComp2->alpha = 0.75f; |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); |
| EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); |
| EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), |
| displaySettings.orientation); |
| EXPECT_EQ(0.5f, layers[0].alpha); |
| EXPECT_EQ(0.75f, layers[1].alpha); |
| EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); |
| |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true))) |
| .WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true))) |
| .WillOnce(Return(clientComp2)); |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| mOutputState.isSecure = true; |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| expectReadyBuffer(cachedSet); |
| |
| EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); |
| EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds()); |
| |
| // Now check that appending a new cached set properly cleans up RenderEngine resources. |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| cachedSet.append(CachedSet(layer3)); |
| } |
| |
| TEST_F(CachedSetTest, renderWhitePoint) { |
| // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) |
| CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; |
| CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(CachedSet(layer2)); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| |
| mOutputState.displayBrightnessNits = 400.f; |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, |
| const std::vector<renderengine::LayerSettings>&, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(*layerFE1, |
| prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( |
| mOutputState.displayBrightnessNits))) |
| .WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, |
| prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( |
| mOutputState.displayBrightnessNits))) |
| .WillOnce(Return(clientComp2)); |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| mOutputState.isSecure = true; |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| expectReadyBuffer(cachedSet); |
| |
| EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); |
| EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds()); |
| |
| // Now check that appending a new cached set properly cleans up RenderEngine resources. |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| cachedSet.append(CachedSet(layer3)); |
| } |
| |
| TEST_F(CachedSetTest, renderWhitePointNoColorTransform) { |
| // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) |
| // This is a duplicate of the "renderWhitePoint" test, but setting "deviceHandlesColorTransform" |
| // to false, in the render call. |
| |
| CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; |
| CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(CachedSet(layer2)); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| |
| mOutputState.displayBrightnessNits = 400.f; |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, |
| const std::vector<renderengine::LayerSettings>&, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(*layerFE1, |
| prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( |
| mOutputState.displayBrightnessNits))) |
| .WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, |
| prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( |
| mOutputState.displayBrightnessNits))) |
| .WillOnce(Return(clientComp2)); |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| mOutputState.isSecure = true; |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false); |
| expectReadyBuffer(cachedSet); |
| |
| EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); |
| EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds()); |
| |
| // Now check that appending a new cached set properly cleans up RenderEngine resources. |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| cachedSet.append(CachedSet(layer3)); |
| } |
| |
| TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { |
| // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) |
| CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; |
| CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(CachedSet(layer2)); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| clientComp1->alpha = 0.5f; |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| clientComp2->alpha = 0.75f; |
| |
| mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5)); |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); |
| EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); |
| EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), |
| displaySettings.orientation); |
| EXPECT_EQ(0.5f, layers[0].alpha); |
| EXPECT_EQ(0.75f, layers[1].alpha); |
| EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); |
| |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| expectReadyBuffer(cachedSet); |
| |
| EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); |
| |
| // Now check that appending a new cached set properly cleans up RenderEngine resources. |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| cachedSet.append(CachedSet(layer3)); |
| } |
| |
| TEST_F(CachedSetTest, cachingHintIncludesLayersByDefault) { |
| CachedSet cachedSet(*mTestLayers[0]->cachedSetLayer.get()); |
| EXPECT_FALSE(cachedSet.cachingHintExcludesLayers()); |
| } |
| |
| TEST_F(CachedSetTest, cachingHintExcludesLayersWhenDisabled) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| mTestLayers[0]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled; |
| mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); |
| |
| CachedSet cachedSet(layer1); |
| EXPECT_TRUE(cachedSet.cachingHintExcludesLayers()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresBuffer) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| EXPECT_CALL(*layerFE1, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| |
| CachedSet cachedSet(layer1); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresSingleLayer) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.append(layer2); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresNonHdr) { |
| mTestLayers[0]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_PQ; |
| mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); |
| |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresNonBT601_625) { |
| mTestLayers[0]->outputLayerCompositionState.dataspace = ui::Dataspace::STANDARD_BT601_625; |
| mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); |
| |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresNonHdrWithExtendedBrightness) { |
| const auto dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_DCI_P3 | |
| ui::Dataspace::TRANSFER_SRGB | |
| ui::Dataspace::RANGE_EXTENDED); |
| mTestLayers[0]->outputLayerCompositionState.dataspace = dataspace; |
| mTestLayers[0]->layerFECompositionState.currentHdrSdrRatio = 5.f; |
| mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); |
| |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresNoBlending) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::PREMULTIPLIED; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, requiresHolePunch) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_TRUE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) { |
| CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; |
| auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; |
| layerFECompositionState.buffer = sp<GraphicBuffer>::make(); |
| layerFECompositionState.blendMode = hal::BlendMode::NONE; |
| layerFECompositionState.forceClientComposition = true; |
| |
| CachedSet cachedSet(layer); |
| EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); |
| |
| EXPECT_FALSE(cachedSet.requiresHolePunch()); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch_requiresOverlap) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, true); |
| |
| ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer()); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch_requiresOpaque) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| mTestLayers[0]->layerFECompositionState.isOpaque = false; |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, false); |
| |
| ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer()); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch_opaque) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| mTestLayers[0]->layerFECompositionState.isOpaque = true; |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, false); |
| |
| ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch_firstLayer) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| mTestLayers[0]->layerFECompositionState.isOpaque = false; |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, true); |
| |
| ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; |
| |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; |
| |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, true); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; |
| clientComp3.emplace(); |
| |
| clientComp3->source.buffer.buffer = |
| std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, |
| 1ULL /* bufferId */, |
| HAL_PIXEL_FORMAT_RGBA_8888, |
| 0ULL /*usage*/); |
| |
| EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); |
| EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3)); |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings&, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| // If the highlight layer is enabled, it will increase the size by 1. |
| // We're interested in the third layer either way. |
| EXPECT_GE(layers.size(), 4u); |
| { |
| const auto holePunchSettings = layers[3]; |
| EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer); |
| EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor); |
| EXPECT_TRUE(holePunchSettings.disableBlending); |
| EXPECT_EQ(0.0f, holePunchSettings.alpha); |
| } |
| |
| { |
| const auto holePunchBackgroundSettings = layers[0]; |
| EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer); |
| EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor); |
| EXPECT_FALSE(holePunchBackgroundSettings.disableBlending); |
| EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha); |
| } |
| |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| } |
| |
| TEST_F(CachedSetTest, addHolePunch_noBuffer) { |
| // Same as addHolePunch, except that clientComp3 does not contain a |
| // buffer. This imitates the case where the buffer had protected content, so |
| // BufferLayer did not add it to the LayerSettings. This should not assert. |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; |
| |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; |
| |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, true); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; |
| clientComp3.emplace(); |
| |
| EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); |
| EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3)); |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings&, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| // If the highlight layer is enabled, it will increase the size by 1. |
| // We're interested in the third layer either way. |
| EXPECT_GE(layers.size(), 4u); |
| |
| { |
| const auto holePunchSettings = layers[3]; |
| EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer); |
| EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor); |
| EXPECT_TRUE(holePunchSettings.disableBlending); |
| EXPECT_EQ(0.0f, holePunchSettings.alpha); |
| } |
| |
| { |
| const auto holePunchBackgroundSettings = layers[0]; |
| EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer); |
| EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor); |
| EXPECT_FALSE(holePunchBackgroundSettings.disableBlending); |
| EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha); |
| } |
| |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| } |
| |
| TEST_F(CachedSetTest, append_removesHolePunch) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| mTestLayers[0]->layerFECompositionState.isOpaque = true; |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, false); |
| |
| ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); |
| |
| CachedSet cachedSet3(layer3); |
| cachedSet.append(cachedSet3); |
| ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer()); |
| } |
| |
| TEST_F(CachedSetTest, decompose_removesHolePunch) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addHolePunchLayerIfFeasible(layer3, true); |
| |
| ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); |
| |
| std::vector<CachedSet> decomposed = cachedSet.decompose(); |
| EXPECT_EQ(2u, decomposed.size()); |
| for (const auto& set : decomposed) { |
| EXPECT_EQ(nullptr, set.getHolePunchLayer()); |
| } |
| } |
| |
| TEST_F(CachedSetTest, hasBlurBehind) { |
| mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1; |
| mTestLayers[1]->layerState->update(&mTestLayers[1]->outputLayer); |
| mTestLayers[2]->layerFECompositionState.blurRegions.push_back( |
| BlurRegion{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}); |
| mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer); |
| |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| |
| CachedSet cachedSet1(layer1); |
| CachedSet cachedSet2(layer2); |
| CachedSet cachedSet3(layer3); |
| |
| // Cached set 4 will consist of layers 1 and 2, which will contain a blur behind |
| CachedSet cachedSet4(layer1); |
| cachedSet4.addLayer(layer2.getState(), kStartTime); |
| |
| EXPECT_FALSE(cachedSet1.hasBlurBehind()); |
| EXPECT_TRUE(cachedSet2.hasBlurBehind()); |
| EXPECT_TRUE(cachedSet3.hasBlurBehind()); |
| EXPECT_TRUE(cachedSet4.hasBlurBehind()); |
| } |
| |
| TEST_F(CachedSetTest, addBackgroundBlurLayer) { |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| CachedSet cachedSet(layer1); |
| |
| EXPECT_EQ(nullptr, cachedSet.getBlurLayer()); |
| |
| cachedSet.addBackgroundBlurLayer(layer2); |
| EXPECT_EQ(layer2.getState()->getOutputLayer(), cachedSet.getBlurLayer()); |
| } |
| |
| TEST_F(CachedSetTest, addBlur) { |
| mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); |
| CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; |
| |
| CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; |
| |
| CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); |
| sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE; |
| |
| CachedSet cachedSet(layer1); |
| cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); |
| |
| cachedSet.addBackgroundBlurLayer(layer3); |
| |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp1; |
| clientComp1.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp2; |
| clientComp2.emplace(); |
| std::optional<compositionengine::LayerFE::LayerSettings> clientComp3; |
| clientComp3.emplace(); |
| |
| clientComp3->source.buffer.buffer = |
| std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, |
| 1ULL /* bufferId */, |
| HAL_PIXEL_FORMAT_RGBA_8888, |
| 0ULL /*usage*/); |
| |
| EXPECT_CALL(*layerFE1, |
| prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( |
| compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: |
| Enabled))) |
| .WillOnce(Return(clientComp1)); |
| EXPECT_CALL(*layerFE2, |
| prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( |
| compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: |
| Enabled))) |
| .WillOnce(Return(clientComp2)); |
| EXPECT_CALL(*layerFE3, |
| prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq( |
| compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting:: |
| BackgroundBlurOnly))) |
| .WillOnce(Return(clientComp3)); |
| |
| const auto drawLayers = [&](const renderengine::DisplaySettings&, |
| const std::vector<renderengine::LayerSettings>& layers, |
| const std::shared_ptr<renderengine::ExternalTexture>&, |
| base::unique_fd&&) -> ftl::Future<FenceResult> { |
| // If the highlight layer is enabled, it will increase the size by 1. |
| // We're interested in the third layer either way. |
| EXPECT_GE(layers.size(), 3u); |
| const auto blurSettings = layers[2]; |
| EXPECT_TRUE(blurSettings.skipContentDraw); |
| EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor); |
| EXPECT_EQ(0.0f, blurSettings.alpha); |
| |
| return ftl::yield<FenceResult>(Fence::NO_FENCE); |
| }; |
| |
| EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); |
| cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); |
| } |
| |
| } // namespace |
| } // namespace android::compositionengine |