| // | |
| // Copyright (c) 2019 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // TranslatorMetal: | |
| // A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl. | |
| // It takes into account some considerations for Metal backend also. | |
| // The shaders are then fed into glslang to spit out SPIR-V (libANGLE-side). | |
| // See: https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt | |
| // | |
| // The SPIR-V will then be translated to Metal Shading Language later in Metal backend. | |
| // | |
| #include "compiler/translator/TranslatorMetal.h" | |
| #include "angle_gl.h" | |
| #include "common/utilities.h" | |
| #include "compiler/translator/OutputVulkanGLSLForMetal.h" | |
| #include "compiler/translator/StaticType.h" | |
| #include "compiler/translator/tree_util/BuiltIn.h" | |
| #include "compiler/translator/tree_util/RunAtTheEndOfShader.h" | |
| #include "compiler/translator/util.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| // Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y | |
| // manually. | |
| // This operation performs flipping the gl_Position.y using this expression: | |
| // gl_Position.y = gl_Position.y * negViewportScaleY | |
| ANGLE_NO_DISCARD bool AppendVertexShaderPositionYCorrectionToMain(TCompiler *compiler, | |
| TIntermBlock *root, | |
| TSymbolTable *symbolTable, | |
| TIntermBinary *negViewportYScale) | |
| { | |
| // Create a symbol reference to "gl_Position" | |
| const TVariable *position = BuiltInVariable::gl_Position(); | |
| TIntermSymbol *positionRef = new TIntermSymbol(position); | |
| // Create a swizzle to "gl_Position.y" | |
| TVector<int> swizzleOffsetY; | |
| swizzleOffsetY.push_back(1); | |
| TIntermSwizzle *positionY = new TIntermSwizzle(positionRef, swizzleOffsetY); | |
| // Create the expression "gl_Position.y * negViewportScaleY" | |
| TIntermBinary *inverseY = new TIntermBinary(EOpMul, positionY->deepCopy(), negViewportYScale); | |
| // Create the assignment "gl_Position.y = gl_Position.y * negViewportScaleY | |
| TIntermTyped *positionYLHS = positionY->deepCopy(); | |
| TIntermBinary *assignment = new TIntermBinary(TOperator::EOpAssign, positionYLHS, inverseY); | |
| // Append the assignment as a statement at the end of the shader. | |
| return RunAtTheEndOfShader(compiler, root, assignment, symbolTable); | |
| } | |
| } // anonymous namespace | |
| TranslatorMetal::TranslatorMetal(sh::GLenum type, ShShaderSpec spec) : TranslatorVulkan(type, spec) | |
| {} | |
| bool TranslatorMetal::translate(TIntermBlock *root, | |
| ShCompileOptions compileOptions, | |
| PerformanceDiagnostics *perfDiagnostics) | |
| { | |
| TInfoSinkBase &sink = getInfoSink().obj; | |
| TOutputVulkanGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), | |
| getNameMap(), &getSymbolTable(), getShaderType(), | |
| getShaderVersion(), getOutputType(), compileOptions); | |
| const TVariable *driverUniforms = nullptr; | |
| if (!TranslatorVulkan::translateImpl(root, compileOptions, perfDiagnostics, &driverUniforms, | |
| &outputGLSL)) | |
| { | |
| return false; | |
| } | |
| if (getShaderType() == GL_VERTEX_SHADER) | |
| { | |
| auto negViewportYScale = getDriverUniformNegViewportYScaleRef(driverUniforms); | |
| // Append gl_Position.y correction to main | |
| if (!AppendVertexShaderPositionYCorrectionToMain(this, root, &getSymbolTable(), | |
| negViewportYScale)) | |
| { | |
| return false; | |
| } | |
| } | |
| // Write translated shader. | |
| root->traverse(&outputGLSL); | |
| return true; | |
| } | |
| // Metal needs to inverse the depth if depthRange is is reverse order, i.e. depth near > depth far | |
| // This is achieved by multiply the depth value with scale value stored in | |
| // driver uniform's depthRange.reserved | |
| bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root, | |
| const TVariable *driverUniforms) | |
| { | |
| // Create a symbol reference to "gl_Position" | |
| const TVariable *position = BuiltInVariable::gl_Position(); | |
| TIntermSymbol *positionRef = new TIntermSymbol(position); | |
| // Create a swizzle to "gl_Position.z" | |
| TVector<int> swizzleOffsetZ = {2}; | |
| TIntermSwizzle *positionZ = new TIntermSwizzle(positionRef, swizzleOffsetZ); | |
| // Create a ref to "depthRange.reserved" | |
| TIntermBinary *viewportZScale = getDriverUniformDepthRangeReservedFieldRef(driverUniforms); | |
| // Create the expression "gl_Position.z * depthRange.reserved". | |
| TIntermBinary *zScale = new TIntermBinary(EOpMul, positionZ->deepCopy(), viewportZScale); | |
| // Create the assignment "gl_Position.z = gl_Position.z * depthRange.reserved" | |
| TIntermTyped *positionZLHS = positionZ->deepCopy(); | |
| TIntermBinary *assignment = new TIntermBinary(TOperator::EOpAssign, positionZLHS, zScale); | |
| // Append the assignment as a statement at the end of the shader. | |
| return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable()); | |
| } | |
| } // namespace sh |