Create xfer processor backend.

This includes:
-Having an actual XP stage at the end of the gl pipeline.
-All Blending work is handled by XP until actually setting GL blend states
-GLPrograms test to test XP

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/4dffc940c430eec66d4707490eace19c9b3f7904

Review URL: https://codereview.chromium.org/764643004
diff --git a/src/gpu/GrGeometryProcessor.h b/src/gpu/GrGeometryProcessor.h
index 4801069..a0cfc09 100644
--- a/src/gpu/GrGeometryProcessor.h
+++ b/src/gpu/GrGeometryProcessor.h
@@ -62,7 +62,10 @@
 
     virtual const char* name() const = 0;
 
-    /** Implemented using GLProcessor::GenKey as described in this class's comment. */
+    /**
+     * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry
+     * processor's GL backend implementation.
+     */
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const = 0;
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index 409305c..b4c1a60 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -44,7 +44,8 @@
                                                    drawState.isColorWriteDisabled(),
                                                    drawState.getStencil().doesWrite(),
                                                    &fColor,
-                                                   &fCoverage);
+                                                   &fCoverage,
+                                                   caps);
     }
 
     // When path rendering the stencil settings are not always set on the draw state
@@ -56,9 +57,6 @@
         // indicate that this can be skipped.
         fFlags = 0;
         fDrawFace = GrDrawState::kInvalid_DrawFace;
-        fSrcBlend = kZero_GrBlendCoeff;
-        fDstBlend = kZero_GrBlendCoeff;
-        fBlendConstant = 0x0;
         fViewMatrix.reset();
         return;
     }
@@ -108,9 +106,6 @@
 
     GrXferProcessor::BlendInfo blendInfo;
     fXferProcessor->getBlendInfo(&blendInfo);
-    fSrcBlend = blendInfo.fSrcBlend;
-    fDstBlend = blendInfo.fDstBlend;
-    fBlendConstant = blendInfo.fBlendConstant;
 
     this->adjustProgramFromOptimizations(drawState, optFlags, colorPOI, coveragePOI,
                                          &firstColorStageIdx, &firstCoverageStageIdx);
@@ -146,42 +141,6 @@
         init.fCoverage = this->getCoverage();
         fGeometryProcessor->initBatchTracker(&fBatchTracker, init);
     }
-
-    this->setOutputStateInfo(drawState, coverageColor, optFlags, caps);
-}
-
-void GrOptDrawState::setOutputStateInfo(const GrDrawState& ds,
-                                        GrColor coverage,
-                                        GrXferProcessor::OptFlags optFlags,
-                                        const GrDrawTargetCaps& caps) {
-    // Set this default and then possibly change our mind if there is coverage.
-    fDescInfo.fPrimaryOutputType = GrProgramDesc::kModulate_PrimaryOutputType;
-    fDescInfo.fSecondaryOutputType = GrProgramDesc::kNone_SecondaryOutputType;
-
-    // Determine whether we should use dual source blending or shader code to keep coverage
-    // separate from color.
-    bool keepCoverageSeparate = !(optFlags & GrXferProcessor::kSetCoverageDrawing_OptFlag);
-    if (keepCoverageSeparate && !ds.hasSolidCoverage(coverage)) {
-        if (caps.dualSourceBlendingSupport()) {
-            if (kZero_GrBlendCoeff == fDstBlend) {
-                // write the coverage value to second color
-                fDescInfo.fSecondaryOutputType = GrProgramDesc::kCoverage_SecondaryOutputType;
-                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
-            } else if (kSA_GrBlendCoeff == fDstBlend) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                fDescInfo.fSecondaryOutputType = GrProgramDesc::kCoverageISA_SecondaryOutputType;
-                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
-            } else if (kSC_GrBlendCoeff == fDstBlend) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                fDescInfo.fSecondaryOutputType = GrProgramDesc::kCoverageISC_SecondaryOutputType;
-                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
-            }
-        } else if (fDescInfo.fReadsDst &&
-                   kOne_GrBlendCoeff == fSrcBlend &&
-                   kZero_GrBlendCoeff == fDstBlend) {
-            fDescInfo.fPrimaryOutputType = GrProgramDesc::kCombineWithDst_PrimaryOutputType;
-        }
-    }
 }
 
 void GrOptDrawState::adjustProgramFromOptimizations(const GrDrawState& ds,
@@ -237,10 +196,7 @@
         this->fNumColorStages != that.fNumColorStages ||
         this->fScissorState != that.fScissorState ||
         !this->fViewMatrix.cheapEqualTo(that.fViewMatrix) ||
-        this->fSrcBlend != that.fSrcBlend ||
-        this->fDstBlend != that.fDstBlend ||
         this->fDrawType != that.fDrawType ||
-        this->fBlendConstant != that.fBlendConstant ||
         this->fFlags != that.fFlags ||
         this->fStencilSettings != that.fStencilSettings ||
         this->fDrawFace != that.fDrawFace ||
@@ -262,6 +218,10 @@
         return false;
     }
 
