Glop support for indexed quads

bug:19014311
Change-Id: If35a873421b41cc4508b0d8ac1b4d900c9bb3717
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 9150869..e2adff8 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -65,7 +65,7 @@
      * Stores mesh - vertex and index data.
      *
      * buffer objects and void*s are mutually exclusive
-     * indices are optional
+     * indices are optional, currently only GL_UNSIGNED_SHORT supported
      */
     struct Mesh {
         VertexAttribFlags vertexFlags;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index e22af40..e0f2ec43 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -78,6 +78,21 @@
     return *this;
 }
 
+GlopBuilder& GlopBuilder::setMeshIndexedQuads(void* vertexData, int quadCount) {
+    TRIGGER_STAGE(kMeshStage);
+
+    mOutGlop->mesh.vertexFlags = kNone_Attrib;
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+    mOutGlop->mesh.vertexBufferObject = 0;
+    mOutGlop->mesh.vertices = vertexData;
+    mOutGlop->mesh.indexBufferObject = mRenderState.meshState().getQuadListIBO();
+    mOutGlop->mesh.indices = nullptr;
+    mOutGlop->mesh.vertexCount = 6 * quadCount;
+    mOutGlop->mesh.stride = kVertexStride;
+
+    return *this;
+}
+
 GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
         const Matrix4& transform, bool fudgingOffset) {
     TRIGGER_STAGE(kTransformStage);
@@ -90,6 +105,7 @@
 
 GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
     TRIGGER_STAGE(kModelViewStage);
+
     mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
     mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
     mOutGlop->bounds = destination;
@@ -98,22 +114,42 @@
 
 GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
     TRIGGER_STAGE(kModelViewStage);
+
     mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
     mOutGlop->bounds = source;
     mOutGlop->bounds.translate(offsetX, offsetY);
     return *this;
 }
 
-GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) {
+GlopBuilder& GlopBuilder::setOptionalPaint(const SkPaint* paint, float alphaScale) {
+    if (paint) {
+        return setPaint(*paint, alphaScale);
+    }
+
     TRIGGER_STAGE(kFillStage);
 
-    // TODO: support null paint
-    const SkShader* shader = paint->getShader();
-    const SkColorFilter* colorFilter = paint->getColorFilter();
+    mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
 
-    SkXfermode::Mode mode = PaintUtils::getXfermode(paint->getXfermode());
+    const bool SWAP_SRC_DST = false;
+    // TODO: account for texture blend
+    if (alphaScale < 1.0f) {
+        Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
+                &mOutGlop->blend.src, &mOutGlop->blend.dst);
+    } else {
+        mOutGlop->blend = { GL_ZERO, GL_ZERO };
+    }
+
+    return *this;
+}
+GlopBuilder& GlopBuilder::setPaint(const SkPaint& paint, float alphaScale) {
+    TRIGGER_STAGE(kFillStage);
+
+    const SkShader* shader = paint.getShader();
+    const SkColorFilter* colorFilter = paint.getColorFilter();
+
+    SkXfermode::Mode mode = PaintUtils::getXfermode(paint.getXfermode());
     if (mode != SkXfermode::kClear_Mode) {
-        int color = paint->getColor();
+        int color = paint.getColor();
         float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
         if (!shader) {
             float colorScale = alpha / 255.0f;
@@ -195,6 +231,8 @@
             colorVector[1] = srcColorMatrix[9] / 255.0f;
             colorVector[2] = srcColorMatrix[14] / 255.0f;
             colorVector[3] = srcColorMatrix[19] / 255.0f;
+        } else {
+            LOG_ALWAYS_FATAL("unsupported ColorFilter");
         }
     } else {
         mOutGlop->fill.filterMode = ProgramDescription::kColorNone;
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index c7464cd..48ce81a 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -36,15 +36,18 @@
     PREVENT_COPY_AND_ASSIGN(GlopBuilder);
 public:
     GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+
     GlopBuilder& setMeshUnitQuad();
     GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
+    GlopBuilder& setMeshIndexedQuads(void* vertexData, int quadCount);
 
     GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
 
     GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
     GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source);
 
-    GlopBuilder& setPaint(const SkPaint* paint, float alphaScale);
+    GlopBuilder& setOptionalPaint(const SkPaint* paint, float alphaScale);
+    GlopBuilder& setPaint(const SkPaint& paint, float alphaScale);
     void build();
 private:
     enum StageFlags {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5a0cc1e..61cd16f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1581,7 +1581,12 @@
         setStencilFromClip();
     }
     mRenderState.render(glop);
-    dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+
+    if (!mRenderState.stencil().isWriteEnabled()) {
+        // TODO: specify more clearly when a draw should dirty the layer.
+        // is writing to the stencil the only time we should ignore this?
+        dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2353,13 +2358,12 @@
         aBuilder.setMeshVertexBuffer(vertexBuffer, shadowInterp)
                 .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset)
                 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
-                .setPaint(paint, currentSnapshot()->alpha)
+                .setPaint(*paint, currentSnapshot()->alpha)
                 .build();
         renderGlop(glop);
         return;
     }
 
