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
54namespace sh
55{
56
57namespace
58{
59constexpr ImmutableString kFlippedPointCoordName = ImmutableString("flippedPointCoord");
60constexpr ImmutableString kFlippedFragCoordName = ImmutableString("flippedFragCoord");
61constexpr ImmutableString kDefaultUniformsBlockName = ImmutableString("defaultUniforms");
62
63bool IsDefaultUniform(const TType &type)
64{
65 return type.getQualifier() == EvqUniform && type.getInterfaceBlock() == nullptr &&
66 !IsOpaqueType(type: type.getBasicType());
67}
68
69class 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
115bool 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
239TIntermSequence *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
652bool 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
663template <typename Variable>
664Variable *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
677ShaderVariable *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
691ShaderVariable *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
717ShaderVariable *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
739void 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
749TranslatorSPIRV::TranslatorSPIRV(sh::GLenum type, ShShaderSpec spec)
750 : TCompiler(type, spec, SH_SPIRV_VULKAN_OUTPUT), mFirstUnusedSpirvId(0)
751{}
752
753bool 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
1260bool 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
1284bool TranslatorSPIRV::shouldFlattenPragmaStdglInvariantAll()
1285{
1286 // Not necessary.
1287 return false;
1288}
1289
1290void TranslatorSPIRV::assignSpirvId(TSymbolUniqueId uniqueId, uint32_t spirvId)
1291{
1292 ASSERT(mUniqueToSpirvIdMap.find(uniqueId.get()) == mUniqueToSpirvIdMap.end());
1293 mUniqueToSpirvIdMap[uniqueId.get()] = spirvId;
1294}
1295
1296void 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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/third_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp