Select viewport index in the GLSL/ESSL vertex shader

The patch enables viewport selection for multiview rendering in the
GLSL/ESSL vertex shader through the use of the GL_NV_viewport_array2
extension. The AST is modified only for GLSL and ESSL to include the
viewport selection expression after ViewID_OVR's initialization.

BUG=angleproject:2062
TEST=angle_unittests

Change-Id: Iee05bb5a4b687ed53ddbdd466f1572227b1f0cde
diff --git a/src/compiler/fuzz/translator_fuzzer.cpp b/src/compiler/fuzz/translator_fuzzer.cpp
index dcc0562..910af8b 100644
--- a/src/compiler/fuzz/translator_fuzzer.cpp
+++ b/src/compiler/fuzz/translator_fuzzer.cpp
@@ -14,6 +14,7 @@
 
 #include "angle_gl.h"
 #include "compiler/translator/Compiler.h"
+#include "compiler/translator/util.h"
 
 using namespace sh;
 
@@ -84,6 +85,14 @@
         return 0;
     }
 
+    ShShaderOutput shaderOutput = static_cast<ShShaderOutput>(output);
+    if (!(IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput)) &&
+        (options & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
+    {
+        // This compiler option is only available in ESSL and GLSL.
+        return 0;
+    }
+
     std::vector<uint32_t> validOutputs;
     validOutputs.push_back(SH_ESSL_OUTPUT);
     validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
@@ -125,8 +134,8 @@
 
     if (translators.find(key) == translators.end())
     {
-        UniqueTCompiler translator(ConstructCompiler(type, static_cast<ShShaderSpec>(spec),
-                                                     static_cast<ShShaderOutput>(output)));
+        UniqueTCompiler translator(
+            ConstructCompiler(type, static_cast<ShShaderSpec>(spec), shaderOutput));
 
         if (translator == nullptr)
         {
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 0d9f415..a2955c4 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -537,7 +537,8 @@
     EvqSecondaryFragColorEXT,  // EXT_blend_func_extended
     EvqSecondaryFragDataEXT,   // EXT_blend_func_extended
 
-    EvqViewIDOVR,  // OVR_multiview
+    EvqViewIDOVR,      // OVR_multiview
+    EvqViewportIndex,  // gl_ViewportIndex
 
     // built-ins written by the shader_framebuffer_fetch extension(s)
     EvqLastFragColor,
@@ -774,6 +775,7 @@
     case EvqSecondaryFragColorEXT:  return "SecondaryFragColorEXT";
     case EvqSecondaryFragDataEXT:   return "SecondaryFragDataEXT";
     case EvqViewIDOVR:              return "ViewIDOVR";
+    case EvqViewportIndex:          return "ViewportIndex";
     case EvqLastFragColor:          return "LastFragColor";
     case EvqLastFragData:           return "LastFragData";
     case EvqSmoothOut:              return "smooth out";
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 6e76fe1..a194071 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -431,7 +431,8 @@
         if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
             parseContext.isMultiviewExtensionEnabled() && getShaderType() != GL_COMPUTE_SHADER)
         {
-            DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews(), getShaderType());
+            DeclareAndInitBuiltinsForInstancedMultiview(root, mNumViews, shaderType, compileOptions,
+                                                        outputType);
         }
 
         // 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 83dec99..597ebbf 100644
--- a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
+++ b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp
@@ -13,6 +13,7 @@
 #include "compiler/translator/InitializeVariables.h"
 #include "compiler/translator/IntermTraverse.h"
 #include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
 
 namespace sh
 {
@@ -47,10 +48,11 @@
     return new TIntermSymbol(0, "gl_InstanceID", TType(EbtInt, EbpHigh, EvqInstanceID));
 }
 
-void InitializeViewIDAndInstanceID(TIntermBlock *root,
-                                   TIntermTyped *viewIDSymbol,
+// Adds the InstanceID and ViewID_OVR initializers to the end of the initializers' sequence.
+void InitializeViewIDAndInstanceID(TIntermTyped *viewIDSymbol,
                                    TIntermTyped *instanceIDSymbol,
-                                   unsigned numberOfViews)
+                                   unsigned numberOfViews,
+                                   TIntermSequence *initializers)
 {
     // Create a signed numberOfViews node.
     TConstantUnion *numberOfViewsConstant = new TConstantUnion();
@@ -65,6 +67,7 @@
     // Create a InstanceID = gl_InstanceID / numberOfViews node.
     TIntermBinary *instanceIDInitializer =
         new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceID);
+    initializers->push_back(instanceIDInitializer);
 
     // Create a uint(gl_InstanceID) node.
     TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
@@ -85,11 +88,7 @@
     // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
     TIntermBinary *viewIDInitializer =
         new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
-
-    // 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);
+    initializers->push_back(viewIDInitializer);
 }
 
 // Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