+    if (!this->getXferProcessor()->isEqual(*that.getXferProcessor())) {
+        return false;
+    }
+
     // The program desc comparison should have already assured that the stage counts match.
     SkASSERT(this->numFragmentStages() == that.numFragmentStages());
     for (int i = 0; i < this->numFragmentStages(); i++) {
diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h
index cf57373..8a7b47e 100644
--- a/src/gpu/GrOptDrawState.h
+++ b/src/gpu/GrOptDrawState.h
@@ -79,7 +79,8 @@
     int numCoverageStages() const { return fFragmentStages.count() - fNumColorStages; }
     int numFragmentStages() const { return fFragmentStages.count(); }
     int numTotalStages() const {
-         return this->numFragmentStages() + (this->hasGeometryProcessor() ? 1 : 0);
+        // the + 1 at the end is for the xferProcessor which will always be present
+        return this->numFragmentStages() + (this->hasGeometryProcessor() ? 1 : 0) + 1;
     }
 
     bool hasGeometryProcessor() const { return SkToBool(fGeometryProcessor.get()); }
@@ -103,21 +104,6 @@
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
-    /// @name Blending
-    ////
-
-    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlend; }
-    GrBlendCoeff getDstBlendCoeff() const { return fDstBlend; }
-
-    /**
-     * Retrieves the last value set by setBlendConstant()
-     * @return the blending constant value
-     */
-    GrColor getBlendConstant() const { return fBlendConstant; }
-
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
     /// @name View Matrix
     ////
 
@@ -223,13 +209,10 @@
     ScissorState                        fScissorState;
     GrColor                             fColor;
     SkMatrix                            fViewMatrix;
-    GrColor                             fBlendConstant;
     GrStencilSettings                   fStencilSettings;
     uint8_t                             fCoverage;
     GrDrawState::DrawFace               fDrawFace;
     GrDeviceCoordTexture                fDstCopy;
-    GrBlendCoeff                        fSrcBlend;
-    GrBlendCoeff                        fDstBlend;
     uint32_t                            fFlags;
     ProgramGeometryProcessor            fGeometryProcessor;
     GrBatchTracker                      fBatchTracker;
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index ccd4d7f..2bcd9b4 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -32,9 +32,9 @@
 }
 
 template<>
-SkTArray<GrProcessorTestFactory<GrXferProcessor>*, true>*
-GrProcessorTestFactory<GrXferProcessor>::GetFactories() {
-    static SkTArray<GrProcessorTestFactory<GrXferProcessor>*, true> gFactories;
+SkTArray<GrProcessorTestFactory<GrXPFactory>*, true>*
+GrProcessorTestFactory<GrXPFactory>::GetFactories() {
+    static SkTArray<GrProcessorTestFactory<GrXPFactory>*, true> gFactories;
     return &gFactories;
 }
 
@@ -52,7 +52,7 @@
  */
 static const int kFPFactoryCount = 37;
 static const int kGPFactoryCount = 14;
-static const int kXPFactoryCount = 0;
+static const int kXPFactoryCount = 1;
 
 template<>
 void GrProcessorTestFactory<GrFragmentProcessor>::VerifyFactoryCount() {
@@ -69,9 +69,9 @@
 }
 
 template<>
-void GrProcessorTestFactory<GrXferProcessor>::VerifyFactoryCount() {
+void GrProcessorTestFactory<GrXPFactory>::VerifyFactoryCount() {
     if (kXPFactoryCount != GetFactories()->count()) {
-        SkFAIL("Wrong number of xfer processor factories!");
+        SkFAIL("Wrong number of xp factory factories!");
     }
 }
 
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index a20b99f..3d53842 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -55,36 +55,6 @@
     }
 
 
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Stage Output Types
-    ////
-
-    enum PrimaryOutputType {
-        // Modulate color and coverage, write result as the color output.
-        kModulate_PrimaryOutputType,
-        // Combines the coverage, dst, and color as coverage * color + (1 - coverage) * dst. This
-        // can only be set if fDstReadKey is non-zero.
-        kCombineWithDst_PrimaryOutputType,
-
-        kPrimaryOutputTypeCnt,
-    };
-
-    enum SecondaryOutputType {
-        // There is no secondary output
-        kNone_SecondaryOutputType,
-        // Writes coverage as the secondary output. Only set if dual source blending is supported
-        // and primary output is kModulate.
-        kCoverage_SecondaryOutputType,
-        // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending
-        // is supported and primary output is kModulate.
-        kCoverageISA_SecondaryOutputType,
-        // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source
-        // blending is supported and primary output is kModulate.
-        kCoverageISC_SecondaryOutputType,
-
-        kSecondaryOutputTypeCnt,
-    };
-
     // Specifies where the initial color comes from before the stages are applied.
     enum ColorInput {
         kAllOnes_ColorInput,
@@ -105,9 +75,6 @@
         ColorInput                  fColorInput : 8;
         ColorInput                  fCoverageInput : 8;
 
-        PrimaryOutputType           fPrimaryOutputType : 8;
-        SecondaryOutputType         fSecondaryOutputType : 8;
-
         SkBool8                     fHasGeometryProcessor;
         int8_t                      fColorEffectCnt;
         int8_t                      fCoverageEffectCnt;
@@ -140,10 +107,7 @@
                    fInputCoverageIsUsed == that.fInputCoverageIsUsed &&
                    fReadsDst == that.fReadsDst &&
                    fReadsFragPosition == that.fReadsFragPosition &&
-                   fRequiresLocalCoordAttrib == that.fRequiresLocalCoordAttrib &&
-                   fPrimaryOutputType == that.fPrimaryOutputType &&
-                   fSecondaryOutputType == that.fSecondaryOutputType;
-
+                   fRequiresLocalCoordAttrib == that.fRequiresLocalCoordAttrib;
         }
         bool operator!=(const DescInfo& that) const { return !(*this == that); };
         // TODO when GPs control uniform / attribute handling of color / coverage, then we can
@@ -162,9 +126,6 @@
         bool            fReadsFragPosition;
         bool            fRequiresLocalCoordAttrib;
 
-        // Fragment shader color outputs
-        GrProgramDesc::PrimaryOutputType  fPrimaryOutputType : 8;
-        GrProgramDesc::SecondaryOutputType  fSecondaryOutputType : 8;
     };
 
 private:
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index 2b4b133..6255e4a 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -9,11 +9,12 @@
 
 #include "GrBlend.h"
 #include "GrDrawState.h"
