Commit ced5c86c by Martin Radev Committed by Commit Bot

D3D11: Handle multi-view Draw* calls

Because the ANGLE_multiview extension uses instancing to multiply geometry for each view, Draw* calls with an active multiview program have to be handled in the follwing way: 1) Convert non-instanced Draw calls to their instanced versions. 2) Multiply the number of instances in an instanced Draw call by the number of views. The patch also applies the viewport offsets to the viewport and scissor rectangle and propagates the computed viewports and scissors to the D3D11 runtime. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I8b4295c95c2cc0c1046c67e1fb1a782a46703292 Reviewed-on: https://chromium-review.googlesource.com/618331 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 0abb7a2a
......@@ -36,6 +36,9 @@ enum
IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS = 4,
// Maximum number of views which are supported by the implementation of ANGLE_multiview.
IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS = 4,
// These are the maximums the implementation can support
// The actual GL caps are limited by the device caps
// and should be queried from the Context
......
......@@ -331,6 +331,7 @@ gl::Error InputLayoutCache::updateInputLayout(
const auto &attribs = state.getVertexArray()->getVertexAttributes();
const auto &bindings = state.getVertexArray()->getVertexBindings();
const auto &locationToSemantic = programD3D->getAttribLocationToD3DSemantics();
int divisorMultiplier = program->usesMultiview() ? program->getNumViews() : 1;
for (size_t attribIndex : program->getActiveAttribLocationsMask())
{
......@@ -347,7 +348,7 @@ gl::Error InputLayoutCache::updateInputLayout(
gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(attrib, currentValue.Type);
layout.addAttributeData(glslElementType, d3dSemantic, vertexFormatType,
binding.getDivisor());
binding.getDivisor() * divisorMultiplier);
}
const d3d11::InputLayout *inputLayout = nullptr;
......
......@@ -388,6 +388,19 @@ bool DrawCallNeedsTranslation(const gl::Context *context, GLenum mode, GLenum ty
return false;
}
int GetAdjustedInstanceCount(const gl::Program *program, int instanceCount)
{
if (!program->usesMultiview())
{
return instanceCount;
}
if (instanceCount == 0)
{
return program->getNumViews();
}
return program->getNumViews() * instanceCount;
}
const uint32_t ScratchMemoryBufferLifetime = 1000;
} // anonymous namespace
......@@ -819,7 +832,7 @@ egl::Error Renderer11::initializeDevice()
const gl::Caps &rendererCaps = getNativeCaps();
if (mStateManager.initialize(rendererCaps).isError())
if (mStateManager.initialize(rendererCaps, getNativeExtensions()).isError())
{
return egl::EglBadAlloc() << "Error initializing state manager.";
}
......@@ -1626,7 +1639,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
{
const auto &data = context->getContextState();
const auto &glState = data.getState();
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(glState.getProgram());
gl::Program *program = glState.getProgram();
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
GLsizei adjustedInstanceCount = GetAdjustedInstanceCount(program, instances);
if (programD3D->usesGeometryShader(mode) && glState.isTransformFeedbackActiveUnpaused())
{
......@@ -1636,9 +1651,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
// geometry shader + pixel shader to rasterize the primitives.
mStateManager.setPixelShader(nullptr);
if (instances > 0)
if (adjustedInstanceCount > 0)
{
mDeviceContext->DrawInstanced(count, instances, 0, 0);
mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
}
else
{
......@@ -1664,9 +1679,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
mStateManager.setGeometryShader(
&GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader());
if (instances > 0)
if (adjustedInstanceCount > 0)
{
mDeviceContext->DrawInstanced(count, instances, 0, 0);
mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
}
else
{
......@@ -1677,12 +1692,12 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
if (mode == GL_LINE_LOOP)
{
return drawLineLoop(data, count, GL_NONE, nullptr, 0, instances);
return drawLineLoop(data, count, GL_NONE, nullptr, 0, adjustedInstanceCount);
}
if (mode == GL_TRIANGLE_FAN)
{
return drawTriangleFan(data, count, GL_NONE, nullptr, 0, instances);
return drawTriangleFan(data, count, GL_NONE, nullptr, 0, adjustedInstanceCount);
}
bool useInstancedPointSpriteEmulation =
......@@ -1690,21 +1705,24 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
if (mode != GL_POINTS || !useInstancedPointSpriteEmulation)
{
if (instances == 0)
if (adjustedInstanceCount == 0)
{
mDeviceContext->Draw(count, 0);
}
else
{
mDeviceContext->DrawInstanced(count, instances, 0, 0);
mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
}
return gl::NoError();
}
// This code should not be reachable by multi-view programs.
ASSERT(program->usesMultiview() == false);
// If the shader is writing to gl_PointSize, then pointsprites are being rendered.
// Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
if (instances == 0)
if (adjustedInstanceCount == 0)
{
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
return gl::NoError();
......@@ -1735,6 +1753,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
{
const auto &data = context->getContextState();
TranslatedIndexData indexInfo;
const gl::Program *program = data.getState().getProgram();
GLsizei adjustedInstanceCount = GetAdjustedInstanceCount(program, instances);
if (!DrawCallNeedsTranslation(context, mode, type))
{
......@@ -1743,9 +1763,10 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
const gl::Type &typeInfo = gl::GetTypeInfo(type);
unsigned int startIndexLocation =
static_cast<unsigned int>(reinterpret_cast<const uintptr_t>(indices)) / typeInfo.bytes;
if (instances > 0)
if (adjustedInstanceCount > 0)
{
mDeviceContext->DrawIndexedInstanced(count, instances, startIndexLocation, 0, 0);
mDeviceContext->DrawIndexedInstanced(count, adjustedInstanceCount, startIndexLocation,
0, 0);
}
else
{
......@@ -1769,29 +1790,32 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
if (mode == GL_LINE_LOOP)
{
return drawLineLoop(data, count, type, indices, baseVertex, instances);
return drawLineLoop(data, count, type, indices, baseVertex, adjustedInstanceCount);
}
if (mode == GL_TRIANGLE_FAN)
{
return drawTriangleFan(data, count, type, indices, baseVertex, instances);
return drawTriangleFan(data, count, type, indices, baseVertex, adjustedInstanceCount);
}
const ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.getState().getProgram());
if (mode != GL_POINTS || !programD3D->usesInstancedPointSpriteEmulation())
{
if (instances == 0)
if (adjustedInstanceCount == 0)
{
mDeviceContext->DrawIndexed(count, 0, baseVertex);
}
else
{
mDeviceContext->DrawIndexedInstanced(count, instances, 0, baseVertex, 0);
mDeviceContext->DrawIndexedInstanced(count, adjustedInstanceCount, 0, baseVertex, 0);
}
return gl::NoError();
}
// This code should not be reachable by multi-view programs.
ASSERT(program->usesMultiview() == false);
// If the shader is writing to gl_PointSize, then pointsprites are being rendered.
// Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
......
......@@ -84,7 +84,7 @@ class StateManager11 final : angle::NonCopyable
StateManager11(Renderer11 *renderer);
~StateManager11();
gl::Error initialize(const gl::Caps &caps);
gl::Error initialize(const gl::Caps &caps, const gl::Extensions &extensions);
void deinitialize();
void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits);
......@@ -225,6 +225,7 @@ class StateManager11 final : angle::NonCopyable
// Faster than calling setTexture a jillion times
gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd);
void handleMultiviewDrawFramebufferChange(const gl::Context *context);
gl::Error syncCurrentValueAttribs(const gl::State &state);
......@@ -272,6 +273,11 @@ class StateManager11 final : angle::NonCopyable
float mCurNear;
float mCurFar;
// The viewport offsets are guaranteed to be updated whenever the gl::State::DirtyBits are
// resolved and can be applied to the viewport and scissor whenever the internal viewport and
// scissor bits are resolved.
std::vector<gl::Offset> mViewportOffsets;
// Things needed in viewport state
dx_VertexConstants11 mVertexConstants;
dx_PixelConstants11 mPixelConstants;
......@@ -375,6 +381,9 @@ class StateManager11 final : angle::NonCopyable
InputLayoutCache mInputLayoutCache;
std::vector<const TranslatedAttribute *> mCurrentAttributes;
Optional<GLint> mLastFirstVertex;
// ANGLE_multiview.
bool mIsMultiviewEnabled;
};
} // namespace rx
......
......@@ -23,7 +23,8 @@ VertexArray11::VertexArray11(const gl::VertexArrayState &data)
: VertexArrayImpl(data),
mAttributeStorageTypes(data.getMaxAttribs(), VertexStorageType::CURRENT_VALUE),
mTranslatedAttribs(data.getMaxAttribs()),
mCurrentBuffers(data.getMaxAttribs())
mCurrentBuffers(data.getMaxAttribs()),
mAppliedNumViewsToDivisor(1)
{
for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex)
{
......@@ -180,6 +181,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
const auto &activeLocations = program->getActiveAttribLocationsMask();
const auto &attribs = mData.getVertexAttributes();
const auto &bindings = mData.getVertexBindings();
mAppliedNumViewsToDivisor =
(program != nullptr && program->usesMultiview()) ? program->getNumViews() : 1;
if (mAttribsToTranslate.any())
{
......@@ -197,7 +200,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
translatedAttrib->attribute = &attribs[dirtyAttribIndex];
translatedAttrib->binding = &bindings[translatedAttrib->attribute->bindingIndex];
translatedAttrib->currentValueType = currentValue.Type;
translatedAttrib->divisor = translatedAttrib->binding->getDivisor();
translatedAttrib->divisor =
translatedAttrib->binding->getDivisor() * mAppliedNumViewsToDivisor;
switch (mAttributeStorageTypes[dirtyAttribIndex])
{
......@@ -232,7 +236,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
dynamicAttrib->attribute = &attribs[dynamicAttribIndex];
dynamicAttrib->binding = &bindings[dynamicAttrib->attribute->bindingIndex];
dynamicAttrib->currentValueType = currentValue.Type;
dynamicAttrib->divisor = dynamicAttrib->binding->getDivisor();
dynamicAttrib->divisor =
dynamicAttrib->binding->getDivisor() * mAppliedNumViewsToDivisor;
}
ANGLE_TRY(vertexDataManager->storeDynamicAttribs(&mTranslatedAttribs, activeDynamicAttribs,
......@@ -265,4 +270,14 @@ void VertexArray11::clearDirtyAndPromoteDynamicAttribs(const gl::State &state, G
auto activeDynamicAttribs = (mDynamicAttribsMask & activeLocations);
VertexDataManager::PromoteDynamicAttribs(mTranslatedAttribs, activeDynamicAttribs, count);
}
void VertexArray11::markAllAttributeDivisorsForAdjustment(int numViews)
{
if (mAppliedNumViewsToDivisor != numViews)
{
mAppliedNumViewsToDivisor = numViews;
mAttribsToUpdate.set();
}
}
} // namespace rx
......@@ -43,6 +43,10 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
Serial getCurrentStateSerial() const { return mCurrentStateSerial; }
// In case of a multi-view program change, we have to update all attributes so that the divisor
// is adjusted.
void markAllAttributeDivisorsForAdjustment(int numViews);
private:
void updateVertexAttribStorage(const gl::Context *context, size_t attribIndex);
void flushAttribUpdates(const gl::Context *context);
......@@ -65,6 +69,9 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
std::vector<OnBufferDataDirtyBinding> mOnBufferDataDirty;
Serial mCurrentStateSerial;
// The numViews value used to adjust the divisor.
int mAppliedNumViewsToDivisor;
};
} // namespace rx
......
......@@ -1393,8 +1393,9 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
if (extensions->multiview)
{
extensions->maxViews =
std::min(static_cast<GLuint>(GetMaximum2DTextureArraySize(featureLevel)),
GetMaxViewportAndScissorRectanglesPerPipeline(featureLevel));
std::min(static_cast<GLuint>(gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS),
std::min(static_cast<GLuint>(GetMaximum2DTextureArraySize(featureLevel)),
GetMaxViewportAndScissorRectanglesPerPipeline(featureLevel)));
}
extensions->textureUsage = true; // This could be false since it has no effect in D3D11
extensions->discardFramebuffer = true;
......
......@@ -902,7 +902,9 @@ void GenerateCaps(const FunctionsGL *functions,
const int maxLayers = QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS);
// GL_MAX_VIEWPORTS is guaranteed to be at least 16.
const int maxViewports = QuerySingleGLInt(functions, GL_MAX_VIEWPORTS);
extensions->maxViews = static_cast<GLuint>(std::min(maxLayers, maxViewports));
extensions->maxViews = static_cast<GLuint>(
std::min(static_cast<int>(gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS),
std::min(maxLayers, maxViewports)));
*multiviewImplementationType = MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2;
}
......
......@@ -14,15 +14,18 @@ using namespace angle;
namespace
{
GLuint CreateSimplePassthroughProgram()
GLuint CreateSimplePassthroughProgram(int numViews)
{
const std::string vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"layout(num_views = " +
ToString(numViews) +
") in;\n"
"layout(location=0) in vec2 vPosition;\n"
"void main()\n"
"{\n"
" gl_PointSize = 1.;\n"
" gl_Position = vec4(vPosition.xy, 0.0, 1.0);\n"
"}\n";
......@@ -1383,7 +1386,7 @@ TEST_P(MultiviewRenderPrimitiveTest, Lines)
return;
}
GLuint program = CreateSimplePassthroughProgram();
GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
......@@ -1420,7 +1423,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineStrip)
return;
}
GLuint program = CreateSimplePassthroughProgram();
GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
......@@ -1457,7 +1460,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineLoop)
return;
}
GLuint program = CreateSimplePassthroughProgram();
GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
......@@ -1491,7 +1494,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleStrip)
return;
}
GLuint program = CreateSimplePassthroughProgram();
GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
......@@ -1522,7 +1525,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleFan)
return;
}
GLuint program = CreateSimplePassthroughProgram();
GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
......@@ -1760,6 +1763,60 @@ TEST_P(MultiviewRenderTest, ProgramRelinkUpdatesAttribDivisor)
}
}
// Test that useProgram applies the number of views in computing the final value of the attribute
// divisor.
TEST_P(MultiviewRenderTest, DivisorUpdatedOnProgramChange)
{
if (!requestMultiviewExtension())
{
return;
}
GLVertexArray vao;
glBindVertexArray(vao);
GLBuffer vbo;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
std::vector<Vector2I> windowCoordinates = {Vector2I(0, 0), Vector2I(1, 0), Vector2I(2, 0),
Vector2I(3, 0)};
std::vector<Vector2> vertexDataInClipSpace =
ConvertPixelCoordinatesToClipSpace(windowCoordinates, 4, 1);
// Fill with x positions so that the resulting clip space coordinate fails the clip test.
glBufferData(GL_ARRAY_BUFFER, sizeof(Vector2) * vertexDataInClipSpace.size(),
vertexDataInClipSpace.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, nullptr);
glVertexAttribDivisor(0, 1);
ASSERT_GL_NO_ERROR();
// Create a program and fbo with N views and draw N instances of a point horizontally.
for (int numViews = 2; numViews <= 4; ++numViews)
{
createFBO(4, 1, numViews);
ASSERT_GL_NO_ERROR();
GLuint program = CreateSimplePassthroughProgram(numViews);
ASSERT_NE(program, 0u);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
glDrawArraysInstanced(GL_POINTS, 0, 1, numViews);
for (int view = 0; view < numViews; ++view)
{
for (int j = 0; j < numViews; ++j)
{
EXPECT_EQ(GLColor::red, GetViewColor(j, 0, view));
}
for (int j = numViews; j < 4; ++j)
{
EXPECT_EQ(GLColor::black, GetViewColor(j, 0, view));
}
}
glDeleteProgram(program);
}
}
MultiviewTestParams SideBySideOpenGL()
{
return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, egl_platform::OPENGL());
......@@ -1776,9 +1833,17 @@ MultiviewTestParams SideBySideD3D11()
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderDualViewTest, SideBySideOpenGL(), LayeredOpenGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderTest, SideBySideOpenGL(), LayeredOpenGL());
ANGLE_INSTANTIATE_TEST(MultiviewOcclusionQueryTest, SideBySideOpenGL(), LayeredOpenGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderDualViewTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderTest, SideBySideOpenGL(), LayeredOpenGL(), SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewOcclusionQueryTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewProgramGenerationTest, SideBySideOpenGL(), SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderPrimitiveTest, SideBySideOpenGL(), LayeredOpenGL());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderPrimitiveTest,
SideBySideOpenGL(),
LayeredOpenGL(),
SideBySideD3D11());
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