Merge "Put Vulkan WebViews on a HW layer if stencil clip"
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 6dc9d34..8594766 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -759,6 +759,8 @@
 void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) {
     this->resetCanvas(bounds.right(), bounds.bottom());
     fDL = dl;
+    mClipMayBeComplex = false;
+    mSaveCount = mComplexSaveCount = 0;
 }
 
 sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
@@ -770,6 +772,7 @@
 }
 
 void RecordingCanvas::willSave() {
+    mSaveCount++;
     fDL->save();
 }
 SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
@@ -778,6 +781,11 @@
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 void RecordingCanvas::willRestore() {
+    mSaveCount--;
+    if (mSaveCount < mComplexSaveCount) {
+        mClipMayBeComplex = false;
+        mComplexSaveCount = 0;
+    }
     fDL->restore();
 }
 
@@ -798,17 +806,27 @@
 
 void RecordingCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle style) {
     fDL->clipRect(rect, op, style == kSoft_ClipEdgeStyle);
+    if (!getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     this->INHERITED::onClipRect(rect, op, style);
 }
 void RecordingCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) {
+    if (rrect.getType() > SkRRect::kRect_Type || !getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     fDL->clipRRect(rrect, op, style == kSoft_ClipEdgeStyle);
     this->INHERITED::onClipRRect(rrect, op, style);
 }
 void RecordingCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle style) {
+    setClipMayBeComplex();
     fDL->clipPath(path, op, style == kSoft_ClipEdgeStyle);
     this->INHERITED::onClipPath(path, op, style);
 }
 void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
+    if (region.isComplex() || !getTotalMatrix().isScaleTranslate()) {
+        setClipMayBeComplex();
+    }
     fDL->clipRegion(region, op);
     this->INHERITED::onClipRegion(region, op);
 }
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index caaef67..3a76ca1 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -203,10 +203,41 @@
 
     void drawVectorDrawable(VectorDrawableRoot* tree);
 
+    /**
+     * If "isClipMayBeComplex" returns false, it is guaranteed the current clip is a rectangle.
+     * If the return value is true, then clip may or may not be complex (there is no guarantee).
+     */
+    inline bool isClipMayBeComplex() { return mClipMayBeComplex; }
+
 private:
     typedef SkCanvasVirtualEnforcer<SkNoDrawCanvas> INHERITED;
 
+    inline void setClipMayBeComplex() {
+        if (!mClipMayBeComplex) {
+            mComplexSaveCount = mSaveCount;
+            mClipMayBeComplex = true;
+        }
+    }
+
     DisplayListData* fDL;
+
+    /**
+     * mClipMayBeComplex tracks if the current clip is a rectangle. This flag is used to promote
+     * FunctorDrawable to a layer, if it is clipped by a non-rect.
+     */
+    bool mClipMayBeComplex = false;
+
+    /**
+     * mSaveCount is the current level of our save tree.
+     */
+    int mSaveCount = 0;
+
+    /**
+     * mComplexSaveCount is the first save level, which has a complex clip. Every level below
+     * mComplexSaveCount is assumed to have a complex clip and every level above mComplexSaveCount
+     * is guaranteed to not be complex.
+     */
+    int mComplexSaveCount = 0;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ddb7e4e..e6710cc 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -151,6 +151,7 @@
         // parent may have already dictated that a descendant layer is needed
         bool functorsNeedLayer =
                 ancestorDictatesFunctorsNeedLayer
+                || CC_UNLIKELY(isClipMayBeComplex())
 
                 // Round rect clipping forces layer for functors
                 || CC_UNLIKELY(getOutline().willRoundRectClip()) ||
@@ -193,6 +194,12 @@
 
     bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; }
 
+    bool setClipMayBeComplex(bool isClipMayBeComplex) {
+        return RP_SET(mPrimitiveFields.mClipMayBeComplex, isClipMayBeComplex);
+    }
+
+    bool isClipMayBeComplex() const { return mPrimitiveFields.mClipMayBeComplex; }
+
     bool setStaticMatrix(const SkMatrix* matrix) {
         delete mStaticMatrix;
         if (matrix) {
@@ -563,6 +570,7 @@
         bool mProjectBackwards = false;
         bool mProjectionReceiver = false;
         bool mAllowForceDark = true;
+        bool mClipMayBeComplex = false;
         Rect mClipBounds;
         Outline mOutline;
         RevealClip mRevealClip;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 230065c..29d5ef2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -73,7 +73,6 @@
         RenderNode* childNode = child.getRenderNode();
         Matrix4 mat4(child.getRecordedMatrix());
         info.damageAccumulator->pushTransform(&mat4);
-        // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
         info.hasBackwardProjectedNodes = false;
         childFn(childNode, observer, info, functorsNeedLayer);
         hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index d54275f..b67aea2 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -113,6 +113,10 @@
     // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
     mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
     auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // Put Vulkan WebViews with non-rectangular clips in a HW layer
+        renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
+    }
     drawDrawable(&renderNodeDrawable);
 
     // use staging property, since recording on UI thread