Remove unnecessary GL calls.

This change batches calls to glScissor() and removes extra GL
queries and glActiveTexture() calls.

Change-Id: I1cd079d314f87cd9c088f95c8d4909c2f860f6aa
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index e0094d8..0994d82 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -43,6 +43,8 @@
     glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
 
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+
     mCurrentBuffer = meshBuffer;
 }
 
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index aff5366..ca22867 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -23,6 +23,7 @@
 
 #include <utils/Singleton.h>
 
+#include "Extensions.h"
 #include "FontRenderer.h"
 #include "GammaFontRenderer.h"
 #include "TextureCache.h"
@@ -93,8 +94,15 @@
     GLenum lastDstMode;
     Program* currentProgram;
 
+    // VBO to draw with
     GLuint meshBuffer;
 
+    // GL extensions
+    Extensions extensions;
+
+    // Misc
+    GLint maxTextureSize;
+
     TextureCache textureCache;
     LayerCache layerCache;
     GradientCache gradientCache;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8592511..b167131 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -107,8 +107,6 @@
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
     mFirstSnapshot = new Snapshot;
-
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -129,6 +127,8 @@
 
     mFirstSnapshot->height = height;
     mFirstSnapshot->viewport.set(0, 0, width, height);
+
+    mDirtyClip = false;
 }
 
 void OpenGLRenderer::prepare(bool opaque) {
@@ -139,16 +139,18 @@
     glViewport(0, 0, mWidth, mHeight);
 
     glDisable(GL_DITHER);
-    glDisable(GL_SCISSOR_TEST);
 
     if (!opaque) {
+        glDisable(GL_SCISSOR_TEST);
         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT);
+        glEnable(GL_SCISSOR_TEST);
+    } else {
+        glEnable(GL_SCISSOR_TEST);
+        glScissor(0, 0, mWidth, mHeight);
+        dirtyClip();
     }
 
-    glEnable(GL_SCISSOR_TEST);
-    glScissor(0, 0, mWidth, mHeight);
-
     mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
 }
 
@@ -175,7 +177,7 @@
     glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight());
 
     glEnable(GL_SCISSOR_TEST);
-    setScissorFromClip();
+    dirtyClip();
 
     glDisable(GL_DITHER);
 
@@ -241,7 +243,7 @@
     }
 
     if (restoreClip) {
-        setScissorFromClip();
+        dirtyClip();
     }
 
     return restoreClip;
@@ -261,7 +263,7 @@
 
     if (p) {
         alpha = p->getAlpha();
-        if (!mExtensions.hasFramebufferFetch()) {
+        if (!mCaches.extensions.hasFramebufferFetch()) {
             const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
             if (!isMode) {
                 // Assume SRC_OVER
@@ -371,8 +373,8 @@
         bounds.intersect(snapshot->previous->viewport);
     }
 
-    if (bounds.isEmpty() || bounds.getWidth() > mMaxTextureSize ||
-            bounds.getHeight() > mMaxTextureSize) {
+    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
+            bounds.getHeight() > mCaches.maxTextureSize) {
         snapshot->invisible = true;
     } else {
         // TODO: Should take the mode into account
@@ -448,7 +450,7 @@
         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT);
 
-        setScissorFromClip();
+        dirtyClip();
 
         // Change the ortho projection
         glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
@@ -506,6 +508,7 @@
     mCaches.unbindMeshBuffer();
     resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
 
+    glActiveTexture(gTextureUnits[0]);
     if (fboLayer) {
         drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
                 layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
@@ -528,6 +531,8 @@
         mCaches.fboCache.put(current->fbo);
     }
 
+    dirtyClip();
+
     // Failing to add the layer to the cache should happen only if the layer is too large
     if (!mCaches.layerCache.put(layer)) {
         LAYER_LOGD("Deleting layer");
@@ -536,6 +541,13 @@
     }
 }
 
+void OpenGLRenderer::setupDraw() {
+    clearLayerRegions();
+    if (mDirtyClip) {
+        setScissorFromClip();
+    }
+}
+
 void OpenGLRenderer::clearLayerRegions() {
     if (mLayers.size() == 0 || mSnapshot->invisible) return;
 
@@ -553,7 +565,7 @@
     mLayers.clear();
 
     // Restore the clip
-    setScissorFromClip();
+    dirtyClip();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -602,6 +614,7 @@
     Rect clip(*mSnapshot->clipRect);
     clip.snapToPixelBoundaries();
     glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
+    mDirtyClip = false;
 }
 
 const Rect& OpenGLRenderer::getClipBounds() {
@@ -626,7 +639,7 @@
 bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
     bool clipped = mSnapshot->clip(left, top, right, bottom, op);
     if (clipped) {
-        setScissorFromClip();
+        dirtyClip();
     }
     return !mSnapshot->clipRect->isEmpty();
 }
@@ -676,7 +689,7 @@
         return;
     }
 
-    glActiveTexture(GL_TEXTURE0);
+    glActiveTexture(gTextureUnits[0]);
     Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
@@ -711,7 +724,7 @@
         return;
     }
 
