Expose ViewID_OVR impostor in the fragment shader

The OVR_multiview specification states that gl_ViewID_OVR is visible
at each pipeline stage. Previously to this patch the ViewID_OVR
impostor was declared only in the vertex shader and occurrences of
gl_ViewID_OVR in the fragment shader were not being handled. The
patch addresses the issue by declaring the ViewID_OVR variable as
a vertex output in the vertex shader and as a fragment input
in the fragment shader.

BUG=angleproject:2062
TEST=angle_unittests

Change-Id: I895953e81d3632d9bb873e8ac081fdf36f63f6b7
Reviewed-on: https://chromium-review.googlesource.com/559337
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 90bb7f7..6e76fe1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -429,9 +429,9 @@
             arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
 
         if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
-            parseContext.isMultiviewExtensionEnabled() && getShaderType() == GL_VERTEX_SHADER)
+            parseContext.isMultiviewExtensionEnabled() && getShaderType() != GL_COMPUTE_SHADER)
         {
-            DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews());
+            DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews(), getShaderType());
         }
 
         // This pass might emit short circuits so keep it before the short circuit unfolding
diff --git a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
index eacd289..697018a 100644
--- a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
+++ b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
@@ -20,28 +20,26 @@
 namespace
 {
 
-class RenameVariableAndMarkAsInternalTraverser : public TIntermTraverser
+class ReplaceVariableTraverser : public TIntermTraverser
 {
   public:
-    RenameVariableAndMarkAsInternalTraverser(const TString &oldName, const TString &newName)
-        : TIntermTraverser(true, false, false), mOldName(oldName), mNewName(newName)
+    ReplaceVariableTraverser(const TString &symbolName, TIntermSymbol *newSymbol)
+        : TIntermTraverser(true, false, false), mSymbolName(symbolName), mNewSymbol(newSymbol)
     {
     }
 
     void visitSymbol(TIntermSymbol *node) override
     {
         TName &name = node->getName();
-        if (name.getString() == mOldName)
+        if (name.getString() == mSymbolName)
         {
-            node->getTypePointer()->setQualifier(EvqTemporary);
-            name.setInternal(true);
-            name.setString(mNewName);
+            queueReplacement(node, mNewSymbol->deepCopy(), OriginalNode::IS_DROPPED);
         }
     }
 
   private:
-    TString mOldName;
-    TString mNewName;
+    TString mSymbolName;
+    TIntermSymbol *mNewSymbol;
 };
 
 TIntermSymbol *CreateGLInstanceIDSymbol()
@@ -49,15 +47,14 @@
     return new TIntermSymbol(0, "gl_InstanceID", TType(EbtInt, EbpHigh, EvqInstanceID));
 }
 
-// Adds initializers for InstanceID and ViewID_OVR at the beginning of main().
-void InitializeBuiltinsInMain(TIntermBlock *root,
-                              TIntermSymbol *instanceIDSymbol,
-                              TIntermSymbol *viewIDSymbol,
-                              unsigned numberOfViews)
+void InitializeViewIDAndInstanceID(TIntermBlock *root,
+                                   TIntermTyped *viewIDSymbol,
+                                   TIntermTyped *instanceIDSymbol,
+                                   unsigned numberOfViews)
 {
     // Create a signed numberOfViews node.
     TConstantUnion *numberOfViewsConstant = new TConstantUnion();
-    numberOfViewsConstant->setIConst(numberOfViews);
+    numberOfViewsConstant->setIConst(static_cast<int>(numberOfViews));
     TIntermConstantUnion *numberOfViewsIntSymbol =
         new TIntermConstantUnion(numberOfViewsConstant, TType(EbtInt, EbpHigh, EvqConst));
 
@@ -67,15 +64,15 @@
 
     // Create a InstanceID = gl_InstanceID / numberOfViews node.
     TIntermBinary *instanceIDInitializer =
-        new TIntermBinary(EOpAssign, instanceIDSymbol, normalizedInstanceID);
+        new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceID);
 
     // Create a uint(gl_InstanceID) node.
-    TIntermSequence *instanceIDSymbolCastArguments = new TIntermSequence();
-    instanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
-    TIntermAggregate *instanceIDAsUint = TIntermAggregate::CreateConstructor(
-        TType(EbtUInt, EbpHigh, EvqTemporary), instanceIDSymbolCastArguments);
+    TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
+    glInstanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
+    TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
+        TType(EbtUInt, EbpHigh, EvqTemporary), glInstanceIDSymbolCastArguments);
 
