Put WebViews with on a HW layer if stencil/shader clipping is needed

bug:17322378
Change-Id: I0de574bf116b30e2ad4194366e19d47d49708902
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 9354e94..b35db28 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -155,8 +155,9 @@
     inline bool currentlyIgnored() const { return currentSnapshot()->isIgnored(); }
     int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
     int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
-    int getWidth() { return mWidth; }
-    int getHeight() { return mHeight; }
+    int getWidth() const { return mWidth; }
+    int getHeight() const { return mHeight; }
+    bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
 
     inline const Snapshot* currentSnapshot() const {
         return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get();
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 900a621..843c412 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -203,10 +203,10 @@
 
 void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
     LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
-
-    // dirty is an out parameter and should not be recorded,
-    // it matters only when replaying the display list
-    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, *mState.currentTransform());
+    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
+            renderNode,
+            *mState.currentTransform(),
+            mState.clipIsSimple());
     addRenderNodeOp(op);
 }
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e9d6ebc..fb2852a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1398,9 +1398,10 @@
     friend class RenderNode; // grant RenderNode access to info of child
     friend class DisplayListData; // grant DisplayListData access to info of child
 public:
-    DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent)
+    DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
             : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
             , mRenderNode(renderNode)
+            , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
             , mTransformFromParent(transformFromParent)
             , mSkipInOrderDraw(false) {}
 
@@ -1436,6 +1437,20 @@
 private:
     RenderNode* mRenderNode;
 
+    /**
+     * This RenderNode was drawn into a DisplayList with the canvas in a state that will likely
+     * require rendering with stencil clipping. Either:
+     *
+     * 1) A path clip or rotated rect clip was in effect on the canvas at record time
+     * 2) The RenderNode was recorded with a non-simple canvas transform (e.g. rotation)
+     *
+     * Note: even if this is false, non-rect clipping may still be applied applied either due to
+     * property-driven rotation (either in this RenderNode, or any ancestor), or record time
+     * clipping in an ancestor. These are handled in RenderNode::prepareTreeImpl since they are
+     * dynamic (relative to a static DisplayList of a parent), and don't affect this flag.
+     */
+    bool mRecordedWithPotentialStencilClip;
+
     ///////////////////////////
     // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
     ///////////////////////////
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 75e700a..b4cbc36 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -120,7 +120,10 @@
     ATRACE_CALL();
     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
 
-    prepareTreeImpl(info);
+    // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
+    bool functorsNeedLayer = Properties::debugOverdraw;
+
+    prepareTreeImpl(info, functorsNeedLayer);
 }
 
 void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
@@ -219,7 +222,15 @@
     }
 }
 
-void RenderNode::prepareTreeImpl(TreeInfo& info) {
+/**
+ * Traverse down the the draw tree to prepare for a frame.
+ *
+ * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
+ *
+ * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
+ * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
+ */
+void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
     info.damageAccumulator->pushTransform(this);
 
     if (info.mode == TreeInfo::MODE_FULL) {
@@ -229,11 +240,17 @@
     if (CC_LIKELY(info.runAnimations)) {
         animatorDirtyMask = mAnimatorManager.animate(info);
     }
+
+    bool willHaveFunctor = info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData
+            ? !mStagingDisplayListData->functors.isEmpty() : !mDisplayListData->functors.isEmpty();
+    bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
+            willHaveFunctor, functorsNeedLayer);
+
     prepareLayer(info, animatorDirtyMask);
     if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingDisplayListChanges(info);
     }
-    prepareSubTree(info, mDisplayListData);
+    prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
     pushLayerUpdate(info);
 
     info.damageAccumulator->popTransform();
@@ -313,7 +330,7 @@
     mDisplayListData = nullptr;
 }
 
-void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
+void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) {
     if (subtree) {
         TextureCache& cache = Caches::getInstance().textureCache;
         info.out.hasFunctors |= subtree->functors.size();
@@ -324,7 +341,10 @@
             DrawRenderNodeOp* op = subtree->children()[i];
             RenderNode* childNode = op->mRenderNode;
             info.damageAccumulator->pushTransform(&op->mTransformFromParent);
-            childNode->prepareTreeImpl(info);
+            bool childFunctorsNeedLayer = functorsNeedLayer
+                    // Recorded with non-rect clip, or canvas-rotated by parent
+                    || op->mRecordedWithPotentialStencilClip;
+            childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
             info.damageAccumulator->popTransform();
         }
     }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d0d81d9..025a4a4 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -235,10 +235,10 @@
         const char* mText;
     };
 
-    void prepareTreeImpl(TreeInfo& info);
+    void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
     void pushStagingDisplayListChanges(TreeInfo& info);
-    void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+    void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree);
     void applyLayerPropertiesToLayer(TreeInfo& info);
     void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
     void pushLayerUpdate(TreeInfo& info);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 65c1c4a..81cf2df 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -158,6 +158,32 @@
         }
     }
 
+    /**
+     * Set internal layer state based on whether this layer
+     *
+     * Additionally, returns true if child RenderNodes with functors will need to use a layer
+     * to support clipping.
+     */
+    bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) {
+        // parent may have already dictated that a descendant layer is needed
+        bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer
+
+                // Round rect clipping forces layer for functors
+                || CC_UNLIKELY(getOutline().willClip())
+                || CC_UNLIKELY(getRevealClip().willClip())
+
+                // Complex matrices forces layer, due to stencil clipping
+                || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate())
+                || CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate())
+                || CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate());
+
+        mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer);
+
+        // If on a layer, will have consumed the need for isolating functors from stencil.
+        // Thus, it's safe to reset the flag until some descendent sets it.
+        return CC_LIKELY(effectiveLayerType() == LayerType::None) && functorsNeedLayer;
+    }
+
     RenderProperties& operator=(const RenderProperties& other);
 
     bool setClipToBounds(bool clipToBounds) {
@@ -580,15 +606,16 @@
     bool promotedToLayer() const {
         const int maxTextureSize = Caches::getInstance().maxTextureSize;
         return mLayerProperties.mType == LayerType::None
-                && !MathUtils::isZero(mPrimitiveFields.mAlpha)
-                && mPrimitiveFields.mAlpha < 1
-                && mPrimitiveFields.mHasOverlappingRendering
                 && mPrimitiveFields.mWidth <= maxTextureSize
-                && mPrimitiveFields.mHeight <= maxTextureSize;
+                && mPrimitiveFields.mHeight <= maxTextureSize
+                && (mComputedFields.mNeedLayerForFunctors
+                        || (!MathUtils::isZero(mPrimitiveFields.mAlpha)
+                                && mPrimitiveFields.mAlpha < 1
+                                && mPrimitiveFields.mHasOverlappingRendering));
     }
 
     LayerType effectiveLayerType() const {
-        return promotedToLayer() ? LayerType::RenderLayer : mLayerProperties.mType;
+        return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
     }
 
 private:
@@ -636,6 +663,9 @@
         SkMatrix* mTransformMatrix;
 
         Sk3DView mTransformCamera;
+
+        // Force layer on for functors to enable render features they don't yet support (clipping)
+        bool mNeedLayerForFunctors = false;
     } mComputedFields;
 };