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