Vulkan: Add driver uniforms to shader.

Bug: angleproject:2717
Change-Id: I542f3b0f2de21857d7fea0267f07d2d0eec78a8c
Reviewed-on: https://chromium-review.googlesource.com/1131567
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Luc Ferron <lucferron@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 24663d0..d72b867 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -595,8 +595,6 @@
                     node->getLeft()->getType().getInterfaceBlock();
                 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
                 const TField *field               = interfaceBlock->fields()[index->getIConst(0)];
-                ASSERT(interfaceBlock->symbolType() == SymbolType::UserDefined ||
-                       interfaceBlock->name() == "gl_PerVertex");
                 out << hashFieldName(field);
                 visitChildren = false;
             }
diff --git a/src/compiler/translator/OutputVulkanGLSL.cpp b/src/compiler/translator/OutputVulkanGLSL.cpp
index 99b0e40..798e64c 100644
--- a/src/compiler/translator/OutputVulkanGLSL.cpp
+++ b/src/compiler/translator/OutputVulkanGLSL.cpp
@@ -46,7 +46,7 @@
     bool needsCustomLayout =
         (type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut ||
          type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier()) ||
-         IsSampler(type.getBasicType()));
+         IsSampler(type.getBasicType()) || type.isInterfaceBlock());
 
     if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout)
     {
@@ -58,8 +58,6 @@
 
     // This isn't super clean, but it gets the job done.
     // See corresponding code in GlslangWrapper.cpp.
-    // TODO(jmadill): Ensure declarations are separated.
-
     TIntermSymbol *symbol = variable->getAsSymbolNode();
     ASSERT(symbol);
 
@@ -99,9 +97,7 @@
     }
 
     TInfoSinkBase &out = objSink();
-    out << "@@ QUALIFIER-";
-    out << symbol->name().data();
-    out << " @@ ";
+    out << "@@ QUALIFIER-" << symbol->name().data() << " @@ ";
 }
 
 void TOutputVulkanGLSL::writeStructType(const TStructure *structure)
@@ -112,5 +108,4 @@
         objSink() << ";\n";
     }
 }
-
 }  // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index 8155105..9c82f99 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -256,6 +256,44 @@
     // Append the assignment as a statement at the end of the shader.
     RunAtTheEndOfShader(root, assignment, symbolTable);
 }
+
+// The AddDriverUniformsToShader operation adds an internal uniform block to a shader. The driver
+// block is used to implement Vulkan-specific features and workarounds. Returns the driver uniforms
+// variable.
+const TVariable *AddDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+    // This field list mirrors the structure of ContextVk::DriverUniforms.
+    TFieldList *driverFieldList = new TFieldList;
+
+    // Add a vec4 field "viewport" to the driver uniform fields.
+    TType *driverViewportType  = new TType(EbtFloat, 4);
+    TField *driverViewportSize = new TField(driverViewportType, ImmutableString("viewport"),
+                                            TSourceLoc(), SymbolType::AngleInternal);
+    driverFieldList->push_back(driverViewportSize);
+
+    // Define a driver uniform block "ANGLEUniformBlock".
+    TLayoutQualifier driverLayoutQualifier = TLayoutQualifier::Create();
+    TInterfaceBlock *interfaceBlock =
+        new TInterfaceBlock(symbolTable, ImmutableString("ANGLEUniformBlock"), driverFieldList,
+                            driverLayoutQualifier, SymbolType::AngleInternal);
+
+    // Make the inteface block into a declaration. Use instance name "ANGLEUniforms".
+    TType *interfaceBlockType = new TType(interfaceBlock, EvqUniform, driverLayoutQualifier);
+    TIntermDeclaration *driverUniformsDecl = new TIntermDeclaration;
+    TVariable *driverUniformsVar = new TVariable(symbolTable, ImmutableString("ANGLEUniforms"),
+                                                 interfaceBlockType, SymbolType::AngleInternal);
+    TIntermSymbol *driverUniformsDeclarator = new TIntermSymbol(driverUniformsVar);
+    driverUniformsDecl->appendDeclarator(driverUniformsDeclarator);
+
+    // Insert the declarations before Main.
+    TIntermSequence *insertSequence = new TIntermSequence;
+    insertSequence->push_back(driverUniformsDecl);
+
+    size_t mainIndex = FindMainIndex(root);
+    root->insertChildNodes(mainIndex, *insertSequence);
+
+    return driverUniformsVar;
+}
 }  // anonymous namespace
 
 TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec)
