Fix clipping and stencil layer issues

bug:8235699

Ensure rectangle clipping operations disable deferring when necessary
(i.e., when the op might create a non-rect region), including in
DisplayList::setViewProperties

Additionally, makes clipping with a kUnion always use a region, for
consistency with software rendering

Change-Id: I6730f1a80250bcf3f91cd4afde646d470a12dbc2
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 398f719..8bfbf22 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -356,7 +356,9 @@
     }
 }
 
-void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t level) {
+status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty,
+        int32_t flags, uint32_t level, DeferredDisplayList* deferredList) {
+    status_t status = DrawGlInfo::kStatusDone;
 #if DEBUG_DISPLAYLIST
     outputViewProperties(level);
 #endif
@@ -377,6 +379,9 @@
         }
     }
     if (mAlpha < 1 && !mCaching) {
+        // flush since we'll either enter a Layer, or set alpha, both not supported in deferral
+        status |= deferredList->flush(renderer, dirty, flags, level);
+
         if (!mHasOverlappingRendering) {
             renderer.setAlpha(mAlpha);
         } else {
@@ -392,9 +397,14 @@
         }
     }
     if (mClipChildren && !mCaching) {
+        if (CC_UNLIKELY(!renderer.hasRectToRectTransform())) {
+            // flush, since clip will likely be a region
+            status |= deferredList->flush(renderer, dirty, flags, level);
+        }
         renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop,
                 SkRegion::kIntersect_Op);
     }
+    return status;
 }
 
 status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level,
@@ -414,12 +424,7 @@
     DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
 
-    if (mAlpha < 1 && !mCaching && CC_LIKELY(deferredList)) {
-        // flush before a saveLayerAlpha/setAlpha
-        // TODO: make this cleaner
-        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
-    }
-    setViewProperties(renderer, level);
+    drawGlStatus |= setViewProperties(renderer, dirty, flags, level, deferredList);
 
     if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
         DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 86c9ec0..f2a2383 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -75,7 +75,8 @@
         kReplayFlag_ClipChildren = 0x1
     };
 
-    void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
+    status_t setViewProperties(OpenGLRenderer& renderer, Rect& dirty,
+            int32_t flags, uint32_t level, DeferredDisplayList* deferredList);
     void outputViewProperties(uint32_t level);
 
     ANDROID_API size_t getSize();
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 1bae0ff..93b281a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -100,7 +100,7 @@
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
             uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         status_t status = DrawGlInfo::kStatusDone;
-        if (deferredList && requiresDrawOpFlush()) {
+        if (deferredList && requiresDrawOpFlush(renderer)) {
             // will be setting renderer state that affects ops in deferredList, so flush list first
             status |= deferredList->flush(renderer, dirty, flags, level);
         }
@@ -114,7 +114,7 @@
      * Returns true if it affects renderer drawing state in such a way to break deferral
      * see OpenGLRenderer::disallowDeferral()
      */
-    virtual bool requiresDrawOpFlush() { return false; }
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) { return false; }
 };
 
 class DrawOp : public DisplayListOp {
@@ -272,7 +272,7 @@
     }
 
     virtual const char* name() { return "SaveLayer"; }
-    virtual bool requiresDrawOpFlush() { return true; }
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) { return true; }
 
 private:
     Rect mArea;
@@ -294,7 +294,7 @@
     }
 
     virtual const char* name() { return "SaveLayerAlpha"; }
-    virtual bool requiresDrawOpFlush() { return true; }
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) { return true; }
 
 private:
     Rect mArea;
@@ -434,7 +434,16 @@
 
     virtual const char* name() { return "ClipRect"; }
 
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) {
+        // TODO: currently, we flush when we *might* cause a clip region to exist. Ideally, we
+        // should only flush when a non-rectangular clip would result
+        return !renderer.hasRectToRectTransform() || !hasRectToRectOp();
+    }
+
 private:
+    inline bool hasRectToRectOp() {
+        return mOp == SkRegion::kIntersect_Op || mOp == SkRegion::kReplace_Op;
+    }
     Rect mArea;
     SkRegion::Op mOp;
 };
@@ -455,7 +464,7 @@
     }
 
     virtual const char* name() { return "ClipPath"; }
-    virtual bool requiresDrawOpFlush() { return true; }
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) { return true; }
 
 private:
     SkPath* mPath;
@@ -478,7 +487,7 @@
     }
 
     virtual const char* name() { return "ClipRegion"; }
-    virtual bool requiresDrawOpFlush() { return true; }
+    virtual bool requiresDrawOpFlush(OpenGLRenderer& renderer) { return true; }
 
 private:
     SkRegion* mRegion;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6b614a7..e5a1a84 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1284,6 +1284,10 @@
     }
 }
 
+bool OpenGLRenderer::hasRectToRectTransform() {
+    return CC_LIKELY(mSnapshot->transform->rectToRect());
+}
+
 void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
     mSnapshot->transform->copyTo(*matrix);
 }
@@ -1505,8 +1509,10 @@
     if (clear) clearLayerRegions();
     // Make sure setScissor & setStencil happen at the beginning of
     // this method
-    if (mDirtyClip && mCaches.scissorEnabled) {
-        setScissorFromClip();
+    if (mDirtyClip) {
+        if (mCaches.scissorEnabled) {
+            setScissorFromClip();
+        }
         setStencilFromClip();
     }
     mDescription.reset();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index e12083c..80f2081 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -198,6 +198,7 @@
     virtual void scale(float sx, float sy);
     virtual void skew(float sx, float sy);
 
+    bool hasRectToRectTransform();
     ANDROID_API void getMatrix(SkMatrix* matrix);
     virtual void setMatrix(SkMatrix* matrix);
     virtual void concatMatrix(SkMatrix* matrix);
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 19a5db7..923913e 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -132,15 +132,6 @@
             }
             break;
         }
-        case SkRegion::kUnion_Op: {
-            if (CC_UNLIKELY(!clipRegion->isEmpty())) {
-                ensureClipRegion();
-                clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
-            } else {
-                clipped = clipRect->unionWith(r);
-            }
-            break;
-        }
         case SkRegion::kReplace_Op: {
             setClip(r.left, r.top, r.right, r.bottom);
             clipped = true;