-    glActiveTexture(GL_TEXTURE0);
+    glActiveTexture(gTextureUnits[0]);
     Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
@@ -749,6 +762,7 @@
     const bool isAA = paint->isAntiAlias();
     if (isAA) {
         GLuint textureUnit = 0;
+        glActiveTexture(gTextureUnits[textureUnit]);
         setupTextureAlpha8(mCaches.line.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
                 mode, false, true, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
                 mCaches.line.getMeshBuffer());
@@ -812,7 +826,7 @@
     }
 
     SkXfermode::Mode mode;
-    if (!mExtensions.hasFramebufferFetch()) {
+    if (!mCaches.extensions.hasFramebufferFetch()) {
         const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
         if (!isMode) {
             // Assume SRC_OVER
@@ -869,10 +883,7 @@
     fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
             paint->getTextSize());
 
-    Rect clipRect(*mSnapshot->clipRect);
-    clipRect.snapToPixelBoundaries();
-    glScissor(clipRect.left, mSnapshot->height - clipRect.bottom,
-            clipRect.getWidth(), clipRect.getHeight());
+    setupDraw();
 
     if (mHasShadow) {
         glActiveTexture(gTextureUnits[0]);
@@ -897,7 +908,6 @@
             x, y, r, g, b, a, mode, false, true, NULL, NULL);
 
     const Rect& clip = mSnapshot->getLocalClip();
-    clearLayerRegions();
 
     mCaches.unbindMeshBuffer();
     fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
@@ -906,8 +916,6 @@
     glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
 
     drawTextDecorations(text, bytesCount, length, x, y, paint);
-
-    setScissorFromClip();
 }
 
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -939,7 +947,7 @@
 
     setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true);
 
-    clearLayerRegions();
+    setupDraw();
 
     // Draw the mesh
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -1035,10 +1043,10 @@
 
      if (applyFilters) {
          if (mShader) {
-             mShader->describe(description, mExtensions);
+             mShader->describe(description, mCaches.extensions);
          }
          if (mColorFilter) {
-             mColorFilter->describe(description, mExtensions);
+             mColorFilter->describe(description, mCaches.extensions);
          }
      }
 
@@ -1048,7 +1056,7 @@
      // Build and use the appropriate shader
      useProgram(mCaches.programCache.get(description));
 
-     bindTexture(texture, textureUnit);
+     bindTexture(texture);
      glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);
 
      int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