-
     const VertexBuffer::MeshFeatureFlags meshFeatureFlags = vertexBuffer.getMeshFeatureFlags();
     Rect bounds(vertexBuffer.getBounds());
     bounds.translate(translateX, translateY);
@@ -3218,12 +3222,6 @@
         return;
     }
 
-    int color = paint->getColor();
-    // If a shader is set, preserve only the alpha
-    if (getShader(paint)) {
-        color |= 0x00ffffff;
-    }
-
     float left = FLT_MAX;
     float top = FLT_MAX;
     float right = FLT_MIN;
@@ -3253,6 +3251,25 @@
         return;
     }
 
+    if (!paint->getShader() && !currentSnapshot()->roundRectClipState) {
+        const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+        Glop glop;
+        GlopBuilder aBuilder(mRenderState, mCaches, &glop);
+        aBuilder.setMeshIndexedQuads(&mesh[0], count / 4)
+                .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+                .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
+                .setPaint(*paint, currentSnapshot()->alpha)
+                .build();
+        renderGlop(glop);
+        return;
+    }
+
+    int color = paint->getColor();
+    // If a shader is set, preserve only the alpha
+    if (getShader(paint)) {
+        color |= 0x00ffffff;
+    }
+
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
@@ -3286,7 +3303,7 @@
         aBuilder.setMeshUnitQuad()
                 .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
                 .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
-                .setPaint(paint, currentSnapshot()->alpha)
+                .setPaint(*paint, currentSnapshot()->alpha)
                 .build();
         renderGlop(glop);
         return;
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index ce6030d..5efac0e 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -31,13 +31,29 @@
         , mCurrentTexCoordsStride(0)
         , mTexCoordsArrayEnabled(false)
         , mQuadListIndices(0) {
-
     glGenBuffers(1, &mUnitQuadBuffer);
     glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW);
 
     mCurrentBuffer = mUnitQuadBuffer;
 
+    std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[kMaxNumberOfQuads * 6]);
+    for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
+        uint16_t quad = i * 4;
+        int index = i * 6;
+        regionIndices[index    ] = quad;       // top-left
+        regionIndices[index + 1] = quad + 1;   // top-right
+        regionIndices[index + 2] = quad + 2;   // bottom-left
+        regionIndices[index + 3] = quad + 2;   // bottom-left
+        regionIndices[index + 4] = quad + 1;   // top-right
+        regionIndices[index + 5] = quad + 3;   // bottom-right
+    }
+    glGenBuffers(1, &mQuadListIndices);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxNumberOfQuads * 6 * sizeof(uint16_t),
+            regionIndices.get(), GL_STATIC_DRAW);
+    mCurrentIndicesBuffer = mQuadListIndices;
+
     // position attribute always enabled
     glEnableVertexAttribArray(Program::kBindingPosition);
 }
@@ -138,26 +154,6 @@
 }
 
 bool MeshState::bindQuadIndicesBuffer() {
-    if (!mQuadListIndices) {
-        std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[kMaxNumberOfQuads * 6]);
-        for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
-            uint16_t quad = i * 4;
-            int index = i * 6;
-            regionIndices[index    ] = quad;       // top-left
-            regionIndices[index + 1] = quad + 1;   // top-right
-            regionIndices[index + 2] = quad + 2;   // bottom-left
-            regionIndices[index + 3] = quad + 2;   // bottom-left
-            regionIndices[index + 4] = quad + 1;   // top-right
-            regionIndices[index + 5] = quad + 3;   // bottom-right
-        }
-
-        glGenBuffers(1, &mQuadListIndices);
-        bool force = bindIndicesBufferInternal(mQuadListIndices);
-        glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxNumberOfQuads * 6 * sizeof(uint16_t),
-                regionIndices.get(), GL_STATIC_DRAW);
-        return force;
-    }
-
     return bindIndicesBufferInternal(mQuadListIndices);
 }
 
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index afc6267..3c92ad8 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -114,6 +114,7 @@
     // Getters - for use in Glop building
     ///////////////////////////////////////////////////////////////////////////////
     GLuint getUnitQuadVBO() { return mUnitQuadBuffer; }
