Select viewport index in GS for multi-view instanced rendering

The patch extends the OutputHLSL and DynamicHLSL translators to
select the viewport index in the geometry shader and propagate
the ViewID variable to the fragment shader.

BUG=angleproject:2062
TEST=angle_end2end_tests

Change-Id: I9e344a7521e2e1137e6eb38d0bfecea8bece778f
Reviewed-on: https://chromium-review.googlesource.com/608967
Commit-Queue: Martin Radev <mradev@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 0dc1c56..39f856d 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -113,6 +113,9 @@
     mUsesFrontFacing             = false;
     mUsesPointSize               = false;
     mUsesInstanceID              = false;
+    mHasMultiviewExtensionEnabled = IsExtensionEnabled(mExtensionBehavior, "GL_OVR_multiview") ||
+                                    IsExtensionEnabled(mExtensionBehavior, "GL_OVR_multiview2");
+    mUsesViewID                  = false;
     mUsesVertexID                = false;
     mUsesFragDepth               = false;
     mUsesNumWorkGroups           = false;
@@ -708,6 +711,16 @@
         out << "#define GL_USES_POINT_SIZE\n";
     }
 
+    if (mHasMultiviewExtensionEnabled)
+    {
+        out << "#define GL_ANGLE_MULTIVIEW_ENABLED\n";
+    }
+
+    if (mUsesViewID)
+    {
+        out << "#define GL_USES_VIEW_ID\n";
+    }
+
     if (mUsesFragDepth)
     {
         out << "#define GL_USES_FRAG_DEPTH\n";
@@ -804,6 +817,10 @@
         {
             mReferencedVaryings[name] = node;
             out << Decorate(name);
+            if (name == "ViewID_OVR")
+            {
+                mUsesViewID = true;
+            }
         }
         else if (qualifier == EvqFragmentOut)
         {
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 49aa90d..46a5887 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -171,6 +171,8 @@
     bool mUsesFrontFacing;
     bool mUsesPointSize;
     bool mUsesInstanceID;
+    bool mHasMultiviewExtensionEnabled;
+    bool mUsesViewID;
     bool mUsesVertexID;
     bool mUsesFragDepth;
     bool mUsesNumWorkGroups;
diff --git a/src/libANGLE/renderer/d3d/DynamicHLSL.cpp b/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
index 27d2994..ea0eaee 100644
--- a/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
+++ b/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
@@ -379,6 +379,18 @@
         hlslStream << "    float gl_PointSize : " << builtins.glPointSize.str() << ";\n";
     }
 
+    if (builtins.glViewIDOVR.enabled)
+    {
+        hlslStream << "    nointerpolation uint gl_ViewID_OVR : " << builtins.glViewIDOVR.str()
+                   << ";\n";
+    }
+
+    if (builtins.glViewportIndex.enabled)
+    {
+        hlslStream << "    nointerpolation uint gl_ViewportIndex : "
+                   << builtins.glViewportIndex.str() << ";\n";
+    }
+
     std::string varyingSemantic =
         GetVaryingSemantic(mRenderer->getMajorShaderModel(), programUsesPointSize);
 
@@ -483,6 +495,11 @@
         vertexStream << "    output.gl_Position = gl_Position;\n";
     }
 
+    if (vertexBuiltins.glViewIDOVR.enabled)
+    {
+        vertexStream << "   output.gl_ViewID_OVR = _ViewID_OVR;\n";
+    }
+
     // On D3D9 or D3D11 Feature Level 9, we need to emulate large viewports using dx_ViewAdjust.
     if (shaderModel >= 4 && mRenderer->getShaderModelSuffix() == "")
     {
@@ -642,6 +659,11 @@
                     << "{\n";
     }
 
+    if (pixelBuiltins.glViewIDOVR.enabled)
+    {
+        pixelStream << "    _ViewID_OVR = input.gl_ViewID_OVR;\n";
+    }
+
     if (pixelBuiltins.glFragCoord.enabled)
     {
         pixelStream << "    float rhw = 1.0 / input.gl_FragCoord.w;\n";
@@ -872,7 +894,8 @@
 }
 
 std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &varyingPacking,
