// // 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, TIntermSwizzle *negFlipY) { // 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 swizzleOffsetY; swizzleOffsetY.push_back(1); TIntermSwizzle *positionY = new TIntermSwizzle(positionRef, swizzleOffsetY); // Create the expression "gl_Position.y * negFlipY" TIntermBinary *inverseY = new TIntermBinary(EOpMul, positionY->deepCopy(), negFlipY); // 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(), false, true, compileOptions); const TVariable *driverUniforms = nullptr; if (!TranslatorVulkan::translateImpl(root, compileOptions, perfDiagnostics, &driverUniforms, &outputGLSL)) { return false; } if (getShaderType() == GL_VERTEX_SHADER) { auto negFlipY = getDriverUniformNegFlipYRef(driverUniforms); // Append gl_Position.y correction to main if (!AppendVertexShaderPositionYCorrectionToMain(this, root, &getSymbolTable(), negFlipY)) { 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 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