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