Move the GL shader compilation step into GrGLShaderBuilder

Moves the compilation step and a few other blocks of code from
GrGLProgram to GrGLShaderBuilder. This way GrGLProgram doesn't have to
know whether or not there is a vertex shader.

R=bsalomon@google.com

Author: cdalton@nvidia.com

Review URL: https://codereview.chromium.org/23533066

git-svn-id: http://skia.googlecode.com/svn/trunk@11523 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index ac9794d..f10e895 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -14,28 +14,13 @@
 #include "GrGpuGL.h"
 #include "GrGLShaderVar.h"
 #include "GrGLSL.h"
-#include "SkTrace.h"
 #include "SkXfermode.h"
 
-#include "SkRTConf.h"
-
 SK_DEFINE_INST_COUNT(GrGLProgram)
 
 #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X)
 
-SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false,
-                "Print the source code for all shaders generated.");
-
-#define COL_ATTR_NAME "aColor"
-#define COV_ATTR_NAME "aCoverage"
-#define EDGE_ATTR_NAME "aEdge"
-
-namespace {
-inline const char* declared_color_output_name() { return "fsColorOut"; }
-inline const char* dual_source_output_name() { return "dualSourceOut"; }
-}
-
 GrGLProgram* GrGLProgram::Create(GrGpuGL* gpu,
                                  const GrGLProgramDesc& desc,
                                  const GrEffectStage* colorStages[],
@@ -55,9 +40,6 @@
 : fGpu(gpu)
 , fUniformManager(gpu) {
     fDesc = desc;
-    fVShaderID = 0;
-    fGShaderID = 0;
-    fFShaderID = 0;
     fProgramID = 0;
 
     fDstCopyTexUnit = -1;
@@ -72,24 +54,12 @@
 }
 
 GrGLProgram::~GrGLProgram() {
-    if (fVShaderID) {
-        GL_CALL(DeleteShader(fVShaderID));
-    }
-    if (fGShaderID) {
-        GL_CALL(DeleteShader(fGShaderID));
-    }
-    if (fFShaderID) {
-        GL_CALL(DeleteShader(fFShaderID));
-    }
     if (fProgramID) {
         GL_CALL(DeleteProgram(fProgramID));
     }
 }
 
 void GrGLProgram::abandon() {
-    fVShaderID = 0;
-    fGShaderID = 0;
-    fFShaderID = 0;
     fProgramID = 0;
 }
 
@@ -219,167 +189,7 @@
 }
 }
 
