Reduce the number of GL commands generated by the UI

This optimization along with the previous one lets us render an
application like Gmail using only 30% of the number of GL commands
previously required

Change-Id: Ifee63edaf495e04490b5abd5433bb9a07bc327a8
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4da576d..27039dd 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -73,9 +73,12 @@
     glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
 
     mCurrentBuffer = meshBuffer;
+    mCurrentIndicesBuffer = 0;
     mCurrentPositionPointer = this;
     mCurrentTexCoordsPointer = this;
 
+    mTexCoordsArrayEnabled = false;
+
     mRegionMesh = NULL;
 
     blend = false;
@@ -243,6 +246,24 @@
     return false;
 }
 
+bool Caches::bindIndicesBuffer(const GLuint buffer) {
+    if (mCurrentIndicesBuffer != buffer) {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+        mCurrentIndicesBuffer = buffer;
+        return true;
+    }
+    return false;
+}
+
+bool Caches::unbindIndicesBuffer() {
+    if (mCurrentIndicesBuffer) {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+        mCurrentIndicesBuffer = 0;
+        return true;
+    }
+    return false;
+}
+
 void Caches::bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, GLsizei stride) {
     if (force || vertices != mCurrentPositionPointer) {
         glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
@@ -266,6 +287,20 @@
     mCurrentTexCoordsPointer = this;
 }
 
+void Caches::enableTexCoordsVertexArray() {
+    if (!mTexCoordsArrayEnabled) {
+        glEnableVertexAttribArray(Program::kBindingTexCoords);
+        mTexCoordsArrayEnabled = true;
+    }
+}
+
+void Caches::disbaleTexCoordsVertexArray() {
+    if (mTexCoordsArrayEnabled) {
+        glDisableVertexAttribArray(Program::kBindingTexCoords);
+        mTexCoordsArrayEnabled = false;
+    }
+}
+
 TextureVertex* Caches::getRegionMesh() {
     // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
     if (!mRegionMesh) {
@@ -284,13 +319,13 @@
         }
 
         glGenBuffers(1, &mRegionMeshIndices);
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+        bindIndicesBuffer(mRegionMeshIndices);
         glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
                 regionIndices, GL_STATIC_DRAW);
 
         delete[] regionIndices;
     } else {
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+        bindIndicesBuffer(mRegionMeshIndices);
     }
 
     return mRegionMesh;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 7ca198a..6143593 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -150,6 +150,9 @@
      */
     bool unbindMeshBuffer();
 
