Glop SkiaShader support

Change-Id: I894a0b62701bd02367ab970813e4c332147351a2
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index 803b9d4..b3bd5fd 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -18,10 +18,19 @@
 
 #include "utils/Macros.h"
 
+#include <stdint.h>
+
 namespace android {
 namespace uirenderer {
 
 struct FloatColor {
+    void set(uint32_t color) {
+        a = ((color >> 24) & 0xff) / 255.0f;
+        r = a * ((color >> 16) & 0xff) / 255.0f;
+        g = a * ((color >>  8) & 0xff) / 255.0f;
+        b = a * ((color      ) & 0xff) / 255.0f;
+    }
+
     float r;
     float g;
     float b;
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 10dbd5c..6eca468 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -19,7 +19,9 @@
 
 #include "FloatColor.h"
 #include "Matrix.h"
+#include "Program.h"
 #include "Rect.h"
+#include "SkiaShader.h"
 #include "utils/Macros.h"
 
 #include <GLES2/gl2.h>
@@ -31,6 +33,7 @@
 
 class Program;
 class RoundRectClipState;
+class Texture;
 
 /*
  * Enumerates optional vertex attributes
@@ -89,11 +92,6 @@
         bool colorEnabled;
         FloatColor color;
 
-        /* TODO
-        union shader {
-            //...
-        }; TODO
-        */
         ProgramDescription::ColorFilterMode filterMode;
         union Filter {
             struct Matrix {
@@ -102,6 +100,8 @@
             } matrix;
             FloatColor color;
         } filter;
+
+        SkiaShaderData skiaShaderData;
     } fill;
 
     struct Transform {
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 91e0f89..bdc5c5c2 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -39,7 +39,7 @@
     LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
             "not prepared for current stage")
 
-static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {;
+static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
     TextureVertex::setUV(quadVertex++, uvs.left, uvs.top);
     TextureVertex::setUV(quadVertex++, uvs.right, uvs.top);
     TextureVertex::setUV(quadVertex++, uvs.left, uvs.bottom);
@@ -49,6 +49,7 @@
 GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
         : mRenderState(renderState)
         , mCaches(caches)
+        , mShader(nullptr)
         , mOutGlop(outGlop) {
     mStageFlags = kInitialStage;
 }
@@ -192,12 +193,7 @@
             }
         }
     }