@@ -1155,7 +1163,7 @@
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode, bool ignoreTransform) {
-    clearLayerRegions();
+    setupDraw();
 
     // If a shader is set, preserve only the alpha
     if (mShader) {
@@ -1184,10 +1192,10 @@
     const bool setColor = description.setColor(r, g, b, a);
 
     if (mShader) {
-        mShader->describe(description, mExtensions);
+        mShader->describe(description, mCaches.extensions);
     }
     if (mColorFilter) {
-        mColorFilter->describe(description, mExtensions);
+        mColorFilter->describe(description, mCaches.extensions);
     }
 
     // Setup the blending mode
@@ -1244,13 +1252,13 @@
         GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
         GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
         bool swapSrcDst, bool ignoreTransform, GLuint vbo) {
-    clearLayerRegions();
+    setupDraw();
 
     ProgramDescription description;
     description.hasTexture = true;
     const bool setColor = description.setColor(alpha, alpha, alpha, alpha);
     if (mColorFilter) {
-        mColorFilter->describe(description, mExtensions);
+        mColorFilter->describe(description, mCaches.extensions);
     }
 
     mModelView.loadTranslate(left, top, 0.0f);
@@ -1318,7 +1326,7 @@
             // These blend modes are not supported by OpenGL directly and have
             // to be implemented using shaders. Since the shader will perform
             // the blending, turn blending off here
-            if (mExtensions.hasFramebufferFetch()) {
+            if (mCaches.extensions.hasFramebufferFetch()) {
                 description.framebufferMode = mode;
                 description.swapSrcDst = swapSrcDst;
             }
@@ -1354,7 +1362,7 @@
 
 void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
     if (paint) {
-        if (!mExtensions.hasFramebufferFetch()) {
+        if (!mCaches.extensions.hasFramebufferFetch()) {
             const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
             if (!isMode) {
                 // Assume SRC_OVER
@@ -1384,16 +1392,9 @@
     return mode->fMode;
 }
 
-void OpenGLRenderer::bindTexture(GLuint texture, GLuint textureUnit) {
-    glActiveTexture(gTextureUnits[textureUnit]);
-    glBindTexture(GL_TEXTURE_2D, texture);
-}
-
-void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT,
-        GLuint textureUnit) {
+void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT) {
     bool bound = false;
     if (wrapS != texture->wrapS) {
-        glActiveTexture(gTextureUnits[textureUnit]);
         glBindTexture(GL_TEXTURE_2D, texture->id);
         bound = true;
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
@@ -1401,7 +1402,6 @@
     }
     if (wrapT != texture->wrapT) {
         if (!bound) {
-            glActiveTexture(gTextureUnits[textureUnit]);
             glBindTexture(GL_TEXTURE_2D, texture->id);
         }
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 07188d4..3492d2c 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -259,7 +259,8 @@
             bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0);
 
     /**
-     * Prepares the renderer to draw the specified shadow.
+     * Prepares the renderer to draw the specified shadow. The active texture
+     * unit must be 0 and the other units must be unbound.
      *
      * @param texture The shadow texture
      * @param x The x coordinate of the shadow
@@ -360,11 +361,18 @@
     inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
 
     /**
-     * Binds the specified texture to the specified texture unit.
+     * Binds the specified texture. The texture unit must have been selected
+     * prior to calling this method.
      */
-    inline void bindTexture(GLuint texture, GLuint textureUnit = 0);
-    inline void setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT,
-            GLuint textureUnit = 0);
+    inline void bindTexture(GLuint texture) {
+        glBindTexture(GL_TEXTURE_2D, texture);
+    }
+
+    /**
+     * Sets the wrap modes for the specified texture. The wrap modes are modified
+     * only when needed.
+     */
+    inline void setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT);
 
     /**
      * Enable or disable blending as necessary. This function sets the appropriate
@@ -391,6 +399,18 @@
      */
     inline bool useProgram(Program* program);
 
+    /**
+     * Invoked before any drawing operation. This sets required state.
+     */
+    void setupDraw();
+
+    /**
+     * Should be invoked every time the glScissor is modified.
+     */
+    inline void dirtyClip() {
+        mDirtyClip = true;
+    }
+
     // Dimensions of the drawing surface
     int mWidth, mHeight;
 
@@ -416,9 +436,6 @@
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
-    // GL extensions
-    Extensions mExtensions;
-
     // Drop shadow
     bool mHasShadow;
     float mShadowRadius;
@@ -432,12 +449,12 @@
     // List of rectangles to clear due to calls to saveLayer()
     Vector<Rect*> mLayers;
 
-    // Misc
-    GLint mMaxTextureSize;
-
     // Indentity matrix
     const mat4 mIdentity;
 
+    // Indicates whether the clip must be restored
+    bool mDirtyClip;
+
     friend class DisplayListRenderer;
 
 }; // class OpenGLRenderer