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