+#include "GrDrawTargetCaps.h"
 #include "GrInvariantOutput.h"
 #include "GrProcessor.h"
 #include "GrTypes.h"
 #include "GrXferProcessor.h"
-#include "gl/GrGLProcessor.h"
+#include "gl/GrGLXferProcessor.h"
 #include "gl/builders/GrGLFragmentShaderBuilder.h"
 #include "gl/builders/GrGLProgramBuilder.h"
 
@@ -42,19 +43,45 @@
 
     virtual ~GrGLPorterDuffXferProcessor() {}
 
-    virtual void emitCode(GrGLFPBuilder* builder,
-                          const GrFragmentProcessor& fp,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TransformedCoordsArray& coords,
-                          const TextureSamplerArray& samplers) SK_OVERRIDE {
-        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
-        fsBuilder->codeAppendf("%s = %s;", outputColor, inputColor);
+    virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        const GrPorterDuffXferProcessor& xp = args.fXP.cast<GrPorterDuffXferProcessor>();
+        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        if (xp.hasSecondaryOutput()) {
+            switch(xp.secondaryOutputType()) {
+                case GrPorterDuffXferProcessor::kCoverage_SecondaryOutputType:
+                    fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, args.fInputCoverage);
+                    break;
+                case GrPorterDuffXferProcessor::kCoverageISA_SecondaryOutputType:
+                    fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;",
+                                           args.fOutputSecondary, args.fInputColor,
+                                           args.fInputCoverage);
+                    break;
+                case GrPorterDuffXferProcessor::kCoverageISC_SecondaryOutputType:
+                    fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;",
+                                           args.fOutputSecondary, args.fInputColor,
+                                           args.fInputCoverage);
+                    break;
+                default:
+                    SkFAIL("Unexpected Secondary Output");
+            }
+        }
+        
+        fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
+                               args.fInputCoverage);
+        if (GrPorterDuffXferProcessor::kCombineWithDst_PrimaryOutputType == xp.primaryOutputType()){
+            fsBuilder->codeAppendf("%s += (vec4(1.0) - %s) * %s;", args.fOutputPrimary,
+                                   args.fInputCoverage, fsBuilder->dstColor());
+        }
     }
 
-    virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {};
+    virtual void setData(const GrGLProgramDataManager&, const GrXferProcessor&) SK_OVERRIDE {};
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b) {};
+    static void GenKey(const GrProcessor& processor, const GrGLCaps& caps,
+                       GrProcessorKeyBuilder* b) {
+        const GrPorterDuffXferProcessor& xp = processor.cast<GrPorterDuffXferProcessor>();
+        b->add32(xp.primaryOutputType());
+        b->add32(xp.secondaryOutputType());
+    };
 
 private:
     typedef GrGLXferProcessor INHERITED;
@@ -64,7 +91,11 @@
 
 GrPorterDuffXferProcessor::GrPorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
                                                      GrColor constant)
-    : fSrcBlend(srcBlend), fDstBlend(dstBlend), fBlendConstant(constant) {
+    : fSrcBlend(srcBlend)
+    , fDstBlend(dstBlend)
+    , fBlendConstant(constant)
+    , fPrimaryOutputType(kModulate_PrimaryOutputType) 
+    , fSecondaryOutputType(kNone_SecondaryOutputType) {
     this->initClassID<GrPorterDuffXferProcessor>();
 }
 
@@ -76,7 +107,7 @@
     GrGLPorterDuffXferProcessor::GenKey(*this, caps, b);
 }
 
