Glop mesh reorg, support for drawBitmapMesh

Change-Id: Iaf5550bdd93da93e59a5b838234ab5612e067387
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index dba5121..1963475 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1399,8 +1399,11 @@
     friend class DisplayListData; // grant DisplayListData access to info of child
 public:
     DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
-            : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr),
-            mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {}
+            : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
+            , mRenderNode(renderNode)
+            , mFlags(flags)
+            , mTransformFromParent(transformFromParent)
+            , mSkipInOrderDraw(false) {}
 
     virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
             bool useQuickReject) override {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index e500546..c9a5679 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -41,12 +41,13 @@
  * Position is always enabled by MeshState, these other attributes
  * are enabled/disabled dynamically based on mesh content.
  */
-enum VertexAttribFlags {
-    kNone_Attrib = 0,
-    kTextureCoord_Attrib = 1 << 0,
-    kColor_Attrib = 1 << 1,
-    kAlpha_Attrib = 1 << 2,
+enum class VertexAttribFlags {
+    kNone = 0,
+    kTextureCoord = 1 << 0,
+    kColor = 1 << 1,
+    kAlpha = 1 << 2,
 };
+MAKE_FLAGS_ENUM(VertexAttribFlags)
 
 /**
  * Structure containing all data required to issue an OpenGL draw
@@ -63,22 +64,28 @@
  */
 // TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
 struct Glop {
-    /*
-     * Stores mesh - vertex and index data.
-     *
-     * buffer objects and void*s are mutually exclusive
-     * indices are optional, currently only GL_UNSIGNED_SHORT supported
-     */
     struct Mesh {
-        VertexAttribFlags vertexFlags;
         GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
-        GLuint vertexBufferObject;
-        GLuint indexBufferObject;
-        const void* vertices;
-        const void* indices;
-        GLvoid* texCoordOffset;
+
+        // buffer object and void* are mutually exclusive.
+        // Only GL_UNSIGNED_SHORT supported.
+        struct Indices {
+            GLuint bufferObject;
+            const void* indices;
+        } indices;
+
+        // buffer object and void*s are mutually exclusive.
+        // TODO: enforce mutual exclusion with restricted setters and/or unions
+        struct Vertices {
+            GLuint bufferObject;
+            VertexAttribFlags flags;
+            const void* position;
+            const void* texCoord;
+            const void* color;
+            GLsizei stride;
+        } vertices;
+
         int elementCount;
-        GLsizei stride;
         TextureVertex mappedVertices[4];
     } mesh;
 
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 1342c52..f50dfc8 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -61,15 +61,14 @@
 GlopBuilder& GlopBuilder::setMeshUnitQuad() {
     TRIGGER_STAGE(kMeshStage);
 
-    mOutGlop->mesh.vertexFlags = kNone_Attrib;
     mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
-    mOutGlop->mesh.vertexBufferObject = mRenderState.meshState().getUnitQuadVBO();
-    mOutGlop->mesh.vertices = nullptr;
-    mOutGlop->mesh.indexBufferObject = 0;
-    mOutGlop->mesh.indices = nullptr;
+    mOutGlop->mesh.indices = { 0, nullptr };
+    mOutGlop->mesh.vertices = {
+            mRenderState.meshState().getUnitQuadVBO(),
+            VertexAttribFlags::kNone,
+            nullptr, nullptr, nullptr,
+            kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
-    mOutGlop->mesh.stride = kTextureVertexStride;
-    mOutGlop->mesh.texCoordOffset = nullptr;
     return *this;
 }
 
@@ -81,70 +80,76 @@
 
     TRIGGER_STAGE(kMeshStage);
 
-    mOutGlop->mesh.vertexFlags = kTextureCoord_Attrib;
     mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
-    mOutGlop->mesh.vertexBufferObject = mRenderState.meshState().getUnitQuadVBO();
-    mOutGlop->mesh.vertices = nullptr;
-    mOutGlop->mesh.texCoordOffset = (GLvoid*) kMeshTextureOffset;
-    mOutGlop->mesh.indexBufferObject = 0;
-    mOutGlop->mesh.indices = nullptr;
+    mOutGlop->mesh.indices = { 0, nullptr };
+    mOutGlop->mesh.vertices = {
+            mRenderState.meshState().getUnitQuadVBO(),
+            VertexAttribFlags::kTextureCoord,
+            nullptr, (const void*) kMeshTextureOffset, nullptr,
+            kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
-    mOutGlop->mesh.stride = kTextureVertexStride;
-    mDescription.hasTexture = true;
     return *this;
 }
 
 GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) {
     TRIGGER_STAGE(kMeshStage);
 
-    mOutGlop->mesh.vertexFlags = kTextureCoord_Attrib;
-    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
-
     if (CC_UNLIKELY(uvMapper)) {
         uvMapper->map(uvs);
     }
     setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]);
 
-    mOutGlop->mesh.vertexBufferObject = 0;
-    mOutGlop->mesh.vertices = &mOutGlop->mesh.mappedVertices[0].x;
-    mOutGlop->mesh.texCoordOffset = &mOutGlop->mesh.mappedVertices[0].u;
-    mOutGlop->mesh.indexBufferObject = 0;
-    mOutGlop->mesh.indices = nullptr;
+    const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices;
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+    mOutGlop->mesh.indices = { 0, nullptr };
+    mOutGlop->mesh.vertices = {
+            0,
+            VertexAttribFlags::kTextureCoord,
+            &textureVertex[0].x, &textureVertex[0].u, nullptr,
+            kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
-    mOutGlop->mesh.stride = kTextureVertexStride;
-    mDescription.hasTexture = true;
     return *this;
 }
 
-GlopBuilder& GlopBuilder::setMeshIndexedQuads(void* vertexData, int quadCount) {
+GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* 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.texCoordOffset = nullptr;
+    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+    mOutGlop->mesh.vertices = {
+            0,
+            VertexAttribFlags::kNone,
+            vertexData, nullptr, nullptr,
+            kVertexStride };
     mOutGlop->mesh.elementCount = 6 * quadCount;
-    mOutGlop->mesh.stride = kVertexStride;
-
     return *this;
 }
 
 GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) {
     TRIGGER_STAGE(kMeshStage);
 
-    mOutGlop->mesh.vertexFlags = kTextureCoord_Attrib;
     mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
-    mOutGlop->mesh.vertexBufferObject = 0;
-    mOutGlop->mesh.vertices = &vertexData[0].x;
-    mOutGlop->mesh.indexBufferObject = mRenderState.meshState().getQuadListIBO();
-    mOutGlop->mesh.indices = nullptr;
-    mOutGlop->mesh.texCoordOffset = &vertexData[0].u;
+    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+    mOutGlop->mesh.vertices = {
+            0,
+            VertexAttribFlags::kTextureCoord,
+            &vertexData[0].x, &vertexData[0].u, nullptr,
+            kTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
-    mOutGlop->mesh.stride = kTextureVertexStride;
-    mDescription.hasTexture = true;
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) {
+    TRIGGER_STAGE(kMeshStage);
+
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+    mOutGlop->mesh.indices = { 0, nullptr };
+    mOutGlop->mesh.vertices = {
+            0,
+            static_cast<VertexAttribFlags>(VertexAttribFlags::kTextureCoord | VertexAttribFlags::kColor),
+            &vertexData[0].x, &vertexData[0].u, &vertexData[0].r,
+            kColorTextureVertexStride };
+    mOutGlop->mesh.elementCount = elementCount;
     return *this;
 }
 
@@ -155,18 +160,17 @@
 
     bool alphaVertex = flags & VertexBuffer::kAlpha;
     bool indices = flags & VertexBuffer::kIndices;
-    mOutGlop->mesh.vertexFlags = alphaVertex ? kAlpha_Attrib : kNone_Attrib;
-    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
-    mOutGlop->mesh.vertexBufferObject = 0;
-    mOutGlop->mesh.vertices = vertexBuffer.getBuffer();
-    mOutGlop->mesh.indexBufferObject = 0;
-    mOutGlop->mesh.indices = vertexBuffer.getIndices();
-    mOutGlop->mesh.texCoordOffset = nullptr;
-    mOutGlop->mesh.elementCount = indices
-            ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
-    mOutGlop->mesh.stride = alphaVertex ? kAlphaVertexStride : kVertexStride;
 
-    mDescription.hasVertexAlpha = alphaVertex;
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+    mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() };
+    mOutGlop->mesh.vertices = {
+            0,
+            alphaVertex ? VertexAttribFlags::kAlpha : VertexAttribFlags::kNone,
+            vertexBuffer.getBuffer(), nullptr, nullptr,
+            alphaVertex ? kAlphaVertexStride : kVertexStride };
+    mOutGlop->mesh.elementCount = indices
+                ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
+
     mDescription.useShadowAlphaInterp = shadowInterp;
     return *this;
 }
@@ -197,7 +201,7 @@
 
     mOutGlop->blend = { GL_ZERO, GL_ZERO };
     if (mOutGlop->fill.color.a < 1.0f
-            || (mOutGlop->mesh.vertexFlags & kAlpha_Attrib)
+            || (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha)
             || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend)
             || mOutGlop->roundRectClipState
             || PaintUtils::isBlendedShader(shader)
@@ -288,7 +292,7 @@
 
         const bool SWAP_SRC_DST = false;
         if (alphaScale < 1.0f
-                || (mOutGlop->mesh.vertexFlags & kAlpha_Attrib)
+                || (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha)
                 || texture.blend
                 || mOutGlop->roundRectClipState) {
             Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
@@ -489,20 +493,25 @@
 void verify(const ProgramDescription& description, const Glop& glop) {
     bool hasTexture = glop.fill.texture.texture != nullptr;
     LOG_ALWAYS_FATAL_IF(description.hasTexture != hasTexture);
-    LOG_ALWAYS_FATAL_IF((glop.mesh.vertexFlags & kTextureCoord_Attrib) != hasTexture);
+    LOG_ALWAYS_FATAL_IF((glop.mesh.vertices.flags & VertexAttribFlags::kTextureCoord) != hasTexture);
+
+    if ((glop.mesh.vertices.flags & VertexAttribFlags::kAlpha) && glop.mesh.vertices.bufferObject) {
+        LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
+    }
 }
 
 void GlopBuilder::build() {
     REQUIRE_STAGES(kAllStages);
 
+    mDescription.hasTexture = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kTextureCoord);
+    mDescription.hasColors = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kColor);
+    mDescription.hasVertexAlpha = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha);
+
     // serialize shader info into ShaderData
     GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
     SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView,
             &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
 
