Identify a PIP window and implement hole punch

Bug: 163076219
Test: manual
Test: I53fc851eca876d44ba7cb9347f1c62d659c38932

In order to use a hole punch + device compositing to render a PIP, we
need to find a PIP that follows a CachedSet. A PIP (or, more generally,
a layer that we can render in this way in order to use device
composition more frequently) must have rounded corners and a buffer.

Add LayerFE::hasRoundedCorners (and implement in Layer) to allow
identifying whether a layer has rounded corners.

If a CachedSet has a hole punch layer, use a modified version of its
LayerSettings to draw a transparent round rect into its buffer.

Only active if caching and debug.sf.enable_hole_punch_pip are enabled
and PIPs use rounded corners (as in frameworks/base change
Ib0f61bbcbee6ead82beec2b149b0892836492b78).

Change-Id: I56e2a6debce6ede4bebfcbd32bffa01c20461542
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 1fd07b0..791e7db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -138,6 +138,9 @@
 
     // Gets the sequence number: a serial number that uniquely identifies a Layer
     virtual int32_t getSequence() const = 0;
+
+    // Whether the layer should be rendered with rounded corners.
+    virtual bool hasRoundedCorners() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 53f4a30..7f068d4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -105,12 +105,23 @@
 
     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.
+    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*);
+
 private:
     CachedSet() = default;
 
     const NonBufferHash mFingerprint;
     std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
     std::vector<Layer> mLayers;
+    const LayerState* mHolePunchLayer = nullptr;
     Rect mBounds = Rect::EMPTY_RECT;
     Region mVisibleRegion;
     size_t mAge = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 2f2ad4c..942592a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -36,7 +36,8 @@
 
 class Flattener {
 public:
-    Flattener(Predictor& predictor) : mPredictor(predictor) {}
+    Flattener(Predictor& predictor, bool enableHolePunch = false)
+          : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {}
 
     void setDisplaySize(ui::Size size) { mDisplaySize = size; }
 
@@ -61,6 +62,7 @@
 
     void buildCachedSets(std::chrono::steady_clock::time_point now);
 
+    const bool mEnableHolePunch;
     Predictor& mPredictor;
 
     ui::Size mDisplaySize;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e6d2b63..c2037a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -41,7 +41,7 @@
 // as a more efficient representation of parts of the layer stack.
 class Planner {
 public:
-    Planner() : mFlattener(mPredictor) {}
+    Planner();
 
     void setDisplaySize(ui::Size);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index dde8999..d215bda 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -43,6 +43,7 @@
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
+    MOCK_CONST_METHOD0(hasRoundedCorners, bool());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 9955e29..238282e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -193,6 +193,23 @@
     std::transform(layerSettings.cbegin(), layerSettings.cend(),
                    std::back_inserter(layerSettingsPointers),
                    [](const renderengine::LayerSettings& settings) { return &settings; });
+    renderengine::LayerSettings holePunchSettings;
+    if (mHolePunchLayer) {
+        auto clientCompositionList =
+                mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+                        targetSettings);
+        // Assume that the final layer contains the buffer that we want to
+        // replace with a hole punch.
+        holePunchSettings = clientCompositionList.back();
+        LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
+        // This mimics Layer::prepareClearClientComposition
+        holePunchSettings.source.buffer.buffer = nullptr;
+        holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+        holePunchSettings.disableBlending = true;
+        holePunchSettings.alpha = 0.0f;
+        holePunchSettings.name = std::string("hole punch layer");
+        layerSettingsPointers.push_back(&holePunchSettings);
+    }
 
     if (sDebugHighlighLayers) {
         highlight = {
@@ -241,6 +258,26 @@
     }
 }
 
+bool CachedSet::requiresHolePunch() const {
+    // In order for the hole punch to be beneficial, the layer must be updating
+    // regularly, meaning  it should not have been merged with other layers.
+    if (getLayerCount() != 1) {
+        return false;
+    }
+
+    // There is no benefit to a hole punch unless the layer has a buffer.
+    if (!mLayers[0].getBuffer()) {
+        return false;
+    }
+
+    const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
+    return layerFE.hasRoundedCorners();
+}
+
+void CachedSet::addHolePunchLayer(const LayerState* layerState) {
+    mHolePunchLayer = layerState;
+}
+
 void CachedSet::dump(std::string& result) const {
     const auto now = std::chrono::steady_clock::now();
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 9c9649c..960d8e1 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -298,6 +298,11 @@
 
     std::vector<Run> runs;
     bool isPartOfRun = false;
+
+    // Keep track of the layer that follows a run. It's possible that we will
+    // render it with a hole-punch.
+    const CachedSet* holePunchLayer = nullptr;
+
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
         if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
             // Layer is inactive
@@ -312,10 +317,20 @@
                     isPartOfRun = true;
                 }
             }
-        } else {
+        } else if (isPartOfRun) {
             // Runs must be at least 2 sets long or there's nothing to combine
-            if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+            if (runs.back().start->getLayerCount() == runs.back().length) {
                 runs.pop_back();
+            } else {
+                // The prior run contained at least two sets. Currently, we'll
+                // only possibly merge a single run, so only keep track of a
+                // holePunchLayer if this is the first run.
+                if (runs.size() == 1) {
+                    holePunchLayer = &(*currentSet);
+                }
+
+                // TODO(b/185114532: Break out of the loop? We may find more runs, but we
+                // won't do anything with them.
             }
 
             isPartOfRun = false;
@@ -341,6 +356,12 @@
         mNewCachedSet->append(*currentSet);
     }
 
+    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());
+    }
+
     // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
     mPredictor.getPredictedPlan({}, 0);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 3a2534b..7d2bf06 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -19,12 +19,17 @@
 #undef LOG_TAG
 #define LOG_TAG "Planner"
 
+#include <android-base/properties.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 
 namespace android::compositionengine::impl::planner {
 
+Planner::Planner()
+      : mFlattener(mPredictor,
+                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {}
+
 void Planner::setDisplaySize(ui::Size size) {
     mFlattener.setDisplaySize(size);
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9f3ea9a..688a2c3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -600,6 +600,8 @@
     // ignored.
     virtual RoundedCornerState getRoundedCornerState() const;
 
+    bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     /**
      * Return whether this layer needs an input info. For most layer types