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