• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2019 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 // TranslatorMetal:
7 //   A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl.
8 //   It takes into account some considerations for Metal backend also.
9 //   The shaders are then fed into glslang to spit out SPIR-V.
10 //   See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt
11 //
12 //   The SPIR-V will then be translated to Metal Shading Language later in Metal backend.
13 //
14 
15 #include "compiler/translator/TranslatorMetal.h"
16 
17 #include "angle_gl.h"
18 #include "common/utilities.h"
19 #include "compiler/translator/OutputVulkanGLSL.h"
20 #include "compiler/translator/StaticType.h"
21 #include "compiler/translator/tree_ops/InitializeVariables.h"
22 #include "compiler/translator/tree_util/BuiltIn.h"
23 #include "compiler/translator/tree_util/DriverUniform.h"
24 #include "compiler/translator/tree_util/FindMain.h"
25 #include "compiler/translator/tree_util/FindSymbolNode.h"
26 #include "compiler/translator/tree_util/IntermNode_util.h"
27 #include "compiler/translator/tree_util/ReplaceArrayOfMatrixVarying.h"
28 #include "compiler/translator/tree_util/ReplaceVariable.h"
29 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
30 #include "compiler/translator/util.h"
31 
32 namespace sh
33 {
34 
35 namespace mtl
36 {
37 /** extern */
38 const char kCoverageMaskEnabledConstName[]      = "ANGLECoverageMaskEnabled";
39 const char kRasterizerDiscardEnabledConstName[] = "ANGLERasterizerDisabled";
40 }  // namespace mtl
41 
42 namespace
43 {
44 
45 constexpr ImmutableString kSampleMaskWriteFuncName = ImmutableString("ANGLEWriteSampleMask");
46 
47 // Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
48 // manually.
49 // This operation performs flipping the gl_Position.y using this expression:
50 // gl_Position.y = gl_Position.y * negViewportScaleY
AppendVertexShaderPositionYCorrectionToMain(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,TIntermTyped * negFlipY)51 ANGLE_NO_DISCARD bool AppendVertexShaderPositionYCorrectionToMain(TCompiler *compiler,
52                                                                   TIntermBlock *root,
53                                                                   TSymbolTable *symbolTable,
54                                                                   TIntermTyped *negFlipY)
55 {
56     // Create a symbol reference to "gl_Position"
57     const TVariable *position  = BuiltInVariable::gl_Position();
58     TIntermSymbol *positionRef = new TIntermSymbol(position);
59 
60     // Create a swizzle to "gl_Position.y"
61     TVector<int> swizzleOffsetY;
62     swizzleOffsetY.push_back(1);
63     TIntermSwizzle *positionY = new TIntermSwizzle(positionRef, swizzleOffsetY);
64 
65     // Create the expression "gl_Position.y * negFlipY"
66     TIntermBinary *inverseY = new TIntermBinary(EOpMul, positionY->deepCopy(), negFlipY);
67 
68     // Create the assignment "gl_Position.y = gl_Position.y * negViewportScaleY
69     TIntermTyped *positionYLHS = positionY->deepCopy();
70     TIntermBinary *assignment  = new TIntermBinary(TOperator::EOpAssign, positionYLHS, inverseY);
71 
72     // Append the assignment as a statement at the end of the shader.
73     return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
74 }
75 
76 // Initialize unused varying outputs.
InitializeUnusedOutputs(TIntermBlock * root,TSymbolTable * symbolTable,const InitVariableList & unusedVars)77 ANGLE_NO_DISCARD bool InitializeUnusedOutputs(TIntermBlock *root,
78                                               TSymbolTable *symbolTable,
79                                               const InitVariableList &unusedVars)
80 {
81     if (unusedVars.empty())
82     {
83         return true;
84     }
85 
86     TIntermSequence insertSequence;
87 
88     for (const sh::ShaderVariable &var : unusedVars)
89     {
90         ASSERT(!var.active);
91         const TIntermSymbol *symbol = FindSymbolNode(root, var.name);
92         ASSERT(symbol);
93 
94         TIntermSequence initCode;
95         CreateInitCode(symbol, false, false, &initCode, symbolTable);
96 
97         insertSequence.insert(insertSequence.end(), initCode.begin(), initCode.end());
98     }
99 
100     if (!insertSequence.empty())
101     {
102         TIntermFunctionDefinition *main = FindMain(root);
103         TIntermSequence *mainSequence   = main->getBody()->getSequence();
104 
105         // Insert init code at the start of main()
106         mainSequence->insert(mainSequence->begin(), insertSequence.begin(), insertSequence.end());
107     }
108 
109     return true;
110 }
111 }  // anonymous namespace
112 
TranslatorMetal(sh::GLenum type,ShShaderSpec spec)113 TranslatorMetal::TranslatorMetal(sh::GLenum type, ShShaderSpec spec) : TranslatorVulkan(type, spec)
114 {}
115 
translate(TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics * perfDiagnostics)116 bool TranslatorMetal::translate(TIntermBlock *root,
117                                 ShCompileOptions compileOptions,
118                                 PerformanceDiagnostics *perfDiagnostics)
119 {
120     TInfoSinkBase sink;
121 
122     SpecConstMetal specConst(&getSymbolTable(), compileOptions, getShaderType());
123     DriverUniformMetal driverUniforms;
124     if (!TranslatorVulkan::translateImpl(sink, root, compileOptions, perfDiagnostics, &specConst,
125                                          &driverUniforms))
126     {
127         return false;
128     }
129 
130     // Replace array of matrix varyings
131     if (!ReplaceArrayOfMatrixVaryings(this, root, &getSymbolTable()))
132     {
133         return false;
134     }
135 
136     if (getShaderType() == GL_VERTEX_SHADER)
137     {
138         TIntermTyped *negFlipY = driverUniforms.getNegFlipYRef();
139 
140         // Append gl_Position.y correction to main
141         if (!AppendVertexShaderPositionYCorrectionToMain(this, root, &getSymbolTable(), negFlipY))
142         {
143             return false;
144         }
145 
146         // Insert rasterizer discard logic
147         if (!insertRasterizerDiscardLogic(sink, root))
148         {
149             return false;
150         }
151     }
152     else if (getShaderType() == GL_FRAGMENT_SHADER)
153     {
154         if (!insertSampleMaskWritingLogic(sink, root, &driverUniforms))
155         {
156             return false;
157         }
158     }
159 
160     // Initialize unused varying outputs to avoid spirv-cross dead-code removing them in later
161     // stage. Only do this if SH_INIT_OUTPUT_VARIABLES is not specified.
162     if ((getShaderType() == GL_VERTEX_SHADER || getShaderType() == GL_GEOMETRY_SHADER_EXT) &&
163         (compileOptions & SH_INIT_OUTPUT_VARIABLES) == 0)
164     {
165         InitVariableList list;
166         for (const sh::ShaderVariable &var : mOutputVaryings)
167         {
168             if (!var.active)
169             {
170                 list.push_back(var);
171             }
172         }
173 
174         if (!InitializeUnusedOutputs(root, &getSymbolTable(), list))
175         {
176             return false;
177         }
178     }
179 
180     // Write translated shader.
181     TOutputVulkanGLSL outputGLSL(this, sink, true, compileOptions);
182     root->traverse(&outputGLSL);
183 
184     return compileToSpirv(sink);
185 }
186 
187 // Metal needs to inverse the depth if depthRange is is reverse order, i.e. depth near > depth far
188 // This is achieved by multiply the depth value with scale value stored in
189 // driver uniform's depthRange.reserved
transformDepthBeforeCorrection(TIntermBlock * root,const DriverUniform * driverUniforms)190 bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
191                                                      const DriverUniform *driverUniforms)
192 {
193     // Create a symbol reference to "gl_Position"
194     const TVariable *position  = BuiltInVariable::gl_Position();
195     TIntermSymbol *positionRef = new TIntermSymbol(position);
196 
197     // Create a swizzle to "gl_Position.z"
198     TVector<int> swizzleOffsetZ = {2};
199     TIntermSwizzle *positionZ   = new TIntermSwizzle(positionRef, swizzleOffsetZ);
200 
201     // Create a ref to "depthRange.reserved"
202     TIntermBinary *viewportZScale = driverUniforms->getDepthRangeReservedFieldRef();
203 
204     // Create the expression "gl_Position.z * depthRange.reserved".
205     TIntermBinary *zScale = new TIntermBinary(EOpMul, positionZ->deepCopy(), viewportZScale);
206 
207     // Create the assignment "gl_Position.z = gl_Position.z * depthRange.reserved"
208     TIntermTyped *positionZLHS = positionZ->deepCopy();
209     TIntermBinary *assignment  = new TIntermBinary(TOperator::EOpAssign, positionZLHS, zScale);
210 
211     // Append the assignment as a statement at the end of the shader.
212     return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable());
213 }
214 
215 // Add sample_mask writing to main, guarded by the specialization constant
216 // kCoverageMaskEnabledConstName
insertSampleMaskWritingLogic(TInfoSinkBase & sink,TIntermBlock * root,const DriverUniformMetal * driverUniforms)217 ANGLE_NO_DISCARD bool TranslatorMetal::insertSampleMaskWritingLogic(
218     TInfoSinkBase &sink,
219     TIntermBlock *root,
220     const DriverUniformMetal *driverUniforms)
221 {
222     // This transformation leaves the tree in an inconsistent state by using a variable that's
223     // defined in text, outside of the knowledge of the AST.  Same with defining the function in
224     // text.
225     mValidateASTOptions.validateVariableReferences = false;
226     mValidateASTOptions.validateFunctionCall       = false;
227 
228     TSymbolTable *symbolTable = &getSymbolTable();
229 
230     // Insert coverageMaskEnabled specialization constant and sample_mask writing function.
231     sink << "layout (constant_id=0) const bool " << mtl::kCoverageMaskEnabledConstName;
232     sink << " = false;\n";
233     sink << "void " << kSampleMaskWriteFuncName << "(uint mask)\n";
234     sink << "{\n";
235     sink << "   if (" << mtl::kCoverageMaskEnabledConstName << ")\n";
236     sink << "   {\n";
237     sink << "       gl_SampleMask[0] = int(mask);\n";
238     sink << "   }\n";
239     sink << "}\n";
240 
241     // Create kCoverageMaskEnabledConstName and kSampleMaskWriteFuncName variable references.
242     TType *boolType = new TType(EbtBool);
243     boolType->setQualifier(EvqConst);
244     TVariable *coverageMaskEnabledVar =
245         new TVariable(symbolTable, ImmutableString(mtl::kCoverageMaskEnabledConstName), boolType,
246                       SymbolType::AngleInternal);
247 
248     TFunction *sampleMaskWriteFunc =
249         new TFunction(symbolTable, kSampleMaskWriteFuncName, SymbolType::AngleInternal,
250                       StaticType::GetBasic<EbtVoid, EbpUndefined>(), false);
251 
252     TType *uintType = new TType(EbtUInt);
253     TVariable *maskArg =
254         new TVariable(symbolTable, ImmutableString("mask"), uintType, SymbolType::AngleInternal);
255     sampleMaskWriteFunc->addParameter(maskArg);
256 
257     // coverageMask
258     TIntermBinary *coverageMask = driverUniforms->getCoverageMaskFieldRef();
259 
260     // Insert this code to the end of main()
261     // if (ANGLECoverageMaskEnabled)
262     // {
263     //      ANGLEWriteSampleMask(ANGLEUniforms.coverageMask);
264     // }
265     TIntermSequence args;
266     args.push_back(coverageMask);
267     TIntermAggregate *callSampleMaskWriteFunc =
268         TIntermAggregate::CreateFunctionCall(*sampleMaskWriteFunc, &args);
269     TIntermBlock *callBlock = new TIntermBlock;
270     callBlock->appendStatement(callSampleMaskWriteFunc);
271 
272     TIntermSymbol *coverageMaskEnabled = new TIntermSymbol(coverageMaskEnabledVar);
273     TIntermIfElse *ifCall              = new TIntermIfElse(coverageMaskEnabled, callBlock, nullptr);
274 
275     return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
276 }
277 
insertRasterizerDiscardLogic(TInfoSinkBase & sink,TIntermBlock * root)278 ANGLE_NO_DISCARD bool TranslatorMetal::insertRasterizerDiscardLogic(TInfoSinkBase &sink,
279                                                                     TIntermBlock *root)
280 {
281     // This transformation leaves the tree in an inconsistent state by using a variable that's
282     // defined in text, outside of the knowledge of the AST.
283     mValidateASTOptions.validateVariableReferences = false;
284 
285     TSymbolTable *symbolTable = &getSymbolTable();
286 
287     // Insert rasterizationDisabled specialization constant.
288     sink << "layout (constant_id=0) const bool " << mtl::kRasterizerDiscardEnabledConstName;
289     sink << " = false;\n";
290 
291     // Create kRasterizerDiscardEnabledConstName variable reference.
292     TType *boolType = new TType(EbtBool);
293     boolType->setQualifier(EvqConst);
294     TVariable *discardEnabledVar =
295         new TVariable(symbolTable, ImmutableString(mtl::kRasterizerDiscardEnabledConstName),
296                       boolType, SymbolType::AngleInternal);
297 
298     // Insert this code to the end of main()
299     // if (ANGLERasterizerDisabled)
300     // {
301     //      gl_Position = vec4(-3.0, -3.0, -3.0, 1.0);
302     // }
303     // Create a symbol reference to "gl_Position"
304     const TVariable *position  = BuiltInVariable::gl_Position();
305     TIntermSymbol *positionRef = new TIntermSymbol(position);
306 
307     // Create vec4(-3, -3, -3, 1):
308     auto vec4Type = new TType(EbtFloat, 4);
309     TIntermSequence vec4Args;
310     vec4Args.push_back(CreateFloatNode(-3.0f, EbpMedium));
311     vec4Args.push_back(CreateFloatNode(-3.0f, EbpMedium));
312     vec4Args.push_back(CreateFloatNode(-3.0f, EbpMedium));
313     vec4Args.push_back(CreateFloatNode(1.0f, EbpMedium));
314     TIntermAggregate *constVarConstructor =
315         TIntermAggregate::CreateConstructor(*vec4Type, &vec4Args);
316 
317     // Create the assignment "gl_Position = vec4(-3, -3, -3, 1)"
318     TIntermBinary *assignment =
319         new TIntermBinary(TOperator::EOpAssign, positionRef->deepCopy(), constVarConstructor);
320 
321     TIntermBlock *discardBlock = new TIntermBlock;
322     discardBlock->appendStatement(assignment);
323 
324     TIntermSymbol *discardEnabled = new TIntermSymbol(discardEnabledVar);
325     TIntermIfElse *ifCall         = new TIntermIfElse(discardEnabled, discardBlock, nullptr);
326 
327     return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
328 }
329 
330 }  // namespace sh
331