//
// 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.
//
// TOutputVulkanGLSLForMetal:
//    This is a special version Vulkan GLSL output that will make some special
//    considerations for Metal backend limitations.
//

#include "compiler/translator/OutputVulkanGLSLForMetal.h"

#include "common/apple_platform_utils.h"
#include "compiler/translator/BaseTypes.h"
#include "compiler/translator/Symbol.h"
#include "compiler/translator/util.h"

namespace sh
{

namespace
{
bool gOverrideRemoveInvariant = false;

bool ShoudRemoveInvariant(const TType &type)
{
    if (gOverrideRemoveInvariant)
    {
        return true;
    }

    if (type.getQualifier() != EvqPosition)
    {
        // Metal only supports invariant for gl_Position
        return true;
    }

    if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12))
    {
        return false;
    }
    else
    {
        // Metal 2.1 is not available, so we need to remove "invariant" keyword
        return true;
    }
}

}

// static
void TOutputVulkanGLSLForMetal::RemoveInvariantForTest(bool remove)
{
    gOverrideRemoveInvariant = remove;
}

TOutputVulkanGLSLForMetal::TOutputVulkanGLSLForMetal(TInfoSinkBase &objSink,
                                                     ShArrayIndexClampingStrategy clampingStrategy,
                                                     ShHashFunction64 hashFunction,
                                                     NameMap &nameMap,
                                                     TSymbolTable *symbolTable,
                                                     sh::GLenum shaderType,
                                                     int shaderVersion,
                                                     ShShaderOutput output,
                                                     ShCompileOptions compileOptions)
    : TOutputVulkanGLSL(objSink,
                        clampingStrategy,
                        hashFunction,
                        nameMap,
                        symbolTable,
                        shaderType,
                        shaderVersion,
                        output,
                        false,
                        true,
                        compileOptions)
{}

void TOutputVulkanGLSLForMetal::writeVariableType(const TType &type,
                                                  const TSymbol *symbol,
                                                  bool isFunctionArgument)
{
    TType overrideType(type);

    // Remove invariant keyword if required.
    if (type.isInvariant() && ShoudRemoveInvariant(type))
    {
        overrideType.setInvariant(false);
    }

    TOutputVulkanGLSL::writeVariableType(overrideType, symbol, isFunctionArgument);
}

bool TOutputVulkanGLSLForMetal::visitGlobalQualifierDeclaration(
    Visit visit,
    TIntermGlobalQualifierDeclaration *node)
{
    TInfoSinkBase &out = objSink();
    ASSERT(visit == PreVisit);
    const TIntermSymbol *symbol = node->getSymbol();

    // No support for the |precise| keyword from EXT_gpu_shader5 (or ES3.2).
    ASSERT(node->isInvariant());

    if (!ShoudRemoveInvariant(symbol->getType()))
    {
        out << "invariant ";
    }
    out << hashName(&symbol->variable());
    return false;
}
}  // namespace sh