-GrGLFragmentProcessor* GrPorterDuffXferProcessor::createGLInstance() const {
+GrGLXferProcessor* GrPorterDuffXferProcessor::createGLInstance() const {
     return SkNEW_ARGS(GrGLPorterDuffXferProcessor, (*this));
 }
 
@@ -90,7 +121,58 @@
                                             bool isCoverageDrawing,
                                             bool colorWriteDisabled,
                                             bool doesStencilWrite,
-                                            GrColor* color, uint8_t* coverage) {
+                                            GrColor* color, uint8_t* coverage,
+                                            const GrDrawTargetCaps& caps) {
+    GrXferProcessor::OptFlags optFlags = this->internalGetOptimizations(colorPOI,
+                                                                        coveragePOI,
+                                                                        isCoverageDrawing,
+                                                                        colorWriteDisabled,
+                                                                        doesStencilWrite,
+                                                                        color,
+                                                                        coverage);
+
+    this->calcOutputTypes(optFlags, caps, isCoverageDrawing || coveragePOI.isSolidWhite(),
+                          colorPOI.readsDst() || coveragePOI.readsDst());
+    return optFlags;
+}
+
+void GrPorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags,
+                                                const GrDrawTargetCaps& caps,
+                                                bool hasSolidCoverage, bool readsDst) {
+    // If we do have coverage determine whether it matters.  Dual source blending is expensive so
+    // we don't do it if we are doing coverage drawing.  If we aren't then We always do dual source
+    // blending if we have any effective coverage stages OR the geometry processor doesn't emits
+    // solid coverage.
+    if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) {
+        if (caps.dualSourceBlendingSupport()) {
+            if (kZero_GrBlendCoeff == fDstBlend) {
+                // write the coverage value to second color
+                fSecondaryOutputType = kCoverage_SecondaryOutputType;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            } else if (kSA_GrBlendCoeff == fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                fSecondaryOutputType = kCoverageISA_SecondaryOutputType;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            } else if (kSC_GrBlendCoeff == fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                fSecondaryOutputType = kCoverageISC_SecondaryOutputType;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            }
+        } else if (readsDst &&
+                   kOne_GrBlendCoeff == fSrcBlend &&
+                   kZero_GrBlendCoeff == fDstBlend) {
+            fPrimaryOutputType = kCombineWithDst_PrimaryOutputType;
+        }
+    }
+}
+
+GrXferProcessor::OptFlags
+GrPorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                    const GrProcOptInfo& coveragePOI,
+                                                    bool isCoverageDrawing,
+                                                    bool colorWriteDisabled,
+                                                    bool doesStencilWrite,
+                                                    GrColor* color, uint8_t* coverage) {
     if (colorWriteDisabled) {
         fSrcBlend = kZero_GrBlendCoeff;
         fDstBlend = kOne_GrBlendCoeff;
@@ -192,6 +274,11 @@
 
     return GrXferProcessor::kNone_Opt;
 }
+
+bool GrPorterDuffXferProcessor::hasSecondaryOutput() const {
+    return kNone_SecondaryOutputType != fSecondaryOutputType;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst)
@@ -450,4 +537,22 @@
     return opaque;
 }
 
+GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);
+
+GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random,
+                                               GrContext*,
+                                               const GrDrawTargetCaps&,
+                                               GrTexture*[]) {
+    GrBlendCoeff src;
+    do {
+        src = GrBlendCoeff(random->nextRangeU(kFirstPublicGrBlendCoeff, kLastPublicGrBlendCoeff));
+    } while (GrBlendCoeffRefsSrc(src));
+
+    GrBlendCoeff dst;
+    do {
+        dst = GrBlendCoeff(random->nextRangeU(kFirstPublicGrBlendCoeff, kLastPublicGrBlendCoeff));
+    } while (GrBlendCoeffRefsDst(dst));
+
+    return GrPorterDuffXPFactory::Create(src, dst);
+}
 
diff --git a/src/gpu/gl/GrGLProcessor.h b/src/gpu/gl/GrGLProcessor.h
index 6dccd33..a4fad75 100644
--- a/src/gpu/gl/GrGLProcessor.h
+++ b/src/gpu/gl/GrGLProcessor.h
@@ -117,14 +117,4 @@
     typedef GrGLProcessor INHERITED;
 };
 
-class GrGLXferProcessor : public GrGLFragmentProcessor {
-public:
-    GrGLXferProcessor() {}
-    
-    virtual ~GrGLXferProcessor() {}
-
-private:
-    typedef GrGLFragmentProcessor INHERITED;
-};
-
 #endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 29c44ac..da687c1 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -12,11 +12,13 @@
 #include "GrCoordTransform.h"
 #include "GrGLGeometryProcessor.h"
 #include "GrGLProcessor.h"
+#include "GrGLXferProcessor.h"
 #include "GrGpuGL.h"
 #include "GrGLPathRendering.h"
 #include "GrGLShaderVar.h"
 #include "GrGLSL.h"
 #include "GrOptDrawState.h"
+#include "GrXferProcessor.h"
 #include "SkXfermode.h"
 
 #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
@@ -57,6 +59,7 @@
                          GrGLuint programID,
                          const UniformInfoArray& uniforms,
                          GrGLInstalledGeoProc* geometryProcessor,
+                         GrGLInstalledXferProc* xferProcessor,
                          GrGLInstalledFragProcs* fragmentProcessors)
     : fColor(GrColor_ILLEGAL)
     , fCoverage(0)
@@ -64,6 +67,7 @@
     , fBuiltinUniformHandles(builtinUniforms)
     , fProgramID(programID)
     , fGeometryProcessor(geometryProcessor)
+    , fXferProcessor(xferProcessor)
     , fFragmentProcessors(SkRef(fragmentProcessors))
     , fDesc(desc)
     , fGpu(gpu)
@@ -91,6 +95,9 @@
     if (fGeometryProcessor.get()) {
         this->initSamplers(fGeometryProcessor.get(), &texUnitIdx);
     }
+    if (fXferProcessor.get()) {
+        this->initSamplers(fXferProcessor.get(), &texUnitIdx);
+    }
     int numProcs = fFragmentProcessors->fProcs.count();
     for (int i = 0; i < numProcs; i++) {
         this->initSamplers(fFragmentProcessors->fProcs[i], &texUnitIdx);
@@ -162,6 +169,11 @@
         fGeometryProcessor->fGLProc->setData(fProgramDataManager, gp, bt);
         this->bindTextures(fGeometryProcessor, gp);
     }
+    if (fXferProcessor.get()) {
+        const GrXferProcessor& xp = *optState.getXferProcessor();
+        fXferProcessor->fGLProc->setData(fProgramDataManager, xp);
+        this->bindTextures(fXferProcessor, xp);
+    }
     this->setFragmentData(optState);
 
     // Some of GrGLProgram subclasses need to update state here
