• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/glsl/GrGLSLGeometryProcessor.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 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
17 
18 #include <unordered_map>
19 
emitCode(EmitArgs & args)20 void GrGLSLGeometryProcessor::emitCode(EmitArgs& args) {
21     GrGPArgs gpArgs;
22     this->onEmitCode(args, &gpArgs);
23 
24     if (gpArgs.fLocalCoordVar.getType() != kVoid_GrSLType) {
25         this->collectTransforms(args.fVertBuilder, args.fVaryingHandler, args.fUniformHandler,
26                                 gpArgs.fLocalCoordVar, args.fFPCoordTransformHandler);
27     } else {
28         // Make sure no FPs needed the local coord variable.
29         SkASSERT(!*args.fFPCoordTransformHandler);
30     }
31 
32     if (args.fGeomProc.willUseTessellationShaders()) {
33         // Tessellation shaders are temporarily responsible for integrating their own code strings
34         // while we work out full support.
35         return;
36     }
37 
38     GrGLSLVertexBuilder* vBuilder = args.fVertBuilder;
39     if (!args.fGeomProc.willUseGeoShader()) {
40         // Emit the vertex position to the hardware in the normalized window coordinates it expects.
41         SkASSERT(kFloat2_GrSLType == gpArgs.fPositionVar.getType() ||
42                  kFloat3_GrSLType == gpArgs.fPositionVar.getType());
43         vBuilder->emitNormalizedSkPosition(gpArgs.fPositionVar.c_str(),
44                                            gpArgs.fPositionVar.getType());
45         if (kFloat2_GrSLType == gpArgs.fPositionVar.getType()) {
46             args.fVaryingHandler->setNoPerspective();
47         }
48     } else {
49         // Since we have a geometry shader, leave the vertex position in Skia device space for now.
50         // The geometry Shader will operate in device space, and then convert the final positions to
51         // normalized hardware window coordinates under the hood, once everything else has finished.
52         // The subclass must call setNoPerspective on the varying handler, if applicable.
53         vBuilder->codeAppendf("sk_Position = float4(%s", gpArgs.fPositionVar.c_str());
54         switch (gpArgs.fPositionVar.getType()) {
55             case kFloat_GrSLType:
56                 vBuilder->codeAppend(", 0");
57                 [[fallthrough]];
58             case kFloat2_GrSLType:
59                 vBuilder->codeAppend(", 0");
60                 [[fallthrough]];
61             case kFloat3_GrSLType:
62                 vBuilder->codeAppend(", 1");
63                 [[fallthrough]];
64             case kFloat4_GrSLType:
65                 vBuilder->codeAppend(");");
66                 break;
67             default:
68                 SK_ABORT("Invalid position var type");
69                 break;
70         }
71     }
72 }
73 
collectTransforms(GrGLSLVertexBuilder * vb,GrGLSLVaryingHandler * varyingHandler,GrGLSLUniformHandler * uniformHandler,const GrShaderVar & localCoordsVar,FPCoordTransformHandler * handler)74 void GrGLSLGeometryProcessor::collectTransforms(GrGLSLVertexBuilder* vb,
75                                                 GrGLSLVaryingHandler* varyingHandler,
76                                                 GrGLSLUniformHandler* uniformHandler,
77                                                 const GrShaderVar& localCoordsVar,
78                                                 FPCoordTransformHandler* handler) {
79     SkASSERT(localCoordsVar.getType() == kFloat2_GrSLType ||
80              localCoordsVar.getType() == kFloat3_GrSLType);
81     // Cached varyings produced by parent FPs. If parent FPs introduce transformations, but all
82     // subsequent children are not transformed, they should share the same varying.
83     std::unordered_map<const GrFragmentProcessor*, GrShaderVar> localCoordsMap;
84 
85     GrGLSLVarying baseLocalCoord;
86     auto getBaseLocalCoord = [&baseLocalCoord, &localCoordsVar, vb, varyingHandler]() {
87         SkASSERT(GrSLTypeIsFloatType(localCoordsVar.getType()));
88         if (baseLocalCoord.type() == kVoid_GrSLType) {
89             // Initialize to the GP provided coordinate
90             SkString baseLocalCoordName = SkStringPrintf("LocalCoord");
91             baseLocalCoord = GrGLSLVarying(localCoordsVar.getType());
92             varyingHandler->addVarying(baseLocalCoordName.c_str(), &baseLocalCoord);
93             vb->codeAppendf("%s = %s;\n", baseLocalCoord.vsOut(),
94                             localCoordsVar.getName().c_str());
95         }
96         return GrShaderVar(SkString(baseLocalCoord.fsIn()), baseLocalCoord.type(),
97                            GrShaderVar::TypeModifier::In);
98     };
99 
100     for (int i = 0; *handler; ++*handler, ++i) {
101         const auto& fp = handler->get();
102 
103         SkASSERT(fp.referencesSampleCoords());
104         SkASSERT(!fp.isSampledWithExplicitCoords());
105 
106         // FPs that use local coordinates need a varying to convey the coordinate. This may be the
107         // base GP's local coord if transforms have to be computed in the FS, or it may be a unique
108         // varying that computes the equivalent transformation hierarchy in the VS.
109         GrShaderVar varyingVar;
110 
111         // The FP's local coordinates are determined by the uniform transform hierarchy
112         // from this FP to the root, and can be computed in the vertex shader.
113         // If this hierarchy would be the identity transform, then we should use the
114         // original local coordinate.
115         // NOTE: The actual transform logic is handled in emitTransformCode(), this just
116         // needs to determine if a unique varying should be added for the FP.
117         GrShaderVar transformedLocalCoord;
118         const GrFragmentProcessor* coordOwner = nullptr;
119 
120         const GrFragmentProcessor* node = &fp;
121         while(node) {
122             SkASSERT(!node->isSampledWithExplicitCoords());
123 
124             if (node->sampleUsage().isUniformMatrix()) {
125                 // We can stop once we hit an FP that adds transforms; this FP can reuse
126                 // that FPs varying (possibly vivifying it if this was the first use).
127                 transformedLocalCoord = localCoordsMap[node];
128                 coordOwner = node;
129                 break;
130             } // else intervening FP is an identity transform so skip past it
131 
132             node = node->parent();
133         }
134 
135         if (coordOwner) {
136             // The FP will use coordOwner's varying; add varying if this was the first use
137             if (transformedLocalCoord.getType() == kVoid_GrSLType) {
138                 GrGLSLVarying v(kFloat2_GrSLType);
139                 if (GrSLTypeVecLength(localCoordsVar.getType()) == 3 ||
140                     coordOwner->hasPerspectiveTransform()) {
141                     v = GrGLSLVarying(kFloat3_GrSLType);
142                 }
143                 SkString strVaryingName;
144                 strVaryingName.printf("TransformedCoords_%d", i);
145                 varyingHandler->addVarying(strVaryingName.c_str(), &v);
146 
147                 fTransformInfos.push_back(
148                         {GrShaderVar(v.vsOut(), v.type()), localCoordsVar, coordOwner});
149                 transformedLocalCoord =
150                         GrShaderVar(SkString(v.fsIn()), v.type(), GrShaderVar::TypeModifier::In);
151                 localCoordsMap[coordOwner] = transformedLocalCoord;
152             }
153 
154             varyingVar = transformedLocalCoord;
155         } else {
156             // The FP transform hierarchy is the identity, so use the original local coord
157             varyingVar = getBaseLocalCoord();
158         }
159 
160         SkASSERT(varyingVar.getType() != kVoid_GrSLType);
161         handler->specifyCoordsForCurrCoordTransform(varyingVar);
162     }
163 }
164 
emitTransformCode(GrGLSLVertexBuilder * vb,GrGLSLUniformHandler * uniformHandler)165 void GrGLSLGeometryProcessor::emitTransformCode(GrGLSLVertexBuilder* vb,
166                                                 GrGLSLUniformHandler* uniformHandler) {
167     std::unordered_map<const GrFragmentProcessor*, GrShaderVar> localCoordsMap;
168     for (const auto& tr : fTransformInfos) {
169         // If we recorded a transform info, its sample matrix must be uniform
170         SkASSERT(tr.fFP->sampleUsage().isUniformMatrix());
171 
172         SkString localCoords;
173         // Build a concatenated matrix expression that we apply to the root local coord.
174         // If we have an expression cached from an early FP in the hierarchy chain, we can stop
175         // there instead of going all the way to the GP.
176         SkString transformExpression;
177 
178         const auto* base = tr.fFP;
179         while(base) {
180             GrShaderVar cachedBaseCoord = localCoordsMap[base];
181             if (cachedBaseCoord.getType() != kVoid_GrSLType) {
182                 // Can stop here, as this varying already holds all transforms from higher FPs
183                 if (cachedBaseCoord.getType() == kFloat3_GrSLType) {
184                     localCoords = cachedBaseCoord.getName();
185                 } else {
186                     localCoords = SkStringPrintf("%s.xy1", cachedBaseCoord.getName().c_str());
187                 }
188                 break;
189             } else if (base->sampleUsage().isUniformMatrix()) {
190                 // The FP knows the matrix expression it's sampled with, but its parent defined
191                 // the uniform (when the expression is not a constant).
192                 GrShaderVar uniform = uniformHandler->liftUniformToVertexShader(
193                         *base->parent(), SkString(base->sampleUsage().fExpression));
194 
195                 // Accumulate the base matrix expression as a preConcat
196                 SkString matrix;
197                 if (uniform.getType() != kVoid_GrSLType) {
198                     SkASSERT(uniform.getType() == kFloat3x3_GrSLType);
199                     matrix = uniform.getName();
200                 } else {
201                     // No uniform found, so presumably this is a constant
202                     matrix = SkString(base->sampleUsage().fExpression);
203                 }
204 
205                 if (!transformExpression.isEmpty()) {
206                     transformExpression.append(" * ");
207                 }
208                 transformExpression.appendf("(%s)", matrix.c_str());
209             } else {
210                 // This intermediate FP is just a pass through and doesn't need to be built
211                 // in to the expression, but must visit its parents in case they add transforms
212                 SkASSERT(base->sampleUsage().isPassThrough() || !base->sampleUsage().isSampled());
213             }
214 
215             base = base->parent();
216         }
217 
218         if (localCoords.isEmpty()) {
219             // Must use GP's local coords
220             if (tr.fLocalCoords.getType() == kFloat3_GrSLType) {
221                 localCoords = tr.fLocalCoords.getName();
222             } else {
223                 localCoords = SkStringPrintf("%s.xy1", tr.fLocalCoords.getName().c_str());
224             }
225         }
226 
227         vb->codeAppend("{\n");
228         if (tr.fOutputCoords.getType() == kFloat2_GrSLType) {
229             if (vb->getProgramBuilder()->shaderCaps()->nonsquareMatrixSupport()) {
230                 vb->codeAppendf("%s = float3x2(%s) * %s", tr.fOutputCoords.getName().c_str(),
231                                                           transformExpression.c_str(),
232                                                           localCoords.c_str());
233             } else {
234                 vb->codeAppendf("%s = ((%s) * %s).xy", tr.fOutputCoords.getName().c_str(),
235                                                        transformExpression.c_str(),
236                                                        localCoords.c_str());
237             }
238         } else {
239             SkASSERT(tr.fOutputCoords.getType() == kFloat3_GrSLType);
240             vb->codeAppendf("%s = (%s) * %s", tr.fOutputCoords.getName().c_str(),
241                                               transformExpression.c_str(),
242                                               localCoords.c_str());
243         }
244         vb->codeAppend(";\n");
245         vb->codeAppend("}\n");
246 
247         localCoordsMap.insert({ tr.fFP, tr.fOutputCoords });
248     }
249 }
250 
setupUniformColor(GrGLSLFPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const char * outputName,UniformHandle * colorUniform)251 void GrGLSLGeometryProcessor::setupUniformColor(GrGLSLFPFragmentBuilder* fragBuilder,
252                                                 GrGLSLUniformHandler* uniformHandler,
253                                                 const char* outputName,
254                                                 UniformHandle* colorUniform) {
255     SkASSERT(colorUniform);
256     const char* stagedLocalVarName;
257     *colorUniform = uniformHandler->addUniform(nullptr,
258                                                kFragment_GrShaderFlag,
259                                                kHalf4_GrSLType,
260                                                "Color",
261                                                &stagedLocalVarName);
262     fragBuilder->codeAppendf("%s = %s;", outputName, stagedLocalVarName);
263     if (fragBuilder->getProgramBuilder()->shaderCaps()->mustObfuscateUniformColor()) {
264         fragBuilder->codeAppendf("%s = max(%s, half4(0));", outputName, outputName);
265     }
266 }
267 
SetTransform(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const UniformHandle & uniform,const SkMatrix & matrix,SkMatrix * state)268 void GrGLSLGeometryProcessor::SetTransform(const GrGLSLProgramDataManager& pdman,
269                                            const GrShaderCaps& shaderCaps,
270                                            const UniformHandle& uniform,
271                                            const SkMatrix& matrix,
272                                            SkMatrix* state) {
273     if (!uniform.isValid() || (state && SkMatrixPriv::CheapEqual(*state, matrix))) {
274         // No update needed
275         return;
276     }
277     if (state) {
278         *state = matrix;
279     }
280     if (matrix.isScaleTranslate() && !shaderCaps.reducedShaderMode()) {
281         // ComputeMatrixKey and writeX() assume the uniform is a float4 (can't assert since nothing
282         // is exposed on a handle, but should be caught lower down).
283         float values[4] = {matrix.getScaleX(), matrix.getTranslateX(),
284                            matrix.getScaleY(), matrix.getTranslateY()};
285         pdman.set4fv(uniform, 1, values);
286     } else {
287         pdman.setSkMatrix(uniform, matrix);
288     }
289 }
290 
write_passthrough_vertex_position(GrGLSLVertexBuilder * vertBuilder,const GrShaderVar & inPos,GrShaderVar * outPos)291 static void write_passthrough_vertex_position(GrGLSLVertexBuilder* vertBuilder,
292                                               const GrShaderVar& inPos,
293                                               GrShaderVar* outPos) {
294     SkASSERT(inPos.getType() == kFloat3_GrSLType || inPos.getType() == kFloat2_GrSLType);
295     SkString outName = vertBuilder->newTmpVarName(inPos.getName().c_str());
296     outPos->set(inPos.getType(), outName.c_str());
297     vertBuilder->codeAppendf("float%d %s = %s;",
298                              GrSLTypeVecLength(inPos.getType()),
299                              outName.c_str(),
300                              inPos.getName().c_str());
301 }
302 
write_vertex_position(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,const GrShaderVar & inPos,const SkMatrix & matrix,const char * matrixName,GrShaderVar * outPos,GrGLSLGeometryProcessor::UniformHandle * matrixUniform)303 static void write_vertex_position(GrGLSLVertexBuilder* vertBuilder,
304                                   GrGLSLUniformHandler* uniformHandler,
305                                   const GrShaderCaps& shaderCaps,
306                                   const GrShaderVar& inPos,
307                                   const SkMatrix& matrix,
308                                   const char* matrixName,
309                                   GrShaderVar* outPos,
310                                   GrGLSLGeometryProcessor::UniformHandle* matrixUniform) {
311     SkASSERT(inPos.getType() == kFloat3_GrSLType || inPos.getType() == kFloat2_GrSLType);
312     SkString outName = vertBuilder->newTmpVarName(inPos.getName().c_str());
313 
314     if (matrix.isIdentity() && !shaderCaps.reducedShaderMode()) {
315         write_passthrough_vertex_position(vertBuilder, inPos, outPos);
316         return;
317     }
318     SkASSERT(matrixUniform);
319 
320     bool useCompactTransform = matrix.isScaleTranslate() && !shaderCaps.reducedShaderMode();
321     const char* mangledMatrixName;
322     *matrixUniform = uniformHandler->addUniform(nullptr,
323                                                 kVertex_GrShaderFlag,
324                                                 useCompactTransform ? kFloat4_GrSLType
325                                                                     : kFloat3x3_GrSLType,
326                                                 matrixName,
327                                                 &mangledMatrixName);
328 
329     if (inPos.getType() == kFloat3_GrSLType) {
330         // A float3 stays a float3 whether or not the matrix adds perspective
331         if (useCompactTransform) {
332             vertBuilder->codeAppendf("float3 %s = %s.xz1 * %s + %s.yw0;\n",
333                                      outName.c_str(),
334                                      mangledMatrixName,
335                                      inPos.getName().c_str(),
336                                      mangledMatrixName);
337         } else {
338             vertBuilder->codeAppendf("float3 %s = %s * %s;\n",
339                                      outName.c_str(),
340                                      mangledMatrixName,
341                                      inPos.getName().c_str());
342         }
343         outPos->set(kFloat3_GrSLType, outName.c_str());
344         return;
345     }
346     if (matrix.hasPerspective()) {
347         // A float2 is promoted to a float3 if we add perspective via the matrix
348         SkASSERT(!useCompactTransform);
349         vertBuilder->codeAppendf("float3 %s = (%s * %s.xy1);",
350                                  outName.c_str(),
351                                  mangledMatrixName,
352                                  inPos.getName().c_str());
353         outPos->set(kFloat3_GrSLType, outName.c_str());
354         return;
355     }
356     if (useCompactTransform) {
357         vertBuilder->codeAppendf("float2 %s = %s.xz * %s + %s.yw;\n",
358                                  outName.c_str(),
359                                  mangledMatrixName,
360                                  inPos.getName().c_str(),
361                                  mangledMatrixName);
362     } else if (shaderCaps.nonsquareMatrixSupport()) {
363         vertBuilder->codeAppendf("float2 %s = float3x2(%s) * %s.xy1;\n",
364                                  outName.c_str(),
365                                  mangledMatrixName,
366                                  inPos.getName().c_str());
367     } else {
368         vertBuilder->codeAppendf("float2 %s = (%s * %s.xy1).xy;\n",
369                                  outName.c_str(),
370                                  mangledMatrixName,
371                                  inPos.getName().c_str());
372     }
373     outPos->set(kFloat2_GrSLType, outName.c_str());
374 }
375 
WriteOutputPosition(GrGLSLVertexBuilder * vertBuilder,GrGPArgs * gpArgs,const char * posName)376 void GrGLSLGeometryProcessor::WriteOutputPosition(GrGLSLVertexBuilder* vertBuilder,
377                                                   GrGPArgs* gpArgs,
378                                                   const char* posName) {
379     // writeOutputPosition assumes the incoming pos name points to a float2 variable
380     GrShaderVar inPos(posName, kFloat2_GrSLType);
381     write_passthrough_vertex_position(vertBuilder, inPos, &gpArgs->fPositionVar);
382 }
383 
WriteOutputPosition(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,GrGPArgs * gpArgs,const char * posName,const SkMatrix & mat,UniformHandle * viewMatrixUniform)384 void GrGLSLGeometryProcessor::WriteOutputPosition(GrGLSLVertexBuilder* vertBuilder,
385                                                   GrGLSLUniformHandler* uniformHandler,
386                                                   const GrShaderCaps& shaderCaps,
387                                                   GrGPArgs* gpArgs,
388                                                   const char* posName,
389                                                   const SkMatrix& mat,
390                                                   UniformHandle* viewMatrixUniform) {
391     GrShaderVar inPos(posName, kFloat2_GrSLType);
392     write_vertex_position(vertBuilder,
393                           uniformHandler,
394                           shaderCaps,
395                           inPos,
396                           mat,
397                           "viewMatrix",
398                           &gpArgs->fPositionVar,
399                           viewMatrixUniform);
400 }
401 
WriteLocalCoord(GrGLSLVertexBuilder * vertBuilder,GrGLSLUniformHandler * uniformHandler,const GrShaderCaps & shaderCaps,GrGPArgs * gpArgs,GrShaderVar localVar,const SkMatrix & localMatrix,UniformHandle * localMatrixUniform)402 void GrGLSLGeometryProcessor::WriteLocalCoord(GrGLSLVertexBuilder* vertBuilder,
403                                               GrGLSLUniformHandler* uniformHandler,
404                                               const GrShaderCaps& shaderCaps,
405                                               GrGPArgs* gpArgs,
406                                               GrShaderVar localVar,
407                                               const SkMatrix& localMatrix,
408                                               UniformHandle* localMatrixUniform) {
409     write_vertex_position(vertBuilder,
410                           uniformHandler,
411                           shaderCaps,
412                           localVar,
413                           localMatrix,
414                           "localMatrix",
415                           &gpArgs->fLocalCoordVar,
416                           localMatrixUniform);
417 }
418 
419 //////////////////////////////////////////////////////////////////////////////
420 
FPCoordTransformHandler(const GrPipeline & pipeline,SkTArray<GrShaderVar> * transformedCoordVars)421 GrGLSLGeometryProcessor::FPCoordTransformHandler::FPCoordTransformHandler(
422             const GrPipeline& pipeline,
423             SkTArray<GrShaderVar>* transformedCoordVars)
424         : fIter(pipeline), fTransformedCoordVars(transformedCoordVars) {
425     while (fIter && !fIter->usesVaryingCoordsDirectly()) {
426         ++fIter;
427     }
428 }
429 
get() const430 const GrFragmentProcessor& GrGLSLGeometryProcessor::FPCoordTransformHandler::get() const {
431     return *fIter;
432 }
433 
434 GrGLSLGeometryProcessor::FPCoordTransformHandler&
operator ++()435 GrGLSLGeometryProcessor::FPCoordTransformHandler::operator++() {
436     SkASSERT(fAddedCoord);
437     do {
438         ++fIter;
439     } while (fIter && !fIter->usesVaryingCoordsDirectly());
440     SkDEBUGCODE(fAddedCoord = false;)
441     return *this;
442 }
443