• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "src/gpu/ganesh/glsl/GrGLSLProgramBuilder.h"
8 
9 #include "include/core/SkSpan.h"
10 #include "include/core/SkString.h"
11 #include "include/gpu/ganesh/GrTypes.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkTemplates.h"
14 #include "src/core/SkSLTypeShared.h"
15 #include "src/gpu/ganesh/GrFragmentProcessor.h"
16 #include "src/gpu/ganesh/GrGeometryProcessor.h"
17 #include "src/gpu/ganesh/GrPipeline.h"
18 #include "src/gpu/ganesh/GrResourceHandle.h"
19 #include "src/gpu/ganesh/GrShaderCaps.h"
20 #include "src/gpu/ganesh/GrSurfaceProxy.h"
21 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
22 #include "src/gpu/ganesh/GrTextureProxy.h"
23 #include "src/gpu/ganesh/GrXferProcessor.h"
24 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
25 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
26 #include "src/sksl/SkSLCompiler.h"
27 #include "src/sksl/SkSLString.h"
28 
29 #include <functional>
30 #include <memory>
31 #include <tuple>
32 #include <unordered_map>
33 #include <utility>
34 
35 
36 using namespace skia_private;
37 
38 const int GrGLSLProgramBuilder::kVarsPerBlock = 8;
39 
GrGLSLProgramBuilder(const GrProgramDesc & desc,const GrProgramInfo & programInfo)40 GrGLSLProgramBuilder::GrGLSLProgramBuilder(const GrProgramDesc& desc,
41                                            const GrProgramInfo& programInfo)
42         : fVS(this)
43         , fFS(this)
44         , fDesc(desc)
45         , fProgramInfo(programInfo)
46         , fNumFragmentSamplers(0) {}
47 
48 GrGLSLProgramBuilder::~GrGLSLProgramBuilder() = default;
49 
addFeature(GrShaderFlags shaders,uint32_t featureBit,const char * extensionName)50 void GrGLSLProgramBuilder::addFeature(GrShaderFlags shaders,
51                                       uint32_t featureBit,
52                                       const char* extensionName) {
53     if (shaders & kVertex_GrShaderFlag) {
54         fVS.addFeature(featureBit, extensionName);
55     }
56     if (shaders & kFragment_GrShaderFlag) {
57         fFS.addFeature(featureBit, extensionName);
58     }
59 }
60 
emitAndInstallProcs()61 bool GrGLSLProgramBuilder::emitAndInstallProcs() {
62     // First we loop over all of the installed processors and collect coord transforms.  These will
63     // be sent to the ProgramImpl in its emitCode function
64     SkString inputColor;
65     SkString inputCoverage;
66     if (!this->emitAndInstallPrimProc(&inputColor, &inputCoverage)) {
67         return false;
68     }
69     if (!this->emitAndInstallDstTexture()) {
70         return false;
71     }
72     if (!this->emitAndInstallFragProcs(&inputColor, &inputCoverage)) {
73         return false;
74     }
75     if (!this->emitAndInstallXferProc(inputColor, inputCoverage)) {
76         return false;
77     }
78     fGPImpl->emitTransformCode(&fVS, this->uniformHandler());
79 
80     return this->checkSamplerCounts();
81 }
82 
emitAndInstallPrimProc(SkString * outputColor,SkString * outputCoverage)83 bool GrGLSLProgramBuilder::emitAndInstallPrimProc(SkString* outputColor, SkString* outputCoverage) {
84     const GrGeometryProcessor& geomProc = this->geometryProcessor();
85 
86     // Program builders have a bit of state we need to clear with each effect
87     this->advanceStage();
88     this->nameExpression(outputColor, "outputColor");
89     this->nameExpression(outputCoverage, "outputCoverage");
90 
91     SkASSERT(!fUniformHandles.fRTAdjustmentUni.isValid());
92     fUniformHandles.fRTAdjustmentUni = this->uniformHandler()->addUniform(
93             nullptr, kVertex_GrShaderFlag, SkSLType::kFloat4, SkSL::Compiler::RTADJUST_NAME);
94 
95 #if defined(SK_DEBUG)
96     fFS.codeAppendf("// Stage %d, %s\n", fStageIndex, geomProc.name());
97     fVS.codeAppendf("// Primitive Processor %s\n", geomProc.name());
98 #endif
99 
100     SkASSERT(!fGPImpl);
101     fGPImpl = geomProc.makeProgramImpl(*this->shaderCaps());
102 
103     AutoSTArray<4, SamplerHandle> texSamplers(geomProc.numTextureSamplers());
104     for (int i = 0; i < geomProc.numTextureSamplers(); ++i) {
105         SkString name;
106         name.printf("TextureSampler_%d", i);
107         const auto& sampler = geomProc.textureSampler(i);
108         texSamplers[i] = this->emitSampler(sampler.backendFormat(),
109                                            sampler.samplerState(),
110                                            sampler.swizzle(),
111                                            name.c_str());
112         if (!texSamplers[i].isValid()) {
113             return false;
114         }
115     }
116 
117     GrGeometryProcessor::ProgramImpl::EmitArgs args(&fVS,
118                                                     &fFS,
119                                                     this->varyingHandler(),
120                                                     this->uniformHandler(),
121                                                     this->shaderCaps(),
122                                                     geomProc,
123                                                     outputColor->c_str(),
124                                                     outputCoverage->c_str(),
125                                                     texSamplers.get());
126     std::tie(fFPCoordsMap, fLocalCoordsVar) = fGPImpl->emitCode(args, this->pipeline());
127 
128     // We have to check that effects and the code they emit are consistent, ie if an effect
129     // asks for dst color, then the emit code needs to follow suit
130     SkDEBUGCODE(verify(geomProc);)
131 
132     return true;
133 }
134 
emitAndInstallFragProcs(SkString * color,SkString * coverage)135 bool GrGLSLProgramBuilder::emitAndInstallFragProcs(SkString* color, SkString* coverage) {
136     int fpCount = this->pipeline().numFragmentProcessors();
137     SkASSERT(fFPImpls.empty());
138     fFPImpls.reserve(fpCount);
139     for (int i = 0; i < fpCount; ++i) {
140         SkString* inOut = this->pipeline().isColorFragmentProcessor(i) ? color : coverage;
141         SkString output;
142         const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i);
143         fFPImpls.push_back(fp.makeProgramImpl());
144         output = this->emitRootFragProc(fp, *fFPImpls.back(), *inOut, output);
145         if (output.isEmpty()) {
146             return false;
147         }
148         *inOut = std::move(output);
149     }
150     return true;
151 }
152 
emitTextureSamplersForFPs(const GrFragmentProcessor & fp,GrFragmentProcessor::ProgramImpl & impl,int * samplerIndex)153 bool GrGLSLProgramBuilder::emitTextureSamplersForFPs(const GrFragmentProcessor& fp,
154                                                      GrFragmentProcessor::ProgramImpl& impl,
155                                                      int* samplerIndex) {
156     bool ok = true;
157     fp.visitWithImpls([&](const GrFragmentProcessor& fp, GrFragmentProcessor::ProgramImpl& impl) {
158         if (const GrTextureEffect* te = fp.asTextureEffect()) {
159             SkString name = SkStringPrintf("TextureSampler_%d", *samplerIndex);
160             *samplerIndex += 1;
161 
162             GrSamplerState samplerState = te->samplerState();
163             const GrBackendFormat& format = te->view().proxy()->backendFormat();
164             skgpu::Swizzle swizzle = te->view().swizzle();
165             SamplerHandle handle = this->emitSampler(format, samplerState, swizzle, name.c_str());
166             if (!handle.isValid()) {
167                 ok = false;
168                 return;
169             }
170             static_cast<GrTextureEffect::Impl&>(impl).setSamplerHandle(handle);
171         }
172     }, impl);
173 
174     return ok;
175 }
176 
invokeFP(const GrFragmentProcessor & fp,const GrFragmentProcessor::ProgramImpl & impl,const char * inputColor,const char * destColor,const char * coords) const177 std::string GrGLSLProgramBuilder::invokeFP(const GrFragmentProcessor& fp,
178                                            const GrFragmentProcessor::ProgramImpl& impl,
179                                            const char* inputColor,
180                                            const char* destColor,
181                                            const char* coords) const {
182     if (fp.isBlendFunction()) {
183         if (this->fragmentProcessorHasCoordsParam(&fp)) {
184             return SkSL::String::printf("%s(%s, %s, %s)", impl.functionName(), inputColor,
185                                                           destColor, coords);
186         } else {
187             return SkSL::String::printf("%s(%s, %s)", impl.functionName(), inputColor, destColor);
188         }
189     }
190 
191     if (this->fragmentProcessorHasCoordsParam(&fp)) {
192         return SkSL::String::printf("%s(%s, %s)", impl.functionName(), inputColor, coords);
193     } else {
194         return SkSL::String::printf("%s(%s)", impl.functionName(), inputColor);
195     }
196 }
197 
emitRootFragProc(const GrFragmentProcessor & fp,GrFragmentProcessor::ProgramImpl & impl,const SkString & input,SkString output)198 SkString GrGLSLProgramBuilder::emitRootFragProc(const GrFragmentProcessor& fp,
199                                                 GrFragmentProcessor::ProgramImpl& impl,
200                                                 const SkString& input,
201                                                 SkString output) {
202     SkASSERT(input.size());
203 
204     // Program builders have a bit of state we need to clear with each effect
205     this->advanceStage();
206     this->nameExpression(&output, "output");
207     fFS.codeAppendf("half4 %s;", output.c_str());
208     int samplerIndex = 0;
209     if (!this->emitTextureSamplersForFPs(fp, impl, &samplerIndex)) {
210         return {};
211     }
212 
213     this->writeFPFunction(fp, impl);
214 
215     fFS.codeAppendf(
216             "%s = %s;",
217             output.c_str(),
218             this->invokeFP(fp, impl, input.c_str(), "half4(1)", fLocalCoordsVar.c_str()).c_str());
219 
220     // We have to check that effects and the code they emit are consistent, ie if an effect asks
221     // for dst color, then the emit code needs to follow suit
222     SkDEBUGCODE(verify(fp);)
223 
224     return output;
225 }
226 
writeChildFPFunctions(const GrFragmentProcessor & fp,GrFragmentProcessor::ProgramImpl & impl)227 void GrGLSLProgramBuilder::writeChildFPFunctions(const GrFragmentProcessor& fp,
228                                                  GrFragmentProcessor::ProgramImpl& impl) {
229     fSubstageIndices.push_back(0);
230     for (int i = 0; i < impl.numChildProcessors(); ++i) {
231         GrFragmentProcessor::ProgramImpl* childImpl = impl.childProcessor(i);
232         if (!childImpl) {
233             continue;
234         }
235 
236         const GrFragmentProcessor* childFP = fp.childProcessor(i);
237         SkASSERT(childFP);
238 
239         this->writeFPFunction(*childFP, *childImpl);
240         ++fSubstageIndices.back();
241     }
242     fSubstageIndices.pop_back();
243 }
244 
writeFPFunction(const GrFragmentProcessor & fp,GrFragmentProcessor::ProgramImpl & impl)245 void GrGLSLProgramBuilder::writeFPFunction(const GrFragmentProcessor& fp,
246                                            GrFragmentProcessor::ProgramImpl& impl) {
247     constexpr const char*       kDstColor    = "_dst";
248               const char* const inputColor   = fp.isBlendFunction() ? "_src" : "_input";
249               const char*       sampleCoords = "_coords";
250     fFS.nextStage();
251     // Conceptually, an FP is always sampled at a particular coordinate. However, if it is only
252     // sampled by a chain of uniform matrix expressions (or legacy coord transforms), the value that
253     // would have been passed to _coords is lifted to the vertex shader and
254     // varying. In that case it uses that variable and we do not pass a second argument for _coords.
255     GrShaderVar params[3];
256     int numParams = 0;
257 
258     params[numParams++] = GrShaderVar(inputColor, SkSLType::kHalf4);
259 
260     if (fp.isBlendFunction()) {
261         // Blend functions take a dest color as input.
262         params[numParams++] = GrShaderVar(kDstColor, SkSLType::kHalf4);
263     }
264 
265     auto fpCoordsIter = fFPCoordsMap.find(&fp);
266     if (fpCoordsIter == fFPCoordsMap.end()) {
267         // This FP isn't in our coords map at all, so its coords (if any) couldn't have been lifted
268         // to a varying.
269         if (fp.usesSampleCoords()) {
270             params[numParams++] = GrShaderVar(sampleCoords, SkSLType::kFloat2);
271         }
272     } else if (fpCoordsIter->second.hasCoordsParam) {
273         // This FP is in our map, and it takes an explicit coords param.
274         params[numParams++] = GrShaderVar(sampleCoords, SkSLType::kFloat2);
275     } else {
276         // Either doesn't use coords at all or sampled through a chain of passthrough/matrix
277         // samples usages. In the latter case the coords are emitted in the vertex shader as a
278         // varying, so this only has to access it. Add a float2 _coords variable that maps to the
279         // associated varying and replaces the absent 2nd argument to the fp's function.
280         GrShaderVar varying = fpCoordsIter->second.coordsVarying;
281 
282         switch (varying.getType()) {
283             case SkSLType::kVoid:
284                 SkASSERT(!fp.usesSampleCoordsDirectly());
285                 break;
286             case SkSLType::kFloat2:
287                 // Just point the local coords to the varying
288                 sampleCoords = varying.getName().c_str();
289                 break;
290             case SkSLType::kFloat3:
291                 // Must perform the perspective divide in the frag shader based on the
292                 // varying, and since we won't actually have a function parameter for local
293                 // coords, add it as a local variable.
294                 fFS.codeAppendf("float2 %s = %s.xy / %s.z;\n",
295                                 sampleCoords,
296                                 varying.getName().c_str(),
297                                 varying.getName().c_str());
298                 break;
299             default:
300                 SkDEBUGFAILF("Unexpected varying type for coord: %s %d\n",
301                              varying.getName().c_str(),
302                              (int)varying.getType());
303                 break;
304         }
305     }
306 
307     SkASSERT(numParams <= (int)std::size(params));
308 
309     // First, emit every child's function. This needs to happen (even for children that aren't
310     // sampled), so that all of the expected uniforms are registered.
311     this->writeChildFPFunctions(fp, impl);
312     GrFragmentProcessor::ProgramImpl::EmitArgs args(&fFS,
313                                                     this->uniformHandler(),
314                                                     this->shaderCaps(),
315                                                     fp,
316                                                     inputColor,
317                                                     kDstColor,
318                                                     sampleCoords);
319 
320     impl.emitCode(args);
321     impl.setFunctionName(fFS.getMangledFunctionName(args.fFp.name()));
322 
323     fFS.emitFunction(SkSLType::kHalf4,
324                      impl.functionName(),
325                      SkSpan(params, numParams),
326                      fFS.code().c_str());
327     fFS.deleteStage();
328 }
329 
emitAndInstallDstTexture()330 bool GrGLSLProgramBuilder::emitAndInstallDstTexture() {
331     fDstTextureOrigin = kTopLeft_GrSurfaceOrigin;
332 
333     const GrSurfaceProxyView& dstView = this->pipeline().dstProxyView();
334     if (this->pipeline().usesDstTexture()) {
335         // Set up a sampler handle for the destination texture.
336         GrTextureProxy* dstTextureProxy = dstView.asTextureProxy();
337         SkASSERT(dstTextureProxy);
338         const skgpu::Swizzle& swizzle = dstView.swizzle();
339         fDstTextureSamplerHandle = this->emitSampler(dstTextureProxy->backendFormat(),
340                                                     GrSamplerState(), swizzle, "DstTextureSampler");
341         if (!fDstTextureSamplerHandle.isValid()) {
342             return false;
343         }
344         fDstTextureOrigin = dstView.origin();
345         SkASSERT(dstTextureProxy->textureType() != GrTextureType::kExternal);
346 
347         // Declare a _dstColor global variable which samples from the dest-texture sampler at the
348         // top of the fragment shader.
349         const char* dstTextureCoordsName;
350         fUniformHandles.fDstTextureCoordsUni = this->uniformHandler()->addUniform(
351                 /*owner=*/nullptr,
352                 kFragment_GrShaderFlag,
353                 SkSLType::kHalf4,
354                 "DstTextureCoords",
355                 &dstTextureCoordsName);
356 #if defined(SK_DEBUG)
357         fFS.codeAppend("// Read color from copy of the destination\n");
358 #endif
359         if (dstTextureProxy->textureType() == GrTextureType::k2D) {
360             fFS.codeAppendf("float2 _dstTexCoord = (sk_FragCoord.xy - %s.xy) * %s.zw;\n",
361                     dstTextureCoordsName, dstTextureCoordsName);
362             if (fDstTextureOrigin == kBottomLeft_GrSurfaceOrigin) {
363                 fFS.codeAppend("_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n");
364             }
365         } else {
366             SkASSERT(dstTextureProxy->textureType() == GrTextureType::kRectangle);
367             fFS.codeAppendf("float2 _dstTexCoord = sk_FragCoord.xy - %s.xy;\n",
368                     dstTextureCoordsName);
369             if (fDstTextureOrigin == kBottomLeft_GrSurfaceOrigin) {
370                 // When the texture type is kRectangle, instead of a scale stored in the zw of the
371                 // uniform, we store the height in z so we can flip the coord here.
372                 fFS.codeAppendf("_dstTexCoord.y = %s.z - _dstTexCoord.y;\n", dstTextureCoordsName);
373             }
374         }
375         const char* dstColor = fFS.dstColor();
376         SkString dstColorDecl = SkStringPrintf("half4 %s;", dstColor);
377         fFS.definitionAppend(dstColorDecl.c_str());
378         fFS.codeAppendf("%s = ", dstColor);
379         fFS.appendTextureLookup(fDstTextureSamplerHandle, "_dstTexCoord");
380         fFS.codeAppend(";\n");
381     } else if (this->pipeline().usesDstInputAttachment()) {
382         // Set up an input attachment for the destination texture.
383         const skgpu::Swizzle& swizzle = dstView.swizzle();
384         fDstTextureSamplerHandle = this->emitInputSampler(swizzle, "DstTextureInput");
385         if (!fDstTextureSamplerHandle.isValid()) {
386             return false;
387         }
388 
389         // Populate the _dstColor variable by loading from the input attachment at the top of the
390         // fragment shader.
391 #if defined(SK_DEBUG)
392         fFS.codeAppend("// Read color from input attachment\n");
393 #endif
394         const char* dstColor = fFS.dstColor();
395         SkString dstColorDecl = SkStringPrintf("half4 %s;", dstColor);
396         fFS.definitionAppend(dstColorDecl.c_str());
397         fFS.codeAppendf("%s = ", dstColor);
398         fFS.appendInputLoad(fDstTextureSamplerHandle);
399         fFS.codeAppend(";\n");
400     }
401 
402     return true;
403 }
404 
emitAndInstallXferProc(const SkString & colorIn,const SkString & coverageIn)405 bool GrGLSLProgramBuilder::emitAndInstallXferProc(const SkString& colorIn,
406                                                   const SkString& coverageIn) {
407     // Program builders have a bit of state we need to clear with each effect
408     this->advanceStage();
409 
410     SkASSERT(!fXPImpl);
411     const GrXferProcessor& xp = this->pipeline().getXferProcessor();
412     fXPImpl = xp.makeProgramImpl();
413 
414     // Enable dual source secondary output if we have one
415     if (xp.hasSecondaryOutput()) {
416         fFS.enableSecondaryOutput();
417     }
418 
419     SkString openBrace;
420 #if defined(SK_DEBUG)
421     openBrace.printf("{ // Xfer Processor: %s\n", xp.name());
422 #else
423     openBrace.printf("{\n");
424 #endif
425     fFS.codeAppend(openBrace.c_str());
426 
427     SkString finalInColor = colorIn.size() ? colorIn : SkString("float4(1)");
428 
429     GrXferProcessor::ProgramImpl::EmitArgs args(
430             &fFS,
431             this->uniformHandler(),
432             this->shaderCaps(),
433             xp,
434             finalInColor.c_str(),
435             coverageIn.size() ? coverageIn.c_str() : "float4(1)",
436             fFS.getPrimaryColorOutputName(),
437             fFS.getSecondaryColorOutputName(),
438             fDstTextureSamplerHandle,
439             fDstTextureOrigin,
440             this->pipeline().writeSwizzle());
441     fXPImpl->emitCode(args);
442 
443     // We have to check that effects and the code they emit are consistent, ie if an effect
444     // asks for dst color, then the emit code needs to follow suit
445     SkDEBUGCODE(verify(xp);)
446     fFS.codeAppend("}");
447     return true;
448 }
449 
emitSampler(const GrBackendFormat & backendFormat,GrSamplerState state,const skgpu::Swizzle & swizzle,const char * name)450 GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(
451         const GrBackendFormat& backendFormat, GrSamplerState state, const skgpu::Swizzle& swizzle,
452         const char* name) {
453     ++fNumFragmentSamplers;
454     return this->uniformHandler()->addSampler(backendFormat, state, swizzle, name,
455                                               this->shaderCaps());
456 }
457 
emitInputSampler(const skgpu::Swizzle & swizzle,const char * name)458 GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitInputSampler(
459         const skgpu::Swizzle& swizzle, const char* name) {
460     return this->uniformHandler()->addInputSampler(swizzle, name);
461 }
462 
checkSamplerCounts()463 bool GrGLSLProgramBuilder::checkSamplerCounts() {
464     const GrShaderCaps& shaderCaps = *this->shaderCaps();
465     if (fNumFragmentSamplers > shaderCaps.fMaxFragmentSamplers) {
466         GrCapsDebugf(this->caps(), "Program would use too many fragment samplers\n");
467         return false;
468     }
469     return true;
470 }
471 
472 #ifdef SK_DEBUG
verify(const GrGeometryProcessor & geomProc)473 void GrGLSLProgramBuilder::verify(const GrGeometryProcessor& geomProc) {
474     SkASSERT(!fFS.fHasReadDstColorThisStage_DebugOnly);
475 }
476 
verify(const GrFragmentProcessor & fp)477 void GrGLSLProgramBuilder::verify(const GrFragmentProcessor& fp) {
478     SkASSERT(fp.willReadDstColor() == fFS.fHasReadDstColorThisStage_DebugOnly);
479 }
480 
verify(const GrXferProcessor & xp)481 void GrGLSLProgramBuilder::verify(const GrXferProcessor& xp) {
482     SkASSERT(xp.willReadDstColor() == fFS.fHasReadDstColorThisStage_DebugOnly);
483 }
484 #endif
485 
getMangleSuffix() const486 SkString GrGLSLProgramBuilder::getMangleSuffix() const {
487     SkASSERT(fStageIndex >= 0);
488     SkString suffix;
489     suffix.printf("_S%d", fStageIndex);
490     for (auto c : fSubstageIndices) {
491         suffix.appendf("_c%d", c);
492     }
493     return suffix;
494 }
495 
nameVariable(char prefix,const char * name,bool mangle)496 SkString GrGLSLProgramBuilder::nameVariable(char prefix, const char* name, bool mangle) {
497     SkString out;
498     if ('\0' == prefix) {
499         out = name;
500     } else {
501         out.printf("%c%s", prefix, name);
502     }
503     if (mangle) {
504         SkString suffix = this->getMangleSuffix();
505         // Names containing "__" are reserved; add "x" if needed to avoid consecutive underscores.
506         const char *underscoreSplitter = out.endsWith('_') ? "x" : "";
507         out.appendf("%s%s", underscoreSplitter, suffix.c_str());
508     }
509     return out;
510 }
511 
nameExpression(SkString * output,const char * baseName)512 void GrGLSLProgramBuilder::nameExpression(SkString* output, const char* baseName) {
513     // Name a variable to hold stage result. If we already have a valid output name, use that as-is;
514     // otherwise, create a new mangled one.
515     if (output->isEmpty()) {
516         *output = this->nameVariable(/*prefix=*/'\0', baseName);
517     }
518 }
519 
appendUniformDecls(GrShaderFlags visibility,SkString * out) const520 void GrGLSLProgramBuilder::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
521     this->uniformHandler()->appendUniformDecls(visibility, out);
522 }
523 
addRTFlipUniform(const char * name)524 void GrGLSLProgramBuilder::addRTFlipUniform(const char* name) {
525     SkASSERT(!fUniformHandles.fRTFlipUni.isValid());
526     GrGLSLUniformHandler* uniformHandler = this->uniformHandler();
527     fUniformHandles.fRTFlipUni =
528             uniformHandler->internalAddUniformArray(nullptr,
529                                                     kFragment_GrShaderFlag,
530                                                     SkSLType::kFloat2,
531                                                     name,
532                                                     false,
533                                                     0,
534                                                     nullptr);
535 }
536 
fragmentProcessorHasCoordsParam(const GrFragmentProcessor * fp) const537 bool GrGLSLProgramBuilder::fragmentProcessorHasCoordsParam(const GrFragmentProcessor* fp) const {
538     auto iter = fFPCoordsMap.find(fp);
539     return (iter != fFPCoordsMap.end()) ? iter->second.hasCoordsParam
540                                         : fp->usesSampleCoords();
541 }
542 
finalizeShaders()543 void GrGLSLProgramBuilder::finalizeShaders() {
544     this->varyingHandler()->finalize();
545     fVS.finalize(kVertex_GrShaderFlag);
546     fFS.finalize(kFragment_GrShaderFlag);
547 }
548