diff options
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/src/Output.cpp | 25 | ||||
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp | 41 |
2 files changed, 64 insertions, 2 deletions
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 070b285f03..aa0b152db0 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -586,8 +586,29 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // Remove the transparent area from the visible region if (!layerFEState->isOpaque) { if (tr.preserveRects()) { - // transform the transparent region - transparentRegion = tr.transform(layerFEState->transparentRegionHint); + // Clip the transparent region to geomLayerBounds first + // The transparent region may be influenced by applications, for + // instance, by overriding ViewGroup#gatherTransparentRegion with a + // custom view. Once the layer stack -> display mapping is known, we + // must guard against very wrong inputs to prevent underflow or + // overflow errors. We do this here by constraining the transparent + // region to be within the pre-transform layer bounds, since the + // layer bounds are expected to play nicely with the full + // transform. + const Region clippedTransparentRegionHint = + layerFEState->transparentRegionHint.intersect( + Rect(layerFEState->geomLayerBounds)); + + if (clippedTransparentRegionHint.isEmpty()) { + if (!layerFEState->transparentRegionHint.isEmpty()) { + ALOGD("Layer: %s had an out of bounds transparent region", + layerFE->getDebugName()); + layerFEState->transparentRegionHint.dump("transparentRegionHint"); + } + transparentRegion.clear(); + } else { + transparentRegion = tr.transform(clippedTransparentRegionHint); + } } else { // transformation too complex, can't do the // transparent region optimization. diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 505f94ee84..cf12890310 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -1505,6 +1505,8 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { static const Region kTransparentRegionHint; static const Region kTransparentRegionHintTwo; static const Region kTransparentRegionHintTwo90Rotation; + static const Region kTransparentRegionHintNegative; + static const Region kTransparentRegionHintNegativeIntersectsBounds; StrictMock<OutputPartialMock> mOutput; LayerFESet mGeomSnapshots; @@ -1528,6 +1530,10 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo = Region(Rect(25, 20, 50, 75)); const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation = Region(Rect(125, 25, 180, 50)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative = + Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds = + Region(Rect(INT32_MIN, INT32_MIN, 100, 100)); TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); @@ -1997,6 +2003,41 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, blockingRegionIsInOutputSpace) { RegionEq(kTransparentRegionHintTwo90Rotation)); } +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) + .WillOnce(Return(&mLayer.outputLayer)); + ensureOutputLayerIfVisible(); + + // Check that the blocking region clips an out-of-bounds transparent region. + EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, + RegionEq(kTransparentRegionHint)); +} + /* * Output::present() */ |