@@ -315,6 +353,8 @@
         sink << "};\n";
     }
 
+    AddDriverUniformsToShader(root, &getSymbolTable());
+
     // 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)
diff --git a/src/compiler/translator/tree_util/FindMain.cpp b/src/compiler/translator/tree_util/FindMain.cpp
index 9823a01..0e3023b 100644
--- a/src/compiler/translator/tree_util/FindMain.cpp
+++ b/src/compiler/translator/tree_util/FindMain.cpp
@@ -14,6 +14,21 @@
 namespace sh
 {
 
+size_t FindMainIndex(TIntermBlock *root)
+{
+    const TIntermSequence &sequence = *root->getSequence();
+    for (size_t index = 0; index < sequence.size(); ++index)
+    {
+        TIntermNode *node                       = sequence[index];
+        TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
+        if (nodeFunction != nullptr && nodeFunction->getFunction()->isMain())
+        {
+            return index;
+        }
+    }
+    return std::numeric_limits<size_t>::max();
+}
+
 TIntermFunctionDefinition *FindMain(TIntermBlock *root)
 {
     for (TIntermNode *node : *root->getSequence())
diff --git a/src/compiler/translator/tree_util/FindMain.h b/src/compiler/translator/tree_util/FindMain.h
index f619e6e..a313315 100644
--- a/src/compiler/translator/tree_util/FindMain.h
+++ b/src/compiler/translator/tree_util/FindMain.h
@@ -9,15 +9,16 @@
 #ifndef COMPILER_TRANSLATOR_TREEUTIL_FINDMAIN_H_
 #define COMPILER_TRANSLATOR_TREEUTIL_FINDMAIN_H_
 
+#include <cstddef>
+
 namespace sh
 {
-
 class TIntermBlock;
 class TIntermFunctionDefinition;
 
+size_t FindMainIndex(TIntermBlock *root);
 TIntermFunctionDefinition *FindMain(TIntermBlock *root);
 TIntermBlock *FindMainBody(TIntermBlock *root);
-
 }  // namespace sh
 
 #endif  // COMPILER_TRANSLATOR_TREEUTIL_FINDMAIN_H_
\ No newline at end of file
diff --git a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
index fdb3d8c..a6d7d13 100644
--- a/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
+++ b/src/libANGLE/renderer/vulkan/GlslangWrapper.cpp
@@ -299,6 +299,15 @@
         }
     }
 
+    // Substitute layout and qualifier strings for the driver uniforms block.
+    constexpr char kDriverBlockLayoutString[] = "set = 2, binding = 0";
+    constexpr char kDriverBlockName[]         = "ANGLEUniforms";
+    InsertLayoutSpecifierString(&vertexSource, kDriverBlockName, kDriverBlockLayoutString);
+    InsertLayoutSpecifierString(&fragmentSource, kDriverBlockName, kDriverBlockLayoutString);
+
+    InsertQualifierSpecifierString(&vertexSource, kDriverBlockName, kUniformQualifier);
+    InsertQualifierSpecifierString(&fragmentSource, kDriverBlockName, kUniformQualifier);
+
     std::array<const char *, 2> strings = {{vertexSource.c_str(), fragmentSource.c_str()}};
     std::array<int, 2> lengths          = {
         {static_cast<int>(vertexSource.length()), static_cast<int>(fragmentSource.length())}};