Replace edge types with GrEdgeEffect. 

This strips out last of the edge types and the fixed function edge attribute and replaces them with using GrEdgeEffect. Also fixes a minor bug when checking attribute counts -- it was using kAttribIndexCount instead of kVertexAttribCnt. 

Original Author: jvanverth@google.com
Review URL: https://codereview.chromium.org/13069003

git-svn-id: http://skia.googlecode.com/svn/trunk@8392 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 5374976..c28045d 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -131,6 +131,8 @@
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrEdgeEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrEdgeEffect.h',
       '<(skia_src_path)/gpu/effects/GrEllipseEdgeEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrEllipseEdgeEffect.h',
       '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index cff6324..9f24190 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -16,6 +16,8 @@
 #include "SkStrokeRec.h"
 #include "SkTrace.h"
 
+#include "effects/GrEdgeEffect.h"
+
 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
 }
 
@@ -446,6 +448,8 @@
     if (path->isEmpty()) {
         return true;
     }
+
+    GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
     GrDrawState* drawState = target->drawState();
 
     GrDrawState::AutoDeviceCoordDraw adcd(drawState);
@@ -484,12 +488,21 @@
         {kVec2f_GrVertexAttribType, 0},
         {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
     };
-    static const GrAttribBindings bindings = GrDrawState::kEdge_AttribBindingsBit;
 
     drawState->setVertexAttribs(kAttribs, SK_ARRAY_COUNT(kAttribs));
     drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
-    drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
-    drawState->setAttribBindings(bindings);
+    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,
+    };
+    static const int kEdgeAttrIndex = 1;
+    GrEffectRef* quadEffect = GrEdgeEffect::Create(GrEdgeEffect::kQuad_EdgeType);
+    drawState->setEffect(kEdgeEffectStage, quadEffect, kEdgeAttrIndex)->unref();
+
     GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
     if (!arg.succeeded()) {
         return false;
@@ -500,14 +513,11 @@
 
     create_vertices(segments, fanPt, verts, idxs);
 
-    GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
-    drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
     target->drawIndexed(kTriangles_GrPrimitiveType,
                         0,        // start vertex
                         0,        // start index
                         vCount,
                         iCount);
-    drawState->setVertexEdgeType(oldEdgeType);
 
     return true;
 }
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 80304f8..079c826 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -18,6 +18,8 @@
 #include "SkStroke.h"
 #include "SkTemplates.h"
 
