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());