@@ -108,11 +107,32 @@
     globalSequence->insert(globalSequence->begin(), declaration);
 }
 
+// Adds the expression gl_ViewportIndex = int(ViewID_OVR) to the end of the initializers' sequence.
+void SelectViewportIndexInVertexShader(TIntermTyped *viewIDSymbol, TIntermSequence *initializers)
+{
+    // Create a gl_ViewportIndex node.
+    TIntermSymbol *viewportIndexSymbol =
+        new TIntermSymbol(0, "gl_ViewportIndex", TType(EbtInt, EbpHigh, EvqViewportIndex));
+
+    // Create an int(ViewID_OVR) node.
+    TIntermSequence *viewIDSymbolCastArguments = new TIntermSequence();
+    viewIDSymbolCastArguments->push_back(viewIDSymbol);
+    TIntermAggregate *viewIDAsInt = TIntermAggregate::CreateConstructor(
+        TType(EbtInt, EbpHigh, EvqTemporary), viewIDSymbolCastArguments);
+
+    // Create a gl_ViewportIndex = int(ViewID_OVR) node.
+    TIntermBinary *viewIDInitializer =
+        new TIntermBinary(EOpAssign, viewportIndexSymbol, viewIDAsInt);
+    initializers->push_back(viewIDInitializer);
+}
+
 }  // namespace
 
 void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
                                                  unsigned numberOfViews,
-                                                 GLenum shaderType)
+                                                 GLenum shaderType,
+                                                 ShCompileOptions compileOptions,
+                                                 ShShaderOutput shaderOutput)
 {
     ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
 
@@ -132,7 +152,28 @@
         instanceIDSymbol->setInternal(true);
         DeclareGlobalVariable(root, instanceIDSymbol);
         ReplaceSymbol(root, "gl_InstanceID", instanceIDSymbol);
-        InitializeViewIDAndInstanceID(root, viewIDSymbol, instanceIDSymbol, numberOfViews);
+
+        TIntermSequence *initializers = new TIntermSequence();
+        InitializeViewIDAndInstanceID(viewIDSymbol, instanceIDSymbol, numberOfViews, initializers);
+
+        // The AST transformation which adds the expression to select the viewport index should
+        // be done only for the GLSL and ESSL output.
+        const bool selectViewport =
+            (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u;
+        // Assert that if the viewport is selected in the vertex shader, then the output is
+        // either GLSL or ESSL.
+        ASSERT(!selectViewport || IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput));
+        if (selectViewport)
+        {
+            // Setting a value to gl_ViewportIndex should happen after ViewID_OVR's initialization.
+            SelectViewportIndexInVertexShader(viewIDSymbol->deepCopy(), initializers);
+        }
+
+        // Insert initializers at the beginning of main().
+        TIntermBlock *initializersBlock = new TIntermBlock();
+        initializersBlock->getSequence()->swap(*initializers);
+        TIntermBlock *mainBody = FindMainBody(root);
+        mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initializersBlock);
     }
 }
 
diff --git a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
index b72dc0a..e9dc8fa 100644
--- a/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
+++ b/src/compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h
@@ -7,16 +7,21 @@
 // - 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.
