Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / gl / builders / GrGLProgramBuilder.cpp
index 909ac76..8fc6020 100644 (file)
  * found in the LICENSE file.
  */
 
+#include "GrGLProgramBuilder.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLSLPrettyPrint.h"
 #include "gl/GrGLUniformHandle.h"
-#include "GrCoordTransform.h"
 #include "../GrGpuGL.h"
-#include "GrGLFragmentShaderBuilder.h"
+#include "GrCoordTransform.h"
+#include "GrGLLegacyNvprProgramBuilder.h"
+#include "GrGLNvprProgramBuilder.h"
 #include "GrGLProgramBuilder.h"
 #include "GrTexture.h"
-#include "GrGLVertexShaderBuilder.h"
 #include "SkRTConf.h"
 #include "SkTraceEvent.h"
 
-namespace {
 #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
 
-// number of each input/output type in a single allocation block
-static const int kVarsPerBlock = 8;
-
 // ES2 FS only guarantees mediump and lowp support
 static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool GrGLProgramBuilder::genProgram(const GrGeometryStage* geometryProcessor,
-                                    const GrFragmentStage* colorStages[],
-                                    const GrFragmentStage* coverageStages[]) {
-    const GrGLProgramDesc::KeyHeader& header = this->desc().getHeader();
+//////////////////////////////////////////////////////////////////////////////
 
-    fFS.emitCodeBeforeEffects();
+const int GrGLProgramBuilder::kVarsPerBlock = 8;
+
+GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrOptDrawState& optState,
+                                               GrGpu::DrawType drawType,
+                                               GrGpuGL* gpu) {
+    // create a builder.  This will be handed off to effects so they can use it to add
+    // uniforms, varyings, textures, etc
+    SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(optState,
+                                                                   drawType,
+                                                                   optState.hasGeometryProcessor(),
+                                                                   gpu));
+
+    GrGLProgramBuilder* pb = builder.get();
+    const GrGLProgramDescBuilder::GLKeyHeader& header = GrGLProgramDescBuilder::GetHeader(pb->desc());
+
+    // emit code to read the dst copy texture, if necessary
+    if (GrGLFragmentShaderBuilder::kNoDstRead_DstReadKey != header.fDstReadKey
+            && !gpu->glCaps().fbFetchSupport()) {
+        pb->fFS.emitCodeToReadDstTexture();
+    }
 
-    ///////////////////////////////////////////////////////////////////////////
     // get the initial color and coverage to feed into the first effect in each effect chain
-
     GrGLSLExpr4 inputColor;
-    GrGLSLExpr4 inputCoverage;
-
-    if (GrGLProgramDesc::kUniform_ColorInput == header.fColorInput) {
-        const char* name;
-        fUniformHandles.fColorUni =
-            this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                             kVec4f_GrSLType,
-                             "Color",
-                             &name);
-        inputColor = GrGLSLExpr4(name);
-    } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fColorInput) {
-        inputColor = GrGLSLExpr4(1);
+    GrGLSLExpr1 inputCoverage;
+    pb->setupUniformColorAndCoverageIfNeeded(&inputColor,  &inputCoverage);
+
+    // if we have a vertex shader(we don't only if we are using NVPR or NVPR ES), then we may have
+    // to setup a few more things like builtin vertex attributes
+    bool hasVertexShader = !(header.fUseNvpr &&
+                             gpu->glPathRendering()->texturingMode() ==
+                             GrGLPathRendering::FixedFunction_TexturingMode);
+    if (hasVertexShader) {
+        pb->fVS.setupLocalCoords();
+        pb->fVS.transformGLToSkiaCoords();
+        if (header.fEmitsPointSize) {
+            pb->fVS.codeAppend("gl_PointSize = 1.0;");
+        }
+        if (GrProgramDesc::kAttribute_ColorInput == header.fColorInput) {
+            pb->fVS.setupBuiltinVertexAttribute("Color", &inputColor);
+        }
+        if (GrProgramDesc::kAttribute_ColorInput == header.fCoverageInput) {
+            pb->fVS.setupBuiltinVertexAttribute("Coverage", &inputCoverage);
+        }
     }
 
-    if (GrGLProgramDesc::kUniform_ColorInput == header.fCoverageInput) {
-        const char* name;
-        fUniformHandles.fCoverageUni =
-            this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                             kVec4f_GrSLType,
-                             "Coverage",
-                             &name);
-        inputCoverage = GrGLSLExpr4(name);
-    } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fCoverageInput) {
-        inputCoverage = GrGLSLExpr4(1);
-    }
+    // TODO: Once all stages can handle taking a float or vec4 and correctly handling them we can
+    // remove this cast to a vec4.
+    GrGLSLExpr4 inputCoverageVec4 = GrGLSLExpr4::VectorCast(inputCoverage);
 
