1 //
2 // Copyright (c) 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 (libANGLE-side).
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_util/BuiltIn.h"
22 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
23 #include "compiler/translator/util.h"
24
25 namespace sh
26 {
27
28 namespace
29 {
30
31 // Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
32 // manually.
33 // This operation performs flipping the gl_Position.y using this expression:
34 // gl_Position.y = gl_Position.y * negViewportScaleY
AppendVertexShaderPositionYCorrectionToMain(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,TIntermBinary * negViewportYScale)35 ANGLE_NO_DISCARD bool AppendVertexShaderPositionYCorrectionToMain(TCompiler *compiler,
36 TIntermBlock *root,
37 TSymbolTable *symbolTable,
38 TIntermBinary *negViewportYScale)
39 {
40 // Create a symbol reference to "gl_Position"
41 const TVariable *position = BuiltInVariable::gl_Position();
42 TIntermSymbol *positionRef = new TIntermSymbol(position);
43
44 // Create a swizzle to "gl_Position.y"
45 TVector<int> swizzleOffsetY;
46 swizzleOffsetY.push_back(1);
47 TIntermSwizzle *positionY = new TIntermSwizzle(positionRef, swizzleOffsetY);
48
49 // Create the expression "gl_Position.y * negViewportScaleY"
50 TIntermBinary *inverseY = new TIntermBinary(EOpMul, positionY->deepCopy(), negViewportYScale);
51
52 // Create the assignment "gl_Position.y = gl_Position.y * negViewportScaleY
53 TIntermTyped *positionYLHS = positionY->deepCopy();
54 TIntermBinary *assignment = new TIntermBinary(TOperator::EOpAssign, positionYLHS, inverseY);
55
56 // Append the assignment as a statement at the end of the shader.
57 return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
58 }
59
60 } // anonymous namespace
61
TranslatorMetal(sh::GLenum type,ShShaderSpec spec)62 TranslatorMetal::TranslatorMetal(sh::GLenum type, ShShaderSpec spec) : TranslatorVulkan(type, spec)
63 {}
64
translate(TIntermBlock * root,ShCompileOptions compileOptions,PerformanceDiagnostics * perfDiagnostics)65 bool TranslatorMetal::translate(TIntermBlock *root,
66 ShCompileOptions compileOptions,
67 PerformanceDiagnostics *perfDiagnostics)
68 {
69 TInfoSinkBase &sink = getInfoSink().obj;
70
71 TOutputVulkanGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(),
72 getNameMap(), &getSymbolTable(), getShaderType(),
73 getShaderVersion(), getOutputType(), false, true, compileOptions);
74
75 const TVariable *driverUniforms = nullptr;
76 if (!TranslatorVulkan::translateImpl(root, compileOptions, perfDiagnostics, &driverUniforms,
77 &outputGLSL))
78 {
79 return false;
80 }
81
82 if (getShaderType() == GL_VERTEX_SHADER)
83 {
84 auto negViewportYScale = getDriverUniformNegViewportYScaleRef(driverUniforms);
85
86 // Append gl_Position.y correction to main
87 if (!AppendVertexShaderPositionYCorrectionToMain(this, root, &getSymbolTable(),
88 negViewportYScale))
89 {
90 return false;
91 }
92 }
93
94 // Write translated shader.
95 root->traverse(&outputGLSL);
96
97 return true;
98 }
99
100 // Metal needs to inverse the depth if depthRange is is reverse order, i.e. depth near > depth far
101 // This is achieved by multiply the depth value with scale value stored in
102 // driver uniform's depthRange.reserved
transformDepthBeforeCorrection(TIntermBlock * root,const TVariable * driverUniforms)103 bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
104 const TVariable *driverUniforms)
105 {
106 // Create a symbol reference to "gl_Position"
107 const TVariable *position = BuiltInVariable::gl_Position();
108 TIntermSymbol *positionRef = new TIntermSymbol(position);
109
110 // Create a swizzle to "gl_Position.z"
111 TVector<int> swizzleOffsetZ = {2};
112 TIntermSwizzle *positionZ = new TIntermSwizzle(positionRef, swizzleOffsetZ);
113
114 // Create a ref to "depthRange.reserved"
115 TIntermBinary *viewportZScale = getDriverUniformDepthRangeReservedFieldRef(driverUniforms);
116
117 // Create the expression "gl_Position.z * depthRange.reserved".
118 TIntermBinary *zScale = new TIntermBinary(EOpMul, positionZ->deepCopy(), viewportZScale);
119
120 // Create the assignment "gl_Position.z = gl_Position.z * depthRange.reserved"
121 TIntermTyped *positionZLHS = positionZ->deepCopy();
122 TIntermBinary *assignment = new TIntermBinary(TOperator::EOpAssign, positionZLHS, zScale);
123
124 // Append the assignment as a statement at the end of the shader.
125 return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable());
126 }
127
128 } // namespace sh
129