+#include "effects/GrEdgeEffect.h"
+
 namespace {
 // quadratics are rendered as 5-sided polys in order to bound the
 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
@@ -508,7 +510,6 @@
         {kVec2f_GrVertexAttribType, 0},
         {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
     };
-    static const GrAttribBindings kBindings = GrDrawState::kEdge_AttribBindingsBit;
     SkMatrix viewM = drawState->getViewMatrix();
 
     PREALLOC_PTARRAY(128) lines;
@@ -522,8 +523,7 @@
 
     target->drawState()->setVertexAttribs(kAttribs, SK_ARRAY_COUNT(kAttribs));
     target->drawState()->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
-    target->drawState()->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
-    target->drawState()->setAttribBindings(kBindings);
+    target->drawState()->setAttribBindings(GrDrawState::kDefault_AttribBindings);
     GrAssert(sizeof(Vertex) == target->getDrawState().getVertexSize());
 
     if (!arg->set(target, vertCnt, 0)) {
@@ -589,8 +589,10 @@
         return false;
     }
 
-    GrDrawState::AutoDeviceCoordDraw adcd;
+    GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
     GrDrawState* drawState = target->drawState();
+
+    GrDrawState::AutoDeviceCoordDraw adcd;
     // createGeom transforms the geometry to device space when the matrix does not have
     // perspective.
     if (!drawState->getViewMatrix().hasPerspective()) {
@@ -603,12 +605,21 @@
     // TODO: See whether rendering lines as degenerate quads improves perf
     // when we have a mix
 
-    GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
+    enum {
+        // the edge effects share this stage with glyph rendering
+        // (kGlyphMaskStage in GrTextContext) && SW path rendering
+        // (kPathMaskStage in GrSWMaskHelper)
+        kEdgeEffectStage = GrPaint::kTotalStages,
+    };
+    static const int kEdgeAttrIndex = 1;
 
+    GrEffectRef* hairLineEffect = GrEdgeEffect::Create(GrEdgeEffect::kHairLine_EdgeType);
+    GrEffectRef* hairQuadEffect = GrEdgeEffect::Create(GrEdgeEffect::kHairQuad_EdgeType);
+ 
     target->setIndexSourceToBuffer(fLinesIndexBuffer);
     int lines = 0;
     int nBufLines = fLinesIndexBuffer->maxQuads();
-    drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType);
+    drawState->setEffect(kEdgeEffectStage, hairLineEffect, kEdgeAttrIndex)->unref();
     while (lines < lineCnt) {
         int n = GrMin(lineCnt - lines, nBufLines);
         target->drawIndexed(kTriangles_GrPrimitiveType,
@@ -621,7 +632,7 @@
 
     target->setIndexSourceToBuffer(fQuadsIndexBuffer);
     int quads = 0;
-    drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
+    drawState->setEffect(kEdgeEffectStage, hairQuadEffect, kEdgeAttrIndex)->unref();
     while (quads < quadCnt) {
         int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType,
@@ -631,6 +642,6 @@
                             kIdxsPerQuad*n);                   // iCount
         quads += n;
     }
-    drawState->setVertexEdgeType(oldEdgeType);
+
     return true;
 }
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 7982935..e108999 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -59,7 +59,7 @@
 #if GR_DEBUG
     uint32_t overlapCheck = 0;
 #endif
-    GrAssert(count <= GrDrawState::kAttribIndexCount);
+    GrAssert(count <= GrDrawState::kVertexAttribCnt);
     size_t size = 0;
     for (int index = 0; index < count; ++index) {
         size_t attribSize = GrDrawState::kVertexAttribSizes[attribs[index].fType];
@@ -83,14 +83,13 @@
     0,                            // position is not reflected in the bindings
     kColor_AttribBindingsBit,
     kCoverage_AttribBindingsBit,
-    kEdge_AttribBindingsBit,
     kLocalCoords_AttribBindingsBit,
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrDrawState::setVertexAttribs(const GrVertexAttrib* attribs, int count) {
-    GrAssert(count <= GrDrawState::kAttribIndexCount);
+    GrAssert(count <= GrDrawState::kVertexAttribCnt);
     fVertexAttribs.reset();
     for (int index = 0; index < count; ++index) {
         fVertexAttribs.push_back(attribs[index]);
@@ -140,18 +139,16 @@
             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;
+    if (fCommon.fAttribBindings & kAttribIndexMasks[kLocalCoords_AttribIndex]) {
+        int attributeIndex = fAttribIndices[kLocalCoords_AttribIndex];
+        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
@@ -371,12 +368,10 @@
         }
     }
 
-    // check for coverage due to constant coverage, per-vertex coverage,
-    // edge aa or coverage stage
+    // check for coverage due to constant coverage, per-vertex coverage, or coverage stage
     bool hasCoverage = forceCoverage ||
                        0xffffffff != this->getCoverage() ||
-                       (bindings & GrDrawState::kCoverage_AttribBindingsBit) ||
-                       (bindings & GrDrawState::kEdge_AttribBindingsBit);
+                       (bindings & GrDrawState::kCoverage_AttribBindingsBit);
     for (int s = this->getFirstCoverageStage();
          !hasCoverage && s < GrDrawState::kNumStages;
          ++s) {
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index ffa7b09..fed89db 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -76,9 +76,7 @@
      *
      * The input color to the first enabled color-stage is either the constant color or interpolated
      * per-vertex colors, depending upon GrAttribBindings. The input to the first coverage stage is
-     * either a constant coverage (usually full-coverage), interpolated per-vertex coverage, or
-     * edge-AA computed coverage. (This latter is going away as soon as it can be rewritten as a
-     * GrEffect).
+     * either a constant coverage (usually full-coverage) or interpolated per-vertex coverage.
      *
      * See the documentation of kCoverageDrawing_StateBit for information about disabling the
      * the color / coverage distinction.
@@ -125,7 +123,6 @@
         fCommon.fDstBlend = kZero_GrBlendCoeff;
         fCommon.fBlendConstant = 0x0;
         fCommon.fFlagBits = 0x0;
-        fCommon.fVertexEdgeType = kHairLine_EdgeType;
         fCommon.fStencilSettings.setDisabled();
         fCommon.fFirstCoverageStage = kNumStages;
         fCommon.fCoverage = 0xffffffff;
@@ -273,10 +270,6 @@
         /* program uses coverage (GrColor)
          */
         kCoverage_AttribBindingsBit           = 0x4,
-        /* program uses edge data. Distance to the edge is used to
-         * compute a coverage. See GrDrawState::setVertexEdgeType().
-         */
-        kEdge_AttribBindingsBit               = 0x8,
         // for below assert
         kDummyAttribBindingsBit,
         kHighAttribBindingsBit = kDummyAttribBindingsBit - 1
@@ -332,7 +325,6 @@
         kPosition_AttribIndex = 0,
         kColor_AttribIndex,
         kCoverage_AttribIndex,
-        kEdge_AttribIndex,
         kLocalCoords_AttribIndex,
 
         kLast_AttribIndex = kLocalCoords_AttribIndex
@@ -947,52 +939,6 @@
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
-    // @name Edge AA
-    // Edge equations can be specified to perform anti-aliasing. Because the
-    // edges are specified as per-vertex data, vertices that are shared by
-    // multiple edges must be split.
-    //
-    ////
-
-    /**
-     * When specifying edges as vertex data this enum specifies what type of
-     * edges are in use. The edges are always 4 SkScalars in memory, even when
-     * the edge type requires fewer than 4.
-     *
-     * TODO: Fix the fact that HairLine and Circle edge types use y-down coords.
-     *       (either adjust in VS or use origin_upper_left in GLSL)
-     */
-    enum VertexEdgeType {
-        /* 1-pixel wide line
-           2D implicit line eq (a*x + b*y +c = 0). 4th component unused */
-        kHairLine_EdgeType,
-        /* Quadratic specified by u^2-v canonical coords (only 2
-           components used). Coverage based on signed distance with negative
-           being inside, positive outside. Edge specified in window space
-           (y-down) */
-        kQuad_EdgeType,
-        /* Same as above but for hairline quadratics. Uses unsigned distance.
-           Coverage is min(0, 1-distance). */
-        kHairQuad_EdgeType,
-
-        kVertexEdgeTypeCnt
-    };
-
-    /**
-     * Determines the interpretation per-vertex edge data when the
-     * kEdge_AttribBindingsBit is set (see GrDrawTarget). When per-vertex edges
-     * are not specified the value of this setting has no effect.
-     */
-    void setVertexEdgeType(VertexEdgeType type) {
-        GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
-        fCommon.fVertexEdgeType = type;
-    }
-
-    VertexEdgeType getVertexEdgeType() const { return fCommon.fVertexEdgeType; }
-
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
     /// @name State Flags
     ////
 
@@ -1188,7 +1134,6 @@
         GrBlendCoeff                    fDstBlend;
         GrColor                         fBlendConstant;
         uint32_t                        fFlagBits;
-        VertexEdgeType                  fVertexEdgeType;
         GrStencilSettings               fStencilSettings;
         int                             fFirstCoverageStage;
         GrColor                         fCoverage;
@@ -1203,7 +1148,6 @@
                    fDstBlend == other.fDstBlend &&
                    fBlendConstant == other.fBlendConstant &&
                    fFlagBits == other.fFlagBits &&
-                   fVertexEdgeType == other.fVertexEdgeType &&
                    fStencilSettings == other.fStencilSettings &&
                    fFirstCoverageStage == other.fFirstCoverageStage &&
                    fCoverage == other.fCoverage &&
diff --git a/src/gpu/effects/GrEdgeEffect.cpp b/src/gpu/effects/GrEdgeEffect.cpp
new file mode 100644
index 0000000..f02fa35
--- /dev/null
+++ b/src/gpu/effects/GrEdgeEffect.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 "GrEdgeEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLSL.h"
+#include "GrTBackendEffectFactory.h"
+
+class GrGLEdgeEffect : public GrGLEffect {
+public:
+    GrGLEdgeEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+    : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrDrawEffect& drawEffect,
+                          EffectKey key,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const GrEdgeEffect& edgeEffect = drawEffect.castEffect<GrEdgeEffect>();
+        GrEdgeEffect::EdgeType type = edgeEffect.edgeType();
+
+        const char *vsName, *fsName;
+        const SkString* attrName =
+            builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
+        builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n");
+
+        switch (type) {
+        case GrEdgeEffect::kHairLine_EdgeType:
+            builder->addVarying(kVec4f_GrSLType, "HairEdge", &vsName, &fsName);
+
+            builder->fsCodeAppendf("\t\tedgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n",
+                                   builder->fragmentPosition(), fsName);
+            builder->fsCodeAppendf("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            break;
+        case GrEdgeEffect::kQuad_EdgeType:
+            GrAssert(builder->ctxInfo().caps()->shaderDerivativeSupport());
+            builder->addVarying(kVec4f_GrSLType, "QuadEdge", &vsName, &fsName);
+
+            // keep the derivative instructions outside the conditional
+            builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            builder->fsCodeAppendf("\t\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
+            // today we know z and w are in device space. We could use derivatives
+            builder->fsCodeAppendf("\t\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
+                                   fsName);
+            builder->fsCodeAppendf ("\t\t} else {\n");
+            builder->fsCodeAppendf("\t\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                   "\t\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                    fsName, fsName);
+            builder->fsCodeAppendf("\t\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, 
+                                   fsName);
+            builder->fsCodeAppendf("\t\t\tedgeAlpha = "
+                                   "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n");
+            if (kES2_GrGLBinding == builder->ctxInfo().binding()) {
+                builder->fHeader.append("#extension GL_OES_standard_derivatives: enable\n");
+            }
+            break;
+        case GrEdgeEffect::kHairQuad_EdgeType:
+            GrAssert(builder->ctxInfo().caps()->shaderDerivativeSupport());
+            builder->addVarying(kVec4f_GrSLType, "HairQuadEdge", &vsName, &fsName);
+
+            builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                   "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                   fsName, fsName);
+            builder->fsCodeAppendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
+                                   fsName);
+            builder->fsCodeAppend("\t\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
+            builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            if (kES2_GrGLBinding == builder->ctxInfo().binding()) {
+                builder->fHeader.append("#extension GL_OES_standard_derivatives: enable\n");
+            }
+            break;
+        };
+
+        SkString modulate;
+        GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
+        builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
+
+        builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
+    }
+
+    static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
+        const GrEdgeEffect& QuadEffect = drawEffect.castEffect<GrEdgeEffect>();
+
+        return QuadEffect.edgeType();
+    }
+
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
+    }
+
+private:
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrEdgeEffect::GrEdgeEffect(EdgeType edgeType) : GrEffect() {
+    if (edgeType == kQuad_EdgeType) {
+        this->addVertexAttrib(kVec4f_GrSLType);
+    } else {
+        this->addVertexAttrib(kVec4f_GrSLType);  // TODO: use different vec sizes for differnt edge
+                                                 // types.
+    }
+    fEdgeType = edgeType;
+}
+
+void GrEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    *validFlags = 0;
+}
+
+const GrBackendEffectFactory& GrEdgeEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrEdgeEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrEdgeEffect);
+
+GrEffectRef* GrEdgeEffect::TestCreate(SkMWCRandom* random,
+                                      GrContext*,
+                                      const GrDrawTargetCaps& caps,
+                                      GrTexture*[]) {
+    // Only kHairLine works without derivative instructions.
+    EdgeType edgeType;
+    if (caps.shaderDerivativeSupport()) {
+        edgeType = static_cast<EdgeType>(random->nextULessThan(kEdgeTypeCount));
+    } else {
+        edgeType = kHairLine_EdgeType;
+    }
+    return GrEdgeEffect::Create(edgeType);
+}
diff --git a/src/gpu/effects/GrEdgeEffect.h b/src/gpu/effects/GrEdgeEffect.h
new file mode 100644
index 0000000..c6238a2
--- /dev/null
+++ b/src/gpu/effects/GrEdgeEffect.h
@@ -0,0 +1,85 @@
+/*
+ * 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 GrEdgeEffect_DEFINED
+#define GrEdgeEffect_DEFINED
+
+#include "GrEffect.h"
+
+class GrGLEdgeEffect;
+
+/**
+ * The output of this effect is one of three different edge types: hairlines, quads, 
+ * and hairline quads.
+ */
+
+class GrEdgeEffect : public GrEffect {
+public:
+    enum EdgeType {
+        /* 1-pixel wide line
+           2D implicit device coord line eq (a*x + b*y +c = 0). 4th component unused. */
+        kHairLine_EdgeType = 0,
+        /* Quadratic specified by 0=u^2-v canonical coords. u and v are the first
+           two components of the vertex attribute. Coverage is based on signed
+           distance with negative being inside, positive outside. The edge is specified in
+           window space (y-down). If either the third or fourth component of the interpolated
+           vertex coord is > 0 then the pixel is considered outside the edge. This is used to
+           attempt to trim to a portion of the infinite quad. Requires shader derivative
+           instruction support. */
+        kQuad_EdgeType,
+        /* Similar to above but for hairline quadratics. Uses unsigned distance.
+           Coverage is min(0, 1-distance). 3rd & 4th component unused. Requires
+           shader derivative instruction support. */
+        kHairQuad_EdgeType,
+
+        kLast_EdgeType = kHairQuad_EdgeType
+    };
+    static const int kEdgeTypeCount = kLast_EdgeType + 1;
+
+    static GrEffectRef* Create(EdgeType type) {
+        // we go through this so we only have one copy of each effect
+        static GrEffectRef* gEdgeEffectRef[kEdgeTypeCount] = {
+            CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEdgeEffect, (kHairLine_EdgeType)))),
+            CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEdgeEffect, (kQuad_EdgeType)))),
+            CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEdgeEffect, (kHairQuad_EdgeType)))),
+        };
+        static SkAutoTUnref<GrEffectRef> gUnref0(gEdgeEffectRef[0]);
+        static SkAutoTUnref<GrEffectRef> gUnref1(gEdgeEffectRef[1]);
+        static SkAutoTUnref<GrEffectRef> gUnref2(gEdgeEffectRef[2]);
+        
+        gEdgeEffectRef[type]->ref();
+        return gEdgeEffectRef[type];
+    }
+
+    virtual ~GrEdgeEffect() {}
+
+    static const char* Name() { return "Edge"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLEdgeEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    EdgeType edgeType() const { return fEdgeType; }
+
+private:
+    GrEdgeEffect(EdgeType edgeType);
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const GrEdgeEffect& qee = CastEffect<GrEdgeEffect>(other);
+        return qee.fEdgeType == fEdgeType;
+    }
+
+    EdgeType fEdgeType;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 3331468..fc80a78 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -87,8 +87,7 @@
 
     // no reason to do edge aa or look at per-vertex coverage if coverage is ignored
     if (skipCoverage) {
-        desc->fAttribBindings &= ~(GrDrawState::kEdge_AttribBindingsBit |
-                                   GrDrawState::kCoverage_AttribBindingsBit);
+        desc->fAttribBindings &= ~(GrDrawState::kCoverage_AttribBindingsBit);
     }
 
     bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag);