@@ -284,8 +296,10 @@
                                          const BuiltinUniformHandles& builtinUniforms,
                                          GrGLuint programID,
                                          const UniformInfoArray& uniforms,
+                                         GrGLInstalledXferProc* xferProcessor,
                                          GrGLInstalledFragProcs* fragmentProcessors)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, NULL, fragmentProcessors) {
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, NULL,
+                xferProcessor, fragmentProcessors) {
 }
 
 void GrGLNvprProgramBase::onSetMatrixAndRenderTargetHeight(const GrOptDrawState& optState) {
@@ -303,9 +317,11 @@
                                  const BuiltinUniformHandles& builtinUniforms,
                                  GrGLuint programID,
                                  const UniformInfoArray& uniforms,
+                                 GrGLInstalledXferProc* xferProcessor,
                                  GrGLInstalledFragProcs* fragmentProcessors,
                                  const SeparableVaryingInfoArray& separableVaryings)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, fragmentProcessors) {
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms,
+                xferProcessor, fragmentProcessors) {
     int count = separableVaryings.count();
     fVaryings.push_back_n(count);
     for (int i = 0; i < count; i++) {
@@ -353,9 +369,10 @@
                                              const BuiltinUniformHandles& builtinUniforms,
                                              GrGLuint programID,
                                              const UniformInfoArray& uniforms,
+                                             GrGLInstalledXferProc* xp,
                                              GrGLInstalledFragProcs* fps,
                                              int texCoordSetCnt)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, fps)
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, xp, fps)
     , fTexCoordSetCnt(texCoordSetCnt) {
 }
 
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 36bf860..ea8be85 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -141,6 +141,7 @@
                 GrGLuint programID,
                 const UniformInfoArray&,
                 GrGLInstalledGeoProc* geometryProcessor,
+                GrGLInstalledXferProc* xferProcessor,
                 GrGLInstalledFragProcs* fragmentProcessors);
 
     // Sets the texture units for samplers.
@@ -180,6 +181,7 @@
 
     // the installed effects
     SkAutoTDelete<GrGLInstalledGeoProc> fGeometryProcessor;
+    SkAutoTDelete<GrGLInstalledXferProc> fXferProcessor;
     SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
     GrProgramDesc fDesc;
@@ -205,6 +207,7 @@
                         const BuiltinUniformHandles&,
                         GrGLuint programID,
                         const UniformInfoArray&,
+                        GrGLInstalledXferProc* xferProcessor,
                         GrGLInstalledFragProcs* fragmentProcessors);
     virtual void onSetMatrixAndRenderTargetHeight(const GrOptDrawState&);
 
@@ -223,6 +226,7 @@
                     const BuiltinUniformHandles&,
                     GrGLuint programID,
                     const UniformInfoArray&,
+                    GrGLInstalledXferProc* xferProcessor,
                     GrGLInstalledFragProcs* fragmentProcessors,
                     const SeparableVaryingInfoArray& separableVaryings);
     virtual void didSetData(GrGpu::DrawType) SK_OVERRIDE;
@@ -252,7 +256,8 @@
                           const BuiltinUniformHandles&,
                           GrGLuint programID,
                           const UniformInfoArray&,
-                          GrGLInstalledFragProcs* fragmentProcessors,
+                          GrGLInstalledXferProc* xp,
+                          GrGLInstalledFragProcs* fps,
                           int texCoordSetCnt);
     virtual void didSetData(GrGpu::DrawType) SK_OVERRIDE;
     virtual void setTransformData(const GrPendingFragmentStage&,
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 068386c..2476c55 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -189,13 +189,21 @@
         const GrFragmentProcessor& fp = *fps.getProcessor();
         GrProcessorKeyBuilder b(&desc->fKey);
         fp.getGLProcessorKey(gpu->glCaps(), &b);
-        if (!get_meta_key(*fps.getProcessor(), gpu->glCaps(),
-                         gen_transform_key(fps, requiresLocalCoordAttrib), 0, &b)) {
+        if (!get_meta_key(fp, gpu->glCaps(),
+                          gen_transform_key(fps, requiresLocalCoordAttrib), 0, &b)) {
             desc->fKey.reset();
             return false;
         }
     }
 
+    const GrXferProcessor& xp = *optState.getXferProcessor();
+    GrProcessorKeyBuilder b(&desc->fKey);
+    xp.getGLProcessorKey(gpu->glCaps(), &b);
+    if (!get_meta_key(xp, gpu->glCaps(), 0, 0, &b)) {
+        desc->fKey.reset();
+        return false;
+    }
+
     // --------DO NOT MOVE HEADER ABOVE THIS LINE--------------------------------------------------
     // Because header is a pointer into the dynamic array, we can't push any new data into the key
     // below here.
@@ -260,9 +268,6 @@
         header->fFragPosKey = 0;
     }
 
-    header->fPrimaryOutputType = descInfo.fPrimaryOutputType;
-    header->fSecondaryOutputType = descInfo.fSecondaryOutputType;
-
     header->fColorEffectCnt = optState.numColorStages();
     header->fCoverageEffectCnt = optState.numCoverageStages();
     desc->finalize();
