Overdraw avoidance and merging of clipped ops

bug:8951267

If an opaque op, or group of opaque ops covers the invalidate region,
skip draw operations that precede it.

Clipped operations may now be merged, but only if they share a
clipRect - this is a very case for e.g. ListView, where all background
elements may now be a part of the same MergingDrawBatch.

It is this more aggressive merging that groups together clipped
background elements in the ListView case, enabling the overdraw
avoidance skipping the window background.

Change-Id: Ib0961977e272c5ac37f59e4c67d828467422d259
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d95a62c..5ac06cf 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1360,15 +1360,26 @@
         // state has bounds initialized in local coordinates
         if (!state.mBounds.isEmpty()) {
             currentMatrix.mapRect(state.mBounds);
-            state.mClipped = !currentClip.contains(state.mBounds);
-            if (!state.mBounds.intersect(currentClip)) {
+            Rect clippedBounds(state.mBounds);
+            if(!clippedBounds.intersect(currentClip)) {
                 // quick rejected
                 return true;
             }
+
+            state.mClipSideFlags = kClipSide_Unclipped;
+            if (!currentClip.contains(state.mBounds)) {
+                int& flags = state.mClipSideFlags;
+                // op partially clipped, so record which sides are clipped for clip-aware merging
+                if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
+                if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
+                if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
+                if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
+            }
+            state.mBounds.set(clippedBounds);
         } else {
             // If we don't have bounds, let's assume we're clipped
             // to prevent merging
-            state.mClipped = true;
+            state.mClipSideFlags = kClipSide_Full;
             state.mBounds.set(currentClip);
         }
     }
@@ -1392,14 +1403,27 @@
     mSnapshot->alpha = state.mAlpha;
 
     if (state.mClipValid && !skipClipRestore) {
-        mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
+        mSnapshot->setClip(state.mClip.left, state.mClip.top,
+                state.mClip.right, state.mClip.bottom);
         dirtyClip();
     }
 }
 
-void OpenGLRenderer::setFullScreenClip() {
-    mSnapshot->setClip(0, 0, mWidth, mHeight);
+/**
+ * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
+ * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
+ * least one op is clipped), or disabled entirely (because no merged op is clipped)
+ *
+ * This method should be called when restoreDisplayState() won't be restoring the clip
+ */
+void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
+    if (clipRect != NULL) {
+        mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+    } else {
+        mSnapshot->setClip(0, 0, mWidth, mHeight);
+    }
     dirtyClip();
+    mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1965,7 +1989,8 @@
             return status | replayStruct.mDrawGlStatus;
         }
 
-        DeferredDisplayList deferredList;
+        bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
+        DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         displayList->defer(deferStruct, 0);
 
@@ -2016,10 +2041,6 @@
 
 status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
         bool transformed, const Rect& bounds, SkPaint* paint) {
-
-    // merged draw operations don't need scissor, but clip should still be valid
-    mCaches.setScissorEnabled(mScissorOptimizationDisabled);
-
     mCaches.activeTexture(0);
     Texture* texture = getTexture(bitmap);
     if (!texture) return DrawGlInfo::kStatusDone;
@@ -2860,16 +2881,13 @@
         const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
         DrawOpMode drawOpMode) {
 
-    if (drawOpMode == kDrawOpMode_Immediate &&
-            (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
-        return DrawGlInfo::kStatusDone;
-    }
-
     if (drawOpMode == kDrawOpMode_Immediate) {
-        if (quickReject(bounds)) return DrawGlInfo::kStatusDone;
-    } else {
-        // merged draw operations don't need scissor, but clip should still be valid
-        mCaches.setScissorEnabled(mScissorOptimizationDisabled);
+        // The checks for corner-case ignorable text and quick rejection is only done for immediate
+        // drawing as ops from DeferredDisplayList are already filtered for these
+        if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
+                quickReject(bounds)) {
+            return DrawGlInfo::kStatusDone;
+        }
     }
 
     const float oldX = x;