-GrSLConstantVec GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
-    switch (fDesc.getHeader().fColorInput) {
-        case GrGLProgramDesc::kAttribute_ColorInput: {
-            GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
-            SkASSERT(NULL != vertexBuilder);
-            vertexBuilder->addAttribute(kVec4f_GrSLType, COL_ATTR_NAME);
-            const char *vsName, *fsName;
-            vertexBuilder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
-            vertexBuilder->vsCodeAppendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
-            *inColor = fsName;
-            return kNone_GrSLConstantVec;
-        }
-        case GrGLProgramDesc::kUniform_ColorInput: {
-            const char* name;
-            fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                                            kVec4f_GrSLType, "Color", &name);
-            *inColor = name;
-            return kNone_GrSLConstantVec;
-        }
-        case GrGLProgramDesc::kTransBlack_ColorInput:
-            inColor->reset();
-            return kZeros_GrSLConstantVec;
-        case GrGLProgramDesc::kSolidWhite_ColorInput:
-            inColor->reset();
-            return kOnes_GrSLConstantVec;
-        default:
-            GrCrash("Unknown color type.");
-            return kNone_GrSLConstantVec;
-    }
-}
-
-GrSLConstantVec GrGLProgram::genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage) {
-    switch (fDesc.getHeader().fCoverageInput) {
-        case GrGLProgramDesc::kAttribute_ColorInput: {
-            GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
-            SkASSERT(NULL != vertexBuilder);
-            vertexBuilder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME);
-            const char *vsName, *fsName;
-            vertexBuilder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
-            vertexBuilder->vsCodeAppendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
-            *inCoverage = fsName;
-            return kNone_GrSLConstantVec;
-        }
-        case GrGLProgramDesc::kUniform_ColorInput: {
-            const char* name;
-            fUniformHandles.fCoverageUni =
-                builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                    kVec4f_GrSLType, "Coverage", &name);
-            *inCoverage = name;
-            return kNone_GrSLConstantVec;
-        }
-        case GrGLProgramDesc::kTransBlack_ColorInput:
-            inCoverage->reset();
-            return kZeros_GrSLConstantVec;
-        case GrGLProgramDesc::kSolidWhite_ColorInput:
-            inCoverage->reset();
-            return kOnes_GrSLConstantVec;
-        default:
-            GrCrash("Unknown color type.");
-            return kNone_GrSLConstantVec;
-    }
-}
-
-void GrGLProgram::genGeometryShader(GrGLShaderBuilder::VertexBuilder* vertexBuilder) const {
-#if GR_GL_EXPERIMENTAL_GS
-    // TODO: The builder should add all this glue code.
-    if (fDesc.getHeader().fExperimentalGS) {
-        SkASSERT(fGpu->glslGeneration() >= k150_GrGLSLGeneration);
-        vertexBuilder->fGSHeader.append("layout(triangles) in;\n"
-                                        "layout(triangle_strip, max_vertices = 6) out;\n");
-        vertexBuilder->gsCodeAppend("\tfor (int i = 0; i < 3; ++i) {\n"
-                                    "\t\tgl_Position = gl_in[i].gl_Position;\n");
-        if (fDesc.getHeader().fEmitsPointSize) {
-            vertexBuilder->gsCodeAppend("\t\tgl_PointSize = 1.0;\n");
-        }
-        SkASSERT(vertexBuilder->fGSInputs.count() == vertexBuilder->fGSOutputs.count());
-        int count = vertexBuilder->fGSInputs.count();
-        for (int i = 0; i < count; ++i) {
-            vertexBuilder->gsCodeAppendf("\t\t%s = %s[i];\n",
-                                         vertexBuilder->fGSOutputs[i].getName().c_str(),
-                                         vertexBuilder->fGSInputs[i].getName().c_str());
-        }
-        vertexBuilder->gsCodeAppend("\t\tEmitVertex();\n"
-                                    "\t}\n"
-                                    "\tEndPrimitive();\n");
-    }
-#endif
-}
-
-const char* GrGLProgram::adjustInColor(const SkString& inColor) const {
-    if (inColor.size()) {
-          return inColor.c_str();
-    } else {
-        if (GrGLProgramDesc::kSolidWhite_ColorInput == fDesc.getHeader().fColorInput) {
-            return GrGLSLOnesVecf(4);
-        } else {
-            return GrGLSLZerosVecf(4);
-        }
-    }
-}
-
 namespace {
-// prints a shader using params similar to glShaderSource
-void print_shader(GrGLint stringCnt,
-                  const GrGLchar** strings,
-                  GrGLint* stringLengths) {
-    for (int i = 0; i < stringCnt; ++i) {
-        if (NULL == stringLengths || stringLengths[i] < 0) {
-            GrPrintf(strings[i]);
-        } else {
-            GrPrintf("%.*s", stringLengths[i], strings[i]);
-        }
-    }
-}
-
-// Compiles a GL shader, returns shader ID or 0 if failed params have same meaning as glShaderSource
-GrGLuint compile_shader(const GrGLInterface* gli,
-                        GrGLenum type,
-                        int stringCnt,
-                        const char** strings,
-                        int* stringLengths) {
-    SK_TRACE_EVENT1("GrGLProgram::CompileShader",
-                    "stringCount", SkStringPrintf("%i", stringCnt).c_str());
-
-    GrGLuint shader;
-    GR_GL_CALL_RET(gli, shader, CreateShader(type));
-    if (0 == shader) {
-        return 0;
-    }
-
-    GrGLint compiled = GR_GL_INIT_ZERO;
-    GR_GL_CALL(gli, ShaderSource(shader, stringCnt, strings, stringLengths));
-    GR_GL_CALL(gli, CompileShader(shader));
-    GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
-
-    if (!compiled) {
-        GrGLint infoLen = GR_GL_INIT_ZERO;
-        GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
-        SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
-        if (infoLen > 0) {
-            // retrieve length even though we don't need it to workaround bug in chrome cmd buffer
-            // param validation.
-            GrGLsizei length = GR_GL_INIT_ZERO;
-            GR_GL_CALL(gli, GetShaderInfoLog(shader, infoLen+1,
-                                             &length, (char*)log.get()));
-            print_shader(stringCnt, strings, stringLengths);
-            GrPrintf("\n%s", log.get());
-        }
-        SkDEBUGFAIL("Shader compilation failed!");
-        GR_GL_CALL(gli, DeleteShader(shader));
-        return 0;
-    }
-    return shader;
-}
-
-// helper version of above for when shader is already flattened into a single SkString
-GrGLuint compile_shader(const GrGLInterface* gli, GrGLenum type, const SkString& shader) {
-    const GrGLchar* str = shader.c_str();
-    int length = shader.size();
-    return compile_shader(gli, type, 1, &str, &length);
-}
 
 void expand_known_value4f(SkString* string, GrSLConstantVec vec) {
     SkASSERT(string->isEmpty() == (vec != kNone_GrSLConstantVec));
@@ -397,50 +207,6 @@
 
 }
 
-// compiles all the shaders from builder and stores the shader IDs
-bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
-
-    SkASSERT(!fVShaderID);
-    SkASSERT(!fGShaderID);
-    SkASSERT(!fFShaderID);
-
-    SkString shader;
-    if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
-        vertexBuilder->vsGetShader(&shader);
-        if (c_PrintShaders) {
-            GrPrintf(shader.c_str());
-            GrPrintf("\n");
-        }
-        if (!(fVShaderID = compile_shader(fGpu->glInterface(), GR_GL_VERTEX_SHADER, shader))) {
-            return false;
-        }
-
-#if GR_GL_EXPERIMENTAL_GS
-        if (fDesc.getHeader().fExperimentalGS) {
-            vertexBuilder->gsGetShader(&shader);
-            if (c_PrintShaders) {
-                GrPrintf(shader.c_str());
-                GrPrintf("\n");
-            }
-            if (!(fGShaderID = compile_shader(fGpu->glInterface(), GR_GL_GEOMETRY_SHADER, shader))) {
-                return false;
-            }
-        }
-#endif
-    }
-
-    builder.fsGetShader(&shader);
-    if (c_PrintShaders) {
-        GrPrintf(shader.c_str());
-        GrPrintf("\n");
-    }
-    if (!(fFShaderID = compile_shader(fGpu->glInterface(), GR_GL_FRAGMENT_SHADER, shader))) {
-        return false;
-    }
-
-    return true;
-}
-
 bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
                              const GrEffectStage* coverageStages[]) {
     SkASSERT(0 == fProgramID);
@@ -449,42 +215,14 @@
 
     bool needsVertexShader = true;
 
-    GrGLShaderBuilder builder(fGpu->ctxInfo(), fUniformManager, fDesc, needsVertexShader);
-
+    GrGLShaderBuilder builder(fGpu, fUniformManager, fDesc, needsVertexShader);
     if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
-        const char* viewMName;
-        fUniformHandles.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_Visibility,
-                                                            kMat33f_GrSLType, "ViewM", &viewMName);
-
-        vertexBuilder->vsCodeAppendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
-                                     "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n",
-                                     viewMName, vertexBuilder->positionAttribute().c_str());
-
-        // we output point size in the GS if present
-        if (header.fEmitsPointSize
-#if GR_GL_EXPERIMENTAL_GS
-            && !header.fExperimentalGS
-#endif
-            ) {
-            vertexBuilder->vsCodeAppend("\tgl_PointSize = 1.0;\n");
-        }
-    }
-
-    // the dual source output has no canonical var name, have to
-    // declare an output, which is incompatible with gl_FragColor/gl_FragData.
-    bool dualSourceOutputWritten = false;
-
-    GrGLShaderVar colorOutput;
-    bool isColorDeclared = GrGLSLSetupFSColorOuput(fGpu->glslGeneration(),
-                                                   declared_color_output_name(),
-                                                   &colorOutput);
-    if (isColorDeclared) {
-        builder.fsOutputAppend(colorOutput);
+        fUniformHandles.fViewMatrixUni = vertexBuilder->getViewMatrixUniform();
     }
 
     // incoming color to current stage being processed.
-    SkString inColor;
-    GrSLConstantVec knownColorValue = this->genInputColor(&builder, &inColor);
+    SkString inColor = builder.getInputColor();
+    GrSLConstantVec knownColorValue = builder.getKnownColorValue();
 
     // Get the coeffs for the Mode-based color filter, determine if color is needed.
     SkXfermode::Coeff colorCoeff;
@@ -544,8 +282,8 @@
 
     ///////////////////////////////////////////////////////////////////////////
     // compute the partial coverage
-    SkString inCoverage;
-    GrSLConstantVec knownCoverageValue = this->genInputCoverage(&builder, &inCoverage);
+    SkString inCoverage = builder.getInputCoverage();
+    GrSLConstantVec knownCoverageValue = builder.getKnownCoverageValue();
 
     for (int e = 0; e < fDesc.numCoverageEffects(); ++e) {
         effectUniformArrays[e] = &fCoverageEffects[e].fSamplerUnis;
@@ -576,9 +314,8 @@
     GrGLProgramDesc::CoverageOutput coverageOutput =
         static_cast<GrGLProgramDesc::CoverageOutput>(header.fCoverageOutput);
     if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(coverageOutput)) {
-        builder.fsOutputAppend().set(kVec4f_GrSLType,
-                                     GrGLShaderVar::kOut_TypeModifier,
-                                     dual_source_output_name());
+        const char* secondaryOutputName = builder.enableSecondaryOutput();
+
         // default coeff to ones for kCoverage_DualSrcOutput
         SkString coeff;
         GrSLConstantVec knownCoeffValue = kOnes_GrSLConstantVec;
@@ -613,8 +350,7 @@
                            knownCoeffValue,
                            knownCoverageValue,
                            false);
-        builder.fsCodeAppendf("\t%s = %s;\n", dual_source_output_name(), modulate.c_str());
-        dualSourceOutputWritten = true;
+        builder.fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, modulate.c_str());
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -655,33 +391,17 @@
     } else {
         expand_known_value4f(&fragColor, knownFragColorValue);
     }