-    mOutGlop->fill.program = mCaches.programCache.get(mDescription);
-    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
-
     // duplicates ProgramCache's definition of color uniform presence
     const bool singleColor = !mDescription.hasTexture
             && !mDescription.hasExternalTexture
@@ -511,6 +520,10 @@
     mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
 
     verify(mDescription, *mOutGlop);
+
+    // Final step: populate program and map bounds into render target space
+    mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 5a8354d..c24b122 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -42,7 +42,8 @@
     GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
     GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
     GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
-    GlopBuilder& setMeshIndexedQuads(void* vertexData, int quadCount);
+    GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
+    GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount);
     GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount
 
     GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2bca9fa..3913ee5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1234,7 +1234,7 @@
             aBuilder.setMeshIndexedQuads(&mesh[0], quadCount)
                     .setFillClear()
                     .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
-                    .setModelViewOffsetRect(0, 0, currentSnapshot()->getClipRect())
+                    .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
                     .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                     .build();
             renderGlop(glop);
@@ -2077,17 +2077,14 @@
         return;
     }
 
-    // TODO: use quickReject on bounds from vertices
-    mRenderState.scissor().setEnabled(true);
-
     float left = FLT_MAX;
     float top = FLT_MAX;
     float right = FLT_MIN;
     float bottom = FLT_MIN;
 
-    const uint32_t count = meshWidth * meshHeight * 6;
+    const uint32_t elementCount = meshWidth * meshHeight * 6;
 
-    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[count]);
+    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
     ColorTextureVertex* vertex = &mesh[0];
 
     std::unique_ptr<int[]> tempColors;
@@ -2098,7 +2095,6 @@
         colors = tempColors.get();
     }
 
-    mCaches.textureState().activateTexture(0);
     Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
     const UvMapper& mapper(getMapper(texture));
 
@@ -2149,6 +2145,25 @@
     }
     const AutoTexture autoCleanup(texture);
 
+    if (USE_GLOPS) {
+        /*
+         * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
+         * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
+         */
+        bool isAlpha8Texture = false;
+        Glop glop;
+        GlopBuilder aBuilder(mRenderState, mCaches, &glop);
+        aBuilder.setMeshColoredTexturedMesh(mesh.get(), elementCount)
+                .setFillTexturePaint(*texture, isAlpha8Texture, paint, currentSnapshot()->alpha)
+                .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+                .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
+                .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+                .build();
+        renderGlop(glop);
+        return;
+    }
+
+    mCaches.textureState().activateTexture(0);
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
     texture->setFilter(PaintUtils::getFilter(paint), true);
 
