/* * Copyright 2019 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CallOrderStateMachineHelper.h" #include "MockHWC2.h" #include "RegionMatcher.h" #include "renderengine/ExternalTexture.h" namespace android::compositionengine { namespace { using testing::_; using testing::ByMove; using testing::ByRef; using testing::DoAll; using testing::ElementsAre; using testing::ElementsAreArray; using testing::Eq; using testing::InSequence; using testing::Invoke; using testing::IsEmpty; using testing::Mock; using testing::Pointee; using testing::Property; using testing::Ref; using testing::Return; using testing::ReturnRef; using testing::SetArgPointee; using testing::StrictMock; constexpr auto TR_IDENT = 0u; constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90; constexpr auto MAX_CLIENT_COMPOSITION_CACHE_SIZE = 3; const mat4 kIdentity; const mat4 kNonIdentityHalf = mat4() * 0.5f; const mat4 kNonIdentityQuarter = mat4() * 0.25f; constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting = static_cast(0x100); struct OutputPartialMockBase : public impl::Output { // compositionengine::Output overrides const OutputCompositionState& getState() const override { return mState; } OutputCompositionState& editState() override { return mState; } // Use mocks for all the remaining virtual functions // not implemented by the base implementation class. MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, compositionengine::OutputLayer*(size_t)); MOCK_METHOD2(ensureOutputLayer, compositionengine::OutputLayer*(std::optional, const sp&)); MOCK_METHOD0(finalizePendingOutputLayers, void()); MOCK_METHOD0(clearOutputLayers, void()); MOCK_CONST_METHOD1(dumpState, void(std::string&)); MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&()); MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp&)); MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr)); impl::OutputCompositionState mState; }; struct InjectedLayer { InjectedLayer() { EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get())); EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState)); EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState)); EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState)); EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0)); EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("InjectedLayer")); } mock::OutputLayer* outputLayer = {new StrictMock}; sp> layerFE = new StrictMock(); LayerFECompositionState layerFEState; impl::OutputLayerCompositionState outputLayerState; }; struct NonInjectedLayer { NonInjectedLayer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get())); EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState)); EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState)); EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState)); EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0)); EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("NonInjectedLayer")); } mock::OutputLayer outputLayer; sp> layerFE = new StrictMock(); LayerFECompositionState layerFEState; impl::OutputLayerCompositionState outputLayerState; }; struct OutputTest : public testing::Test { class Output : public impl::Output { public: using impl::Output::injectOutputLayerForTest; virtual void injectOutputLayerForTest(std::unique_ptr) = 0; }; static std::shared_ptr createOutput( const compositionengine::CompositionEngine& compositionEngine) { return impl::createOutputTemplated(compositionEngine); } OutputTest() { mOutput->setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput->setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); mOutput->editState().displaySpace.bounds = kDefaultDisplaySize; EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); } void injectOutputLayer(InjectedLayer& layer) { mOutput->injectOutputLayerForTest(std::unique_ptr(layer.outputLayer)); } void injectNullOutputLayer() { mOutput->injectOutputLayerForTest(std::unique_ptr(nullptr)); } static const Rect kDefaultDisplaySize; StrictMock mCompositionEngine; StrictMock mRenderEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); std::shared_ptr mOutput = createOutput(mCompositionEngine); }; const Rect OutputTest::kDefaultDisplaySize{100, 200}; using ColorProfile = compositionengine::Output::ColorProfile; void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) { android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name, toString(profile.mode).c_str(), profile.mode, toString(profile.dataspace).c_str(), profile.dataspace, toString(profile.renderIntent).c_str(), profile.renderIntent, toString(profile.colorSpaceAgnosticDataspace).c_str(), profile.colorSpaceAgnosticDataspace); } // Checks for a ColorProfile match MATCHER_P(ColorProfileEq, expected, "") { std::string buf; buf.append("ColorProfiles are not equal\n"); dumpColorProfile(expected, buf, "expected value"); dumpColorProfile(arg, buf, "actual value"); *result_listener << buf; return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) && (expected.renderIntent == arg.renderIntent) && (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace); } /* * Basic construction */ TEST_F(OutputTest, canInstantiateOutput) { // The validation check checks each required component. EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true)); EXPECT_TRUE(mOutput->isValid()); // If we take away the required components, it is no longer valid. mOutput->setRenderSurfaceForTest(std::unique_ptr()); EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true)); EXPECT_FALSE(mOutput->isValid()); } /* * Output::setCompositionEnabled() */ TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) { mOutput->editState().isEnabled = true; mOutput->setCompositionEnabled(true); EXPECT_TRUE(mOutput->getState().isEnabled); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) { mOutput->editState().isEnabled = false; mOutput->setCompositionEnabled(true); EXPECT_TRUE(mOutput->getState().isEnabled); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) { mOutput->editState().isEnabled = true; mOutput->setCompositionEnabled(false); EXPECT_FALSE(mOutput->getState().isEnabled); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } /* * Output::setLayerCachingEnabled() */ TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) { const auto kSize = ui::Size(1, 1); EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); mOutput->setLayerCachingEnabled(false); mOutput->setLayerCachingEnabled(true); EXPECT_TRUE(mOutput->plannerEnabled()); } TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) { const auto kSize = ui::Size(1, 1); EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); mOutput->setLayerCachingEnabled(true); mOutput->setLayerCachingEnabled(false); EXPECT_FALSE(mOutput->plannerEnabled()); } TEST_F(OutputTest, setLayerCachingEnabled_disablesCachingAndResetsOverrideInfo) { renderengine::mock::RenderEngine renderEngine; const auto kSize = ui::Size(1, 1); EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); mOutput->setLayerCachingEnabled(true); // Inject some layers InjectedLayer layer; layer.outputLayerState.overrideInfo.buffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); injectOutputLayer(layer); // inject a null layer to check for null exceptions injectNullOutputLayer(); EXPECT_NE(nullptr, layer.outputLayerState.overrideInfo.buffer); mOutput->setLayerCachingEnabled(false); EXPECT_EQ(nullptr, layer.outputLayerState.overrideInfo.buffer); } /* * Output::setProjection() */ TEST_F(OutputTest, setProjectionWorks) { const Rect displayRect{0, 0, 1000, 2000}; mOutput->editState().displaySpace.bounds = displayRect; mOutput->editState().framebufferSpace.bounds = displayRect; const ui::Rotation orientation = ui::ROTATION_90; const Rect frame{50, 60, 100, 100}; const Rect viewport{10, 20, 30, 40}; mOutput->setProjection(orientation, viewport, frame); EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); const auto state = mOutput->getState(); EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); EXPECT_EQ(viewport, state.layerStackSpace.content); EXPECT_EQ(viewport, state.layerStackSpace.bounds); EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); EXPECT_EQ(frame, state.orientedDisplaySpace.content); EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); EXPECT_EQ(displayRect, state.displaySpace.bounds); EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); EXPECT_EQ(orientation, state.displaySpace.orientation); EXPECT_EQ(displayRect, state.framebufferSpace.bounds); EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content); EXPECT_EQ(orientation, state.framebufferSpace.orientation); EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint()); } TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) { const Rect displayRect{0, 0, 1000, 2000}; const Rect framebufferRect{0, 0, 500, 1000}; mOutput->editState().displaySpace.bounds = displayRect; mOutput->editState().framebufferSpace.bounds = framebufferRect; const ui::Rotation orientation = ui::ROTATION_90; const Rect frame{50, 60, 100, 100}; const Rect viewport{10, 20, 30, 40}; mOutput->setProjection(orientation, viewport, frame); EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); const auto state = mOutput->getState(); EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); EXPECT_EQ(viewport, state.layerStackSpace.content); EXPECT_EQ(viewport, state.layerStackSpace.bounds); EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); EXPECT_EQ(frame, state.orientedDisplaySpace.content); EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); EXPECT_EQ(displayRect, state.displaySpace.bounds); EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); EXPECT_EQ(orientation, state.displaySpace.orientation); EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds); EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content); EXPECT_EQ(orientation, state.framebufferSpace.orientation); EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); } /* * Output::setDisplaySize() */ TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) { mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000); mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000); mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900); mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000); mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800); mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000); mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90; mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800); mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000); mOutput->editState().displaySpace.orientation = ui::ROTATION_90; const ui::Size newDisplaySize{500, 1000}; EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1); mOutput->setDisplaySize(newDisplaySize); const auto state = mOutput->getState(); const Rect displayRect(newDisplaySize); EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content); EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds); EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds); EXPECT_EQ(displayRect, state.displaySpace.bounds); EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation); EXPECT_EQ(displayRect, state.framebufferSpace.bounds); EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation); EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect))); } /* * Output::setLayerStackFilter() */ TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) { const uint32_t layerStack = 123u; mOutput->setLayerStackFilter(layerStack, true); EXPECT_TRUE(mOutput->getState().layerStackInternal); EXPECT_EQ(layerStack, mOutput->getState().layerStackId); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } /* * Output::setColorTransform */ TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) { mOutput->editState().colorTransformMatrix = kIdentity; // If no colorTransformMatrix is set the update should be skipped. CompositionRefreshArgs refreshArgs; refreshArgs.colorTransformMatrix = std::nullopt; mOutput->setColorTransform(refreshArgs); // The internal state should be unchanged EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); // No dirty region should be set EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) { mOutput->editState().colorTransformMatrix = kIdentity; // Attempting to set the same colorTransformMatrix that is already set should // also skip the update. CompositionRefreshArgs refreshArgs; refreshArgs.colorTransformMatrix = kIdentity; mOutput->setColorTransform(refreshArgs); // The internal state should be unchanged EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); // No dirty region should be set EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) { mOutput->editState().colorTransformMatrix = kNonIdentityHalf; // Setting a different colorTransformMatrix should perform the update. CompositionRefreshArgs refreshArgs; refreshArgs.colorTransformMatrix = kIdentity; mOutput->setColorTransform(refreshArgs); // The internal state should have been updated EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); // The dirtyRegion should be set to the full display size EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) { mOutput->editState().colorTransformMatrix = kIdentity; // Setting a different colorTransformMatrix should perform the update. CompositionRefreshArgs refreshArgs; refreshArgs.colorTransformMatrix = kNonIdentityHalf; mOutput->setColorTransform(refreshArgs); // The internal state should have been updated EXPECT_EQ(kNonIdentityHalf, mOutput->getState().colorTransformMatrix); // The dirtyRegion should be set to the full display size EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) { mOutput->editState().colorTransformMatrix = kNonIdentityHalf; // Setting a different colorTransformMatrix should perform the update. CompositionRefreshArgs refreshArgs; refreshArgs.colorTransformMatrix = kNonIdentityQuarter; mOutput->setColorTransform(refreshArgs); // The internal state should have been updated EXPECT_EQ(kNonIdentityQuarter, mOutput->getState().colorTransformMatrix); // The dirtyRegion should be set to the full display size EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } /* * Output::setColorProfile */ using OutputSetColorProfileTest = OutputTest; TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) { using ColorProfile = Output::ColorProfile; EXPECT_CALL(*mDisplayColorProfile, getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, ui::Dataspace::UNKNOWN)) .WillOnce(Return(ui::Dataspace::UNKNOWN)); EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1); mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN}); EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode); EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace); EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent); EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) { using ColorProfile = Output::ColorProfile; EXPECT_CALL(*mDisplayColorProfile, getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, ui::Dataspace::UNKNOWN)) .WillOnce(Return(ui::Dataspace::UNKNOWN)); mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3; mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3; mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC; mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN; mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN}); EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } /* * Output::setRenderSurface() */ TEST_F(OutputTest, setRenderSurfaceResetsBounds) { const ui::Size newDisplaySize{640, 480}; mock::RenderSurface* renderSurface = new StrictMock(); EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize)); mOutput->setRenderSurface(std::unique_ptr(renderSurface)); EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds); } /* * Output::getDirtyRegion() */ TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { const Rect viewport{100, 200}; mOutput->editState().layerStackSpace.content = viewport; mOutput->editState().dirtyRegion.set(50, 300); { Region result = mOutput->getDirtyRegion(true); EXPECT_THAT(result, RegionEq(Region(viewport))); } } TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) { const Rect viewport{100, 200}; mOutput->editState().layerStackSpace.content = viewport; mOutput->editState().dirtyRegion.set(50, 300); { Region result = mOutput->getDirtyRegion(false); // The dirtyRegion should be clipped to the display bounds. EXPECT_THAT(result, RegionEq(Region(Rect(50, 200)))); } } /* * Output::belongsInOutput() */ TEST_F(OutputTest, belongsInOutputFiltersAsExpected) { const uint32_t layerStack1 = 123u; const uint32_t layerStack2 = 456u; // If the output accepts layerStack1 and internal-only layers.... mOutput->setLayerStackFilter(layerStack1, true); // A layer with no layerStack does not belong to it, internal-only or not. EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false)); EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true)); // Any layer with layerStack1 belongs to it, internal-only or not. EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true)); EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); // If the output accepts layerStack21 but not internal-only layers... mOutput->setLayerStackFilter(layerStack1, false); // Only non-internal layers with layerStack1 belong to it. EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true)); EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); } TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) { NonInjectedLayer layer; sp layerFE(layer.layerFE); // If the layer has no composition state, it does not belong to any output. EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr)); EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); } TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) { NonInjectedLayer layer; sp layerFE(layer.layerFE); const uint32_t layerStack1 = 123u; const uint32_t layerStack2 = 456u; // If the output accepts layerStack1 and internal-only layers.... mOutput->setLayerStackFilter(layerStack1, true); // A layer with no layerStack does not belong to it, internal-only or not. layer.layerFEState.layerStackId = std::nullopt; layer.layerFEState.internalOnly = false; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = std::nullopt; layer.layerFEState.internalOnly = true; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); // Any layer with layerStack1 belongs to it, internal-only or not. layer.layerFEState.layerStackId = layerStack1; layer.layerFEState.internalOnly = false; EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack1; layer.layerFEState.internalOnly = true; EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack2; layer.layerFEState.internalOnly = true; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack2; layer.layerFEState.internalOnly = false; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); // If the output accepts layerStack1 but not internal-only layers... mOutput->setLayerStackFilter(layerStack1, false); // Only non-internal layers with layerStack1 belong to it. layer.layerFEState.layerStackId = layerStack1; layer.layerFEState.internalOnly = false; EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack1; layer.layerFEState.internalOnly = true; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack2; layer.layerFEState.internalOnly = true; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); layer.layerFEState.layerStackId = layerStack2; layer.layerFEState.internalOnly = false; EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); } /* * Output::getOutputLayerForLayer() */ TEST_F(OutputTest, getOutputLayerForLayerWorks) { InjectedLayer layer1; InjectedLayer layer2; NonInjectedLayer layer3; injectOutputLayer(layer1); injectNullOutputLayer(); injectOutputLayer(layer2); // If the input layer matches the first OutputLayer, it will be returned. EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get())); EXPECT_EQ(layer1.outputLayer, mOutput->getOutputLayerForLayer(layer1.layerFE)); // If the input layer matches the second OutputLayer, it will be returned. EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get())); EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get())); EXPECT_EQ(layer2.outputLayer, mOutput->getOutputLayerForLayer(layer2.layerFE)); // If the input layer does not match an output layer, null will be returned. EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get())); EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get())); EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(layer3.layerFE)); } /* * Output::setReleasedLayers() */ using OutputSetReleasedLayersTest = OutputTest; TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) { sp> layer1FE{new StrictMock()}; sp> layer2FE{new StrictMock()}; sp> layer3FE{new StrictMock()}; Output::ReleasedLayers layers; layers.push_back(layer1FE); layers.push_back(layer2FE); layers.push_back(layer3FE); mOutput->setReleasedLayers(std::move(layers)); const auto& setLayers = mOutput->getReleasedLayersForTest(); ASSERT_EQ(3u, setLayers.size()); ASSERT_EQ(layer1FE.get(), setLayers[0].promote().get()); ASSERT_EQ(layer2FE.get(), setLayers[1].promote().get()); ASSERT_EQ(layer3FE.get(), setLayers[2].promote().get()); } /* * Output::updateLayerStateFromFE() */ using OutputUpdateLayerStateFromFETest = OutputTest; TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) { CompositionRefreshArgs refreshArgs; mOutput->updateLayerStateFromFE(refreshArgs); } TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content)); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); CompositionRefreshArgs refreshArgs; refreshArgs.updatingGeometryThisFrame = false; mOutput->updateLayerStateFromFE(refreshArgs); } TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent)); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); CompositionRefreshArgs refreshArgs; refreshArgs.updatingGeometryThisFrame = true; mOutput->updateLayerStateFromFE(refreshArgs); } /* * Output::updateAndWriteCompositionState() */ using OutputUpdateAndWriteCompositionStateTest = OutputTest; TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) { mOutput->editState().isEnabled = true; CompositionRefreshArgs args; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; mOutput->editState().isEnabled = false; injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); CompositionRefreshArgs args; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; uint32_t z = 0; EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; args.devOptForceClientComposition = false; args.internalDisplayRotationFlags = ui::Transform::ROT_180; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; uint32_t z = 0; EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = true; args.devOptForceClientComposition = false; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; uint32_t z = 0; EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; args.devOptForceClientComposition = true; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { renderengine::mock::RenderEngine renderEngine; InjectedLayer layer0; InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; InSequence seq; EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); uint32_t z = 0; EXPECT_CALL(*layer0.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); // After calling planComposition (which clears overrideInfo), this test sets // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it // comes first, setting isPeekingThrough to true and zIsOverridden to true // for it and the following layers. EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ true, /*isPeekingThrough*/ true)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++, /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); injectOutputLayer(layer0); injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = true; args.devOptForceClientComposition = false; mOutput->updateCompositionState(args); mOutput->planComposition(); std::shared_ptr buffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); layer1.outputLayerState.overrideInfo.buffer = buffer; layer2.outputLayerState.overrideInfo.buffer = buffer; layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer; layer2.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer; mOutput->writeCompositionState(args); } /* * Output::prepareFrame() */ struct OutputPrepareFrameTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD0(chooseCompositionStrategy, void()); }; OutputPrepareFrameTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); } StrictMock mCompositionEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); StrictMock mOutput; }; TEST_F(OutputPrepareFrameTest, takesEarlyOutIfNotEnabled) { mOutput.editState().isEnabled = false; mOutput.prepareFrame(); } TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurface) { mOutput.editState().isEnabled = true; mOutput.editState().usesClientComposition = false; mOutput.editState().usesDeviceComposition = true; EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u)); EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)); mOutput.prepareFrame(); } // Note: Use OutputTest and not OutputPrepareFrameTest, so the real // base chooseCompositionStrategy() is invoked. TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) { mOutput->editState().isEnabled = true; mOutput->editState().usesClientComposition = false; mOutput->editState().usesDeviceComposition = true; EXPECT_CALL(*mRenderSurface, prepareFrame(true, false)); mOutput->prepareFrame(); EXPECT_TRUE(mOutput->getState().usesClientComposition); EXPECT_FALSE(mOutput->getState().usesDeviceComposition); } /* * Output::prepare() */ struct OutputPrepareTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD2(rebuildLayerStacks, void(const compositionengine::CompositionRefreshArgs&, compositionengine::LayerFESet&)); }; StrictMock mOutput; CompositionRefreshArgs mRefreshArgs; LayerFESet mGeomSnapshots; }; TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) { InSequence seq; EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots))); mOutput.prepare(mRefreshArgs, mGeomSnapshots); } /* * Output::rebuildLayerStacks() */ struct OutputRebuildLayerStacksTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD2(collectVisibleLayers, void(const compositionengine::CompositionRefreshArgs&, compositionengine::Output::CoverageState&)); }; OutputRebuildLayerStacksTest() { mOutput.mState.isEnabled = true; mOutput.mState.transform = kIdentityTransform; mOutput.mState.displaySpace.bounds = kOutputBounds; mRefreshArgs.updatingOutputGeometryThisFrame = true; mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10)); EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _)) .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues)); } void setTestCoverageValues(const CompositionRefreshArgs&, compositionengine::Output::CoverageState& state) { state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet; state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet; state.dirtyRegion = mCoverageDirtyRegionToSet; } static const ui::Transform kIdentityTransform; static const ui::Transform kRotate90Transform; static const Rect kOutputBounds; StrictMock mOutput; CompositionRefreshArgs mRefreshArgs; LayerFESet mGeomSnapshots; Region mCoverageAboveCoveredLayersToSet; Region mCoverageAboveOpaqueLayersToSet; Region mCoverageDirtyRegionToSet; }; const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080}; const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080}; const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080}; TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) { mOutput.mState.isEnabled = false; mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); } TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) { mRefreshArgs.updatingOutputGeometryThisFrame = false; mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); } TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) { mOutput.mState.transform = kIdentityTransform; mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0)))); } TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) { mOutput.mState.transform = kIdentityTransform; mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080)))); } TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) { mOutput.mState.transform = kRotate90Transform; mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0)))); } TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) { mOutput.mState.transform = kRotate90Transform; mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080)))); } TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) { mOutput.mState.transform = kIdentityTransform; mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080)); mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080)))); } TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) { mOutput.mState.transform = kRotate90Transform; mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920)); mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960)); mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots); EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920)))); } /* * Output::collectVisibleLayers() */ struct OutputCollectVisibleLayersTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD2(ensureOutputLayerIfVisible, void(sp&, compositionengine::Output::CoverageState&)); MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(finalizePendingOutputLayers, void()); }; struct Layer { Layer() { EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState)); EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState)); } StrictMock outputLayer; impl::OutputLayerCompositionState outputLayerState; sp> layerFE{new StrictMock()}; }; OutputCollectVisibleLayersTest() { EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0)) .WillRepeatedly(Return(&mLayer1.outputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1)) .WillRepeatedly(Return(&mLayer2.outputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2)) .WillRepeatedly(Return(&mLayer3.outputLayer)); mRefreshArgs.layers.push_back(mLayer1.layerFE); mRefreshArgs.layers.push_back(mLayer2.layerFE); mRefreshArgs.layers.push_back(mLayer3.layerFE); } StrictMock mOutput; CompositionRefreshArgs mRefreshArgs; LayerFESet mGeomSnapshots; Output::CoverageState mCoverageState{mGeomSnapshots}; Layer mLayer1; Layer mLayer2; Layer mLayer3; }; TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) { mRefreshArgs.layers.clear(); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u)); EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs))); EXPECT_CALL(mOutput, finalizePendingOutputLayers()); mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState); } TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) { // Enforce a call order sequence for this test. InSequence seq; // Layer coverage is evaluated from front to back! EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layerFE), Ref(mCoverageState))); EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layerFE), Ref(mCoverageState))); EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layerFE), Ref(mCoverageState))); EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs))); EXPECT_CALL(mOutput, finalizePendingOutputLayers()); mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState); } /* * Output::ensureOutputLayerIfVisible() */ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD1(belongsInOutput, bool(const sp&)); MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t)); MOCK_METHOD2(ensureOutputLayer, compositionengine::OutputLayer*(std::optional, const sp&)); }; OutputEnsureOutputLayerIfVisibleTest() { EXPECT_CALL(mOutput, belongsInOutput(sp(mLayer.layerFE))) .WillRepeatedly(Return(true)); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300); mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300); mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300); mLayer.layerFEState.isVisible = true; mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200}; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); mLayer.layerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100)); mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200)); mLayer.outputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200)); mGeomSnapshots.insert(mLayer.layerFE); } void ensureOutputLayerIfVisible() { sp layerFE(mLayer.layerFE); mOutput.ensureOutputLayerIfVisible(layerFE, mCoverageState); } static const Region kEmptyRegion; static const Region kFullBoundsNoRotation; static const Region kRightHalfBoundsNoRotation; static const Region kLowerHalfBoundsNoRotation; static const Region kFullBounds90Rotation; StrictMock mOutput; LayerFESet mGeomSnapshots; Output::CoverageState mCoverageState{mGeomSnapshots}; NonInjectedLayer mLayer; }; const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0)); const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation = Region(Rect(0, 0, 100, 200)); const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation = Region(Rect(0, 100, 100, 200)); const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation = Region(Rect(50, 0, 100, 200)); const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation = Region(Rect(0, 0, 200, 100)); TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) { EXPECT_CALL(mOutput, belongsInOutput(sp(mLayer.layerFE))).WillOnce(Return(false)); EXPECT_CALL(*mLayer.layerFE, prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry)); mGeomSnapshots.clear(); ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) { EXPECT_CALL(mOutput, belongsInOutput(sp(mLayer.layerFE))).WillOnce(Return(false)); ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasNoCompositionState) { EXPECT_CALL(*mLayer.layerFE, getCompositionState()).WillOnce(Return(nullptr)); ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) { mLayer.layerFEState.isVisible = false; ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) { mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0}; ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) { mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0); ensureOutputLayerIfVisible(); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = false; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRightHalfBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = false; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRightHalfBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = false; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = false; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100}; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200); mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100)); mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100)); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100}; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200); mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100)); mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) { mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) { ui::Transform arbitraryTransform; arbitraryTransform.set(1, 1, -1, 1); arbitraryTransform.set(0, 100); mLayer.layerFEState.isOpaque = true; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200}; mLayer.layerFEState.geomLayerTransform = arbitraryTransform; EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); const Region kRegion = Region(Rect(0, 0, 300, 300)); const Region kRegionClipped = Region(Rect(0, 0, 200, 300)); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRegion)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) { mLayer.layerFEState.isOpaque = false; mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500)); mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200)); mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500)); const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200)); const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200)); const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200)); const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200)); const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200)); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kExpectedLayerVisibleNonTransparentRegion)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion)); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) { ui::Transform translate; translate.set(50, 50); mLayer.layerFEState.geomLayerTransform = translate; mLayer.layerFEState.shadowRadius = 10.0f; mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500)); // half of the layer including the casting shadow is covered and opaque mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260)); mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500)); const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260)); // add starting opaque region to the opaque half of the casting layer bounds const Region kExpectedAboveOpaqueRegion = Region(Rect(40, 40, 100, 260)).orSelf(Rect(100, 50, 150, 250)); const Region kExpectedLayerVisibleRegion = Region(Rect(100, 40, 160, 260)); const Region kExpectedoutputSpaceLayerVisibleRegion = Region(Rect(100, 50, 150, 250)); const Region kExpectedLayerCoveredRegion = Region(Rect(40, 40, 100, 260)); const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(100, 40, 160, 260)); const Region kExpectedLayerShadowRegion = Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250)); EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion)); EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion)); EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion)); EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kExpectedLayerVisibleNonTransparentRegion)); EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion)); EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedoutputSpaceLayerVisibleRegion)); EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion)); EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty()); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) { ui::Transform translate; translate.set(50, 50); mLayer.layerFEState.geomLayerTransform = translate; mLayer.layerFEState.shadowRadius = 10.0f; mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500)); // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260)); mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260)); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) .WillOnce(Return(&mLayer.outputLayer)); ensureOutputLayerIfVisible(); const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260)); const Region kExpectedLayerShadowRegion = Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250)); EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion)); EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion)); EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty()); } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) { ui::Transform translate; translate.set(50, 50); mLayer.layerFEState.geomLayerTransform = translate; mLayer.layerFEState.shadowRadius = 10.0f; mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500)); // Casting layer and its shadows are covered by an opaque region mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260)); mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260)); ensureOutputLayerIfVisible(); } /* * Output::present() */ struct OutputPresentTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(updateCompositionState, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(planComposition, void()); MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(beginFrame, void()); MOCK_METHOD0(prepareFrame, void()); MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(postFramebuffer, void()); MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&)); }; StrictMock mOutput; }; TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) { CompositionRefreshArgs args; InSequence seq; EXPECT_CALL(mOutput, updateColorProfile(Ref(args))); EXPECT_CALL(mOutput, updateCompositionState(Ref(args))); EXPECT_CALL(mOutput, planComposition()); EXPECT_CALL(mOutput, writeCompositionState(Ref(args))); EXPECT_CALL(mOutput, setColorTransform(Ref(args))); EXPECT_CALL(mOutput, beginFrame()); EXPECT_CALL(mOutput, prepareFrame()); EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args))); EXPECT_CALL(mOutput, finishFrame(Ref(args))); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, renderCachedSets(Ref(args))); mOutput.present(args); } /* * Output::updateColorProfile() */ struct OutputUpdateColorProfileTest : public testing::Test { using TestType = OutputUpdateColorProfileTest; struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD1(setColorProfile, void(const ColorProfile&)); }; struct Layer { Layer() { EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE)); EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState)); } StrictMock mOutputLayer; sp> mLayerFE = sp>::make(); LayerFECompositionState mLayerFEState; }; OutputUpdateColorProfileTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0)) .WillRepeatedly(Return(&mLayer1.mOutputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1)) .WillRepeatedly(Return(&mLayer2.mOutputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2)) .WillRepeatedly(Return(&mLayer3.mOutputLayer)); } struct ExecuteState : public CallOrderStateMachineHelper { void execute() { getInstance()->mOutput.updateColorProfile(getInstance()->mRefreshArgs); } }; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); StrictMock mOutput; Layer mLayer1; Layer mLayer2; Layer mLayer3; CompositionRefreshArgs mRefreshArgs; }; // TODO(b/144522012): Refactor Output::updateColorProfile and the related code // to make it easier to write unit tests. TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) { // When the outputColorSetting is set to kUnmanaged, the implementation sets // a simple default color profile without looking at anything else. EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u)); EXPECT_CALL(mOutput, setColorProfile(ColorProfileEq( ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN}))); mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; mOutput.updateColorProfile(mRefreshArgs); } struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile : public OutputUpdateColorProfileTest { OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() { EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u)); mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; } struct ExpectBestColorModeCallResultUsedToSetColorProfileState : public CallOrderStateMachineHelper< TestType, ExpectBestColorModeCallResultUsedToSetColorProfileState> { [[nodiscard]] auto expectBestColorModeCallResultUsedToSetColorProfile( ui::ColorMode colorMode, ui::Dataspace dataspace, ui::RenderIntent renderIntent) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _)) .WillOnce(DoAll(SetArgPointee<2>(dataspace), SetArgPointee<3>(colorMode), SetArgPointee<4>(renderIntent))); EXPECT_CALL(getInstance()->mOutput, setColorProfile( ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent, ui::Dataspace::UNKNOWN}))); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return ExpectBestColorModeCallResultUsedToSetColorProfileState::make(this); } }; TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile, Native_Unknown_Colorimetric_Set) { verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, ui::RenderIntent::COLORIMETRIC) .execute(); } TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile, DisplayP3_DisplayP3_Enhance_Set) { verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, ui::RenderIntent::ENHANCE) .execute(); } struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile : public OutputUpdateColorProfileTest { OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() { EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u)); EXPECT_CALL(*mDisplayColorProfile, getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN), SetArgPointee<3>(ui::ColorMode::NATIVE), SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC))); mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; } struct IfColorSpaceAgnosticDataspaceSetToState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) { getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace; return nextState(); } }; struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState : public CallOrderStateMachineHelper< TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> { [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace( ui::Dataspace dataspace) { EXPECT_CALL(getInstance()->mOutput, setColorProfile(ColorProfileEq( ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, ui::RenderIntent::COLORIMETRIC, dataspace}))); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); } }; TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) { verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3) .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) { verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB) .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB) .execute(); } struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference : public OutputUpdateColorProfileTest { // Internally the implementation looks through the dataspaces of all the // visible layers. The topmost one that also has an actual dataspace // preference set is used to drive subsequent choices. OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() { mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u)); EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return()); } struct IfTopLayerDataspaceState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) { getInstance()->mLayer3.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto ifTopLayerHasNoPreference() { return ifTopLayerIs(ui::Dataspace::UNKNOWN); } }; struct AndIfMiddleLayerDataspaceState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfMiddleLayerIs(ui::Dataspace dataspace) { getInstance()->mLayer2.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto andIfMiddleLayerHasNoPreference() { return andIfMiddleLayerIs(ui::Dataspace::UNKNOWN); } }; struct AndIfBottomLayerDataspaceState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) { getInstance()->mLayer1.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto andIfBottomLayerHasNoPreference() { return andIfBottomLayerIs(ui::Dataspace::UNKNOWN); } }; struct ThenExpectBestColorModeCallUsesState : public CallOrderStateMachineHelper { [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, getBestColorMode(dataspace, _, _, _, _)); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); } }; TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, noStrongLayerPrefenceUses_V0_SRGB) { // If none of the layers indicate a preference, then V0_SRGB is the // preferred choice (subject to additional checks). verify().ifTopLayerHasNoPreference() .andIfMiddleLayerHasNoPreference() .andIfBottomLayerHasNoPreference() .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB) .execute(); } TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, ifTopmostUses_DisplayP3_Then_DisplayP3_Chosen) { // If only the topmost layer has a preference, then that is what is chosen. verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3) .andIfMiddleLayerHasNoPreference() .andIfBottomLayerHasNoPreference() .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, ifMiddleUses_DisplayP3_Then_DisplayP3_Chosen) { // If only the middle layer has a preference, that that is what is chosen. verify().ifTopLayerHasNoPreference() .andIfMiddleLayerIs(ui::Dataspace::DISPLAY_P3) .andIfBottomLayerHasNoPreference() .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, ifBottomUses_DisplayP3_Then_DisplayP3_Chosen) { // If only the middle layer has a preference, that that is what is chosen. verify().ifTopLayerHasNoPreference() .andIfMiddleLayerHasNoPreference() .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3) .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, ifTopUses_DisplayBT2020_AndBottomUses_DisplayP3_Then_DisplayBT2020_Chosen) { // If multiple layers have a preference, the topmost value is what is used. verify().ifTopLayerIs(ui::Dataspace::DISPLAY_BT2020) .andIfMiddleLayerHasNoPreference() .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3) .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020) .execute(); } TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference, ifTopUses_DisplayP3_AndBottomUses_V0_SRGB_Then_DisplayP3_Chosen) { // If multiple layers have a preference, the topmost value is what is used. verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3) .andIfMiddleLayerHasNoPreference() .andIfBottomLayerIs(ui::Dataspace::DISPLAY_BT2020) .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3) .execute(); } struct OutputUpdateColorProfileTest_ForceOutputColorOverrides : public OutputUpdateColorProfileTest { // If CompositionRefreshArgs::forceOutputColorMode is set to some specific // values, it overrides the layer dataspace choice. OutputUpdateColorProfileTest_ForceOutputColorOverrides() { mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return()); } struct IfForceOutputColorModeState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifForceOutputColorMode(ui::ColorMode colorMode) { getInstance()->mRefreshArgs.forceOutputColorMode = colorMode; return nextState(); } [[nodiscard]] auto ifNoOverride() { return ifForceOutputColorMode(ui::ColorMode::NATIVE); } }; struct ThenExpectBestColorModeCallUsesState : public CallOrderStateMachineHelper { [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, getBestColorMode(dataspace, _, _, _, _)); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return IfForceOutputColorModeState::make(this); } }; TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, NoOverride_DoesNotOverride) { // By default the layer state is used to set the preferred dataspace verify().ifNoOverride() .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020) .execute(); } TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, SRGB_Override_USES_V0_SRGB) { // Setting ui::ColorMode::SRGB overrides it with ui::Dataspace::V0_SRGB verify().ifForceOutputColorMode(ui::ColorMode::SRGB) .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB) .execute(); } TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Override_Uses_DisplayP3) { // Setting ui::ColorMode::DISPLAY_P3 overrides it with ui::Dataspace::DISPLAY_P3 verify().ifForceOutputColorMode(ui::ColorMode::DISPLAY_P3) .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3) .execute(); } // HDR output requires all layers to be compatible with the chosen HDR // dataspace, along with there being proper support. struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest { OutputUpdateColorProfileTest_Hdr() { mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return()); } static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3; static constexpr ui::Dataspace BT2020_PQ = ui::Dataspace::BT2020_PQ; static constexpr ui::Dataspace BT2020_HLG = ui::Dataspace::BT2020_HLG; static constexpr ui::Dataspace DISPLAY_P3 = ui::Dataspace::DISPLAY_P3; struct IfTopLayerDataspaceState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) { getInstance()->mLayer2.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto ifTopLayerIsNotHdr() { return ifTopLayerIs(kNonHdrDataspace); } }; struct AndTopLayerCompositionTypeState : public CallOrderStateMachineHelper { [[nodiscard]] auto andTopLayerIsREComposed(bool renderEngineComposed) { getInstance()->mLayer2.mLayerFEState.forceClientComposition = renderEngineComposed; return nextState(); } }; struct AndIfBottomLayerDataspaceState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) { getInstance()->mLayer1.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto andIfBottomLayerIsNotHdr() { return andIfBottomLayerIs(kNonHdrDataspace); } }; struct AndBottomLayerCompositionTypeState : public CallOrderStateMachineHelper { [[nodiscard]] auto andBottomLayerIsREComposed(bool renderEngineComposed) { getInstance()->mLayer1.mLayerFEState.forceClientComposition = renderEngineComposed; return nextState(); } }; struct AndIfHasLegacySupportState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfLegacySupportFor(ui::Dataspace dataspace, bool legacySupport) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasLegacyHdrSupport(dataspace)) .WillOnce(Return(legacySupport)); return nextState(); } }; struct ThenExpectBestColorModeCallUsesState : public CallOrderStateMachineHelper { [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, getBestColorMode(dataspace, _, _, _, _)); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); } }; TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_Uses_PQ) { // If all layers use BT2020_PQ, and there are no other special conditions, // BT2020_PQ is used. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) { // BT2020_PQ is not used if there is only legacy support for it. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, true) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_RE_Uses_PQ) { // BT2020_PQ is still used if the bottom layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(true) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_PQ_HW_Uses_DisplayP3) { // BT2020_PQ is not used if the top layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(true) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_Uses_PQ) { // If there is mixed HLG/PQ use, and the topmost layer is PQ, then PQ is used if there // are no other special conditions. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) { // BT2020_PQ is not used if there is only legacy support for it. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, true) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_RE_Uses_PQ) { // BT2020_PQ is used if the bottom HLG layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(true) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_HLG_HW_Uses_DisplayP3) { // BT2020_PQ is not used if the top PQ layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(true) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_Uses_PQ) { // If there is mixed HLG/PQ use, and the topmost layer is HLG, then PQ is // used if there are no other special conditions. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) { // BT2020_PQ is not used if there is only legacy support for it. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, true) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_RE_Uses_DisplayP3) { // BT2020_PQ is not used if the bottom PQ layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(true) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_PQ_HW_Uses_PQ) { // BT2020_PQ is still used if the top HLG layer is RenderEngine composed. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(true) .andIfBottomLayerIs(BT2020_PQ) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_Uses_HLG) { // If all layers use HLG then HLG is used if there are no other special // conditions. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_HLG, false) .thenExpectBestColorModeCallUses(BT2020_HLG) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) { // BT2020_HLG is not used if there is legacy support for it. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_HLG, true) .thenExpectBestColorModeCallUses(DISPLAY_P3) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_RE_Uses_HLG) { // BT2020_HLG is used even if the bottom layer is client composed. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(true) .andIfLegacySupportFor(BT2020_HLG, false) .thenExpectBestColorModeCallUses(BT2020_HLG) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_HLG_HW_Uses_HLG) { // BT2020_HLG is used even if the top layer is client composed. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(true) .andIfBottomLayerIs(BT2020_HLG) .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_HLG, false) .thenExpectBestColorModeCallUses(BT2020_HLG) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_NonHdr_HW_Uses_PQ) { // Even if there are non-HDR layers present, BT2020_PQ can still be used. verify().ifTopLayerIs(BT2020_PQ) .andTopLayerIsREComposed(false) .andIfBottomLayerIsNotHdr() .andBottomLayerIsREComposed(false) .andIfLegacySupportFor(BT2020_PQ, false) .thenExpectBestColorModeCallUses(BT2020_PQ) .execute(); } TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_NonHdr_RE_Uses_HLG) { // If all layers use HLG then HLG is used if there are no other special // conditions. verify().ifTopLayerIs(BT2020_HLG) .andTopLayerIsREComposed(false) .andIfBottomLayerIsNotHdr() .andBottomLayerIsREComposed(true) .andIfLegacySupportFor(BT2020_HLG, false) .thenExpectBestColorModeCallUses(BT2020_HLG) .execute(); } struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest : public OutputUpdateColorProfileTest { // The various values for CompositionRefreshArgs::outputColorSetting affect // the chosen renderIntent, along with whether the preferred dataspace is an // HDR dataspace or not. OutputUpdateColorProfile_AffectsChosenRenderIntentTest() { mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced; mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN; mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return()); EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ)) .WillRepeatedly(Return(false)); } // The tests here involve enough state and GMock setup that using a mini-DSL // makes the tests much more readable, and allows the test to focus more on // the intent than on some of the details. static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3; static constexpr ui::Dataspace kHdrDataspace = ui::Dataspace::BT2020_PQ; struct IfDataspaceChosenState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifDataspaceChosenIs(ui::Dataspace dataspace) { getInstance()->mLayer1.mLayerFEState.dataspace = dataspace; return nextState(); } [[nodiscard]] auto ifDataspaceChosenIsNonHdr() { return ifDataspaceChosenIs(kNonHdrDataspace); } [[nodiscard]] auto ifDataspaceChosenIsHdr() { return ifDataspaceChosenIs(kHdrDataspace); } }; struct AndOutputColorSettingState : public CallOrderStateMachineHelper { [[nodiscard]] auto andOutputColorSettingIs(OutputColorSetting setting) { getInstance()->mRefreshArgs.outputColorSetting = setting; return nextState(); } }; struct ThenExpectBestColorModeCallUsesState : public CallOrderStateMachineHelper { [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::RenderIntent intent) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, getBestColorMode(getInstance()->mLayer1.mLayerFEState.dataspace, intent, _, _, _)); return nextState(); } }; // Tests call one of these two helper member functions to start using the // mini-DSL defined above. [[nodiscard]] auto verify() { return IfDataspaceChosenState::make(this); } }; TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Managed_NonHdr_Prefers_Colorimetric) { verify().ifDataspaceChosenIsNonHdr() .andOutputColorSettingIs(OutputColorSetting::kManaged) .thenExpectBestColorModeCallUses(ui::RenderIntent::COLORIMETRIC) .execute(); } TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Managed_Hdr_Prefers_ToneMapColorimetric) { verify().ifDataspaceChosenIsHdr() .andOutputColorSettingIs(OutputColorSetting::kManaged) .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_COLORIMETRIC) .execute(); } TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_NonHdr_Prefers_Enhance) { verify().ifDataspaceChosenIsNonHdr() .andOutputColorSettingIs(OutputColorSetting::kEnhanced) .thenExpectBestColorModeCallUses(ui::RenderIntent::ENHANCE) .execute(); } TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_Hdr_Prefers_ToneMapEnhance) { verify().ifDataspaceChosenIsHdr() .andOutputColorSettingIs(OutputColorSetting::kEnhanced) .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_ENHANCE) .execute(); } TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_NonHdr_Prefers_Vendor) { verify().ifDataspaceChosenIsNonHdr() .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting) .thenExpectBestColorModeCallUses( static_cast(kVendorSpecifiedOutputColorSetting)) .execute(); } TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_Hdr_Prefers_Vendor) { verify().ifDataspaceChosenIsHdr() .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting) .thenExpectBestColorModeCallUses( static_cast(kVendorSpecifiedOutputColorSetting)) .execute(); } /* * Output::beginFrame() */ struct OutputBeginFrameTest : public ::testing::Test { using TestType = OutputBeginFrameTest; struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); }; OutputBeginFrameTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); } struct IfGetDirtyRegionExpectationState : public CallOrderStateMachineHelper { [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) { EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false)) .WillOnce(Return(dirtyRegion)); return nextState(); } }; struct AndIfGetOutputLayerCountExpectationState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfGetOutputLayerCountReturns(size_t layerCount) { EXPECT_CALL(getInstance()->mOutput, getOutputLayerCount()).WillOnce(Return(layerCount)); return nextState(); } }; struct AndIfLastCompositionHadVisibleLayersState : public CallOrderStateMachineHelper { [[nodiscard]] auto andIfLastCompositionHadVisibleLayersIs(bool hadOutputLayers) { getInstance()->mOutput.mState.lastCompositionHadVisibleLayers = hadOutputLayers; return nextState(); } }; struct ThenExpectRenderSurfaceBeginFrameCallState : public CallOrderStateMachineHelper { [[nodiscard]] auto thenExpectRenderSurfaceBeginFrameCall(bool mustRecompose) { EXPECT_CALL(*getInstance()->mRenderSurface, beginFrame(mustRecompose)); return nextState(); } }; struct ExecuteState : public CallOrderStateMachineHelper { [[nodiscard]] auto execute() { getInstance()->mOutput.beginFrame(); return nextState(); } }; struct CheckPostconditionHadVisibleLayersState : public CallOrderStateMachineHelper { void checkPostconditionHadVisibleLayers(bool expected) { EXPECT_EQ(expected, getInstance()->mOutput.mState.lastCompositionHadVisibleLayers); } }; // Tests call one of these two helper member functions to start using the // mini-DSL defined above. [[nodiscard]] auto verify() { return IfGetDirtyRegionExpectationState::make(this); } static const Region kEmptyRegion; static const Region kNotEmptyRegion; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); StrictMock mOutput; }; const Region OutputBeginFrameTest::kEmptyRegion{Rect{0, 0, 0, 0}}; const Region OutputBeginFrameTest::kNotEmptyRegion{Rect{0, 0, 1, 1}}; TEST_F(OutputBeginFrameTest, hasDirtyHasLayersHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kNotEmptyRegion) .andIfGetOutputLayerCountReturns(1u) .andIfLastCompositionHadVisibleLayersIs(true) .thenExpectRenderSurfaceBeginFrameCall(true) .execute() .checkPostconditionHadVisibleLayers(true); } TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kNotEmptyRegion) .andIfGetOutputLayerCountReturns(0u) .andIfLastCompositionHadVisibleLayersIs(true) .thenExpectRenderSurfaceBeginFrameCall(true) .execute() .checkPostconditionHadVisibleLayers(false); } TEST_F(OutputBeginFrameTest, hasDirtyHasLayersNotHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kNotEmptyRegion) .andIfGetOutputLayerCountReturns(1u) .andIfLastCompositionHadVisibleLayersIs(false) .thenExpectRenderSurfaceBeginFrameCall(true) .execute() .checkPostconditionHadVisibleLayers(true); } TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersNotHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kNotEmptyRegion) .andIfGetOutputLayerCountReturns(0u) .andIfLastCompositionHadVisibleLayersIs(false) .thenExpectRenderSurfaceBeginFrameCall(false) .execute() .checkPostconditionHadVisibleLayers(false); } TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kEmptyRegion) .andIfGetOutputLayerCountReturns(1u) .andIfLastCompositionHadVisibleLayersIs(true) .thenExpectRenderSurfaceBeginFrameCall(false) .execute() .checkPostconditionHadVisibleLayers(true); } TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kEmptyRegion) .andIfGetOutputLayerCountReturns(0u) .andIfLastCompositionHadVisibleLayersIs(true) .thenExpectRenderSurfaceBeginFrameCall(false) .execute() .checkPostconditionHadVisibleLayers(true); } TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersNotHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kEmptyRegion) .andIfGetOutputLayerCountReturns(1u) .andIfLastCompositionHadVisibleLayersIs(false) .thenExpectRenderSurfaceBeginFrameCall(false) .execute() .checkPostconditionHadVisibleLayers(false); } TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersNotHadLayersLastFrame) { verify().ifGetDirtyRegionReturns(kEmptyRegion) .andIfGetOutputLayerCountReturns(0u) .andIfLastCompositionHadVisibleLayersIs(false) .thenExpectRenderSurfaceBeginFrameCall(false) .execute() .checkPostconditionHadVisibleLayers(false); } /* * Output::devOptRepaintFlash() */ struct OutputDevOptRepaintFlashTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); MOCK_METHOD2(composeSurfaces, std::optional( const Region&, const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(postFramebuffer, void()); MOCK_METHOD0(prepareFrame, void()); }; OutputDevOptRepaintFlashTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); } static const Region kEmptyRegion; static const Region kNotEmptyRegion; StrictMock mOutput; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); CompositionRefreshArgs mRefreshArgs; }; const Region OutputDevOptRepaintFlashTest::kEmptyRegion{Rect{0, 0, 0, 0}}; const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}}; TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) { mRefreshArgs.devOptFlashDirtyRegionsDelay = {}; mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = true; mOutput.devOptRepaintFlash(mRefreshArgs); } TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = false; InSequence seq; EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, prepareFrame()); mOutput.devOptRepaintFlash(mRefreshArgs); } TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = true; InSequence seq; EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion)); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, prepareFrame()); mOutput.devOptRepaintFlash(mRefreshArgs); } TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); mRefreshArgs.repaintEverything = false; mOutput.mState.isEnabled = true; InSequence seq; EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion)); EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs))); EXPECT_CALL(*mRenderSurface, queueBuffer(_)); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, prepareFrame()); mOutput.devOptRepaintFlash(mRefreshArgs); } /* * Output::finishFrame() */ struct OutputFinishFrameTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD2(composeSurfaces, std::optional( const Region&, const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD0(postFramebuffer, void()); }; OutputFinishFrameTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); } StrictMock mOutput; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); CompositionRefreshArgs mRefreshArgs; }; TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) { mOutput.mState.isEnabled = false; mOutput.finishFrame(mRefreshArgs); } TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) { mOutput.mState.isEnabled = true; InSequence seq; EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _)); mOutput.finishFrame(mRefreshArgs); } TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) { mOutput.mState.isEnabled = true; InSequence seq; EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _)) .WillOnce(Return(ByMove(base::unique_fd()))); EXPECT_CALL(*mRenderSurface, queueBuffer(_)); mOutput.finishFrame(mRefreshArgs); } /* * Output::postFramebuffer() */ struct OutputPostFramebufferTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences()); }; struct Layer { Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE)); EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer)); } StrictMock outputLayer; sp> layerFE = sp>::make(); StrictMock hwc2Layer; }; OutputPostFramebufferTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer1.outputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) .WillRepeatedly(Return(&mLayer2.outputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2u)) .WillRepeatedly(Return(&mLayer3.outputLayer)); } StrictMock mOutput; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); Layer mLayer1; Layer mLayer2; Layer mLayer3; }; TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) { mOutput.mState.isEnabled = false; mOutput.postFramebuffer(); } TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) { mOutput.mState.isEnabled = true; compositionengine::Output::FrameFences frameFences; // This should happen even if there are no output layers. EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); // For this test in particular we want to make sure the call expectations // setup below are satisfied in the specific order. InSequence seq; EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); mOutput.postFramebuffer(); } TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { // Simulate getting release fences from each layer, and ensure they are passed to the // front-end layer interface for each layer correctly. mOutput.mState.isEnabled = true; // Create three unique fence instances sp layer1Fence = new Fence(); sp layer2Fence = new Fence(); sp layer3Fence = new Fence(); Output::FrameFences frameFences; frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence); frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence); frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence); EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); // Compare the pointers values of each fence to make sure the correct ones // are passed. This happens to work with the current implementation, but // would not survive certain calls like Fence::merge() which would return a // new instance. EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Property(&sp::get, Eq(layer1Fence.get())))); EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Property(&sp::get, Eq(layer2Fence.get())))); EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Property(&sp::get, Eq(layer3Fence.get())))); mOutput.postFramebuffer(); } TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) { mOutput.mState.isEnabled = true; mOutput.mState.usesClientComposition = true; sp clientTargetAcquireFence = new Fence(); sp layer1Fence = new Fence(); sp layer2Fence = new Fence(); sp layer3Fence = new Fence(); Output::FrameFences frameFences; frameFences.clientTargetAcquireFence = clientTargetAcquireFence; frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence); frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence); frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence); EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); // Fence::merge is called, and since none of the fences are actually valid, // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call. // This is the best we can do without creating a real kernel fence object. EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE)); EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE)); EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE)); mOutput.postFramebuffer(); } TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { mOutput.mState.isEnabled = true; mOutput.mState.usesClientComposition = true; // This should happen even if there are no (current) output layers. EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); // Load up the released layers with some mock instances sp> releasedLayer1{new StrictMock()}; sp> releasedLayer2{new StrictMock()}; sp> releasedLayer3{new StrictMock()}; Output::ReleasedLayers layers; layers.push_back(releasedLayer1); layers.push_back(releasedLayer2); layers.push_back(releasedLayer3); mOutput.setReleasedLayers(std::move(layers)); // Set up a fake present fence sp presentFence = new Fence(); Output::FrameFences frameFences; frameFences.presentFence = presentFence; EXPECT_CALL(*mRenderSurface, flip()); EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences)); EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted()); // Each released layer should be given the presentFence. EXPECT_CALL(*releasedLayer1, onLayerDisplayed(Property(&sp::get, Eq(presentFence.get())))); EXPECT_CALL(*releasedLayer2, onLayerDisplayed(Property(&sp::get, Eq(presentFence.get())))); EXPECT_CALL(*releasedLayer3, onLayerDisplayed(Property(&sp::get, Eq(presentFence.get())))); mOutput.postFramebuffer(); // After the call the list of released layers should have been cleared. EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty()); } /* * Output::composeSurfaces() */ struct OutputComposeSurfacesTest : public testing::Test { using TestType = OutputComposeSurfacesTest; struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD0(getSkipColorTransform, bool()); MOCK_METHOD3(generateClientCompositionRequests, std::vector(bool, Region&, ui::Dataspace)); MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); }; OutputComposeSurfacesTest() { mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE); mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame; mOutput.mState.layerStackSpace.content = kDefaultOutputViewport; mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip; mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip; mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation; mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags}; mOutput.mState.dataspace = kDefaultOutputDataspace; mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat; mOutput.mState.isSecure = false; mOutput.mState.needsFiltering = false; mOutput.mState.usesClientComposition = true; mOutput.mState.usesDeviceComposition = false; mOutput.mState.reusedClientComposition = false; mOutput.mState.flipClientTarget = false; EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine)); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mCompositionEngine, getTimeStats()) .WillRepeatedly(ReturnRef(*mTimeStats.get())); EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()) .WillRepeatedly(ReturnRef(kHdrCapabilities)); } struct ExecuteState : public CallOrderStateMachineHelper { auto execute() { getInstance()->mReadyFence = getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); return nextState(); } }; struct FenceCheckState : public CallOrderStateMachineHelper { void expectNoFenceWasReturned() { EXPECT_FALSE(getInstance()->mReadyFence); } void expectAFenceWasReturned() { EXPECT_TRUE(getInstance()->mReadyFence); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return ExecuteState::make(this); } static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0; static constexpr uint32_t kDefaultOutputOrientationFlags = ui::Transform::toRotationFlags(kDefaultOutputOrientation); static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN; static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3; static constexpr float kDefaultMaxLuminance = 0.9f; static constexpr float kDefaultAvgLuminance = 0.7f; static constexpr float kDefaultMinLuminance = 0.1f; static const Rect kDefaultOutputFrame; static const Rect kDefaultOutputViewport; static const Rect kDefaultOutputDestinationClip; static const mat4 kDefaultColorTransformMat; static const Region kDebugRegion; static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs; static const HdrCapabilities kHdrCapabilities; StrictMock mCompositionEngine; StrictMock mRenderEngine; // TODO: make this is a proper mock. std::shared_ptr mTimeStats = std::make_shared(); mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); StrictMock mOutput; std::shared_ptr mOutputBuffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); std::optional mReadyFence; }; const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004}; const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008}; const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016}; const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f}; const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs; const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}}; const HdrCapabilities OutputComposeSurfacesTest:: kHdrCapabilities{{}, OutputComposeSurfacesTest::kDefaultMaxLuminance, OutputComposeSurfacesTest::kDefaultAvgLuminance, OutputComposeSurfacesTest::kDefaultMinLuminance}; TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoClientComposition) { mOutput.mState.usesClientComposition = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); verify().execute().expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, dequeuesABufferIfNoClientCompositionButFlipClientTargetRequested) { mOutput.mState.usesClientComposition = false; mOutput.mState.flipClientTarget = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer)); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); verify().execute().expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) { EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr)); verify().execute().expectNoFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForNoClientCompositionButFlipClientTargetRequested) { mOutput.mState.usesClientComposition = false; mOutput.mState.flipClientTarget = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr)); verify().execute().expectNoFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _)) .WillRepeatedly(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{r1})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly( Invoke([&](const Region&, std::vector& clientCompositionLayers) { clientCompositionLayers.emplace_back(r2); })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) .WillRepeatedly(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestListAndCachesFramebufferForInternalLayers) { LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; const constexpr uint32_t kInternalLayerStack = 1234; mOutput.setLayerStackFilter(kInternalLayerStack, true); EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{r1})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly( Invoke([&](const Region&, std::vector& clientCompositionLayers) { clientCompositionLayers.emplace_back(r2); })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _)) .WillRepeatedly(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithoutCache) { mOutput.cacheClientCompositionRequests(0); LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) .Times(2) .WillOnce(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); } TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { mOutput.cacheClientCompositionRequests(3); LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); // We do not expect another call to draw layers. verify().execute().expectAFenceWasReturned(); EXPECT_TRUE(mOutput.mState.reusedClientComposition); } TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); const auto otherOutputBuffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) .WillOnce(Return(mOutputBuffer)) .WillOnce(Return(otherOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) .WillRepeatedly(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); } TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { LayerFE::LayerSettings r1; LayerFE::LayerSettings r2; LayerFE::LayerSettings r3; r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; r3.geometry.boundaries = FloatRect{5, 6, 7, 9}; EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillOnce(Return(std::vector{r1, r2})) .WillOnce(Return(std::vector{r1, r3})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _)) .WillOnce(Return(NO_ERROR)); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); } struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest { OutputComposeSurfacesTest_UsesExpectedDisplaySettings() { EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); } struct MixedCompositionState : public CallOrderStateMachineHelper { auto ifMixedCompositionIs(bool used) { getInstance()->mOutput.mState.usesDeviceComposition = used; return nextState(); } }; struct OutputUsesHdrState : public CallOrderStateMachineHelper { auto andIfUsesHdr(bool used) { EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut()) .WillOnce(Return(used)); return nextState(); } }; struct SkipColorTransformState : public CallOrderStateMachineHelper { auto andIfSkipColorTransform(bool skip) { // May be called zero or one times. EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform()) .WillRepeatedly(Return(skip)); return nextState(); } }; struct ExpectDisplaySettingsState : public CallOrderStateMachineHelper { auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) { EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _)) .WillOnce(Return(NO_ERROR)); return nextState(); } }; // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return MixedCompositionState::make(this); } }; TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) { verify().ifMixedCompositionIs(true) .andIfUsesHdr(true) .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) { verify().ifMixedCompositionIs(true) .andIfUsesHdr(false) .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, kDefaultColorTransformMat, Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(false) .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, kDefaultColorTransformMat, Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) { verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .andIfSkipColorTransform(true) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest { struct Layer { Layer() { EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState)); EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE)); } StrictMock mOutputLayer; sp> mLayerFE = sp>::make(); LayerFECompositionState mLayerFEState; }; OutputComposeSurfacesTest_HandlesProtectedContent() { mLayer1.mLayerFEState.hasProtectedContent = false; mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer1.mOutputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) .WillRepeatedly(Return(&mLayer2.mOutputLayer)); EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _)) .WillRepeatedly(Return(std::vector{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)) .WillRepeatedly(Return(NO_ERROR)); } Layer mLayer1; Layer mLayer2; }; TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) { mOutput.mState.isSecure = false; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(mRenderEngine, useProtectedContext(false)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); EXPECT_CALL(mRenderEngine, useProtectedContext(false)); EXPECT_CALL(*mRenderSurface, setProtected(false)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); // For this test, we also check the call order of key functions. InSequence seq; EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); EXPECT_CALL(mRenderEngine, useProtectedContext(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, setProtected(true)); // Must happen after setting the protected content state. EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); EXPECT_CALL(mRenderEngine, useProtectedContext(true)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, setProtected(true)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); EXPECT_CALL(mRenderEngine, useProtectedContext(true)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest { OutputComposeSurfacesTest_SetsExpensiveRendering() { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); } }; TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) { mOutput.mState.dataspace = kExpensiveOutputDataspace; EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace)) .WillOnce(Return(std::vector{})); // For this test, we also check the call order of key functions. InSequence seq; EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur : public OutputComposeSurfacesTest_SetsExpensiveRendering { OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() { mLayer.layerFEState.backgroundBlurRadius = 10; mLayer.layerFEState.isOpaque = false; mOutput.editState().isEnabled = true; EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillOnce(Return(std::vector{})); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); } NonInjectedLayer mLayer; compositionengine::CompositionRefreshArgs mRefreshArgs; }; TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) { mRefreshArgs.blursAreExpensive = true; mOutput.updateCompositionState(mRefreshArgs); mOutput.planComposition(); mOutput.writeCompositionState(mRefreshArgs); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); mOutput.composeSurfaces(kDebugRegion, mRefreshArgs); } TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) { mRefreshArgs.blursAreExpensive = false; mOutput.updateCompositionState(mRefreshArgs); mOutput.planComposition(); mOutput.writeCompositionState(mRefreshArgs); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0); mOutput.composeSurfaces(kDebugRegion, mRefreshArgs); } /* * Output::generateClientCompositionRequests() */ struct GenerateClientCompositionRequestsTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // compositionengine::Output overrides std::vector generateClientCompositionRequests( bool supportsProtectedContent, Region& clearRegion, ui::Dataspace dataspace) override { return impl::Output::generateClientCompositionRequests(supportsProtectedContent, clearRegion, dataspace); } }; struct Layer { Layer() { EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState)); EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState)); EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE)); EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState)); } StrictMock mOutputLayer; sp> mLayerFE = sp>::make(); LayerFECompositionState mLayerFEState; impl::OutputLayerCompositionState mOutputLayerState; LayerFE::LayerSettings mLayerSettings; }; GenerateClientCompositionRequestsTest() { mOutput.mState.needsFiltering = false; mOutput.setDisplayColorProfileForTest( std::unique_ptr(mDisplayColorProfile)); mOutput.setRenderSurfaceForTest(std::unique_ptr(mRenderSurface)); } mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock(); mock::RenderSurface* mRenderSurface = new StrictMock(); StrictMock mOutput; }; struct GenerateClientCompositionRequestsTest_ThreeLayers : public GenerateClientCompositionRequestsTest { GenerateClientCompositionRequestsTest_ThreeLayers() { mOutput.mState.orientedDisplaySpace.content = kDisplayFrame; mOutput.mState.layerStackSpace.content = kDisplayViewport; mOutput.mState.displaySpace.content = kDisplayDestinationClip; mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)}; mOutput.mState.displaySpace.orientation = kDisplayOrientation; mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = false; for (size_t i = 0; i < mLayers.size(); i++) { mLayers[i].mOutputLayerState.clearClientTarget = false; mLayers[i].mOutputLayerState.visibleRegion = Region(kDisplayFrame); mLayers[i].mLayerFEState.isOpaque = true; mLayers[i].mLayerSettings.geometry.boundaries = FloatRect{static_cast(i + 1), 0.f, 0.f, 0.f}; mLayers[i].mLayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f}; mLayers[i].mLayerSettings.alpha = 1.0f; mLayers[i].mLayerSettings.disableBlending = false; EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(i)) .WillRepeatedly(Return(&mLayers[i].mOutputLayer)); EXPECT_CALL(mLayers[i].mOutputLayer, requiresClientComposition()) .WillRepeatedly(Return(true)); EXPECT_CALL(mLayers[i].mOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); } EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size())); } static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0; static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN; static const Rect kDisplayFrame; static const Rect kDisplayViewport; static const Rect kDisplayDestinationClip; std::array mLayers; }; const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200); const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201); const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103, 203); TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) { EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); EXPECT_EQ(0u, requests.size()); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) { mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 10, 10)); mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10)); mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0)); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); EXPECT_EQ(0u, requests.size()); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) { LayerFE::LayerSettings mShadowSettings; mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector({mLayers[1].mLayerSettings}))); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector( {mShadowSettings, mLayers[2].mLayerSettings}))); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp); } 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; } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) { LayerFE::LayerSettings mShadowSettings; mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector({mLayers[1].mLayerSettings}))); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq( LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly))) .WillOnce(Return(std::vector( {mShadowSettings, mLayers[2].mLayerSettings}))); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, onlyClientComposesClientComposedLayersIfNoClearingNeeded) { EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true)); mLayers[0].mOutputLayerState.clearClientTarget = false; mLayers[1].mOutputLayerState.clearClientTarget = false; mLayers[2].mOutputLayerState.clearClientTarget = false; mLayers[0].mLayerFEState.isOpaque = true; mLayers[1].mLayerFEState.isOpaque = true; mLayers[2].mLayerFEState.isOpaque = true; EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector({mLayers[2].mLayerSettings}))); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, onlyClientComposesClientComposedLayersIfOthersAreNotOpaque) { EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true)); mLayers[0].mOutputLayerState.clearClientTarget = true; mLayers[1].mOutputLayerState.clearClientTarget = true; mLayers[2].mOutputLayerState.clearClientTarget = true; mLayers[0].mLayerFEState.isOpaque = false; mLayers[1].mLayerFEState.isOpaque = false; mLayers[2].mLayerFEState.isOpaque = false; EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector({mLayers[2].mLayerSettings}))); Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) { // If client composition is performed with some layers set to use device // composition, device layers after the first layer (device or client) will // clear the frame buffer if they are opaque and if that layer has a flag // set to do so. The first layer is skipped as the frame buffer is already // expected to be clear. EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true)); mLayers[0].mOutputLayerState.clearClientTarget = true; mLayers[1].mOutputLayerState.clearClientTarget = true; mLayers[2].mOutputLayerState.clearClientTarget = true; mLayers[0].mLayerFEState.isOpaque = true; mLayers[1].mLayerFEState.isOpaque = true; mLayers[2].mLayerFEState.isOpaque = true; Region accumClearRegion(Rect(10, 11, 12, 13)); Region stubRegion; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ stubRegion, /* clear region */ kDisplayViewport, kDisplayDataspace, false /* realContentIsVisible */, true /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings; mBlackoutSettings.source.buffer.buffer = nullptr; mBlackoutSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; mBlackoutSettings.alpha = 0.f; mBlackoutSettings.disableBlending = true; EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector({mBlackoutSettings}))); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector({mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(2u, requests.size()); // The second layer is expected to be rendered as alpha=0 black with no blending EXPECT_EQ(mBlackoutSettings, requests[0]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clippedVisibleRegionUsedToGenerateRequest) { mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 20, 20)); mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30)); mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000)); Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(Rect(10, 10, 20, 20)), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(Rect(0, 0, 30, 30)), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(Rect(0, 0, 40, 201)), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector())); static_cast( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, perLayerNeedsFilteringUsedToGenerateRequests) { mOutput.mState.needsFiltering = false; EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true)); Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector())); static_cast( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, wholeOutputNeedsFilteringUsedToGenerateRequests) { mOutput.mState.needsFiltering = true; EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true)); Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector())); static_cast( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, wholeOutputSecurityUsedToGenerateRequests) { mOutput.mState.isSecure = true; Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector())); static_cast( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, protectedContentSupportUsedToGenerateRequests) { Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings)))) .WillOnce(Return(std::vector())); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector())); static_cast(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace)); } TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) { InjectedLayer layer1; InjectedLayer layer2; uint32_t z = 0; // Layer requesting blur, or below, should request client composition, unless opaque. EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); layer2.layerFEState.backgroundBlurRadius = 10; layer2.layerFEState.isOpaque = true; injectOutputLayer(layer1); injectOutputLayer(layer2); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; args.devOptForceClientComposition = false; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; uint32_t z = 0; // Layer requesting blur, or below, should request client composition. EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); layer2.layerFEState.backgroundBlurRadius = 10; layer2.layerFEState.isOpaque = false; injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; args.devOptForceClientComposition = false; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { InjectedLayer layer1; InjectedLayer layer2; InjectedLayer layer3; uint32_t z = 0; // Layer requesting blur, or below, should request client composition. EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); BlurRegion region; layer2.layerFEState.blurRegions.push_back(region); layer2.layerFEState.isOpaque = false; injectOutputLayer(layer1); injectOutputLayer(layer2); injectOutputLayer(layer3); mOutput->editState().isEnabled = true; CompositionRefreshArgs args; args.updatingGeometryThisFrame = false; args.devOptForceClientComposition = false; mOutput->updateCompositionState(args); mOutput->planComposition(); mOutput->writeCompositionState(args); } TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer // on the right covering that side of the output. const Rect kPortraitFrame(0, 0, 1000, 2000); const Rect kPortraitViewport(0, 0, 2000, 1000); const Rect kPortraitDestinationClip(0, 0, 1000, 2000); const ui::Rotation kPortraitOrientation = ui::ROTATION_90; constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3; mOutput.mState.orientedDisplaySpace.content = kPortraitFrame; mOutput.mState.layerStackSpace.content = kPortraitViewport; mOutput.mState.displaySpace.content = kPortraitDestinationClip; mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)}; mOutput.mState.displaySpace.orientation = kPortraitOrientation; mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = true; Layer leftLayer; Layer rightLayer; leftLayer.mOutputLayerState.clearClientTarget = false; leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000)); leftLayer.mLayerFEState.isOpaque = true; leftLayer.mLayerSettings.source.solidColor = {1.f, 0.f, 0.f}; rightLayer.mOutputLayerState.clearClientTarget = false; rightLayer.mOutputLayerState.visibleRegion = Region(Rect(1000, 0, 2000, 1000)); rightLayer.mLayerFEState.isOpaque = true; rightLayer.mLayerSettings.source.solidColor = {0.f, 1.f, 0.f}; EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&leftLayer.mOutputLayer)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) .WillRepeatedly(Return(&rightLayer.mOutputLayer)); Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{ Region(Rect(0, 0, 1000, 1000)), false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings)))) .WillOnce(Return(std::vector({leftLayer.mLayerSettings}))); compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{ Region(Rect(1000, 0, 2000, 1000)), false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings)))) .WillOnce(Return(std::vector({rightLayer.mLayerSettings}))); constexpr bool supportsProtectedContent = true; auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, accumClearRegion, kOutputDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(leftLayer.mLayerSettings, requests[0]); EXPECT_EQ(rightLayer.mLayerSettings, requests[1]); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, shadowRegionOnlyVisibleSkipsContentComposition) { const Rect kContentWithShadow(40, 40, 70, 90); const Rect kContent(50, 50, 60, 80); const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent); const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80)); Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, false /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; LayerFE::LayerSettings mShadowSettings; mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; mLayers[2].mOutputLayerState.visibleRegion = kPartialShadowRegion; mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion; EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings)))) .WillOnce(Return(std::vector({mShadowSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, shadowRegionWithContentVisibleRequestsContentAndShadowComposition) { const Rect kContentWithShadow(40, 40, 70, 90); const Rect kContent(50, 50, 60, 80); const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent); const Region kPartialContentWithPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80)); LayerFE::LayerSettings mShadowSettings; mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f}; mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion; mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion; Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, false /* clearContent */, compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled, }; EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings)))) .WillOnce(Return(std::vector( {mShadowSettings, mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, accumClearRegion, kDisplayDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); } } // namespace } // namespace android::compositionengine