Improve rendering speed by disabling scissor tests

This change improves execution of display lists, particularly on
tiled renderers. The goal is to disable the scissor test as
often as possible. Drawing commands are rarely clipped by View
bounds so most of them can be drawn without doing a scissor test.

The speed improvements scale with the number of views and drawing
commands.

Change-Id: Ibd9b5e051a3e4300562463805acc4fd744ba6266
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index f210820..214cc92 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -74,6 +74,7 @@
 
     mTexCoordsArrayEnabled = false;
 
+    scissorEnabled = false;
     mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
 
     glActiveTexture(gTextureUnits[0]);
@@ -352,7 +353,9 @@
 }
 
 void Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
-    if (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight) {
+    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
+            width != mScissorWidth || height != mScissorHeight)) {
+
         glScissor(x, y, width, height);
 
         mScissorX = x;
@@ -362,6 +365,28 @@
     }
 }
 
+void Caches::enableScissor() {
+    if (!scissorEnabled) {
+        glEnable(GL_SCISSOR_TEST);
+        scissorEnabled = true;
+    }
+}
+
+void Caches::disableScissor() {
+    if (scissorEnabled) {
+        glDisable(GL_SCISSOR_TEST);
+        scissorEnabled = false;
+    }
+}
+
+void Caches::setScissorEnabled(bool enabled) {
+    if (scissorEnabled != enabled) {
+        if (enabled) glEnable(GL_SCISSOR_TEST);
+        else glDisable(GL_SCISSOR_TEST);
+        scissorEnabled = enabled;
+    }
+}
+
 void Caches::resetScissor() {
     mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
 }
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 58361c9..f83e291 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -204,6 +204,10 @@
      */
     void resetScissor();
 
+    void enableScissor();
+    void disableScissor();
+    void setScissorEnabled(bool enabled);
+
     /**
      * Returns the mesh used to draw regions. Calling this method will
      * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
@@ -221,6 +225,7 @@
     GLenum lastSrcMode;
     GLenum lastDstMode;
     Program* currentProgram;
+    bool scissorEnabled;
 
     // VBO to draw with
     GLuint meshBuffer;
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 65f8c7c..41a5f0d 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -233,9 +233,8 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->getTexture(), 0);
 
-    glDisable(GL_SCISSOR_TEST);
+    caches.disableScissor();
     glClear(GL_COLOR_BUFFER_BIT);
-    glEnable(GL_SCISSOR_TEST);
 
     glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
 
@@ -431,7 +430,7 @@
             renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
                     bitmap->width(), bitmap->height(), !layer->isBlend());
 
-            glDisable(GL_SCISSOR_TEST);
+            caches.disableScissor();
             renderer.translate(0.0f, bitmap->height());
             renderer.scale(1.0f, -1.0f);
 
@@ -460,8 +459,6 @@
         }
 
 error:
-        glEnable(GL_SCISSOR_TEST);
-
 #if DEBUG_OPENGL
         if (error != GL_NO_ERROR) {
             ALOGD("GL error while copying layer into bitmap = 0x%x", error);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 933d012..cbf7b02 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -157,7 +157,6 @@
     mFirstSnapshot->viewport.set(0, 0, width, height);
 
     glDisable(GL_DITHER);
-    glEnable(GL_SCISSOR_TEST);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
     glEnableVertexAttribArray(Program::kBindingPosition);
@@ -181,6 +180,7 @@
     syncState();
 
     if (!opaque) {
+        mCaches.enableScissor();
         mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
         glClear(GL_COLOR_BUFFER_BIT);
         return DrawGlInfo::kStatusDrew;
@@ -250,7 +250,7 @@
     glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
-    glEnable(GL_SCISSOR_TEST);
+    mCaches.enableScissor();
     mCaches.resetScissor();
     dirtyClip();
 
@@ -651,6 +651,7 @@
 #endif
 
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
+    mCaches.enableScissor();
     mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
             clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -981,7 +982,7 @@
         // The list contains bounds that have already been clipped
         // against their initial clip rect, and the current clip
         // is likely different so we need to disable clipping here
-        glDisable(GL_SCISSOR_TEST);
+        mCaches.disableScissor();
 
         Vertex mesh[count * 6];
         Vertex* vertex = mesh;
@@ -1008,8 +1009,6 @@
         setupDrawVertices(&mesh[0].position[0]);
 
         glDrawArrays(GL_TRIANGLES, 0, count * 6);
-
-        glEnable(GL_SCISSOR_TEST);
     } else {
         for (uint32_t i = 0; i < count; i++) {
             delete mLayers.itemAt(i);
@@ -1088,7 +1087,12 @@
     Rect clipRect(*mSnapshot->clipRect);
     clipRect.snapToPixelBoundaries();
 
-    return !clipRect.intersects(r);
+    bool rejected = !clipRect.intersects(r);
+    if (!isDeferred() && !rejected) {
+        mCaches.setScissorEnabled(!clipRect.contains(r));
+    }
+
+    return rejected;
 }
 
 bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 2ca4f50..80f39ff90 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,11 +125,11 @@
         return intersect(r.left, r.top, r.right, r.bottom);
     }
 
-    bool contains(float l, float t, float r, float b) {
+    inline bool contains(float l, float t, float r, float b) {
         return l >= left && t >= top && r <= right && b <= bottom;
     }
 
-    bool contains(const Rect& r) {
+    inline bool contains(const Rect& r) {
         return contains(r.left, r.top, r.right, r.bottom);
     }