| Index: src/gpu/gl/builders/GrGLProgramBuilder.cpp
 | 
| diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
 | 
| index 4360f7858d2129171fb19382dfb02bb0e1e64d34..4503d1a1972d49f2c259d31ae41b4d44b469175a 100644
 | 
| --- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
 | 
| +++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
 | 
| @@ -42,7 +42,10 @@ GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp
 | 
|      GrGLSLExpr4 inputColor;
 | 
|      GrGLSLExpr4 inputCoverage;
 | 
|  
 | 
| -    if (!pb->emitAndInstallProcs(&inputColor, &inputCoverage)) {
 | 
| +    if (!pb->emitAndInstallProcs(&inputColor,
 | 
| +                                 &inputCoverage,
 | 
| +                                 gpu->glCaps().maxFragmentTextureUnits())) {
 | 
| +        pb->cleanupFragmentProcessors();
 | 
|          return nullptr;
 | 
|      }
 | 
|  
 | 
| @@ -53,243 +56,18 @@ GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp
 | 
|  
 | 
|  GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu, const DrawArgs& args)
 | 
|      : INHERITED(args)
 | 
| -    , fGeometryProcessor(nullptr)
 | 
| -    , fXferProcessor(nullptr)
 | 
|      , fGpu(gpu)
 | 
|      , fSamplerUniforms(4)
 | 
|      , fVaryingHandler(this)
 | 
|      , fUniformHandler(this) {
 | 
|  }
 | 
|  
 | 
| -const GrGLSLCaps* GrGLProgramBuilder::glslCaps() const {
 | 
| -    return this->fGpu->ctxInfo().caps()->glslCaps();
 | 
| -}
 | 
| -
 | 
| -bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) {
 | 
| -    // First we loop over all of the installed processors and collect coord transforms.  These will
 | 
| -    // be sent to the GrGLSLPrimitiveProcessor in its emitCode function
 | 
| -    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
 | 
| -    int totalTextures = primProc.numTextures();
 | 
| -    const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits();
 | 
| -
 | 
| -    for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) {
 | 
| -        const GrFragmentProcessor& processor = this->pipeline().getFragmentProcessor(i);
 | 
| -
 | 
| -        if (!primProc.hasTransformedLocalCoords()) {
 | 
| -            SkTArray<const GrCoordTransform*, true>& procCoords = fCoordTransforms.push_back();
 | 
| -            processor.gatherCoordTransforms(&procCoords);
 | 
| -        }
 | 
| -
 | 
| -        totalTextures += processor.numTextures();
 | 
| -        if (totalTextures >= maxTextureUnits) {
 | 
| -            GrCapsDebugf(fGpu->caps(), "Program would use too many texture units\n");
 | 
| -            return false;
 | 
| -        }
 | 
| -    }
 | 
| -
 | 
| -    this->emitAndInstallProc(primProc, inputColor, inputCoverage);
 | 
| -
 | 
| -    fFragmentProcessors.reset(new GrGLInstalledFragProcs);
 | 
| -    int numProcs = this->pipeline().numFragmentProcessors();
 | 
| -    this->emitAndInstallFragProcs(0, this->pipeline().numColorFragmentProcessors(), inputColor);
 | 
| -    this->emitAndInstallFragProcs(this->pipeline().numColorFragmentProcessors(), numProcs,
 | 
| -                                  inputCoverage);
 | 
| -    this->emitAndInstallXferProc(this->pipeline().getXferProcessor(), *inputColor, *inputCoverage,
 | 
| -                                 this->pipeline().ignoresCoverage());
 | 
| -    this->emitFSOutputSwizzle(this->pipeline().getXferProcessor().hasSecondaryOutput());
 | 
| -    return true;
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset,
 | 
| -                                                 int numProcs,
 | 
| -                                                 GrGLSLExpr4* inOut) {
 | 
| -    for (int i = procOffset; i < numProcs; ++i) {
 | 
| -        GrGLSLExpr4 output;
 | 
| -        const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i);
 | 
| -        this->emitAndInstallProc(fp, i, *inOut, &output);
 | 
| -        *inOut = output;
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::nameExpression(GrGLSLExpr4* output, const char* baseName) {
 | 
| -    // create var to hold stage result.  If we already have a valid output name, just use that
 | 
| -    // otherwise create a new mangled one.  This name is only valid if we are reordering stages
 | 
| -    // and have to tell stage exactly where to put its output.
 | 
| -    SkString outName;
 | 
| -    if (output->isValid()) {
 | 
| -        outName = output->c_str();
 | 
| -    } else {
 | 
| -        this->nameVariable(&outName, '\0', baseName);
 | 
| -    }
 | 
| -    fFS.codeAppendf("vec4 %s;", outName.c_str());
 | 
| -    *output = outName;
 | 
| -}
 | 
| -
 | 
| -// TODO Processors cannot output zeros because an empty string is all 1s
 | 
| -// the fix is to allow effects to take the GrGLSLExpr4 directly
 | 
| -void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentProcessor& fp,
 | 
| -                                            int index,
 | 
| -                                            const GrGLSLExpr4& input,
 | 
| -                                            GrGLSLExpr4* output) {
 | 
| -    // Program builders have a bit of state we need to clear with each effect
 | 
| -    AutoStageAdvance adv(this);
 | 
| -    this->nameExpression(output, "output");
 | 
| -
 | 
| -    // Enclose custom code in a block to avoid namespace conflicts
 | 
| -    SkString openBrace;
 | 
| -    openBrace.printf("{ // Stage %d, %s\n", fStageIndex, fp.name());
 | 
| -    fFS.codeAppend(openBrace.c_str());
 | 
| -
 | 
| -    this->emitAndInstallProc(fp, index, output->c_str(), input.isOnes() ? nullptr : input.c_str());
 | 
| -
 | 
| -    fFS.codeAppend("}");
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::emitAndInstallProc(const GrPrimitiveProcessor& proc,
 | 
| -                                            GrGLSLExpr4* outputColor,
 | 
| -                                            GrGLSLExpr4* outputCoverage) {
 | 
| -    // Program builders have a bit of state we need to clear with each effect
 | 
| -    AutoStageAdvance adv(this);
 | 
| -    this->nameExpression(outputColor, "outputColor");
 | 
| -    this->nameExpression(outputCoverage, "outputCoverage");
 | 
| -
 | 
| -    // Enclose custom code in a block to avoid namespace conflicts
 | 
| -    SkString openBrace;
 | 
| -    openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name());
 | 
| -    fFS.codeAppend(openBrace.c_str());
 | 
| -    fVS.codeAppendf("// Primitive Processor %s\n", proc.name());
 | 
| -
 | 
| -    this->emitAndInstallProc(proc, outputColor->c_str(), outputCoverage->c_str());
 | 
| -
 | 
| -    fFS.codeAppend("}");
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentProcessor& fp,
 | 
| -                                            int index,
 | 
| -                                            const char* outColor,
 | 
| -                                            const char* inColor) {
 | 
| -    GrGLInstalledFragProc* ifp = new GrGLInstalledFragProc;
 | 
| -
 | 
| -    ifp->fGLProc.reset(fp.createGLSLInstance());
 | 
| -
 | 
| -    SkSTArray<4, GrGLSLTextureSampler> samplers(fp.numTextures());
 | 
| -    this->emitSamplers(fp, &samplers, ifp);
 | 
| -
 | 
| -    GrGLSLFragmentProcessor::EmitArgs args(&fFS,
 | 
| -                                           &fUniformHandler,
 | 
| -                                           this->glslCaps(),
 | 
| -                                           fp,
 | 
| -                                           outColor,
 | 
| -                                           inColor,
 | 
| -                                           fOutCoords[index],
 | 
| -                                           samplers);
 | 
| -    ifp->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(fp);
 | 
| -    fFragmentProcessors->fProcs.push_back(ifp);
 | 
| +const GrCaps* GrGLProgramBuilder::caps() const {
 | 
| +    return fGpu->caps();
 | 
|  }
 | 
|  
 | 
| -void GrGLProgramBuilder::emitAndInstallProc(const GrPrimitiveProcessor& gp,
 | 
| -                                            const char* outColor,
 | 
| -                                            const char* outCoverage) {
 | 
| -    SkASSERT(!fGeometryProcessor);
 | 
| -    fGeometryProcessor = new GrGLInstalledGeoProc;
 | 
| -
 | 
| -    fGeometryProcessor->fGLProc.reset(gp.createGLSLInstance(*fGpu->glCaps().glslCaps()));
 | 
| -
 | 
| -    SkSTArray<4, GrGLSLTextureSampler> samplers(gp.numTextures());
 | 
| -    this->emitSamplers(gp, &samplers, fGeometryProcessor);
 | 
| -
 | 
| -    GrGLSLGeometryProcessor::EmitArgs args(&fVS,
 | 
| -                                           &fFS,
 | 
| -                                           &fVaryingHandler,
 | 
| -                                           &fUniformHandler,
 | 
| -                                           this->glslCaps(),
 | 
| -                                           gp,
 | 
| -                                           outColor,
 | 
| -                                           outCoverage,
 | 
| -                                           samplers,
 | 
| -                                           fCoordTransforms,
 | 
| -                                           &fOutCoords);
 | 
| -    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::emitAndInstallXferProc(const GrXferProcessor& xp,
 | 
| -                                                const GrGLSLExpr4& colorIn,
 | 
| -                                                const GrGLSLExpr4& coverageIn,
 | 
| -                                                bool ignoresCoverage) {
 | 
| -    // Program builders have a bit of state we need to clear with each effect
 | 
| -    AutoStageAdvance adv(this);
 | 
| -
 | 
| -    SkASSERT(!fXferProcessor);
 | 
| -    fXferProcessor = new GrGLInstalledXferProc;
 | 
| -
 | 
| -    fXferProcessor->fGLProc.reset(xp.createGLSLInstance());
 | 
| -
 | 
| -    // Enable dual source secondary output if we have one
 | 
| -    if (xp.hasSecondaryOutput()) {
 | 
| -        fFS.enableSecondaryOutput();
 | 
| -    }
 | 
| -
 | 
| -    if (this->glslCaps()->mustDeclareFragmentShaderOutput()) {
 | 
| -        fFS.enableCustomOutput();
 | 
| -    }
 | 
| -
 | 
| -    SkString openBrace;
 | 
| -    openBrace.printf("{ // Xfer Processor: %s\n", xp.name());
 | 
| -    fFS.codeAppend(openBrace.c_str());
 | 
| -
 | 
| -    SkSTArray<4, GrGLSLTextureSampler> samplers(xp.numTextures());
 | 
| -    this->emitSamplers(xp, &samplers, fXferProcessor);
 | 
| -
 | 
| -    GrGLSLXferProcessor::EmitArgs args(&fFS,
 | 
| -                                       &fUniformHandler,
 | 
| -                                       this->glslCaps(),
 | 
| -                                       xp, colorIn.c_str(),
 | 
| -                                       ignoresCoverage ? nullptr : 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::emitFSOutputSwizzle(bool hasSecondaryOutput) {
 | 
| -    // Swizzle the fragment shader outputs if necessary.
 | 
| -    GrSwizzle swizzle;
 | 
| -    swizzle.setFromKey(this->desc().header().fOutputSwizzle);
 | 
| -    if (swizzle != GrSwizzle::RGBA()) {
 | 
| -        fFS.codeAppendf("%s = %s.%s;", fFS.getPrimaryColorOutputName(),
 | 
| -                        fFS.getPrimaryColorOutputName(),
 | 
| -                        swizzle.c_str());
 | 
| -        if (hasSecondaryOutput) {
 | 
| -            fFS.codeAppendf("%s = %s.%s;", fFS.getSecondaryColorOutputName(),
 | 
| -                            fFS.getSecondaryColorOutputName(),
 | 
| -                            swizzle.c_str());
 | 
| -        }
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::verify(const GrPrimitiveProcessor& gp) {
 | 
| -    SkASSERT(fFS.hasReadFragmentPosition() == gp.willReadFragmentPosition());
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::verify(const GrXferProcessor& xp) {
 | 
| -    SkASSERT(fFS.hasReadDstColor() == xp.willReadDstColor());
 | 
| -}
 | 
| -
 | 
| -void GrGLProgramBuilder::verify(const GrFragmentProcessor& fp) {
 | 
| -    SkASSERT(fFS.hasReadFragmentPosition() == fp.willReadFragmentPosition());
 | 
| +const GrGLSLCaps* GrGLProgramBuilder::glslCaps() const {
 | 
| +    return fGpu->ctxInfo().caps()->glslCaps();
 | 
|  }
 | 
|  
 | 
|  static GrSLType get_sampler_type(const GrTextureAccess& access) {
 | 
| @@ -302,11 +80,8 @@ static GrSLType get_sampler_type(const GrTextureAccess& access) {
 | 
|      }
 | 
|  }
 | 
|  
 | 
| -template <class Proc>
 | 
|  void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor,
 | 
| -                                      GrGLSLTextureSampler::TextureSamplerArray* outSamplers,
 | 
| -                                      GrGLInstalledProc<Proc>* ip) {
 | 
| -    SkDEBUGCODE(ip->fSamplersIdx = fSamplerUniforms.count();)
 | 
| +                                      GrGLSLTextureSampler::TextureSamplerArray* outSamplers) {
 | 
|      int numTextures = processor.numTextures();
 | 
|      UniformHandle* localSamplerUniforms = fSamplerUniforms.push_back_n(numTextures);
 | 
|      SkString name;
 | 
| @@ -356,6 +131,7 @@ GrGLProgram* GrGLProgramBuilder::finalize() {
 | 
|      GrGLuint programID;
 | 
|      GL_CALL_RET(programID, CreateProgram());
 | 
|      if (0 == programID) {
 | 
| +        this->cleanupFragmentProcessors();
 | 
|          return nullptr;
 | 
|      }
 | 
|  
 | 
| @@ -474,7 +250,8 @@ void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID) {
 | 
|  
 | 
|  void GrGLProgramBuilder::cleanupProgram(GrGLuint programID, const SkTDArray<GrGLuint>& shaderIDs) {
 | 
|      GL_CALL(DeleteProgram(programID));
 | 
| -    cleanupShaders(shaderIDs);
 | 
| +    this->cleanupShaders(shaderIDs);
 | 
| +    this->cleanupFragmentProcessors();
 | 
|  }
 | 
|  void GrGLProgramBuilder::cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs) {
 | 
|      for (int i = 0; i < shaderIDs.count(); ++i) {
 | 
| @@ -491,15 +268,7 @@ GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) {
 | 
|                             fVaryingHandler.fPathProcVaryingInfos,
 | 
|                             fGeometryProcessor,
 | 
|                             fXferProcessor,
 | 
| -                           fFragmentProcessors.get(),
 | 
| +                           fFragmentProcessors,
 | 
|                             &fSamplerUniforms);
 | 
|  }
 | 
|  
 | 
| -///////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| -
 | 
| -GrGLInstalledFragProcs::~GrGLInstalledFragProcs() {
 | 
| -    int numProcs = fProcs.count();
 | 
| -    for (int i = 0; i < numProcs; ++i) {
 | 
| -        delete fProcs[i];
 | 
| -    }
 | 
| -}
 | 
| 
 |