-    builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), fragColor.c_str());
+    builder.fsCodeAppendf("\t%s = %s;\n", builder.getColorOutputName(), fragColor.c_str());
 
-    ///////////////////////////////////////////////////////////////////////////
-    // insert GS
-#ifdef SK_DEBUG
-    if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
-        this->genGeometryShader(vertexBuilder);
-    }
-#endif
-
-    ///////////////////////////////////////////////////////////////////////////
-    // compile and setup attribs and unis
-
-    if (!this->compileShaders(builder)) {
+    if (!builder.finish(&fProgramID)) {
         return false;
     }
 
-    if (!this->bindOutputsAttribsAndLinkProgram(builder,
-                                                isColorDeclared,
-                                                dualSourceOutputWritten)) {
-        return false;
-    }
-
-    builder.finished(fProgramID);
     fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
     fUniformHandles.fDstCopyTopLeftUni = builder.getDstCopyTopLeftUniform();
     fUniformHandles.fDstCopyScaleUni = builder.getDstCopyScaleUniform();
+    fUniformHandles.fColorUni = builder.getColorUniform();
+    fUniformHandles.fCoverageUni = builder.getCoverageUniform();
     fUniformHandles.fDstCopySamplerUni = builder.getDstCopySamplerUniform();
     // This must be called after we set fDstCopySamplerUni above.
     this->initSamplerUniforms();
@@ -689,82 +409,6 @@
     return true;
 }
 
-bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
-                                                   bool bindColorOut,
-                                                   bool bindDualSrcOut) {
-    GL_CALL_RET(fProgramID, CreateProgram());
-    if (!fProgramID) {
-        return false;
-    }
-
-    if (fVShaderID) {
-        GL_CALL(AttachShader(fProgramID, fVShaderID));
-    }
-    if (fGShaderID) {
-        GL_CALL(AttachShader(fProgramID, fGShaderID));
-    }
-    GL_CALL(AttachShader(fProgramID, fFShaderID));
-
-    if (bindColorOut) {
-        GL_CALL(BindFragDataLocation(fProgramID, 0, declared_color_output_name()));
-    }
-    if (bindDualSrcOut) {
-        GL_CALL(BindFragDataLocationIndexed(fProgramID, 0, 1, dual_source_output_name()));
-    }
-
-    const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
-
-    // Bind the attrib locations to same values for all shaders
-    if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
-        GL_CALL(BindAttribLocation(fProgramID,
-                                   header.fPositionAttributeIndex,
-                                   vertexBuilder->positionAttribute().c_str()));
-        if (-1 != header.fLocalCoordAttributeIndex) {
-            GL_CALL(BindAttribLocation(fProgramID,
-                                       header.fLocalCoordAttributeIndex,
-                                       vertexBuilder->localCoordsAttribute().c_str()));
-        }
-        if (-1 != header.fColorAttributeIndex) {
-            GL_CALL(BindAttribLocation(fProgramID, header.fColorAttributeIndex, COL_ATTR_NAME));
-        }
-        if (-1 != header.fCoverageAttributeIndex) {
-            GL_CALL(BindAttribLocation(fProgramID, header.fCoverageAttributeIndex, COV_ATTR_NAME));
-        }
-
-        const GrGLShaderBuilder::VertexBuilder::AttributePair* attribEnd = vertexBuilder->getEffectAttributes().end();
-        for (const GrGLShaderBuilder::VertexBuilder::AttributePair* attrib = vertexBuilder->getEffectAttributes().begin();
-             attrib != attribEnd;
-             ++attrib) {
-             GL_CALL(BindAttribLocation(fProgramID, attrib->fIndex, attrib->fName.c_str()));
-        }
-    }
-
-    GL_CALL(LinkProgram(fProgramID));
-
-    GrGLint linked = GR_GL_INIT_ZERO;
-    GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked));
-    if (!linked) {
-        GrGLint infoLen = GR_GL_INIT_ZERO;
-        GL_CALL(GetProgramiv(fProgramID, GR_GL_INFO_LOG_LENGTH, &infoLen));
-        SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
-        if (infoLen > 0) {
-            // retrieve length even though we don't need it to workaround
-            // bug in chrome cmd buffer param validation.
-            GrGLsizei length = GR_GL_INIT_ZERO;
-            GL_CALL(GetProgramInfoLog(fProgramID,
-                                      infoLen+1,
-                                      &length,
-                                      (char*)log.get()));
-            GrPrintf((char*)log.get());
-        }
-        SkDEBUGFAIL("Error linking program");
-        GL_CALL(DeleteProgram(fProgramID));
-        fProgramID = 0;
-        return false;
-    }
-    return true;
-}
-
 void GrGLProgram::initSamplerUniforms() {
     GL_CALL(UseProgram(fProgramID));
     GrGLint texUnitIdx = 0;
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 09f7668..0304acf 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -172,25 +172,10 @@
      */
     bool genProgram(const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]);
 
-    GrSLConstantVec genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
-
-    GrSLConstantVec genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage);
-
-    void genGeometryShader(GrGLShaderBuilder::VertexBuilder* vertexBuilder) const;
-
-    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
-    bool bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
-                                          bool bindColorOut,
-                                          bool bindDualSrcOut);
-
     // Sets the texture units for samplers
     void initSamplerUniforms();
     void initEffectSamplerUniforms(EffectAndSamplers* effect, int* texUnitIdx);
 
-    bool compileShaders(const GrGLShaderBuilder& builder);
-
-    const char* adjustInColor(const SkString& inColor) const;
-
     // Helper for setData().
     void setEffectData(const GrEffectStage& stage, const EffectAndSamplers& effect);
 
@@ -205,10 +190,7 @@
     // Helper for setData() that sets the view matrix and loads the render target height uniform
     void setMatrixAndRenderTargetHeight(const GrDrawState&);
 
-    // GL IDs
-    GrGLuint                    fVShaderID;
-    GrGLuint                    fGShaderID;
-    GrGLuint                    fFShaderID;
+    // GL program ID
     GrGLuint                    fProgramID;
 
     // these reflect the current values of uniforms (GL uniform values travel with program)
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
index 2959682..f48f038 100644
--- a/src/gpu/gl/GrGLSL.cpp
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -63,14 +63,6 @@
     }
 }
 
-bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen, const char* nameIfDeclared, GrGLShaderVar* var) {
-    bool declaredOutput = k110_GrGLSLGeneration != gen;
-    var->set(kVec4f_GrSLType,
-             GrGLShaderVar::kOut_TypeModifier,
-             declaredOutput ? nameIfDeclared : "gl_FragColor");
-    return declaredOutput;
-}
-
 const char* GrGLSLVectorHomogCoord(int count) {
     static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
     SkASSERT(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index a54bd3b..b97e709 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -87,24 +87,6 @@
 const char* GrGetGLSLVersionDecl(const GrGLContextInfo&);
 
 /**
- * Depending on the GLSL version being emitted there may be an assumed output
- * variable from the fragment shader for the color. Otherwise, the shader must
- * declare an output variable for the color. If this function returns true:
- *    * Parameter var's name will be set to nameIfDeclared
- *    * The variable must be declared in the fragment shader
- *    * The variable has to be bound as the color output
- *      (using glBindFragDataLocation)
- *    If the function returns false:
- *    * Parameter var's name will be set to the GLSL built-in color output name.
- *    * Do not declare the variable in the shader.
- *    * Do not use glBindFragDataLocation to bind the variable
- * In either case var is initialized to represent the color output in the
- * shader.
- */
-bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
-                             const char* nameIfDeclared,
-                             GrGLShaderVar* var);
-/**
  * Converts a GrSLType to a string containing the name of the equivalent GLSL type.
  */
 static const char* GrGLSLTypeString(GrSLType t) {
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index d11394a..f4ffb4d 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -9,7 +9,13 @@
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLUniformHandle.h"
 #include "GrDrawEffect.h"
+#include "GrGpuGL.h"
 #include "GrTexture.h"
+#include "SkRTConf.h"
+#include "SkTrace.h"
+
+#define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X)
 
 // number of each input/output type in a single allocation block
 static const int kVarsPerBlock = 8;
@@ -21,10 +27,18 @@
 static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision;
 
 typedef GrGLUniformManager::UniformHandle UniformHandle;
+
+SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false,
+                "Print the source code for all shaders generated.");
+
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace {
 
+inline const char* color_attribute_name() { return "aColor"; }
+inline const char* coverage_attribute_name() { return "aCoverage"; }
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
 inline const char* sample_function_name(GrSLType type, GrGLSLGeneration glslGen) {
     if (kVec2f_GrSLType == type) {
         return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D";
@@ -91,28 +105,32 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo,
+GrGLShaderBuilder::GrGLShaderBuilder(GrGpuGL* gpu,
                                      GrGLUniformManager& uniformManager,
                                      const GrGLProgramDesc& desc,
                                      bool needsVertexShader)
     : fUniforms(kVarsPerBlock)
-    , fCtxInfo(ctxInfo)
+    , fGpu(gpu)
     , fUniformManager(uniformManager)
     , fFSFeaturesAddedMask(0)
     , fFSInputs(kVarsPerBlock)
     , fFSOutputs(kMaxFSOutputs)
     , fSetupFragPosition(false)
+    , fKnownColorValue(kNone_GrSLConstantVec)
+    , fKnownCoverageValue(kNone_GrSLConstantVec)
+    , fHasCustomColorOutput(false)
+    , fHasSecondaryOutput(false)
     , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.getHeader().fFragPosKey) {
 
     const GrGLProgramDesc::KeyHeader& header = desc.getHeader();
 
     if (needsVertexShader) {
-        fVertexBuilder.reset(SkNEW_ARGS(VertexBuilder, (this, desc)));
+        fVertexBuilder.reset(SkNEW_ARGS(VertexBuilder, (this, fGpu, desc)));
     }
 
     // Emit code to read the dst copy textue if necessary.
     if (kNoDstRead_DstReadKey != header.fDstReadKey &&
-        GrGLCaps::kNone_FBFetchType == ctxInfo.caps()->fbFetchType()) {
+        GrGLCaps::kNone_FBFetchType == fGpu->glCaps().fbFetchType()) {
         bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & header.fDstReadKey);
         const char* dstCopyTopLeftName;
         const char* dstCopyCoordScaleName;
@@ -143,15 +161,76 @@
         this->fsAppendTextureLookup(fDstCopySampler, "_dstTexCoord");
         this->fsCodeAppend(";\n\n");
     }
+
+    switch (header.fColorInput) {
+        case GrGLProgramDesc::kAttribute_ColorInput: {
+            SkASSERT(NULL != fVertexBuilder.get());
+            fVertexBuilder->addAttribute(kVec4f_GrSLType, color_attribute_name());
+            const char *vsName, *fsName;
+            fVertexBuilder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
+            fVertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsName, color_attribute_name());
+            fInputColor = fsName;
+            break;
+        }
+        case GrGLProgramDesc::kUniform_ColorInput: {
+            const char* name;
+            fColorUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                             kVec4f_GrSLType, "Color", &name);
+            fInputColor = name;
+            break;
+        }
+        case GrGLProgramDesc::kTransBlack_ColorInput:
+            fKnownColorValue = kZeros_GrSLConstantVec;
+            break;
+        case GrGLProgramDesc::kSolidWhite_ColorInput:
+            fKnownColorValue = kOnes_GrSLConstantVec;
+            break;
+        default:
+            GrCrash("Unknown color type.");
+    }
+
+    switch (header.fCoverageInput) {
+        case GrGLProgramDesc::kAttribute_ColorInput: {
+            SkASSERT(NULL != fVertexBuilder.get());
+            fVertexBuilder->addAttribute(kVec4f_GrSLType, coverage_attribute_name());
+            const char *vsName, *fsName;
+            fVertexBuilder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
+            fVertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsName, coverage_attribute_name());
+            fInputCoverage = fsName;
+            break;
+        }
+        case GrGLProgramDesc::kUniform_ColorInput: {
+            const char* name;
+            fCoverageUniform = this->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                kVec4f_GrSLType, "Coverage", &name);
+            fInputCoverage = name;
+            break;
+        }
+        case GrGLProgramDesc::kTransBlack_ColorInput:
+            fKnownCoverageValue = kZeros_GrSLConstantVec;
+            break;
+        case GrGLProgramDesc::kSolidWhite_ColorInput:
+            fKnownCoverageValue = kOnes_GrSLConstantVec;
+            break;
+        default:
+            GrCrash("Unknown coverage type.");
+    }
+
+    if (k110_GrGLSLGeneration != fGpu->glslGeneration()) {
+        fFSOutputs.push_back().set(kVec4f_GrSLType,
+                                   GrGLShaderVar::kOut_TypeModifier,
+                                   declared_color_output_name());
+        fHasCustomColorOutput = true;
+    }
 }
 
 bool GrGLShaderBuilder::enableFeature(GLSLFeature feature) {
     switch (feature) {
         case kStandardDerivatives_GLSLFeature:
-            if (!fCtxInfo.caps()->shaderDerivativeSupport()) {
+            if (!fGpu->glCaps().shaderDerivativeSupport()) {
                 return false;
             }
-            if (kES_GrGLBinding == fCtxInfo.binding()) {
+            if (kES_GrGLBinding == fGpu->glBinding()) {
                 this->addFSFeature(1 << kStandardDerivatives_GLSLFeature,
                                    "GL_OES_standard_derivatives");
             }
@@ -165,23 +244,23 @@
 bool GrGLShaderBuilder::enablePrivateFeature(GLSLPrivateFeature feature) {
     switch (feature) {
         case kFragCoordConventions_GLSLPrivateFeature:
-            if (!fCtxInfo.caps()->fragCoordConventionsSupport()) {
+            if (!fGpu->glCaps().fragCoordConventionsSupport()) {
                 return false;
             }
-            if (fCtxInfo.glslGeneration() < k150_GrGLSLGeneration) {
+            if (fGpu->glslGeneration() < k150_GrGLSLGeneration) {
                 this->addFSFeature(1 << kFragCoordConventions_GLSLPrivateFeature,
                                    "GL_ARB_fragment_coord_conventions");
             }
             return true;
         case kEXTShaderFramebufferFetch_GLSLPrivateFeature:
-            if (GrGLCaps::kEXT_FBFetchType != fCtxInfo.caps()->fbFetchType()) {
+            if (GrGLCaps::kEXT_FBFetchType != fGpu->glCaps().fbFetchType()) {
                 return false;
             }
             this->addFSFeature(1 << kEXTShaderFramebufferFetch_GLSLPrivateFeature,
                                "GL_EXT_shader_framebuffer_fetch");
             return true;
         case kNVShaderFramebufferFetch_GLSLPrivateFeature:
-            if (GrGLCaps::kNV_FBFetchType != fCtxInfo.caps()->fbFetchType()) {
+            if (GrGLCaps::kNV_FBFetchType != fGpu->glCaps().fbFetchType()) {
                 return false;
             }
             this->addFSFeature(1 << kNVShaderFramebufferFetch_GLSLPrivateFeature,
@@ -225,7 +304,7 @@
         }
     }
     static const char kFBFetchColorName[] = "gl_LastFragData[0]";
-    GrGLCaps::FBFetchType fetchType = fCtxInfo.caps()->fbFetchType();
+    GrGLCaps::FBFetchType fetchType = fGpu->glCaps().fbFetchType();
     if (GrGLCaps::kEXT_FBFetchType == fetchType) {
         SkAssertResult(this->enablePrivateFeature(kEXTShaderFramebufferFetch_GLSLPrivateFeature));
         return kFBFetchColorName;
@@ -246,10 +325,10 @@
     SkASSERT(NULL != coordName);
 
     out->appendf("%s(%s, %s)",
-                 sample_function_name(varyingType, fCtxInfo.glslGeneration()),
+                 sample_function_name(varyingType, fGpu->glslGeneration()),
                  this->getUniformCStr(sampler.fSamplerUniform),
                  coordName);
-    append_swizzle(out, sampler, *fCtxInfo.caps());
+    append_swizzle(out, sampler, fGpu->glCaps());
 }
 
 void GrGLShaderBuilder::fsAppendTextureLookup(const GrGLShaderBuilder::TextureSampler& sampler,
@@ -379,7 +458,7 @@
             fSetupFragPosition = true;
         }
         return "gl_FragCoord";
-    } else if (fCtxInfo.caps()->fragCoordConventionsSupport()) {
+    } else if (fGpu->glCaps().fragCoordConventionsSupport()) {
         if (!fSetupFragPosition) {
             SkAssertResult(this->enablePrivateFeature(kFragCoordConventions_GLSLPrivateFeature));
             fFSInputs.push_back().set(kVec4f_GrSLType,
@@ -425,7 +504,7 @@
     fFSFunctions.appendf(" %s", outName->c_str());
     fFSFunctions.append("(");
     for (int i = 0; i < argCnt; ++i) {
-        args[i].appendDecl(fCtxInfo, &fFSFunctions);
+        args[i].appendDecl(this->ctxInfo(), &fFSFunctions);
         if (i < argCnt - 1) {
             fFSFunctions.append(", ");
         }
@@ -463,7 +542,7 @@
 
 void GrGLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const {
     for (int i = 0; i < vars.count(); ++i) {
-        vars[i].appendDecl(fCtxInfo, out);
+        vars[i].appendDecl(this->ctxInfo(), out);
         out->append(";\n");
     }
 }
@@ -472,33 +551,12 @@
                                            SkString* out) const {
     for (int i = 0; i < fUniforms.count(); ++i) {
         if (fUniforms[i].fVisibility & visibility) {
-            fUniforms[i].fVariable.appendDecl(fCtxInfo, out);
+            fUniforms[i].fVariable.appendDecl(this->ctxInfo(), out);
             out->append(";\n");
         }
     }
 }
 
-void GrGLShaderBuilder::fsGetShader(SkString* shaderStr) const {
-    *shaderStr = GrGetGLSLVersionDecl(fCtxInfo);
-    shaderStr->append(fFSExtensions);
-    append_default_precision_qualifier(kDefaultFragmentPrecision,
-                                       fCtxInfo.binding(),
-                                       shaderStr);
-    this->appendUniformDecls(kFragment_Visibility, shaderStr);
-    this->appendDecls(fFSInputs, shaderStr);
-    // We shouldn't have declared outputs on 1.10
-    SkASSERT(k110_GrGLSLGeneration != fCtxInfo.glslGeneration() || fFSOutputs.empty());
-    this->appendDecls(fFSOutputs, shaderStr);
-    shaderStr->append(fFSFunctions);
-    shaderStr->append("void main() {\n");
-    shaderStr->append(fFSCode);
-    shaderStr->append("}\n");
-}
-
-void GrGLShaderBuilder::finished(GrGLuint programID) {
-    fUniformManager.getUniformLocations(programID, fUniforms);
-}
-
 void GrGLShaderBuilder::emitEffects(
                         const GrEffectStage* effectStages[],
                         const GrBackendEffectFactory::EffectKey effectKeys[],
@@ -526,7 +584,7 @@
             textureSamplers[t].init(this, &effect->textureAccess(t), t);
             effectSamplerHandles[e]->push_back(textureSamplers[t].fSamplerUniform);
         }
-        GrDrawEffect drawEffect(stage, fVertexBuilder.get()
+        GrDrawEffect drawEffect(stage, NULL != fVertexBuilder.get()
                                        && fVertexBuilder->hasExplicitLocalCoords());
 
         int numAttributes = stage.getVertexAttribIndexCount();
@@ -534,7 +592,7 @@
         SkSTArray<GrEffect::kMaxVertexAttribs, SkString> attributeNames;
         for (int a = 0; a < numAttributes; ++a) {
             // TODO: Make addAttribute mangle the name.
-            SkASSERT(fVertexBuilder.get());
+            SkASSERT(NULL != fVertexBuilder.get());
             SkString attributeName("aAttr");
             attributeName.appendS32(attributeIndices[a]);
             fVertexBuilder->addEffectAttribute(attributeIndices[a],
@@ -557,7 +615,7 @@
         // Enclose custom code in a block to avoid namespace conflicts
         SkString openBrace;
         openBrace.printf("\t{ // Stage %d: %s\n", fCodeStage.stageIndex(), glEffects[e]->name());
-        if (fVertexBuilder.get()) {
+        if (NULL != fVertexBuilder.get()) {
             fVertexBuilder->vsCodeAppend(openBrace.c_str());
         }
         this->fsCodeAppend(openBrace.c_str());
@@ -569,7 +627,7 @@
                                inColor.isEmpty() ? NULL : inColor.c_str(),
                                textureSamplers);
 
-        if (fVertexBuilder.get()) {
+        if (NULL != fVertexBuilder.get()) {
             fVertexBuilder->vsCodeAppend("\t}\n");
         }
         this->fsCodeAppend("\t}\n");
@@ -584,22 +642,170 @@
     }
 }
 
+const char* GrGLShaderBuilder::getColorOutputName() const {
+    return fHasCustomColorOutput ? declared_color_output_name() : "gl_FragColor";
+}
+
+const char* GrGLShaderBuilder::enableSecondaryOutput() {
+    if (!fHasSecondaryOutput) {
+        fFSOutputs.push_back().set(kVec4f_GrSLType,
+                                   GrGLShaderVar::kOut_TypeModifier,
+                                   dual_source_output_name());
+        fHasSecondaryOutput = true;
+    }
+    return dual_source_output_name();
+}
+
+
+bool GrGLShaderBuilder::finish(GrGLuint* outProgramId) {
+    SK_TRACE_EVENT0("GrGLShaderBuilder::finish");
+
+    GrGLuint programId = 0;
+    GL_CALL_RET(programId, CreateProgram());
+    if (!programId) {
+        return false;
+    }
+
+    if (!this->compileAndAttachShaders(programId)) {
+        GL_CALL(DeleteProgram(programId));
+        return false;
+    }
+
+    this->bindProgramLocations(programId);
+
+    GL_CALL(LinkProgram(programId));
+    GrGLint linked = GR_GL_INIT_ZERO;
+    GL_CALL(GetProgramiv(programId, GR_GL_LINK_STATUS, &linked));
+    if (!linked) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        GL_CALL(GetProgramiv(programId, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
+        if (infoLen > 0) {
+            // retrieve length even though we don't need it to workaround
+            // bug in chrome cmd buffer param validation.
+            GrGLsizei length = GR_GL_INIT_ZERO;
+            GL_CALL(GetProgramInfoLog(programId,
+                                      infoLen+1,
+                                      &length,
+                                      (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        SkDEBUGFAIL("Error linking program");
+        GL_CALL(DeleteProgram(programId));
+        return false;
+    }
+
+    fUniformManager.getUniformLocations(programId, fUniforms);
+    *outProgramId = programId;
+    return true;
+}
+
+namespace {
+// Compiles a GL shader, attaches it to a program, and releases the shader's reference.
+// (That way there's no need to hang on to the GL shader id and delete it later.)
+bool attach_shader(const GrGLInterface* gli,
+                   GrGLuint programId,
+                   GrGLenum type,
+                   const SkString& shaderSrc) {
+    GrGLuint shaderId;
+    GR_GL_CALL_RET(gli, shaderId, CreateShader(type));
+    if (0 == shaderId) {
+        return false;
+    }
+
+    const GrGLchar* sourceStr = shaderSrc.c_str();
+    int sourceLength = shaderSrc.size();
+    GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength));
+
+    GrGLint compiled = GR_GL_INIT_ZERO;
+    GR_GL_CALL(gli, CompileShader(shaderId));
+    GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled));
+
+    if (!compiled) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+        if (infoLen > 0) {
+            // retrieve length even though we don't need it to workaround bug in chrome cmd buffer
+            // param validation.
+            GrGLsizei length = GR_GL_INIT_ZERO;
+            GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1,
+                                             &length, (char*)log.get()));
+            GrPrintf(shaderSrc.c_str());
+            GrPrintf("\n%s", log.get());
+        }
+        SkDEBUGFAIL("Shader compilation failed!");
+        GR_GL_CALL(gli, DeleteShader(shaderId));
+        return false;
+    } else if (c_PrintShaders) {
+        GrPrintf(shaderSrc.c_str());
+        GrPrintf("\n");
+    }
+
+    GR_GL_CALL(gli, AttachShader(programId, shaderId));
+    GR_GL_CALL(gli, DeleteShader(shaderId));
+    return true;
+}
+
+}
+
+bool GrGLShaderBuilder::compileAndAttachShaders(GrGLuint programId) const {
+    if (NULL != fVertexBuilder.get() && !fVertexBuilder->compileAndAttachShaders(programId)) {
+        return false;
+    }
+
+    SkString fragShaderSrc(GrGetGLSLVersionDecl(this->ctxInfo()));
+    fragShaderSrc.append(fFSExtensions);
+    append_default_precision_qualifier(kDefaultFragmentPrecision,
+                                       fGpu->glBinding(),
+                                       &fragShaderSrc);
+    this->appendUniformDecls(kFragment_Visibility, &fragShaderSrc);
+    this->appendDecls(fFSInputs, &fragShaderSrc);
+    // We shouldn't have declared outputs on 1.10
+    SkASSERT(k110_GrGLSLGeneration != fGpu->glslGeneration() || fFSOutputs.empty());
+    this->appendDecls(fFSOutputs, &fragShaderSrc);
+    fragShaderSrc.append(fFSFunctions);
+    fragShaderSrc.append("void main() {\n");
+    fragShaderSrc.append(fFSCode);
+    fragShaderSrc.append("}\n");
+    if (!attach_shader(fGpu->glInterface(), programId, GR_GL_FRAGMENT_SHADER, fragShaderSrc)) {
+        return false;
+    }
+
+    return true;
+}
+
+void GrGLShaderBuilder::bindProgramLocations(GrGLuint programId) const {
+    if (NULL != fVertexBuilder.get()) {
+        fVertexBuilder->bindProgramLocations(programId);
+    }
+
+    if (fHasCustomColorOutput) {
+        GL_CALL(BindFragDataLocation(programId, 0, declared_color_output_name()));
+    }
+    if (fHasSecondaryOutput) {
+        GL_CALL(BindFragDataLocationIndexed(programId, 0, 1, dual_source_output_name()));
+    }
+}
+
+const GrGLContextInfo& GrGLShaderBuilder::ctxInfo() const {
+    return fGpu->ctxInfo();
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 GrGLShaderBuilder::VertexBuilder::VertexBuilder(GrGLShaderBuilder* parent,
+                                                GrGpuGL* gpu,
                                                 const GrGLProgramDesc& desc)
-    : fVSAttrs(kVarsPerBlock)
+    : fParent(parent)
+    , fGpu(gpu)
+    , fDesc(desc)
+    , fVSAttrs(kVarsPerBlock)
     , fVSOutputs(kVarsPerBlock)
     , fGSInputs(kVarsPerBlock)
-    , fGSOutputs(kVarsPerBlock)
-    , fParent(parent)
-#if GR_GL_EXPERIMENTAL_GS
-    , fUsesGS(SkToBool(desc.getHeader().fExperimentalGS))
-#else
-    , fUsesGS(false)
-#endif
-{
-    const GrGLProgramDesc::KeyHeader& header = desc.getHeader();
+    , fGSOutputs(kVarsPerBlock) {
+
+    const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
 
     fPositionVar = &fVSAttrs.push_back();
     fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition");
@@ -611,6 +817,23 @@
     } else {
         fLocalCoordsVar = fPositionVar;
     }
+
+    const char* viewMName;
+    fViewMatrixUniform = fParent->addUniform(GrGLShaderBuilder::kVertex_Visibility,
+                                             kMat33f_GrSLType, "ViewM", &viewMName);
+
+    this->vsCodeAppendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
+                        "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n",
+                        viewMName, fPositionVar->c_str());
+
+    // we output point size in the GS if present
+    if (header.fEmitsPointSize
+#if GR_GL_EXPERIMENTAL_GS
+        && !header.fExperimentalGS
+#endif
+        ) {
+        this->vsCodeAppend("\tgl_PointSize = 1.0;\n");
+    }
 }
 
 bool GrGLShaderBuilder::VertexBuilder::addAttribute(GrSLType type,
@@ -654,7 +877,8 @@
     }
     // input to FS comes either from VS or GS
     const SkString* fsName;
-    if (fUsesGS) {
+#if GR_GL_EXPERIMENTAL_GS
+    if (fDesc.getHeader().fExperimentalGS) {
         // if we have a GS take each varying in as an array
         // and output as non-array.
         fGSInputs.push_back();
@@ -667,7 +891,9 @@
         fGSOutputs.back().setTypeModifier(GrGLShaderVar::kVaryingOut_TypeModifier);
         fParent->nameVariable(fGSOutputs.back().accessName(), 'g', name);
         fsName = fGSOutputs.back().accessName();
-    } else {
+    } else
+#endif
+    {
         fsName = fVSOutputs.back().accessName();
     }
     fParent->fsInputAppend().set(type,
@@ -678,37 +904,9 @@
     }
 }
 
-void GrGLShaderBuilder::VertexBuilder::vsGetShader(SkString* shaderStr) const {
-    *shaderStr = GrGetGLSLVersionDecl(fParent->ctxInfo());
-    fParent->appendUniformDecls(kVertex_Visibility, shaderStr);
-    fParent->appendDecls(fVSAttrs, shaderStr);
-    fParent->appendDecls(fVSOutputs, shaderStr);
-    shaderStr->append("void main() {\n");
-    shaderStr->append(fVSCode);
-    shaderStr->append("}\n");
-}
-
-void GrGLShaderBuilder::VertexBuilder::gsGetShader(SkString* shaderStr) const {
-    if (!fUsesGS) {
-        shaderStr->reset();
-        return;
-    }
-
-    *shaderStr = GrGetGLSLVersionDecl(fParent->ctxInfo());
-    shaderStr->append(fGSHeader);
-    fParent->appendDecls(fGSInputs, shaderStr);
-    fParent->appendDecls(fGSOutputs, shaderStr);
-    shaderStr->append("void main() {\n");
-    shaderStr->append(fGSCode);
-    shaderStr->append("}\n");
-}
-
-
 const SkString* GrGLShaderBuilder::VertexBuilder::getEffectAttributeName(int attributeIndex) const {
-    const AttributePair* attribEnd = this->getEffectAttributes().end();
-    for (const AttributePair* attrib = this->getEffectAttributes().begin();
-         attrib != attribEnd;
-         ++attrib) {
+    const AttributePair* attribEnd = fEffectAttributes.end();
+    for (const AttributePair* attrib = fEffectAttributes.begin(); attrib != attribEnd; ++attrib) {
         if (attrib->fIndex == attributeIndex) {
             return &attrib->fName;
         }
@@ -716,3 +914,78 @@
 
     return NULL;
 }
+
+bool GrGLShaderBuilder::VertexBuilder::compileAndAttachShaders(GrGLuint programId) const {
+    SkString vertShaderSrc(GrGetGLSLVersionDecl(fParent->ctxInfo()));
+    fParent->appendUniformDecls(kVertex_Visibility, &vertShaderSrc);
+    fParent->appendDecls(fVSAttrs, &vertShaderSrc);
+    fParent->appendDecls(fVSOutputs, &vertShaderSrc);
+    vertShaderSrc.append("void main() {\n");
+    vertShaderSrc.append(fVSCode);
+    vertShaderSrc.append("}\n");
+    if (!attach_shader(fGpu->glInterface(), programId, GR_GL_VERTEX_SHADER, vertShaderSrc)) {
+        return false;
+    }
+
+#if GR_GL_EXPERIMENTAL_GS
+    if (fDesc.getHeader().fExperimentalGS) {
+        SkASSERT(fGpu->glslGeneration() >= k150_GrGLSLGeneration);
+        SkString geomShaderSrc(GrGetGLSLVersionDecl(fParent->ctxInfo()));
+        geomShaderSrc.append("layout(triangles) in;\n"
+                             "layout(triangle_strip, max_vertices = 6) out;\n");
+        fParent->appendDecls(fGSInputs, &geomShaderSrc);
+        fParent->appendDecls(fGSOutputs, &geomShaderSrc);
+        geomShaderSrc.append("void main() {\n");
+        geomShaderSrc.append("\tfor (int i = 0; i < 3; ++i) {\n"
+                             "\t\tgl_Position = gl_in[i].gl_Position;\n");
+        if (fDesc.getHeader().fEmitsPointSize) {
+            geomShaderSrc.append("\t\tgl_PointSize = 1.0;\n");
+        }
+        SkASSERT(fGSInputs.count() == fGSOutputs.count());
+        for (int i = 0; i < fGSInputs.count(); ++i) {
+            geomShaderSrc.appendf("\t\t%s = %s[i];\n",
+                                  fGSOutputs[i].getName().c_str(),
+                                  fGSInputs[i].getName().c_str());
+        }
+        geomShaderSrc.append("\t\tEmitVertex();\n"
+                             "\t}\n"
+                             "\tEndPrimitive();\n");
+        geomShaderSrc.append("}\n");
+        if (!attach_shader(fGpu->glInterface(), programId, GR_GL_GEOMETRY_SHADER, geomShaderSrc)) {
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+void GrGLShaderBuilder::VertexBuilder::bindProgramLocations(GrGLuint programId) const {
+    const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
+
+    // Bind the attrib locations to same values for all shaders
+    SkASSERT(-1 != header.fPositionAttributeIndex);
+    GL_CALL(BindAttribLocation(programId,
+                               header.fPositionAttributeIndex,
+                               fPositionVar->c_str()));
+    if (-1 != header.fLocalCoordAttributeIndex) {
+        GL_CALL(BindAttribLocation(programId,
+                                   header.fLocalCoordAttributeIndex,
+                                   fLocalCoordsVar->c_str()));
+    }
+    if (-1 != header.fColorAttributeIndex) {
+        GL_CALL(BindAttribLocation(programId,
+                                   header.fColorAttributeIndex,
+                                   color_attribute_name()));
+    }
+    if (-1 != header.fCoverageAttributeIndex) {
+        GL_CALL(BindAttribLocation(programId,
+                                   header.fCoverageAttributeIndex,
+                                   coverage_attribute_name()));
+    }
+
+    const AttributePair* attribEnd = fEffectAttributes.end();
+    for (const AttributePair* attrib = fEffectAttributes.begin(); attrib != attribEnd; ++attrib) {
+         GL_CALL(BindAttribLocation(programId, attrib->fIndex, attrib->fName.c_str()));
+    }
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 3c3275c..d73a731 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -105,7 +105,7 @@
         kFragment_Visibility = 0x4,
     };
 
-    GrGLShaderBuilder(const GrGLContextInfo&,
+    GrGLShaderBuilder(GrGpuGL*,
                       GrGLUniformManager&,
                       const GrGLProgramDesc&,
                       bool needsVertexShader);
@@ -171,9 +171,6 @@
 
     /** Add input/output variable declarations (i.e. 'varying') to the fragment shader. */
     GrGLShaderVar& fsInputAppend() { return fFSInputs.push_back(); }
-    GrGLShaderVar& fsOutputAppend() { return fFSOutputs.push_back(); }
-    GrGLShaderVar& fsInputAppend(const GrGLShaderVar& var) { return fFSInputs.push_back(var); }
-    GrGLShaderVar& fsOutputAppend(const GrGLShaderVar& var) { return fFSOutputs.push_back(var); }
 
     /** Generates a EffectKey for the shader code based on the texture access parameters and the
         capabilities of the GL context.  This is useful for keying the shader programs that may
@@ -238,13 +235,13 @@
 
     /**
      * Interfaces used by GrGLProgram.
-     * TODO: Hide these from the GrEffects using friend or splitting this into two related classes.
-     * Also, GrGLProgram's shader string construction should be moved to this class.
+     * TODO: These are used by GrGLProgram to insert a mode color filter. Remove these when the
+     * color filter is expressed as a GrEffect.
      */
-
-    /** Called after building is complete to get the final shader string. To acces the vertex
-        and geometry shaders, use the VertexBuilder. */
-    void fsGetShader(SkString*) const;
+    const SkString& getInputColor() const { return fInputColor; }
+    GrSLConstantVec getKnownColorValue() const { return fKnownColorValue; }
+    const SkString& getInputCoverage() const { return fInputCoverage; }
+    GrSLConstantVec getKnownCoverageValue() const { return fKnownCoverageValue; }
 
     /**
      * Adds code for effects. effectStages contains the effects to add. effectKeys[i] is the key
@@ -265,6 +262,9 @@
                      SkTArray<GrGLUniformManager::UniformHandle, true>* effectSamplerHandles[],
                      GrGLEffect* glEffects[]);
 
+    const char* getColorOutputName() const;
+    const char* enableSecondaryOutput();
+
     GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
     GrGLUniformManager::UniformHandle getDstCopyTopLeftUniform() const {
         return fDstCopyTopLeftUniform;
@@ -272,6 +272,8 @@
     GrGLUniformManager::UniformHandle getDstCopyScaleUniform() const {
         return fDstCopyScaleUniform;
     }
+    GrGLUniformManager::UniformHandle getColorUniform() const { return fColorUniform; }
+    GrGLUniformManager::UniformHandle getCoverageUniform() const { return fCoverageUniform; }
     GrGLUniformManager::UniformHandle getDstCopySamplerUniform() const {
         return fDstCopySampler.fSamplerUniform;
     }
@@ -281,7 +283,7 @@
         that only use the fragment shader. */
     class VertexBuilder {
     public:
-        VertexBuilder(GrGLShaderBuilder* parent, const GrGLProgramDesc&);
+        VertexBuilder(GrGLShaderBuilder* parent, GrGpuGL* gpu, const GrGLProgramDesc&);
 
         /**
          * Called by GrGLEffects to add code to one of the shaders.
@@ -293,15 +295,7 @@
             va_end(args);
         }
 
-        void gsCodeAppendf(const char format[], ...) SK_PRINTF_LIKE(2, 3) {
-            va_list args;
-            va_start(args, format);
-            fGSCode.appendf(format, args);
-            va_end(args);
-        }
-
         void vsCodeAppend(const char* str) { fVSCode.append(str); }
-        void gsCodeAppend(const char* str) { fGSCode.append(str); }
 
        /** Add a vertex attribute to the current program that is passed in from the vertex data.
            Returns false if the attribute was already there, true otherwise. */
@@ -330,9 +324,26 @@
          */
         bool hasExplicitLocalCoords() const { return (fLocalCoordsVar != fPositionVar); }
 
-        /** Called after building is complete to get the final shader string. */
-        void vsGetShader(SkString*) const;
-        void gsGetShader(SkString*) const;
+        bool addEffectAttribute(int attributeIndex, GrSLType type, const SkString& name);
+        const SkString* getEffectAttributeName(int attributeIndex) const;
+
+        GrGLUniformManager::UniformHandle getViewMatrixUniform() const {
+            return fViewMatrixUniform;
+        }
+
+        bool compileAndAttachShaders(GrGLuint programId) const;
+        void bindProgramLocations(GrGLuint programId) const;
+
+    private:
+        GrGLShaderBuilder*                  fParent;
+        GrGpuGL*                            fGpu;
+        const GrGLProgramDesc&              fDesc;
+        VarArray                            fVSAttrs;
+        VarArray                            fVSOutputs;
+        VarArray                            fGSInputs;
+        VarArray                            fGSOutputs;
+
+        SkString                            fVSCode;
 
         struct AttributePair {
             void set(int index, const SkString& name) {
@@ -341,31 +352,10 @@
             int      fIndex;
             SkString fName;
         };
-        const SkTArray<AttributePair, true>& getEffectAttributes() const {
-            return fEffectAttributes;
-        }
-        bool addEffectAttribute(int attributeIndex, GrSLType type, const SkString& name);
-        const SkString* getEffectAttributeName(int attributeIndex) const;
-
-        // TODO: Everything below here private.
-    public:
-
-        VarArray    fVSAttrs;
-        VarArray    fVSOutputs;
-        VarArray    fGSInputs;
-        VarArray    fGSOutputs;
-        SkString    fGSHeader; // layout qualifiers specific to GS
-
-    private:
-        GrGLShaderBuilder*                  fParent;
-
-        bool                                fUsesGS;
-
-        SkString                            fVSCode;
-        SkString                            fGSCode;
-
         SkSTArray<10, AttributePair, true>  fEffectAttributes;
 
+        GrGLUniformManager::UniformHandle   fViewMatrixUniform;
+
         GrGLShaderVar*                      fPositionVar;
         GrGLShaderVar*                      fLocalCoordsVar;
     };
@@ -374,15 +364,17 @@
         It may be NULL if this shader program is only meant to have a fragment shader. */
     VertexBuilder* getVertexBuilder() const { return fVertexBuilder.get(); }
 
-    // TODO: Make this do all the compiling, linking, etc.
-    void finished(GrGLuint programID);
+    bool finish(GrGLuint* outProgramId);
 
-    const GrGLContextInfo& ctxInfo() const { return fCtxInfo; }
+    const GrGLContextInfo& ctxInfo() const;
 
 private:
     void appendDecls(const VarArray&, SkString*) const;
     void appendUniformDecls(ShaderVisibility, SkString*) const;
 
+    bool compileAndAttachShaders(GrGLuint programId) const;
+    void bindProgramLocations(GrGLuint programId) const;
+
     typedef GrGLUniformManager::BuilderUniform BuilderUniform;
     GrGLUniformManager::BuilderUniformArray fUniforms;
 
@@ -471,7 +463,7 @@
         kBottomLeftFragPosRead_FragPosKey   = 0x2,// Read frag pos relative to bottom-left.
     };
 
-    const GrGLContextInfo&              fCtxInfo;
+    GrGpuGL*                            fGpu;
     GrGLUniformManager&                 fUniformManager;
     uint32_t                            fFSFeaturesAddedMask;
     SkString                            fFSFunctions;
@@ -484,13 +476,23 @@
     bool                                fSetupFragPosition;
     TextureSampler                      fDstCopySampler;
 
+    SkString                            fInputColor;
+    GrSLConstantVec                     fKnownColorValue;
+    SkString                            fInputCoverage;
+    GrSLConstantVec                     fKnownCoverageValue;
+
+    bool                                fHasCustomColorOutput;
+    bool                                fHasSecondaryOutput;
+
     GrGLUniformManager::UniformHandle   fRTHeightUniform;
     GrGLUniformManager::UniformHandle   fDstCopyTopLeftUniform;
     GrGLUniformManager::UniformHandle   fDstCopyScaleUniform;
+    GrGLUniformManager::UniformHandle   fColorUniform;
+    GrGLUniformManager::UniformHandle   fCoverageUniform;
 
     bool                                fTopLeftFragPosRead;
 
-    SkAutoTDelete<VertexBuilder> fVertexBuilder;
+    SkAutoTDelete<VertexBuilder>        fVertexBuilder;
 };
 
 #endif