Vulkan: Implement new GLSL translator back-end.

The Vulkan GLSL translator back-end will handle validating and
translating our WebGL/ESSL shaders into Vulkan-specific GLSL.

glslang (the Vulkan one) accepts both GLSL and GLSL ES shaders
as inputs, and both the desktop and ESSL back-ends give
incompleteness warnings when used. For now, use the desktop GL
450 as a target for Vulkan GLSL.

The Vulkan-specific changes are currently only to add locations
to every vertex input and fragment output.

BUG=angleproject:1575

Change-Id: I7c3f32f522e9d18e5f8618eb7927336bf4fbdcf2
Reviewed-on: https://chromium-review.googlesource.com/412266
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/CodeGen.cpp b/src/compiler/translator/CodeGen.cpp
index f9f50c4..75f0d36 100644
--- a/src/compiler/translator/CodeGen.cpp
+++ b/src/compiler/translator/CodeGen.cpp
@@ -16,6 +16,10 @@
 #include "compiler/translator/TranslatorHLSL.h"
 #endif  // ANGLE_ENABLE_HLSL
 
+#ifdef ANGLE_ENABLE_VULKAN
+#include "compiler/translator/TranslatorVulkan.h"
+#endif  // ANGLE_ENABLE_VULKAN
+
 namespace sh
 {
 
@@ -68,9 +72,13 @@
 #endif  // ANGLE_ENABLE_HLSL
 
         case SH_GLSL_VULKAN_OUTPUT:
-            UNIMPLEMENTED();
-            // TODO(jmadill): Vulkan GLSL
+#ifdef ANGLE_ENABLE_VULKAN
+            return new TranslatorVulkan(type, spec);
+#else
+            // This compiler is not supported in this configuration. Return NULL per the
+            // ShConstructCompiler API.
             return nullptr;
+#endif  // ANGLE_ENABLE_VULKAN
 
         default:
             // Unknown format. Return NULL per the sh::ConstructCompiler API.
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index 5f7d7d9..4c26c73 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -39,7 +39,7 @@
     TInfoSinkBase &objSink() { return mObjSink; }
     void writeFloat(TInfoSinkBase &out, float f);
     void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr);
-    void writeLayoutQualifier(const TType &type);
+    virtual void writeLayoutQualifier(const TType &type);
     void writeInvariantQualifier(const TType &type);
     void writeVariableType(const TType &type);
     virtual bool writeVariablePrecision(TPrecision precision) = 0;