diff --git a/src/gpu/gl/GrGLXferProcessor.h b/src/gpu/gl/GrGLXferProcessor.h
new file mode 100644
index 0000000..5c92559
--- /dev/null
+++ b/src/gpu/gl/GrGLXferProcessor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLXferProcessor_DEFINED
+#define GrGLXferProcessor_DEFINED
+
+#include "GrGLProcessor.h"
+
+class GrGLXPBuilder;
+
+class GrGLXferProcessor {
+public:
+    GrGLXferProcessor() {}
+    virtual ~GrGLXferProcessor() {}
+
+    typedef GrGLProcessor::TextureSamplerArray TextureSamplerArray;
+    struct EmitArgs {
+        EmitArgs(GrGLXPBuilder* pb,
+                 const GrXferProcessor& xp,
+                 const char* inputColor,
+                 const char* inputCoverage,
+                 const char* outputPrimary,
+                 const char* outputSecondary,
+                 const TextureSamplerArray& samplers)
+            : fPB(pb)
+            , fXP(xp)
+            , fInputColor(inputColor)
+            , fInputCoverage(inputCoverage)
+            , fOutputPrimary(outputPrimary)
+            , fOutputSecondary(outputSecondary)
+            , fSamplers(samplers) {}
+
+        GrGLXPBuilder* fPB;
+        const GrXferProcessor& fXP;
+        const char* fInputColor;
+        const char* fInputCoverage;
+        const char* fOutputPrimary;
+        const char* fOutputSecondary;
+        const TextureSamplerArray& fSamplers;
+    };
+    /**
+     * This is similar to emitCode() in the base class, except it takes a full shader builder.
+     * This allows the effect subclass to emit vertex code.
+     */
+    virtual void emitCode(const EmitArgs&) = 0;
+
+    /** A GrGLXferProcessor instance can be reused with any GrGLXferProcessor that produces
+        the same stage key; this function reads data from a GrGLXferProcessor and uploads any
+        uniform variables required  by the shaders created in emitCode(). The GrXferProcessor
+        parameter is guaranteed to be of the same type that created this GrGLXferProcessor and
+        to have an identical processor key as the one that created this GrGLXferProcessor.  */
+    virtual void setData(const GrGLProgramDataManager&,
+                         const GrXferProcessor&) = 0;
+private:
+    typedef GrGLProcessor INHERITED;
+};
+#endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index c899eba..7db6abd 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -1919,10 +1919,14 @@
     }
 }
 
-void GrGpuGL::flushBlend(const GrOptDrawState& optState, bool isLines,
-                         GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+void GrGpuGL::flushBlend(const GrOptDrawState& optState) {
     // Any optimization to disable blending should have already been applied and
     // tweaked the coeffs to (1, 0).
+    
+    GrXferProcessor::BlendInfo blendInfo;
+    optState.getXferProcessor()->getBlendInfo(&blendInfo);
+    GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
+    GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
     bool blendOff = kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;
     if (blendOff) {
         if (kNo_TriState != fHWBlendState.fEnabled) {
@@ -1941,7 +1945,7 @@
             fHWBlendState.fSrcCoeff = srcCoeff;
             fHWBlendState.fDstCoeff = dstCoeff;
         }
-        GrColor blendConst = optState.getBlendConstant();
+        GrColor blendConst = blendInfo.fBlendConstant;
         if ((BlendCoeffReferencesConstant(srcCoeff) ||
              BlendCoeffReferencesConstant(dstCoeff)) &&
             (!fHWBlendState.fConstColorValid ||
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 8a60988..76995b9 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -172,11 +172,7 @@
                        size_t* indexOffsetInBytes);
 
     // Subclasses should call this to flush the blend state.
-    // The params should be the final coefficients to apply
-    // (after any blending optimizations or dual source blending considerations
-    // have been accounted for).
-    void flushBlend(const GrOptDrawState& optState, bool isLines,
-                    GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
+    void flushBlend(const GrOptDrawState& optState);
 
     bool hasExtension(const char* ext) const { return fGLContext.hasExtension(ext); }
 
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index a808aa8..19ddc76 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -213,9 +213,6 @@
     } else {
         this->flushMiscFixedFunctionState(optState);
 
-        GrBlendCoeff srcCoeff = optState.getSrcBlendCoeff();
-        GrBlendCoeff dstCoeff = optState.getDstBlendCoeff();
-
         fCurrentProgram.reset(fProgramCache->getProgram(optState));
         if (NULL == fCurrentProgram.get()) {
             SkDEBUGFAIL("Failed to create program!");
@@ -230,7 +227,7 @@
             fHWProgramID = programID;
         }
 
-        this->flushBlend(optState, kDrawLines_DrawType == optState.drawType(), srcCoeff, dstCoeff);
+        this->flushBlend(optState);
 
         fCurrentProgram->setData(optState);
     }
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
index 49be12d..b6daca7 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -256,55 +256,6 @@
     return dual_source_output_name();
 }
 
-void GrGLFragmentShaderBuilder::enableSecondaryOutput(const GrGLSLExpr4& inputColor,
-                                                      const GrGLSLExpr4& inputCoverage) {
-    this->enableSecondaryOutput();
-    const char* secondaryOutputName = this->getSecondaryColorOutputName();
-    GrGLSLExpr4 coeff(1);
-    switch (fProgramBuilder->header().fSecondaryOutputType) {
-        case GrProgramDesc::kCoverage_SecondaryOutputType:
-            break;
-        case GrProgramDesc::kCoverageISA_SecondaryOutputType:
-            // Get (1-A) into coeff
-            coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inputColor.a());
-            break;
-        case GrProgramDesc::kCoverageISC_SecondaryOutputType:
-            // Get (1-RGBA) into coeff
-            coeff = GrGLSLExpr4(1) - inputColor;
-            break;
-        default:
-            SkFAIL("Unexpected Secondary Output");
-    }
-    // Get coeff * coverage into modulate and then write that to the dual source output.
-    this->codeAppendf("\t%s = %s;\n", secondaryOutputName, (coeff * inputCoverage).c_str());
-}
-
-void GrGLFragmentShaderBuilder::combineColorAndCoverage(const GrGLSLExpr4& inputColor,
-                                                        const GrGLSLExpr4& inputCoverage) {
-    GrGLSLExpr4 fragColor = inputColor * inputCoverage;
-    switch (fProgramBuilder->header().fPrimaryOutputType) {
-        case GrProgramDesc::kModulate_PrimaryOutputType:
-            break;
-        case GrProgramDesc::kCombineWithDst_PrimaryOutputType:
-            {
-                // Tack on "+(1-coverage)dst onto the frag color.
-                GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inputCoverage;
-                GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(this->dstColor());
-                fragColor = fragColor + dstContribution;
-            }
-            break;
-        default:
-            SkFAIL("Unknown Primary Output");
-    }
-
-    // On any post 1.10 GLSL supporting GPU, we declare custom output
-    if (k110_GrGLSLGeneration != fProgramBuilder->gpu()->glslGeneration()) {
-        this->enableCustomOutput();
-    }
-
-    this->codeAppendf("\t%s = %s;\n", this->getPrimaryColorOutputName(), fragColor.c_str());
-}
-
 bool GrGLFragmentShaderBuilder::compileAndAttachShaders(GrGLuint programId,
                                                         SkTDArray<GrGLuint>* shaderIds) const {
     GrGpuGL* gpu = fProgramBuilder->gpu();
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
index 03f0e7f..456ec4f 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
@@ -100,8 +100,6 @@
     void enableSecondaryOutput();
     const char* getPrimaryColorOutputName() const;
     const char* getSecondaryColorOutputName() const;
-    void enableSecondaryOutput(const GrGLSLExpr4& inputColor, const GrGLSLExpr4& inputCoverage);
-    void combineColorAndCoverage(const GrGLSLExpr4& inputColor, const GrGLSLExpr4& inputCoverage);
     bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) const;
     void bindFragmentShaderLocations(GrGLuint programID);
 
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
index b251593..b90bab2 100644
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
@@ -45,5 +45,6 @@
 
 GrGLProgram* GrGLLegacyNvprProgramBuilder::createProgram(GrGLuint programID) {
     return SkNEW_ARGS(GrGLLegacyNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                              fFragmentProcessors.get(),  fTexCoordSetCnt));
+                                              fXferProcessor, fFragmentProcessors.get(),
+                                              fTexCoordSetCnt));
 }
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
index f5a5586..ba19275 100644
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
@@ -72,5 +72,6 @@
     // building
     this->resolveSeparableVaryings(programID);
     return SkNEW_ARGS(GrGLNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                        fFragmentProcessors.get(), fSeparableVaryingInfos));
