Add GrEllipseEdgeEffect.

Adds the effect that replaces the old oval rendering code. Also hooks in code to set attribute names and indices for effects.

Author: jvanverth@google.com

Review URL: https://chromiumcodereview.appspot.com/12462008

git-svn-id: http://skia.googlecode.com/svn/trunk@8092 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 046e067..7c85212 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -12,6 +12,7 @@
 #include "effects/GrConvolutionEffect.h"
 #include "effects/GrSingleTextureEffect.h"
 #include "effects/GrConfigConversionEffect.h"
+#include "effects/GrEllipseEdgeEffect.h"
 
 #include "GrBufferAllocPool.h"
 #include "GrGpu.h"
@@ -1072,12 +1073,8 @@
         {kVec2f_GrVertexAttribType, 0},
         {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
     };
-    static const GrAttribBindings kAttributeBindings = GrDrawState::kEdge_AttribBindingsBit;
-
     drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
     drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
-    drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
-    drawState->setAttribBindings(kAttributeBindings);
     GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
 
     GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
@@ -1097,7 +1094,9 @@
     SkScalar B;
 
     if (isCircle) {
+        drawState->setAttribBindings(GrDrawState::kEdge_AttribBindingsBit);
         drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
+        drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
 
         xRadius = vm.mapRadius(xRadius);
 
@@ -1129,7 +1128,17 @@
         T = -outerRadius;
         B = +outerRadius;
     } else {  // is axis-aligned ellipse
-        drawState->setVertexEdgeType(GrDrawState::kEllipse_EdgeType);
+        drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
+
+        enum {
+            // the edge effects share this stage with glyph rendering 
+            // (kGlyphMaskStage in GrTextContext) && SW path rendering 
+            // (kPathMaskStage in GrSWMaskHelper)
+            kEdgeEffectStage = GrPaint::kTotalStages,
+        };
+        GrEffectRef* effect = GrEllipseEdgeEffect::Create();
+        static const int kEdgeAttrIndex = 1;
+        drawState->setEffect(kEdgeEffectStage, effect, &kEdgeAttrIndex)->unref();
 
         SkRect xformedRect;
         vm.mapRect(&xformedRect, oval);
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index e797546..fddce41 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -133,6 +133,85 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+bool GrDrawState::validateVertexAttribs() const {
+    // color and coverage can set indices beyond the standard count
+    static const int kMaxValidAttribIndex = kVertexAttribCnt+2;
+    int attributeTypes[kMaxValidAttribIndex];
+    for (int i = 0; i < kMaxValidAttribIndex; ++i) {
+        attributeTypes[i] = -1;
+    }
+
+    // sentinel to make sure effects don't try to use built-in attributes
+    static const int kBuiltInAttributeType = 10000; 
+
+    // check our built-in indices
+    if (fAttribIndices[kPosition_AttribIndex] >= kVertexAttribCnt) {
+        return false;
+    }
+    attributeTypes[fAttribIndices[kPosition_AttribIndex]] = kBuiltInAttributeType;
+    for (int j = kColor_AttribIndex; j <= kCoverage_AttribIndex; ++j) {
+        if (fCommon.fAttribBindings & kAttribIndexMasks[j]) {
+            int attributeIndex = fAttribIndices[j];
+            if (attributeIndex >= kMaxValidAttribIndex) {
+                return false;
+            }
+            // they should not be shared at all
+            if (attributeTypes[attributeIndex] != -1) {
+                return false;
+            }
+            attributeTypes[attributeIndex] = kBuiltInAttributeType;
+        }
+    }
+    for (int j = kEdge_AttribIndex; j < kAttribIndexCount; ++j) {
+        if (fCommon.fAttribBindings & kAttribIndexMasks[j]) {
+            int attributeIndex = fAttribIndices[j];
+            if (attributeIndex >= kVertexAttribCnt) {
+                return false;
+            }
+            // they should not be shared at all
+            if (attributeTypes[attributeIndex] != -1) {
+                return false;
+            }
+            attributeTypes[attributeIndex] = kBuiltInAttributeType;
+        }
+    }
+
+    // now those set by effects
+    for (int s = 0; s < kNumStages; ++s) { 
+        const GrEffectStage& stage = fStages[s];
+        const GrEffectRef* effect = stage.getEffect();
+        if (effect == NULL) {
+            continue;
+        }
+
+        // make sure that the count in the stage and the effect matches
+        int numAttributes = stage.getVertexAttribIndexCount();
+        if (numAttributes != effect->get()->numVertexAttribs()) {
+            return false;
+        }
+
+        // make sure that any shared indices have the same type
+        const int* attributeIndices = stage.getVertexAttribIndices();
+        for (int i = 0; i < numAttributes; ++i) {
+            int attributeIndex = attributeIndices[i];
+            if (attributeIndex >= kVertexAttribCnt) {
+                return false;
+            }
+
+            GrSLType attributeType = effect->get()->vertexAttribType(i);
+            if (attributeTypes[attributeIndex] != -1 && 
+                attributeTypes[attributeIndex] != attributeType) {
+                return false;
+            }
+            attributeTypes[attributeIndex] = attributeType;
+        }
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 bool GrDrawState::AttributesBindExplicitTexCoords(GrAttribBindings attribBindings) {
     return SkToBool(kTexCoord_AttribBindingsMask & attribBindings);
 }
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 3f37297..220e290 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -181,6 +181,8 @@
      */
     void setDefaultVertexAttribs();
 
+    bool validateVertexAttribs() const;
+
     ////////////////////////////////////////////////////////////////////////////
     // Helpers for picking apart vertex attributes
 
@@ -332,7 +334,7 @@
     bool hasSolidCoverage(GrAttribBindings) const;
 
     static void VertexAttributesUnitTest();
-
+   
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
@@ -483,8 +485,9 @@
     /// @name Effect Stages
     ////
 
-    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) {
-        fStages[stageIdx].setEffect(effect);
+    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect, 
+                                 const int* indices = NULL) {
+        fStages[stageIdx].setEffect(effect, indices);
         return effect;
     }
 
@@ -514,7 +517,9 @@
         return true;
     }
 
-    void disableStage(int stageIdx) { this->setEffect(stageIdx, NULL); }
+    void disableStage(int stageIdx) { 
+        this->setEffect(stageIdx, NULL, NULL); 
+    }
 
     /**
      * Release all the GrEffects referred to by this draw state.
@@ -1021,9 +1026,6 @@
         /* Circle specified as center_x, center_y, outer_radius, inner_radius
            all in window space (y-down). */
         kCircle_EdgeType,
-        /* Axis-aligned ellipse specified as center_x, center_y, x_radius, x_radius/y_radius
-           all in window space (y-down). */
-        kEllipse_EdgeType,
 
         kVertexEdgeTypeCnt
     };
@@ -1189,14 +1191,9 @@
         if (fRenderTarget.get() != s.fRenderTarget.get() || fCommon != s.fCommon) {
             return false;
         }
-        if (fVertexAttribs.count() != s.fVertexAttribs.count()) {
+        if (fVertexAttribs != s.fVertexAttribs) {
             return false;
         }
-        for (int i = 0; i < fVertexAttribs.count(); ++i) {
-            if (fVertexAttribs[i] != s.fVertexAttribs[i]) {
-                return false;
-            }
-        }
         for (int i = 0; i < kAttribIndexCount; ++i) {
             if ((i == kPosition_AttribIndex ||
                     s.fCommon.fAttribBindings & kAttribIndexMasks[i]) &&
@@ -1335,13 +1332,9 @@
                     return false;
                 }
             }
-            if (fVertexAttribs.count() != state.fVertexAttribs.count()) {
+            if (fVertexAttribs != state.fVertexAttribs) {
                 return false;
             }
-            for (int i = 0; i < fVertexAttribs.count(); ++i)
-                if (fVertexAttribs[i] != state.fVertexAttribs[i]) {
-                    return false;
-            }
             for (int i = 0; i < kNumStages; ++i) {
                 if (!fStages[i].isEqual(state.fStages[i])) {
                     return false;
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 954a4e7..6fdc3a0 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -392,6 +392,8 @@
             }
         }
     }
+
+    GrAssert(drawState.validateVertexAttribs());
 #endif
     if (NULL == drawState.getRenderTarget()) {
         return false;
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index 6e743e7..2aad7cf 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -90,6 +90,11 @@
     fTextureAccesses.push_back(access);
 }
 
+void GrEffect::addVertexAttrib(GrSLType type) {
+    GrAssert(fVertexAttribTypes.count() < kMaxVertexAttribs);
+    fVertexAttribTypes.push_back(type);
+}
+
 void* GrEffect::operator new(size_t size) {
     return GrEffect_Globals::GetTLS()->allocate(size);
 }
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 9682ec4..fe9fd82 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -193,7 +193,8 @@
     }
     enum {
         // the SW path renderer shares this stage with glyph
-        // rendering (kGlyphMaskStage in GrBatchedTextContext)
+        // rendering (kGlyphMaskStage in GrTextContext)
+        // && edge rendering (kEdgeEffectStage in GrContext)
         kPathMaskStage = GrPaint::kTotalStages,
     };
     GrAssert(!drawState->isStageEnabled(kPathMaskStage));
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index db5abb4..a8defcb 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -19,6 +19,9 @@
 #include "SkStrokeRec.h"
 
 enum {
+    // glyph rendering shares this stage with edge rendering 
+    // (kEdgeEffectStage in GrContext) && SW path rendering 
+    // (kPathMaskStage in GrSWMaskHelper)
     kGlyphMaskStage = GrPaint::kTotalStages,
 };
 
