Commit 72b4e1e5 by Martin Radev Committed by Commit Bot

D3D11: Add support for multiview layered rendering

A branch is added in the geometry shader to select either the viewport, or texture layer which is being rendered to based on the value of a uniform in the driver constant buffer. Using this approach there is no need for separate programs for side-by-side and layered rendering. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I66701164ff02a851c13695d5409f8ad350534e69 Reviewed-on: https://chromium-review.googlesource.com/645547 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent b5c5fb1b
......@@ -518,6 +518,13 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << " float2 dx_ViewScale : packoffset(c3);\n";
}
if (mHasMultiviewExtensionEnabled)
{
// We have to add a value which we can use to keep track of which multi-view code
// path is to be selected in the GS.
out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n";
}
if (mOutputType == SH_HLSL_4_1_OUTPUT)
{
mUniformHLSL->samplerMetadataUniforms(out, "c4");
......@@ -631,6 +638,13 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
out << " float2 dx_ViewCoords : packoffset(c2);\n";
out << " float2 dx_ViewScale : packoffset(c3);\n";
if (mHasMultiviewExtensionEnabled)
{
// We have to add a value which we can use to keep track of which multi-view code
// path is to be selected in the GS.
out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n";
}
if (mOutputType == SH_HLSL_4_1_OUTPUT)
{
mUniformHLSL->samplerMetadataUniforms(out, "c4");
......
......@@ -396,6 +396,11 @@ void DynamicHLSL::generateVaryingLinkHLSL(const VaryingPacking &varyingPacking,
<< builtins.glViewportIndex.str() << ";\n";
}
if (builtins.glLayer.enabled)
{
hlslStream << " nointerpolation uint gl_Layer : " << builtins.glLayer.str() << ";\n";
}
std::string varyingSemantic =
GetVaryingSemantic(mRenderer->getMajorShaderModel(), programUsesPointSize);
......@@ -948,12 +953,27 @@ std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &va
<< "#endif // ANGLE_POINT_SPRITE_SHADER\n"
<< "}\n";
if (builtinsD3D[SHADER_GEOMETRY].glViewportIndex.enabled && hasANGLEMultiviewEnabled)
if (hasANGLEMultiviewEnabled)
{
ASSERT(builtinsD3D[SHADER_GEOMETRY].glViewportIndex.enabled &&
builtinsD3D[SHADER_GEOMETRY].glLayer.enabled);
// According to the HLSL reference, using SV_RenderTargetArrayIndex is only valid if the
// render target is an array resource. Because of this we do not write to gl_Layer if we are
// taking the side-by-side code path. We still select the viewport index in the layered code
// path as that is always valid. See:
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647(v=vs.85).aspx
preambleStream << "\n"
<< "void selectView(inout GS_OUTPUT output, GS_INPUT input)\n"
<< "{\n"
<< " output.gl_ViewportIndex = input.gl_ViewID_OVR;\n"
<< " output.gl_ViewID_OVR = input.gl_ViewID_OVR;\n"
<< " if (multiviewSelectViewportIndex)\n"
<< " {\n"
<< " output.gl_ViewportIndex = input.gl_ViewID_OVR;\n"
<< " } else {\n"
<< " output.gl_ViewportIndex = 0;\n"
<< " output.gl_Layer = input.gl_ViewID_OVR;\n"
<< " }\n"
<< "}\n";
}
......@@ -1022,18 +1042,34 @@ std::string DynamicHLSL::generateGeometryShaderHLSL(gl::PrimitiveType primitiveT
break;
}
if (pointSprites)
if (pointSprites || hasANGLEMultiviewEnabled)
{
shaderStream << "#define ANGLE_POINT_SPRITE_SHADER\n"
"\n"
"uniform float4 dx_ViewCoords : register(c1);\n";
shaderStream << "cbuffer DriverConstants : register(b0)\n"
"{\n";
if (useViewScale)
if (pointSprites)
{
shaderStream << "uniform float2 dx_ViewScale : register(c3);\n";
shaderStream << " float4 dx_ViewCoords : packoffset(c1);\n";
if (useViewScale)
{
shaderStream << " float2 dx_ViewScale : packoffset(c3);\n";
}
}
shaderStream << "\n"
if (hasANGLEMultiviewEnabled)
{
// We have to add a value which we can use to keep track of which multi-view code path
// is to be selected in the GS.
shaderStream << " float multiviewSelectViewportIndex : packoffset(c3.z);\n";
}
shaderStream << "};\n\n";
}
if (pointSprites)
{
shaderStream << "#define ANGLE_POINT_SPRITE_SHADER\n"
"\n"
"static float2 pointSpriteCorners[] = \n"
"{\n"
" float2( 0.5f, -0.5f),\n"
......@@ -1313,12 +1349,20 @@ void BuiltinVaryingsD3D::updateBuiltins(ShaderType shaderType,
if (shaderType == SHADER_PIXEL && metadata.hasANGLEMultiviewEnabled())
{
builtins->glViewIDOVR.enableSystem("SV_ViewportArrayIndex");
builtins->glViewIDOVR.enable(userSemantic, reservedSemanticIndex++);
}
if (shaderType == SHADER_GEOMETRY && metadata.hasANGLEMultiviewEnabled())
{
// Although it is possible to retrieve gl_ViewID_OVR from the value of
// SV_ViewportArrayIndex or SV_RenderTargetArrayIndex based on the multi-view state in the
// driver constant buffer, it is easier and cleaner to pass it as a varying.
builtins->glViewIDOVR.enable(userSemantic, reservedSemanticIndex++);
// gl_Layer and gl_ViewportIndex are necessary so that we can write to either based on the
// multiview state in the driver constant buffer.
builtins->glViewportIndex.enableSystem("SV_ViewportArrayIndex");
builtins->glLayer.enableSystem("SV_RenderTargetArrayIndex");
}
// Special case: do not include PSIZE semantic in HLSL 3 pixel shaders
......
......@@ -78,6 +78,7 @@ struct BuiltinInfo
BuiltinVarying glPointSize;
BuiltinVarying glViewIDOVR;
BuiltinVarying glViewportIndex;
BuiltinVarying glLayer;
};
inline std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize)
......
......@@ -743,6 +743,24 @@ void StateManager11::handleMultiviewDrawFramebufferChange(const gl::Context *con
mInternalDirtyBits.set(DIRTY_BIT_VIEWPORT_STATE);
mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE);
}
switch (drawFramebuffer->getMultiviewLayout())
{
case GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE:
mVertexConstants.multiviewWriteToViewportIndex = 1.0f;
mPixelConstants.multiviewWriteToViewportIndex = 1.0f;
break;
case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
// Because the base view index is applied as an offset to the 2D texture array when the
// RTV is created, we just have to pass a boolean to select which code path is to be
// used.
mVertexConstants.multiviewWriteToViewportIndex = 0.0f;
mPixelConstants.multiviewWriteToViewportIndex = 0.0f;
break;
default:
// There is no need to update the value in the constant buffer if the active framebuffer
// object does not have a multiview layout.
break;
}
}
gl::Error StateManager11::syncBlendState(const gl::Context *context,
......@@ -1038,13 +1056,11 @@ void StateManager11::syncViewport(const gl::Context *context)
mPixelConstants.viewScale[0] = 1.0f;
mPixelConstants.viewScale[1] = mCurPresentPathFastEnabled ? 1.0f : -1.0f;
mPixelConstants.viewScale[2] = 1.0f;
mPixelConstants.viewScale[3] = 1.0f;
// Updates to the multiviewWriteToViewportIndex member are to be handled whenever the draw
// framebuffer's layout is changed.
mVertexConstants.viewScale[0] = mPixelConstants.viewScale[0];
mVertexConstants.viewScale[1] = mPixelConstants.viewScale[1];
mVertexConstants.viewScale[2] = mPixelConstants.viewScale[2];
mVertexConstants.viewScale[3] = mPixelConstants.viewScale[3];
}
void StateManager11::invalidateRenderTarget(const gl::Context *context)
......
......@@ -29,22 +29,56 @@ struct Renderer11DeviceCaps;
struct dx_VertexConstants11
{
dx_VertexConstants11()
: depthRange{.0f},
viewAdjust{.0f},
viewCoords{.0f},
viewScale{.0f},
multiviewWriteToViewportIndex{.0f},
padding{.0f}
{
}
float depthRange[4];
float viewAdjust[4];
float viewCoords[4];
float viewScale[4];
float viewScale[2];
// multiviewWriteToViewportIndex is used to select either the side-by-side or layered code-path
// in the GS. It's value, if set, is either 0.0f or 1.0f. The value is updated whenever a
// multi-view draw framebuffer is made active.
float multiviewWriteToViewportIndex;
// Added here to manually pad the struct.
float padding;
};
struct dx_PixelConstants11
{
dx_PixelConstants11()
: depthRange{.0f},
viewCoords{.0f},
depthFront{.0f},
viewScale{.0f},
multiviewWriteToViewportIndex{.0f},
padding{.0f}
{
}
float depthRange[4];
float viewCoords[4];
float depthFront[4];
float viewScale[4];
float viewScale[2];
// multiviewWriteToViewportIndex is used to select either the side-by-side or layered code-path
// in the GS. It's value, if set, is either 0.0f or 1.0f. The value is updated whenever a
// multi-view draw framebuffer is made active.
float multiviewWriteToViewportIndex;
// Added here to manually pad the struct.
float padding;
};
struct dx_ComputeConstants11
{
dx_ComputeConstants11() : numWorkGroups{0u}, padding{0u} {}
unsigned int numWorkGroups[3];
unsigned int padding; // This just pads the struct to 16 bytes
};
......
......@@ -173,8 +173,10 @@ class MultiviewRenderTestBase : public MultiviewDrawTest
{
}
void RenderTestSetUp() { MultiviewDrawTest::DrawTestSetUp(); }
void createFBO(int viewWidth, int height, int numViews)
void createFBO(int viewWidth, int height, int numViews, int numLayers, int baseViewIndex)
{
ASSERT(numViews + baseViewIndex <= numLayers);
mViewWidth = viewWidth;
mViewHeight = height;
mNumViews = numViews;
......@@ -204,14 +206,14 @@ class MultiviewRenderTestBase : public MultiviewDrawTest
}
case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
glBindTexture(GL_TEXTURE_2D_ARRAY, mColorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, viewWidth, height, numViews, 0,
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, viewWidth, height, numLayers, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D_ARRAY, mDepthTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, viewWidth, height,
numViews, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
numLayers, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
break;
default:
......@@ -241,9 +243,11 @@ class MultiviewRenderTestBase : public MultiviewDrawTest
}
case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
glFramebufferTextureMultiviewLayeredANGLE(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
mColorTexture, 0, 0, numViews);
mColorTexture, 0, baseViewIndex,
numViews);
glFramebufferTextureMultiviewLayeredANGLE(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
mDepthTexture, 0, 0, numViews);
mDepthTexture, 0, baseViewIndex,
numViews);
break;
default:
UNREACHABLE();
......@@ -263,20 +267,26 @@ class MultiviewRenderTestBase : public MultiviewDrawTest
glBindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer[0]);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mColorTexture, 0);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE,
glCheckFramebufferStatus(GL_READ_FRAMEBUFFER));
break;
case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
mReadFramebuffer.resize(numViews);
for (int i = 0; i < numViews; ++i)
mReadFramebuffer.resize(numLayers);
for (int i = 0; i < numLayers; ++i)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer[i]);
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
mColorTexture, 0, i);
glBindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer[i]);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mColorTexture,
0, i);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE,
glCheckFramebufferStatus(GL_READ_FRAMEBUFFER));
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDrawFramebuffer);
break;
default:
UNREACHABLE();
}
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_READ_FRAMEBUFFER));
// Clear the buffers.
glViewport(0, 0, viewWidth, height);
......@@ -290,6 +300,11 @@ class MultiviewRenderTestBase : public MultiviewDrawTest
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void createFBO(int viewWidth, int height, int numViews)
{
createFBO(viewWidth, height, numViews, numViews, 0);
}
GLColor GetViewColor(int x, int y, int view)
{
switch (mMultiviewLayout)
......@@ -453,6 +468,17 @@ class MultiviewSideBySideRenderTest : public MultiviewRenderTestBase,
void SetUp() override { MultiviewRenderTestBase::RenderTestSetUp(); }
};
class MultiviewLayeredRenderTest : public MultiviewRenderTestBase,
public ::testing::TestWithParam<PlatformParameters>
{
protected:
MultiviewLayeredRenderTest()
: MultiviewRenderTestBase(GetParam(), GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE)
{
}
void SetUp() override { MultiviewRenderTestBase::RenderTestSetUp(); }
};
// 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.
......@@ -1817,6 +1843,96 @@ TEST_P(MultiviewRenderTest, DivisorUpdatedOnProgramChange)
}
}
// The test checks that gl_ViewID_OVR is correctly propagated to the fragment shader.
TEST_P(MultiviewRenderTest, SelectColorBasedOnViewIDOVR)
{
if (!requestMultiviewExtension())
{
return;
}
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"layout(num_views = 3) in;\n"
"in vec3 vPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(vPosition, 1.);\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 if (gl_ViewID_OVR == 1u) {\n"
" col = vec4(0,1,0,1);\n"
" } else if (gl_ViewID_OVR == 2u) {\n"
" col = vec4(0,0,1,1);\n"
" } else {\n"
" col = vec4(0,0,0,0);\n"
" }\n"
"}\n";
createFBO(1, 1, 3);
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
drawQuad(program, "vPosition", 0.0f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(GLColor::red, GetViewColor(0, 0, 0));
EXPECT_EQ(GLColor::green, GetViewColor(0, 0, 1));
EXPECT_EQ(GLColor::blue, GetViewColor(0, 0, 2));
}
// The test checks that the inactive layers of a 2D texture array are not written to by a
// multi-view program.
TEST_P(MultiviewLayeredRenderTest, RenderToSubrageOfLayers)
{
if (!requestMultiviewExtension())
{
return;
}
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"in vec3 vPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(vPosition, 1.);\n"
"}\n";
const std::string fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,1);\n"
"}\n";
createFBO(1, 1, 2, 4, 1);
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
drawQuad(program, "vPosition", 0.0f, 1.0f, true);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(GLColor::transparentBlack, GetViewColor(0, 0, 0));
EXPECT_EQ(GLColor::red, GetViewColor(0, 0, 1));
EXPECT_EQ(GLColor::red, GetViewColor(0, 0, 2));
EXPECT_EQ(GLColor::transparentBlack, GetViewColor(0, 0, 3));
}
MultiviewTestParams SideBySideOpenGL()
{
return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, egl_platform::OPENGL());
......@@ -1832,18 +1948,35 @@ MultiviewTestParams SideBySideD3D11()
return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, egl_platform::D3D11());
}
MultiviewTestParams LayeredD3D11()
{
return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, egl_platform::D3D11());
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderDualViewTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderTest, SideBySideOpenGL(), LayeredOpenGL(), SideBySideD3D11());
SideBySideD3D11(),
LayeredD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11(),
LayeredD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewOcclusionQueryTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewProgramGenerationTest, SideBySideOpenGL(), SideBySideD3D11());
SideBySideD3D11(),
LayeredD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewProgramGenerationTest,
SideBySideOpenGL(),
SideBySideD3D11(),
LayeredD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderPrimitiveTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
SideBySideD3D11(),
LayeredD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL(), ES3_D3D11());
ANGLE_INSTANTIATE_TEST(MultiviewLayeredRenderTest, ES3_OPENGL(), ES3_D3D11());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment