Refactoring of Program ownership/lifecycle, and WIP Glop rendering path

Change-Id: I2549032790bddbc048b0bccc224ed8f386b4517c
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index cef2c84..03b8283 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -72,9 +72,8 @@
 
     ATRACE_NAME("Caches::init");
 
-
     mRegionMesh = nullptr;
-    currentProgram = nullptr;
+    mProgram = nullptr;
 
     mFunctorsCount = 0;
 
@@ -200,7 +199,7 @@
     fboCache.clear();
 
     programCache.clear();
-    currentProgram = nullptr;
+    setProgram(nullptr);
 
     patchCache.clear();
 
@@ -213,6 +212,22 @@
     mInitialized = false;
 }
 
+void Caches::setProgram(const ProgramDescription& description) {
+    setProgram(programCache.get(description));
+}
+
+void Caches::setProgram(Program* program) {
+    if (!program || !program->isInUse()) {
+        if (mProgram) {
+            mProgram->remove();
+        }
+        if (program) {
+            program->use();
+        }
+        mProgram = program;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Debug
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index f6d3476..16e2058 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -162,8 +162,6 @@
     void registerFunctors(uint32_t functorCount);
     void unregisterFunctors(uint32_t functorCount);
 
-    Program* currentProgram;
-
     bool drawDeferDisabled;
     bool drawReorderDisabled;
 
@@ -219,6 +217,10 @@
     int propertyAmbientShadowStrength;
     int propertySpotShadowStrength;
 
+    void setProgram(const ProgramDescription& description);
+    void setProgram(Program* program);
+
+    Program& program() { return *mProgram; }
     PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
     TextureState& textureState() { return *mTextureState; }
 
@@ -246,10 +248,6 @@
     }
 
     RenderState* mRenderState;
-
-    PixelBufferState* mPixelBufferState = nullptr; // TODO: move to RenderState
-    TextureState* mTextureState = nullptr; // TODO: move to RenderState
-
     Extensions& mExtensions;
 
     // Used to render layers
@@ -264,6 +262,12 @@
     uint32_t mFunctorsCount;
 
     OverdrawColorSet mOverdrawDebugColorSet;
+
+    // TODO: move below to RenderState
+    PixelBufferState* mPixelBufferState = nullptr;
+    TextureState* mTextureState = nullptr;
+    Program* mProgram = nullptr; // note: object owned by ProgramCache
+
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index d637ec1..359c193 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -89,13 +89,13 @@
 // Program management
 ///////////////////////////////////////////////////////////////////////////////
 
-void Dither::setupProgram(Program* program, GLuint* textureUnit) {
+void Dither::setupProgram(Program& program, GLuint* textureUnit) {
     GLuint textureSlot = (*textureUnit)++;
     mCaches.textureState().activateTexture(textureSlot);
 
     bindDitherTexture();
 
-    glUniform1i(program->getUniform("ditherSampler"), textureSlot);
+    glUniform1i(program.getUniform("ditherSampler"), textureSlot);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 38633af..facd1ea 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -39,7 +39,7 @@
     Dither(Caches& caches);
 
     void clear();
-    void setupProgram(Program* program, GLuint* textureUnit);
+    void setupProgram(Program& program, GLuint* textureUnit);
 
 private:
     void bindDitherTexture();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 6dcd3e1..55b2d19 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -516,9 +516,8 @@
 
             TextureVertex* mesh = texture->mesh();
             MeshState& meshState = renderState.meshState();
-            Program* program = caches.currentProgram;
-            meshState.bindPositionVertexPointer(program, forceRebind, &mesh[0].x);
-            meshState.bindTexCoordsVertexPointer(program, forceRebind, &mesh[0].u);
+            meshState.bindPositionVertexPointer(forceRebind, &mesh[0].x);
+            meshState.bindTexCoordsVertexPointer(forceRebind, &mesh[0].u);
 
             glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
                     GL_UNSIGNED_SHORT, texture->indices());
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index e97a477..0bcd83a 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
-
 #include "Debug.h"
 #include "GammaFontRenderer.h"
 #include "Properties.h"
@@ -96,7 +94,8 @@
 // Shader-based renderer
 ///////////////////////////////////////////////////////////////////////////////
 
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma): GammaFontRenderer() {
+ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
+        : GammaFontRenderer() {
     INIT_LOGD("Creating shader gamma font renderer");
     mRenderer = nullptr;
     mMultiGamma = multiGamma;
@@ -123,9 +122,9 @@
 }
 
 void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
-        Program* program) const {
+        Program& program) const {
     if (description.hasGammaCorrection) {
-        glUniform1f(program->getUniform("gamma"), description.gamma);
+        glUniform1f(program.getUniform("gamma"), description.gamma);
     }
 }
 
@@ -139,7 +138,8 @@
 // Lookup-based renderer
 ///////////////////////////////////////////////////////////////////////////////
 
-LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
+LookupGammaFontRenderer::LookupGammaFontRenderer()
+        : GammaFontRenderer() {
     INIT_LOGD("Creating lookup gamma font renderer");
 
     // Compute the gamma tables
@@ -162,7 +162,8 @@
 // Lookup-based renderer, using 3 different correction tables
 ///////////////////////////////////////////////////////////////////////////////
 
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() {
+Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
+        : GammaFontRenderer() {
     INIT_LOGD("Creating lookup3 gamma font renderer");
 
     // Compute the gamma tables
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 19352d7..ca55bf1 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -38,7 +38,7 @@
     virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
 
     virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
-    virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
+    virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
 
     virtual void endPrecaching() = 0;
 
@@ -86,7 +86,7 @@
     }
 
     void describe(ProgramDescription& description, const SkPaint* paint) const override;
-    void setupProgram(ProgramDescription& description, Program* program) const override;
+    void setupProgram(ProgramDescription& description, Program& program) const override;
 
     void endPrecaching() override;
 
@@ -135,7 +135,7 @@
     void describe(ProgramDescription& description, const SkPaint* paint) const override {
     }
 
-    void setupProgram(ProgramDescription& description, Program* program) const override {
+    void setupProgram(ProgramDescription& description, Program& program) const override {
     }
 
     void endPrecaching() override;
@@ -171,7 +171,7 @@
     void describe(ProgramDescription& description, const SkPaint* paint) const override {
     }
 
-    void setupProgram(ProgramDescription& description, Program* program) const override {
+    void setupProgram(ProgramDescription& description, Program& program) const override {
     }
 
     void endPrecaching() override;
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
new file mode 100644
index 0000000..730d9df
--- /dev/null
+++ b/libs/hwui/Glop.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_GLOP_H
+#define ANDROID_HWUI_GLOP_H
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+/*
+ * Enumerates optional vertex attributes
+ *
+ * Position is always enabled by MeshState, these other attributes
+ * are enabled/disabled dynamically based on mesh content.
+ */
+enum VertexAttribFlags {
+    // NOTE: position attribute always enabled
+    kTextureCoord_Attrib = 1 << 0,
+    kColor_Attrib = 1 << 1,
+    kAlpha_Attrib = 1 << 2,
+};
+
+/**
+ * Structure containing all data required to issue a single OpenGL draw
+ *
+ * Includes all of the mesh, fill, and GL state required to perform
+ * the operation. Pieces of data are either directly copied into the
+ * structure, or stored as a pointer or GL object reference to data
+ * managed
+ */
+// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
+struct Glop {
+    Rect bounds;
+
+    struct Mesh {
+        VertexAttribFlags vertexFlags = static_cast<VertexAttribFlags>(0);
+        GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
+        GLuint vertexBufferObject = 0;
+        GLuint indexBufferObject = 0;
+        int vertexCount;
+        GLsizei stride;
+    } mesh;
+
+    struct Fill {
+        Program* program;
+        GLuint shaderId;
+        GLuint textureId;
+
+        struct Color {
+            float a, r, g, b;
+        } color;
+
+        /* TODO
+        union shader {
+            //...
+        }; TODO
+        union filter {
+            //color
+            //matrix + vector
+        };
+        */
+    } fill;
+
+    struct Transform {
+        Matrix4 ortho; // TODO: out of op, since this is static per FBO
+        Matrix4 modelView;
+        Matrix4 canvas;
+        bool offset;
+    } transform;
+
+    struct Blend {
+        static const SkXfermode::Mode kDisable =
+                static_cast<SkXfermode::Mode>(SkXfermode::kLastMode + 1);
+        SkXfermode::Mode mode;
+        bool swapSrcDst;
+    } blend;
+
+    /**
+     * Additional render state to enumerate:
+     * - scissor + (bits for whether each of LTRB needed?)
+     * - stencil mode (draw into, mask, count, etc)
+     */
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // ANDROID_HWUI_GLOP_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2378337..ab6f0ce 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
-
 #include "OpenGLRenderer.h"
 
 #include "DeferredDisplayList.h"
@@ -137,8 +135,6 @@
 void OpenGLRenderer::onViewportInitialized() {
     glDisable(GL_DITHER);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    glEnableVertexAttribArray(Program::kBindingPosition);
     mFirstFrameAfterResize = true;
 }
 
@@ -1715,20 +1711,20 @@
 }
 
 void OpenGLRenderer::setupDrawProgram() {
-    useProgram(mCaches.programCache.get(mDescription));
+    mCaches.setProgram(mDescription);
     if (mDescription.hasRoundRectClip) {
         // TODO: avoid doing this repeatedly, stashing state pointer in program
         const RoundRectClipState* state = writableSnapshot()->roundRectClipState;
         const Rect& innerRect = state->innerRect;
-        glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"),
+        glUniform4f(mCaches.program().getUniform("roundRectInnerRectLTRB"),
                 innerRect.left, innerRect.top,
                 innerRect.right, innerRect.bottom);
-        glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"),
+        glUniformMatrix4fv(mCaches.program().getUniform("roundRectInvTransform"),
                 1, GL_FALSE, &state->matrix.data[0]);
 
         // add half pixel to round out integer rect space to cover pixel centers
         float roundedOutRadius = state->radius + 0.5f;
-        glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"),
+        glUniform1f(mCaches.program().getUniform("roundRectRadius"),
                 roundedOutRadius);
     }
 }
@@ -1746,7 +1742,8 @@
 
     bool dirty = right - left > 0.0f && bottom - top > 0.0f;
     const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform();
-    mCaches.currentProgram->set(writableSnapshot()->getOrthoMatrix(),
+
+    mCaches.program().set(currentSnapshot()->getOrthoMatrix(),
             mModelViewMatrix, transformMatrix, offset);
     if (dirty && mTrackDirtyRegions) {
         if (!ignoreTransform) {
@@ -1759,13 +1756,13 @@
 
 void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
     if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
-        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
+        mCaches.program().setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
 
 void OpenGLRenderer::setupDrawPureColorUniforms() {
     if (mSetShaderColor) {
-        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
+        mCaches.program().setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
 
@@ -1800,7 +1797,7 @@
         const GLfloat r = a * SkColorGetR(color) / 255.0f;
         const GLfloat g = a * SkColorGetG(color) / 255.0f;
         const GLfloat b = a * SkColorGetB(color) / 255.0f;
-        glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a);
+        glUniform4f(mCaches.program().getUniform("colorBlend"), r, g, b, a);
         return;
     }
 
@@ -1821,9 +1818,9 @@
         colorVector[2] = srcColorMatrix[14] / 255.0f;
         colorVector[3] = srcColorMatrix[19] / 255.0f;
 
-        glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1,
+        glUniformMatrix4fv(mCaches.program().getUniform("colorMatrix"), 1,
                 GL_FALSE, colorMatrix);
-        glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector);
+        glUniform4fv(mCaches.program().getUniform("colorMatrixVector"), 1, colorVector);
         return;
     }
 
@@ -1831,12 +1828,12 @@
 }
 
 void OpenGLRenderer::setupDrawTextGammaUniforms() {
-    mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
+    mCaches.fontRenderer->setupProgram(mDescription, mCaches.program());
 }
 
 void OpenGLRenderer::setupDrawSimpleMesh() {
     bool force = mRenderState.meshState().bindMeshBuffer();
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, nullptr);
+    mRenderState.meshState().bindPositionVertexPointer(force, nullptr);
     mRenderState.meshState().unbindIndicesBuffer();
 }
 
@@ -1857,7 +1854,7 @@
 }
 
 void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
-    glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
+    glUniformMatrix4fv(mCaches.program().getUniform("mainTextureTransform"), 1,
             GL_FALSE, &transform.data[0]);
 }
 
@@ -1870,9 +1867,9 @@
         force = mRenderState.meshState().unbindMeshBuffer();
     }
 
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, vertices);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram, force, texCoords);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords);
     }
 
     mRenderState.meshState().unbindIndicesBuffer();
@@ -1883,13 +1880,11 @@
     bool force = mRenderState.meshState().unbindMeshBuffer();
     GLsizei stride = sizeof(ColorTextureVertex);
 
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force,
-            vertices, stride);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram, force,
-                texCoords, stride);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices, stride);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords, stride);
     }
-    int slot = mCaches.currentProgram->getAttrib("colors");
+    int slot = mCaches.program().getAttrib("colors");
     if (slot >= 0) {
         glEnableVertexAttribArray(slot);
         glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
@@ -1911,18 +1906,16 @@
     }
     mRenderState.meshState().bindQuadIndicesBuffer();
 
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, vertices);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram,
-                force, texCoords);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords);
     }
 }
 
 void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
     bool force = mRenderState.meshState().unbindMeshBuffer();
     mRenderState.meshState().bindQuadIndicesBuffer();
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force,
-            vertices, kVertexStride);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices, kVertexStride);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2144,7 +2137,7 @@
 
     glDrawArrays(GL_TRIANGLES, 0, count);
 
-    int slot = mCaches.currentProgram->getAttrib("colors");
+    int slot = mCaches.program().getAttrib("colors");
     if (slot >= 0) {
         glDisableVertexAttribArray(slot);
     }
@@ -2360,14 +2353,14 @@
 
     const void* vertices = vertexBuffer.getBuffer();
     mRenderState.meshState().unbindMeshBuffer();
