Initial change to create GeometryProcessor

BUG=skia:
R=bsalomon@google.com, robertphillips@google.com, egdaniel@google.com, jvanverth@google.com

Author: joshualitt@chromium.org

Review URL: https://codereview.chromium.org/509153002
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index c260382..e4fc6ec 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -678,7 +678,7 @@
 
     static const int kEdgeAttrIndex = 1;
     GrEffect* quadEffect = QuadEdgeEffect::Create();
-    drawState->addCoverageEffect(quadEffect, kEdgeAttrIndex)->unref();
+    drawState->setGeometryProcessor(quadEffect, kEdgeAttrIndex)->unref();
 
     GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
     if (!arg.succeeded()) {
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 2114720..9986ac8 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -1002,7 +1002,7 @@
             SkASSERT(NULL != hairQuadEffect);
             GrDrawState::AutoRestoreEffects are(drawState);
             target->setIndexSourceToBuffer(fQuadsIndexBuffer);
-            drawState->addCoverageEffect(hairQuadEffect, kEdgeAttrIndex)->unref();
+            drawState->setGeometryProcessor(hairQuadEffect, kEdgeAttrIndex)->unref();
             int quads = 0;
             while (quads < quadCnt) {
                 int n = SkTMin(quadCnt - quads, kNumQuadsInIdxBuffer);
@@ -1021,7 +1021,7 @@
             GrEffect* hairConicEffect = GrConicEffect::Create(kHairlineAA_GrEffectEdgeType,
                                                               *target->caps());
             SkASSERT(NULL != hairConicEffect);
-            drawState->addCoverageEffect(hairConicEffect, 1, 2)->unref();
+            drawState->setGeometryProcessor(hairConicEffect, 1, 2)->unref();
             int conics = 0;
             while (conics < conicCnt) {
                 int n = SkTMin(conicCnt - conics, kNumQuadsInIdxBuffer);
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index a7ddfde..9384457 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -648,7 +648,7 @@
     GrEffect* effect = GrRectEffect::Create();
     static const int kRectAttrIndex = 1;
     static const int kWidthIndex = 2;
-    drawState->addCoverageEffect(effect, kRectAttrIndex, kWidthIndex)->unref();
+    drawState->setGeometryProcessor(effect, kRectAttrIndex, kWidthIndex)->unref();
 
     for (int i = 0; i < 4; ++i) {
         verts[i].fCenter = center;
@@ -697,7 +697,7 @@
 
     GrEffect* effect = GrAlignedRectEffect::Create();
     static const int kOffsetIndex = 1;
-    drawState->addCoverageEffect(effect, kOffsetIndex)->unref();
+    drawState->setGeometryProcessor(effect, kOffsetIndex)->unref();
 
     SkRect devRect;
     combinedMatrix.mapRect(&devRect, rect);
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp
index 6599d4e..ac4c078 100755
--- a/src/gpu/GrBitmapTextContext.cpp
+++ b/src/gpu/GrBitmapTextContext.cpp
@@ -108,7 +108,7 @@
         // This effect could be stored with one of the cache objects (atlas?)
         int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
                                                                kGlyphCoordsNoColorAttributeIndex;
-        drawState->addCoverageEffect(fCachedEffect.get(), coordsIdx);
+        drawState->setGeometryProcessor(fCachedEffect.get(), coordsIdx);
         SkASSERT(NULL != fStrike);
         switch (fStrike->getMaskFormat()) {
             // Color bitmap text
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 322ea87..85d5311 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1738,7 +1738,8 @@
                                        AutoCheckFlush* acf) {
     // All users of this draw state should be freeing up all effects when they're done.
     // Otherwise effects that own resources may keep those resources alive indefinitely.
-    SkASSERT(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages());
+    SkASSERT(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages() &&
+             !fDrawState->hasGeometryProcessor());
 
     if (NULL == fGpu) {
         return NULL;
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 0c2b1c3..236d48f 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -54,6 +54,9 @@
     SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
     *this = state;
     if (!preConcatMatrix.isIdentity()) {
+        if (this->hasGeometryProcessor()) {
+            fGeometryProcessor->localCoordChange(preConcatMatrix);
+        }
         for (int i = 0; i < this->numColorStages(); ++i) {
             fColorStages[i].localCoordChange(preConcatMatrix);
         }
@@ -79,6 +82,11 @@
     fStencilSettings = that.fStencilSettings;
     fCoverage = that.fCoverage;
     fDrawFace = that.fDrawFace;
+    if (that.hasGeometryProcessor()) {
+        fGeometryProcessor.reset(SkNEW_ARGS(GrEffectStage, (*that.fGeometryProcessor.get())));
+    } else {
+        fGeometryProcessor.reset(NULL);
+    }
     fColorStages = that.fColorStages;
     fCoverageStages = that.fCoverageStages;
     fOptSrcBlend = that.fOptSrcBlend;
@@ -95,6 +103,7 @@
 
 void GrDrawState::onReset(const SkMatrix* initialViewMatrix) {
     SkASSERT(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages());
+    fGeometryProcessor.reset(NULL);
     fColorStages.reset();
     fCoverageStages.reset();
 
@@ -128,6 +137,9 @@
             // sad trombone sound
             return false;
         }
+        if (this->hasGeometryProcessor()) {
+            fGeometryProcessor->localCoordChange(invVM);
+        }
         for (int s = 0; s < this->numColorStages(); ++s) {
             fColorStages[s].localCoordChange(invVM);
         }
@@ -142,6 +154,7 @@
 void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRenderTarget* rt) {
     SkASSERT(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages());
 
+    fGeometryProcessor.reset(NULL);
     fColorStages.reset();
     fCoverageStages.reset();
 
@@ -286,6 +299,8 @@
 
 void GrDrawState::AutoRestoreEffects::set(GrDrawState* ds) {
     if (NULL != fDrawState) {
+        fDrawState->fGeometryProcessor.reset(fGeometryProcessor.detach());
+
         int m = fDrawState->numColorStages() - fColorEffectCnt;
         SkASSERT(m >= 0);
         fDrawState->fColorStages.pop_back_n(m);
@@ -300,6 +315,11 @@
     }
     fDrawState = ds;
     if (NULL != ds) {
+        if (ds->hasGeometryProcessor()) {
+            fGeometryProcessor.reset(SkNEW_ARGS(GrEffectStage, (*ds->getGeometryProcessor())));
+        } else {
+            fGeometryProcessor.reset(NULL);
+        }
         fColorEffectCnt = ds->numColorStages();
         fCoverageEffectCnt = ds->numCoverageStages();
         SkDEBUGCODE(++ds->fBlockEffectRemovalCnt;)
@@ -429,6 +449,10 @@
         SkASSERT(fDrawState->numCoverageStages() >= numCoverageStages);
 
         int i = 0;
+        if (fHasGeometryProcessor) {
+            SkASSERT(fDrawState->hasGeometryProcessor());
+            fDrawState->fGeometryProcessor->restoreCoordChange(fSavedCoordChanges[i++]);
+        }
         for (int s = 0; s < fNumColorStages; ++s, ++i) {
             fDrawState->fColorStages[s].restoreCoordChange(fSavedCoordChanges[i]);
         }
@@ -471,6 +495,7 @@
     if (0 == drawState->numTotalStages()) {
         drawState->fViewMatrix.reset();
         fDrawState = drawState;
+        fHasGeometryProcessor = false;
         fNumColorStages = 0;
         fSavedCoordChanges.reset(0);
         SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
@@ -492,6 +517,13 @@
     fSavedCoordChanges.reset(fDrawState->numTotalStages());
     int i = 0;
 
+    fHasGeometryProcessor = false;
+    if (fDrawState->hasGeometryProcessor()) {
+        fDrawState->fGeometryProcessor->saveCoordChange(&fSavedCoordChanges[i++]);
+        fDrawState->fGeometryProcessor->localCoordChange(coordChangeMatrix);
+        fHasGeometryProcessor = true;
+    }
+
     fNumColorStages = fDrawState->numColorStages();
     for (int s = 0; s < fNumColorStages; ++s, ++i) {
         fDrawState->getColorStage(s).saveCoordChange(&fSavedCoordChanges[i]);
@@ -543,6 +575,10 @@
         }
 
         // Run through the coverage stages
+        if (this->hasGeometryProcessor()) {
+            const GrEffect* effect = fGeometryProcessor->getEffect();
+            effect->getConstantColorComponents(&coverage, &coverageComponentFlags);
+        }
         for (int s = 0; s < this->numCoverageStages(); ++s) {
             const GrEffect* effect = this->getCoverageStage(s).getEffect();
             effect->getConstantColorComponents(&coverage, &coverageComponentFlags);
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 00bec9f..a1b07de 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -165,6 +165,20 @@
 
     /// @}
 
+    /**
+     * The geometry processor is the sole element of the skia pipeline which can use the vertex,
+     * geometry, and tesselation shaders.  The GP may also compute a coverage in its fragment shader
+     * but is never put in the color processing pipeline.
+     */
+
+    const GrEffect* setGeometryProcessor(const GrEffect* effect, int attr0 = -1, int attr1 = -1) {
+        SkASSERT(NULL != effect);
+        SkASSERT(!this->hasGeometryProcessor());
+        fGeometryProcessor.reset(new GrEffectStage(effect, attr0, attr1));
+        this->invalidateBlendOptFlags();
+        return effect;
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     /// @name Effect Stages
     /// Each stage hosts a GrEffect. The effect produces an output color or coverage in the fragment
@@ -242,6 +256,7 @@
 
     private:
         GrDrawState* fDrawState;
+        SkAutoTDelete<GrEffectStage> fGeometryProcessor;
         int fColorEffectCnt;
         int fCoverageEffectCnt;
     };
@@ -364,6 +379,7 @@
         GrDrawState*                                        fDrawState;
         SkMatrix                                            fViewMatrix;
         int                                                 fNumColorStages;
+        bool                                                fHasGeometryProcessor;
         SkAutoSTArray<8, GrEffectStage::SavedCoordChange>   fSavedCoordChanges;
     };
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 9605781..191194e 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -389,6 +389,15 @@
 
     SkASSERT(NULL != drawState.getRenderTarget());
 
+    if (drawState.hasGeometryProcessor()) {
+        const GrEffect* effect = drawState.getGeometryProcessor()->getEffect();
+        int numTextures = effect->numTextures();
+        for (int t = 0; t < numTextures; ++t) {
+            GrTexture* texture = effect->texture(t);
+            SkASSERT(texture->asRenderTarget() != drawState.getRenderTarget());
+        }
+    }
+
     for (int s = 0; s < drawState.numColorStages(); ++s) {
         const GrEffect* effect = drawState.getColorStage(s).getEffect();
         int numTextures = effect->numTextures();
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index f368dc8..330d4e9 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -553,7 +553,7 @@
 
     GrEffect* effect = CircleEdgeEffect::Create(isStrokeOnly && innerRadius > 0);
     static const int kCircleEdgeAttrIndex = 1;
-    drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
+    drawState->setGeometryProcessor(effect, kCircleEdgeAttrIndex)->unref();
 
     // The radii are outset for two reasons. First, it allows the shader to simply perform
     // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
@@ -696,7 +696,7 @@
 
     static const int kEllipseCenterAttrIndex = 1;
     static const int kEllipseEdgeAttrIndex = 2;
-    drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
+    drawState->setGeometryProcessor(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
 
     // Compute the reciprocals of the radii here to save time in the shader
     SkScalar xRadRecip = SkScalarInvert(xRadius);
@@ -814,8 +814,8 @@
 
     static const int kEllipseOuterOffsetAttrIndex = 1;
     static const int kEllipseInnerOffsetAttrIndex = 2;
-    drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex,
-                                         kEllipseInnerOffsetAttrIndex)->unref();
+    drawState->setGeometryProcessor(effect, kEllipseOuterOffsetAttrIndex,
+                                    kEllipseInnerOffsetAttrIndex)->unref();
 
     // This expands the outer rect so that after CTM we end up with a half-pixel border
     SkScalar a = vm[SkMatrix::kMScaleX];
@@ -1063,7 +1063,7 @@
 
         GrEffect* effect = CircleEdgeEffect::Create(isStrokeOnly);
         static const int kCircleEdgeAttrIndex = 1;
-        drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
+        drawState->setGeometryProcessor(effect, kCircleEdgeAttrIndex)->unref();
 
         // The radii are outset for two reasons. First, it allows the shader to simply perform
         // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
@@ -1168,8 +1168,9 @@
         GrEffect* effect = EllipseEdgeEffect::Create(isStrokeOnly);
         static const int kEllipseOffsetAttrIndex = 1;
         static const int kEllipseRadiiAttrIndex = 2;
-        drawState->addCoverageEffect(effect,
-                                     kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
+        drawState->setGeometryProcessor(effect,
+                                        kEllipseOffsetAttrIndex,
+                                        kEllipseRadiiAttrIndex)->unref();
 
         // Compute the reciprocals of the radii here to save time in the shader
         SkScalar xRadRecip = SkScalarInvert(xRadius);
diff --git a/src/gpu/GrRODrawState.cpp b/src/gpu/GrRODrawState.cpp
index 9118d0d..f6a2355 100644
--- a/src/gpu/GrRODrawState.cpp
+++ b/src/gpu/GrRODrawState.cpp
@@ -38,6 +38,18 @@
     }
 
     bool explicitLocalCoords = this->hasLocalCoordAttribute();
+    if (this->hasGeometryProcessor()) {
+        if (!that.hasGeometryProcessor()) {
+            return kIncompatible_CombinedState;
+        } else if (!GrEffectStage::AreCompatible(*this->getGeometryProcessor(),
+                                                 *that.getGeometryProcessor(),
+                                                 explicitLocalCoords)) {
+            return kIncompatible_CombinedState;
+        }
+    } else if (that.hasGeometryProcessor()) {
+        return kIncompatible_CombinedState;
+    }
+
     for (int i = 0; i < this->numColorStages(); i++) {
         if (!GrEffectStage::AreCompatible(this->getColorStage(i), that.getColorStage(i),
                                           explicitLocalCoords)) {
@@ -66,11 +78,9 @@
     for (int i = 0; i < kMaxVertexAttribCnt; ++i) {
         slTypes[i] = static_cast<GrSLType>(-1);
     }
-    int totalStages = this->numTotalStages();
-    for (int s = 0; s < totalStages; ++s) {
-        int covIdx = s - this->numColorStages();
-        const GrEffectStage& stage = covIdx < 0 ? this->getColorStage(s) :
-                                                  this->getCoverageStage(covIdx);
+
+    if (this->hasGeometryProcessor()) {
+        const GrEffectStage& stage = *this->getGeometryProcessor();
         const GrEffect* effect = stage.getEffect();
         SkASSERT(NULL != effect);
         // make sure that any attribute indices have the correct binding type, that the attrib
@@ -118,6 +128,10 @@
     }
 
     // Run through the coverage stages and see if the coverage will be all ones at the end.
+    if (this->hasGeometryProcessor()) {
+        const GrEffect* effect = fGeometryProcessor->getEffect();
+        effect->getConstantColorComponents(&coverage, &validComponentFlags);
+    }
     for (int s = 0; s < this->numCoverageStages(); ++s) {
         const GrEffect* effect = this->getCoverageStage(s).getEffect();
         effect->getConstantColorComponents(&coverage, &validComponentFlags);
@@ -140,6 +154,11 @@
             return true;
         }
     }
+    if (this->hasGeometryProcessor()) {
+        if (fGeometryProcessor->getEffect()->willReadDstColor()) {
+            return true;
+        }
+    }
     return false;
 }
 
diff --git a/src/gpu/GrRODrawState.h b/src/gpu/GrRODrawState.h
index 54d87bd..88392ba 100644
--- a/src/gpu/GrRODrawState.h
+++ b/src/gpu/GrRODrawState.h
@@ -121,8 +121,13 @@
 
     int numColorStages() const { return fColorStages.count(); }
     int numCoverageStages() const { return fCoverageStages.count(); }
-    int numTotalStages() const { return this->numColorStages() + this->numCoverageStages(); }
+    int numTotalStages() const {
+         return this->numColorStages() + this->numCoverageStages() +
+                 (this->hasGeometryProcessor() ? 1 : 0);
+    }
 
+    bool hasGeometryProcessor() const { return NULL != fGeometryProcessor.get(); }
+    const GrEffectStage* getGeometryProcessor() const { return fGeometryProcessor.get(); }
     const GrEffectStage& getColorStage(int stageIdx) const { return fColorStages[stageIdx]; }
     const GrEffectStage& getCoverageStage(int stageIdx) const { return fCoverageStages[stageIdx]; }
 
@@ -358,6 +363,7 @@
     GrBlendCoeff                        fDstBlend;
 
     typedef SkSTArray<4, GrEffectStage> EffectStageArray;
+    SkAutoTDelete<GrEffectStage>        fGeometryProcessor;
     EffectStageArray                    fColorStages;
     EffectStageArray                    fCoverageStages;
 
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index adee4ae..f4298eb 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -345,7 +345,7 @@
         bool isRoundCap = SkPaint::kRound_Cap == cap;
         GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap :
                                                         GrDashingEffect::kNonRound_DashCap;
-        drawState->addCoverageEffect(
+        drawState->setGeometryProcessor(
             GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType), 1)->unref();
     }
 
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 534e3c3..7b5dbb4 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -23,17 +23,19 @@
 
 GrGLProgram* GrGLProgram::Create(GrGpuGL* gpu,
                                  const GrGLProgramDesc& desc,
+                                 const GrEffectStage* geometryProcessor,
                                  const GrEffectStage* colorStages[],
                                  const GrEffectStage* coverageStages[]) {
     SkAutoTDelete<GrGLProgramBuilder> builder;
     if (!desc.getHeader().fRequiresVertexShader &&
         gpu->glCaps().pathRenderingSupport() &&
         gpu->glPathRendering()->texturingMode() == GrGLPathRendering::FixedFunction_TexturingMode) {
+        SkASSERT(NULL == geometryProcessor);
         builder.reset(SkNEW_ARGS(GrGLFragmentOnlyProgramBuilder, (gpu, desc)));
     } else {
         builder.reset(SkNEW_ARGS(GrGLFullProgramBuilder, (gpu, desc)));
     }
-    if (builder->genProgram(colorStages, coverageStages)) {
+    if (builder->genProgram(geometryProcessor, colorStages, coverageStages)) {
         SkASSERT(0 != builder->getProgramID());
         return SkNEW_ARGS(GrGLProgram, (gpu, desc, *builder));
     }
@@ -47,6 +49,7 @@
     , fCoverage(GrColor_ILLEGAL)
     , fDstCopyTexUnit(-1)
     , fBuiltinUniformHandles(builder.getBuiltinUniformHandles())
+    , fGeometryProcessor(SkSafeRef(builder.getGeometryProcessor()))
     , fColorEffects(SkRef(builder.getColorEffects()))
     , fCoverageEffects(SkRef(builder.getCoverageEffects()))
     , fProgramID(builder.getProgramID())
@@ -97,6 +100,9 @@
         fProgramDataManager.setSampler(fBuiltinUniformHandles.fDstCopySamplerUni, texUnitIdx);
         fDstCopyTexUnit = texUnitIdx++;
     }
+    if (NULL != fGeometryProcessor.get()) {
+        fGeometryProcessor->initSamplers(fProgramDataManager, &texUnitIdx);
+    }
     fColorEffects->initSamplers(fProgramDataManager, &texUnitIdx);
     fCoverageEffects->initSamplers(fProgramDataManager, &texUnitIdx);
 }
@@ -105,6 +111,7 @@
 
 void GrGLProgram::setData(GrGpu::DrawType drawType,
                           GrDrawState::BlendOptFlags blendOpts,
+                          const GrEffectStage* geometryProcessor,
                           const GrEffectStage* colorStages[],
                           const GrEffectStage* coverageStages[],
                           const GrDeviceCoordTexture* dstCopy,
@@ -149,6 +156,10 @@
         SkASSERT(!fBuiltinUniformHandles.fDstCopySamplerUni.isValid());
     }
 
+    if (NULL != fGeometryProcessor.get()) {
+        SkASSERT(NULL != geometryProcessor);
+        fGeometryProcessor->setData(fGpu, drawType,fProgramDataManager, geometryProcessor);
+    }
     fColorEffects->setData(fGpu, drawType,fProgramDataManager, colorStages);
     fCoverageEffects->setData(fGpu, drawType,fProgramDataManager, coverageStages);
 
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 5b9deb9..0f89e07 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -41,6 +41,7 @@
 
     static GrGLProgram* Create(GrGpuGL* gpu,
                                const GrGLProgramDesc& desc,
+                               const GrEffectStage* geometryProcessor,
                                const GrEffectStage* colorStages[],
                                const GrEffectStage* coverageStages[]);
 
@@ -158,6 +159,7 @@
      */
     void setData(GrGpu::DrawType,
                  GrDrawState::BlendOptFlags,
+                 const GrEffectStage* geometryProcessor,
                  const GrEffectStage* colorStages[],
                  const GrEffectStage* coverageStages[],
                  const GrDeviceCoordTexture* dstCopy, // can be NULL
@@ -191,6 +193,7 @@
     int                                 fDstCopyTexUnit;
 
     BuiltinUniformHandles               fBuiltinUniformHandles;
+    SkAutoTUnref<GrGLProgramEffects>    fGeometryProcessor;
     SkAutoTUnref<GrGLProgramEffects>    fColorEffects;
     SkAutoTUnref<GrGLProgramEffects>    fCoverageEffects;
     GrGLuint                            fProgramID;
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 545ed21..8d0ea3d 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -53,6 +53,7 @@
                             GrBlendCoeff dstCoeff,
                             const GrGpuGL* gpu,
                             const GrDeviceCoordTexture* dstCopy,
+                            const GrEffectStage** geometryProcessor,
                             SkTArray<const GrEffectStage*, true>* colorStages,
                             SkTArray<const GrEffectStage*, true>* coverageStages,
                             GrGLProgramDesc* desc) {
@@ -69,6 +70,7 @@
 
     int firstEffectiveColorStage = 0;
     bool inputColorIsUsed = true;
+
     if (!skipColor) {
         firstEffectiveColorStage = drawState.numColorStages();
         while (firstEffectiveColorStage > 0 && inputColorIsUsed) {
@@ -107,6 +109,9 @@
     bool requiresVertexShader = !GrGpu::IsPathRenderingDrawType(drawType);
 
     int numStages = 0;
+    if (drawState.hasGeometryProcessor()) {
+        numStages++;
+    }
     if (!skipColor) {
         numStages += drawState.numColorStages() - firstEffectiveColorStage;
     }
@@ -120,9 +125,14 @@
 
     int offsetAndSizeIndex = 0;
     bool effectKeySuccess = true;
-    if (!skipColor) {
-        for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) {
-            uint16_t* offsetAndSize =
+
+    KeyHeader* header = desc->header();
+    // make sure any padding in the header is zeroed.
+    memset(desc->header(), 0, kHeaderSize);
+
+    // We can only have one effect which touches the vertex shader
+    if (drawState.hasGeometryProcessor()) {
+        uint16_t* offsetAndSize =
                 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset +
                                             offsetAndSizeIndex * 2 * sizeof(uint16_t));
 
@@ -130,7 +140,7 @@
             uint16_t effectKeySize;
             uint32_t effectOffset = desc->fKey.count();
             effectKeySuccess |= GetEffectKeyAndUpdateStats(
-                                    drawState.getColorStage(s), gpu->glCaps(),
+                                    *drawState.getGeometryProcessor(), gpu->glCaps(),
                                     requiresLocalCoordAttrib, &b,
                                     &effectKeySize, &readsDst,
                                     &readFragPosition, &requiresVertexShader);
@@ -139,6 +149,32 @@
             offsetAndSize[0] = SkToU16(effectOffset);
             offsetAndSize[1] = effectKeySize;
             ++offsetAndSizeIndex;
+            *geometryProcessor = drawState.getGeometryProcessor();
+            SkASSERT(requiresVertexShader);
+            header->fHasGeometryProcessor = true;
+    }
+
+    if (!skipColor) {
+        for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) {
+            uint16_t* offsetAndSize =
+                reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset +
+                                            offsetAndSizeIndex * 2 * sizeof(uint16_t));
+
+            bool effectRequiresVertexShader = false;
+            GrEffectKeyBuilder b(&desc->fKey);
+            uint16_t effectKeySize;
+            uint32_t effectOffset = desc->fKey.count();
+            effectKeySuccess |= GetEffectKeyAndUpdateStats(
+                                    drawState.getColorStage(s), gpu->glCaps(),
+                                    requiresLocalCoordAttrib, &b,
+                                    &effectKeySize, &readsDst,
+                                    &readFragPosition, &effectRequiresVertexShader);
+            effectKeySuccess |= (effectOffset <= SK_MaxU16);
+
+            offsetAndSize[0] = SkToU16(effectOffset);
+            offsetAndSize[1] = effectKeySize;
+            ++offsetAndSizeIndex;
+            SkASSERT(!effectRequiresVertexShader);
         }
     }
     if (!skipCoverage) {
@@ -147,6 +183,7 @@
                 reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset +
                                             offsetAndSizeIndex * 2 * sizeof(uint16_t));
 
+            bool effectRequiresVertexShader = false;
             GrEffectKeyBuilder b(&desc->fKey);
             uint16_t effectKeySize;
             uint32_t effectOffset = desc->fKey.count();
@@ -154,12 +191,13 @@
                                     drawState.getCoverageStage(s), gpu->glCaps(),
                                     requiresLocalCoordAttrib, &b,
                                     &effectKeySize, &readsDst,
-                                    &readFragPosition, &requiresVertexShader);
+                                    &readFragPosition, &effectRequiresVertexShader);
             effectKeySuccess |= (effectOffset <= SK_MaxU16);
 
             offsetAndSize[0] = SkToU16(effectOffset);
             offsetAndSize[1] = effectKeySize;
             ++offsetAndSizeIndex;
+            SkASSERT(!effectRequiresVertexShader);
         }
     }
     if (!effectKeySuccess) {
@@ -167,10 +205,6 @@
         return false;
     }
 
-    KeyHeader* header = desc->header();
-    // make sure any padding in the header is zeroed.
-    memset(desc->header(), 0, kHeaderSize);
-
     // Because header is a pointer into the dynamic array, we can't push any new data into the key
     // below here.
 
@@ -259,9 +293,11 @@
     header->fCoverageOutput = kModulate_CoverageOutput;
 
     // If we do have coverage determine whether it matters.
-    bool separateCoverageFromColor = false;
+    bool separateCoverageFromColor = drawState.hasGeometryProcessor();
     if (!drawState.isCoverageDrawing() && !skipCoverage &&
-        (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) {
+        (drawState.numCoverageStages() > 0 ||
+         drawState.hasGeometryProcessor() ||
+         requiresCoverageAttrib)) {
 
         if (gpu->caps()->dualSourceBlendingSupport() &&
             !(blendOpts & (GrDrawState::kEmitCoverage_BlendOptFlag |
@@ -286,6 +322,7 @@
             separateCoverageFromColor = true;
         }
     }
+
     if (!skipColor) {
         for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) {
             colorStages->push_back(&drawState.getColorStage(s));
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index aefcb50..b1f54b7 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -48,6 +48,7 @@
                    const GrGpuGL* gpu,
                    const GrRenderTarget* dummyDstRenderTarget,
                    const GrTexture* dummyDstCopyTexture,
+                   const GrEffectStage* geometryProcessor,
                    const GrEffectStage* stages[],
                    int numColorStages,
                    int numCoverageStages,
@@ -67,10 +68,15 @@
                       GrBlendCoeff dstCoeff,
                       const GrGpuGL* gpu,
                       const GrDeviceCoordTexture* dstCopy,
+                      const GrEffectStage** outGeometryProcessor,
                       SkTArray<const GrEffectStage*, true>* outColorStages,
                       SkTArray<const GrEffectStage*, true>* outCoverageStages,
                       GrGLProgramDesc* outDesc);
 
+    bool hasGeometryProcessor() const {
+        return SkToBool(this->getHeader().fHasGeometryProcessor);
+    }
+
     int numColorEffects() const {
         return this->getHeader().fColorEffectCnt;
     }
@@ -161,6 +167,7 @@
         int8_t                      fColorAttributeIndex;
         int8_t                      fCoverageAttributeIndex;
 
+        SkBool8                     fHasGeometryProcessor;
         int8_t                      fColorEffectCnt;
         int8_t                      fCoverageEffectCnt;
     };
@@ -213,13 +220,24 @@
     class EffectKeyProvider {
     public:
         enum EffectType {
+            kGeometryProcessor_EffectType,
             kColor_EffectType,
             kCoverage_EffectType,
         };
 
         EffectKeyProvider(const GrGLProgramDesc* desc, EffectType type) : fDesc(desc) {
-            // Coverage effect key offsets begin immediately after those of the color effects.
-            fBaseIndex = kColor_EffectType == type ? 0 : desc->numColorEffects();
+            switch (type) {
+                case kGeometryProcessor_EffectType:
+                    // there can be only one
+                    fBaseIndex = 0;
+                    break;
+                case kColor_EffectType:
+                    fBaseIndex = desc->hasGeometryProcessor() ? 1 : 0;
+                    break;
+                case kCoverage_EffectType:
+                    fBaseIndex = desc->numColorEffects() + (desc->hasGeometryProcessor() ? 1 : 0);
+                    break;
+            }
         }
 
         GrEffectKey get(int index) const {
diff --git a/src/gpu/gl/GrGLProgramEffects.cpp b/src/gpu/gl/GrGLProgramEffects.cpp
index 8d97b42..45edca8 100644
--- a/src/gpu/gl/GrGLProgramEffects.cpp
+++ b/src/gpu/gl/GrGLProgramEffects.cpp
@@ -387,6 +387,24 @@
     }
 }
 
+void GrGLVertexProgramEffects::setData(GrGpuGL* gpu,
+                                       GrGpu::DrawType drawType,
+                                       const GrGLProgramDataManager& programDataManager,
+                                       const GrEffectStage* effectStage) {
+    SkASSERT(1 == fTransforms.count());
+    SkASSERT(1 == fSamplers.count());
+    SkASSERT(1 == fGLEffects.count());
+    GrDrawEffect drawEffect(*effectStage, fHasExplicitLocalCoords);
+    fGLEffects[0]->setData(programDataManager, drawEffect);
+    if (GrGpu::IsPathRenderingDrawType(drawType)) {
+        this->setPathTransformData(gpu, programDataManager, drawEffect, 0);
+    } else {
+        this->setTransformData(gpu, programDataManager, drawEffect, 0);
+    }
+
+    this->bindTextures(gpu, drawEffect.effect(), 0);
+}
+
 void GrGLVertexProgramEffects::setTransformData(GrGpuGL* gpu,
                                                 const GrGLProgramDataManager& pdman,
                                                 const GrDrawEffect& drawEffect,
diff --git a/src/gpu/gl/GrGLProgramEffects.h b/src/gpu/gl/GrGLProgramEffects.h
index 0eaff53..daaebd6 100644
--- a/src/gpu/gl/GrGLProgramEffects.h
+++ b/src/gpu/gl/GrGLProgramEffects.h
@@ -57,6 +57,11 @@
                          const GrGLProgramDataManager&,
                          const GrEffectStage* effectStages[]) = 0;
 
+    virtual void setData(GrGpuGL*,
+                         GrGpu::DrawType,
+                         const GrGLProgramDataManager&,
+                         const GrEffectStage* effectStages) { SkFAIL("DO NOT USE"); }
+
     void addEffect(GrGLEffect* effect) { fGLEffects.push_back(effect); }
 
     /**
@@ -140,7 +145,6 @@
     SkTArray<SkSTArray<4, Sampler, true> > fSamplers;
 
 private:
-    friend class GrGLFragmentO;
     typedef SkRefCnt INHERITED;
 };
 
@@ -172,6 +176,11 @@
                          const GrGLProgramDataManager&,
                          const GrEffectStage* effectStages[]) SK_OVERRIDE;
 
+    virtual void setData(GrGpuGL*,
+                         GrGpu::DrawType,
+                         const GrGLProgramDataManager&,
+                         const GrEffectStage* effectStages) SK_OVERRIDE;
+
 private:
     friend class GrGLFullProgramBuilder;
 
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 1f748bb..004ce25 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -179,6 +179,7 @@
 
         void abandon();
         GrGLProgram* getProgram(const GrGLProgramDesc& desc,
+                                const GrEffectStage* geometryProcessor,
                                 const GrEffectStage* colorStages[],
                                 const GrEffectStage* coverageStages[]);
 
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 8cdceb8..67c0cd3 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -90,6 +90,7 @@
 }
 
 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc,
+                                               const GrEffectStage* geometryProcessor,
                                                const GrEffectStage* colorStages[],
                                                const GrEffectStage* coverageStages[]) {
 #ifdef PROGRAM_CACHE_STATS
@@ -126,7 +127,8 @@
 #ifdef PROGRAM_CACHE_STATS
         ++fCacheMisses;
 #endif
-        GrGLProgram* program = GrGLProgram::Create(fGpu, desc, colorStages, coverageStages);
+        GrGLProgram* program = GrGLProgram::Create(fGpu, desc, geometryProcessor,
+                colorStages, coverageStages);
         if (NULL == program) {
             return NULL;
         }
@@ -222,6 +224,7 @@
             return false;
         }
 
+        const GrEffectStage* geometryProcessor = NULL;
         SkSTArray<8, const GrEffectStage*, true> colorStages;
         SkSTArray<8, const GrEffectStage*, true> coverageStages;
         GrGLProgramDesc desc;
@@ -232,6 +235,7 @@
                                dstCoeff,
                                this,
                                dstCopy,
+                               &geometryProcessor,
                                &colorStages,
                                &coverageStages,
                                &desc)) {
@@ -240,6 +244,7 @@
         }
 
         fCurrentProgram.reset(fProgramCache->getProgram(desc,
+                                                        geometryProcessor,
                                                         colorStages.begin(),
                                                         coverageStages.begin()));
         if (NULL == fCurrentProgram.get()) {
@@ -260,6 +265,7 @@
 
         fCurrentProgram->setData(type,
                                  blendOpts,
+                                 geometryProcessor,
                                  colorStages.begin(),
                                  coverageStages.begin(),
                                  dstCopy,
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 7664bab..ab64e28 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -31,7 +31,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool GrGLProgramBuilder::genProgram(const GrEffectStage* colorStages[],
+bool GrGLProgramBuilder::genProgram(const GrEffectStage* geometryProcessor,
+                                    const GrEffectStage* colorStages[],
                                     const GrEffectStage* coverageStages[]) {
     const GrGLProgramDesc::KeyHeader& header = this->desc().getHeader();
 
@@ -79,6 +80,8 @@
                                                    colorKeyProvider,
                                                    &inputColor));
 
+    this->emitGeometryProcessor(geometryProcessor, &inputCoverage);
+
     GrGLProgramDesc::EffectKeyProvider coverageKeyProvider(
         &this->desc(), GrGLProgramDesc::EffectKeyProvider::kCoverage_EffectType);
     fCoverageEffects.reset(this->createAndEmitEffects(coverageStages,
@@ -334,10 +337,23 @@
     , fVS(this) {
 }
 
-void GrGLFullProgramBuilder::emitCodeBeforeEffects(GrGLSLExpr4* color, GrGLSLExpr4* coverage) {
+void GrGLFullProgramBuilder::emitCodeBeforeEffects(GrGLSLExpr4* color,
+                                                   GrGLSLExpr4* coverage) {
     fVS.emitCodeBeforeEffects(color, coverage);
 }
 
+void GrGLFullProgramBuilder::emitGeometryProcessor(const GrEffectStage* geometryProcessor,
+                                                   GrGLSLExpr4* coverage) {
+    if (NULL != geometryProcessor) {
+        GrGLProgramDesc::EffectKeyProvider geometryProcessorKeyProvider(
+                &this->desc(), GrGLProgramDesc::EffectKeyProvider::kGeometryProcessor_EffectType);
+        fGeometryProcessor.reset(this->createAndEmitEffect(
+                                 geometryProcessor,
+                                 geometryProcessorKeyProvider,
+                                 coverage));
+    }
+}
+
 void GrGLFullProgramBuilder::emitCodeAfterEffects() {
     fVS.emitCodeAfterEffects();
 }
@@ -388,6 +404,56 @@
     return programEffectsBuilder.finish();
 }
 
+void GrGLFullProgramBuilder::createAndEmitEffect(GrGLProgramEffectsBuilder* programEffectsBuilder,
+                                              const GrEffectStage* effectStages,
+                                              const GrGLProgramDesc::EffectKeyProvider& keyProvider,
+                                              GrGLSLExpr4* fsInOutColor) {
+    GrGLSLExpr4 inColor = *fsInOutColor;
+    GrGLSLExpr4 outColor;
+
+    SkASSERT(NULL != effectStages && NULL != effectStages->getEffect());
+    const GrEffectStage& stage = *effectStages;
+
+    // Using scope to force ASR destructor to be triggered
+    {
+        CodeStage::AutoStageRestore csar(&fCodeStage, &stage);
+
+        if (inColor.isZeros()) {
+            SkString inColorName;
+
+            // Effects have no way to communicate zeros, they treat an empty string as ones.
+            this->nameVariable(&inColorName, '\0', "input");
+            fFS.codeAppendf("vec4 %s = %s;", inColorName.c_str(), inColor.c_str());
+            inColor = inColorName;
+        }
+
+        // create var to hold stage result
+        SkString outColorName;
+        this->nameVariable(&outColorName, '\0', "output");
+        fFS.codeAppendf("vec4 %s;", outColorName.c_str());
+        outColor = outColorName;
+
+
+        programEffectsBuilder->emitEffect(stage,
+                                          keyProvider.get(0),
+                                          outColor.c_str(),
+                                          inColor.isOnes() ? NULL : inColor.c_str(),
+                                          fCodeStage.stageIndex());
+    }
+
+    *fsInOutColor = outColor;
+}
+
+GrGLProgramEffects* GrGLFullProgramBuilder::createAndEmitEffect(
+        const GrEffectStage* geometryProcessor,
+        const GrGLProgramDesc::EffectKeyProvider& keyProvider,
+        GrGLSLExpr4* inOutFSColor) {
+
+    GrGLVertexProgramEffectsBuilder programEffectsBuilder(this, 1);
+    this->createAndEmitEffect(&programEffectsBuilder, geometryProcessor, keyProvider, inOutFSColor);
+    return programEffectsBuilder.finish();
+}
+
 bool GrGLFullProgramBuilder::compileAndAttachShaders(GrGLuint programId,
                                                      SkTDArray<GrGLuint>* shaderIds) const {
     return INHERITED::compileAndAttachShaders(programId, shaderIds)
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 00193ef..deb3708 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -84,11 +84,14 @@
      * to be used.
      * @return true if generation was successful.
      */
-    bool genProgram(const GrEffectStage* inColorStages[],
+
+    bool genProgram(const GrEffectStage* inGeometryProcessor,
+                    const GrEffectStage* inColorStages[],
                     const GrEffectStage* inCoverageStages[]);
 
-    // Below are the results of the shader generation.
-
+    GrGLProgramEffects* getGeometryProcessor() const {
+        SkASSERT(fProgramID); return fGeometryProcessor.get();
+    }
     GrGLProgramEffects* getColorEffects() const { SkASSERT(fProgramID); return fColorEffects.get(); }
     GrGLProgramEffects* getCoverageEffects() const { SkASSERT(fProgramID); return fCoverageEffects.get(); }
     const BuiltinUniformHandles& getBuiltinUniformHandles() const {
@@ -165,6 +168,7 @@
     void appendDecls(const VarArray&, SkString*) const;
     void appendUniformDecls(ShaderVisibility, SkString*) const;
 
+    SkAutoTUnref<GrGLProgramEffects> fGeometryProcessor;
     SkAutoTUnref<GrGLProgramEffects> fColorEffects;
     SkAutoTUnref<GrGLProgramEffects> fCoverageEffects;
     BuiltinUniformHandles            fUniformHandles;
@@ -173,7 +177,7 @@
     GrGLuint                         fProgramID;
     GrGLFragmentShaderBuilder        fFS;
     SeparableVaryingInfoArray        fSeparableVaryingInfos;
-private:
+
     class CodeStage : SkNoncopyable {
     public:
         CodeStage() : fNextIndex(0), fCurrentIndex(-1), fEffectStage(NULL) {}
@@ -224,6 +228,7 @@
         int                     fCurrentIndex;
         const GrEffectStage*    fEffectStage;
     } fCodeStage;
+private:
 
     /**
      * The base class will emit the fragment code that precedes the per-effect code and then call
@@ -232,7 +237,14 @@
      *
      * The subclass can modify the initial color or coverage 
      */
-    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color, GrGLSLExpr4* coverage) = 0;
+    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color,
+                                       GrGLSLExpr4* coverage) = 0;
+
+    /*
+     * Full shader builder needs to emit code after the color stages and before the coverage stages
+     */
+    virtual void emitGeometryProcessor(const GrEffectStage* geometryProcessor,
+                                       GrGLSLExpr4* coverage) = 0;
 
     /**
     * Adds code for effects and returns a GrGLProgramEffects* object. The caller is responsible for
@@ -297,13 +309,30 @@
     GrGLVertexShaderBuilder* getVertexShaderBuilder() { return &fVS; }
 
 private:
-    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color, GrGLSLExpr4* coverage) SK_OVERRIDE;
+    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color,
+                                       GrGLSLExpr4* coverage) SK_OVERRIDE;
+
+    virtual void emitGeometryProcessor(const GrEffectStage* geometryProcessor,
+                                       GrGLSLExpr4* coverage) SK_OVERRIDE;
 
     virtual GrGLProgramEffects* createAndEmitEffects(const GrEffectStage* effectStages[],
                                                      int effectCnt,
                                                      const GrGLProgramDesc::EffectKeyProvider&,
                                                      GrGLSLExpr4* inOutFSColor) SK_OVERRIDE;
 
+    /*
+     * These functions are temporary and will eventually operate not on effects but on
+     * geometry processors
+     */
+    void createAndEmitEffect(GrGLProgramEffectsBuilder*,
+                             const GrEffectStage* effectStage,
+                             const GrGLProgramDesc::EffectKeyProvider&,
+                             GrGLSLExpr4* inOutFSColor);
+
+    GrGLProgramEffects* createAndEmitEffect(const GrEffectStage* geometryProcessor,
+                                            const GrGLProgramDesc::EffectKeyProvider&,
+                                            GrGLSLExpr4* inOutFSColor);
+
     virtual void emitCodeAfterEffects() SK_OVERRIDE;
 
     virtual bool compileAndAttachShaders(GrGLuint programId,
@@ -326,7 +355,13 @@
     int addTexCoordSets(int count);
 
 private:
-    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color, GrGLSLExpr4* coverage) SK_OVERRIDE {}
+    virtual void emitCodeBeforeEffects(GrGLSLExpr4* color,
+                                       GrGLSLExpr4* coverage) SK_OVERRIDE {}
+
+    virtual void emitGeometryProcessor(const GrEffectStage* geometryProcessor,
+                                       GrGLSLExpr4* coverage) SK_OVERRIDE {
+        SkASSERT(NULL == geometryProcessor);
+    }
 
     virtual GrGLProgramEffects* createAndEmitEffects(const GrEffectStage* effectStages[],
                                                      int effectCnt,