diff --git a/src/gpu/effects/GrEllipseEdgeEffect.cpp b/src/gpu/effects/GrEllipseEdgeEffect.cpp
new file mode 100644
index 0000000..002753e
--- /dev/null
+++ b/src/gpu/effects/GrEllipseEdgeEffect.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrEllipseEdgeEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+
+class GrGLEllipseEdgeEffect : public GrGLEffect {
+public:
+    GrGLEllipseEdgeEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
+    : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage& stage,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const char *vsName, *fsName;
+        builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsName, &fsName);
+
+        const SkString* attrName = builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]);
+        builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
+        
+        builder->fsCodeAppend("\tfloat edgeAlpha;\n");
+        // translate to origin
+        builder->fsCodeAppendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
+        // scale y by xRadius/yRadius
+        builder->fsCodeAppendf("\toffset.y *= %s.w;\n", fsName);  
+        builder->fsCodeAppend("\tfloat d = length(offset);\n");
+        // compare length against xRadius
+        builder->fsCodeAppendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+        SkString modulate;
+        GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+        builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
+    }
+
+    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return 0;
+    }
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
+    }
+
+private:
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrEllipseEdgeEffect::GrEllipseEdgeEffect() : GrEffect() {
+    this->addVertexAttrib(kVec4f_GrSLType);
+}
+
+void GrEllipseEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    *validFlags = 0;
+}
+
+const GrBackendEffectFactory& GrEllipseEdgeEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrEllipseEdgeEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrEllipseEdgeEffect);
+
+GrEffectRef* GrEllipseEdgeEffect::TestCreate(SkMWCRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
+    return GrEllipseEdgeEffect::Create();
+}
diff --git a/src/gpu/effects/GrEllipseEdgeEffect.h b/src/gpu/effects/GrEllipseEdgeEffect.h
new file mode 100644
index 0000000..8f56d77
--- /dev/null
+++ b/src/gpu/effects/GrEllipseEdgeEffect.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrEllipseEdgeEffect_DEFINED
+#define GrEllipseEdgeEffect_DEFINED
+
+#include "GrEffect.h"
+
+class GrGLEllipseEdgeEffect;
+
+/**
+ * The output of this effect is a modulation of the input color and coverage for an axis-aligned
+ * ellipse, specified as center_x, center_y, x_radius, x_radius/y_radius in window space (y-down).
+ */
+
+class GrEllipseEdgeEffect : public GrEffect {
+public:
+    static GrEffectRef* Create() {
+        // maybe only have one static copy?
+        AutoEffectUnref effect(SkNEW(GrEllipseEdgeEffect));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrEllipseEdgeEffect() {}
+
+    static const char* Name() { return "EllipseEdge"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLEllipseEdgeEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrEllipseEdgeEffect();
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        return true;
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
index ccea269..f2cd37c 100644
--- a/src/gpu/gl/GrGLEffect.cpp
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -31,3 +31,18 @@
     }
     return key;
 }
+
+GrGLEffect::EffectKey GrGLEffect::GenAttribKey(const GrEffectStage& stage) {
+    EffectKey key = 0;
+
+    int numAttributes = stage.getVertexAttribIndexCount();
+    GrAssert(numAttributes <= 2);
+    const int* attributeIndices = stage.getVertexAttribIndices();
+    for (int index = 0; index < numAttributes; ++index) {
+        EffectKey value = attributeIndices[index] << 2*index;
+        GrAssert(0 == (value & key)); // keys for each attribute ought not to overlap
+        key |= value;
+    }
+
+    return key;
+}
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index 76f865e..869fbda 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -87,6 +87,7 @@
     const char* name() const { return fFactory.name(); }
 
     static EffectKey GenTextureKey(const GrEffectRef*, const GrGLCaps&);
+    static EffectKey GenAttribKey(const GrEffectStage& stage);
 
    /**
     * GrGLEffect subclasses get passed a GrEffectStage in their emitCode and setData functions.
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 2b6ccbd..c164357 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -419,9 +419,7 @@
     if (fDesc.fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
         const char *vsName, *fsName;
         builder->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
-        builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
-                                          GrGLShaderVar::kAttribute_TypeModifier,
-                                          EDGE_ATTR_NAME);
+        builder->addAttribute(kVec4f_GrSLType, EDGE_ATTR_NAME);
         builder->vsCodeAppendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
         switch (fDesc.fVertexEdgeType) {
         case GrDrawState::kHairLine_EdgeType:
@@ -467,13 +465,6 @@
             builder->fsCodeAppendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
             builder->fsCodeAppend("\tedgeAlpha = outerAlpha * innerAlpha;\n");
             break;
-        case GrDrawState::kEllipse_EdgeType:
-            builder->fsCodeAppend("\tfloat edgeAlpha;\n");
-            builder->fsCodeAppendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
-            builder->fsCodeAppendf("\toffset.y *= %s.w;\n", fsName);
-            builder->fsCodeAppend("\tfloat d = length(offset);\n");
-            builder->fsCodeAppendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
-            break;
         default:
             GrCrash("Unknown Edge Type!");
             break;
@@ -492,9 +483,7 @@
 void GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
     switch (fDesc.fColorInput) {
         case GrGLProgram::Desc::kAttribute_ColorInput: {
-            builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
-                GrGLShaderVar::kAttribute_TypeModifier,
-                COL_ATTR_NAME);
+            builder->addAttribute(kVec4f_GrSLType, COL_ATTR_NAME);
             const char *vsName, *fsName;
             builder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
             builder->vsCodeAppendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
@@ -534,9 +523,7 @@
 namespace {
 void gen_attribute_coverage(GrGLShaderBuilder* builder,
                             SkString* inOutCoverage) {
-    builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
-                                      GrGLShaderVar::kAttribute_TypeModifier,
-                                      COV_ATTR_NAME);
+    builder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME);
     const char *vsName, *fsName;
     builder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
     builder->vsCodeAppendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
@@ -777,9 +764,7 @@
 
     // add texture coordinates that are used to the list of vertex attr decls
     if (GrDrawState::AttributesBindExplicitTexCoords(attribBindings)) {
-        builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
-            GrGLShaderVar::kAttribute_TypeModifier,
-            TEX_ATTR_NAME);
+        builder.addAttribute(kVec2f_GrSLType, TEX_ATTR_NAME);
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -917,6 +902,11 @@
                     inCoverage = outCoverage;
                 }
             }
+
+            // discard if coverage is zero
+            if (fDesc.fDiscardIfOutsideEdge && !outCoverage.isEmpty()) {
+                builder.fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n", outCoverage.c_str());
+            }
         }
 
         if (Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
@@ -1022,6 +1012,13 @@
         GL_CALL(BindAttribLocation(fProgramID, fDesc.fTexCoordAttributeIndex, TEX_ATTR_NAME));
     }
 
+    const GrGLShaderBuilder::AttributePair* attribEnd = builder.getEffectAttributes().end();
+    for (const GrGLShaderBuilder::AttributePair* attrib = builder.getEffectAttributes().begin(); 
+         attrib != attribEnd;
+         ++attrib) {
+         GL_CALL(BindAttribLocation(fProgramID, attrib->fIndex, attrib->fName.c_str()));
+    }
+
     GL_CALL(LinkProgram(fProgramID));
 
     GrGLint linked = GR_GL_INIT_ZERO;
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index 4559fdd..940501c 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -9,6 +9,7 @@
 #define GrGLSL_DEFINED
 
 #include "gl/GrGLInterface.h"
+#include "GrTypesPriv.h"
 
 class GrGLShaderVar;
 class SkString;
@@ -34,22 +35,6 @@
     k150_GrGLSLGeneration,
 };
 
-/**
- * Types of shader-language-specific boxed variables we can create.
- * (Currently only GrGLShaderVars, but should be applicable to other shader
- * languages.)
- */
-enum GrSLType {
-    kVoid_GrSLType,
-    kFloat_GrSLType,
-    kVec2f_GrSLType,
-    kVec3f_GrSLType,
-    kVec4f_GrSLType,
-    kMat33f_GrSLType,
-    kMat44f_GrSLType,
-    kSampler2D_GrSLType
-};
-
 enum GrSLConstantVec {
     kZeros_GrSLConstantVec,
     kOnes_GrSLConstantVec,
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 5c3f5b3..1a238ae 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -258,6 +258,22 @@
     return fUniforms[handle_to_index(u)].fVariable;
 }
 
+bool GrGLShaderBuilder::addAttribute(GrSLType type,
+                                     const char* name) {
+    for (int i = 0; i < fVSAttrs.count(); ++i) {
+        const GrGLShaderVar& attr = fVSAttrs[i];
+        // if attribute already added, don't add it again
+        if (attr.getName().equals(name)) {
+            GrAssert(attr.getType() == type);
+            return false;
+        }
+    }
+    fVSAttrs.push_back().set(type,
+                             GrGLShaderVar::kAttribute_TypeModifier,
+                             name);
+    return true;
+}
+
 void GrGLShaderBuilder::addVarying(GrSLType type,
                                    const char* name,
                                    const char** vsOutName,
@@ -491,6 +507,18 @@
         samplerHandles->push_back(textureSamplers[i].fSamplerUniform);
     }
 
+    int numAttributes = stage.getVertexAttribIndexCount();
+    const int* attributeIndices = stage.getVertexAttribIndices();
+    SkSTArray<GrEffect::kMaxVertexAttribs, SkString> attributeNames;
+    for (int i = 0; i < numAttributes; ++i) {
+        SkString attributeName("aAttr");
+        attributeName.appendS32(attributeIndices[i]);
+
+        if (this->addAttribute(effect->vertexAttribType(i), attributeName.c_str())) {
+            fEffectAttributes.push_back().set(attributeIndices[i], attributeName);
+        }
+    }
+
     GrGLEffect* glEffect = effect->getFactory().createGLInstance(effect);
 
     // Enclose custom code in a block to avoid namespace conflicts
@@ -508,3 +536,16 @@
 
     return glEffect;
 }
