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 // TranslatorVulkan:
7 // A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl and feeds them into
8 // glslang to spit out SPIR-V.
9 // See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
10 //
11
12 #include "compiler/translator/TranslatorVulkan.h"
13
14 #include "angle_gl.h"
15 #include "common/PackedEnums.h"
16 #include "common/utilities.h"
17 #include "compiler/translator/BuiltinsWorkaroundGLSL.h"
18 #include "compiler/translator/ImmutableStringBuilder.h"
19 #include "compiler/translator/IntermNode.h"
20 #include "compiler/translator/OutputSPIRV.h"
21 #include "compiler/translator/OutputVulkanGLSL.h"
22 #include "compiler/translator/StaticType.h"
23 #include "compiler/translator/glslang_wrapper.h"
24 #include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
25 #include "compiler/translator/tree_ops/RecordConstantPrecision.h"
26 #include "compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h"
27 #include "compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h"
28 #include "compiler/translator/tree_ops/RewriteArrayOfArrayOfOpaqueUniforms.h"
29 #include "compiler/translator/tree_ops/RewriteAtomicCounters.h"
30 #include "compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h"
31 #include "compiler/translator/tree_ops/RewriteDfdy.h"
32 #include "compiler/translator/tree_ops/RewriteStructSamplers.h"
33 #include "compiler/translator/tree_ops/SeparateStructFromUniformDeclarations.h"
34 #include "compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h"
35 #include "compiler/translator/tree_ops/vulkan/EmulateFragColorData.h"
36 #include "compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.h"
37 #include "compiler/translator/tree_ops/vulkan/ReplaceForShaderFramebufferFetch.h"
38 #include "compiler/translator/tree_ops/vulkan/RewriteInterpolateAtOffset.h"
39 #include "compiler/translator/tree_ops/vulkan/RewriteR32fImages.h"
40 #include "compiler/translator/tree_util/BuiltIn.h"
41 #include "compiler/translator/tree_util/DriverUniform.h"
42 #include "compiler/translator/tree_util/FindFunction.h"
43 #include "compiler/translator/tree_util/FindMain.h"
44 #include "compiler/translator/tree_util/IntermNode_util.h"
45 #include "compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h"
46 #include "compiler/translator/tree_util/ReplaceVariable.h"
47 #include "compiler/translator/tree_util/RewriteSampleMaskVariable.h"
48 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
49 #include "compiler/translator/tree_util/SpecializationConstant.h"
50 #include "compiler/translator/util.h"
51
52 namespace sh
53 {
54
55 namespace
56 {
57 constexpr ImmutableString kFlippedPointCoordName = ImmutableString("flippedPointCoord");
58 constexpr ImmutableString kFlippedFragCoordName = ImmutableString("flippedFragCoord");
59
60 constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
61 {gl::ShaderType::Vertex, vk::kDefaultUniformsNameVS},
62 {gl::ShaderType::TessControl, vk::kDefaultUniformsNameTCS},
63 {gl::ShaderType::TessEvaluation, vk::kDefaultUniformsNameTES},
64 {gl::ShaderType::Geometry, vk::kDefaultUniformsNameGS},
65 {gl::ShaderType::Fragment, vk::kDefaultUniformsNameFS},
66 {gl::ShaderType::Compute, vk::kDefaultUniformsNameCS},
67 };
68
IsDefaultUniform(const TType & type)69 bool IsDefaultUniform(const TType &type)
70 {
71 return type.getQualifier() == EvqUniform && type.getInterfaceBlock() == nullptr &&
72 !IsOpaqueType(type.getBasicType());
73 }
74
75 class ReplaceDefaultUniformsTraverser : public TIntermTraverser
76 {
77 public:
ReplaceDefaultUniformsTraverser(const VariableReplacementMap & variableMap)78 ReplaceDefaultUniformsTraverser(const VariableReplacementMap &variableMap)
79 : TIntermTraverser(true, false, false), mVariableMap(variableMap)
80 {}
81
visitDeclaration(Visit visit,TIntermDeclaration * node)82 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
83 {
84 const TIntermSequence &sequence = *(node->getSequence());
85
86 TIntermTyped *variable = sequence.front()->getAsTyped();
87 const TType &type = variable->getType();
88
89 if (IsDefaultUniform(type))
90 {
91 // Remove the uniform declaration.
92 TIntermSequence emptyReplacement;
93 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
94 std::move(emptyReplacement));
95
96 return false;
97 }
98
99 return true;
100 }
101
visitSymbol(TIntermSymbol * symbol)102 void visitSymbol(TIntermSymbol *symbol) override
103 {
104 const TVariable &variable = symbol->variable();
105 const TType &type = variable.getType();
106
107 if (!IsDefaultUniform(type) || gl::IsBuiltInName(variable.name().data()))
108 {
109 return;
110 }
111
112 ASSERT(mVariableMap.count(&variable) > 0);
113
114 queueReplacement(mVariableMap.at(&variable)->deepCopy(), OriginalNode::IS_DROPPED);
115 }
116
117 private:
118 const VariableReplacementMap &mVariableMap;
119 };
120
DeclareDefaultUniforms(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,gl::ShaderType shaderType)121 bool DeclareDefaultUniforms(TCompiler *compiler,
122 TIntermBlock *root,
123 TSymbolTable *symbolTable,
124 gl::ShaderType shaderType)
125 {
126 // First, collect all default uniforms and declare a uniform block.
127 TFieldList *uniformList = new TFieldList;
128 TVector<const TVariable *> uniformVars;
129
130 for (TIntermNode *node : *root->getSequence())
131 {
132 TIntermDeclaration *decl = node->getAsDeclarationNode();
133 if (decl == nullptr)
134 {
135 continue;
136 }
137
138 const TIntermSequence &sequence = *(decl->getSequence());
139
140 TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
141 if (symbol == nullptr)
142 {
143 continue;
144 }
145
146 const TType &type = symbol->getType();
147 if (IsDefaultUniform(type))
148 {
149 TType *fieldType = new TType(type);
150
151 uniformList->push_back(new TField(fieldType, symbol->getName(), symbol->getLine(),
152 symbol->variable().symbolType()));
153 uniformVars.push_back(&symbol->variable());
154 }
155 }
156
157 TLayoutQualifier layoutQualifier = TLayoutQualifier::Create();
158 layoutQualifier.blockStorage = EbsStd140;
159 const TVariable *uniformBlock = DeclareInterfaceBlock(
160 root, symbolTable, uniformList, EvqUniform, layoutQualifier, TMemoryQualifier::Create(), 0,
161 ImmutableString(kDefaultUniformNames[shaderType]), ImmutableString(""));
162
163 // Create a map from the uniform variables to new variables that reference the fields of the
164 // block.
165 VariableReplacementMap variableMap;
166 for (size_t fieldIndex = 0; fieldIndex < uniformVars.size(); ++fieldIndex)
167 {
168 const TVariable *variable = uniformVars[fieldIndex];
169
170 TType *replacementType = new TType(variable->getType());
171 replacementType->setInterfaceBlockField(uniformBlock->getType().getInterfaceBlock(),
172 fieldIndex);
173
174 TVariable *replacementVariable =
175 new TVariable(symbolTable, variable->name(), replacementType, variable->symbolType());
176
177 variableMap[variable] = new TIntermSymbol(replacementVariable);
178 }
179
180 // Finally transform the AST and make sure references to the uniforms are replaced with the new
181 // variables.
182 ReplaceDefaultUniformsTraverser defaultTraverser(variableMap);
183 root->traverse(&defaultTraverser);
184 return defaultTraverser.updateTree(compiler, root);
185 }
186
187 // 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 * flipXY,TSymbolTable * symbolTable,const TVariable * builtin,const ImmutableString & flippedVariableName,TIntermTyped * pivot,TIntermTyped * fragRotation)188 ANGLE_NO_DISCARD bool RotateAndFlipBuiltinVariable(TCompiler *compiler,
189 TIntermBlock *root,
190 TIntermSequence *insertSequence,
191 TIntermTyped *flipXY,
192 TSymbolTable *symbolTable,
193 const TVariable *builtin,
194 const ImmutableString &flippedVariableName,
195 TIntermTyped *pivot,
196 TIntermTyped *fragRotation)
197 {
198 // Create a symbol reference to 'builtin'.
199 TIntermSymbol *builtinRef = new TIntermSymbol(builtin);
200
201 // Create a swizzle to "builtin.xy"
202 TVector<int> swizzleOffsetXY = {0, 1};
203 TIntermSwizzle *builtinXY = new TIntermSwizzle(builtinRef, swizzleOffsetXY);
204
205 // Create a symbol reference to our new variable that will hold the modified builtin.
206 TType *type = new TType(builtin->getType());
207 type->setQualifier(EvqGlobal);
208 type->setPrimarySize(static_cast<unsigned char>(builtin->getType().getNominalSize()));
209 TVariable *replacementVar =
210 new TVariable(symbolTable, flippedVariableName, type, SymbolType::AngleInternal);
211 DeclareGlobalVariable(root, replacementVar);
212 TIntermSymbol *flippedBuiltinRef = new TIntermSymbol(replacementVar);
213
214 // Use this new variable instead of 'builtin' everywhere.
215 if (!ReplaceVariable(compiler, root, builtin, replacementVar))
216 {
217 return false;
218 }
219
220 // Create the expression "(builtin.xy * fragRotation)"
221 TIntermTyped *rotatedXY;
222 if (fragRotation)
223 {
224 rotatedXY = new TIntermBinary(EOpMatrixTimesVector, fragRotation, builtinXY);
225 }
226 else
227 {
228 // No rotation applied, use original variable.
229 rotatedXY = builtinXY;
230 }
231
232 // Create the expression "(builtin.xy - pivot) * flipXY + pivot
233 TIntermBinary *removePivot = new TIntermBinary(EOpSub, rotatedXY, pivot);
234 TIntermBinary *inverseXY = new TIntermBinary(EOpMul, removePivot, flipXY);
235 TIntermBinary *plusPivot = new TIntermBinary(EOpAdd, inverseXY, pivot->deepCopy());
236
237 // Create the corrected variable and copy the value of the original builtin.
238 TIntermBinary *assignment =
239 new TIntermBinary(EOpAssign, flippedBuiltinRef, builtinRef->deepCopy());
240
241 // Create an assignment to the replaced variable's .xy.
242 TIntermSwizzle *correctedXY =
243 new TIntermSwizzle(flippedBuiltinRef->deepCopy(), swizzleOffsetXY);
244 TIntermBinary *assignToY = new TIntermBinary(EOpAssign, correctedXY, plusPivot);
245
246 // Add this assigment at the beginning of the main function
247 insertSequence->insert(insertSequence->begin(), assignToY);
248 insertSequence->insert(insertSequence->begin(), assignment);
249
250 return compiler->validateAST(root);
251 }
252
GetMainSequence(TIntermBlock * root)253 TIntermSequence *GetMainSequence(TIntermBlock *root)
254 {
255 TIntermFunctionDefinition *main = FindMain(root);
256 return main->getBody()->getSequence();
257 }
258
259 // 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)260 ANGLE_NO_DISCARD bool ReplaceGLDepthRangeWithDriverUniform(TCompiler *compiler,
261 TIntermBlock *root,
262 const DriverUniform *driverUniforms,
263 TSymbolTable *symbolTable)
264 {
265 // Create a symbol reference to "gl_DepthRange"
266 const TVariable *depthRangeVar = static_cast<const TVariable *>(
267 symbolTable->findBuiltIn(ImmutableString("gl_DepthRange"), 0));
268
269 // ANGLEUniforms.depthRange
270 TIntermBinary *angleEmulatedDepthRangeRef = driverUniforms->getDepthRangeRef();
271
272 // Use this variable instead of gl_DepthRange everywhere.
273 return ReplaceVariableWithTyped(compiler, root, depthRangeVar, angleEmulatedDepthRangeRef);
274 }
275
276 // Declares a new variable to replace gl_BoundingBoxEXT, its values are fed from a global temporary
277 // variable.
ReplaceGLBoundingBoxWithGlobal(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)278 ANGLE_NO_DISCARD bool ReplaceGLBoundingBoxWithGlobal(TCompiler *compiler,
279 TIntermBlock *root,
280 TSymbolTable *symbolTable)
281 {
282 // Create a symbol reference to "gl_BoundingBoxEXT"
283 const TVariable *builtinBoundingBoxVar = static_cast<const TVariable *>(
284 symbolTable->findBuiltIn(ImmutableString("gl_BoundingBoxEXT"), 310));
285
286 if (builtinBoundingBoxVar != nullptr)
287 {
288 // Declare the replacement bounding box variable type
289 TType *emulatedBoundingBoxDeclType = new TType(builtinBoundingBoxVar->getType());
290 emulatedBoundingBoxDeclType->setQualifier(EvqGlobal);
291
292 TVariable *ANGLEBoundingBoxVar =
293 new TVariable(symbolTable->nextUniqueId(), ImmutableString("ANGLEBoundingBox"),
294 SymbolType::AngleInternal, TExtension::EXT_primitive_bounding_box,
295 emulatedBoundingBoxDeclType);
296
297 DeclareGlobalVariable(root, ANGLEBoundingBoxVar);
298
299 // Use the replacement variable instead of builtin gl_BoundingBoxEXT everywhere.
300 return ReplaceVariable(compiler, root, builtinBoundingBoxVar, ANGLEBoundingBoxVar);
301 }
302
303 return true;
304 }
305
AddANGLEPositionVaryingDeclaration(TIntermBlock * root,TSymbolTable * symbolTable,TQualifier qualifier)306 TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
307 TSymbolTable *symbolTable,
308 TQualifier qualifier)
309 {
310 // Define a vec2 driver varying to hold the line rasterization emulation position.
311 TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 2);
312 TVariable *varyingVar =
313 new TVariable(symbolTable, ImmutableString(vk::kLineRasterEmulationPosition), varyingType,
314 SymbolType::AngleInternal);
315 TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar);
316 TIntermDeclaration *varyingDecl = new TIntermDeclaration;
317 varyingDecl->appendDeclarator(varyingDeclarator);
318
319 TIntermSequence insertSequence;
320 insertSequence.push_back(varyingDecl);
321
322 // Insert the declarations before Main.
323 size_t mainIndex = FindMainIndex(root);
324 root->insertChildNodes(mainIndex, insertSequence);
325
326 return varyingVar;
327 }
328
AddBresenhamEmulationVS(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,SpecConst * specConst,const DriverUniform * driverUniforms)329 ANGLE_NO_DISCARD bool AddBresenhamEmulationVS(TCompiler *compiler,
330 TIntermBlock *root,
331 TSymbolTable *symbolTable,
332 SpecConst *specConst,
333 const DriverUniform *driverUniforms)
334 {
335 TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingOut);
336
337 // Clamp position to subpixel grid.
338 // Do perspective divide (get normalized device coords)
339 // "vec2 ndc = gl_Position.xy / gl_Position.w"
340 const TType *vec2Type = StaticType::GetTemporary<EbtFloat, EbpHigh, 2>();
341 TIntermBinary *viewportRef = driverUniforms->getViewportRef();
342 TIntermSymbol *glPos = new TIntermSymbol(BuiltInVariable::gl_Position());
343 TIntermSwizzle *glPosXY = CreateSwizzle(glPos, 0, 1);
344 TIntermSwizzle *glPosW = CreateSwizzle(glPos->deepCopy(), 3);
345 TVariable *ndc = CreateTempVariable(symbolTable, vec2Type);
346 TIntermBinary *noPerspective = new TIntermBinary(EOpDiv, glPosXY, glPosW);
347 TIntermDeclaration *ndcDecl = CreateTempInitDeclarationNode(ndc, noPerspective);
348
349 // Convert NDC to window coordinates. According to Vulkan spec.
350 // "vec2 window = 0.5 * viewport.wh * (ndc + 1) + viewport.xy"
351 TIntermBinary *ndcPlusOne =
352 new TIntermBinary(EOpAdd, CreateTempSymbolNode(ndc), CreateFloatNode(1.0f, EbpMedium));
353 TIntermSwizzle *viewportZW = CreateSwizzle(viewportRef, 2, 3);
354 TIntermBinary *ndcViewport = new TIntermBinary(EOpMul, viewportZW, ndcPlusOne);
355 TIntermBinary *ndcViewportHalf =
356 new TIntermBinary(EOpVectorTimesScalar, ndcViewport, CreateFloatNode(0.5f, EbpMedium));
357 TIntermSwizzle *viewportXY = CreateSwizzle(viewportRef->deepCopy(), 0, 1);
358 TIntermBinary *ndcToWindow = new TIntermBinary(EOpAdd, ndcViewportHalf, viewportXY);
359 TVariable *windowCoords = CreateTempVariable(symbolTable, vec2Type);
360 TIntermDeclaration *windowDecl = CreateTempInitDeclarationNode(windowCoords, ndcToWindow);
361
362 // Clamp to subpixel grid.
363 // "vec2 clamped = round(window * 2^{subpixelBits}) / 2^{subpixelBits}"
364 int subpixelBits = compiler->getResources().SubPixelBits;
365 TIntermConstantUnion *scaleConstant =
366 CreateFloatNode(static_cast<float>(1 << subpixelBits), EbpHigh);
367 TIntermBinary *windowScaled =
368 new TIntermBinary(EOpVectorTimesScalar, CreateTempSymbolNode(windowCoords), scaleConstant);
369 TIntermTyped *windowRounded =
370 CreateBuiltInUnaryFunctionCallNode("round", windowScaled, *symbolTable, 300);
371 TIntermBinary *windowRoundedBack =
372 new TIntermBinary(EOpDiv, windowRounded, scaleConstant->deepCopy());
373 TVariable *clampedWindowCoords = CreateTempVariable(symbolTable, vec2Type);
374 TIntermDeclaration *clampedDecl =
375 CreateTempInitDeclarationNode(clampedWindowCoords, windowRoundedBack);
376
377 // Set varying.
378 // "ANGLEPosition = 2 * (clamped - viewport.xy) / viewport.wh - 1"
379 TIntermBinary *clampedOffset = new TIntermBinary(
380 EOpSub, CreateTempSymbolNode(clampedWindowCoords), viewportXY->deepCopy());
381 TIntermBinary *clampedOff2x =
382 new TIntermBinary(EOpVectorTimesScalar, clampedOffset, CreateFloatNode(2.0f, EbpMedium));
383 TIntermBinary *clampedDivided = new TIntermBinary(EOpDiv, clampedOff2x, viewportZW->deepCopy());
384 TIntermBinary *clampedNDC =
385 new TIntermBinary(EOpSub, clampedDivided, CreateFloatNode(1.0f, EbpMedium));
386 TIntermSymbol *varyingRef = new TIntermSymbol(anglePosition);
387 TIntermBinary *varyingAssign = new TIntermBinary(EOpAssign, varyingRef, clampedNDC);
388
389 TIntermBlock *emulationBlock = new TIntermBlock;
390 emulationBlock->appendStatement(ndcDecl);
391 emulationBlock->appendStatement(windowDecl);
392 emulationBlock->appendStatement(clampedDecl);
393 emulationBlock->appendStatement(varyingAssign);
394 TIntermIfElse *ifEmulation =
395 new TIntermIfElse(specConst->getLineRasterEmulation(), emulationBlock, nullptr);
396
397 // Ensure the statements run at the end of the main() function.
398 return RunAtTheEndOfShader(compiler, root, ifEmulation, symbolTable);
399 }
400
AddXfbEmulationSupport(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const DriverUniform * driverUniforms)401 ANGLE_NO_DISCARD bool AddXfbEmulationSupport(TCompiler *compiler,
402 TIntermBlock *root,
403 TSymbolTable *symbolTable,
404 const DriverUniform *driverUniforms)
405 {
406 // Generate the following function and place it before main(). This function takes a "strides"
407 // parameter that is determined at link time, and calculates for each transform feedback buffer
408 // (of which there are a maximum of four) what the starting index is to write to the output
409 // buffer.
410 //
411 // ivec4 ANGLEGetXfbOffsets(ivec4 strides)
412 // {
413 // int xfbIndex = gl_VertexIndex
414 // + gl_InstanceIndex * ANGLEUniforms.xfbVerticesPerInstance;
415 // return ANGLEUniforms.xfbBufferOffsets + xfbIndex * strides;
416 // }
417
418 constexpr uint32_t kMaxXfbBuffers = 4;
419
420 const TType *ivec4Type = StaticType::GetBasic<EbtInt, EbpHigh, kMaxXfbBuffers>();
421 TType *stridesType = new TType(*ivec4Type);
422 stridesType->setQualifier(EvqParamConst);
423
424 // Create the parameter variable.
425 TVariable *stridesVar = new TVariable(symbolTable, ImmutableString("strides"), stridesType,
426 SymbolType::AngleInternal);
427 TIntermSymbol *stridesSymbol = new TIntermSymbol(stridesVar);
428
429 // Create references to gl_VertexIndex, gl_InstanceIndex, ANGLEUniforms.xfbVerticesPerInstance
430 // and ANGLEUniforms.xfbBufferOffsets.
431 TIntermSymbol *vertexIndex = new TIntermSymbol(BuiltInVariable::gl_VertexIndex());
432 TIntermSymbol *instanceIndex = new TIntermSymbol(BuiltInVariable::gl_InstanceIndex());
433 TIntermBinary *xfbVerticesPerInstance = driverUniforms->getXfbVerticesPerInstance();
434 TIntermBinary *xfbBufferOffsets = driverUniforms->getXfbBufferOffsets();
435
436 // gl_InstanceIndex * ANGLEUniforms.xfbVerticesPerInstance
437 TIntermBinary *xfbInstanceIndex =
438 new TIntermBinary(EOpMul, instanceIndex, xfbVerticesPerInstance);
439
440 // gl_VertexIndex + |xfbInstanceIndex|
441 TIntermBinary *xfbIndex = new TIntermBinary(EOpAdd, vertexIndex, xfbInstanceIndex);
442
443 // |xfbIndex| * |strides|
444 TIntermBinary *xfbStrides = new TIntermBinary(EOpVectorTimesScalar, xfbIndex, stridesSymbol);
445
446 // ANGLEUniforms.xfbBufferOffsets + |xfbStrides|
447 TIntermBinary *xfbOffsets = new TIntermBinary(EOpAdd, xfbBufferOffsets, xfbStrides);
448
449 // Create the function body, which has a single return statement. Note that the `xfbIndex`
450 // variable declared in the comment at the beginning of this function is simply replaced in the
451 // return statement for brevity.
452 TIntermBlock *body = new TIntermBlock;
453 body->appendStatement(new TIntermBranch(EOpReturn, xfbOffsets));
454
455 // Declare the function
456 TFunction *getOffsetsFunction =
457 new TFunction(symbolTable, ImmutableString(vk::kXfbEmulationGetOffsetsFunctionName),
458 SymbolType::AngleInternal, ivec4Type, true);
459 getOffsetsFunction->addParameter(stridesVar);
460
461 TIntermFunctionDefinition *functionDef =
462 CreateInternalFunctionDefinitionNode(*getOffsetsFunction, body);
463
464 // Insert the function declaration before main().
465 const size_t mainIndex = FindMainIndex(root);
466 root->insertChildNodes(mainIndex, {functionDef});
467
468 // Generate the following function and place it before main(). This function will be filled
469 // with transform feedback capture code at link time.
470 //
471 // void ANGLECaptureXfb()
472 // {
473 // }
474 const TType *voidType = StaticType::GetBasic<EbtVoid, EbpUndefined>();
475
476 // Create the function body, which is empty.
477 body = new TIntermBlock;
478
479 // Declare the function
480 TFunction *xfbCaptureFunction =
481 new TFunction(symbolTable, ImmutableString(vk::kXfbEmulationCaptureFunctionName),
482 SymbolType::AngleInternal, voidType, false);
483
484 // Insert the function declaration before main().
485 root->insertChildNodes(mainIndex,
486 {CreateInternalFunctionDefinitionNode(*xfbCaptureFunction, body)});
487
488 // Create the following logic and add it at the end of main():
489 //
490 // if (ANGLEUniforms.xfbActiveUnpaused)
491 // {
492 // ANGLECaptureXfb();
493 // }
494 //
495
496 // Create a reference ANGLEUniforms.xfbActiveUnpaused
497 TIntermBinary *xfbActiveUnpaused = driverUniforms->getXfbActiveUnpaused();
498
499 // ANGLEUniforms.xfbActiveUnpaused != 0
500 TIntermBinary *isXfbActiveUnpaused =
501 new TIntermBinary(EOpNotEqual, xfbActiveUnpaused, CreateUIntNode(0));
502
503 // Create the function call
504 TIntermAggregate *captureXfbCall =
505 TIntermAggregate::CreateFunctionCall(*xfbCaptureFunction, {});
506
507 TIntermBlock *captureXfbBlock = new TIntermBlock;
508 captureXfbBlock->appendStatement(captureXfbCall);
509
510 // Create a call to ANGLEGetXfbOffsets too, for the sole purpose of preventing it from being
511 // culled as unused by glslang.
512 TIntermSequence ivec4Zero;
513 ivec4Zero.push_back(CreateZeroNode(*ivec4Type));
514 TIntermAggregate *getOffsetsCall =
515 TIntermAggregate::CreateFunctionCall(*getOffsetsFunction, &ivec4Zero);
516 captureXfbBlock->appendStatement(getOffsetsCall);
517
518 // Create the if
519 TIntermIfElse *captureXfb = new TIntermIfElse(isXfbActiveUnpaused, captureXfbBlock, nullptr);
520
521 // Run it at the end of the shader.
522 if (!RunAtTheEndOfShader(compiler, root, captureXfb, symbolTable))
523 {
524 return false;
525 }
526
527 // Additionally, generate the following storage buffer declarations used to capture transform
528 // feedback output. Again, there's a maximum of four buffers.
529 //
530 // buffer ANGLEXfbBuffer0
531 // {
532 // float xfbOut[];
533 // } ANGLEXfb0;
534 // buffer ANGLEXfbBuffer1
535 // {
536 // float xfbOut[];
537 // } ANGLEXfb1;
538 // ...
539
540 for (uint32_t bufferIndex = 0; bufferIndex < kMaxXfbBuffers; ++bufferIndex)
541 {
542 TFieldList *fieldList = new TFieldList;
543 TType *xfbOutType = new TType(EbtFloat, EbpHigh, EvqGlobal);
544 xfbOutType->makeArray(0);
545
546 TField *field = new TField(xfbOutType, ImmutableString(vk::kXfbEmulationBufferFieldName),
547 TSourceLoc(), SymbolType::AngleInternal);
548
549 fieldList->push_back(field);
550
551 static_assert(
552 kMaxXfbBuffers < 10,
553 "ImmutableStringBuilder memory size below needs to accomodate the number of buffers");
554
555 ImmutableStringBuilder blockName(strlen(vk::kXfbEmulationBufferBlockName) + 2);
556 blockName << vk::kXfbEmulationBufferBlockName;
557 blockName.appendDecimal(bufferIndex);
558
559 ImmutableStringBuilder varName(strlen(vk::kXfbEmulationBufferName) + 2);
560 varName << vk::kXfbEmulationBufferName;
561 varName.appendDecimal(bufferIndex);
562
563 TLayoutQualifier layoutQualifier = TLayoutQualifier::Create();
564 layoutQualifier.blockStorage = EbsStd430;
565
566 DeclareInterfaceBlock(root, symbolTable, fieldList, EvqBuffer, layoutQualifier,
567 TMemoryQualifier::Create(), 0, blockName, varName);
568 }
569
570 return compiler->validateAST(root);
571 }
572
AddXfbExtensionSupport(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const DriverUniform * driverUniforms)573 ANGLE_NO_DISCARD bool AddXfbExtensionSupport(TCompiler *compiler,
574 TIntermBlock *root,
575 TSymbolTable *symbolTable,
576 const DriverUniform *driverUniforms)
577 {
578 // Generate the following output varying declaration used to capture transform feedback output
579 // from gl_Position, as it can't be captured directly due to changes that are applied to it for
580 // clip-space correction and pre-rotation.
581 //
582 // out vec4 ANGLEXfbPosition;
583
584 const TType *vec4Type = nullptr;
585
586 switch (compiler->getShaderType())
587 {
588 case GL_VERTEX_SHADER:
589 vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqVertexOut, 4, 1>();
590 break;
591 case GL_TESS_EVALUATION_SHADER_EXT:
592 vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqTessEvaluationOut, 4, 1>();
593 break;
594 case GL_GEOMETRY_SHADER_EXT:
595 vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqGeometryOut, 4, 1>();
596 break;
597 default:
598 UNREACHABLE();
599 }
600
601 TVariable *varyingVar =
602 new TVariable(symbolTable, ImmutableString(vk::kXfbExtensionPositionOutName), vec4Type,
603 SymbolType::AngleInternal);
604
605 TIntermDeclaration *varyingDecl = new TIntermDeclaration();
606 varyingDecl->appendDeclarator(new TIntermSymbol(varyingVar));
607
608 // Insert the varying declaration before the first function.
609 const size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
610 root->insertChildNodes(firstFunctionIndex, {varyingDecl});
611
612 return compiler->validateAST(root);
613 }
614
InsertFragCoordCorrection(TCompiler * compiler,ShCompileOptions compileOptions,TIntermBlock * root,TIntermSequence * insertSequence,TSymbolTable * symbolTable,SpecConst * specConst,const DriverUniform * driverUniforms)615 ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler,
616 ShCompileOptions compileOptions,
617 TIntermBlock *root,
618 TIntermSequence *insertSequence,
619 TSymbolTable *symbolTable,
620 SpecConst *specConst,
621 const DriverUniform *driverUniforms)
622 {
623 TIntermTyped *flipXY = specConst->getFlipXY();
624 if (!flipXY)
625 {
626 flipXY = driverUniforms->getFlipXYRef();
627 }
628
629 TIntermBinary *pivot = specConst->getHalfRenderArea();
630 if (!pivot)
631 {
632 pivot = driverUniforms->getHalfRenderAreaRef();
633 }
634
635 TIntermTyped *fragRotation = nullptr;
636 if ((compileOptions & SH_ADD_PRE_ROTATION) != 0)
637 {
638 fragRotation = specConst->getFragRotationMatrix();
639 if (!fragRotation)
640 {
641 fragRotation = driverUniforms->getFragRotationMatrixRef();
642 }
643 }
644 const TVariable *fragCoord = static_cast<const TVariable *>(
645 symbolTable->findBuiltIn(ImmutableString("gl_FragCoord"), compiler->getShaderVersion()));
646 return RotateAndFlipBuiltinVariable(compiler, root, insertSequence, flipXY, symbolTable,
647 fragCoord, kFlippedFragCoordName, pivot, fragRotation);
648 }
649
650 // This block adds OpenGL line segment rasterization emulation behind a specialization constant
651 // guard. OpenGL's simple rasterization algorithm is a strict subset of the pixels generated by the
652 // Vulkan algorithm. Thus we can implement a shader patch that rejects pixels if they would not be
653 // generated by the OpenGL algorithm. OpenGL's algorithm is similar to Bresenham's line algorithm.
654 // It is implemented for each pixel by testing if the line segment crosses a small diamond inside
655 // the pixel. See the OpenGL ES 2.0 spec section "3.4.1 Basic Line Segment Rasterization". Also
656 // see the Vulkan spec section "24.6.1. Basic Line Segment Rasterization":
657 // https://khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#primsrast-lines-basic
658 //
659 // Using trigonometric math and the fact that we know the size of the diamond we can derive a
660 // formula to test if the line segment crosses the pixel center. gl_FragCoord is used along with an
661 // internal position varying to determine the inputs to the formula.
662 //
663 // The implementation of the test is similar to the following pseudocode:
664 //
665 // void main()
666 // {
667 // vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
668 // vec2 d = dFdx(p) + dFdy(p);
669 // vec2 f = gl_FragCoord.xy;
670 // vec2 p_ = p.yx;
671 // vec2 d_ = d.yx;
672 // vec2 f_ = f.yx;
673 //
674 // vec2 i = abs(p - f + (d / d_) * (f_ - p_));
675 //
676 // if (i.x > (0.5 + e) && i.y > (0.5 + e))
677 // discard;
678 // <otherwise run fragment shader main>
679 // }
680 //
681 // Note this emulation can not provide fully correct rasterization. See the docs more more info.
682
AddBresenhamEmulationFS(TCompiler * compiler,ShCompileOptions compileOptions,TIntermBlock * root,TSymbolTable * symbolTable,SpecConst * specConst,const DriverUniform * driverUniforms,bool usesFragCoord)683 ANGLE_NO_DISCARD bool AddBresenhamEmulationFS(TCompiler *compiler,
684 ShCompileOptions compileOptions,
685 TIntermBlock *root,
686 TSymbolTable *symbolTable,
687 SpecConst *specConst,
688 const DriverUniform *driverUniforms,
689 bool usesFragCoord)
690 {
691 TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingIn);
692 const TType *vec2Type = StaticType::GetTemporary<EbtFloat, EbpHigh, 2>();
693
694 TIntermBinary *viewportRef = driverUniforms->getViewportRef();
695
696 // vec2 p = ((ANGLEPosition * 0.5) + 0.5) * viewport.zw + viewport.xy
697 TIntermSwizzle *viewportXY = CreateSwizzle(viewportRef->deepCopy(), 0, 1);
698 TIntermSwizzle *viewportZW = CreateSwizzle(viewportRef, 2, 3);
699 TIntermSymbol *position = new TIntermSymbol(anglePosition);
700 TIntermConstantUnion *oneHalf = CreateFloatNode(0.5f, EbpMedium);
701 TIntermBinary *halfPosition = new TIntermBinary(EOpVectorTimesScalar, position, oneHalf);
702 TIntermBinary *offsetHalfPosition =
703 new TIntermBinary(EOpAdd, halfPosition, oneHalf->deepCopy());
704 TIntermBinary *scaledPosition = new TIntermBinary(EOpMul, offsetHalfPosition, viewportZW);
705 TIntermBinary *windowPosition = new TIntermBinary(EOpAdd, scaledPosition, viewportXY);
706 TVariable *p = CreateTempVariable(symbolTable, vec2Type);
707 TIntermDeclaration *pDecl = CreateTempInitDeclarationNode(p, windowPosition);
708
709 // vec2 d = dFdx(p) + dFdy(p)
710 TIntermTyped *dfdx =
711 CreateBuiltInUnaryFunctionCallNode("dFdx", new TIntermSymbol(p), *symbolTable, 300);
712 TIntermTyped *dfdy =
713 CreateBuiltInUnaryFunctionCallNode("dFdy", new TIntermSymbol(p), *symbolTable, 300);
714 TIntermBinary *dfsum = new TIntermBinary(EOpAdd, dfdx, dfdy);
715 TVariable *d = CreateTempVariable(symbolTable, vec2Type);
716 TIntermDeclaration *dDecl = CreateTempInitDeclarationNode(d, dfsum);
717
718 // vec2 f = gl_FragCoord.xy
719 const TVariable *fragCoord = static_cast<const TVariable *>(
720 symbolTable->findBuiltIn(ImmutableString("gl_FragCoord"), compiler->getShaderVersion()));
721 TIntermSwizzle *fragCoordXY = CreateSwizzle(new TIntermSymbol(fragCoord), 0, 1);
722 TVariable *f = CreateTempVariable(symbolTable, vec2Type);
723 TIntermDeclaration *fDecl = CreateTempInitDeclarationNode(f, fragCoordXY);
724
725 // vec2 p_ = p.yx
726 TIntermSwizzle *pyx = CreateSwizzle(new TIntermSymbol(p), 1, 0);
727 TVariable *p_ = CreateTempVariable(symbolTable, vec2Type);
728 TIntermDeclaration *p_decl = CreateTempInitDeclarationNode(p_, pyx);
729
730 // vec2 d_ = d.yx
731 TIntermSwizzle *dyx = CreateSwizzle(new TIntermSymbol(d), 1, 0);
732 TVariable *d_ = CreateTempVariable(symbolTable, vec2Type);
733 TIntermDeclaration *d_decl = CreateTempInitDeclarationNode(d_, dyx);
734
735 // vec2 f_ = f.yx
736 TIntermSwizzle *fyx = CreateSwizzle(new TIntermSymbol(f), 1, 0);
737 TVariable *f_ = CreateTempVariable(symbolTable, vec2Type);
738 TIntermDeclaration *f_decl = CreateTempInitDeclarationNode(f_, fyx);
739
740 // vec2 i = abs(p - f + (d/d_) * (f_ - p_))
741 TIntermBinary *dd = new TIntermBinary(EOpDiv, new TIntermSymbol(d), new TIntermSymbol(d_));
742 TIntermBinary *fp = new TIntermBinary(EOpSub, new TIntermSymbol(f_), new TIntermSymbol(p_));
743 TIntermBinary *ddfp = new TIntermBinary(EOpMul, dd, fp);
744 TIntermBinary *pf = new TIntermBinary(EOpSub, new TIntermSymbol(p), new TIntermSymbol(f));
745 TIntermBinary *expr = new TIntermBinary(EOpAdd, pf, ddfp);
746
747 TIntermTyped *absd = CreateBuiltInUnaryFunctionCallNode("abs", expr, *symbolTable, 100);
748 TVariable *i = CreateTempVariable(symbolTable, vec2Type);
749 TIntermDeclaration *iDecl = CreateTempInitDeclarationNode(i, absd);
750
751 // Using a small epsilon value ensures that we don't suffer from numerical instability when
752 // lines are exactly vertical or horizontal.
753 static constexpr float kEpsilon = 0.0001f;
754 static constexpr float kThreshold = 0.5 + kEpsilon;
755 TIntermConstantUnion *threshold = CreateFloatNode(kThreshold, EbpHigh);
756
757 // if (i.x > (0.5 + e) && i.y > (0.5 + e))
758 TIntermSwizzle *ix = CreateSwizzle(new TIntermSymbol(i), 0);
759 TIntermBinary *checkX = new TIntermBinary(EOpGreaterThan, ix, threshold);
760 TIntermSwizzle *iy = CreateSwizzle(new TIntermSymbol(i), 1);
761 TIntermBinary *checkY = new TIntermBinary(EOpGreaterThan, iy, threshold->deepCopy());
762 TIntermBinary *checkXY = new TIntermBinary(EOpLogicalAnd, checkX, checkY);
763
764 // discard
765 TIntermBranch *discard = new TIntermBranch(EOpKill, nullptr);
766 TIntermBlock *discardBlock = new TIntermBlock;
767 discardBlock->appendStatement(discard);
768 TIntermIfElse *ifStatement = new TIntermIfElse(checkXY, discardBlock, nullptr);
769
770 TIntermBlock *emulationBlock = new TIntermBlock;
771 TIntermSequence *emulationSequence = emulationBlock->getSequence();
772
773 std::array<TIntermNode *, 8> nodes = {
774 {pDecl, dDecl, fDecl, p_decl, d_decl, f_decl, iDecl, ifStatement}};
775 emulationSequence->insert(emulationSequence->begin(), nodes.begin(), nodes.end());
776
777 TIntermIfElse *ifEmulation =
778 new TIntermIfElse(specConst->getLineRasterEmulation(), emulationBlock, nullptr);
779
780 // Ensure the line raster code runs at the beginning of main().
781 TIntermFunctionDefinition *main = FindMain(root);
782 TIntermSequence *mainSequence = main->getBody()->getSequence();
783 ASSERT(mainSequence);
784
785 mainSequence->insert(mainSequence->begin(), ifEmulation);
786
787 // If the shader does not use frag coord, we should insert it inside the emulation if.
788 if (!usesFragCoord)
789 {
790 if (!InsertFragCoordCorrection(compiler, compileOptions, root, emulationSequence,
791 symbolTable, specConst, driverUniforms))
792 {
793 return false;
794 }
795 }
796
797 return compiler->validateAST(root);
798 }
799 } // anonymous namespace
800
TranslatorVulkan(sh::GLenum type,ShShaderSpec spec)801 TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec)
802 : TCompiler(type, spec, SH_GLSL_450_CORE_OUTPUT)
803 {}
804
translateImpl(TInfoSinkBase & sink,TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics *,SpecConst * specConst,DriverUniform * driverUniforms)805 bool TranslatorVulkan::translateImpl(TInfoSinkBase &sink,
806 TIntermBlock *root,
807 ShCompileOptions compileOptions,
808 PerformanceDiagnostics * /*perfDiagnostics*/,
809 SpecConst *specConst,
810 DriverUniform *driverUniforms)
811 {
812 if (getShaderType() == GL_VERTEX_SHADER)
813 {
814 if (!ShaderBuiltinsWorkaround(this, root, &getSymbolTable(), compileOptions))
815 {
816 return false;
817 }
818 }
819
820 sink << "#version 450 core\n";
821 writeExtensionBehavior(compileOptions, sink);
822 WritePragma(sink, compileOptions, getPragma());
823
824 // Write out default uniforms into a uniform block assigned to a specific set/binding.
825 int defaultUniformCount = 0;
826 int aggregateTypesUsedForUniforms = 0;
827 int r32fImageCount = 0;
828 int atomicCounterCount = 0;
829 for (const auto &uniform : getUniforms())
830 {
831 if (!uniform.isBuiltIn() && uniform.active && !gl::IsOpaqueType(uniform.type))
832 {
833 ++defaultUniformCount;
834 }
835
836 if (uniform.isStruct() || uniform.isArrayOfArrays())
837 {
838 ++aggregateTypesUsedForUniforms;
839 }
840
841 if (uniform.active && gl::IsImageType(uniform.type) && uniform.imageUnitFormat == GL_R32F)
842 {
843 ++r32fImageCount;
844 }
845
846 if (uniform.active && gl::IsAtomicCounterType(uniform.type))
847 {
848 ++atomicCounterCount;
849 }
850 }
851
852 // Remove declarations of inactive shader interface variables so glslang wrapper doesn't need to
853 // replace them. Note: this is done before extracting samplers from structs, as removing such
854 // inactive samplers is not yet supported. Note also that currently, CollectVariables marks
855 // every field of an active uniform that's of struct type as active, i.e. no extracted sampler
856 // is inactive.
857 if (!RemoveInactiveInterfaceVariables(this, root, &getSymbolTable(), getAttributes(),
858 getInputVaryings(), getOutputVariables(), getUniforms(),
859 getInterfaceBlocks(), true))
860 {
861 return false;
862 }
863
864 // If there are any function calls that take array-of-array of opaque uniform parameters, or
865 // other opaque uniforms that need special handling in Vulkan, such as atomic counters,
866 // monomorphize the functions by removing said parameters and replacing them in the function
867 // body with the call arguments.
868 //
869 // This has a few benefits:
870 //
871 // - It dramatically simplifies future transformations w.r.t to samplers in structs, array of
872 // arrays of opaque types, atomic counters etc.
873 // - Avoids the need for shader*ArrayDynamicIndexing Vulkan features.
874 if (!MonomorphizeUnsupportedFunctions(this, root, &getSymbolTable(), compileOptions))
875 {
876 return false;
877 }
878
879 if (aggregateTypesUsedForUniforms > 0)
880 {
881 if (!SeparateStructFromUniformDeclarations(this, root, &getSymbolTable()))
882 {
883 return false;
884 }
885
886 int removedUniformsCount;
887
888 if (!RewriteStructSamplers(this, root, &getSymbolTable(), &removedUniformsCount))
889 {
890 return false;
891 }
892 defaultUniformCount -= removedUniformsCount;
893 }
894
895 // Replace array of array of opaque uniforms with a flattened array. This is run after
896 // MonomorphizeUnsupportedFunctions and RewriteStructSamplers so that it's not possible for an
897 // array of array of opaque type to be partially subscripted and passed to a function.
898 if (!RewriteArrayOfArrayOfOpaqueUniforms(this, root, &getSymbolTable()))
899 {
900 return false;
901 }
902
903 // Rewrite samplerCubes as sampler2DArrays. This must be done after rewriting struct samplers
904 // as it doesn't expect that.
905 if ((compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING) != 0)
906 {
907 if (!RewriteCubeMapSamplersAs2DArray(this, root, &getSymbolTable(),
908 getShaderType() == GL_FRAGMENT_SHADER))
909 {
910 return false;
911 }
912 }
913
914 if (!FlagSamplersForTexelFetch(this, root, &getSymbolTable(), &mUniforms))
915 {
916 return false;
917 }
918
919 gl::ShaderType packedShaderType = gl::FromGLenum<gl::ShaderType>(getShaderType());
920
921 if (defaultUniformCount > 0)
922 {
923 if (!DeclareDefaultUniforms(this, root, &getSymbolTable(), packedShaderType))
924 {
925 return false;
926 }
927 }
928
929 if (getShaderType() == GL_COMPUTE_SHADER)
930 {
931 driverUniforms->addComputeDriverUniformsToShader(root, &getSymbolTable());
932 }
933 else
934 {
935 driverUniforms->addGraphicsDriverUniformsToShader(root, &getSymbolTable());
936 }
937
938 if (r32fImageCount > 0)
939 {
940 if (!RewriteR32fImages(this, root, &getSymbolTable()))
941 {
942 return false;
943 }
944 }
945
946 if (atomicCounterCount > 0)
947 {
948 // ANGLEUniforms.acbBufferOffsets
949 const TIntermTyped *acbBufferOffsets = driverUniforms->getAbcBufferOffsets();
950 if (!RewriteAtomicCounters(this, root, &getSymbolTable(), acbBufferOffsets))
951 {
952 return false;
953 }
954 }
955 else if (getShaderVersion() >= 310)
956 {
957 // Vulkan doesn't support Atomic Storage as a Storage Class, but we've seen
958 // cases where builtins are using it even with no active atomic counters.
959 // This pass simply removes those builtins in that scenario.
960 if (!RemoveAtomicCounterBuiltins(this, root))
961 {
962 return false;
963 }
964 }
965
966 if (packedShaderType != gl::ShaderType::Compute)
967 {
968 if (!ReplaceGLDepthRangeWithDriverUniform(this, root, driverUniforms, &getSymbolTable()))
969 {
970 return false;
971 }
972
973 // Search for the gl_ClipDistance/gl_CullDistance usage, if its used, we need to do some
974 // replacements.
975 bool useClipDistance = false;
976 bool useCullDistance = false;
977 for (const ShaderVariable &outputVarying : mOutputVaryings)
978 {
979 if (outputVarying.name == "gl_ClipDistance")
980 {
981 useClipDistance = true;
982 }
983 else if (outputVarying.name == "gl_CullDistance")
984 {
985 useCullDistance = true;
986 }
987 }
988 for (const ShaderVariable &inputVarying : mInputVaryings)
989 {
990 if (inputVarying.name == "gl_ClipDistance")
991 {
992 useClipDistance = true;
993 }
994 else if (inputVarying.name == "gl_CullDistance")
995 {
996 useCullDistance = true;
997 }
998 }
999
1000 if (useClipDistance &&
1001 !ReplaceClipDistanceAssignments(this, root, &getSymbolTable(), getShaderType(),
1002 driverUniforms->getClipDistancesEnabled()))
1003 {
1004 return false;
1005 }
1006 if (useCullDistance &&
1007 !ReplaceCullDistanceAssignments(this, root, &getSymbolTable(), getShaderType()))
1008 {
1009 return false;
1010 }
1011 }
1012
1013 if (gl::ShaderTypeSupportsTransformFeedback(packedShaderType))
1014 {
1015 if ((compileOptions & SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE) != 0)
1016 {
1017 // Add support code for transform feedback extension.
1018 if (!AddXfbExtensionSupport(this, root, &getSymbolTable(), driverUniforms))
1019 {
1020 return false;
1021 }
1022 }
1023 }
1024
1025 switch (packedShaderType)
1026 {
1027 case gl::ShaderType::Fragment:
1028 {
1029 bool usesPointCoord = false;
1030 bool usesFragCoord = false;
1031 bool usesSampleMaskIn = false;
1032 bool usesLastFragData = false;
1033 bool useSamplePosition = false;
1034
1035 // Search for the gl_PointCoord usage, if its used, we need to flip the y coordinate.
1036 for (const ShaderVariable &inputVarying : mInputVaryings)
1037 {
1038 if (!inputVarying.isBuiltIn())
1039 {
1040 continue;
1041 }
1042
1043 if (inputVarying.name == "gl_SampleMaskIn")
1044 {
1045 usesSampleMaskIn = true;
1046 continue;
1047 }
1048
1049 if (inputVarying.name == "gl_SamplePosition")
1050 {
1051 useSamplePosition = true;
1052 continue;
1053 }
1054
1055 if (inputVarying.name == "gl_PointCoord")
1056 {
1057 usesPointCoord = true;
1058 break;
1059 }
1060
1061 if (inputVarying.name == "gl_FragCoord")
1062 {
1063 usesFragCoord = true;
1064 break;
1065 }
1066
1067 if (inputVarying.name == "gl_LastFragData")
1068 {
1069 usesLastFragData = true;
1070 break;
1071 }
1072 }
1073
1074 if ((compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION) != 0)
1075 {
1076 if (!AddBresenhamEmulationFS(this, compileOptions, root, &getSymbolTable(),
1077 specConst, driverUniforms, usesFragCoord))
1078 {
1079 return false;
1080 }
1081 }
1082
1083 bool usePreRotation = (compileOptions & SH_ADD_PRE_ROTATION) != 0;
1084 bool hasGLSampleMask = false;
1085
1086 for (const ShaderVariable &outputVar : mOutputVariables)
1087 {
1088 if (outputVar.name == "gl_SampleMask")
1089 {
1090 ASSERT(!hasGLSampleMask);
1091 hasGLSampleMask = true;
1092 continue;
1093 }
1094 }
1095
1096 // Emulate gl_FragColor and gl_FragData with normal output variables.
1097 if (!EmulateFragColorData(this, root, &getSymbolTable()))
1098 {
1099 return false;
1100 }
1101
1102 if (usesPointCoord)
1103 {
1104 TIntermTyped *flipNegXY = specConst->getNegFlipXY();
1105 if (!flipNegXY)
1106 {
1107 flipNegXY = driverUniforms->getNegFlipXYRef();
1108 }
1109 TIntermConstantUnion *pivot = CreateFloatNode(0.5f, EbpMedium);
1110 TIntermTyped *fragRotation = nullptr;
1111 if (usePreRotation)
1112 {
1113 fragRotation = specConst->getFragRotationMatrix();
1114 if (!fragRotation)
1115 {
1116 fragRotation = driverUniforms->getFragRotationMatrixRef();
1117 }
1118 }
1119 if (!RotateAndFlipBuiltinVariable(this, root, GetMainSequence(root), flipNegXY,
1120 &getSymbolTable(),
1121 BuiltInVariable::gl_PointCoord(),
1122 kFlippedPointCoordName, pivot, fragRotation))
1123 {
1124 return false;
1125 }
1126 }
1127
1128 if (useSamplePosition)
1129 {
1130 TIntermTyped *flipXY = specConst->getFlipXY();
1131 if (!flipXY)
1132 {
1133 flipXY = driverUniforms->getFlipXYRef();
1134 }
1135 TIntermConstantUnion *pivot = CreateFloatNode(0.5f, EbpMedium);
1136 TIntermTyped *fragRotation = nullptr;
1137 if (usePreRotation)
1138 {
1139 fragRotation = specConst->getFragRotationMatrix();
1140 if (!fragRotation)
1141 {
1142 fragRotation = driverUniforms->getFragRotationMatrixRef();
1143 }
1144 }
1145 if (!RotateAndFlipBuiltinVariable(this, root, GetMainSequence(root), flipXY,
1146 &getSymbolTable(),
1147 BuiltInVariable::gl_SamplePosition(),
1148 kFlippedPointCoordName, pivot, fragRotation))
1149 {
1150 return false;
1151 }
1152 }
1153
1154 if (usesFragCoord)
1155 {
1156 if (!InsertFragCoordCorrection(this, compileOptions, root, GetMainSequence(root),
1157 &getSymbolTable(), specConst, driverUniforms))
1158 {
1159 return false;
1160 }
1161 }
1162
1163 if (usesLastFragData && !ReplaceLastFragData(this, root, &getSymbolTable(), &mUniforms))
1164 {
1165 return false;
1166 }
1167
1168 if (!ReplaceInOutVariables(this, root, &getSymbolTable(), &mUniforms))
1169 {
1170 return false;
1171 }
1172
1173 if (!RewriteDfdy(this, compileOptions, root, getSymbolTable(), getShaderVersion(),
1174 specConst, driverUniforms))
1175 {
1176 return false;
1177 }
1178
1179 if (!RewriteInterpolateAtOffset(this, compileOptions, root, getSymbolTable(),
1180 getShaderVersion(), specConst, driverUniforms))
1181 {
1182 return false;
1183 }
1184
1185 if (usesSampleMaskIn && !RewriteSampleMaskIn(this, root, &getSymbolTable()))
1186 {
1187 return false;
1188 }
1189
1190 if (hasGLSampleMask)
1191 {
1192 TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
1193 if (!RewriteSampleMask(this, root, &getSymbolTable(), numSamples))
1194 {
1195 return false;
1196 }
1197 }
1198
1199 {
1200 const TVariable *numSamplesVar =
1201 static_cast<const TVariable *>(getSymbolTable().findBuiltIn(
1202 ImmutableString("gl_NumSamples"), getShaderVersion()));
1203 TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
1204 if (!ReplaceVariableWithTyped(this, root, numSamplesVar, numSamples))
1205 {
1206 return false;
1207 }
1208 }
1209
1210 EmitEarlyFragmentTestsGLSL(*this, sink);
1211 break;
1212 }
1213
1214 case gl::ShaderType::Vertex:
1215 {
1216 if ((compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION) != 0)
1217 {
1218 if (!AddBresenhamEmulationVS(this, root, &getSymbolTable(), specConst,
1219 driverUniforms))
1220 {
1221 return false;
1222 }
1223 }
1224
1225 if ((compileOptions & SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE) != 0)
1226 {
1227 // Add support code for transform feedback emulation. Only applies to vertex shader
1228 // as tessellation and geometry shader transform feedback capture require
1229 // VK_EXT_transform_feedback.
1230 if (!AddXfbEmulationSupport(this, root, &getSymbolTable(), driverUniforms))
1231 {
1232 return false;
1233 }
1234 }
1235
1236 // Append depth range translation to main.
1237 if (!transformDepthBeforeCorrection(root, driverUniforms))
1238 {
1239 return false;
1240 }
1241
1242 break;
1243 }
1244
1245 case gl::ShaderType::Geometry:
1246 {
1247 int maxVertices = getGeometryShaderMaxVertices();
1248
1249 // max_vertices=0 is not valid in Vulkan
1250 maxVertices = std::max(1, maxVertices);
1251
1252 WriteGeometryShaderLayoutQualifiers(
1253 sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
1254 getGeometryShaderOutputPrimitiveType(), maxVertices);
1255 break;
1256 }
1257
1258 case gl::ShaderType::TessControl:
1259 {
1260 if (!ReplaceGLBoundingBoxWithGlobal(this, root, &getSymbolTable()))
1261 {
1262 return false;
1263 }
1264 WriteTessControlShaderLayoutQualifiers(sink, getTessControlShaderOutputVertices());
1265 break;
1266 }
1267
1268 case gl::ShaderType::TessEvaluation:
1269 {
1270 WriteTessEvaluationShaderLayoutQualifiers(
1271 sink, getTessEvaluationShaderInputPrimitiveType(),
1272 getTessEvaluationShaderInputVertexSpacingType(),
1273 getTessEvaluationShaderInputOrderingType(),
1274 getTessEvaluationShaderInputPointType());
1275 break;
1276 }
1277
1278 case gl::ShaderType::Compute:
1279 {
1280 EmitWorkGroupSizeGLSL(*this, sink);
1281 break;
1282 }
1283
1284 default:
1285 UNREACHABLE();
1286 break;
1287 }
1288
1289 specConst->declareSpecConsts(root);
1290 mValidateASTOptions.validateSpecConstReferences = true;
1291
1292 // Gather specialization constant usage bits so that we can feedback to context.
1293 mSpecConstUsageBits = specConst->getSpecConstUsageBits();
1294
1295 if (!validateAST(root))
1296 {
1297 return false;
1298 }
1299
1300 // Make sure function call validation is not accidentally left off anywhere.
1301 ASSERT(mValidateASTOptions.validateFunctionCall);
1302 ASSERT(mValidateASTOptions.validateNoRawFunctionCalls);
1303
1304 return true;
1305 }
1306
writeExtensionBehavior(ShCompileOptions compileOptions,TInfoSinkBase & sink)1307 void TranslatorVulkan::writeExtensionBehavior(ShCompileOptions compileOptions, TInfoSinkBase &sink)
1308 {
1309 const TExtensionBehavior &extBehavior = getExtensionBehavior();
1310 TBehavior multiviewBehavior = EBhUndefined;
1311 TBehavior multiview2Behavior = EBhUndefined;
1312 for (const auto &iter : extBehavior)
1313 {
1314 if (iter.second == EBhUndefined || iter.second == EBhDisable)
1315 {
1316 continue;
1317 }
1318
1319 switch (iter.first)
1320 {
1321 case TExtension::OVR_multiview:
1322 multiviewBehavior = iter.second;
1323 break;
1324 case TExtension::OVR_multiview2:
1325 multiviewBehavior = iter.second;
1326 break;
1327 default:
1328 break;
1329 }
1330 }
1331
1332 if (multiviewBehavior != EBhUndefined || multiview2Behavior != EBhUndefined)
1333 {
1334 // Only either OVR_multiview or OVR_multiview2 should be emitted.
1335 TExtension ext = TExtension::OVR_multiview;
1336 TBehavior behavior = multiviewBehavior;
1337 if (multiview2Behavior != EBhUndefined)
1338 {
1339 ext = TExtension::OVR_multiview2;
1340 behavior = multiview2Behavior;
1341 }
1342 EmitMultiviewGLSL(*this, compileOptions, ext, behavior, sink);
1343 }
1344 }
1345
translate(TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics * perfDiagnostics)1346 bool TranslatorVulkan::translate(TIntermBlock *root,
1347 ShCompileOptions compileOptions,
1348 PerformanceDiagnostics *perfDiagnostics)
1349 {
1350 TInfoSinkBase sink;
1351
1352 SpecConst specConst(&getSymbolTable(), compileOptions, getShaderType());
1353
1354 if ((compileOptions & SH_USE_SPECIALIZATION_CONSTANT) != 0)
1355 {
1356 DriverUniform driverUniforms(DriverUniformMode::InterfaceBlock);
1357 if (!translateImpl(sink, root, compileOptions, perfDiagnostics, &specConst,
1358 &driverUniforms))
1359 {
1360 return false;
1361 }
1362 }
1363 else
1364 {
1365 DriverUniformExtended driverUniformsExt(DriverUniformMode::InterfaceBlock);
1366 if (!translateImpl(sink, root, compileOptions, perfDiagnostics, &specConst,
1367 &driverUniformsExt))
1368 {
1369 return false;
1370 }
1371 }
1372
1373 #if defined(ANGLE_ENABLE_SPIRV_GENERATION_THROUGH_GLSLANG)
1374 if ((compileOptions & SH_GENERATE_SPIRV_THROUGH_GLSLANG) != 0)
1375 {
1376 // When generating text, glslang cannot know the precision of folded constants so it may
1377 // infer the wrong precisions. The following transformation gives constants names with
1378 // precision to guide glslang. This is not an issue for SPIR-V generation because the
1379 // precision information is present in the tree already.
1380 if (!RecordConstantPrecision(this, root, &getSymbolTable()))
1381 {
1382 return false;
1383 }
1384
1385 const bool enablePrecision = (compileOptions & SH_IGNORE_PRECISION_QUALIFIERS) == 0;
1386
1387 // Write translated shader.
1388 TOutputVulkanGLSL outputGLSL(this, sink, enablePrecision, compileOptions);
1389 root->traverse(&outputGLSL);
1390
1391 return compileToSpirv(sink);
1392 }
1393 #endif
1394
1395 // Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help SPIR-V
1396 // generation treat them mostly like usual I/O blocks.
1397 if (!DeclarePerVertexBlocks(this, root, &getSymbolTable()))
1398 {
1399 return false;
1400 }
1401
1402 return OutputSPIRV(this, root, compileOptions);
1403 }
1404
shouldFlattenPragmaStdglInvariantAll()1405 bool TranslatorVulkan::shouldFlattenPragmaStdglInvariantAll()
1406 {
1407 // Not necessary.
1408 return false;
1409 }
1410
compileToSpirv(const TInfoSinkBase & glsl)1411 bool TranslatorVulkan::compileToSpirv(const TInfoSinkBase &glsl)
1412 {
1413 angle::spirv::Blob spirvBlob;
1414 if (!GlslangCompileToSpirv(getResources(), getShaderType(), glsl.str(), &spirvBlob))
1415 {
1416 return false;
1417 }
1418
1419 getInfoSink().obj.setBinary(std::move(spirvBlob));
1420 return true;
1421 }
1422 } // namespace sh
1423