-                                                        const BuiltinVaryingsD3D &builtinsD3D) const
+                                                        const BuiltinVaryingsD3D &builtinsD3D,
+                                                        const bool hasANGLEMultiviewEnabled) const
 {
     ASSERT(mRenderer->getMajorShaderModel() >= 4);
 
@@ -919,6 +942,15 @@
                    << "#endif  // ANGLE_POINT_SPRITE_SHADER\n"
                    << "}\n";
 
+    if (builtinsD3D[SHADER_GEOMETRY].glViewportIndex.enabled && hasANGLEMultiviewEnabled)
+    {
+        preambleStream << "\n"
+                       << "void selectView(inout GS_OUTPUT output, GS_INPUT input)\n"
+                       << "{\n"
+                       << "    output.gl_ViewportIndex = input.gl_ViewID_OVR;\n"
+                       << "}\n";
+    }
+
     return preambleStream.str();
 }
 
@@ -926,13 +958,15 @@
                                                     const gl::ContextState &data,
                                                     const gl::ProgramState &programData,
                                                     const bool useViewScale,
+                                                    const bool hasANGLEMultiviewEnabled,
+                                                    const bool pointSpriteEmulation,
                                                     const std::string &preambleString) const
 {
     ASSERT(mRenderer->getMajorShaderModel() >= 4);
 
     std::stringstream shaderStream;
 
-    const bool pointSprites   = (primitiveType == PRIMITIVE_POINTS);
+    const bool pointSprites   = (primitiveType == PRIMITIVE_POINTS) && pointSpriteEmulation;
     const bool usesPointCoord = preambleString.find("gl_PointCoord") != std::string::npos;
 
     const char *inputPT  = nullptr;
@@ -944,9 +978,19 @@
     {
         case PRIMITIVE_POINTS:
             inputPT         = "point";
-            outputPT        = "Triangle";
             inputSize       = 1;
-            maxVertexOutput = 4;
+
+            if (pointSprites)
+            {
+                outputPT        = "Triangle";
+                maxVertexOutput = 4;
+            }
+            else
+            {
+                outputPT        = "Point";
+                maxVertexOutput = 1;
+            }
+
             break;
 
         case PRIMITIVE_LINES:
@@ -1034,7 +1078,10 @@
     {
         shaderStream << "    copyVertex(output, input[" << vertexIndex
                      << "], input[lastVertexIndex]);\n";
-
+        if (hasANGLEMultiviewEnabled)
+        {
+            shaderStream << "   selectView(output, input[" << vertexIndex << "]);\n";
+        }
         if (!pointSprites)
         {
             ASSERT(inputSize == maxVertexOutput);
@@ -1253,6 +1300,21 @@
         }
     }
 
+    if (shaderType == SHADER_VERTEX && metadata.hasANGLEMultiviewEnabled())
+    {
+        builtins->glViewIDOVR.enable(userSemantic, reservedSemanticIndex++);
+    }
+
+    if (shaderType == SHADER_PIXEL && metadata.usesViewID())
+    {
+        builtins->glViewIDOVR.enableSystem("SV_ViewportArrayIndex");
+    }
+
+    if (shaderType == SHADER_GEOMETRY && metadata.hasANGLEMultiviewEnabled())
+    {
+        builtins->glViewportIndex.enableSystem("SV_ViewportArrayIndex");
+    }
+
     // Special case: do not include PSIZE semantic in HLSL 3 pixel shaders
     if (metadata.usesSystemValuePointSize() &&
         (shaderType != SHADER_PIXEL || metadata.getRendererMajorShaderModel() >= 4))
diff --git a/src/libANGLE/renderer/d3d/DynamicHLSL.h b/src/libANGLE/renderer/d3d/DynamicHLSL.h
index 4ab0626..a5fa3b5 100644
--- a/src/libANGLE/renderer/d3d/DynamicHLSL.h
+++ b/src/libANGLE/renderer/d3d/DynamicHLSL.h
@@ -76,6 +76,8 @@
     BuiltinVarying glFragCoord;
     BuiltinVarying glPointCoord;
     BuiltinVarying glPointSize;
+    BuiltinVarying glViewIDOVR;
+    BuiltinVarying glViewportIndex;
 };
 
 inline std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize)