+    bool bindIndicesBuffer(const GLuint buffer);
+    bool unbindIndicesBuffer();
+
     /**
      * Binds an attrib to the specified float vertex pointer.
      * Assumes a stride of gMeshStride and a size of 2.
@@ -169,6 +172,9 @@
     void resetVertexPointers();
     void resetTexCoordsVertexPointer();
 
+    void enableTexCoordsVertexArray();
+    void disbaleTexCoordsVertexArray();
+
     /**
      * Returns the mesh used to draw regions. Calling this method will
      * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
@@ -214,9 +220,12 @@
 
 private:
     GLuint mCurrentBuffer;
+    GLuint mCurrentIndicesBuffer;
     void* mCurrentPositionPointer;
     void* mCurrentTexCoordsPointer;
 
+    bool mTexCoordsArrayEnabled;
+
     // Used to render layers
     TextureVertex* mRegionMesh;
     GLuint mRegionMeshIndices;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index f04ea6f..102aea6 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -22,6 +22,7 @@
 
 #include <utils/Log.h>
 
+#include "Caches.h"
 #include "Debug.h"
 #include "FontRenderer.h"
 
@@ -536,9 +537,8 @@
     }
 
     glGenBuffers(1, &mIndexBufferID);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
     free(indexBufferData);
 
@@ -597,7 +597,18 @@
 void FontRenderer::issueDrawCommand() {
     checkTextureUpdate();
 
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+    Caches& caches = Caches::getInstance();
+    if (!mDrawn) {
+        float* buffer = mTextMeshPtr;
+        int offset = 2;
+
+        bool force = caches.unbindMeshBuffer();
+        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
+        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
+                buffer + offset);
+    }
+
+    caches.bindIndicesBuffer(mIndexBufferID);
     glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
 
     mDrawn = true;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index f945873..2a5a788 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -178,15 +178,6 @@
         mGammaTable = gammaTable;
     }
 
-    inline float* getMeshBuffer() {
-        checkInit();
-        return mTextMeshPtr;
-    }
-
-    inline int getMeshTexCoordsOffset() const {
-        return 2;
-    }
-
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
     bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index fcac053..59d3fcc 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -144,6 +144,7 @@
     glDisable(GL_DITHER);
     glEnable(GL_SCISSOR_TEST);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
     glEnableVertexAttribArray(Program::kBindingPosition);
 }
 
@@ -199,7 +200,9 @@
         }
     }
     mCaches.unbindMeshBuffer();
+    mCaches.unbindIndicesBuffer();
     mCaches.resetVertexPointers();
+    mCaches.disbaleTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::resume() {
@@ -212,7 +215,6 @@
     dirtyClip();
 
     glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
     mCaches.blend = true;
     glEnable(GL_BLEND);
@@ -771,7 +773,7 @@
             layer->setFilter(GL_LINEAR);
             setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
         }
-        setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0]);
+        setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
 
         for (size_t i = 0; i < count; i++) {
             const android::Rect* r = &rects[i];
@@ -800,7 +802,6 @@
             glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
         }
 
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
         finishDrawTexture();
 
 #if DEBUG_LAYERS_AS_REGIONS
@@ -1015,7 +1016,6 @@
     mColorA = mColorR = mColorG = mColorB = 0.0f;
     mTextureUnit = 0;
     mTrackDirtyRegions = true;
-    mTexCoordsSlot = -1;
 }
 
 void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1027,6 +1027,10 @@
     mDescription.hasExternalTexture = true;
 }
 
+void OpenGLRenderer::setupDrawNoTexture() {
+    mCaches.disbaleTexCoordsVertexArray();
+}
+
 void OpenGLRenderer::setupDrawAALine() {
     mDescription.isAA = true;
 }
@@ -1203,22 +1207,19 @@
 void OpenGLRenderer::setupDrawSimpleMesh() {
     bool force = mCaches.bindMeshBuffer();
     mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0);
+    mCaches.unbindIndicesBuffer();
 }
 
 void OpenGLRenderer::setupDrawTexture(GLuint texture) {
     bindTexture(texture);
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
-
-    mTexCoordsSlot = mCaches.currentProgram->texCoords;
-    glEnableVertexAttribArray(mTexCoordsSlot);
+    mCaches.enableTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
     bindExternalTexture(texture);
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
-
-    mTexCoordsSlot = mCaches.currentProgram->texCoords;
-    glEnableVertexAttribArray(mTexCoordsSlot);
+    mCaches.enableTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::setupDrawTextureTransform() {
@@ -1239,8 +1240,18 @@
     }
 
     mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
-    if (mTexCoordsSlot >= 0) {
-        mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, texCoords);
+    if (mCaches.currentProgram->texCoords >= 0) {
+        mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
+    }
+
+    mCaches.unbindIndicesBuffer();
+}
+
+void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) {
+    bool force = mCaches.unbindMeshBuffer();
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
+    if (mCaches.currentProgram->texCoords >= 0) {
+        mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
     }
 }
 
@@ -1248,6 +1259,7 @@
     bool force = mCaches.unbindMeshBuffer();
     mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
             vertices, gVertexStride);
+    mCaches.unbindIndicesBuffer();
 }
 
 /**
@@ -1267,6 +1279,7 @@
     mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
             vertices, gAAVertexStride);
     mCaches.resetTexCoordsVertexPointer();
+    mCaches.unbindIndicesBuffer();
 
     int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
     glEnableVertexAttribArray(widthSlot);
@@ -1285,7 +1298,6 @@
 }
 
 void OpenGLRenderer::finishDrawTexture() {
-    glDisableVertexAttribArray(mTexCoordsSlot);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1624,6 +1636,7 @@
     }
 
     setupDraw();
+    setupDrawNoTexture();
     setupDrawAALine();
     setupDrawColor(color);
     setupDrawColorFilter();
@@ -1734,6 +1747,7 @@
 
     getAlphaAndMode(paint, &alpha, &mode);
     setupDraw();
+    setupDrawNoTexture();
     if (isAA) {
         setupDrawAALine();
     }
@@ -1943,6 +1957,7 @@
     TextureVertex* vertex = &pointsData[0];
 
     setupDraw();
+    setupDrawNoTexture();
     setupDrawPoint(strokeWidth);
     setupDrawColor(paint->getColor(), alpha);
     setupDrawColorFilter();
@@ -2186,13 +2201,6 @@
     bool hasActiveLayer = false;
 #endif
 
-    float* buffer = fontRenderer.getMeshBuffer();
-    int offset = fontRenderer.getMeshTexCoordsOffset();
-
-    bool force = mCaches.unbindMeshBuffer();
-    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, buffer);
-    mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, buffer + offset);
-
     if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
             hasActiveLayer ? &bounds : NULL)) {
 #if RENDER_LAYERS_AS_REGIONS
@@ -2205,9 +2213,6 @@
 #endif
     }
 
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    glDisableVertexAttribArray(mCaches.currentProgram->texCoords);
-
     drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
 }
 
@@ -2437,6 +2442,7 @@
     }
 
     setupDraw();
+    setupDrawNoTexture();
     setupDrawColor(color);
     setupDrawShader();
     setupDrawColorFilter();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index cd9ff93..019e9b2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -498,6 +498,7 @@
      */
     void setupDrawWithTexture(bool isAlpha8 = false);
     void setupDrawWithExternalTexture();
+    void setupDrawNoTexture();
     void setupDrawAALine();
     void setupDrawPoint(float pointSize);
     void setupDrawColor(int color);
@@ -530,6 +531,7 @@
     void setupDrawTextureTransform();
     void setupDrawTextureTransformUniforms(mat4& transform);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
+    void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
     void setupDrawVertices(GLvoid* vertices);
     void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
             float strokeWidth);
@@ -601,8 +603,6 @@
     GLuint mTextureUnit;
     // Track dirty regions, true by default
     bool mTrackDirtyRegions;
-    // Texture coordinates slot
-    int mTexCoordsSlot;
 
     friend class DisplayListRenderer;