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 | |
19 | void 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 | |
70 | void 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 | |
162 | void 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 | |
242 | void 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 | |
264 | static 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 | |
323 | void 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 | |
332 | void 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 | |
343 | void 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 | |