@@ -128,12 +130,15 @@
                                               const gl::ProgramState &programData) const;
 
     std::string generateGeometryShaderPreamble(const gl::VaryingPacking &varyingPacking,
-                                               const BuiltinVaryingsD3D &builtinsD3D) const;
+                                               const BuiltinVaryingsD3D &builtinsD3D,
+                                               const bool hasANGLEMultiviewEnabled) const;
 
     std::string generateGeometryShaderHLSL(gl::PrimitiveType primitiveType,
                                            const gl::ContextState &data,
                                            const gl::ProgramState &programData,
                                            const bool useViewScale,
+                                           const bool hasANGLEMultiviewEnabled,
+                                           const bool pointSpriteEmulation,
                                            const std::string &preambleString) const;
 
     void getPixelShaderOutputKey(const gl::ContextState &data,
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 5953a66..3c4c432 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -343,6 +343,8 @@
       mUsesInstancedPointSpriteEmulation(
           renderer->getWorkarounds().useInstancedPointSpriteEmulation),
       mUsesViewScale(renderer->presentPathFastEnabled()),
+      mHasANGLEMultiviewEnabled(vertexShader->hasANGLEMultiviewEnabled()),
+      mUsesViewID(fragmentShader->usesViewID()),
       mVertexShader(vertexShader),
       mFragmentShader(fragmentShader)
 {
@@ -390,6 +392,16 @@
     return mUsesViewScale;
 }
 
+bool ProgramD3DMetadata::hasANGLEMultiviewEnabled() const
+{
+    return mHasANGLEMultiviewEnabled;
+}
+
+bool ProgramD3DMetadata::usesViewID() const
+{
+    return mUsesViewID;
+}
+
 bool ProgramD3DMetadata::addsPointCoordToVertexShader() const
 {
     // PointSprite emulation requiress that gl_PointCoord is present in the vertex shader
@@ -549,14 +561,22 @@
     return mUsesPointSize && mRenderer->getMajorShaderModel() >= 4;
 }
 
+bool ProgramD3D::usesGeometryShaderForPointSpriteEmulation() const
+{
+    return usesPointSpriteEmulation() && !usesInstancedPointSpriteEmulation();
+}
+
 bool ProgramD3D::usesGeometryShader(GLenum drawMode) const
 {
+    if (mHasANGLEMultiviewEnabled)
+    {
+        return true;
+    }
     if (drawMode != GL_POINTS)
     {
         return mUsesFlatInterpolation;
     }
-
-    return usesPointSpriteEmulation() && !usesInstancedPointSpriteEmulation();
+    return usesGeometryShaderForPointSpriteEmulation();
 }
 
 bool ProgramD3D::usesInstancedPointSpriteEmulation() const
@@ -843,6 +863,8 @@
     stream->readBytes(reinterpret_cast<unsigned char *>(&mPixelWorkarounds),
                       sizeof(angle::CompilerWorkaroundsD3D));
     stream->readBool(&mUsesFragDepth);
+    stream->readBool(&mHasANGLEMultiviewEnabled);
+    stream->readBool(&mUsesViewID);
     stream->readBool(&mUsesPointSize);
     stream->readBool(&mUsesFlatInterpolation);
 
@@ -1066,6 +1088,8 @@
     stream->writeBytes(reinterpret_cast<unsigned char *>(&mPixelWorkarounds),
                        sizeof(angle::CompilerWorkaroundsD3D));
     stream->writeInt(mUsesFragDepth);
+    stream->writeInt(mHasANGLEMultiviewEnabled);
+    stream->writeInt(mUsesViewID);
     stream->writeInt(mUsesPointSize);
     stream->writeInt(mUsesFlatInterpolation);
 
@@ -1269,6 +1293,7 @@
 
     std::string geometryHLSL = mDynamicHLSL->generateGeometryShaderHLSL(
         geometryShaderType, data, mState, mRenderer->presentPathFastEnabled(),
+        mHasANGLEMultiviewEnabled, usesGeometryShaderForPointSpriteEmulation(),
         mGeometryShaderPreamble);
 
     gl::InfoLog tempInfoLog;
@@ -1558,6 +1583,8 @@
         mUsesPointSize = vertexShaderD3D->usesPointSize();
         mDynamicHLSL->getPixelShaderOutputKey(data, mState, metadata, &mPixelShaderKey);
         mUsesFragDepth = metadata.usesFragDepth();
+        mUsesViewID               = metadata.usesViewID();
+        mHasANGLEMultiviewEnabled = metadata.hasANGLEMultiviewEnabled();
 
         // Cache if we use flat shading
         mUsesFlatInterpolation =
@@ -1566,8 +1593,8 @@
 
         if (mRenderer->getMajorShaderModel() >= 4)
         {
-            mGeometryShaderPreamble =
-                mDynamicHLSL->generateGeometryShaderPreamble(packing, builtins);
+            mGeometryShaderPreamble = mDynamicHLSL->generateGeometryShaderPreamble(
+                packing, builtins, mHasANGLEMultiviewEnabled);
         }
 
         initAttribLocationsToD3DSemantic(context);
@@ -2330,6 +2357,8 @@
     mPixelHLSL.clear();
     mPixelWorkarounds = angle::CompilerWorkaroundsD3D();
     mUsesFragDepth = false;
+    mHasANGLEMultiviewEnabled = false;
+    mUsesViewID               = false;
     mPixelShaderKey.clear();
     mUsesPointSize = false;
     mUsesFlatInterpolation = false;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index a570f8e..bc27b31 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -122,6 +122,8 @@
     bool usesPointSize() const;
     bool usesInsertedPointCoordValue() const;
     bool usesViewScale() const;
+    bool hasANGLEMultiviewEnabled() const;
+    bool usesViewID() const;
     bool addsPointCoordToVertexShader() const;
     bool usesTransformFeedbackGLPosition() const;
     bool usesSystemValuePointSize() const;
@@ -134,6 +136,8 @@
     const std::string mShaderModelSuffix;
     const bool mUsesInstancedPointSpriteEmulation;
     const bool mUsesViewScale;
+    const bool mHasANGLEMultiviewEnabled;
+    const bool mUsesViewID;
     const ShaderD3D *mVertexShader;
     const ShaderD3D *mFragmentShader;
 };