-    mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram,
-            true, vertices, isAA ? kAlphaVertexStride : kVertexStride);
+    mRenderState.meshState().bindPositionVertexPointer(true, vertices,
+            isAA ? kAlphaVertexStride : kVertexStride);
     mRenderState.meshState().resetTexCoordsVertexPointer();
 
     int alphaSlot = -1;
     if (isAA) {
         void* alphaCoords = ((GLbyte*) vertices) + kVertexAlphaOffset;
-        alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
+        alphaSlot = mCaches.program().getAttrib("vtxAlpha");
         // TODO: avoid enable/disable in back to back uses of the alpha attribute
         glEnableVertexAttribArray(alphaSlot);
         glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
@@ -3440,16 +3433,6 @@
     }
 }
 
-bool OpenGLRenderer::useProgram(Program* program) {
-    if (!program->isInUse()) {
-        if (mCaches.currentProgram != nullptr) mCaches.currentProgram->remove();
-        program->use();
-        mCaches.currentProgram = program;
-        return false;
-    }
-    return true;
-}
-
 void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
     TextureVertex* v = &mMeshVertices[0];
     TextureVertex::setUV(v++, u1, v1);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index cf6f0c8..f0de089 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -853,18 +853,6 @@
             bool swapSrcDst = false);
 
     /**
-     * Use the specified program with the current GL context. If the program is already
-     * in use, it will not be bound again. If it is not in use, the current program is
-     * marked unused and the specified program becomes used and becomes the new
-     * current program.
-     *
-     * @param program The program to use
-     *
-     * @return true If the specified program was already in use, false otherwise.
-     */
-    inline bool useProgram(Program* program);
-
-    /**
      * Invoked before any drawing operation. This sets required state.
      */
     void setupDraw(bool clear = true);
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index fb07dfa..5f34b34 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -46,7 +46,7 @@
             glAttachShader(mProgramId, mVertexShader);
             glAttachShader(mProgramId, mFragmentShader);
 