+
+const SkString* GrGLShaderBuilder::getEffectAttributeName(int attributeIndex) const {
+    const AttributePair* attribEnd = this->getEffectAttributes().end();
+    for (const AttributePair* attrib = this->getEffectAttributes().begin(); 
+         attrib != attribEnd;
+         ++attrib) {
+        if (attrib->fIndex == attributeIndex) {
+            return &attrib->fName;
+        }
+    }   
+
+    return NULL;
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 08b3ef5..524a885 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -184,7 +184,11 @@
         return this->getUniformVariable(u).c_str();
     }
 
-    /** Add a varying variable to the current program to pass values between vertex and fragment
+   /** Add a vertex attribute to the current program that is passed in from the vertex data.
+       Returns false if the attribute was already there, true otherwise. */
+    bool addAttribute(GrSLType type, const char* name);
+
+   /** Add a varying variable to the current program to pass values between vertex and fragment
         shaders. If the last two parameters are non-NULL, they are filled in with the name
         generated. */
     void addVarying(GrSLType type,
@@ -222,6 +226,19 @@
                                 const char* vsInCoord,
                                 SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
     GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+
+    struct AttributePair {
+        void set(int index, const SkString& name) {
+            fIndex = index; fName = name;
+        }
+        int      fIndex;
+        SkString fName;
+    };
+    const SkSTArray<10, AttributePair, true>& getEffectAttributes() const {
+        return fEffectAttributes;
+    }
+    const SkString* getEffectAttributeName(int attributeIndex) const;
+
     // TODO: Make this do all the compiling, linking, etc.
     void finished(GrGLuint programID);
 
@@ -270,6 +287,8 @@
     bool                                fSetupFragPosition;
     GrGLUniformManager::UniformHandle   fRTHeightUniform;
 
+    SkSTArray<10, AttributePair, true>  fEffectAttributes;
+
     GrGLShaderVar*                      fPositionVar;
 };