@@ -2156,12 +2171,10 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
+
+    dirtyLayer(left, top, right, bottom, *currentTransform());
+
     float a = alpha / 255.0f;
-
-    if (hasLayer()) {
-        dirtyLayer(left, top, right, bottom, *currentTransform());
-    }
-
     setupDraw();
     setupDrawWithTextureAndColor();
     setupDrawColor(a, a, a, a);
@@ -2175,7 +2188,7 @@
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r);
 
-    glDrawArrays(GL_TRIANGLES, 0, count);
+    glDrawArrays(GL_TRIANGLES, 0, elementCount);
 
     int slot = mCaches.program().getAttrib("colors");
     if (slot >= 0) {
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index 3c92ad8..e80f4d0 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -42,6 +42,7 @@
 const GLsizei kVertexStride = sizeof(Vertex);
 const GLsizei kAlphaVertexStride = sizeof(AlphaVertex);
 const GLsizei kTextureVertexStride = sizeof(TextureVertex);
+const GLsizei kColorTextureVertexStride = sizeof(ColorTextureVertex);
 
 const GLsizei kMeshTextureOffset = 2 * sizeof(float);
 const GLsizei kVertexAlphaOffset = 2 * sizeof(float);
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 192bf81..7992077 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -199,16 +199,10 @@
 // Render
 ///////////////////////////////////////////////////////////////////////////////
 
-/*
- * Not yet supported:
- *
- * Textures + coordinates
- * SkiaShader
- * RoundRect clipping
- */
-
 void RenderState::render(const Glop& glop) {
     const Glop::Mesh& mesh = glop.mesh;
+    const Glop::Mesh::Vertices& vertices = mesh.vertices;
+    const Glop::Mesh::Indices& indices = mesh.indices;
     const Glop::Fill& fill = glop.fill;
 
     // ---------------------------------------------
@@ -226,15 +220,15 @@
             glop.transform.fudgingOffset);
 
     // Color filter uniforms
-    if (glop.fill.filterMode == ProgramDescription::kColorBlend) {
-        const FloatColor& color = glop.fill.filter.color;
+    if (fill.filterMode == ProgramDescription::kColorBlend) {
+        const FloatColor& color = fill.filter.color;
         glUniform4f(mCaches->program().getUniform("colorBlend"),
                 color.r, color.g, color.b, color.a);
-    } else if (glop.fill.filterMode == ProgramDescription::kColorMatrix) {
+    } else if (fill.filterMode == ProgramDescription::kColorMatrix) {
         glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE,
-                glop.fill.filter.matrix.matrix);
+                fill.filter.matrix.matrix);
         glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1,
-                glop.fill.filter.matrix.vector);
+                fill.filter.matrix.vector);
     }
 
     // Round rect clipping uniforms
@@ -253,48 +247,51 @@
         glUniform1f(fill.program->getUniform("roundRectRadius"),
                 roundedOutRadius);
     }
+
     // --------------------------------
     // ---------- Mesh setup ----------
     // --------------------------------
     // vertices
-    const bool force = meshState().bindMeshBufferInternal(mesh.vertexBufferObject)
-            || (mesh.vertices != nullptr);
-    meshState().bindPositionVertexPointer(force, mesh.vertices, mesh.stride);
+    const bool force = meshState().bindMeshBufferInternal(vertices.bufferObject)
+            || (vertices.position != nullptr);
+    meshState().bindPositionVertexPointer(force, vertices.position, vertices.stride);
 
     // indices
-    meshState().bindIndicesBufferInternal(mesh.indexBufferObject);
+    meshState().bindIndicesBufferInternal(indices.bufferObject);
 