-            position = bindAttrib("position", kBindingPosition);
+            bindAttrib("position", kBindingPosition);
             if (description.hasTexture || description.hasExternalTexture) {
                 texCoords = bindAttrib("texCoords", kBindingTexCoords);
             } else {
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index d05b331..b637450 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -366,12 +366,7 @@
     void setColor(const float r, const float g, const float b, const float a);
 
     /**
-     * Name of the position attribute.
-     */
-    int position;
-
-    /**
-     * Name of the texCoords attribute if it exists, -1 otherwise.
+     * Name of the texCoords attribute if it exists (kBindingTexCoords), -1 otherwise.
      */
     int texCoords;
 
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index e13c861..9c929da 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -191,11 +191,11 @@
     layer->setWrap(GL_CLAMP_TO_EDGE);
     layer->setFilter(GL_LINEAR);
 
-    Program* program = caches->currentProgram;
-    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
-    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+    Program& program = caches->program();
+    glUniform1i(program.getUniform("bitmapSampler"), textureSlot);
+    glUniformMatrix4fv(program.getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+    glUniform2f(program.getUniform("textureDimension"), 1.0f / width, 1.0f / height);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -277,7 +277,7 @@
         return;
     }
 
-    Program* program = caches->currentProgram;
+    Program& program = caches->program();
     Texture* texture = shaderInfo.texture;
 
     const AutoTexture autoCleanup(texture);
@@ -290,10 +290,10 @@
     bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
     texture->setFilter(GL_LINEAR);
 
-    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
-    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+    glUniform1i(program.getUniform("bitmapSampler"), textureSlot);
+    glUniformMatrix4fv(program.getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+    glUniform2f(program.getUniform("textureDimension"), 1.0f / shaderInfo.width,
             1.0f / shaderInfo.height);
 }
 
@@ -381,7 +381,7 @@
 
     SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
 
-    Program* program = caches->currentProgram;
+    Program& program = caches->program();
     if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
         if (gradInfo.fColorCount > COLOR_COUNT) {
             // There was not enough room in our arrays for all the colors and offsets. Try again,
@@ -402,10 +402,10 @@
 
         // Uniforms
         bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
-        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+        glUniform1i(program.getUniform("gradientSampler"), textureSlot);
     } else {
-        bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
-        bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
+        bindUniformColor(program.getUniform("startColor"), gradInfo.fColors[0]);
+        bindUniformColor(program.getUniform("endColor"), gradInfo.fColors[1]);
     }
 
     caches->dither.setupProgram(program, textureUnit);
@@ -428,7 +428,7 @@
 
     mat4 screenSpace;
     computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
-    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+    glUniformMatrix4fv(program.getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 7820a66..022faf7d 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -39,6 +39,9 @@
 
     mQuadListIndices = 0;
     mShadowStripsIndices = 0;
+
+    // position attribute always enabled
+    glEnableVertexAttribArray(Program::kBindingPosition);
 }
 
 MeshState::~MeshState() {
@@ -83,21 +86,17 @@
 // Vertices
 ///////////////////////////////////////////////////////////////////////////////
 
-void MeshState::bindPositionVertexPointer(const Program* currentProgram, bool force,
-        const GLvoid* vertices, GLsizei stride) {
+void MeshState::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
     if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
-        GLuint slot = currentProgram->position;
-        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+        glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices);
         mCurrentPositionPointer = vertices;
         mCurrentPositionStride = stride;
     }
 }
 
-void MeshState::bindTexCoordsVertexPointer(const Program* currentProgram, bool force,
-        const GLvoid* vertices, GLsizei stride) {
+void MeshState::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
     if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
-        GLuint slot = currentProgram->texCoords;
-        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+        glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices);
         mCurrentTexCoordsPointer = vertices;
         mCurrentTexCoordsStride = stride;
     }
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index 76f73d4..9b1021d 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -80,15 +80,15 @@
      * Binds an attrib to the specified float vertex pointer.
      * Assumes a stride of gTextureVertexStride and a size of 2.
      */
-    void bindPositionVertexPointer(const Program* currentProgram, bool force,
-            const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
+    void bindPositionVertexPointer(bool force, const GLvoid* vertices,
+            GLsizei stride = kTextureVertexStride);
 
     /**
      * Binds an attrib to the specified float vertex pointer.
      * Assumes a stride of gTextureVertexStride and a size of 2.
      */
-    void bindTexCoordsVertexPointer(const Program* currentProgram, bool force,
-            const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
+    void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices,
+            GLsizei stride = kTextureVertexStride);
 
     /**
      * Resets the vertex pointers.
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index f913cd9..8eda7c9 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -127,12 +127,7 @@
 }
 
 void RenderState::interruptForFunctorInvoke() {
-    if (mCaches->currentProgram) {
-        if (mCaches->currentProgram->isInUse()) {
-            mCaches->currentProgram->remove();
-            mCaches->currentProgram = nullptr;
-        }
-    }
+    mCaches->setProgram(nullptr);
     mCaches->textureState().resetActiveTexture();
     meshState().unbindMeshBuffer();
     meshState().unbindIndicesBuffer();
@@ -179,7 +174,6 @@
     LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
 }
 
-
 class DecStrongTask : public renderthread::RenderTask {
 public:
     DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
@@ -198,5 +192,82 @@
     mRenderThread.queue(new DecStrongTask(object));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Render
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Not yet supported:
+ *
+ * Textures + coordinates
+ * SkiaShader
+ * ColorFilter
+ *
+    // TODO: texture coord
+    // TODO: texture support
+    // TODO: skiashader support
+    // TODO: color filter support
+ */
+
+void RenderState::render(const Glop& glop) {
+    const Glop::Mesh& mesh = glop.mesh;
+    const Glop::Fill& shader = glop.fill;
+
+    // ---------- Shader + uniform setup ----------
+    mCaches->setProgram(shader.program);
+
+    Glop::Fill::Color color = shader.color;
+    shader.program->setColor(color.a, color.r, color.g, color.b);
+
+    shader.program->set(glop.transform.ortho,
+            glop.transform.modelView,
+            glop.transform.canvas,
+            glop.transform.offset);
+
+    // ---------- Mesh setup ----------
+    if (glop.mesh.vertexFlags & kTextureCoord_Attrib) {
+        // TODO: support textures
+        LOG_ALWAYS_FATAL("textures not yet supported");
+    } else {
+        meshState().disableTexCoordsVertexArray();
+    }
+    if (glop.mesh.vertexFlags & kColor_Attrib) {
+        LOG_ALWAYS_FATAL("color attribute not yet supported");
+        // TODO: enable color, disable when done
+    }
+    if (glop.mesh.vertexFlags & kAlpha_Attrib) {
+        LOG_ALWAYS_FATAL("alpha attribute not yet supported");
+        // TODO: enable alpha attribute, disable when done
+    }
+
+    /**
+    * Hard-coded vertex assumptions:
+     *     - required
+     *     - xy floats
+     *     - 0 offset
+     *     - in VBO
+     */
+    bool force = meshState().bindMeshBuffer(mesh.vertexBufferObject);
+    meshState().bindPositionVertexPointer(force, nullptr, mesh.stride);
+
+    /**
+     * Hard-coded index assumptions:
+     *     - optional
+     *     - 0 offset
+     *     - in IBO
+     */
+    meshState().bindIndicesBufferInternal(mesh.indexBufferObject);
+
+    // ---------- GL state setup ----------
+
+    if (glop.blend.mode != Glop::Blend::kDisable) {
+        blend().enable(glop.blend.mode, glop.blend.swapSrcDst);
+    } else {
+        blend().disable();
+    }
+
+    glDrawElements(glop.mesh.primitiveMode, glop.mesh.vertexCount, GL_UNSIGNED_BYTE, nullptr);
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4180f44..2e28ff6 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -22,11 +22,12 @@
 #include <utils/Mutex.h>
 #include <utils/Functor.h>
 #include <utils/RefBase.h>
-
 #include <private/hwui/DrawGlInfo.h>
 #include <renderstate/Blend.h>
+
 #include "AssetAtlas.h"
 #include "Caches.h"
+#include "Glop.h"
 #include "renderstate/MeshState.h"
 #include "renderstate/PixelBufferState.h"
 #include "renderstate/Scissor.h"
@@ -83,6 +84,8 @@
     // more thinking...
     void postDecStrong(VirtualLightRefBase* object);
 
+    void render(const Glop& glop);
+
     AssetAtlas& assetAtlas() { return mAssetAtlas; }
     Blend& blend() { return *mBlend; }
     MeshState& meshState() { return *mMeshState; }
@@ -96,6 +99,9 @@
     void resumeFromFunctorInvoke();
     void assertOnGLThread();
 
+    void setupVertexAttributes(const Glop& glop);
+    void tearDownVertexAttributes(const Glop& glop);
+
     RenderState(renderthread::RenderThread& thread);
     ~RenderState();