@@ -118,12 +117,10 @@
 
     int lastEnabledStage = -1;
 
-    if (!skipCoverage && (desc->fAttribBindings & GrDrawState::kEdge_AttribBindingsBit)) {
-        desc->fVertexEdgeType = drawState.getVertexEdgeType();
+    if (!skipCoverage) {
         desc->fDiscardIfOutsideEdge = drawState.getStencil().doesWrite();
     } else {
         // Use canonical values when edge-aa is not enabled to avoid program cache misses.
-        desc->fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
         desc->fDiscardIfOutsideEdge = false;
     }
 
@@ -167,8 +164,7 @@
 
     // other coverage inputs
     if (!hasCoverage) {
-        hasCoverage = requiresAttributeCoverage ||
-                      (desc->fAttribBindings & GrDrawState::kEdge_AttribBindingsBit);
+        hasCoverage = requiresAttributeCoverage;
     }
 
     if (hasCoverage) {
@@ -207,9 +203,6 @@
     } else {
         desc->fCoverageAttributeIndex = GrDrawState::kCoverageOverrideAttribIndexValue;
     }
-    if (desc->fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
-        desc->fEdgeAttributeIndex = drawState.getAttribIndex(GrDrawState::kEdge_AttribIndex);
-    }
     if (desc->fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
         desc->fLocalCoordsAttributeIndex = drawState.getAttribIndex(GrDrawState::kLocalCoords_AttribIndex);
     }
@@ -227,10 +220,6 @@
         GrAssert(desc->fCoverageAttributeIndex < GrDrawState::kVertexAttribCnt);
         GrAssert(kAttribLayouts[vertexAttribs[desc->fCoverageAttributeIndex].fType].fCount == 4);
     }