diff --git a/src/compiler/translator/OutputVulkanGLSL.cpp b/src/compiler/translator/OutputVulkanGLSL.cpp
new file mode 100644
index 0000000..c2c7813
--- /dev/null
+++ b/src/compiler/translator/OutputVulkanGLSL.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright (c) 2016 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.
+//
+// OutputVulkanGLSL:
+//   Code that outputs shaders that fit GL_KHR_vulkan_glsl.
+//   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
+//
+
+#include "compiler/translator/OutputVulkanGLSL.h"
+
+namespace sh
+{
+
+TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
+                                     ShArrayIndexClampingStrategy clampingStrategy,
+                                     ShHashFunction64 hashFunction,
+                                     NameMap &nameMap,
+                                     TSymbolTable &symbolTable,
+                                     sh::GLenum shaderType,
+                                     int shaderVersion,
+                                     ShShaderOutput output,
+                                     ShCompileOptions compileOptions)
+    : TOutputGLSLBase(objSink,
+                      clampingStrategy,
+                      hashFunction,
+                      nameMap,
+                      symbolTable,
+                      shaderType,
+                      shaderVersion,
+                      output,
+                      compileOptions)
+{
+}
+
+// TODO(jmadill): This is not complete.
+void TOutputVulkanGLSL::writeLayoutQualifier(const TType &type)
+{
+    TInfoSinkBase &out                      = objSink();
+    const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+    out << "layout(";
+
+    if (type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut ||
+        type.getQualifier() == EvqVertexIn)
+    {
+        // TODO(jmadill): Multiple output locations.
+        out << "location = "
+            << "0";
+    }
+
+    if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+    {
+        ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
+        out << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
+    }
+
+    out << ") ";
+}
+
+bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision)
+{
+    if (precision == EbpUndefined)
+        return false;
+
+    TInfoSinkBase &out = objSink();
+    out << getPrecisionString(precision);
+    return true;
+}
+
+void TOutputVulkanGLSL::visitSymbol(TIntermSymbol *node)
+{
+    TInfoSinkBase &out = objSink();
+
+    const TString &symbol = node->getSymbol();
+    if (symbol == "gl_FragColor")
+    {
+        out << "webgl_FragColor";
+    }
+    else if (symbol == "gl_FragData")
+    {
+        out << "webgl_FragData";
+    }
+    else
+    {
+        TOutputGLSLBase::visitSymbol(node);
+    }
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/OutputVulkanGLSL.h b/src/compiler/translator/OutputVulkanGLSL.h
new file mode 100644
index 0000000..75f2f35
--- /dev/null
+++ b/src/compiler/translator/OutputVulkanGLSL.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2016 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.
+//
+// OutputVulkanGLSL:
+//   Code that outputs shaders that fit GL_KHR_vulkan_glsl.
+//   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
+//
+
+#include "compiler/translator/OutputGLSL.h"
+
+namespace sh
+{
+
+class TOutputVulkanGLSL : public TOutputGLSLBase
+{
+  public:
+    TOutputVulkanGLSL(TInfoSinkBase &objSink,
+                      ShArrayIndexClampingStrategy clampingStrategy,
+                      ShHashFunction64 hashFunction,
+                      NameMap &nameMap,
+                      TSymbolTable &symbolTable,
+                      sh::GLenum shaderType,
+                      int shaderVersion,
+                      ShShaderOutput output,
+                      ShCompileOptions compileOptions);
+
+  protected:
+    void writeLayoutQualifier(const TType &type) override;
+    bool writeVariablePrecision(TPrecision precision) override;
+    void visitSymbol(TIntermSymbol *node) override;
+};
+
+}  // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
new file mode 100644
index 0000000..5bc7eb0
--- /dev/null
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright (c) 2016 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.
+//
+// TranslatorVulkan:
+//   A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl.
+//   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
+//
+
+#include "compiler/translator/TranslatorVulkan.h"
+
+#include "angle_gl.h"
+#include "compiler/translator/OutputVulkanGLSL.h"
+
+namespace sh
+{
+
+TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec)
+    : TCompiler(type, spec, SH_GLSL_450_CORE_OUTPUT)
+{
+}
+
+void TranslatorVulkan::translate(TIntermNode *root, ShCompileOptions compileOptions)
+{
+    TInfoSinkBase &sink = getInfoSink().obj;
+
+    sink << "#version 450 core\n";
+
+    // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
+    // if it's core profile shaders and they are used.
+    if (getShaderType() == GL_FRAGMENT_SHADER)
+    {
+        bool hasGLFragColor = false;
+        bool hasGLFragData  = false;
+
+        for (const auto &outputVar : outputVariables)
+        {
+            if (outputVar.name == "gl_FragColor")
+            {
+                ASSERT(!hasGLFragColor);
+                hasGLFragColor = true;
+                continue;
+            }
+            else if (outputVar.name == "gl_FragData")
+            {
+                ASSERT(!hasGLFragData);
+                hasGLFragData = true;
+                continue;
+            }
+        }
+        ASSERT(!(hasGLFragColor && hasGLFragData));
+        if (hasGLFragColor)
+        {
+            sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
+        }
+        if (hasGLFragData)
+        {
+            sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
+        }
+    }
+
+    // Write translated shader.
+    TOutputVulkanGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(),
+                                 getNameMap(), getSymbolTable(), getShaderType(),
+                                 getShaderVersion(), getOutputType(), compileOptions);
+    root->traverse(&outputGLSL);
+}
+
+bool TranslatorVulkan::shouldFlattenPragmaStdglInvariantAll()
+{
+    // Not necessary.
+    return false;
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.h b/src/compiler/translator/TranslatorVulkan.h
new file mode 100644
index 0000000..5bca394
--- /dev/null
+++ b/src/compiler/translator/TranslatorVulkan.h
@@ -0,0 +1,32 @@
+//
+// Copyright (c) 2016 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.
+//
+// TranslatorVulkan:
+//   A GLSL-based translator that outputs shaders that fit GL_KHR_vulkan_glsl.
+//   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
+//
+
+#ifndef COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_
+#define COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_
+
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+class TranslatorVulkan : public TCompiler
+{
+  public:
+    TranslatorVulkan(sh::GLenum type, ShShaderSpec spec);
+
+  protected:
+    void translate(TIntermNode *root, ShCompileOptions compileOptions) override;
+    bool shouldFlattenPragmaStdglInvariantAll() override;
+};
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_TRANSLATORVULKAN_H_