-    // Create a unsigned numberOfViews node.
+    // Create an unsigned numberOfViews node.
     TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
     numberOfViewsUnsignedConstant->setUConst(numberOfViews);
     TIntermConstantUnion *numberOfViewsUintSymbol =
@@ -83,79 +80,60 @@
 
     // Create a uint(gl_InstanceID) % numberOfViews node.
     TIntermBinary *normalizedViewID =
-        new TIntermBinary(EOpIMod, instanceIDAsUint, numberOfViewsUintSymbol);
+        new TIntermBinary(EOpIMod, glInstanceIDAsUint, numberOfViewsUintSymbol);
 
     // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
-    TIntermBinary *viewIDInitializer = new TIntermBinary(EOpAssign, viewIDSymbol, normalizedViewID);
+    TIntermBinary *viewIDInitializer =
+        new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
 
-    // Add nodes to sequence and insert into main.
-    TIntermSequence *initializers = new TIntermSequence();
-    initializers->push_back(viewIDInitializer);
-    initializers->push_back(instanceIDInitializer);
-
-    // Insert init code as a block at the beginning of the main() function.
-    TIntermBlock *initGlobalsBlock = new TIntermBlock();
-    initGlobalsBlock->getSequence()->swap(*initializers);
-
-    TIntermFunctionDefinition *main = FindMain(root);
-    ASSERT(main != nullptr);
-    TIntermBlock *mainBody = main->getBody();
-    ASSERT(mainBody != nullptr);
-    mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
+    // Add initializers at the beginning of main().
+    TIntermBlock *mainBody = FindMainBody(root);
+    mainBody->getSequence()->insert(mainBody->getSequence()->begin(), instanceIDInitializer);
+    mainBody->getSequence()->insert(mainBody->getSequence()->begin(), viewIDInitializer);
 }
 
-// Adds declarations for ViewID_OVR and InstanceID in global scope at the beginning of
-// the shader.
-void DeclareBuiltinsInGlobalScope(TIntermBlock *root,
-                                  TIntermSymbol *instanceIDSymbol,
-                                  TIntermSymbol *viewIDSymbol)
+// Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
+void ReplaceSymbol(TIntermBlock *root, const TString &symbolName, TIntermSymbol *newSymbolNode)
+{
+    ReplaceVariableTraverser traverser(symbolName, newSymbolNode);
+    root->traverse(&traverser);
+    traverser.updateTree();
+}
+
+void DeclareGlobalVariable(TIntermBlock *root, TIntermTyped *typedNode)
 {
     TIntermSequence *globalSequence = root->getSequence();
-
-    TIntermDeclaration *instanceIDDeclaration = new TIntermDeclaration();
-    instanceIDDeclaration->appendDeclarator(instanceIDSymbol);
-
-    TIntermDeclaration *viewIDOVRDeclaration = new TIntermDeclaration();
-    viewIDOVRDeclaration->appendDeclarator(viewIDSymbol);
-
-    globalSequence->insert(globalSequence->begin(), viewIDOVRDeclaration);
-    globalSequence->insert(globalSequence->begin(), instanceIDDeclaration);
-}
-
-// Replaces every occurrence of gl_InstanceID with InstanceID, sets the name to internal
-// and changes the qualifier from EvqInstanceID to EvqTemporary.
-void RenameGLInstanceIDAndMarkAsInternal(TIntermBlock *root)
-{
-    RenameVariableAndMarkAsInternalTraverser traverser("gl_InstanceID", "InstanceID");
-    root->traverse(&traverser);
-}
-
-// Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR, sets the name to internal
-// and changes the qualifier from EvqViewIDOVR to EvqTemporary.
-void RenameGLViewIDAndMarkAsInternal(TIntermBlock *root)
-{
-    RenameVariableAndMarkAsInternalTraverser traverser("gl_ViewID_OVR", "ViewID_OVR");
-    root->traverse(&traverser);
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->appendDeclarator(typedNode->deepCopy());
+    globalSequence->insert(globalSequence->begin(), declaration);
 }
 
 }  // namespace
 
-void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews)
+void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
+                                                 unsigned numberOfViews,
+                                                 GLenum shaderType)
 {
-    TIntermSymbol *instanceIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "InstanceID",
-                                                        TType(EbtInt, EbpHigh, EvqGlobal));
-    instanceIDSymbol->setInternal(true);
+    ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
 
+    TQualifier viewIDQualifier  = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
     TIntermSymbol *viewIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "ViewID_OVR",
-                                                    TType(EbtUInt, EbpHigh, EvqGlobal));
+                                                    TType(EbtUInt, EbpHigh, viewIDQualifier));
     viewIDSymbol->setInternal(true);
 
-    // Renaming the variables should happen before adding the initializers.
-    RenameGLInstanceIDAndMarkAsInternal(root);
-    DeclareBuiltinsInGlobalScope(root, instanceIDSymbol, viewIDSymbol);
-    InitializeBuiltinsInMain(root, instanceIDSymbol->deepCopy()->getAsSymbolNode(),
-                             viewIDSymbol->deepCopy()->getAsSymbolNode(), numberOfViews);
-    RenameGLViewIDAndMarkAsInternal(root);
+    DeclareGlobalVariable(root, viewIDSymbol);
+    ReplaceSymbol(root, "gl_ViewID_OVR", viewIDSymbol);
+    if (shaderType == GL_VERTEX_SHADER)
+    {
+        // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
+        // InstanceID and ViewID.
+        TIntermSymbol *instanceIDSymbol = new TIntermSymbol(
+            TSymbolTable::nextUniqueId(), "InstanceID", TType(EbtInt, EbpHigh, EvqGlobal));
+        instanceIDSymbol->setInternal(true);
+        DeclareGlobalVariable(root, instanceIDSymbol);
+        ReplaceSymbol(root, "gl_InstanceID", instanceIDSymbol);
+        InitializeViewIDAndInstanceID(root, viewIDSymbol, instanceIDSymbol, numberOfViews);
+    }
 }
 
 }  // namespace sh
\ No newline at end of file
diff --git a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
index 25937ec..b72dc0a 100644
--- a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
+++ b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
@@ -3,24 +3,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// This pass applies the following modifications to the AST:
-// 1) Replaces every occurrence of gl_InstanceID with InstanceID and marks InstanceID as internal.
-// 2) Adds declarations of the global variables ViewID_OVR and InstanceID.
-// 3) Initializes ViewID_OVR and InstanceID depending on the number of views.
-// 4) Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR and marks ViewID_OVR as internal.
-// The pass should be executed before any variables get collected so that usage of gl_InstanceID is
-// recorded.
+// Regardless of the shader type, the following AST transformations are applied:
+// - Add declaration of View_ID_OVR.
+// - Replace every occurrence of gl_ViewID_OVR with ViewID_OVR, mark ViewID_OVR as internal and
+// declare it as a flat varying.
+// If the shader type is a vertex shader, the following AST transformations are applied:
+// - Replace every occurrence of gl_InstanceID with InstanceID, mark InstanceID as internal and set
+// its qualifier to EvqTemporary.
+// - Add initializers of ViewID_OVR and InstanceID to the beginning of the body of main. The pass
+// should be executed before any variables get collected so that usage of gl_InstanceID is recorded.
 //
 
 #ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
 #define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
 
+#include "angle_gl.h"
+
 class TIntermBlock;
 
 namespace sh
 {
 
-void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews);
+void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
+                                                 unsigned numberOfViews,
+                                                 GLenum shaderType);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/FindMain.cpp b/src/compiler/translator/FindMain.cpp
index f73666b..7417fba 100644
--- a/src/compiler/translator/FindMain.cpp
+++ b/src/compiler/translator/FindMain.cpp
@@ -26,4 +26,13 @@
     return nullptr;
 }
 
+TIntermBlock *FindMainBody(TIntermBlock *root)
+{
+    TIntermFunctionDefinition *main = FindMain(root);
+    ASSERT(main != nullptr);
+    TIntermBlock *mainBody = main->getBody();
+    ASSERT(mainBody != nullptr);
+    return mainBody;
+}
+
 }  // namespace sh
diff --git a/src/compiler/translator/FindMain.h b/src/compiler/translator/FindMain.h
index fb06583..bf2c45d 100644
--- a/src/compiler/translator/FindMain.h
+++ b/src/compiler/translator/FindMain.h
@@ -4,7 +4,7 @@
 // found in the LICENSE file.
 //
 
-// FindMain.h: Find the main() function definition in a given AST.
+// FindMain.h: Adds functions to get the main function definition and its body.
 
 #ifndef COMPILER_TRANSLATOR_FINDMAIN_H_
 #define COMPILER_TRANSLATOR_FINDMAIN_H_
@@ -16,6 +16,7 @@
 class TIntermFunctionDefinition;
 
 TIntermFunctionDefinition *FindMain(TIntermBlock *root);
+TIntermBlock *FindMainBody(TIntermBlock *root);
 
 }  // namespace sh