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