-    // Subclasses drive effect emitting
-    this->createAndEmitEffects(geometryProcessor, colorStages, coverageStages, &inputColor,
-                               &inputCoverage);
+    pb->emitAndInstallProcs(optState, &inputColor, &inputCoverageVec4);
 
-    fFS.emitCodeAfterEffects(inputColor, inputCoverage);
+    if (hasVertexShader) {
+        pb->fVS.transformSkiaToGLCoords();
+    }
 
-    if (!this->finish()) {
-        return false;
+    // write the secondary color output if necessary
+    if (GrProgramDesc::kNone_SecondaryOutputType != header.fSecondaryOutputType) {
+        pb->fFS.enableSecondaryOutput(inputColor, inputCoverageVec4);
     }
 
-    return true;
+    pb->fFS.combineColorAndCoverage(inputColor, inputCoverageVec4);
+
+    return pb->finalize();
 }
 
-//////////////////////////////////////////////////////////////////////////////
+GrGLProgramBuilder*
+GrGLProgramBuilder::CreateProgramBuilder(const GrOptDrawState& optState,
+                                         GrGpu::DrawType drawType,
+                                         bool hasGeometryProcessor,
+                                         GrGpuGL* gpu) {
+    const GrProgramDesc& desc = optState.programDesc();
+    if (GrGLProgramDescBuilder::GetHeader(desc).fUseNvpr) {
+        SkASSERT(gpu->glCaps().pathRenderingSupport());
+        SkASSERT(GrProgramDesc::kAttribute_ColorInput != desc.header().fColorInput);
+        SkASSERT(GrProgramDesc::kAttribute_ColorInput != desc.header().fCoverageInput);
+        SkASSERT(!hasGeometryProcessor);
+        if (gpu->glPathRendering()->texturingMode() ==
+            GrGLPathRendering::FixedFunction_TexturingMode) {
+            return SkNEW_ARGS(GrGLLegacyNvprProgramBuilder, (gpu, optState));
+        } else {
+            return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, optState));
+        }
+    } else {
+        return SkNEW_ARGS(GrGLProgramBuilder, (gpu, optState));
+    }
+}
 
-GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu,
-                                       const GrGLProgramDesc& desc)
-    : fEffectEmitter(NULL)
-    , fFragOnly(SkToBool(desc.getHeader().fUseFragShaderOnly))
-    , fTexCoordSetCnt(0)
-    , fProgramID(0)
-    , fFS(this, desc)
-    , fSeparableVaryingInfos(kVarsPerBlock)
-    , fGrProcessorEmitter(this)
-    , fDesc(desc)
+/////////////////////////////////////////////////////////////////////////////
+
+GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu, const GrOptDrawState& optState)
+    : fVS(this)
+    , fGS(this)
+    , fFS(this, optState.programDesc().header().fFragPosKey)
+    , fOutOfStage(true)
+    , fStageIndex(-1)
+    , fGeometryProcessor(NULL)
+    , fOptState(optState)
+    , fDesc(optState.programDesc())
     , fGpu(gpu)
     , fUniforms(kVarsPerBlock) {
 }
 
+void GrGLProgramBuilder::addVarying(const char* name,
+                                    GrGLVarying* varying,
+                                    GrGLShaderVar::Precision fsPrecision) {
+    SkASSERT(varying);
+    if (varying->vsVarying()) {
+        fVS.addVarying(name, varying);
+    }
+    if (fOptState.hasGeometryProcessor() && fOptState.getGeometryProcessor()->willUseGeoShader()) {
+        fGS.addVarying(name, varying);
+    }
+    if (varying->fsVarying()) {
+        fFS.addVarying(varying, fsPrecision);
+    }
+}
+
 void GrGLProgramBuilder::nameVariable(SkString* out, char prefix, const char* name) {
     if ('\0' == prefix) {
         *out = name;
     } else {
         out->printf("%c%s", prefix, name);
     }
-    if (fCodeStage.inStageCode()) {
+    if (!fOutOfStage) {
         if (out->endsWith('_')) {
             // Names containing "__" are reserved.
             out->append("x");
         }
-        out->appendf("_Stage%d", fCodeStage.stageIndex());
+        out->appendf("_Stage%d", fStageIndex);
     }
 }
 
@@ -142,13 +189,6 @@ GrGLProgramDataManager::UniformHandle GrGLProgramBuilder::addUniformArray(uint32
     return GrGLProgramDataManager::UniformHandle::CreateFromUniformIndex(fUniforms.count() - 1);
 }
 
-void GrGLProgramBuilder::appendDecls(const VarArray& vars, SkString* out) const {
-    for (int i = 0; i < vars.count(); ++i) {
-        vars[i].appendDecl(this->ctxInfo(), out);
-        out->append(";\n");
-    }
-}
-
 void GrGLProgramBuilder::appendUniformDecls(ShaderVisibility visibility,
                                             SkString* out) const {
     for (int i = 0; i < fUniforms.count(); ++i) {
@@ -159,91 +199,248 @@ void GrGLProgramBuilder::appendUniformDecls(ShaderVisibility visibility,
     }
 }
 
-void GrGLProgramBuilder::createAndEmitEffects(const GrFragmentStage* effectStages[],
-                                              int effectCnt,
-                                              const GrGLProgramDesc::EffectKeyProvider& keyProvider,
-                                              GrGLSLExpr4* fsInOutColor) {
-    bool effectEmitted = false;
-
-    GrGLSLExpr4 inColor = *fsInOutColor;
-    GrGLSLExpr4 outColor;
-
-    for (int e = 0; e < effectCnt; ++e) {
-        fGrProcessorEmitter.set(effectStages[e]->getFragmentProcessor());
-        fEffectEmitter = &fGrProcessorEmitter;
-        // calls into the subclass to emit the actual effect into the program effect object
-        this->emitEffect(*effectStages[e], e, keyProvider, &inColor, &outColor);
-        effectEmitted = true;
+const GrGLContextInfo& GrGLProgramBuilder::ctxInfo() const {
+    return fGpu->ctxInfo();
+}
+
+void GrGLProgramBuilder::setupUniformColorAndCoverageIfNeeded(GrGLSLExpr4* inputColor,
+                                                              GrGLSLExpr1* inputCoverage) {
+    const GrProgramDesc::KeyHeader& header = this->header();
+    if (GrProgramDesc::kUniform_ColorInput == header.fColorInput) {
+        const char* name;
+        fUniformHandles.fColorUni =
+            this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                             kVec4f_GrSLType,
+                             "Color",
+                             &name);
+        *inputColor = GrGLSLExpr4(name);
+    } else if (GrProgramDesc::kAllOnes_ColorInput == header.fColorInput) {
+        *inputColor = GrGLSLExpr4(1);
     }
+    if (GrProgramDesc::kUniform_ColorInput == header.fCoverageInput) {
+        const char* name;
+        fUniformHandles.fCoverageUni =
+            this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                             kFloat_GrSLType,
+                             "Coverage",
+                             &name);
+        *inputCoverage = GrGLSLExpr1(name);
+    } else if (GrProgramDesc::kAllOnes_ColorInput == header.fCoverageInput) {
+        *inputCoverage = GrGLSLExpr1(1);
+    }
+}
 
-    if (effectEmitted) {
-        *fsInOutColor = outColor;
+void GrGLProgramBuilder::emitAndInstallProcs(const GrOptDrawState& optState,
+                                             GrGLSLExpr4* inputColor,
+                                             GrGLSLExpr4* inputCoverage) {
+    fFragmentProcessors.reset(SkNEW(GrGLInstalledFragProcs));
+    int numProcs = optState.numFragmentStages();
+    this->emitAndInstallFragProcs(0, optState.numColorStages(), inputColor);
+    if (optState.hasGeometryProcessor()) {
+        const GrGeometryProcessor& gp = *optState.getGeometryProcessor();
+        fVS.emitAttributes(gp);
+        ProcKeyProvider keyProvider(&fDesc,
+                                    ProcKeyProvider::kGeometry_ProcessorType,
+                                    GrGLProgramDescBuilder::kProcessorKeyOffsetsAndLengthOffset);
+        GrGLSLExpr4 output;
+        this->emitAndInstallProc<GrGeometryProcessor>(gp, 0, keyProvider, *inputCoverage, &output);
+        *inputCoverage = output;
     }
+    this->emitAndInstallFragProcs(optState.numColorStages(), numProcs,  inputCoverage);
 }
 
-void GrGLProgramBuilder::emitEffect(const GrProcessorStage& effectStage,
-                                    int effectIndex,
-                                    const GrGLProgramDesc::EffectKeyProvider& keyProvider,
-                                    GrGLSLExpr4* inColor,
-                                    GrGLSLExpr4* outColor) {
-    SkASSERT(effectStage.getProcessor());
-    CodeStage::AutoStageRestore csar(&fCodeStage, &effectStage);
-
-    if (inColor->isZeros()) {
-        SkString inColorName;
-
-        // Effects have no way to communicate zeros, they treat an empty string as ones.
-        this->nameVariable(&inColorName, '\0', "input");
-        fFS.codeAppendf("\tvec4 %s = %s;\n", inColorName.c_str(), inColor->c_str());
-        *inColor = inColorName;
+void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut) {
+    ProcKeyProvider keyProvider(&fDesc,
+                                ProcKeyProvider::kFragment_ProcessorType,
+                                GrGLProgramDescBuilder::kProcessorKeyOffsetsAndLengthOffset);
+    for (int e = procOffset; e < numProcs; ++e) {
+        GrGLSLExpr4 output;
+        const GrFragmentStage& stage = fOptState.getFragmentStage(e);
+        this->emitAndInstallProc<GrFragmentStage>(stage, e, keyProvider, *inOut, &output);
+        *inOut = output;
     }
+}
+
+// TODO Processors cannot output zeros because an empty string is all 1s
+// the fix is to allow effects to take the GrGLSLExpr4 directly
+template <class Proc>
+void GrGLProgramBuilder::emitAndInstallProc(const Proc& proc,
+                                            int index,
+                                            const ProcKeyProvider& keyProvider,
+                                            const GrGLSLExpr4& input,
+                                            GrGLSLExpr4* output) {
+    // Program builders have a bit of state we need to clear with each effect
+    AutoStageAdvance adv(this);
 
     // create var to hold stage result
     SkString outColorName;
     this->nameVariable(&outColorName, '\0', "output");
-    fFS.codeAppendf("\tvec4 %s;\n", outColorName.c_str());
-    *outColor = outColorName;
+    fFS.codeAppendf("vec4 %s;", outColorName.c_str());
+    *output = outColorName;
 
-    this->emitEffect(effectStage, keyProvider.get(effectIndex), outColor->c_str(),
-                     inColor->isOnes() ? NULL : inColor->c_str(), fCodeStage.stageIndex());
+    // Enclose custom code in a block to avoid namespace conflicts
+    SkString openBrace;
+    openBrace.printf("{ // Stage %d\n", fStageIndex);
+    fFS.codeAppend(openBrace.c_str());
 
-    *inColor = *outColor;
+    this->emitAndInstallProc(proc, keyProvider.get(index), output->c_str(),
+                             input.isOnes() ? NULL : input.c_str());
+
+    fFS.codeAppend("}");
 }
 
-void GrGLProgramBuilder::emitSamplers(const GrProcessor& effect,
-                                      GrGLProcessor::TextureSamplerArray* outSamplers) {
-    SkTArray<GrGLProgramEffects::Sampler, true>& samplers =
-            this->getProgramEffects()->addSamplers();
-    int numTextures = effect.numTextures();
-    samplers.push_back_n(numTextures);
+void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentStage& fs,
+                                            const GrProcessorKey& key,
+                                            const char* outColor,
+                                            const char* inColor) {
+    GrGLInstalledFragProc* ifp = SkNEW_ARGS(GrGLInstalledFragProc, (fVS.hasLocalCoords()));
+
+    const GrFragmentProcessor& fp = *fs.getProcessor();
+    ifp->fGLProc.reset(fp.getFactory().createGLInstance(fp));
+
+    SkSTArray<4, GrGLProcessor::TextureSampler> samplers(fp.numTextures());
+    this->emitSamplers(fp, &samplers, ifp);
+
+    // Fragment processors can have coord transforms
+    SkSTArray<2, GrGLProcessor::TransformedCoords> coords(fp.numTransforms());
+    this->emitTransforms(fs, &coords, ifp);
+
+    ifp->fGLProc->emitCode(this, fp, key, outColor, inColor, coords, samplers);
+
+    // 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(fp);
+    fFragmentProcessors->fProcs.push_back(ifp);
+}
+
+void GrGLProgramBuilder::emitAndInstallProc(const GrGeometryProcessor& gp,
+                                            const GrProcessorKey& key,
+                                            const char* outColor,
+                                            const char* inColor) {
+    SkASSERT(!fGeometryProcessor);
+    fGeometryProcessor = SkNEW(GrGLInstalledGeoProc);
+
+    fGeometryProcessor->fGLProc.reset(gp.getFactory().createGLInstance(gp));
+
+    SkSTArray<4, GrGLProcessor::TextureSampler> samplers(gp.numTextures());
+    this->emitSamplers(gp, &samplers, fGeometryProcessor);
+
+    GrGLGeometryProcessor::EmitArgs args(this, gp, key, outColor, inColor, samplers);
+    fGeometryProcessor->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(gp);
+}
+
+void GrGLProgramBuilder::verify(const GrGeometryProcessor& gp) {
+    SkASSERT(fFS.hasReadFragmentPosition() == gp.willReadFragmentPosition());
+}
+
+void GrGLProgramBuilder::verify(const GrFragmentProcessor& fp) {
+    SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition());
+    SkASSERT(fFS.hasReadDstColor() == fp.willReadDstColor());
+}
+
+void GrGLProgramBuilder::emitTransforms(const GrFragmentStage& effectStage,
+                                        GrGLProcessor::TransformedCoordsArray* outCoords,
+                                        GrGLInstalledFragProc* ifp) {
+    const GrFragmentProcessor* effect = effectStage.getProcessor();
+    int numTransforms = effect->numTransforms();
+    ifp->fTransforms.push_back_n(numTransforms);
+
+    for (int t = 0; t < numTransforms; t++) {
+        const char* uniName = "StageMatrix";
+        GrSLType varyingType =
+                effectStage.isPerspectiveCoordTransform(t, fVS.hasLocalCoords()) ?
+                        kVec3f_GrSLType :
+                        kVec2f_GrSLType;
+
+        SkString suffixedUniName;
+        if (0 != t) {
+            suffixedUniName.append(uniName);
+            suffixedUniName.appendf("_%i", t);
+            uniName = suffixedUniName.c_str();
+        }
+        ifp->fTransforms[t].fHandle = this->addUniform(GrGLProgramBuilder::kVertex_Visibility,
+                                                       kMat33f_GrSLType,
+                                                       uniName,
+                                                       &uniName).toShaderBuilderIndex();
+
+        const char* varyingName = "MatrixCoord";
+        SkString suffixedVaryingName;
+        if (0 != t) {
+            suffixedVaryingName.append(varyingName);
+            suffixedVaryingName.appendf("_%i", t);
+            varyingName = suffixedVaryingName.c_str();
+        }
+        GrGLVertToFrag v(varyingType);
+        this->addVarying(varyingName, &v);
+
+        const GrGLShaderVar& coords =
+                kPosition_GrCoordSet == effect->coordTransform(t).sourceCoords() ?
+                                          fVS.positionAttribute() :
+                                          fVS.localCoordsAttribute();
+
+        // varying = matrix * coords (logically)
+        SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+        if (kVec2f_GrSLType == varyingType) {
+            fVS.codeAppendf("%s = (%s * vec3(%s, 1)).xy;",
+                            v.vsOut(), uniName, coords.c_str());
+        } else {
+            fVS.codeAppendf("%s = %s * vec3(%s, 1);",
+                            v.vsOut(), uniName, coords.c_str());
+        }
+        SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords,
+                               (SkString(v.fsIn()), varyingType));
+    }
+}
+
+void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor,
+                                      GrGLProcessor::TextureSamplerArray* outSamplers,
+                                      GrGLInstalledProc* ip) {
+    int numTextures = processor.numTextures();
+    ip->fSamplers.push_back_n(numTextures);
     SkString name;
     for (int t = 0; t < numTextures; ++t) {
         name.printf("Sampler%d", t);
-        samplers[t].fUniform = this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                                kSampler2D_GrSLType,
-                                                name.c_str());
+        ip->fSamplers[t].fUniform = this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                                     kSampler2D_GrSLType,
+                                                     name.c_str());
         SkNEW_APPEND_TO_TARRAY(outSamplers, GrGLProcessor::TextureSampler,
-                               (samplers[t].fUniform, effect.textureAccess(t)));
+                               (ip->fSamplers[t].fUniform, processor.textureAccess(t)));
     }
 }
 
-bool GrGLProgramBuilder::finish() {
-    SkASSERT(0 == fProgramID);
-    GL_CALL_RET(fProgramID, CreateProgram());
-    if (!fProgramID) {
-        return false;
+GrGLProgram* GrGLProgramBuilder::finalize() {
+    // verify we can get a program id
+    GrGLuint programID;
+    GL_CALL_RET(programID, CreateProgram());
+    if (0 == programID) {
+        return NULL;
     }
 
+    // compile shaders and bind attributes / uniforms
     SkTDArray<GrGLuint> shadersToDelete;
-
-    if (!this->compileAndAttachShaders(fProgramID, &shadersToDelete)) {
-        GL_CALL(DeleteProgram(fProgramID));
-        return false;
+    if (!fFS.compileAndAttachShaders(programID, &shadersToDelete)) {
+        this->cleanupProgram(programID, shadersToDelete);
+        return NULL;
     }
-
-    this->bindProgramLocations(fProgramID);
-
-    GL_CALL(LinkProgram(fProgramID));
+    if (!(GrGLProgramDescBuilder::GetHeader(fDesc).fUseNvpr &&
+          fGpu->glPathRendering()->texturingMode() ==
+          GrGLPathRendering::FixedFunction_TexturingMode)) {
+        if (!fVS.compileAndAttachShaders(programID, &shadersToDelete)) {
+            this->cleanupProgram(programID, shadersToDelete);
+            return NULL;
+        }
+        fVS.bindVertexAttributes(programID);
+    }
+    bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL;
+    if (usingBindUniform) {
+        this->bindUniformLocations(programID);
+    }
+    fFS.bindFragmentShaderLocations(programID);
+    GL_CALL(LinkProgram(programID));
 
     // Calling GetProgramiv is expensive in Chromium. Assume success in release builds.
     bool checkLinked = !fGpu->ctxInfo().isChromium();
@@ -251,80 +448,78 @@ bool GrGLProgramBuilder::finish() {
     checkLinked = true;
 #endif
     if (checkLinked) {
-        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;
-        }
+        checkLinkStatus(programID);
     }
-
-    this->resolveProgramLocations(fProgramID);
-
-    for (int i = 0; i < shadersToDelete.count(); ++i) {
-      GL_CALL(DeleteShader(shadersToDelete[i]));
+    if (!usingBindUniform) {
+        this->resolveUniformLocations(programID);
     }
 
-    return true;
-}
+    this->cleanupShaders(shadersToDelete);
 
-bool GrGLProgramBuilder::compileAndAttachShaders(GrGLuint programId,
-                                                 SkTDArray<GrGLuint>* shaderIds) const {
-    return fFS.compileAndAttachShaders(programId, shaderIds);
+    return this->createProgram(programID);
 }
 
-void GrGLProgramBuilder::bindProgramLocations(GrGLuint programId) {
-    fFS.bindProgramLocations(programId);
-
-    // skbug.com/2056
-    bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL;
-    if (usingBindUniform) {
-        int count = fUniforms.count();
-        for (int i = 0; i < count; ++i) {
-            GL_CALL(BindUniformLocation(programId, i, fUniforms[i].fVariable.c_str()));
-            fUniforms[i].fLocation = i;
-        }
+void GrGLProgramBuilder::bindUniformLocations(GrGLuint programID) {
+    int count = fUniforms.count();
+    for (int i = 0; i < count; ++i) {
+        GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_str()));
+        fUniforms[i].fLocation = i;
     }
 }
 
-void GrGLProgramBuilder::resolveProgramLocations(GrGLuint programId) {
-    bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL;
-    if (!usingBindUniform) {
-        int count = fUniforms.count();
-        for (int i = 0; i < count; ++i) {
-            GrGLint location;
-            GL_CALL_RET(location,
-                        GetUniformLocation(programId, fUniforms[i].fVariable.c_str()));
-            fUniforms[i].fLocation = location;
+bool GrGLProgramBuilder::checkLinkStatus(GrGLuint 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()));
+            SkDebugf((char*)log.get());
         }
+        SkDEBUGFAIL("Error linking program");
+        GL_CALL(DeleteProgram(programID));
+        programID = 0;
     }
+    return SkToBool(linked);
+}
 
-    int count = fSeparableVaryingInfos.count();
+void GrGLProgramBuilder::resolveUniformLocations(GrGLuint programID) {
+    int count = fUniforms.count();
     for (int i = 0; i < count; ++i) {
         GrGLint location;
-        GL_CALL_RET(location,
-                    GetProgramResourceLocation(programId,
-                                               GR_GL_FRAGMENT_INPUT,
-                                               fSeparableVaryingInfos[i].fVariable.c_str()));
-        fSeparableVaryingInfos[i].fLocation = location;
+        GL_CALL_RET(location, GetUniformLocation(programID, fUniforms[i].fVariable.c_str()));
+        fUniforms[i].fLocation = location;
     }
 }
 
-const GrGLContextInfo& GrGLProgramBuilder::ctxInfo() const {
-    return fGpu->ctxInfo();
+void GrGLProgramBuilder::cleanupProgram(GrGLuint programID, const SkTDArray<GrGLuint>& shaderIDs) {
+    GL_CALL(DeleteProgram(programID));
+    cleanupShaders(shaderIDs);
+}
+void GrGLProgramBuilder::cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs) {
+    for (int i = 0; i < shaderIDs.count(); ++i) {
+      GL_CALL(DeleteShader(shaderIDs[i]));
+    }
+}
+
+GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) {
+    return SkNEW_ARGS(GrGLProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
+                                    fGeometryProcessor, fFragmentProcessors.get()));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+GrGLInstalledFragProcs::~GrGLInstalledFragProcs() {
+    int numProcs = fProcs.count();
+    for (int e = 0; e < numProcs; ++e) {
+        SkDELETE(fProcs[e]);
+    }
 }