-    if (mesh.vertexFlags & kTextureCoord_Attrib) {
-        // glop.fill.texture always takes slot 0, shader samplers increment from there
+    if (vertices.flags & VertexAttribFlags::kTextureCoord) {
+        // fill.texture always takes slot 0, shader samplers increment from there
         mCaches->textureState().activateTexture(0);
 
-        if (glop.fill.texture.clamp != GL_INVALID_ENUM) {
-            glop.fill.texture.texture->setWrap(glop.fill.texture.clamp, true);
+        if (fill.texture.clamp != GL_INVALID_ENUM) {
+            fill.texture.texture->setWrap(fill.texture.clamp, true);
         }
-        if (glop.fill.texture.filter != GL_INVALID_ENUM) {
-            glop.fill.texture.texture->setFilter(glop.fill.texture.filter, true);
+        if (fill.texture.filter != GL_INVALID_ENUM) {
+            fill.texture.texture->setFilter(fill.texture.filter, true);
         }
 
         mCaches->textureState().bindTexture(fill.texture.texture->id);
         meshState().enableTexCoordsVertexArray();
-        meshState().bindTexCoordsVertexPointer(force, mesh.texCoordOffset, mesh.stride);
+        meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride);
     } else {
         meshState().disableTexCoordsVertexArray();
     }
-    if (mesh.vertexFlags & kColor_Attrib) {
-        LOG_ALWAYS_FATAL("color vertex attribute not yet supported");
-        // TODO: enable color attribute, disable when done
+    int colorLocation = -1;
+    if (vertices.flags & VertexAttribFlags::kColor) {
+        colorLocation = fill.program->getAttrib("colors");
+        glEnableVertexAttribArray(colorLocation);
+        glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, vertices.color);
     }
-    int alphaSlot = -1;
-    if (mesh.vertexFlags & kAlpha_Attrib) {
-        const void* alphaCoords = ((const GLbyte*) glop.mesh.vertices) + kVertexAlphaOffset;
-        alphaSlot = fill.program->getAttrib("vtxAlpha");
-        glEnableVertexAttribArray(alphaSlot);
-        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
+    int alphaLocation = -1;
+    if (vertices.flags & VertexAttribFlags::kAlpha) {
+        // NOTE: alpha vertex position is computed assuming no VBO
+        const void* alphaCoords = ((const GLbyte*) vertices.position) + kVertexAlphaOffset;
+        alphaLocation = fill.program->getAttrib("vtxAlpha");
+        glEnableVertexAttribArray(alphaLocation);
+        glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords);
     }
-
     // Shader uniforms
-    SkiaShader::apply(*mCaches, glop.fill.skiaShaderData);
+    SkiaShader::apply(*mCaches, fill.skiaShaderData);
 
     // ------------------------------------
     // ---------- GL state setup ----------
@@ -304,27 +301,27 @@
     // ------------------------------------
     // ---------- Actual drawing ----------
     // ------------------------------------
-    if (mesh.indexBufferObject == meshState().getQuadListIBO()) {
+    if (indices.bufferObject == 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.elementCount;
-        const GLbyte* vertices = static_cast<const GLbyte*>(mesh.vertices);
+        const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position);
         while (elementsCount > 0) {
             GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
 
             // rebind pointers without forcing, since initial bind handled above
-            meshState().bindPositionVertexPointer(false, vertices, mesh.stride);
-            if (mesh.vertexFlags & kTextureCoord_Attrib) {
+            meshState().bindPositionVertexPointer(false, vertexData, vertices.stride);
+            if (vertices.flags & VertexAttribFlags::kTextureCoord) {
                 meshState().bindTexCoordsVertexPointer(false,
-                        vertices + kMeshTextureOffset, mesh.stride);
+                        vertexData + kMeshTextureOffset, vertices.stride);
             }
 
             glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr);
             elementsCount -= drawCount;
-            vertices += (drawCount / 6) * 4 * mesh.stride;
+            vertexData += (drawCount / 6) * 4 * vertices.stride;
         }
-    } else if (mesh.indexBufferObject || mesh.indices) {
-        glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, mesh.indices);
+    } else if (indices.bufferObject || indices.indices) {
+        glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices);
     } else {
         glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount);
     }
@@ -332,8 +329,11 @@
     // -----------------------------------
     // ---------- Mesh teardown ----------
     // -----------------------------------
-    if (glop.mesh.vertexFlags & kAlpha_Attrib) {
-        glDisableVertexAttribArray(alphaSlot);
+    if (vertices.flags & VertexAttribFlags::kAlpha) {
+        glDisableVertexAttribArray(alphaLocation);
+    }
+    if (vertices.flags & VertexAttribFlags::kColor) {
+        glDisableVertexAttribArray(colorLocation);
     }
 }
 
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 49d364e7..2ed605e 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -41,6 +41,12 @@
         } \
         inline int operator&(int lhs, enumType rhs) { \
             return lhs & static_cast<int>(rhs); \
+        } \
+        inline int operator&(enumType lhs, int rhs) { \
+            return static_cast<int>(lhs) & rhs; \
+        } \
+        inline int operator&(enumType lhs, enumType rhs) { \
+            return static_cast<int>(lhs) & static_cast<int>(rhs); \
         }
 
 #endif /* MACROS_H */