+    GLuint getQuadListIBO() { return mQuadListIndices; }
 private:
     MeshState();
     bool bindMeshBufferInternal(const GLuint buffer);
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index ba49833..6394dc1 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -204,12 +204,7 @@
  *
  * Textures + coordinates
  * SkiaShader
- * ColorFilter
- *
-    // TODO: texture coord
-    // TODO: texture support
-    // TODO: skiashader support
-    // TODO: color filter support
+ * RoundRect clipping
  */
 
 void RenderState::render(const Glop& glop) {
@@ -251,18 +246,18 @@
     // indices
     meshState().bindIndicesBufferInternal(mesh.indexBufferObject);
 
-    if (glop.mesh.vertexFlags & kTextureCoord_Attrib) {
+    if (mesh.vertexFlags & kTextureCoord_Attrib) {
         // TODO: support textures
         LOG_ALWAYS_FATAL("textures not yet supported");
     } else {
         meshState().disableTexCoordsVertexArray();
     }
-    if (glop.mesh.vertexFlags & kColor_Attrib) {
+    if (mesh.vertexFlags & kColor_Attrib) {
         LOG_ALWAYS_FATAL("color vertex attribute not yet supported");
         // TODO: enable color, disable when done
     }
     int alphaSlot = -1;
-    if (glop.mesh.vertexFlags & kAlpha_Attrib) {
+    if (mesh.vertexFlags & kAlpha_Attrib) {
         const void* alphaCoords = ((const GLbyte*) glop.mesh.vertices) + kVertexAlphaOffset;
         alphaSlot = shader.program->getAttrib("vtxAlpha");
         glEnableVertexAttribArray(alphaSlot);
@@ -275,15 +270,31 @@
     blend().setFactors(glop.blend.src, glop.blend.dst);
 
     // ------------------------------------
-    // ---------- GL state setup ----------
+    // ---------- Actual drawing ----------
     // ------------------------------------
-    if (mesh.indexBufferObject || mesh.indices) {
-        glDrawElements(glop.mesh.primitiveMode, glop.mesh.vertexCount,
-                GL_UNSIGNED_SHORT, mesh.indices);
+    if (mesh.indexBufferObject == meshState().getQuadListIBO()) {
+        // Since the indexed quad list is of limited length, we loop over
+        // the glDrawXXX method while updating the vertex pointer
+        GLsizei elementsCount = mesh.vertexCount;
+        const GLbyte* vertices = static_cast<const GLbyte*>(mesh.vertices);
+        while (elementsCount > 0) {
+            GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+
+            // TODO: this double binds on first pass
+            meshState().bindPositionVertexPointer(true, vertices, mesh.stride);
+            glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr);
+            elementsCount -= drawCount;
+            vertices += (drawCount / 6) * 4 * mesh.stride;
+        }
+    } else if (mesh.indexBufferObject || mesh.indices) {
+        glDrawElements(mesh.primitiveMode, mesh.vertexCount, GL_UNSIGNED_SHORT, mesh.indices);
     } else {
-        glDrawArrays(glop.mesh.primitiveMode, 0, glop.mesh.vertexCount);
+        glDrawArrays(mesh.primitiveMode, 0, mesh.vertexCount);
     }
 
+    // -----------------------------------
+    // ---------- Mesh teardown ----------
+    // -----------------------------------
     if (glop.mesh.vertexFlags & kAlpha_Attrib) {
         glDisableVertexAttribArray(alphaSlot);
     }
diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h
index a88beae..e4f0f3f 100644
--- a/libs/hwui/renderstate/Stencil.h
+++ b/libs/hwui/renderstate/Stencil.h
@@ -98,6 +98,10 @@
         return mState == kTest;
     }
 
+    bool isWriteEnabled() {
+        return mState == kWrite;
+    }
+
     void dump();
 
 private: