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