• 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/OutputVulkanGLSLForMetal.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 // Metal specific driver uniforms
45 constexpr const char kHalfRenderArea[] = "halfRenderArea";
46 constexpr const char kFlipXY[]         = "flipXY";
47 constexpr const char kNegFlipXY[]      = "negFlipXY";
48 constexpr const char kCoverageMask[]   = "coverageMask";
49 
50 constexpr ImmutableString kSampleMaskWriteFuncName = ImmutableString("ANGLEWriteSampleMask");
51 
52 // Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
53 // manually.
54 // This operation performs flipping the gl_Position.y using this expression:
55 // gl_Position.y = gl_Position.y * negViewportScaleY
AppendVertexShaderPositionYCorrectionToMain(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,TIntermTyped * negFlipY)56 ANGLE_NO_DISCARD bool AppendVertexShaderPositionYCorrectionToMain(TCompiler *compiler,
57                                                                   TIntermBlock *root,
58                                                                   TSymbolTable *symbolTable,
59                                                                   TIntermTyped *negFlipY)
60 {
61     // Create a symbol reference to "gl_Position"
62     const TVariable *position  = BuiltInVariable::gl_Position();
63     TIntermSymbol *positionRef = new TIntermSymbol(position);
64 
65     // Create a swizzle to "gl_Position.y"
66     TVector<int> swizzleOffsetY;
67     swizzleOffsetY.push_back(1);
68     TIntermSwizzle *positionY = new TIntermSwizzle(positionRef, swizzleOffsetY);
69 
70     // Create the expression "gl_Position.y * negFlipY"
71     TIntermBinary *inverseY = new TIntermBinary(EOpMul, positionY->deepCopy(), negFlipY);
72 
73     // Create the assignment "gl_Position.y = gl_Position.y * negViewportScaleY
74     TIntermTyped *positionYLHS = positionY->deepCopy();
75     TIntermBinary *assignment  = new TIntermBinary(TOperator::EOpAssign, positionYLHS, inverseY);
76 
77     // Append the assignment as a statement at the end of the shader.
78     return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
79 }
80 
81 // Initialize unused varying outputs.
InitializeUnusedOutputs(TIntermBlock * root,TSymbolTable * symbolTable,const InitVariableList & unusedVars)82 ANGLE_NO_DISCARD bool InitializeUnusedOutputs(TIntermBlock *root,
83                                               TSymbolTable *symbolTable,
84                                               const InitVariableList &unusedVars)
85 {
86     if (unusedVars.empty())
87     {
88         return true;
89     }
90 
91     TIntermSequence insertSequence;
92 
93     for (const sh::ShaderVariable &var : unusedVars)
94     {
95         ASSERT(!var.active);
96         const TIntermSymbol *symbol = FindSymbolNode(root, var.name);
97         ASSERT(symbol);
98 
99         TIntermSequence initCode;
100         CreateInitCode(symbol, false, false, &initCode, symbolTable);
101 
102         insertSequence.insert(insertSequence.end(), initCode.begin(), initCode.end());
103     }
104 
105     if (!insertSequence.empty())
106     {
107         TIntermFunctionDefinition *main = FindMain(root);
108         TIntermSequence *mainSequence   = main->getBody()->getSequence();
109 
110         // Insert init code at the start of main()
111         mainSequence->insert(mainSequence->begin(), insertSequence.begin(), insertSequence.end());
112     }
113 
114     return true;
115 }
116 }  // anonymous namespace
117 
118 // class DriverUniformMetal
createUniformFields(TSymbolTable * symbolTable)119 TFieldList *DriverUniformMetal::createUniformFields(TSymbolTable *symbolTable)
120 {
121     TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable);
122 
123     constexpr size_t kNumGraphicsDriverUniformsMetal = 4;
124     constexpr std::array<const char *, kNumGraphicsDriverUniformsMetal>
125         kGraphicsDriverUniformNamesMetal = {{kHalfRenderArea, kFlipXY, kNegFlipXY, kCoverageMask}};
126 
127     const std::array<TType *, kNumGraphicsDriverUniformsMetal> kDriverUniformTypesMetal = {{
128         new TType(EbtFloat, 2),  // halfRenderArea
129         new TType(EbtFloat, 2),  // flipXY
130         new TType(EbtFloat, 2),  // negFlipXY
131         new TType(EbtUInt),      // kCoverageMask
132     }};
133 
134     for (size_t uniformIndex = 0; uniformIndex < kNumGraphicsDriverUniformsMetal; ++uniformIndex)
135     {
136         TField *driverUniformField =
137             new TField(kDriverUniformTypesMetal[uniformIndex],
138                        ImmutableString(kGraphicsDriverUniformNamesMetal[uniformIndex]),
139                        TSourceLoc(), SymbolType::AngleInternal);
140         driverFieldList->push_back(driverUniformField);
141     }
142 
143     return driverFieldList;
144 }
145 
getHalfRenderAreaRef() const146 TIntermBinary *DriverUniformMetal::getHalfRenderAreaRef() const
147 {
148     return createDriverUniformRef(kHalfRenderArea);
149 }
150 
getFlipXYRef() const151 TIntermBinary *DriverUniformMetal::getFlipXYRef() const
152 {
153     return createDriverUniformRef(kFlipXY);
154 }
155 
getNegFlipXYRef() const156 TIntermBinary *DriverUniformMetal::getNegFlipXYRef() const
157 {
158     return createDriverUniformRef(kNegFlipXY);
159 }
160 
getNegFlipYRef() const161 TIntermSwizzle *DriverUniformMetal::getNegFlipYRef() const
162 {
163     // Create a swizzle to "negFlipXY.y"
164     TIntermBinary *negFlipXY    = createDriverUniformRef(kNegFlipXY);
165     TVector<int> swizzleOffsetY = {1};
166     TIntermSwizzle *negFlipY    = new TIntermSwizzle(negFlipXY, swizzleOffsetY);
167     return negFlipY;
168 }
169 
getCoverageMaskFieldRef() const170 TIntermBinary *DriverUniformMetal::getCoverageMaskFieldRef() const
171 {
172     return createDriverUniformRef(kCoverageMask);
173 }
174 
TranslatorMetal(sh::GLenum type,ShShaderSpec spec)175 TranslatorMetal::TranslatorMetal(sh::GLenum type, ShShaderSpec spec) : TranslatorVulkan(type, spec)
176 {}
177 
translate(TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics * perfDiagnostics)178 bool TranslatorMetal::translate(TIntermBlock *root,
179                                 ShCompileOptions compileOptions,
180                                 PerformanceDiagnostics *perfDiagnostics)
181 {
182     TInfoSinkBase sink;
183 
184     SpecConstMetal specConst(&getSymbolTable(), compileOptions, getShaderType());
185     DriverUniformMetal driverUniforms;
186     if (!TranslatorVulkan::translateImpl(sink, root, compileOptions, perfDiagnostics, &specConst,
187                                          &driverUniforms))
188     {
189         return false;
190     }
191 
192     // Replace array of matrix varyings
193     if (!ReplaceArrayOfMatrixVaryings(this, root, &getSymbolTable()))
194     {
195         return false;
196     }
197 
198     if (getShaderType() == GL_VERTEX_SHADER)
199     {
200         TIntermTyped *negFlipY = driverUniforms.getNegFlipYRef();
201 
202         // Append gl_Position.y correction to main
203         if (!AppendVertexShaderPositionYCorrectionToMain(this, root, &getSymbolTable(), negFlipY))
204         {
205             return false;
206         }
207 
208         // Insert rasterizer discard logic
209         if (!insertRasterizerDiscardLogic(sink, root))
210         {
211             return false;
212         }
213     }
214     else if (getShaderType() == GL_FRAGMENT_SHADER)
215     {
216         if (!insertSampleMaskWritingLogic(sink, root, &driverUniforms))
217         {
218             return false;
219         }
220     }
221 
222     // Initialize unused varying outputs to avoid spirv-cross dead-code removing them in later
223     // stage. Only do this if SH_INIT_OUTPUT_VARIABLES is not specified.
224     if ((getShaderType() == GL_VERTEX_SHADER || getShaderType() == GL_GEOMETRY_SHADER_EXT) &&
225         (compileOptions & SH_INIT_OUTPUT_VARIABLES) == 0)
226     {
227         InitVariableList list;
228         for (const sh::ShaderVariable &var : mOutputVaryings)
229         {
230             if (!var.active)
231             {
232                 list.push_back(var);
233             }
234         }
235 
236         if (!InitializeUnusedOutputs(root, &getSymbolTable(), list))
237         {
238             return false;
239         }
240     }
241 
242     // Write translated shader.
243     TOutputVulkanGLSL outputGLSL(sink, getHashFunction(), getNameMap(), &getSymbolTable(),
244                                  getShaderType(), getShaderVersion(), getOutputType(), false, true,
245                                  compileOptions);
246     root->traverse(&outputGLSL);
247 
248     return compileToSpirv(sink);
249 }
250 
251 // Metal needs to inverse the depth if depthRange is is reverse order, i.e. depth near > depth far
252 // This is achieved by multiply the depth value with scale value stored in
253 // driver uniform's depthRange.reserved
transformDepthBeforeCorrection(TIntermBlock * root,const DriverUniform * driverUniforms)254 bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
255                                                      const DriverUniform *driverUniforms)
256 {
257     // Create a symbol reference to "gl_Position"
258     const TVariable *position  = BuiltInVariable::gl_Position();
259     TIntermSymbol *positionRef = new TIntermSymbol(position);
260 
261     // Create a swizzle to "gl_Position.z"
262     TVector<int> swizzleOffsetZ = {2};
263     TIntermSwizzle *positionZ   = new TIntermSwizzle(positionRef, swizzleOffsetZ);
264 
265     // Create a ref to "depthRange.reserved"
266     TIntermBinary *viewportZScale = driverUniforms->getDepthRangeReservedFieldRef();
267 
268     // Create the expression "gl_Position.z * depthRange.reserved".
269     TIntermBinary *zScale = new TIntermBinary(EOpMul, positionZ->deepCopy(), viewportZScale);
270 
271     // Create the assignment "gl_Position.z = gl_Position.z * depthRange.reserved"
272     TIntermTyped *positionZLHS = positionZ->deepCopy();
273     TIntermBinary *assignment  = new TIntermBinary(TOperator::EOpAssign, positionZLHS, zScale);
274 
275     // Append the assignment as a statement at the end of the shader.
276     return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable());
277 }
278 
279 // Add sample_mask writing to main, guarded by the specialization constant
280 // kCoverageMaskEnabledConstName
insertSampleMaskWritingLogic(TInfoSinkBase & sink,TIntermBlock * root,const DriverUniformMetal * driverUniforms)281 ANGLE_NO_DISCARD bool TranslatorMetal::insertSampleMaskWritingLogic(
282     TInfoSinkBase &sink,
283     TIntermBlock *root,
284     const DriverUniformMetal *driverUniforms)
285 {
286     // This transformation leaves the tree in an inconsistent state by using a variable that's
287     // defined in text, outside of the knowledge of the AST.
288     mValidateASTOptions.validateVariableReferences = false;
289 
290     TSymbolTable *symbolTable = &getSymbolTable();
291 
292     // Insert coverageMaskEnabled specialization constant and sample_mask writing function.
293     sink << "layout (constant_id=0) const bool " << mtl::kCoverageMaskEnabledConstName;
294     sink << " = false;\n";
295     sink << "void " << kSampleMaskWriteFuncName << "(uint mask)\n";
296     sink << "{\n";
297     sink << "   if (" << mtl::kCoverageMaskEnabledConstName << ")\n";
298     sink << "   {\n";
299     sink << "       gl_SampleMask[0] = int(mask);\n";
300     sink << "   }\n";
301     sink << "}\n";
302 
303     // Create kCoverageMaskEnabledConstName and kSampleMaskWriteFuncName variable references.
304     TType *boolType = new TType(EbtBool);
305     boolType->setQualifier(EvqConst);
306     TVariable *coverageMaskEnabledVar =
307         new TVariable(symbolTable, ImmutableString(mtl::kCoverageMaskEnabledConstName), boolType,
308                       SymbolType::AngleInternal);
309 
310     TFunction *sampleMaskWriteFunc =
311         new TFunction(symbolTable, kSampleMaskWriteFuncName, SymbolType::AngleInternal,
312                       StaticType::GetBasic<EbtVoid>(), false);
313 
314     TType *uintType = new TType(EbtUInt);
315     TVariable *maskArg =
316         new TVariable(symbolTable, ImmutableString("mask"), uintType, SymbolType::AngleInternal);
317     sampleMaskWriteFunc->addParameter(maskArg);
318 
319     // coverageMask
320     TIntermBinary *coverageMask = driverUniforms->getCoverageMaskFieldRef();
321 
322     // Insert this code to the end of main()
323     // if (ANGLECoverageMaskEnabled)
324     // {
325     //      ANGLEWriteSampleMask(ANGLEUniforms.coverageMask);
326     // }
327     TIntermSequence args;
328     args.push_back(coverageMask);
329     TIntermAggregate *callSampleMaskWriteFunc =
330         TIntermAggregate::CreateFunctionCall(*sampleMaskWriteFunc, &args);
331     TIntermBlock *callBlock = new TIntermBlock;
332     callBlock->appendStatement(callSampleMaskWriteFunc);
333 
334     TIntermSymbol *coverageMaskEnabled = new TIntermSymbol(coverageMaskEnabledVar);
335     TIntermIfElse *ifCall              = new TIntermIfElse(coverageMaskEnabled, callBlock, nullptr);
336 
337     return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
338 }
339 
insertRasterizerDiscardLogic(TInfoSinkBase & sink,TIntermBlock * root)340 ANGLE_NO_DISCARD bool TranslatorMetal::insertRasterizerDiscardLogic(TInfoSinkBase &sink,
341                                                                     TIntermBlock *root)
342 {
343     // This transformation leaves the tree in an inconsistent state by using a variable that's
344     // defined in text, outside of the knowledge of the AST.
345     mValidateASTOptions.validateVariableReferences = false;
346 
347     TSymbolTable *symbolTable = &getSymbolTable();
348 
349     // Insert rasterizationDisabled specialization constant.
350     sink << "layout (constant_id=0) const bool " << mtl::kRasterizerDiscardEnabledConstName;
351     sink << " = false;\n";
352 
353     // Create kRasterizerDiscardEnabledConstName variable reference.
354     TType *boolType = new TType(EbtBool);
355     boolType->setQualifier(EvqConst);
356     TVariable *discardEnabledVar =
357         new TVariable(symbolTable, ImmutableString(mtl::kRasterizerDiscardEnabledConstName),
358                       boolType, SymbolType::AngleInternal);
359 
360     // Insert this code to the end of main()
361     // if (ANGLERasterizerDisabled)
362     // {
363     //      gl_Position = vec4(-3.0, -3.0, -3.0, 1.0);
364     // }
365     // Create a symbol reference to "gl_Position"
366     const TVariable *position  = BuiltInVariable::gl_Position();
367     TIntermSymbol *positionRef = new TIntermSymbol(position);
368 
369     // Create vec4(-3, -3, -3, 1):
370     auto vec4Type = new TType(EbtFloat, 4);
371     TIntermSequence vec4Args;
372     vec4Args.push_back(CreateFloatNode(-3.0f));
373     vec4Args.push_back(CreateFloatNode(-3.0f));
374     vec4Args.push_back(CreateFloatNode(-3.0f));
375     vec4Args.push_back(CreateFloatNode(1.0f));
376     TIntermAggregate *constVarConstructor =
377         TIntermAggregate::CreateConstructor(*vec4Type, &vec4Args);
378 
379     // Create the assignment "gl_Position = vec4(-3, -3, -3, 1)"
380     TIntermBinary *assignment =
381         new TIntermBinary(TOperator::EOpAssign, positionRef->deepCopy(), constVarConstructor);
382 
383     TIntermBlock *discardBlock = new TIntermBlock;
384     discardBlock->appendStatement(assignment);
385 
386     TIntermSymbol *discardEnabled = new TIntermSymbol(discardEnabledVar);
387     TIntermIfElse *ifCall         = new TIntermIfElse(discardEnabled, discardBlock, nullptr);
388 
389     return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
390 }
391 
392 }  // namespace sh
393