+// - If the output is ESSL or GLSL and the SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is
+// enabled, the expression "gl_ViewportIndex = int(ViewID_OVR)" is added after ViewID and InstanceID
+// are initialized.
 //
 
 #ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
 #define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
 
+#include "GLSLANG/ShaderLang.h"
 #include "angle_gl.h"
 
 class TIntermBlock;
@@ -26,7 +31,9 @@
 
 void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
                                                  unsigned numberOfViews,
-                                                 GLenum shaderType);
+                                                 GLenum shaderType,
+                                                 ShCompileOptions compileOptions,
+                                                 ShShaderOutput shaderOutput);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index aef369f..b61805d 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -121,14 +121,16 @@
     TInfoSinkBase &sink                   = getInfoSink().obj;
     const TExtensionBehavior &extBehavior = getExtensionBehavior();
     const bool isMultiviewExtEmulated =
-        (compileOptions & (SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM |
-                           SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW)) != 0u;
-
+        (compileOptions &
+         (SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM | SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW |
+          SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER)) != 0u;
     for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); iter != extBehavior.end();
          ++iter)
     {
         if (iter->second != EBhUndefined)
         {
+            const bool isMultiview =
+                iter->first == "GL_OVR_multiview" || iter->first == "GL_OVR_multiview2";
             if (getResources().NV_shader_framebuffer_fetch &&
                 iter->first == "GL_EXT_shader_framebuffer_fetch")
             {
@@ -140,11 +142,16 @@
                 sink << "#extension GL_NV_draw_buffers : " << getBehaviorString(iter->second)
                      << "\n";
             }
-            else if (isMultiviewExtEmulated &&
-                     (iter->first == "GL_OVR_multiview" || iter->first == "GL_OVR_multiview2"))
+            else if (isMultiview && isMultiviewExtEmulated)
             {
-                // No output
-                continue;
+                if (getShaderType() == GL_VERTEX_SHADER &&
+                    (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
+                {
+                    // Emit the NV_viewport_array2 extension in a vertex shader if the
+                    // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
+                    // OVR_multiview(2) extension is requested.
+                    sink << "#extension GL_NV_viewport_array2 : require\n";
+                }
             }
             else
             {
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index 41abbfc..e45e665 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -53,7 +53,7 @@
     writeVersion(root);
 
     // Write extension behaviour as needed
-    writeExtensionBehavior(root);
+    writeExtensionBehavior(root, compileOptions);
 
     // Write pragmas after extensions because some drivers consider pragmas
     // like non-preprocessor tokens.
@@ -242,7 +242,7 @@
     }
 }
 
-void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
+void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions)
 {
     TInfoSinkBase &sink                   = getInfoSink().obj;
     const TExtensionBehavior &extBehavior = getExtensionBehavior();
@@ -269,6 +269,17 @@
                      << "\n";
             }
         }
+
+        const bool isMultiview =
+            iter.first == "GL_OVR_multiview" || iter.first == "GL_OVR_multiview2";
+        if (isMultiview && getShaderType() == GL_VERTEX_SHADER &&
+            (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u)
+        {
+            // Emit the NV_viewport_array2 extension in a vertex shader if the
+            // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the OVR_multiview(2)
+            // extension is requested.
+            sink << "#extension GL_NV_viewport_array2 : require\n";
+        }
     }
 
     // GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330
diff --git a/src/compiler/translator/TranslatorGLSL.h b/src/compiler/translator/TranslatorGLSL.h
index 9e47db9..335d543 100644
--- a/src/compiler/translator/TranslatorGLSL.h
+++ b/src/compiler/translator/TranslatorGLSL.h
@@ -27,7 +27,7 @@
 
   private:
     void writeVersion(TIntermNode *root);
-    void writeExtensionBehavior(TIntermNode *root);
+    void writeExtensionBehavior(TIntermNode *root, ShCompileOptions compileOptions);
     void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName);
 };