1 | // |
---|---|
2 | // Copyright 2016 The ANGLE Project Authors. All rights reserved. |
3 | // Use of this source code is governed by a BSD-style license that can be |
4 | // found in the LICENSE file. |
5 | // |
6 | // TranslatorSPIRV: |
7 | // A set of transformations that prepare the AST to be compatible with GL_KHR_vulkan_glsl followed |
8 | // by a pass that generates SPIR-V. |
9 | // See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt |
10 | // |
11 | |
12 | #include "compiler/translator/spirv/TranslatorSPIRV.h" |
13 | |
14 | #include "angle_gl.h" |
15 | #include "common/PackedEnums.h" |
16 | #include "common/utilities.h" |
17 | #include "compiler/translator/ImmutableStringBuilder.h" |
18 | #include "compiler/translator/IntermNode.h" |
19 | #include "compiler/translator/StaticType.h" |
20 | #include "compiler/translator/spirv/BuiltinsWorkaround.h" |
21 | #include "compiler/translator/spirv/OutputSPIRV.h" |
22 | #include "compiler/translator/tree_ops/DeclarePerVertexBlocks.h" |
23 | #include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h" |
24 | #include "compiler/translator/tree_ops/RecordConstantPrecision.h" |
25 | #include "compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h" |
26 | #include "compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h" |
27 | #include "compiler/translator/tree_ops/RewriteArrayOfArrayOfOpaqueUniforms.h" |
28 | #include "compiler/translator/tree_ops/RewriteAtomicCounters.h" |
29 | #include "compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h" |
30 | #include "compiler/translator/tree_ops/RewriteDfdy.h" |
31 | #include "compiler/translator/tree_ops/RewriteStructSamplers.h" |
32 | #include "compiler/translator/tree_ops/SeparateStructFromUniformDeclarations.h" |
33 | #include "compiler/translator/tree_ops/spirv/EmulateAdvancedBlendEquations.h" |
34 | #include "compiler/translator/tree_ops/spirv/EmulateDithering.h" |
35 | #include "compiler/translator/tree_ops/spirv/EmulateFragColorData.h" |
36 | #include "compiler/translator/tree_ops/spirv/EmulateFramebufferFetch.h" |
37 | #include "compiler/translator/tree_ops/spirv/EmulateYUVBuiltIns.h" |
38 | #include "compiler/translator/tree_ops/spirv/FlagSamplersWithTexelFetch.h" |
39 | #include "compiler/translator/tree_ops/spirv/RewriteInterpolateAtOffset.h" |
40 | #include "compiler/translator/tree_ops/spirv/RewriteR32fImages.h" |
41 | #include "compiler/translator/tree_util/BuiltIn.h" |
42 | #include "compiler/translator/tree_util/DriverUniform.h" |
43 | #include "compiler/translator/tree_util/FindFunction.h" |
44 | #include "compiler/translator/tree_util/FindMain.h" |
45 | #include "compiler/translator/tree_util/IntermNode_util.h" |
46 | #include "compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h" |
47 | #include "compiler/translator/tree_util/ReplaceVariable.h" |
48 | #include "compiler/translator/tree_util/RewriteSampleMaskVariable.h" |
49 | #include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h" |
50 | #include "compiler/translator/tree_util/RunAtTheEndOfShader.h" |
51 | #include "compiler/translator/tree_util/SpecializationConstant.h" |
52 | #include "compiler/translator/util.h" |
53 | |
54 | namespace sh |
55 | { |
56 | |
57 | namespace |
58 | { |
59 | constexpr ImmutableString kFlippedPointCoordName = ImmutableString("flippedPointCoord"); |
60 | constexpr ImmutableString kFlippedFragCoordName = ImmutableString("flippedFragCoord"); |
61 | constexpr ImmutableString kDefaultUniformsBlockName = ImmutableString("defaultUniforms"); |
62 | |
63 | bool IsDefaultUniform(const TType &type) |
64 | { |
65 | return type.getQualifier() == EvqUniform && type.getInterfaceBlock() == nullptr && |
66 | !IsOpaqueType(type: type.getBasicType()); |
67 | } |
68 | |
69 | class ReplaceDefaultUniformsTraverser : public TIntermTraverser |
70 | { |
71 | public: |
72 | ReplaceDefaultUniformsTraverser(const VariableReplacementMap &variableMap) |
73 | : TIntermTraverser(true, false, false), mVariableMap(variableMap) |
74 | {} |
75 | |
76 | bool visitDeclaration(Visit visit, TIntermDeclaration *node) override |
77 | { |
78 | const TIntermSequence &sequence = *(node->getSequence()); |
79 | |
80 | TIntermTyped *variable = sequence.front()->getAsTyped(); |
81 | const TType &type = variable->getType(); |
82 | |
83 | if (IsDefaultUniform(type)) |
84 | { |
85 | // Remove the uniform declaration. |
86 | TIntermSequence emptyReplacement; |
87 | mMultiReplacements.emplace_back(args: getParentNode()->getAsBlock(), args&: node, |
88 | args: std::move(emptyReplacement)); |
89 | |
90 | return false; |
91 | } |
92 | |
93 | return true; |
94 | } |
95 | |
96 | void visitSymbol(TIntermSymbol *symbol) override |
97 | { |
98 | const TVariable &variable = symbol->variable(); |
99 | const TType &type = variable.getType(); |
100 | |
101 | if (!IsDefaultUniform(type) || gl::IsBuiltInName(name: variable.name().data())) |
102 | { |
103 | return; |
104 | } |
105 | |
106 | ASSERT(mVariableMap.count(&variable) > 0); |
107 | |
108 | queueReplacement(replacement: mVariableMap.at(key: &variable)->deepCopy(), originalStatus: OriginalNode::IS_DROPPED); |
109 | } |
110 | |
111 | private: |
112 | const VariableReplacementMap &mVariableMap; |
113 | }; |
114 | |
115 | bool DeclareDefaultUniforms(TranslatorSPIRV *compiler, |
116 | TIntermBlock *root, |
117 | TSymbolTable *symbolTable, |
118 | gl::ShaderType shaderType) |
119 | { |
120 | // First, collect all default uniforms and declare a uniform block. |
121 | TFieldList *uniformList = new TFieldList; |
122 | TVector<const TVariable *> uniformVars; |
123 | |
124 | for (TIntermNode *node : *root->getSequence()) |
125 | { |
126 | TIntermDeclaration *decl = node->getAsDeclarationNode(); |
127 | if (decl == nullptr) |
128 | { |
129 | continue; |
130 | } |
131 | |
132 | const TIntermSequence &sequence = *(decl->getSequence()); |
133 | |
134 | TIntermSymbol *symbol = sequence.front()->getAsSymbolNode(); |
135 | if (symbol == nullptr) |
136 | { |
137 | continue; |
138 | } |
139 | |
140 | const TType &type = symbol->getType(); |
141 | if (IsDefaultUniform(type)) |
142 | { |
143 | TType *fieldType = new TType(type); |
144 | |
145 | uniformList->push_back(x: new TField(fieldType, symbol->getName(), symbol->getLine(), |
146 | symbol->variable().symbolType())); |
147 | uniformVars.push_back(x: &symbol->variable()); |
148 | } |
149 | } |
150 | |
151 | TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); |
152 | layoutQualifier.blockStorage = EbsStd140; |
153 | const TVariable *uniformBlock = DeclareInterfaceBlock( |
154 | root, symbolTable, fieldList: uniformList, qualifier: EvqUniform, layoutQualifier, memoryQualifier: TMemoryQualifier::Create(), arraySize: 0, |
155 | blockTypeName: kDefaultUniformsBlockName, blockVariableName: ImmutableString("")); |
156 | |
157 | compiler->assignSpirvId(uniqueId: uniformBlock->getType().getInterfaceBlock()->uniqueId(), |
158 | spirvId: vk::spirv::kIdDefaultUniformsBlock); |
159 | |
160 | // Create a map from the uniform variables to new variables that reference the fields of the |
161 | // block. |
162 | VariableReplacementMap variableMap; |
163 | for (size_t fieldIndex = 0; fieldIndex < uniformVars.size(); ++fieldIndex) |
164 | { |
165 | const TVariable *variable = uniformVars[fieldIndex]; |
166 | |
167 | TType *replacementType = new TType(variable->getType()); |
168 | replacementType->setInterfaceBlockField(interfaceBlockIn: uniformBlock->getType().getInterfaceBlock(), |
169 | fieldIndex); |
170 | |
171 | TVariable *replacementVariable = |
172 | new TVariable(symbolTable, variable->name(), replacementType, variable->symbolType()); |
173 | |
174 | variableMap[variable] = new TIntermSymbol(replacementVariable); |
175 | } |
176 | |
177 | // Finally transform the AST and make sure references to the uniforms are replaced with the new |
178 | // variables. |
179 | ReplaceDefaultUniformsTraverser defaultTraverser(variableMap); |
180 | root->traverse(it: &defaultTraverser); |
181 | return defaultTraverser.updateTree(compiler, node: root); |
182 | } |
183 | |
184 | // Replaces a builtin variable with a version that is rotated and corrects the X and Y coordinates. |
185 | [[nodiscard]] bool RotateAndFlipBuiltinVariable(TCompiler *compiler, |
186 | TIntermBlock *root, |
187 | TIntermSequence *insertSequence, |
188 | TIntermTyped *swapXY, |
189 | TIntermTyped *flipXY, |
190 | TSymbolTable *symbolTable, |
191 | const TVariable *builtin, |
192 | const ImmutableString &flippedVariableName, |
193 | TIntermTyped *pivot) |
194 | { |
195 | // Create a symbol reference to 'builtin'. |
196 | TIntermSymbol *builtinRef = new TIntermSymbol(builtin); |
197 | |
198 | // Create a symbol reference to our new variable that will hold the modified builtin. |
199 | TType *type = new TType(builtin->getType()); |
200 | type->setQualifier(EvqGlobal); |
201 | type->setPrimarySize(builtin->getType().getNominalSize()); |
202 | TVariable *replacementVar = |
203 | new TVariable(symbolTable, flippedVariableName, type, SymbolType::AngleInternal); |
204 | DeclareGlobalVariable(root, variable: replacementVar); |
205 | TIntermSymbol *flippedBuiltinRef = new TIntermSymbol(replacementVar); |
206 | |
207 | // Use this new variable instead of 'builtin' everywhere. |
208 | if (!ReplaceVariable(compiler, root, toBeReplaced: builtin, replacement: replacementVar)) |
209 | { |
210 | return false; |
211 | } |
212 | |
213 | // Create the expression "(swapXY ? builtin.yx : builtin.xy)" |
214 | TIntermTyped *builtinXY = new TIntermSwizzle(builtinRef, {0, 1}); |
215 | TIntermTyped *builtinYX = new TIntermSwizzle(builtinRef->deepCopy(), {1, 0}); |
216 | |
217 | builtinXY = new TIntermTernary(swapXY, builtinYX, builtinXY); |
218 | |
219 | // Create the expression "(builtin.xy - pivot) * flipXY + pivot |
220 | TIntermBinary *removePivot = new TIntermBinary(EOpSub, builtinXY, pivot); |
221 | TIntermBinary *inverseXY = new TIntermBinary(EOpMul, removePivot, flipXY); |
222 | TIntermBinary *plusPivot = new TIntermBinary(EOpAdd, inverseXY, pivot->deepCopy()); |
223 | |
224 | // Create the corrected variable and copy the value of the original builtin. |
225 | TIntermBinary *assignment = |
226 | new TIntermBinary(EOpAssign, flippedBuiltinRef, builtinRef->deepCopy()); |
227 | |
228 | // Create an assignment to the replaced variable's .xy. |
229 | TIntermSwizzle *correctedXY = new TIntermSwizzle(flippedBuiltinRef->deepCopy(), {0, 1}); |
230 | TIntermBinary *assignToXY = new TIntermBinary(EOpAssign, correctedXY, plusPivot); |
231 | |
232 | // Add this assigment at the beginning of the main function |
233 | insertSequence->insert(position: insertSequence->begin(), x: assignToXY); |
234 | insertSequence->insert(position: insertSequence->begin(), x: assignment); |
235 | |
236 | return compiler->validateAST(root); |
237 | } |
238 | |
239 | TIntermSequence *GetMainSequence(TIntermBlock *root) |
240 | { |
241 | TIntermFunctionDefinition *main = FindMain(root); |
242 | return main->getBody()->getSequence(); |
243 | } |
244 | |
245 | // Declares a new variable to replace gl_DepthRange, its values are fed from a driver uniform. |
246 | [[nodiscard]] bool ReplaceGLDepthRangeWithDriverUniform(TCompiler *compiler, |
247 | TIntermBlock *root, |
248 | const DriverUniform *driverUniforms, |
249 | TSymbolTable *symbolTable) |
250 | { |
251 | // Create a symbol reference to "gl_DepthRange" |
252 | const TVariable *depthRangeVar = static_cast<const TVariable *>( |
253 | symbolTable->findBuiltIn(name: ImmutableString("gl_DepthRange"), shaderVersion: 0)); |
254 | |
255 | // ANGLEUniforms.depthRange |
256 | TIntermTyped *angleEmulatedDepthRangeRef = driverUniforms->getDepthRange(); |
257 | |
258 | // Use this variable instead of gl_DepthRange everywhere. |
259 | return ReplaceVariableWithTyped(compiler, root, toBeReplaced: depthRangeVar, replacement: angleEmulatedDepthRangeRef); |
260 | } |
261 | |
262 | // Declares a new variable to replace gl_BoundingBoxEXT, its values are fed from a global temporary |
263 | // variable. |
264 | [[nodiscard]] bool ReplaceGLBoundingBoxWithGlobal(TCompiler *compiler, |
265 | TIntermBlock *root, |
266 | TSymbolTable *symbolTable, |
267 | int shaderVersion) |
268 | { |
269 | // Declare the replacement bounding box variable type |
270 | TType *emulatedBoundingBoxDeclType = new TType(EbtFloat, EbpHigh, EvqGlobal, 4); |
271 | emulatedBoundingBoxDeclType->makeArray(s: 2u); |
272 | |
273 | TVariable *ANGLEBoundingBoxVar = new TVariable( |
274 | symbolTable->nextUniqueId(), ImmutableString("ANGLEBoundingBox"), SymbolType::AngleInternal, |
275 | TExtension::EXT_primitive_bounding_box, emulatedBoundingBoxDeclType); |
276 | |
277 | DeclareGlobalVariable(root, variable: ANGLEBoundingBoxVar); |
278 | |
279 | const TVariable *builtinBoundingBoxVar; |
280 | bool replacementResult = true; |
281 | |
282 | // Create a symbol reference to "gl_BoundingBoxEXT" |
283 | builtinBoundingBoxVar = static_cast<const TVariable *>( |
284 | symbolTable->findBuiltIn(name: ImmutableString("gl_BoundingBoxEXT"), shaderVersion)); |
285 | if (builtinBoundingBoxVar != nullptr) |
286 | { |
287 | // Use the replacement variable instead of builtin gl_BoundingBoxEXT everywhere. |
288 | replacementResult &= |
289 | ReplaceVariable(compiler, root, toBeReplaced: builtinBoundingBoxVar, replacement: ANGLEBoundingBoxVar); |
290 | } |
291 | |
292 | // Create a symbol reference to "gl_BoundingBoxOES" |
293 | builtinBoundingBoxVar = static_cast<const TVariable *>( |
294 | symbolTable->findBuiltIn(name: ImmutableString("gl_BoundingBoxOES"), shaderVersion)); |
295 | if (builtinBoundingBoxVar != nullptr) |
296 | { |
297 | // Use the replacement variable instead of builtin gl_BoundingBoxOES everywhere. |
298 | replacementResult &= |
299 | ReplaceVariable(compiler, root, toBeReplaced: builtinBoundingBoxVar, replacement: ANGLEBoundingBoxVar); |
300 | } |
301 | |
302 | if (shaderVersion >= 320) |
303 | { |
304 | // Create a symbol reference to "gl_BoundingBox" |
305 | builtinBoundingBoxVar = static_cast<const TVariable *>( |
306 | symbolTable->findBuiltIn(name: ImmutableString("gl_BoundingBox"), shaderVersion)); |
307 | if (builtinBoundingBoxVar != nullptr) |
308 | { |
309 | // Use the replacement variable instead of builtin gl_BoundingBox everywhere. |
310 | replacementResult &= |
311 | ReplaceVariable(compiler, root, toBeReplaced: builtinBoundingBoxVar, replacement: ANGLEBoundingBoxVar); |
312 | } |
313 | } |
314 | return replacementResult; |
315 | } |
316 | |
317 | [[nodiscard]] bool AddXfbEmulationSupport(TranslatorSPIRV *compiler, |
318 | TIntermBlock *root, |
319 | TSymbolTable *symbolTable, |
320 | const DriverUniform *driverUniforms) |
321 | { |
322 | // Generate the following function and place it before main(). This function takes a "strides" |
323 | // parameter that is determined at link time, and calculates for each transform feedback buffer |
324 | // (of which there are a maximum of four) what the starting index is to write to the output |
325 | // buffer. |
326 | // |
327 | // ivec4 ANGLEGetXfbOffsets(ivec4 strides) |
328 | // { |
329 | // int xfbIndex = gl_VertexIndex |
330 | // + gl_InstanceIndex * ANGLEUniforms.xfbVerticesPerInstance; |
331 | // return ANGLEUniforms.xfbBufferOffsets + xfbIndex * strides; |
332 | // } |
333 | |
334 | constexpr uint32_t kMaxXfbBuffers = 4; |
335 | |
336 | const TType *ivec4Type = StaticType::GetBasic<EbtInt, EbpHigh, kMaxXfbBuffers>(); |
337 | TType *stridesType = new TType(*ivec4Type); |
338 | stridesType->setQualifier(EvqParamConst); |
339 | |
340 | // Create the parameter variable. |
341 | TVariable *stridesVar = new TVariable(symbolTable, ImmutableString("strides"), stridesType, |
342 | SymbolType::AngleInternal); |
343 | TIntermSymbol *stridesSymbol = new TIntermSymbol(stridesVar); |
344 | |
345 | // Create references to gl_VertexIndex, gl_InstanceIndex, ANGLEUniforms.xfbVerticesPerInstance |
346 | // and ANGLEUniforms.xfbBufferOffsets. |
347 | TIntermSymbol *vertexIndex = new TIntermSymbol(BuiltInVariable::gl_VertexIndex()); |
348 | TIntermSymbol *instanceIndex = new TIntermSymbol(BuiltInVariable::gl_InstanceIndex()); |
349 | TIntermTyped *xfbVerticesPerInstance = driverUniforms->getXfbVerticesPerInstance(); |
350 | TIntermTyped *xfbBufferOffsets = driverUniforms->getXfbBufferOffsets(); |
351 | |
352 | // gl_InstanceIndex * ANGLEUniforms.xfbVerticesPerInstance |
353 | TIntermBinary *xfbInstanceIndex = |
354 | new TIntermBinary(EOpMul, instanceIndex, xfbVerticesPerInstance); |
355 | |
356 | // gl_VertexIndex + |xfbInstanceIndex| |
357 | TIntermBinary *xfbIndex = new TIntermBinary(EOpAdd, vertexIndex, xfbInstanceIndex); |
358 | |
359 | // |xfbIndex| * |strides| |
360 | TIntermBinary *xfbStrides = new TIntermBinary(EOpVectorTimesScalar, xfbIndex, stridesSymbol); |
361 | |
362 | // ANGLEUniforms.xfbBufferOffsets + |xfbStrides| |
363 | TIntermBinary *xfbOffsets = new TIntermBinary(EOpAdd, xfbBufferOffsets, xfbStrides); |
364 | |
365 | // Create the function body, which has a single return statement. Note that the `xfbIndex` |
366 | // variable declared in the comment at the beginning of this function is simply replaced in the |
367 | // return statement for brevity. |
368 | TIntermBlock *body = new TIntermBlock; |
369 | body->appendStatement(statement: new TIntermBranch(EOpReturn, xfbOffsets)); |
370 | |
371 | // Declare the function |
372 | TFunction *getOffsetsFunction = |
373 | new TFunction(symbolTable, ImmutableString("ANGLEGetXfbOffsets"), SymbolType::AngleInternal, |
374 | ivec4Type, true); |
375 | getOffsetsFunction->addParameter(p: stridesVar); |
376 | |
377 | compiler->assignSpirvId(uniqueId: getOffsetsFunction->uniqueId(), |
378 | spirvId: vk::spirv::kIdXfbEmulationGetOffsetsFunction); |
379 | |
380 | TIntermFunctionDefinition *functionDef = |
381 | CreateInternalFunctionDefinitionNode(func: *getOffsetsFunction, functionBody: body); |
382 | |
383 | // Insert the function declaration before main(). |
384 | const size_t mainIndex = FindMainIndex(root); |
385 | root->insertChildNodes(position: mainIndex, insertions: {functionDef}); |
386 | |
387 | // Generate the following function and place it before main(). This function will be filled |
388 | // with transform feedback capture code at link time. |
389 | // |
390 | // void ANGLECaptureXfb() |
391 | // { |
392 | // } |
393 | const TType *voidType = StaticType::GetBasic<EbtVoid, EbpUndefined>(); |
394 | |
395 | // Create the function body, which is empty. |
396 | body = new TIntermBlock; |
397 | |
398 | // Declare the function |
399 | TFunction *xfbCaptureFunction = new TFunction(symbolTable, ImmutableString("ANGLECaptureXfb"), |
400 | SymbolType::AngleInternal, voidType, false); |
401 | |
402 | compiler->assignSpirvId(uniqueId: xfbCaptureFunction->uniqueId(), |
403 | spirvId: vk::spirv::kIdXfbEmulationCaptureFunction); |
404 | |
405 | // Insert the function declaration before main(). |
406 | root->insertChildNodes(position: mainIndex, |
407 | insertions: {CreateInternalFunctionDefinitionNode(func: *xfbCaptureFunction, functionBody: body)}); |
408 | |
409 | // Create the following logic and add it at the end of main(): |
410 | // |
411 | // ANGLECaptureXfb(); |
412 | // |
413 | |
414 | // Create the function call |
415 | TIntermAggregate *captureXfbCall = |
416 | TIntermAggregate::CreateFunctionCall(func: *xfbCaptureFunction, arguments: {}); |
417 | |
418 | // Run it at the end of the shader. |
419 | if (!RunAtTheEndOfShader(compiler, root, codeToRun: captureXfbCall, symbolTable)) |
420 | { |
421 | return false; |
422 | } |
423 | |
424 | // Additionally, generate the following storage buffer declarations used to capture transform |
425 | // feedback output. Again, there's a maximum of four buffers. |
426 | // |
427 | // buffer ANGLEXfbBuffer0 |
428 | // { |
429 | // float xfbOut[]; |
430 | // } ANGLEXfb0; |
431 | // buffer ANGLEXfbBuffer1 |
432 | // { |
433 | // float xfbOut[]; |
434 | // } ANGLEXfb1; |
435 | // ... |
436 | |
437 | for (uint32_t bufferIndex = 0; bufferIndex < kMaxXfbBuffers; ++bufferIndex) |
438 | { |
439 | TFieldList *fieldList = new TFieldList; |
440 | TType *xfbOutType = new TType(EbtFloat, EbpHigh, EvqGlobal); |
441 | xfbOutType->makeArray(s: 0); |
442 | |
443 | TField *field = new TField(xfbOutType, ImmutableString("xfbOut"), TSourceLoc(), |
444 | SymbolType::AngleInternal); |
445 | |
446 | fieldList->push_back(x: field); |
447 | |
448 | static_assert( |
449 | kMaxXfbBuffers < 10, |
450 | "ImmutableStringBuilder memory size below needs to accomodate the number of buffers"); |
451 | |
452 | ImmutableStringBuilder blockName(strlen(s: "ANGLEXfbBuffer") + 2); |
453 | blockName << "ANGLEXfbBuffer"; |
454 | blockName.appendDecimal(i: bufferIndex); |
455 | |
456 | ImmutableStringBuilder varName(strlen(s: "ANGLEXfb") + 2); |
457 | varName << "ANGLEXfb"; |
458 | varName.appendDecimal(i: bufferIndex); |
459 | |
460 | TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); |
461 | layoutQualifier.blockStorage = EbsStd430; |
462 | |
463 | const TVariable *xfbBuffer = |
464 | DeclareInterfaceBlock(root, symbolTable, fieldList, qualifier: EvqBuffer, layoutQualifier, |
465 | memoryQualifier: TMemoryQualifier::Create(), arraySize: 0, blockTypeName: blockName, blockVariableName: varName); |
466 | |
467 | static_assert(vk::spirv::kIdXfbEmulationBufferBlockOne == |
468 | vk::spirv::kIdXfbEmulationBufferBlockZero + 1); |
469 | static_assert(vk::spirv::kIdXfbEmulationBufferBlockTwo == |
470 | vk::spirv::kIdXfbEmulationBufferBlockZero + 2); |
471 | static_assert(vk::spirv::kIdXfbEmulationBufferBlockThree == |
472 | vk::spirv::kIdXfbEmulationBufferBlockZero + 3); |
473 | |
474 | static_assert(vk::spirv::kIdXfbEmulationBufferVarOne == |
475 | vk::spirv::kIdXfbEmulationBufferVarZero + 1); |
476 | static_assert(vk::spirv::kIdXfbEmulationBufferVarTwo == |
477 | vk::spirv::kIdXfbEmulationBufferVarZero + 2); |
478 | static_assert(vk::spirv::kIdXfbEmulationBufferVarThree == |
479 | vk::spirv::kIdXfbEmulationBufferVarZero + 3); |
480 | |
481 | compiler->assignSpirvId(uniqueId: xfbBuffer->getType().getInterfaceBlock()->uniqueId(), |
482 | spirvId: vk::spirv::kIdXfbEmulationBufferBlockZero + bufferIndex); |
483 | compiler->assignSpirvId(uniqueId: xfbBuffer->uniqueId(), |
484 | spirvId: vk::spirv::kIdXfbEmulationBufferVarZero + bufferIndex); |
485 | } |
486 | |
487 | return compiler->validateAST(root); |
488 | } |
489 | |
490 | [[nodiscard]] bool AddXfbExtensionSupport(TranslatorSPIRV *compiler, |
491 | TIntermBlock *root, |
492 | TSymbolTable *symbolTable, |
493 | const DriverUniform *driverUniforms) |
494 | { |
495 | // Generate the following output varying declaration used to capture transform feedback output |
496 | // from gl_Position, as it can't be captured directly due to changes that are applied to it for |
497 | // clip-space correction and pre-rotation. |
498 | // |
499 | // out vec4 ANGLEXfbPosition; |
500 | |
501 | const TType *vec4Type = nullptr; |
502 | |
503 | switch (compiler->getShaderType()) |
504 | { |
505 | case GL_VERTEX_SHADER: |
506 | vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqVertexOut, 4, 1>(); |
507 | break; |
508 | case GL_TESS_EVALUATION_SHADER_EXT: |
509 | vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqTessEvaluationOut, 4, 1>(); |
510 | break; |
511 | case GL_GEOMETRY_SHADER_EXT: |
512 | vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqGeometryOut, 4, 1>(); |
513 | break; |
514 | default: |
515 | UNREACHABLE(); |
516 | } |
517 | |
518 | TVariable *varyingVar = new TVariable(symbolTable, ImmutableString("ANGLEXfbPosition"), |
519 | vec4Type, SymbolType::AngleInternal); |
520 | |
521 | compiler->assignSpirvId(uniqueId: varyingVar->uniqueId(), spirvId: vk::spirv::kIdXfbExtensionPosition); |
522 | |
523 | TIntermDeclaration *varyingDecl = new TIntermDeclaration(); |
524 | varyingDecl->appendDeclarator(declarator: new TIntermSymbol(varyingVar)); |
525 | |
526 | // Insert the varying declaration before the first function. |
527 | const size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root); |
528 | root->insertChildNodes(position: firstFunctionIndex, insertions: {varyingDecl}); |
529 | |
530 | return compiler->validateAST(root); |
531 | } |
532 | |
533 | [[nodiscard]] bool AddVertexTransformationSupport(TranslatorSPIRV *compiler, |
534 | const ShCompileOptions &compileOptions, |
535 | TIntermBlock *root, |
536 | TSymbolTable *symbolTable, |
537 | SpecConst *specConst, |
538 | const DriverUniform *driverUniforms) |
539 | { |
540 | // In GL the viewport transformation is slightly different - see the GL 2.0 spec section "2.12.1 |
541 | // Controlling the Viewport". In Vulkan the corresponding spec section is currently "23.4. |
542 | // Coordinate Transformations". The following transformation needs to be done: |
543 | // |
544 | // z_vk = 0.5 * (w_gl + z_gl) |
545 | // |
546 | // where z_vk is the depth output of a Vulkan geometry-stage shader and z_gl is the same for GL. |
547 | // |
548 | // Generate the following function and place it before main(). This function takes |
549 | // gl_Position and rotates xy, and adjusts z (if necessary). |
550 | // |
551 | // vec4 ANGLETransformPosition(vec4 position) |
552 | // { |
553 | // return vec4((swapXY ? position.yx : position.xy) * flipXY, |
554 | // transformDepth ? (gl_Position.z + gl_Position.w) / 2 : gl_Position.z, |
555 | // gl_Postion.w); |
556 | // } |
557 | |
558 | const TType *vec4Type = StaticType::GetBasic<EbtFloat, EbpHigh, 4>(); |
559 | TType *positionType = new TType(*vec4Type); |
560 | positionType->setQualifier(EvqParamConst); |
561 | |
562 | // Create the parameter variable. |
563 | TVariable *positionVar = new TVariable(symbolTable, ImmutableString("position"), positionType, |
564 | SymbolType::AngleInternal); |
565 | TIntermSymbol *positionSymbol = new TIntermSymbol(positionVar); |
566 | |
567 | // swapXY ? position.yx : position.xy |
568 | TIntermTyped *swapXY = specConst->getSwapXY(); |
569 | if (swapXY == nullptr) |
570 | { |
571 | swapXY = driverUniforms->getSwapXY(); |
572 | } |
573 | |
574 | TIntermTyped *xy = new TIntermSwizzle(positionSymbol, {0, 1}); |
575 | TIntermTyped *swappedXY = new TIntermSwizzle(positionSymbol->deepCopy(), {1, 0}); |
576 | TIntermTyped *rotatedXY = new TIntermTernary(swapXY, swappedXY, xy); |
577 | |
578 | // (swapXY ? position.yx : position.xy) * flipXY |
579 | TIntermTyped *flipXY = driverUniforms->getFlipXY(symbolTable, stage: DriverUniformFlip::PreFragment); |
580 | TIntermTyped *rotatedFlippedXY = new TIntermBinary(EOpMul, rotatedXY, flipXY); |
581 | |
582 | // (gl_Position.z + gl_Position.w) / 2 |
583 | TIntermTyped *z = new TIntermSwizzle(positionSymbol->deepCopy(), {2}); |
584 | TIntermTyped *w = new TIntermSwizzle(positionSymbol->deepCopy(), {3}); |
585 | |
586 | TIntermTyped *transformedDepth = z; |
587 | if (compileOptions.addVulkanDepthCorrection) |
588 | { |
589 | TIntermBinary *zPlusW = new TIntermBinary(EOpAdd, z, w->deepCopy()); |
590 | TIntermBinary *halfZPlusW = |
591 | new TIntermBinary(EOpMul, zPlusW, CreateFloatNode(value: 0.5, precision: EbpMedium)); |
592 | |
593 | // transformDepth ? (gl_Position.z + gl_Position.w) / 2 : gl_Position.z, |
594 | TIntermTyped *transformDepth = driverUniforms->getTransformDepth(); |
595 | transformedDepth = new TIntermTernary(transformDepth, halfZPlusW, z->deepCopy()); |
596 | } |
597 | |
598 | // vec4(...); |
599 | TIntermSequence args = { |
600 | rotatedFlippedXY, |
601 | transformedDepth, |
602 | w, |
603 | }; |
604 | TIntermTyped *transformedPosition = TIntermAggregate::CreateConstructor(type: *vec4Type, arguments: &args); |
605 | |
606 | // Create the function body, which has a single return statement. |
607 | TIntermBlock *body = new TIntermBlock; |
608 | body->appendStatement(statement: new TIntermBranch(EOpReturn, transformedPosition)); |
609 | |
610 | // Declare the function |
611 | TFunction *transformPositionFunction = |
612 | new TFunction(symbolTable, ImmutableString("ANGLETransformPosition"), |
613 | SymbolType::AngleInternal, vec4Type, true); |
614 | transformPositionFunction->addParameter(p: positionVar); |
615 | |
616 | compiler->assignSpirvId(uniqueId: transformPositionFunction->uniqueId(), |
617 | spirvId: vk::spirv::kIdTransformPositionFunction); |
618 | |
619 | TIntermFunctionDefinition *functionDef = |
620 | CreateInternalFunctionDefinitionNode(func: *transformPositionFunction, functionBody: body); |
621 | |
622 | // Insert the function declaration before main(). |
623 | const size_t mainIndex = FindMainIndex(root); |
624 | root->insertChildNodes(position: mainIndex, insertions: {functionDef}); |
625 | |
626 | return compiler->validateAST(root); |
627 | } |
628 | |
629 | [[nodiscard]] bool InsertFragCoordCorrection(TCompiler *compiler, |
630 | const ShCompileOptions &compileOptions, |
631 | TIntermBlock *root, |
632 | TIntermSequence *insertSequence, |
633 | TSymbolTable *symbolTable, |
634 | SpecConst *specConst, |
635 | const DriverUniform *driverUniforms) |
636 | { |
637 | TIntermTyped *flipXY = driverUniforms->getFlipXY(symbolTable, stage: DriverUniformFlip::Fragment); |
638 | TIntermTyped *pivot = driverUniforms->getHalfRenderArea(); |
639 | |
640 | TIntermTyped *swapXY = specConst->getSwapXY(); |
641 | if (swapXY == nullptr) |
642 | { |
643 | swapXY = driverUniforms->getSwapXY(); |
644 | } |
645 | |
646 | const TVariable *fragCoord = static_cast<const TVariable *>( |
647 | symbolTable->findBuiltIn(name: ImmutableString("gl_FragCoord"), shaderVersion: compiler->getShaderVersion())); |
648 | return RotateAndFlipBuiltinVariable(compiler, root, insertSequence, swapXY, flipXY, symbolTable, |
649 | builtin: fragCoord, flippedVariableName: kFlippedFragCoordName, pivot); |
650 | } |
651 | |
652 | bool HasFramebufferFetch(const TExtensionBehavior &extBehavior, |
653 | const ShCompileOptions &compileOptions) |
654 | { |
655 | return IsExtensionEnabled(extBehavior, extension: TExtension::EXT_shader_framebuffer_fetch) || |
656 | IsExtensionEnabled(extBehavior, extension: TExtension::EXT_shader_framebuffer_fetch_non_coherent) || |
657 | IsExtensionEnabled(extBehavior, extension: TExtension::ARM_shader_framebuffer_fetch) || |
658 | IsExtensionEnabled(extBehavior, extension: TExtension::NV_shader_framebuffer_fetch) || |
659 | (compileOptions.pls.type == ShPixelLocalStorageType::FramebufferFetch && |
660 | IsExtensionEnabled(extBehavior, extension: TExtension::ANGLE_shader_pixel_local_storage)); |
661 | } |
662 | |
663 | template <typename Variable> |
664 | Variable *FindShaderVariable(std::vector<Variable> *vars, const ImmutableString &name) |
665 | { |
666 | for (Variable &var : *vars) |
667 | { |
668 | if (name == var.name) |
669 | { |
670 | return &var; |
671 | } |
672 | } |
673 | UNREACHABLE(); |
674 | return nullptr; |
675 | } |
676 | |
677 | ShaderVariable *FindIOBlockShaderVariable(std::vector<ShaderVariable> *vars, |
678 | const ImmutableString &name) |
679 | { |
680 | for (ShaderVariable &var : *vars) |
681 | { |
682 | if (name == var.structOrBlockName) |
683 | { |
684 | return &var; |
685 | } |
686 | } |
687 | UNREACHABLE(); |
688 | return nullptr; |
689 | } |
690 | |
691 | ShaderVariable *FindUniformFieldShaderVariable(std::vector<ShaderVariable> *vars, |
692 | const ImmutableString &name, |
693 | const char *prefix) |
694 | { |
695 | for (ShaderVariable &var : *vars) |
696 | { |
697 | // The name of the sampler is derived from the uniform name + fields |
698 | // that reach the uniform, concatenated with '_' per RewriteStructSamplers. |
699 | std::string varName = prefix; |
700 | varName += '_'; |
701 | varName += var.name; |
702 | |
703 | if (name == varName) |
704 | { |
705 | return &var; |
706 | } |
707 | |
708 | ShaderVariable *field = FindUniformFieldShaderVariable(vars: &var.fields, name, prefix: varName.c_str()); |
709 | if (field != nullptr) |
710 | { |
711 | return field; |
712 | } |
713 | } |
714 | return nullptr; |
715 | } |
716 | |
717 | ShaderVariable *FindUniformShaderVariable(std::vector<ShaderVariable> *vars, |
718 | const ImmutableString &name) |
719 | { |
720 | for (ShaderVariable &var : *vars) |
721 | { |
722 | if (name == var.name) |
723 | { |
724 | return &var; |
725 | } |
726 | |
727 | // Note: samplers in structs are moved out. Such samplers will be found in the fields of |
728 | // the struct uniform. |
729 | ShaderVariable *field = FindUniformFieldShaderVariable(vars: &var.fields, name, prefix: var.name.c_str()); |
730 | if (field != nullptr) |
731 | { |
732 | return field; |
733 | } |
734 | } |
735 | UNREACHABLE(); |
736 | return nullptr; |
737 | } |
738 | |
739 | void SetSpirvIdInFields(uint32_t id, std::vector<ShaderVariable> *fields) |
740 | { |
741 | for (ShaderVariable &field : *fields) |
742 | { |
743 | field.id = id; |
744 | SetSpirvIdInFields(id, fields: &field.fields); |
745 | } |
746 | } |
747 | } // anonymous namespace |
748 | |
749 | TranslatorSPIRV::TranslatorSPIRV(sh::GLenum type, ShShaderSpec spec) |
750 | : TCompiler(type, spec, SH_SPIRV_VULKAN_OUTPUT), mFirstUnusedSpirvId(0) |
751 | {} |
752 | |
753 | bool TranslatorSPIRV::translateImpl(TIntermBlock *root, |
754 | const ShCompileOptions &compileOptions, |
755 | PerformanceDiagnostics * /*perfDiagnostics*/, |
756 | SpecConst *specConst, |
757 | DriverUniform *driverUniforms) |
758 | { |
759 | if (getShaderType() == GL_VERTEX_SHADER) |
760 | { |
761 | if (!ShaderBuiltinsWorkaround(compiler: this, root, symbolTable: &getSymbolTable(), compileOptions)) |
762 | { |
763 | return false; |
764 | } |
765 | } |
766 | |
767 | // Write out default uniforms into a uniform block assigned to a specific set/binding. |
768 | int defaultUniformCount = 0; |
769 | int aggregateTypesUsedForUniforms = 0; |
770 | int r32fImageCount = 0; |
771 | int atomicCounterCount = 0; |
772 | for (const auto &uniform : getUniforms()) |
773 | { |
774 | if (!uniform.isBuiltIn() && uniform.active && !gl::IsOpaqueType(type: uniform.type)) |
775 | { |
776 | ++defaultUniformCount; |
777 | } |
778 | |
779 | if (uniform.isStruct() || uniform.isArrayOfArrays()) |
780 | { |
781 | ++aggregateTypesUsedForUniforms; |
782 | } |
783 | |
784 | if (uniform.active && gl::IsImageType(type: uniform.type) && uniform.imageUnitFormat == GL_R32F) |
785 | { |
786 | ++r32fImageCount; |
787 | } |
788 | |
789 | if (uniform.active && gl::IsAtomicCounterType(type: uniform.type)) |
790 | { |
791 | ++atomicCounterCount; |
792 | } |
793 | } |
794 | |
795 | // Remove declarations of inactive shader interface variables so SPIR-V transformer doesn't need |
796 | // to replace them. Note that currently, CollectVariables marks every field of an active |
797 | // uniform that's of struct type as active, i.e. no extracted sampler is inactive, so this can |
798 | // be done before extracting samplers from structs. |
799 | if (!RemoveInactiveInterfaceVariables(compiler: this, root, symbolTable: &getSymbolTable(), attributes: getAttributes(), |
800 | inputVaryings: getInputVaryings(), outputVariables: getOutputVariables(), uniforms: getUniforms(), |
801 | interfaceBlocks: getInterfaceBlocks(), removeFragmentOutputs: true)) |
802 | { |
803 | return false; |
804 | } |
805 | |
806 | // If there are any function calls that take array-of-array of opaque uniform parameters, or |
807 | // other opaque uniforms that need special handling in Vulkan, such as atomic counters, |
808 | // monomorphize the functions by removing said parameters and replacing them in the function |
809 | // body with the call arguments. |
810 | // |
811 | // This has a few benefits: |
812 | // |
813 | // - It dramatically simplifies future transformations w.r.t to samplers in structs, array of |
814 | // arrays of opaque types, atomic counters etc. |
815 | // - Avoids the need for shader*ArrayDynamicIndexing Vulkan features. |
816 | UnsupportedFunctionArgsBitSet args{UnsupportedFunctionArgs::StructContainingSamplers, |
817 | UnsupportedFunctionArgs::ArrayOfArrayOfSamplerOrImage, |
818 | UnsupportedFunctionArgs::AtomicCounter, |
819 | UnsupportedFunctionArgs::SamplerCubeEmulation, |
820 | UnsupportedFunctionArgs::Image}; |
821 | if (!MonomorphizeUnsupportedFunctions(compiler: this, root, symbolTable: &getSymbolTable(), compileOptions, args)) |
822 | { |
823 | return false; |
824 | } |
825 | |
826 | if (aggregateTypesUsedForUniforms > 0) |
827 | { |
828 | if (!SeparateStructFromUniformDeclarations(compiler: this, root, symbolTable: &getSymbolTable())) |
829 | { |
830 | return false; |
831 | } |
832 | |
833 | int removedUniformsCount; |
834 | |
835 | if (!RewriteStructSamplers(compiler: this, root, symbolTable: &getSymbolTable(), removedUniformsCountOut: &removedUniformsCount)) |
836 | { |
837 | return false; |
838 | } |
839 | defaultUniformCount -= removedUniformsCount; |
840 | } |
841 | |
842 | // Replace array of array of opaque uniforms with a flattened array. This is run after |
843 | // MonomorphizeUnsupportedFunctions and RewriteStructSamplers so that it's not possible for an |
844 | // array of array of opaque type to be partially subscripted and passed to a function. |
845 | if (!RewriteArrayOfArrayOfOpaqueUniforms(compiler: this, root, symbolTable: &getSymbolTable())) |
846 | { |
847 | return false; |
848 | } |
849 | |
850 | // Rewrite samplerCubes as sampler2DArrays. This must be done after rewriting struct samplers |
851 | // as it doesn't expect that. |
852 | if (compileOptions.emulateSeamfulCubeMapSampling) |
853 | { |
854 | if (!RewriteCubeMapSamplersAs2DArray(compiler: this, root, symbolTable: &getSymbolTable(), |
855 | isFragmentShader: getShaderType() == GL_FRAGMENT_SHADER)) |
856 | { |
857 | return false; |
858 | } |
859 | } |
860 | |
861 | if (!FlagSamplersForTexelFetch(compiler: this, root, symbolTable: &getSymbolTable(), uniforms: &mUniforms)) |
862 | { |
863 | return false; |
864 | } |
865 | |
866 | gl::ShaderType packedShaderType = gl::FromGLenum<gl::ShaderType>(from: getShaderType()); |
867 | |
868 | if (defaultUniformCount > 0) |
869 | { |
870 | if (!DeclareDefaultUniforms(compiler: this, root, symbolTable: &getSymbolTable(), shaderType: packedShaderType)) |
871 | { |
872 | return false; |
873 | } |
874 | } |
875 | |
876 | if (getShaderType() == GL_COMPUTE_SHADER) |
877 | { |
878 | driverUniforms->addComputeDriverUniformsToShader(root, symbolTable: &getSymbolTable()); |
879 | } |
880 | else |
881 | { |
882 | driverUniforms->addGraphicsDriverUniformsToShader(root, symbolTable: &getSymbolTable()); |
883 | } |
884 | |
885 | assignSpirvId( |
886 | uniqueId: driverUniforms->getDriverUniformsVariable()->getType().getInterfaceBlock()->uniqueId(), |
887 | spirvId: vk::spirv::kIdDriverUniformsBlock); |
888 | |
889 | if (r32fImageCount > 0) |
890 | { |
891 | if (!RewriteR32fImages(compiler: this, root, symbolTable: &getSymbolTable())) |
892 | { |
893 | return false; |
894 | } |
895 | } |
896 | |
897 | if (atomicCounterCount > 0) |
898 | { |
899 | // ANGLEUniforms.acbBufferOffsets |
900 | const TIntermTyped *acbBufferOffsets = driverUniforms->getAcbBufferOffsets(); |
901 | const TVariable *atomicCounters = nullptr; |
902 | if (!RewriteAtomicCounters(compiler: this, root, symbolTable: &getSymbolTable(), acbBufferOffsets, |
903 | atomicCountersOut: &atomicCounters)) |
904 | { |
905 | return false; |
906 | } |
907 | assignSpirvId(uniqueId: atomicCounters->getType().getInterfaceBlock()->uniqueId(), |
908 | spirvId: vk::spirv::kIdAtomicCounterBlock); |
909 | } |
910 | else if (getShaderVersion() >= 310) |
911 | { |
912 | // Vulkan doesn't support Atomic Storage as a Storage Class, but we've seen |
913 | // cases where builtins are using it even with no active atomic counters. |
914 | // This pass simply removes those builtins in that scenario. |
915 | if (!RemoveAtomicCounterBuiltins(compiler: this, root)) |
916 | { |
917 | return false; |
918 | } |
919 | } |
920 | |
921 | if (packedShaderType != gl::ShaderType::Compute) |
922 | { |
923 | if (!ReplaceGLDepthRangeWithDriverUniform(compiler: this, root, driverUniforms, symbolTable: &getSymbolTable())) |
924 | { |
925 | return false; |
926 | } |
927 | |
928 | // Search for the gl_ClipDistance/gl_CullDistance usage, if its used, we need to do some |
929 | // replacements. |
930 | bool useClipDistance = false; |
931 | bool useCullDistance = false; |
932 | for (const ShaderVariable &outputVarying : mOutputVaryings) |
933 | { |
934 | if (outputVarying.name == "gl_ClipDistance") |
935 | { |
936 | useClipDistance = true; |
937 | } |
938 | else if (outputVarying.name == "gl_CullDistance") |
939 | { |
940 | useCullDistance = true; |
941 | } |
942 | } |
943 | for (const ShaderVariable &inputVarying : mInputVaryings) |
944 | { |
945 | if (inputVarying.name == "gl_ClipDistance") |
946 | { |
947 | useClipDistance = true; |
948 | } |
949 | else if (inputVarying.name == "gl_CullDistance") |
950 | { |
951 | useCullDistance = true; |
952 | } |
953 | } |
954 | |
955 | if (useClipDistance && |
956 | !ReplaceClipDistanceAssignments(compiler: this, root, symbolTable: &getSymbolTable(), shaderType: getShaderType(), |
957 | clipDistanceEnableFlags: driverUniforms->getClipDistancesEnabled())) |
958 | { |
959 | return false; |
960 | } |
961 | if (useCullDistance && |
962 | !ReplaceCullDistanceAssignments(compiler: this, root, symbolTable: &getSymbolTable(), shaderType: getShaderType())) |
963 | { |
964 | return false; |
965 | } |
966 | } |
967 | |
968 | if (gl::ShaderTypeSupportsTransformFeedback(shaderType: packedShaderType)) |
969 | { |
970 | if (compileOptions.addVulkanXfbExtensionSupportCode) |
971 | { |
972 | // Add support code for transform feedback extension. |
973 | if (!AddXfbExtensionSupport(compiler: this, root, symbolTable: &getSymbolTable(), driverUniforms)) |
974 | { |
975 | return false; |
976 | } |
977 | } |
978 | |
979 | // Add support code for pre-rotation and depth correction in the vertex processing stages. |
980 | if (!AddVertexTransformationSupport(compiler: this, compileOptions, root, symbolTable: &getSymbolTable(), |
981 | specConst, driverUniforms)) |
982 | { |
983 | return false; |
984 | } |
985 | } |
986 | |
987 | switch (packedShaderType) |
988 | { |
989 | case gl::ShaderType::Fragment: |
990 | { |
991 | bool usesPointCoord = false; |
992 | bool usesFragCoord = false; |
993 | bool usesSampleMaskIn = false; |
994 | bool useSamplePosition = false; |
995 | |
996 | // Search for the gl_PointCoord usage, if its used, we need to flip the y coordinate. |
997 | for (const ShaderVariable &inputVarying : mInputVaryings) |
998 | { |
999 | if (!inputVarying.isBuiltIn()) |
1000 | { |
1001 | continue; |
1002 | } |
1003 | |
1004 | if (inputVarying.name == "gl_SampleMaskIn") |
1005 | { |
1006 | usesSampleMaskIn = true; |
1007 | continue; |
1008 | } |
1009 | |
1010 | if (inputVarying.name == "gl_SamplePosition") |
1011 | { |
1012 | useSamplePosition = true; |
1013 | continue; |
1014 | } |
1015 | |
1016 | if (inputVarying.name == "gl_PointCoord") |
1017 | { |
1018 | usesPointCoord = true; |
1019 | break; |
1020 | } |
1021 | |
1022 | if (inputVarying.name == "gl_FragCoord") |
1023 | { |
1024 | usesFragCoord = true; |
1025 | break; |
1026 | } |
1027 | } |
1028 | |
1029 | bool hasGLSampleMask = false; |
1030 | bool hasGLSecondaryFragData = false; |
1031 | |
1032 | for (const ShaderVariable &outputVar : mOutputVariables) |
1033 | { |
1034 | if (outputVar.name == "gl_SampleMask") |
1035 | { |
1036 | ASSERT(!hasGLSampleMask); |
1037 | hasGLSampleMask = true; |
1038 | continue; |
1039 | } |
1040 | if (outputVar.name == "gl_SecondaryFragDataEXT") |
1041 | { |
1042 | ASSERT(!hasGLSecondaryFragData); |
1043 | hasGLSecondaryFragData = true; |
1044 | continue; |
1045 | } |
1046 | } |
1047 | |
1048 | if (usesPointCoord) |
1049 | { |
1050 | TIntermTyped *flipNegXY = |
1051 | driverUniforms->getNegFlipXY(symbolTable: &getSymbolTable(), stage: DriverUniformFlip::Fragment); |
1052 | TIntermConstantUnion *pivot = CreateFloatNode(value: 0.5f, precision: EbpMedium); |
1053 | TIntermTyped *swapXY = specConst->getSwapXY(); |
1054 | if (swapXY == nullptr) |
1055 | { |
1056 | swapXY = driverUniforms->getSwapXY(); |
1057 | } |
1058 | if (!RotateAndFlipBuiltinVariable( |
1059 | compiler: this, root, insertSequence: GetMainSequence(root), swapXY, flipXY: flipNegXY, symbolTable: &getSymbolTable(), |
1060 | builtin: BuiltInVariable::gl_PointCoord(), flippedVariableName: kFlippedPointCoordName, pivot)) |
1061 | { |
1062 | return false; |
1063 | } |
1064 | } |
1065 | |
1066 | if (useSamplePosition) |
1067 | { |
1068 | TIntermTyped *flipXY = |
1069 | driverUniforms->getFlipXY(symbolTable: &getSymbolTable(), stage: DriverUniformFlip::Fragment); |
1070 | TIntermConstantUnion *pivot = CreateFloatNode(value: 0.5f, precision: EbpMedium); |
1071 | TIntermTyped *swapXY = specConst->getSwapXY(); |
1072 | if (swapXY == nullptr) |
1073 | { |
1074 | swapXY = driverUniforms->getSwapXY(); |
1075 | } |
1076 | |
1077 | const TVariable *samplePositionBuiltin = |
1078 | static_cast<const TVariable *>(getSymbolTable().findBuiltIn( |
1079 | name: ImmutableString("gl_SamplePosition"), shaderVersion: getShaderVersion())); |
1080 | if (!RotateAndFlipBuiltinVariable(compiler: this, root, insertSequence: GetMainSequence(root), swapXY, flipXY, |
1081 | symbolTable: &getSymbolTable(), builtin: samplePositionBuiltin, |
1082 | flippedVariableName: kFlippedPointCoordName, pivot)) |
1083 | { |
1084 | return false; |
1085 | } |
1086 | } |
1087 | |
1088 | if (usesFragCoord) |
1089 | { |
1090 | if (!InsertFragCoordCorrection(compiler: this, compileOptions, root, insertSequence: GetMainSequence(root), |
1091 | symbolTable: &getSymbolTable(), specConst, driverUniforms)) |
1092 | { |
1093 | return false; |
1094 | } |
1095 | } |
1096 | |
1097 | // Emulate gl_FragColor and gl_FragData with normal output variables. |
1098 | if (!EmulateFragColorData(compiler: this, root, symbolTable: &getSymbolTable(), hasGLSecondaryFragData)) |
1099 | { |
1100 | return false; |
1101 | } |
1102 | |
1103 | // Emulate framebuffer fetch if used. |
1104 | if (HasFramebufferFetch(extBehavior: getExtensionBehavior(), compileOptions)) |
1105 | { |
1106 | if (!EmulateFramebufferFetch(compiler: this, root, uniforms: &mUniforms)) |
1107 | { |
1108 | return false; |
1109 | } |
1110 | } |
1111 | |
1112 | // This should be operated after doing ReplaceLastFragData and ReplaceInOutVariables, |
1113 | // because they will create the input attachment variables. AddBlendMainCaller will |
1114 | // check the existing input attachment variables and if there is no existing input |
1115 | // attachment variable then create a new one. |
1116 | if (getAdvancedBlendEquations().any() && |
1117 | compileOptions.addAdvancedBlendEquationsEmulation && |
1118 | !EmulateAdvancedBlendEquations(compiler: this, root, symbolTable: &getSymbolTable(), driverUniforms, |
1119 | uniforms: &mUniforms, advancedBlendEquations: getAdvancedBlendEquations())) |
1120 | { |
1121 | return false; |
1122 | } |
1123 | |
1124 | if (!RewriteDfdy(compiler: this, root, symbolTable: &getSymbolTable(), shaderVersion: getShaderVersion(), specConst, |
1125 | driverUniforms)) |
1126 | { |
1127 | return false; |
1128 | } |
1129 | |
1130 | if (!RewriteInterpolateAtOffset(compiler: this, root, symbolTable: &getSymbolTable(), shaderVersion: getShaderVersion(), |
1131 | specConst, driverUniforms)) |
1132 | { |
1133 | return false; |
1134 | } |
1135 | |
1136 | if (usesSampleMaskIn && !RewriteSampleMaskIn(compiler: this, root, symbolTable: &getSymbolTable())) |
1137 | { |
1138 | return false; |
1139 | } |
1140 | |
1141 | if (hasGLSampleMask) |
1142 | { |
1143 | TIntermTyped *numSamples = driverUniforms->getNumSamples(); |
1144 | if (!RewriteSampleMask(compiler: this, root, symbolTable: &getSymbolTable(), numSamplesUniform: numSamples)) |
1145 | { |
1146 | return false; |
1147 | } |
1148 | } |
1149 | |
1150 | { |
1151 | const TVariable *numSamplesVar = |
1152 | static_cast<const TVariable *>(getSymbolTable().findBuiltIn( |
1153 | name: ImmutableString("gl_NumSamples"), shaderVersion: getShaderVersion())); |
1154 | TIntermTyped *numSamples = driverUniforms->getNumSamples(); |
1155 | if (!ReplaceVariableWithTyped(compiler: this, root, toBeReplaced: numSamplesVar, replacement: numSamples)) |
1156 | { |
1157 | return false; |
1158 | } |
1159 | } |
1160 | |
1161 | if (IsExtensionEnabled(extBehavior: getExtensionBehavior(), extension: TExtension::EXT_YUV_target)) |
1162 | { |
1163 | if (!EmulateYUVBuiltIns(compiler: this, root, symbolTable: &getSymbolTable())) |
1164 | { |
1165 | return false; |
1166 | } |
1167 | } |
1168 | |
1169 | if (!EmulateDithering(compiler: this, compileOptions, root, symbolTable: &getSymbolTable(), specConst, |
1170 | driverUniforms)) |
1171 | { |
1172 | return false; |
1173 | } |
1174 | |
1175 | break; |
1176 | } |
1177 | |
1178 | case gl::ShaderType::Vertex: |
1179 | { |
1180 | if (compileOptions.addVulkanXfbEmulationSupportCode) |
1181 | { |
1182 | // Add support code for transform feedback emulation. Only applies to vertex shader |
1183 | // as tessellation and geometry shader transform feedback capture require |
1184 | // VK_EXT_transform_feedback. |
1185 | if (!AddXfbEmulationSupport(compiler: this, root, symbolTable: &getSymbolTable(), driverUniforms)) |
1186 | { |
1187 | return false; |
1188 | } |
1189 | } |
1190 | |
1191 | break; |
1192 | } |
1193 | |
1194 | case gl::ShaderType::Geometry: |
1195 | break; |
1196 | |
1197 | case gl::ShaderType::TessControl: |
1198 | { |
1199 | if (!ReplaceGLBoundingBoxWithGlobal(compiler: this, root, symbolTable: &getSymbolTable(), shaderVersion: getShaderVersion())) |
1200 | { |
1201 | return false; |
1202 | } |
1203 | break; |
1204 | } |
1205 | |
1206 | case gl::ShaderType::TessEvaluation: |
1207 | break; |
1208 | |
1209 | case gl::ShaderType::Compute: |
1210 | break; |
1211 | |
1212 | default: |
1213 | UNREACHABLE(); |
1214 | break; |
1215 | } |
1216 | |
1217 | specConst->declareSpecConsts(root); |
1218 | mValidateASTOptions.validateSpecConstReferences = true; |
1219 | |
1220 | // Gather specialization constant usage bits so that we can feedback to context. |
1221 | mSpecConstUsageBits = specConst->getSpecConstUsageBits(); |
1222 | |
1223 | if (!validateAST(root)) |
1224 | { |
1225 | return false; |
1226 | } |
1227 | |
1228 | // Make sure function call validation is not accidentally left off anywhere. |
1229 | ASSERT(mValidateASTOptions.validateFunctionCall); |
1230 | ASSERT(mValidateASTOptions.validateNoRawFunctionCalls); |
1231 | |
1232 | // Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help SPIR-V |
1233 | // generation treat them mostly like usual I/O blocks. |
1234 | const TVariable *inputPerVertex = nullptr; |
1235 | const TVariable *outputPerVertex = nullptr; |
1236 | if (!DeclarePerVertexBlocks(compiler: this, root, symbolTable: &getSymbolTable(), inputPerVertexOut: &inputPerVertex, outputPerVertexOut: &outputPerVertex)) |
1237 | { |
1238 | return false; |
1239 | } |
1240 | |
1241 | if (inputPerVertex) |
1242 | { |
1243 | assignSpirvId(uniqueId: inputPerVertex->getType().getInterfaceBlock()->uniqueId(), |
1244 | spirvId: vk::spirv::kIdInputPerVertexBlock); |
1245 | } |
1246 | if (outputPerVertex) |
1247 | { |
1248 | assignSpirvId(uniqueId: outputPerVertex->getType().getInterfaceBlock()->uniqueId(), |
1249 | spirvId: vk::spirv::kIdOutputPerVertexBlock); |
1250 | assignSpirvId(uniqueId: outputPerVertex->uniqueId(), spirvId: vk::spirv::kIdOutputPerVertexVar); |
1251 | } |
1252 | |
1253 | // Now that all transformations are done, assign SPIR-V ids to whatever shader variable is still |
1254 | // present in the shader in some form. This should be the last thing done in this function. |
1255 | assignSpirvIds(root); |
1256 | |
1257 | return true; |
1258 | } |
1259 | |
1260 | bool TranslatorSPIRV::translate(TIntermBlock *root, |
1261 | const ShCompileOptions &compileOptions, |
1262 | PerformanceDiagnostics *perfDiagnostics) |
1263 | { |
1264 | mUniqueToSpirvIdMap.clear(); |
1265 | mFirstUnusedSpirvId = 0; |
1266 | |
1267 | SpecConst specConst(&getSymbolTable(), compileOptions, getShaderType()); |
1268 | |
1269 | DriverUniform driverUniforms(DriverUniformMode::InterfaceBlock); |
1270 | DriverUniformExtended driverUniformsExt(DriverUniformMode::InterfaceBlock); |
1271 | |
1272 | const bool useExtendedDriverUniforms = compileOptions.addVulkanXfbEmulationSupportCode; |
1273 | |
1274 | DriverUniform *uniforms = useExtendedDriverUniforms ? &driverUniformsExt : &driverUniforms; |
1275 | |
1276 | if (!translateImpl(root, compileOptions, perfDiagnostics, specConst: &specConst, driverUniforms: uniforms)) |
1277 | { |
1278 | return false; |
1279 | } |
1280 | |
1281 | return OutputSPIRV(compiler: this, root, compileOptions, uniqueToSpirvIdMap: mUniqueToSpirvIdMap, firstUnusedSpirvId: mFirstUnusedSpirvId); |
1282 | } |
1283 | |
1284 | bool TranslatorSPIRV::shouldFlattenPragmaStdglInvariantAll() |
1285 | { |
1286 | // Not necessary. |
1287 | return false; |
1288 | } |
1289 | |
1290 | void TranslatorSPIRV::assignSpirvId(TSymbolUniqueId uniqueId, uint32_t spirvId) |
1291 | { |
1292 | ASSERT(mUniqueToSpirvIdMap.find(uniqueId.get()) == mUniqueToSpirvIdMap.end()); |
1293 | mUniqueToSpirvIdMap[uniqueId.get()] = spirvId; |
1294 | } |
1295 | |
1296 | void TranslatorSPIRV::assignSpirvIds(TIntermBlock *root) |
1297 | { |
1298 | // Match the declarations with collected variables and assign a new id to each, starting from |
1299 | // the first unreserved id. This makes sure that the reserved ids for internal variables and |
1300 | // ids for shader variables form a minimal contiguous range. The Vulkan backend takes advantage |
1301 | // of this fact for optimal hashing. |
1302 | mFirstUnusedSpirvId = vk::spirv::kIdFirstUnreserved; |
1303 | |
1304 | for (TIntermNode *node : *root->getSequence()) |
1305 | { |
1306 | TIntermDeclaration *decl = node->getAsDeclarationNode(); |
1307 | if (decl == nullptr) |
1308 | { |
1309 | continue; |
1310 | } |
1311 | |
1312 | TIntermSymbol *symbol = decl->getSequence()->front()->getAsSymbolNode(); |
1313 | if (symbol == nullptr) |
1314 | { |
1315 | continue; |
1316 | } |
1317 | |
1318 | const TType &type = symbol->getType(); |
1319 | const TQualifier qualifier = type.getQualifier(); |
1320 | |
1321 | // Skip internal symbols, which already have a reserved id. |
1322 | const TSymbolUniqueId uniqueId = |
1323 | type.isInterfaceBlock() ? type.getInterfaceBlock()->uniqueId() : symbol->uniqueId(); |
1324 | if (mUniqueToSpirvIdMap.find(key: uniqueId.get()) != mUniqueToSpirvIdMap.end()) |
1325 | { |
1326 | continue; |
1327 | } |
1328 | |
1329 | uint32_t *variableId = nullptr; |
1330 | std::vector<ShaderVariable> *fields = nullptr; |
1331 | if (type.isInterfaceBlock()) |
1332 | { |
1333 | if (IsVaryingIn(qualifier)) |
1334 | { |
1335 | ShaderVariable *varying = |
1336 | FindIOBlockShaderVariable(vars: &mInputVaryings, name: type.getInterfaceBlock()->name()); |
1337 | variableId = &varying->id; |
1338 | fields = &varying->fields; |
1339 | } |
1340 | else if (IsVaryingOut(qualifier)) |
1341 | { |
1342 | ShaderVariable *varying = |
1343 | FindIOBlockShaderVariable(vars: &mOutputVaryings, name: type.getInterfaceBlock()->name()); |
1344 | variableId = &varying->id; |
1345 | fields = &varying->fields; |
1346 | } |
1347 | else if (IsStorageBuffer(qualifier)) |
1348 | { |
1349 | InterfaceBlock *block = |
1350 | FindShaderVariable(vars: &mShaderStorageBlocks, name: type.getInterfaceBlock()->name()); |
1351 | variableId = &block->id; |
1352 | } |
1353 | else |
1354 | { |
1355 | InterfaceBlock *block = |
1356 | FindShaderVariable(vars: &mUniformBlocks, name: type.getInterfaceBlock()->name()); |
1357 | variableId = &block->id; |
1358 | } |
1359 | } |
1360 | else if (qualifier == EvqUniform) |
1361 | { |
1362 | ShaderVariable *uniform = FindUniformShaderVariable(vars: &mUniforms, name: symbol->getName()); |
1363 | variableId = &uniform->id; |
1364 | } |
1365 | else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) |
1366 | { |
1367 | ShaderVariable *attribute = FindShaderVariable(vars: &mAttributes, name: symbol->getName()); |
1368 | variableId = &attribute->id; |
1369 | } |
1370 | else if (IsShaderIn(qualifier)) |
1371 | { |
1372 | ShaderVariable *varying = FindShaderVariable(vars: &mInputVaryings, name: symbol->getName()); |
1373 | variableId = &varying->id; |
1374 | fields = &varying->fields; |
1375 | } |
1376 | else if (qualifier == EvqFragmentOut) |
1377 | { |
1378 | // webgl_FragColor, webgl_FragData, webgl_SecondaryFragColor and webgl_SecondaryFragData |
1379 | // are recorded with their original names (starting with gl_) |
1380 | ImmutableString name(symbol->getName()); |
1381 | if (angle::BeginsWith(str: name.data(), prefix: "webgl_")) |
1382 | { |
1383 | name = ImmutableString(name.data() + 3, name.length() - 3); |
1384 | } |
1385 | |
1386 | ShaderVariable *output = FindShaderVariable(vars: &mOutputVariables, name); |
1387 | variableId = &output->id; |
1388 | } |
1389 | else if (IsShaderOut(qualifier)) |
1390 | { |
1391 | ShaderVariable *varying = FindShaderVariable(vars: &mOutputVaryings, name: symbol->getName()); |
1392 | variableId = &varying->id; |
1393 | fields = &varying->fields; |
1394 | } |
1395 | |
1396 | if (variableId == nullptr) |
1397 | { |
1398 | continue; |
1399 | } |
1400 | |
1401 | ASSERT(variableId != nullptr); |
1402 | assignSpirvId(uniqueId, spirvId: mFirstUnusedSpirvId); |
1403 | *variableId = mFirstUnusedSpirvId; |
1404 | |
1405 | // Propagate the id to the first field of structs/blocks too. The front-end gathers |
1406 | // varyings as fields, and the transformer needs to infer the variable id (of struct type) |
1407 | // just by looking at the fields. |
1408 | if (fields != nullptr) |
1409 | { |
1410 | SetSpirvIdInFields(id: mFirstUnusedSpirvId, fields); |
1411 | } |
1412 | |
1413 | ++mFirstUnusedSpirvId; |
1414 | } |
1415 | } |
1416 | } // namespace sh |
1417 |
Definitions
- kFlippedPointCoordName
- kFlippedFragCoordName
- kDefaultUniformsBlockName
- IsDefaultUniform
- ReplaceDefaultUniformsTraverser
- ReplaceDefaultUniformsTraverser
- visitDeclaration
- visitSymbol
- DeclareDefaultUniforms
- RotateAndFlipBuiltinVariable
- GetMainSequence
- ReplaceGLDepthRangeWithDriverUniform
- ReplaceGLBoundingBoxWithGlobal
- AddXfbEmulationSupport
- AddXfbExtensionSupport
- AddVertexTransformationSupport
- InsertFragCoordCorrection
- HasFramebufferFetch
- FindShaderVariable
- FindIOBlockShaderVariable
- FindUniformFieldShaderVariable
- FindUniformShaderVariable
- SetSpirvIdInFields
- TranslatorSPIRV
- translateImpl
- translate
- shouldFlattenPragmaStdglInvariantAll
- assignSpirvId
Learn more about Flutter for embedded and desktop on industrialflutter.com