+                                        fXferProcessor, fFragmentProcessors.get(),
+                                        fSeparableVaryingInfos));
 }
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 7929801..daa0b6b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -9,6 +9,7 @@
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLSLPrettyPrint.h"
 #include "gl/GrGLUniformHandle.h"
+#include "../GrGLXferProcessor.h"
 #include "../GrGpuGL.h"
 #include "GrCoordTransform.h"
 #include "GrGLLegacyNvprProgramBuilder.h"
@@ -55,20 +56,12 @@
 
     pb->emitAndInstallProcs(&inputColor, &inputCoverageVec4);
 
-    // write the secondary color output if necessary
-    if (GrProgramDesc::kNone_SecondaryOutputType != header.fSecondaryOutputType) {
-        pb->fFS.enableSecondaryOutput(inputColor, inputCoverageVec4);
-    }
-
-    pb->fFS.combineColorAndCoverage(inputColor, inputCoverageVec4);
-
     return pb->finalize();
 }
 
-GrGLProgramBuilder*
-GrGLProgramBuilder::CreateProgramBuilder(const GrOptDrawState& optState,
-                                         bool hasGeometryProcessor,
-                                         GrGpuGL* gpu) {
+GrGLProgramBuilder* GrGLProgramBuilder::CreateProgramBuilder(const GrOptDrawState& optState,
+                                                             bool hasGeometryProcessor,
+                                                             GrGpuGL* gpu) {
     const GrProgramDesc& desc = optState.programDesc();
     if (GrGLProgramDescBuilder::GetHeader(desc).fUseNvpr) {
         SkASSERT(gpu->glCaps().pathRenderingSupport());
@@ -95,6 +88,7 @@
     , fOutOfStage(true)
     , fStageIndex(-1)
     , fGeometryProcessor(NULL)
+    , fXferProcessor(NULL)
     , fOptState(optState)
     , fDesc(optState.programDesc())
     , fGpu(gpu)
@@ -260,6 +254,8 @@
     if (fOptState.hasGeometryProcessor()) {
         fVS.transformToNormalizedDeviceSpace();
     }
+
+    this->emitAndInstallXferProc(*fOptState.getXferProcessor(), *inputColor, *inputCoverage);
 }
 
 void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset,
@@ -368,10 +364,55 @@
     verify(gp);
 }
 
+void GrGLProgramBuilder::emitAndInstallXferProc(const GrXferProcessor& xp,
+                                                const GrGLSLExpr4& colorIn,
+                                                const GrGLSLExpr4& coverageIn) {
+    // Program builders have a bit of state we need to clear with each effect
+    AutoStageAdvance adv(this);
+
+    SkASSERT(!fXferProcessor);
+    fXferProcessor = SkNEW(GrGLInstalledXferProc);
+
+    fXferProcessor->fGLProc.reset(xp.createGLInstance());
+
+    // Enable dual source secondary output if we have one
+    if (xp.hasSecondaryOutput()) {
+        fFS.enableSecondaryOutput();
+    }
+
+    // On any post 1.10 GLSL supporting GPU, we declare custom output
+    if (k110_GrGLSLGeneration != fFS.fProgramBuilder->gpu()->glslGeneration()) {
+        fFS.enableCustomOutput();
+    }
+
+    SkString openBrace;
+    openBrace.printf("{ // Xfer Processor: %s\n", xp.name());
+    fFS.codeAppend(openBrace.c_str());
+
+    SkSTArray<4, GrGLProcessor::TextureSampler> samplers(xp.numTextures());
+    this->emitSamplers(xp, &samplers, fXferProcessor);
+
+    GrGLXferProcessor::EmitArgs args(this, xp, colorIn.c_str(), coverageIn.c_str(),
+                                     fFS.getPrimaryColorOutputName(),
+                                     fFS.getSecondaryColorOutputName(), samplers);
+    fXferProcessor->fGLProc->emitCode(args);
+
+    // We have to check that effects and the code they emit are consistent, ie if an effect
+    // asks for dst color, then the emit code needs to follow suit
+    verify(xp);
+    fFS.codeAppend("}");
+}
+
 void GrGLProgramBuilder::verify(const GrGeometryProcessor& gp) {
     SkASSERT(fFS.hasReadFragmentPosition() == gp.willReadFragmentPosition());
 }
 
+void GrGLProgramBuilder::verify(const GrXferProcessor& xp) {
+    // TODO: Once will readDst is only xp enable this assert and remove it from the
+    // FragmentProcessor verify()
+    //SkASSERT(fFS.hasReadDstColor() == xp.willReadDstColor());
+}
+
 void GrGLProgramBuilder::verify(const GrFragmentProcessor& fp) {
     SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition());
     SkASSERT(fFS.hasReadDstColor() == fp.willReadDstColor());
@@ -543,7 +584,7 @@
 
 GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) {
     return SkNEW_ARGS(GrGLProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                    fGeometryProcessor, fFragmentProcessors.get()));