@@ -156,6 +160,7 @@
     bool usesPointSize() const { return mUsesPointSize; }
     bool usesPointSpriteEmulation() const;
     bool usesGeometryShader(GLenum drawMode) const;
+    bool usesGeometryShaderForPointSpriteEmulation() const;
     bool usesInstancedPointSpriteEmulation() const;
 
     gl::LinkResult load(const gl::Context *context,
@@ -398,6 +403,8 @@
     std::string mPixelHLSL;
     angle::CompilerWorkaroundsD3D mPixelWorkarounds;
     bool mUsesFragDepth;
+    bool mHasANGLEMultiviewEnabled;
+    bool mUsesViewID;
     std::vector<PixelShaderOutputVariable> mPixelShaderKey;
 
     // Common code for all dynamic geometry shaders. Consists mainly of the GS input and output
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index 4696adb..43d8f3d 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -9,11 +9,12 @@
 #include "libANGLE/renderer/d3d/ShaderD3D.h"
 
 #include "common/utilities.h"
+#include "libANGLE/Caps.h"
 #include "libANGLE/Compiler.h"
 #include "libANGLE/Shader.h"
 #include "libANGLE/features.h"
-#include "libANGLE/renderer/d3d/RendererD3D.h"
 #include "libANGLE/renderer/d3d/ProgramD3D.h"
+#include "libANGLE/renderer/d3d/RendererD3D.h"
 
 // Definitions local to the translation unit
 namespace
@@ -43,7 +44,9 @@
 namespace rx
 {
 
-ShaderD3D::ShaderD3D(const gl::ShaderState &data, const angle::WorkaroundsD3D &workarounds)
+ShaderD3D::ShaderD3D(const gl::ShaderState &data,
+                     const angle::WorkaroundsD3D &workarounds,
+                     const gl::Extensions &extensions)
     : ShaderImpl(data), mAdditionalOptions(0)
 {
     uncompile();
@@ -70,6 +73,10 @@
     {
         mAdditionalOptions |= SH_EMULATE_ISNAN_FLOAT_FUNCTION;
     }
+    if (extensions.multiview)
+    {
+        mAdditionalOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
+    }
 }
 
 ShaderD3D::~ShaderD3D()
@@ -102,6 +109,8 @@
     mUsesPointCoord = false;
     mUsesDepthRange = false;
     mUsesFragDepth = false;
+    mHasANGLEMultiviewEnabled    = false;
+    mUsesViewID                  = false;
     mUsesDiscardRewriting = false;
     mUsesNestedBreak = false;
     mRequiresIEEEStrictCompiling = false;
@@ -201,6 +210,9 @@
     mUsesPointCoord            = translatedSource.find("GL_USES_POINT_COORD") != std::string::npos;
     mUsesDepthRange            = translatedSource.find("GL_USES_DEPTH_RANGE") != std::string::npos;
     mUsesFragDepth             = translatedSource.find("GL_USES_FRAG_DEPTH") != std::string::npos;
+    mHasANGLEMultiviewEnabled =
+        translatedSource.find("GL_ANGLE_MULTIVIEW_ENABLED") != std::string::npos;
+    mUsesViewID = translatedSource.find("GL_USES_VIEW_ID") != std::string::npos;
     mUsesDiscardRewriting =
         translatedSource.find("ANGLE_USES_DISCARD_REWRITING") != std::string::npos;
     mUsesNestedBreak  = translatedSource.find("ANGLE_USES_NESTED_BREAK") != std::string::npos;
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.h b/src/libANGLE/renderer/d3d/ShaderD3D.h
index 8a54757..ed6b5ec 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -19,6 +19,11 @@
 struct WorkaroundsD3D;
 }
 
+namespace gl
+{
+struct Extensions;
+}
+
 namespace rx
 {
 class DynamicHLSL;
@@ -28,7 +33,9 @@
 class ShaderD3D : public ShaderImpl
 {
   public:
-    ShaderD3D(const gl::ShaderState &data, const angle::WorkaroundsD3D &workarounds);
+    ShaderD3D(const gl::ShaderState &data,
+              const angle::WorkaroundsD3D &workarounds,
+              const gl::Extensions &extensions);
     virtual ~ShaderD3D();
 
     // ShaderImpl implementation
@@ -60,6 +67,8 @@
     bool usesPointCoord() const { return mUsesPointCoord; }
     bool usesDepthRange() const { return mUsesDepthRange; }
     bool usesFragDepth() const { return mUsesFragDepth; }
+    bool usesViewID() const { return mUsesViewID; }
+    bool hasANGLEMultiviewEnabled() const { return mHasANGLEMultiviewEnabled; }
 
     ShShaderOutput getCompilerOutputType() const;
 
@@ -73,6 +82,8 @@
     bool mUsesPointCoord;
     bool mUsesDepthRange;
     bool mUsesFragDepth;
+    bool mHasANGLEMultiviewEnabled;
+    bool mUsesViewID;
     bool mUsesDiscardRewriting;
     bool mUsesNestedBreak;
     bool mRequiresIEEEStrictCompiling;
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
index b726c6c..46fee8d 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
@@ -57,7 +57,7 @@
 
 ShaderImpl *Context11::createShader(const gl::ShaderState &data)
 {
-    return new ShaderD3D(data, mRenderer->getWorkarounds());
+    return new ShaderD3D(data, mRenderer->getWorkarounds(), mRenderer->getNativeExtensions());
 }
 
 ProgramImpl *Context11::createProgram(const gl::ProgramState &data)
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
index 3841a7a..26325a7 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
@@ -48,7 +48,7 @@
 
 ShaderImpl *Context9::createShader(const gl::ShaderState &data)
 {
-    return new ShaderD3D(data, mRenderer->getWorkarounds());
+    return new ShaderD3D(data, mRenderer->getWorkarounds(), mRenderer->getNativeExtensions());
 }
 
 ProgramImpl *Context9::createProgram(const gl::ProgramState &data)
diff --git a/src/tests/gl_tests/MultiviewDrawTest.cpp b/src/tests/gl_tests/MultiviewDrawTest.cpp
index ad11209..bb7453f 100644
--- a/src/tests/gl_tests/MultiviewDrawTest.cpp
+++ b/src/tests/gl_tests/MultiviewDrawTest.cpp
@@ -238,6 +238,12 @@
     }
 };
 
+class MultiviewProgramGenerationTest : public MultiviewSideBySideRenderTest
+{
+  protected:
+    MultiviewProgramGenerationTest() {}
+};
+
 // The test verifies that glDraw*Indirect:
 // 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
 // than 1.
@@ -993,7 +999,112 @@
     EXPECT_GL_TRUE(result);
 }
 