-
-    if (shader) {
-        SkiaShader::describe(&mCaches, mDescription, mCaches.extensions(), *shader);
-        // TODO: store shader data
-        LOG_ALWAYS_FATAL("shaders not yet supported");
-    }
+    mShader = shader; // shader resolved in ::build()
 
     if (colorFilter) {
         SkColor color;
@@ -394,6 +390,11 @@
 void GlopBuilder::build() {
     REQUIRE_STAGES(kAllStages);
 
+    // serialize shader info into ShaderData
+    GLuint textureUnit = mOutGlop->fill.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);
 
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index d724041..657e642 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -21,16 +21,17 @@
 #include "utils/Macros.h"
 
 class SkPaint;
+class SkShader;
 
 namespace android {
 namespace uirenderer {
 
 class Caches;
-struct Glop;
 class Matrix4;
 class RenderState;
 class Texture;
 class VertexBuffer;
+struct Glop;
 
 class GlopBuilder {
     PREVENT_COPY_AND_ASSIGN(GlopBuilder);
@@ -74,6 +75,7 @@
     ProgramDescription mDescription;
     RenderState& mRenderState;
     Caches& mCaches;
+    const SkShader* mShader;
     Glop* mOutGlop;
 };
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a00a2bc..4761ab4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -63,20 +63,6 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Functions
-///////////////////////////////////////////////////////////////////////////////
-
-template<typename T>
-static inline T min(T a, T b) {
-    return a < b ? a : b;
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1199,7 +1185,7 @@
 void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
     GLsizei elementsCount = quadsCount * 6;
     while (elementsCount > 0) {
-        GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+        GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
 
         setupDrawIndexedVertices(&mesh[0].x);
         glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, nullptr);
@@ -1970,7 +1956,7 @@
 }
 
 void OpenGLRenderer::drawAlphaBitmap(Texture* texture, const SkPaint* paint) {
-    if (USE_GLOPS && (!paint || !paint->getShader())) {
+    if (USE_GLOPS) {
         Glop glop;
         GlopBuilder aBuilder(mRenderState, mCaches, &glop);
         aBuilder.setMeshTexturedUnitQuad(texture->uvMapper, true)
@@ -2358,7 +2344,7 @@
         return;
     }
 
-    if (USE_GLOPS && !paint->getShader()) {
+    if (USE_GLOPS) {
         Glop glop;
         GlopBuilder aBuilder(mRenderState, mCaches, &glop);
         bool fudgeOffset = displayFlags & kVertexBuffer_Offset;
@@ -3052,7 +3038,7 @@
             GLsizei elementsCount = layer->meshElementCount;
 
             while (elementsCount > 0) {
-                GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+                GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
 
                 setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
                 DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
@@ -3113,7 +3099,7 @@
         return;
     }
 
-    if (USE_GLOPS && !paint->getShader()) {
+    if (USE_GLOPS) {
         Glop glop;
         GlopBuilder aBuilder(mRenderState, mCaches, &glop);
         aBuilder.setMeshTexturedUnitQuad(nullptr, true)
@@ -3274,7 +3260,7 @@
         return;
     }
 
-    if (USE_GLOPS && !paint->getShader()) {
+    if (USE_GLOPS) {
         const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
         Glop glop;
         GlopBuilder aBuilder(mRenderState, mCaches, &glop);
@@ -3320,7 +3306,7 @@
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         const SkPaint* paint, bool ignoreTransform) {
 
-    if (USE_GLOPS && !paint->getShader()) {
+    if (USE_GLOPS) {
         const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
         Glop glop;
         GlopBuilder aBuilder(mRenderState, mCaches, &glop);
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9c929da..81531e8 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -16,17 +16,17 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <utils/Log.h>
-
-#include <SkMatrix.h>
+#include "SkiaShader.h"
 
 #include "Caches.h"
 #include "Extensions.h"
 #include "Layer.h"
 #include "Matrix.h"
-#include "SkiaShader.h"
 #include "Texture.h"
 
+#include <SkMatrix.h>
+#include <utils/Log.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -34,7 +34,7 @@
 // Support
 ///////////////////////////////////////////////////////////////////////////////
 
-static const GLint gTileModes[] = {
+static const GLenum gTileModes[] = {
         GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
         GL_REPEAT,          // == SkShader::kRepeat_Mode
         GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
@@ -56,6 +56,10 @@
             a);
 }
 
+static inline void bindUniformColor(int slot, FloatColor color) {
+    glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color));
+}
+
 static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
     caches->textureState().bindTexture(texture->id);
     texture->setWrapST(wrapS, wrapT);
@@ -270,7 +274,7 @@
     }
 
     GLuint textureSlot = (*textureUnit)++;
-    Caches::getInstance().textureState().activateTexture(textureSlot);
+    caches->textureState().activateTexture(textureSlot);
 
     BitmapShaderInfo shaderInfo;
     if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) {
@@ -470,5 +474,290 @@
     SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Store / apply
+///////////////////////////////////////////////////////////////////////////////
+
+bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
+        GLuint* textureUnit, ProgramDescription* description,
+        SkiaShaderData::GradientShaderData* outData) {
+    SkShader::GradientInfo gradInfo;
+    gradInfo.fColorCount = 0;
+    gradInfo.fColors = nullptr;
+    gradInfo.fColorOffsets = nullptr;
+
+    SkMatrix unitMatrix;
+    switch (shader.asAGradient(&gradInfo)) {
+        case SkShader::kLinear_GradientType:
+            description->gradientType = ProgramDescription::kGradientLinear;
+
+            toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+            break;
+        case SkShader::kRadial_GradientType:
+            description->gradientType = ProgramDescription::kGradientCircular;
+
+            toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+                    gradInfo.fRadius[0], &unitMatrix);
+            break;
+        case SkShader::kSweep_GradientType:
+            description->gradientType = ProgramDescription::kGradientSweep;
+
+            toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+            break;
+        default:
+            // Do nothing. This shader is unsupported.
+            return false;
+    }
+    description->hasGradient = true;
+    description->isSimpleGradient = isSimpleGradient(gradInfo);
+
+    computeScreenSpaceMatrix(outData->screenSpace, unitMatrix,
+            shader.getLocalMatrix(), modelViewMatrix);
+
+    // re-query shader to get full color / offset data
+    std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]);
+    std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]);
+    gradInfo.fColors = &colorStorage[0];
+    gradInfo.fColorOffsets = &colorOffsets[0];
+    shader.asAGradient(&gradInfo);
+
+    if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+        outData->gradientSampler = (*textureUnit)++;
+
+#ifndef SK_SCALAR_IS_FLOAT
+    #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+        outData->gradientTexture = caches.gradientCache.get(
+                gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount);
+        outData->wrapST = gTileModes[gradInfo.fTileMode];
+    } else {
+        outData->gradientSampler = 0;
+        outData->gradientTexture = nullptr;
+
+        outData->startColor.set(gradInfo.fColors[0]);
+        outData->endColor.set(gradInfo.fColors[1]);
+    }
+
+    outData->ditherSampler = (*textureUnit)++;
+    return true;
+}
+
+void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) {
+    if (CC_UNLIKELY(data.gradientTexture)) {
+        caches.textureState().activateTexture(data.gradientSampler);
+        bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
+        glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
+    } else {
+        bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
+        bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
+    }
+
+    // TODO: remove sampler slot incrementing from dither.setupProgram,
+    // since this assignment of slots is done at store, not apply time
+    GLuint ditherSampler = data.ditherSampler;
+    caches.dither.setupProgram(caches.program(), &ditherSampler);
+    glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
+            GL_FALSE, &data.screenSpace.data[0]);
+}
+
+bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+        GLuint* textureUnit, ProgramDescription* description,
+        SkiaShaderData::BitmapShaderData* outData) {
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) {
+        return false;
+    }
+
+    outData->bitmapTexture = caches.textureCache.get(&bitmap);
+    if (!outData->bitmapTexture) return false;
+
+    outData->bitmapSampler = (*textureUnit)++;
+
+    const float width = outData->bitmapTexture->width;
+    const float height = outData->bitmapTexture->height;
+
+    description->hasBitmap = true;
+    if (!caches.extensions().hasNPot()
+            && (!isPowerOfTwo(width) || !isPowerOfTwo(height))
+            && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) {
+        description->isBitmapNpot = true;
+        description->bitmapWrapS = gTileModes[xy[0]];
+        description->bitmapWrapT = gTileModes[xy[1]];
+
+        outData->wrapS = GL_CLAMP_TO_EDGE;
+        outData->wrapT = GL_CLAMP_TO_EDGE;
+    } else {
+        outData->wrapS = gTileModes[xy[0]];
+        outData->wrapT = gTileModes[xy[1]];
+    }
+
+    computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
+    outData->textureDimension[0] = 1.0f / width;
+    outData->textureDimension[1] = 1.0f / height;
+
+    return true;
+}
+
+void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
+    caches.textureState().activateTexture(data.bitmapSampler);
+    bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
+    data.bitmapTexture->setFilter(GL_LINEAR);
+
+    glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+    glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
+            &data.textureTransform.data[0]);
+    glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+SkiaShaderType getComposeSubType(const SkShader& shader) {
+    // First check for a gradient shader.
+    switch (shader.asAGradient(nullptr)) {
+        case SkShader::kNone_GradientType:
+            // Not a gradient shader. Fall through to check for other types.
+            break;
+        case SkShader::kLinear_GradientType:
+        case SkShader::kRadial_GradientType:
+        case SkShader::kSweep_GradientType:
+            return kGradient_SkiaShaderType;
+        default:
+            // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+            return kNone_SkiaShaderType;
+    }
+
+    // The shader is not a gradient. Check for a bitmap shader.
+    if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
+        return kBitmap_SkiaShaderType;
+    }
+    return kNone_SkiaShaderType;
+}
+
+void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
+        const Matrix4& modelViewMatrix, GLuint* textureUnit,
+        ProgramDescription* description, SkiaShaderData* outData) {
+    LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix,
+                textureUnit, description, &outData->bitmapData),
+            "failed storing bitmap shader data");
+    LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix,
+                textureUnit, description, &outData->gradientData),
+            "failing storing gradient shader data");
+}
+
+bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+        GLuint* textureUnit, ProgramDescription* description,
+        SkiaShaderData* outData) {
+
+    SkShader::ComposeRec rec;
+    if (!shader.asACompose(&rec)) return false;
+
+    const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
+    const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
+
+    // check that type enum values are the 2 flags that compose the kCompose value
+    if ((shaderAType & shaderBType) != 0) return false;
+    if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
+
+    mat4 transform;
+    computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
+    if (shaderAType == kBitmap_SkiaShaderType) {
+        description->isBitmapFirst = true;
+        storeCompose(caches, *rec.fShaderA, *rec.fShaderB,
+                transform, textureUnit, description, outData);
+    } else {
+        description->isBitmapFirst = false;
+        storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
+                transform, textureUnit, description, outData);
+    }
+    if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) {
+        // TODO: Support other modes.
+        description->shadersMode = SkXfermode::kSrcOver_Mode;
+    }
+    return true;
+}
+
+bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+        GLuint* textureUnit, ProgramDescription* description,
+        SkiaShaderData::LayerShaderData* outData) {
+    Layer* layer;
+    if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+        return false;
+    }
+
+    description->hasBitmap = true;
+
+    outData->bitmapSampler = (*textureUnit)++;
+
+    const float width = layer->getWidth();
+    const float height = layer->getHeight();
+
+    computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
+
+    outData->textureDimension[0] = 1.0f / width;
+    outData->textureDimension[1] = 1.0f / height;
+    return true;
+}
+
+void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) {
+    caches.textureState().activateTexture(data.bitmapSampler);
+
+    data.layer->bindTexture();
+    data.layer->setWrap(GL_CLAMP_TO_EDGE);
+    data.layer->setFilter(GL_LINEAR);
+
+    glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+    glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1,
+            GL_FALSE, &data.textureTransform.data[0]);
+    glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+        GLuint* textureUnit, ProgramDescription* description,
+        SkiaShaderData* outData) {
+    if (!shader) {
+        outData->skiaShaderType = kNone_SkiaShaderType;
+        return;
+    }
+
+    if (tryStoreGradient(caches, *shader, modelViewMatrix,
+            textureUnit, description, &outData->gradientData)) {
+        outData->skiaShaderType = kGradient_SkiaShaderType;
+        return;
+    }
+
+    if (tryStoreBitmap(caches, *shader, modelViewMatrix,
+            textureUnit, description, &outData->bitmapData)) {
+        outData->skiaShaderType = kBitmap_SkiaShaderType;
+        return;
+    }
+
+    if (tryStoreCompose(caches, *shader, modelViewMatrix,
+            textureUnit, description, outData)) {
+        outData->skiaShaderType = kCompose_SkiaShaderType;
+        return;
+    }
+
+    if (tryStoreLayer(caches, *shader, modelViewMatrix,
+            textureUnit, description, &outData->layerData)) {
+        outData->skiaShaderType = kLayer_SkiaShaderType;
+    }
+}
+
+void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) {
+    if (!data.skiaShaderType) return;
+
+    if (data.skiaShaderType & kGradient_SkiaShaderType) {
+        applyGradient(caches, data.gradientData);
+    }
+    if (data.skiaShaderType & kBitmap_SkiaShaderType) {
+        applyBitmap(caches, data.bitmapData);
+    }
+
+    if (data.skiaShaderType == kLayer_SkiaShaderType) {
+        applyLayer(caches, data.layerData);
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index e110ca5..2962441c 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -17,32 +17,72 @@
 #ifndef ANDROID_HWUI_SKIA_SHADER_H
 #define ANDROID_HWUI_SKIA_SHADER_H
 
-#include <SkShader.h>
-#include <SkXfermode.h>
+#include "FloatColor.h"
+#include "Matrix.h"
 
 #include <GLES2/gl2.h>
-
+#include <SkShader.h>
+#include <SkXfermode.h>
 #include <cutils/compiler.h>
 
-#include "Matrix.h"
-
 namespace android {
 namespace uirenderer {
 
 class Caches;
 class Extensions;
 class Layer;
+class Texture;
 struct ProgramDescription;
 
 /**
  * Type of Skia shader in use.
+ *
+ * Note that kBitmap | kGradient = kCompose, since Compose implies
+ * both its component types are in use simultaneously. No other
+ * composition of multiple types is supported.
  */
 enum SkiaShaderType {
-    kNone_SkiaShaderType,
-    kBitmap_SkiaShaderType,
-    kGradient_SkiaShaderType,
-    kCompose_SkiaShaderType,
-    kLayer_SkiaShaderType
+    kNone_SkiaShaderType = 0,
+    kBitmap_SkiaShaderType = 1,
+    kGradient_SkiaShaderType = 2,
+    kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType,
+    kLayer_SkiaShaderType = 4,
+};
+
+struct SkiaShaderData {
+    SkiaShaderType skiaShaderType;
+    struct BitmapShaderData {
+        Texture* bitmapTexture;
+        GLuint bitmapSampler;
+        GLenum wrapS;
+        GLenum wrapT;
+
+        Matrix4 textureTransform;
+        float textureDimension[2];
+    } bitmapData;
+    struct GradientShaderData {
+        Matrix4 screenSpace;
+        GLuint ditherSampler;
+
+        // simple gradient
+        FloatColor startColor;
+        FloatColor endColor;
+
+        // complex gradient
+        Texture* gradientTexture;
+        GLuint gradientSampler;
+        GLenum wrapST;
+
+    } gradientData;
+    struct LayerShaderData {
+        Layer* layer;
+        GLuint bitmapSampler;
+        GLenum wrapS;
+        GLenum wrapT;
+
+        Matrix4 textureTransform;
+        float textureDimension[2];
+    } layerData;
 };
 
 class SkiaShader {
@@ -52,6 +92,12 @@
             const Extensions& extensions, const SkShader& shader);
     static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
             GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+
+    // new SkiaShader interaction model - store into ShaderData, and apply to Caches/Program/GL
+    static void store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+            GLuint* textureUnit, ProgramDescription* description,
+            SkiaShaderData* outData);
+    static void apply(Caches& caches, const SkiaShaderData& data);
 };
 
 class InvalidSkiaShader {
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index fd3845d..11d0c4b 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -21,8 +21,6 @@
 
 #include "utils/Macros.h"
 
-#include <type_traits>
-
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index e35327b..b64dbdc 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -266,7 +266,7 @@
     meshState().bindIndicesBufferInternal(mesh.indexBufferObject);
 
     if (mesh.vertexFlags & kTextureCoord_Attrib) {
-        // TODO: to support shaders, increment texture unit
+        // glop.fill.texture always takes slot 0, shader samplers increment from there
         mCaches->textureState().activateTexture(0);
 
         if (glop.fill.textureClamp != GL_INVALID_ENUM) {
@@ -294,6 +294,9 @@
         glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
     }
 
+    // Shader uniforms
+    SkiaShader::apply(*mCaches, glop.fill.skiaShaderData);
+
     // ------------------------------------
     // ---------- GL state setup ----------
     // ------------------------------------
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index eae73a9..5ca9083 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -16,6 +16,8 @@
 #ifndef MACROS_H
 #define MACROS_H
 
+#include <type_traits>
+
 #define PREVENT_COPY_AND_ASSIGN(Type) \
     private: \
         Type(const Type&) = delete; \