+                                    fGeometryProcessor, fXferProcessor, fFragmentProcessors.get()));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 1798e60..69eed27 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -14,6 +14,7 @@
 #include "../GrGLProgramDataManager.h"
 #include "../GrGLUniformHandle.h"
 #include "../GrGLGeometryProcessor.h"
+#include "../GrGLXferProcessor.h"
 #include "../../GrOptDrawState.h"
 #include "../../GrPendingFragmentStage.h"
 
@@ -111,6 +112,7 @@
 
     friend class GrGLVertexBuilder;
     friend class GrGLGeometryBuilder;
+    friend class GrGLXferBuilder;
     friend class GrGLFragmentShaderBuilder;
 };
 
@@ -170,8 +172,18 @@
      */
 };
 
+/* a specializations for XPs. Lets the user add uniforms and FS code */
+class GrGLXPBuilder : public virtual GrGLUniformBuilder {
+public:
+    virtual GrGLFPFragmentBuilder* getFragmentShaderBuilder() = 0;
+
+    /*
+     * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE
+     */
+};
 struct GrGLInstalledProc;
 struct GrGLInstalledGeoProc;
+struct GrGLInstalledXferProc;
 struct GrGLInstalledFragProc;
 struct GrGLInstalledFragProcs;
 
@@ -183,7 +195,8 @@
  * respective builders
 */
 class GrGLProgramBuilder : public GrGLGPBuilder,
-                           public GrGLFPBuilder {
+                           public GrGLFPBuilder,
+                           public GrGLXPBuilder {
 public:
     /** Generates a shader program.
      *
@@ -283,8 +296,12 @@
     void emitAndInstallProc(const GrGeometryProcessor&,
                             const char* outColor,
                             const char* outCoverage);
+    void emitAndInstallXferProc(const GrXferProcessor&,
+                                const GrGLSLExpr4& colorIn,
+                                const GrGLSLExpr4& coverageIn);
 
     void verify(const GrGeometryProcessor&);
+    void verify(const GrXferProcessor&);
     void verify(const GrFragmentProcessor&);
     void emitSamplers(const GrProcessor&,
                       GrGLProcessor::TextureSamplerArray* outSamplers,
@@ -358,6 +375,7 @@
     int fStageIndex;
 
     GrGLInstalledGeoProc* fGeometryProcessor;
+    GrGLInstalledXferProc* fXferProcessor;
     SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
     const GrOptDrawState& fOptState;
@@ -391,6 +409,10 @@
     SkAutoTDelete<GrGLGeometryProcessor> fGLProc;
 };
 
+struct GrGLInstalledXferProc : public GrGLInstalledProc {
+    SkAutoTDelete<GrGLXferProcessor> fGLProc;
+};
+
 struct GrGLInstalledFragProc : public GrGLInstalledProc {
     GrGLInstalledFragProc() : fGLProc(NULL) {}
     class ShaderVarHandle {