SF: only hole punch through opaque layers
Bug: 163076219
Test: manual
The hole punch requires that the layer with the hole be opaque where the
PIP is (or more accurately, where its corners are), so that it will
cover up the square corners. Only add a hole punch if a layer in the
prior CachedSet is opaque and contains the PIP's area.
Consider a layer with rounded corners non-opaque for simplicity and
practical reasons (it is an unexpected use case).
Consider the very first layer to be opaque because nothing is drawn
behind it (besides the layer that peeks through the hole punch). This
allows apps like Gmail, which may not report its layer to be opaque, to
support using a hole punch.
Change-Id: I593bedb1308cf318431b5ef7f177f20d91102a9b
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 14c18ec..1ee0e2f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -106,14 +106,18 @@
void dump(std::string& result) const;
// Whether this represents a single layer with a buffer and rounded corners.
- // If it is, we can draw it by placing it behind another CachedSet and
- // punching a hole.
+ // If it is, we may be able to draw it by placing it behind another
+ // CachedSet and punching a hole.
bool requiresHolePunch() const;
// Add a layer that will be drawn behind this one. ::render() will render a
// hole in this CachedSet's buffer, allowing the supplied layer to peek
// through. Must be called before ::render().
- void addHolePunchLayer(const LayerState*);
+ // Will do nothing if this CachedSet is not opaque where the hole punch
+ // layer is displayed.
+ // If isFirstLayer is true, this CachedSet can be considered opaque because
+ // nothing (besides the hole punch layer) will be drawn behind it.
+ void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
// Retrieve the layer that will be drawn behind this one.
OutputLayer* getHolePunchLayer() const;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 989b155..b31e89e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -278,8 +278,30 @@
return layerFE.hasRoundedCorners();
}
-void CachedSet::addHolePunchLayer(const LayerState* layerState) {
- mHolePunchLayer = layerState;
+namespace {
+bool contains(const Rect& outer, const Rect& inner) {
+ return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
+ outer.bottom >= inner.bottom;
+}
+}; // namespace
+
+void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
+ // Verify that this CachedSet is opaque where the hole punch layer
+ // will draw.
+ const Rect& holePunchBounds = holePunchLayer.getBounds();
+ for (const auto& layer : mLayers) {
+ // The first layer is considered opaque because nothing is behind it.
+ // Note that isOpaque is always false for a layer with rounded
+ // corners, even if the interior is opaque. In theory, such a layer
+ // could be used for a hole punch, but this is unlikely to happen in
+ // practice.
+ const auto* outputLayer = layer.getState()->getOutputLayer();
+ if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
+ (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
+ mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
+ return;
+ }
+ }
}
OutputLayer* CachedSet::getHolePunchLayer() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index a4b979e..13de622 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -363,7 +363,8 @@
if (mEnableHolePunch && holePunchLayer && holePunchLayer->requiresHolePunch()) {
// Add the pip layer to mNewCachedSet, but in a special way - it should
// replace the buffer with a clear round rect.
- mNewCachedSet->addHolePunchLayer(holePunchLayer->getFirstLayer().getState());
+ mNewCachedSet->addHolePunchLayerIfFeasible(*holePunchLayer,
+ runs[0].start == mLayers.cbegin());
}
// TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run