-    if (desc->fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
-        GrAssert(desc->fEdgeAttributeIndex < GrDrawState::kVertexAttribCnt);
-        GrAssert(kAttribLayouts[vertexAttribs[desc->fEdgeAttributeIndex].fType].fCount == 4);
-    }
     if (desc->fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
         GrAssert(desc->fLocalCoordsAttributeIndex < GrDrawState::kVertexAttribCnt);
         GrAssert(kAttribLayouts[vertexAttribs[desc->fLocalCoordsAttributeIndex].fType].fCount == 2);
@@ -419,68 +408,6 @@
 }
 }
 
-bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
-                                  GrGLShaderBuilder* builder) const {
-    if (fDesc.fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
-        const char *vsName, *fsName;
-        builder->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
-        builder->addAttribute(kVec4f_GrSLType, EDGE_ATTR_NAME);
-        builder->vsCodeAppendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
-        switch (fDesc.fVertexEdgeType) {
-        case GrDrawState::kHairLine_EdgeType:
-            builder->fsCodeAppendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n",
-                                   builder->fragmentPosition(), fsName);
-            builder->fsCodeAppendf("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
-            break;
-        case GrDrawState::kQuad_EdgeType:
-            builder->fsCodeAppendf("\tfloat edgeAlpha;\n");
-            // keep the derivative instructions outside the conditional
-            builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
-            builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
-            builder->fsCodeAppendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
-            // today we know z and w are in device space. We could use derivatives
-            builder->fsCodeAppendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
-                                   fsName);
-            builder->fsCodeAppendf ("\t} else {\n");
-            builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
-                                   "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
-                                    fsName, fsName);
-            builder->fsCodeAppendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
-            builder->fsCodeAppendf("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
-                                   "\t}\n");
-            if (kES2_GrGLBinding == fContext.info().binding()) {
-                builder->fHeader.append("#extension GL_OES_standard_derivatives: enable\n");
-            }
-            break;
-        case GrDrawState::kHairQuad_EdgeType:
-            builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
-            builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
-            builder->fsCodeAppendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
-                                   "\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
-                                   fsName, fsName);
-            builder->fsCodeAppendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
-                                   fsName);
-            builder->fsCodeAppend("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
-            builder->fsCodeAppend("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
-            if (kES2_GrGLBinding == fContext.info().binding()) {
-                builder->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
-            }
-            break;
-        default:
-            GrCrash("Unknown Edge Type!");
-            break;
-        }
-        if (fDesc.fDiscardIfOutsideEdge) {
-            builder->fsCodeAppend("\tif (edgeAlpha <= 0.0) {\n\t\tdiscard;\n\t}\n");
-        }
-        *coverageVar = "edgeAlpha";
-        return true;
-    } else {
-        coverageVar->reset();
-        return false;
-    }
-}
-
 void GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
     switch (fDesc.fColorInput) {
         case GrGLProgram::Desc::kAttribute_ColorInput: {
@@ -834,19 +761,15 @@
     if (!wroteFragColorZero || Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
 
         if (!coverageIsZero) {
-            bool inCoverageIsScalar  = this->genEdgeCoverage(&inCoverage, &builder);
-
             switch (fDesc.fCoverageInput) {
                 case Desc::kSolidWhite_ColorInput:
                     // empty string implies solid white
                     break;
                 case Desc::kAttribute_ColorInput:
                     gen_attribute_coverage(&builder, &inCoverage);
-                    inCoverageIsScalar = false;
                     break;
                 case Desc::kUniform_ColorInput:
                     this->genUniformCoverage(&builder, &inCoverage);
-                    inCoverageIsScalar = false;
                     break;
                 default:
                     GrCrash("Unexpected input coverage.");
@@ -861,13 +784,6 @@
                     outCoverage.appendS32(s);
                     builder.fsCodeAppendf("\tvec4 %s;\n", outCoverage.c_str());
 
-                    // stages don't know how to deal with a scalar input. (Maybe they should. We
-                    // could pass a GrGLShaderVar)
-                    if (inCoverageIsScalar) {
-                        builder.fsCodeAppendf("\tvec4 %s4 = vec4(%s);\n",
-                                              inCoverage.c_str(), inCoverage.c_str());
-                        inCoverage.append("4");
-                    }
                     builder.setCurrentStage(s);
                     fEffects[s] = builder.createAndEmitGLEffect(
                                                     *stages[s],
@@ -984,9 +900,6 @@
     GL_CALL(BindAttribLocation(fProgramID, fDesc.fColorAttributeIndex, COL_ATTR_NAME));
     GL_CALL(BindAttribLocation(fProgramID, fDesc.fCoverageAttributeIndex, COV_ATTR_NAME));
 
-    if (fDesc.fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
-        GL_CALL(BindAttribLocation(fProgramID, fDesc.fEdgeAttributeIndex, EDGE_ATTR_NAME));
-    }
     if (fDesc.fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
         GL_CALL(BindAttribLocation(fProgramID,
                                    fDesc.fLocalCoordsAttributeIndex,
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 9fd01fd..a6ece6c 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -165,8 +165,6 @@
             kDualSrcOutputCnt
         };
 
-        // TODO: remove these two members when edge-aa can be rewritten as a GrEffect.
-        GrDrawState::VertexEdgeType fVertexEdgeType;
         // should the FS discard if the edge-aa coverage is zero (to avoid stencil manipulation)
         bool                        fDiscardIfOutsideEdge;
 
@@ -191,7 +189,6 @@
         int8_t                      fPositionAttributeIndex;
         int8_t                      fColorAttributeIndex;
         int8_t                      fCoverageAttributeIndex;
-        int8_t                      fEdgeAttributeIndex;
         int8_t                      fLocalCoordsAttributeIndex;
 
         friend class GrGLProgram;
@@ -225,11 +222,6 @@
 
     void genUniformCoverage(GrGLShaderBuilder* segments, SkString* inOutCoverage);
 
-    // generates code to compute coverage based on edge AA. Returns true if edge coverage was
-    // inserted in which case coverageVar will be updated to refer to a scalar. Otherwise,
-    // coverageVar is set to an empty string.
-    bool genEdgeCoverage(SkString* coverageVar, GrGLShaderBuilder* builder) const;
-
     // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
     bool bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
                                           bool bindColorOut,
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 1270b93..50ba9c3 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -39,17 +39,10 @@
     fExperimentalGS = gpu->caps()->geometryShaderSupport() && random->nextBool();
 #endif
 
-    bool edgeAA = random->nextBool();
-    if (edgeAA) {
-        fAttribBindings |= GrDrawState::kEdge_AttribBindingsBit;
-        if (gpu->caps()->shaderDerivativeSupport()) {
-            fVertexEdgeType = (GrDrawState::VertexEdgeType)
-                              random->nextULessThan(GrDrawState::kVertexEdgeTypeCnt);
-            fDiscardIfOutsideEdge = random->nextBool();
-        } else {
-            fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
-            fDiscardIfOutsideEdge = false;
-        }
+    if (gpu->caps()->shaderDerivativeSupport()) {
+        fDiscardIfOutsideEdge = random->nextBool();
+    } else {
+        fDiscardIfOutsideEdge = false;
     }
 
     if (gpu->caps()->dualSourceBlendingSupport()) {
@@ -84,10 +77,6 @@
         fCoverageAttributeIndex = attributeIndex;
         ++attributeIndex;
     }
-    if (fAttribBindings & GrDrawState::kEdge_AttribBindingsBit) {
-        fEdgeAttributeIndex = attributeIndex;
-        ++attributeIndex;
-    }
     if (fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
         fLocalCoordsAttributeIndex = attributeIndex;
         ++attributeIndex;