+// Test that a simple multi-view program which doesn't use gl_ViewID_OVR in neither VS nor FS
+// compiles and links without an error.
+TEST_P(MultiviewProgramGenerationTest, SimpleProgram)
+{
+    if (!requestMultiviewExtension())
+    {
+        return;
+    }
+
+    const std::string vsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview : require\n"
+        "layout(num_views = 2) in;\n"
+        "void main()\n"
+        "{\n"
+        "}\n";
+
+    const std::string fsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview : require\n"
+        "precision mediump float;\n"
+        "void main()\n"
+        "{\n"
+        "}\n";
+
+    ANGLE_GL_PROGRAM(program, vsSource, fsSource);
+    glUseProgram(program);
+
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test that a simple multi-view program which uses gl_ViewID_OVR only in VS compiles and links
+// without an error.
+TEST_P(MultiviewProgramGenerationTest, UseViewIDInVertexShader)
+{
+    if (!requestMultiviewExtension())
+    {
+        return;
+    }
+
+    const std::string vsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview2 : require\n"
+        "layout(num_views = 2) in;\n"
+        "void main()\n"
+        "{\n"
+        "   if (gl_ViewID_OVR == 0u) {\n"
+        "       gl_Position = vec4(1,0,0,1);\n"
+        "   } else {\n"
+        "       gl_Position = vec4(-1,0,0,1);\n"
+        "   }\n"
+        "}\n";
+
+    const std::string fsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview2 : require\n"
+        "precision mediump float;\n"
+        "void main()\n"
+        "{\n"
+        "}\n";
+
+    ANGLE_GL_PROGRAM(program, vsSource, fsSource);
+    glUseProgram(program);
+
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test that a simple multi-view program which uses gl_ViewID_OVR only in FS compiles and links
+// without an error.
+TEST_P(MultiviewProgramGenerationTest, UseViewIDInFragmentShader)
+{
+    if (!requestMultiviewExtension())
+    {
+        return;
+    }
+
+    const std::string vsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview2 : require\n"
+        "layout(num_views = 2) in;\n"
+        "void main()\n"
+        "{\n"
+        "}\n";
+
+    const std::string fsSource =
+        "#version 300 es\n"
+        "#extension GL_OVR_multiview2 : require\n"
+        "precision mediump float;\n"
+        "out vec4 col;\n"
+        "void main()\n"
+        "{\n"
+        "   if (gl_ViewID_OVR == 0u) {\n"
+        "       col = vec4(1,0,0,1);\n"
+        "   } else {\n"
+        "       col = vec4(-1,0,0,1);\n"
+        "   }\n"
+        "}\n";
+
+    ANGLE_GL_PROGRAM(program, vsSource, fsSource);
+    glUseProgram(program);
+
+    EXPECT_GL_NO_ERROR();
+}
+
 ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
 ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
 ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
 ANGLE_INSTANTIATE_TEST(MultiviewSideBySideOcclusionQueryTest, ES3_OPENGL());
+ANGLE_INSTANTIATE_TEST(MultiviewProgramGenerationTest, ES3_OPENGL(), ES3_D3D11());