1 /*
2 * Copyright 2014 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
8 #include "src/gpu/GrGeometryProcessor.h"
9
10 #include "src/core/SkMatrixPriv.h"
11 #include "src/gpu/GrPipeline.h"
12 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
13 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
14 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
15 #include "src/gpu/glsl/GrGLSLVarying.h"
16
17 #include <queue>
18
GrGeometryProcessor(ClassID classID)19 GrGeometryProcessor::GrGeometryProcessor(ClassID classID) : GrProcessor(classID) {}
20
textureSampler(int i) const21 const GrGeometryProcessor::TextureSampler& GrGeometryProcessor::textureSampler(int i) const {
22 SkASSERT(i >= 0 && i < this->numTextureSamplers());
23 return this->onTextureSampler(i);
24 }
25
ComputeCoordTransformsKey(const GrFragmentProcessor & fp)26 uint32_t GrGeometryProcessor::ComputeCoordTransformsKey(const GrFragmentProcessor& fp) {
27 // This is highly coupled with the code in ProgramImpl::collectTransforms().
28 uint32_t key = static_cast<uint32_t>(fp.sampleUsage().kind()) << 1;
29 // This needs to be updated if GP starts specializing varyings on additional matrix types.
30 if (fp.sampleUsage().hasPerspective()) {
31 key |= 0b1;
32 }
33 return key;
34 }
35
36 ///////////////////////////////////////////////////////////////////////////////////////////////////
37
clamp_filter(GrTextureType type,GrSamplerState::Filter requestedFilter)38 static inline GrSamplerState::Filter clamp_filter(GrTextureType type,
39 GrSamplerState::Filter requestedFilter) {
40 if (GrTextureTypeHasRestrictedSampling(type)) {
41 return std::min(requestedFilter, GrSamplerState::Filter::kLinear);
42 }
43 return requestedFilter;
44 }
45
TextureSampler(GrSamplerState samplerState,const GrBackendFormat & backendFormat,const GrSwizzle & swizzle)46 GrGeometryProcessor::TextureSampler::TextureSampler(GrSamplerState samplerState,
47 const GrBackendFormat& backendFormat,
48 const GrSwizzle& swizzle) {
49 this->reset(samplerState, backendFormat, swizzle);
50 }
51
reset(GrSamplerState samplerState,const GrBackendFormat & backendFormat,const GrSwizzle & swizzle)52 void GrGeometryProcessor::TextureSampler::reset(GrSamplerState samplerState,
53 const GrBackendFormat& backendFormat,
54 const GrSwizzle& swizzle) {
55 fSamplerState = samplerState;
56 fSamplerState.setFilterMode(clamp_filter(backendFormat.textureType(), samplerState.filter()));
57 fBackendFormat = backendFormat;
58 fSwizzle = swizzle;
59 fIsInitialized = true;
60 }
61
62 //////////////////////////////////////////////////////////////////////////////
63
64 using ProgramImpl = GrGeometryProcessor::ProgramImpl;
65
emitCode(EmitArgs & args,const GrPipeline & pipeline)66 ProgramImpl::FPCoordsMap ProgramImpl::emitCode(EmitArgs& args, const GrPipeline& pipeline) {
67 GrGPArgs gpArgs;
68 this->onEmitCode(args, &gpArgs);
69
70 GrShaderVar positionVar = gpArgs.fPositionVar;
71 // skia:12198
72 if (args.fGeomProc.willUseTessellationShaders()) {
73 positionVar = {};
74 }
75 FPCoordsMap transformMap = this->collectTransforms(args.fVertBuilder,
76 args.fVaryingHandler,
77 args.fUniformHandler,
78 gpArgs.fLocalCoordVar,
79 positionVar,
80 pipeline);
81
82 if (args.fGeomProc.willUseTessellationShaders()) {
83 // Tessellation shaders are temporarily responsible for integrating their own code strings
84 // while we work out full support.
85 return transformMap;
86 }
87
88 GrGLSLVertexBuilder* vBuilder = args.fVertBuilder;
89 // Emit the vertex position to the hardware in the normalized window coordinates it expects.
90 SkASSERT(kFloat2_GrSLType == gpArgs.fPositionVar.getType() ||
91 kFloat3_GrSLType == gpArgs.fPositionVar.getType());
92 vBuilder->emitNormalizedSkPosition(gpArgs.fPositionVar.c_str(),
93 gpArgs.fPositionVar.getType());
94 if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
95 args.fVaryingHandler->setNoPerspective();
96 }
97 return transformMap;
98 }
99
collectTransforms(GrGLSLVertexBuilder * vb,GrGLSLVaryingHandler * varyingHandler,GrGLSLUniformHandler * uniformHandler,const GrShaderVar & localCoordsVar,const GrShaderVar & positionVar,const GrPipeline & pipeline)100 ProgramImpl::FPCoordsMap ProgramImpl::collectTransforms(GrGLSLVertexBuilder* vb,
101 GrGLSLVaryingHandler* varyingHandler,
102 GrGLSLUniformHandler* uniformHandler,
103 const GrShaderVar& localCoordsVar,
104 const GrShaderVar& positionVar,
105 const GrPipeline& pipeline) {
106 SkASSERT(localCoordsVar.getType() == kFloat2_GrSLType ||
107 localCoordsVar.getType() == kFloat3_GrSLType ||
108 localCoordsVar.getType() == kVoid_GrSLType);
109 SkASSERT(positionVar.getType() == kFloat2_GrSLType ||
110 positionVar.getType() == kFloat3_GrSLType ||
111 positionVar.getType() == kVoid_GrSLType);
112
113 enum class BaseCoord { kNone, kLocal, kPosition };
114
115 auto baseLocalCoordFSVar = [&, baseLocalCoord = GrGLSLVarying()]() mutable {
116 SkASSERT(GrSLTypeIsFloatType(localCoordsVar.getType()));
117 if (baseLocalCoord.type() == kVoid_GrSLType) {
118 // Initialize to the GP provided coordinate
119 baseLocalCoord = GrGLSLVarying(localCoordsVar.getType());
120 varyingHandler->addVarying("LocalCoord", &baseLocalCoord);
121 vb->codeAppendf("%s = %s;\n", baseLocalCoord.vsOut(), localCoordsVar.getName().c_str());
122 }
123 return baseLocalCoord.fsInVar();
124 };
125
126 bool canUsePosition = positionVar.getType() != kVoid_GrSLType;
127
128 FPCoordsMap result;
129 // Performs a pre-order traversal of FP hierarchy rooted at fp and identifies FPs that are
130 // sampled with a series of matrices applied to local coords. For each such FP a varying is
131 // added to the varying handler and added to 'result'.
132 auto liftTransforms = [&, traversalIndex = 0](
133 auto& self,
134 const GrFragmentProcessor& fp,
135 bool hasPerspective,
136 const GrFragmentProcessor* lastMatrixFP = nullptr,
137 int lastMatrixTraversalIndex = -1,
138 BaseCoord baseCoord = BaseCoord::kLocal) mutable -> void {
139 ++traversalIndex;
140 switch (fp.sampleUsage().kind()) {
141 case SkSL::SampleUsage::Kind::kNone:
142 // This should only happen at the root. Otherwise how did this FP get added?
143 SkASSERT(!fp.parent());
144 break;
145 case SkSL::SampleUsage::Kind::kPassThrough:
146 break;
147 case SkSL::SampleUsage::Kind::kUniformMatrix:
148 // Update tracking of last matrix and matrix props.
149 hasPerspective |= fp.sampleUsage().hasPerspective();
150 lastMatrixFP = &fp;
151 lastMatrixTraversalIndex = traversalIndex;
152 break;
153 case SkSL::SampleUsage::Kind::kFragCoord:
154 hasPerspective = positionVar.getType() == kFloat3_GrSLType;
155 lastMatrixFP = nullptr;
156 lastMatrixTraversalIndex = -1;
157 baseCoord = BaseCoord::kPosition;
158 break;
159 case SkSL::SampleUsage::Kind::kExplicit:
160 baseCoord = BaseCoord::kNone;
161 break;
162 }
163
164 auto& [varyingFSVar, hasCoordsParam] = result[&fp];
165 hasCoordsParam = fp.usesSampleCoordsDirectly();
166
167 // We add a varying if we're in a chain of matrices multiplied by local or device coords.
168 // If the coord is the untransformed local coord we add a varying. We don't if it is
169 // untransformed device coords since it doesn't save us anything over "sk_FragCoord.xy". Of
170 // course, if the FP doesn't directly use its coords then we don't add a varying.
171 if (fp.usesSampleCoordsDirectly() &&
172 (baseCoord == BaseCoord::kLocal ||
173 (baseCoord == BaseCoord::kPosition && lastMatrixFP && canUsePosition))) {
174 // Associate the varying with the highest possible node in the FP tree that shares the
175 // same coordinates so that multiple FPs in a subtree can share. If there are no matrix
176 // sample nodes on the way up the tree then directly use the local coord.
177 if (!lastMatrixFP) {
178 varyingFSVar = baseLocalCoordFSVar();
179 } else {
180 // If there is an already a varying that incorporates all matrices from the root to
181 // lastMatrixFP just use it. Otherwise, we add it.
182 auto& [varying, inputCoords, varyingIdx] = fTransformVaryingsMap[lastMatrixFP];
183 if (varying.type() == kVoid_GrSLType) {
184 varying = GrGLSLVarying(hasPerspective ? kFloat3_GrSLType : kFloat2_GrSLType);
185 SkString strVaryingName = SkStringPrintf("TransformedCoords_%d",
186 lastMatrixTraversalIndex);
187 varyingHandler->addVarying(strVaryingName.c_str(), &varying);
188 inputCoords = baseCoord == BaseCoord::kLocal ? localCoordsVar : positionVar;
189 varyingIdx = lastMatrixTraversalIndex;
190 }
191 SkASSERT(varyingIdx == lastMatrixTraversalIndex);
192 // The FP will use the varying in the fragment shader as its coords.
193 varyingFSVar = varying.fsInVar();
194 }
195 hasCoordsParam = false;
196 }
197
198 for (int c = 0; c < fp.numChildProcessors(); ++c) {
199 if (auto* child = fp.childProcessor(c)) {
200 self(self,
201 *child,
202 hasPerspective,
203 lastMatrixFP,
204 lastMatrixTraversalIndex,
205 baseCoord);
206 // If we have a varying then we never need a param. Otherwise, if one of our
207 // children takes a non-explicit coord then we'll need our coord.
208 hasCoordsParam |= varyingFSVar.getType() == kVoid_GrSLType &&
209 !child->sampleUsage().isExplicit() &&
210 !child->sampleUsage().isFragCoord() &&
211 result[child].hasCoordsParam;
212 }
213 }
214 };
215
216 bool hasPerspective = GrSLTypeVecLength(localCoordsVar.getType()) == 3;
217 for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) {
218 liftTransforms(liftTransforms, pipeline.getFragmentProcessor(i), hasPerspective);
219 }
220 return result;
221 }
222
emitTransformCode(GrGLSLVertexBuilder * vb,GrGLSLUniformHandler * uniformHandler)223 void ProgramImpl::emitTransformCode(GrGLSLVertexBuilder* vb, GrGLSLUniformHandler* uniformHandler) {
224 // Because descendant varyings may be computed using the varyings of ancestor FPs we make
225 // sure to visit the varyings according to FP pre-order traversal by dumping them into a
226 // priority queue.
227 using FPAndInfo = std::tuple<const GrFragmentProcessor*, TransformInfo>;
228 auto compare = [](const FPAndInfo& a, const FPAndInfo& b) {
229 return std::get<1>(a).traversalOrder > std::get<1>(b).traversalOrder;
230 };
231 std::priority_queue<FPAndInfo, std::vector<FPAndInfo>, decltype(compare)> pq(compare);
232 std::for_each(fTransformVaryingsMap.begin(), fTransformVaryingsMap.end(), [&pq](auto entry) {
233 pq.push(entry);
234 });
235 for (; !pq.empty(); pq.pop()) {
236 const auto& [fp, info] = pq.top();
237 // If we recorded a transform info, its sample matrix must be uniform
238 SkASSERT(fp->sampleUsage().isUniformMatrix());
239 GrShaderVar uniform = uniformHandler->liftUniformToVertexShader(
240 *fp->parent(), SkString(SkSL::SampleUsage::MatrixUniformName()));
241 // Start with this matrix and accumulate additional matrices as we walk up the FP tree
242 // to either the base coords or an ancestor FP that has an associated varying.
243 SkString transformExpression = uniform.getName();
244
245 // If we hit an ancestor with a varying on our walk up then save off the varying as the
246 // input to our accumulated transformExpression. Start off assuming we'll reach the root.
247 GrShaderVar inputCoords = info.inputCoords;
248
249 for (const auto* base = fp->parent(); base; base = base->parent()) {
250 if (auto iter = fTransformVaryingsMap.find(base); iter != fTransformVaryingsMap.end()) {
251 // Can stop here, as this varying already holds all transforms from higher FPs
252 // We'll apply the residual transformExpression we've accumulated up from our
253 // starting FP to this varying.
254 inputCoords = iter->second.varying.vsOutVar();
255 break;
256 } else if (base->sampleUsage().isUniformMatrix()) {
257 // Accumulate any matrices along the path to either the original local/device coords
258 // or a parent varying. Getting here means this FP was sampled with a uniform matrix
259 // but all uses of coords below here in the FP hierarchy are beneath additional
260 // matrix samples and thus this node wasn't assigned a varying.
261 GrShaderVar parentUniform = uniformHandler->liftUniformToVertexShader(
262 *base->parent(), SkString(SkSL::SampleUsage::MatrixUniformName()));
263 transformExpression.appendf(" * %s", parentUniform.getName().c_str());
264 } else if (base->sampleUsage().isFragCoord()) {
265 // Our chain of matrices starts here and is based on the device space position.
266 break;
267 } else {
268 // This intermediate FP is just a pass through and doesn't need to be built
269 // in to the expression, but we must visit its parents in case they add transforms.
270 SkASSERT(base->sampleUsage().isPassThrough() || !base->sampleUsage().isSampled());
271 }
272 }
273
274 SkString inputStr;
275 if (inputCoords.getType() == kFloat2_GrSLType) {
276 inputStr = SkStringPrintf("%s.xy1", inputCoords.getName().c_str());
277 } else {
278 SkASSERT(inputCoords.getType() == kFloat3_GrSLType);
279 inputStr = inputCoords.getName();
280 }
281
282 vb->codeAppend("{\n");
283 if (info.varying.type() == kFloat2_GrSLType) {
284 if (vb->getProgramBuilder()->shaderCaps()->nonsquareMatrixSupport()) {
285 vb->codeAppendf("%s = float3x2(%s) * %s",
286 info.varying.vsOut(),
287 transformExpression.c_str(),
288 inputStr.c_str());
289 } else {
290 vb->codeAppendf("%s = (%s * %s).xy",
291 info.varying.vsOut(),
292 transformExpression.c_str(),
293 inputStr.c_str());
294 }
295 } else {
296 SkASSERT(info.varying.type() == kFloat3_GrSLType);
297 vb->codeAppendf("%s = %s * %s",
298 info.varying.vsOut(),
299 transformExpression.c_str(),
300 inputStr.c_str());
301 }
302 vb->codeAppend(";\n");
303 vb->codeAppend("}\n");
304 }
305 // We don't need this map anymore.
306 fTransformVaryingsMap.clear();
307 }
308
setupUniformColor(GrGLSLFPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const char * outputName,UniformHandle * colorUniform)309 void ProgramImpl::setupUniformColor(GrGLSLFPFragmentBuilder* fragBuilder,
310 GrGLSLUniformHandler* uniformHandler,
311 const char* outputName,
312 UniformHandle* colorUniform) {
313 SkASSERT(colorUniform);
314 const char* stagedLocalVarName;
315 *colorUniform = uniformHandler->addUniform(nullptr,
316 kFragment_GrShaderFlag,
317 kHalf4_GrSLType,
318 "Color",
319 &stagedLocalVarName);
320 fragBuilder->codeAppendf("%s = %s;", outputName, stagedLocalVarName);
321 if (fragBuilder->getProgramBuilder()->shaderCaps()->mustObfuscateUniformColor()) {
322 fragBuilder->codeAppendf("%s = max(%s, half4(0));", outputName, outputName);
323 }
324 }
325
SetTransform(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const UniformHandle & uniform,const SkMatrix & matrix,SkMatrix * state)326 void ProgramImpl::SetTransform(const GrGLSLProgramDataManager& pdman,
327 const GrShaderCaps& shaderCaps,
328 const UniformHandle& uniform,
329 const SkMatrix& matrix,
330 SkMatrix* state) {
331 if (!uniform.isValid() || (state && SkMatrixPriv::CheapEqual(*state, matrix))) {
332 // No update needed
333 return;
334 }
335 if (state) {
336 *state = matrix;
337 }
338 if (matrix.isScaleTranslate() && !shaderCaps.reducedShaderMode()) {
339 // ComputeMatrixKey and writeX() assume the uniform is a float4 (can't assert since nothing
340 // is exposed on a handle, but should be caught lower down).
341 float values[4] = {matrix.getScaleX(), matrix.getTranslateX(),
342 matrix.getScaleY(), matrix.getTranslateY()};
343 pdman.set4fv(uniform, 1, values);
344 } else {
345 pdman.setSkMatrix(uniform, matrix);
346 }
347 }
348
write_passthrough_vertex_position(GrGLSLVertexBuilder * vertBuilder,const GrShaderVar & inPos,GrShaderVar * outPos)349 static void write_passthrough_vertex_position(GrGLSLVertexBuilder* vertBuilder,
350 const GrShaderVar& inPos,
351 GrShaderVar* outPos) {
352 SkASSERT(inPos.getType() == kFloat3_GrSLType || inPos.getType() == kFloat2_GrSLType);
353 SkString outName = vertBuilder->newTmpVarName(inPos.getName().c_str());
354 outPos->set(inPos.getType(), outName.c_str());
355 vertBuilder->codeAppendf("float%d %s = %s;",
356 GrSLTypeVecLength(inPos.getType()),
357 outName.c_str(),
358 inPos.getName().c_str());
359 }
360
write_vertex_position(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,const GrShaderVar & inPos,const SkMatrix & matrix,const char * matrixName,GrShaderVar * outPos,ProgramImpl::UniformHandle * matrixUniform)361 static void write_vertex_position(GrGLSLVertexBuilder* vertBuilder,
362 GrGLSLUniformHandler* uniformHandler,
363 const GrShaderCaps& shaderCaps,
364 const GrShaderVar& inPos,
365 const SkMatrix& matrix,
366 const char* matrixName,
367 GrShaderVar* outPos,
368 ProgramImpl::UniformHandle* matrixUniform) {
369 SkASSERT(inPos.getType() == kFloat3_GrSLType || inPos.getType() == kFloat2_GrSLType);
370 SkString outName = vertBuilder->newTmpVarName(inPos.getName().c_str());
371
372 if (matrix.isIdentity() && !shaderCaps.reducedShaderMode()) {
373 write_passthrough_vertex_position(vertBuilder, inPos, outPos);
374 return;
375 }
376 SkASSERT(matrixUniform);
377
378 bool useCompactTransform = matrix.isScaleTranslate() && !shaderCaps.reducedShaderMode();
379 const char* mangledMatrixName;
380 *matrixUniform = uniformHandler->addUniform(nullptr,
381 kVertex_GrShaderFlag,
382 useCompactTransform ? kFloat4_GrSLType
383 : kFloat3x3_GrSLType,
384 matrixName,
385 &mangledMatrixName);
386
387 if (inPos.getType() == kFloat3_GrSLType) {
388 // A float3 stays a float3 whether or not the matrix adds perspective
389 if (useCompactTransform) {
390 vertBuilder->codeAppendf("float3 %s = %s.xz1 * %s + %s.yw0;\n",
391 outName.c_str(),
392 mangledMatrixName,
393 inPos.getName().c_str(),
394 mangledMatrixName);
395 } else {
396 vertBuilder->codeAppendf("float3 %s = %s * %s;\n",
397 outName.c_str(),
398 mangledMatrixName,
399 inPos.getName().c_str());
400 }
401 outPos->set(kFloat3_GrSLType, outName.c_str());
402 return;
403 }
404 if (matrix.hasPerspective()) {
405 // A float2 is promoted to a float3 if we add perspective via the matrix
406 SkASSERT(!useCompactTransform);
407 vertBuilder->codeAppendf("float3 %s = (%s * %s.xy1);",
408 outName.c_str(),
409 mangledMatrixName,
410 inPos.getName().c_str());
411 outPos->set(kFloat3_GrSLType, outName.c_str());
412 return;
413 }
414 if (useCompactTransform) {
415 vertBuilder->codeAppendf("float2 %s = %s.xz * %s + %s.yw;\n",
416 outName.c_str(),
417 mangledMatrixName,
418 inPos.getName().c_str(),
419 mangledMatrixName);
420 } else if (shaderCaps.nonsquareMatrixSupport()) {
421 vertBuilder->codeAppendf("float2 %s = float3x2(%s) * %s.xy1;\n",
422 outName.c_str(),
423 mangledMatrixName,
424 inPos.getName().c_str());
425 } else {
426 vertBuilder->codeAppendf("float2 %s = (%s * %s.xy1).xy;\n",
427 outName.c_str(),
428 mangledMatrixName,
429 inPos.getName().c_str());
430 }
431 outPos->set(kFloat2_GrSLType, outName.c_str());
432 }
433
WriteOutputPosition(GrGLSLVertexBuilder * vertBuilder,GrGPArgs * gpArgs,const char * posName)434 void ProgramImpl::WriteOutputPosition(GrGLSLVertexBuilder* vertBuilder,
435 GrGPArgs* gpArgs,
436 const char* posName) {
437 // writeOutputPosition assumes the incoming pos name points to a float2 variable
438 GrShaderVar inPos(posName, kFloat2_GrSLType);
439 write_passthrough_vertex_position(vertBuilder, inPos, &gpArgs->fPositionVar);
440 }
441
WriteOutputPosition(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,GrGPArgs * gpArgs,const char * posName,const SkMatrix & mat,UniformHandle * viewMatrixUniform)442 void ProgramImpl::WriteOutputPosition(GrGLSLVertexBuilder* vertBuilder,
443 GrGLSLUniformHandler* uniformHandler,
444 const GrShaderCaps& shaderCaps,
445 GrGPArgs* gpArgs,
446 const char* posName,
447 const SkMatrix& mat,
448 UniformHandle* viewMatrixUniform) {
449 GrShaderVar inPos(posName, kFloat2_GrSLType);
450 write_vertex_position(vertBuilder,
451 uniformHandler,
452 shaderCaps,
453 inPos,
454 mat,
455 "viewMatrix",
456 &gpArgs->fPositionVar,
457 viewMatrixUniform);
458 }
459
WriteLocalCoord(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,GrGPArgs * gpArgs,GrShaderVar localVar,const SkMatrix & localMatrix,UniformHandle * localMatrixUniform)460 void ProgramImpl::WriteLocalCoord(GrGLSLVertexBuilder* vertBuilder,
461 GrGLSLUniformHandler* uniformHandler,
462 const GrShaderCaps& shaderCaps,
463 GrGPArgs* gpArgs,
464 GrShaderVar localVar,
465 const SkMatrix& localMatrix,
466 UniformHandle* localMatrixUniform) {
467 write_vertex_position(vertBuilder,
468 uniformHandler,
469 shaderCaps,
470 localVar,
471 localMatrix,
472 "localMatrix",
473 &gpArgs->fLocalCoordVar,
474 localMatrixUniform);
475 }
476