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 ...@@ -36,6 +36,9 @@ enum
IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS = 4, 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 // These are the maximums the implementation can support
// The actual GL caps are limited by the device caps // The actual GL caps are limited by the device caps
// and should be queried from the Context // and should be queried from the Context
......
...@@ -331,6 +331,7 @@ gl::Error InputLayoutCache::updateInputLayout( ...@@ -331,6 +331,7 @@ gl::Error InputLayoutCache::updateInputLayout(
const auto &attribs = state.getVertexArray()->getVertexAttributes(); const auto &attribs = state.getVertexArray()->getVertexAttributes();
const auto &bindings = state.getVertexArray()->getVertexBindings(); const auto &bindings = state.getVertexArray()->getVertexBindings();
const auto &locationToSemantic = programD3D->getAttribLocationToD3DSemantics(); const auto &locationToSemantic = programD3D->getAttribLocationToD3DSemantics();
int divisorMultiplier = program->usesMultiview() ? program->getNumViews() : 1;
for (size_t attribIndex : program->getActiveAttribLocationsMask()) for (size_t attribIndex : program->getActiveAttribLocationsMask())
{ {
...@@ -347,7 +348,7 @@ gl::Error InputLayoutCache::updateInputLayout( ...@@ -347,7 +348,7 @@ gl::Error InputLayoutCache::updateInputLayout(
gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(attrib, currentValue.Type); gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(attrib, currentValue.Type);
layout.addAttributeData(glslElementType, d3dSemantic, vertexFormatType, layout.addAttributeData(glslElementType, d3dSemantic, vertexFormatType,
binding.getDivisor()); binding.getDivisor() * divisorMultiplier);
} }
const d3d11::InputLayout *inputLayout = nullptr; const d3d11::InputLayout *inputLayout = nullptr;
......
...@@ -388,6 +388,19 @@ bool DrawCallNeedsTranslation(const gl::Context *context, GLenum mode, GLenum ty ...@@ -388,6 +388,19 @@ bool DrawCallNeedsTranslation(const gl::Context *context, GLenum mode, GLenum ty
return false; 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; const uint32_t ScratchMemoryBufferLifetime = 1000;
} // anonymous namespace } // anonymous namespace
...@@ -819,7 +832,7 @@ egl::Error Renderer11::initializeDevice() ...@@ -819,7 +832,7 @@ egl::Error Renderer11::initializeDevice()
const gl::Caps &rendererCaps = getNativeCaps(); const gl::Caps &rendererCaps = getNativeCaps();
if (mStateManager.initialize(rendererCaps).isError()) if (mStateManager.initialize(rendererCaps, getNativeExtensions()).isError())
{ {
return egl::EglBadAlloc() << "Error initializing state manager."; return egl::EglBadAlloc() << "Error initializing state manager.";
} }
...@@ -1626,7 +1639,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1626,7 +1639,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
{ {
const auto &data = context->getContextState(); const auto &data = context->getContextState();
const auto &glState = data.getState(); 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()) if (programD3D->usesGeometryShader(mode) && glState.isTransformFeedbackActiveUnpaused())
{ {
...@@ -1636,9 +1651,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1636,9 +1651,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
// geometry shader + pixel shader to rasterize the primitives. // geometry shader + pixel shader to rasterize the primitives.
mStateManager.setPixelShader(nullptr); mStateManager.setPixelShader(nullptr);
if (instances > 0) if (adjustedInstanceCount > 0)
{ {
mDeviceContext->DrawInstanced(count, instances, 0, 0); mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
} }
else else
{ {
...@@ -1664,9 +1679,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1664,9 +1679,9 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
mStateManager.setGeometryShader( mStateManager.setGeometryShader(
&GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader()); &GetAs<ShaderExecutable11>(geometryExe)->getGeometryShader());
if (instances > 0) if (adjustedInstanceCount > 0)
{ {
mDeviceContext->DrawInstanced(count, instances, 0, 0); mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
} }
else else
{ {
...@@ -1677,12 +1692,12 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1677,12 +1692,12 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
if (mode == GL_LINE_LOOP) 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) 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 = bool useInstancedPointSpriteEmulation =
...@@ -1690,21 +1705,24 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context, ...@@ -1690,21 +1705,24 @@ gl::Error Renderer11::drawArraysImpl(const gl::Context *context,
if (mode != GL_POINTS || !useInstancedPointSpriteEmulation) if (mode != GL_POINTS || !useInstancedPointSpriteEmulation)
{ {
if (instances == 0) if (adjustedInstanceCount == 0)
{ {
mDeviceContext->Draw(count, 0); mDeviceContext->Draw(count, 0);
} }
else else
{ {
mDeviceContext->DrawInstanced(count, instances, 0, 0); mDeviceContext->DrawInstanced(count, adjustedInstanceCount, 0, 0);
} }
return gl::NoError(); 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. // 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 // Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
if (instances == 0) if (adjustedInstanceCount == 0)
{ {
mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0); mDeviceContext->DrawIndexedInstanced(6, count, 0, 0, 0);
return gl::NoError(); return gl::NoError();
...@@ -1735,6 +1753,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1735,6 +1753,8 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
{ {
const auto &data = context->getContextState(); const auto &data = context->getContextState();
TranslatedIndexData indexInfo; TranslatedIndexData indexInfo;
const gl::Program *program = data.getState().getProgram();
GLsizei adjustedInstanceCount = GetAdjustedInstanceCount(program, instances);
if (!DrawCallNeedsTranslation(context, mode, type)) if (!DrawCallNeedsTranslation(context, mode, type))
{ {
...@@ -1743,9 +1763,10 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1743,9 +1763,10 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
const gl::Type &typeInfo = gl::GetTypeInfo(type); const gl::Type &typeInfo = gl::GetTypeInfo(type);
unsigned int startIndexLocation = unsigned int startIndexLocation =
static_cast<unsigned int>(reinterpret_cast<const uintptr_t>(indices)) / typeInfo.bytes; 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 else
{ {
...@@ -1769,29 +1790,32 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context, ...@@ -1769,29 +1790,32 @@ gl::Error Renderer11::drawElementsImpl(const gl::Context *context,
if (mode == GL_LINE_LOOP) 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) 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()); const ProgramD3D *programD3D = GetImplAs<ProgramD3D>(data.getState().getProgram());
if (mode != GL_POINTS || !programD3D->usesInstancedPointSpriteEmulation()) if (mode != GL_POINTS || !programD3D->usesInstancedPointSpriteEmulation())
{ {
if (instances == 0) if (adjustedInstanceCount == 0)
{ {
mDeviceContext->DrawIndexed(count, 0, baseVertex); mDeviceContext->DrawIndexed(count, 0, baseVertex);
} }
else else
{ {
mDeviceContext->DrawIndexedInstanced(count, instances, 0, baseVertex, 0); mDeviceContext->DrawIndexedInstanced(count, adjustedInstanceCount, 0, baseVertex, 0);
} }
return gl::NoError(); 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. // 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 // Emulating instanced point sprites for FL9_3 requires the topology to be
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead. // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST and DrawIndexedInstanced is called instead.
......
...@@ -343,7 +343,8 @@ StateManager11::StateManager11(Renderer11 *renderer) ...@@ -343,7 +343,8 @@ StateManager11::StateManager11(Renderer11 *renderer)
mAppliedIBOffset(0), mAppliedIBOffset(0),
mAppliedIBChanged(false), mAppliedIBChanged(false),
mVertexDataManager(renderer), mVertexDataManager(renderer),
mIndexDataManager(renderer, RENDERER_D3D11) mIndexDataManager(renderer, RENDERER_D3D11),
mIsMultiviewEnabled(false)
{ {
mCurBlendState.blend = false; mCurBlendState.blend = false;
mCurBlendState.sourceBlendRGB = GL_ONE; mCurBlendState.sourceBlendRGB = GL_ONE;
...@@ -677,13 +678,33 @@ void StateManager11::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -677,13 +678,33 @@ void StateManager11::syncState(const gl::Context *context, const gl::State::Dirt
break; break;
case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
invalidateRenderTarget(context); invalidateRenderTarget(context);
if (mIsMultiviewEnabled)
{
handleMultiviewDrawFramebufferChange(context);
}
break; break;
case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
invalidateVertexBuffer(); invalidateVertexBuffer();
break; break;
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
{
invalidateVertexBuffer(); invalidateVertexBuffer();
invalidateRenderTarget(context); invalidateRenderTarget(context);
gl::VertexArray *vao = state.getVertexArray();
if (mIsMultiviewEnabled && vao != nullptr)
{
// If ANGLE_multiview is enabled, the attribute divisor has to be updated for
// each binding.
VertexArray11 *vao11 = GetImplAs<VertexArray11>(vao);
const gl::Program *program = state.getProgram();
int numViews = 1;
if (program != nullptr && program->usesMultiview())
{
numViews = program->getNumViews();
}
vao11->markAllAttributeDivisorsForAdjustment(numViews);
}
}
break; break;
default: default:
if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 && if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
...@@ -700,6 +721,30 @@ void StateManager11::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -700,6 +721,30 @@ void StateManager11::syncState(const gl::Context *context, const gl::State::Dirt
// TODO(jmadill): Input layout and vertex buffer state. // TODO(jmadill): Input layout and vertex buffer state.
} }
void StateManager11::handleMultiviewDrawFramebufferChange(const gl::Context *context)
{
const auto &glState = context->getGLState();
const gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer();
ASSERT(drawFramebuffer != nullptr);
// Update viewport offsets.
const std::vector<gl::Offset> *attachmentViewportOffsets =
drawFramebuffer->getViewportOffsets();
const std::vector<gl::Offset> &viewportOffsets =
attachmentViewportOffsets != nullptr
? *attachmentViewportOffsets
: gl::FramebufferAttachment::GetDefaultViewportOffsetVector();
if (mViewportOffsets != viewportOffsets)
{
mViewportOffsets = viewportOffsets;
// Because new viewport offsets are to be applied, we have to mark the internal viewport and
// scissor state as dirty.
mInternalDirtyBits.set(DIRTY_BIT_VIEWPORT_STATE);
mInternalDirtyBits.set(DIRTY_BIT_SCISSOR_STATE);
}
}
gl::Error StateManager11::syncBlendState(const gl::Context *context, gl::Error StateManager11::syncBlendState(const gl::Context *context,
const gl::Framebuffer *framebuffer, const gl::Framebuffer *framebuffer,
const gl::BlendState &blendState, const gl::BlendState &blendState,
...@@ -847,13 +892,19 @@ void StateManager11::syncScissorRectangle(const gl::Rectangle &scissor, bool ena ...@@ -847,13 +892,19 @@ void StateManager11::syncScissorRectangle(const gl::Rectangle &scissor, bool ena
if (enabled) if (enabled)
{ {
D3D11_RECT rect; std::array<D3D11_RECT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> rectangles;
rect.left = std::max(0, scissor.x); const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size());
rect.top = std::max(0, modifiedScissorY); for (UINT i = 0u; i < numRectangles; ++i)
rect.right = scissor.x + std::max(0, scissor.width); {
rect.bottom = modifiedScissorY + std::max(0, scissor.height); D3D11_RECT &rect = rectangles[i];
int x = scissor.x + mViewportOffsets[i].x;
mRenderer->getDeviceContext()->RSSetScissorRects(1, &rect); int y = modifiedScissorY + mViewportOffsets[i].y;
rect.left = std::max(0, x);
rect.top = std::max(0, y);
rect.right = x + std::max(0, scissor.width);
rect.bottom = y + std::max(0, scissor.height);
}
mRenderer->getDeviceContext()->RSSetScissorRects(numRectangles, rectangles.data());
} }
mCurScissorRect = scissor; mCurScissorRect = scissor;
...@@ -883,14 +934,25 @@ void StateManager11::syncViewport(const gl::Context *context) ...@@ -883,14 +934,25 @@ void StateManager11::syncViewport(const gl::Context *context)
} }
const auto &viewport = glState.getViewport(); const auto &viewport = glState.getViewport();
int dxViewportTopLeftX = gl::clamp(viewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX); std::array<D3D11_VIEWPORT, gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS> dxViewports;
int dxViewportTopLeftY = gl::clamp(viewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY); const UINT numRectangles = static_cast<UINT>(mViewportOffsets.size());
int dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX);
int dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY);
D3D11_VIEWPORT dxViewport; int dxViewportTopLeftX = 0;
dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX); int dxViewportTopLeftY = 0;
int dxViewportWidth = 0;
int dxViewportHeight = 0;
for (UINT i = 0u; i < numRectangles; ++i)
{
dxViewportTopLeftX = gl::clamp(viewport.x + mViewportOffsets[i].x, dxMinViewportBoundsX,
dxMaxViewportBoundsX);
dxViewportTopLeftY = gl::clamp(viewport.y + mViewportOffsets[i].y, dxMinViewportBoundsY,
dxMaxViewportBoundsY);
dxViewportWidth = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX);
dxViewportHeight = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY);
D3D11_VIEWPORT &dxViewport = dxViewports[i];
dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX);
if (mCurPresentPathFastEnabled) if (mCurPresentPathFastEnabled)
{ {
// When present path fast is active and we're rendering to framebuffer 0, we must invert // When present path fast is active and we're rendering to framebuffer 0, we must invert
...@@ -906,11 +968,6 @@ void StateManager11::syncViewport(const gl::Context *context) ...@@ -906,11 +968,6 @@ void StateManager11::syncViewport(const gl::Context *context)
dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY); dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY);
} }
dxViewport.Width = static_cast<float>(dxViewportWidth);
dxViewport.Height = static_cast<float>(dxViewportHeight);
dxViewport.MinDepth = actualZNear;
dxViewport.MaxDepth = actualZFar;
// The es 3.1 spec section 9.2 states that, "If there are no attachments, rendering // The es 3.1 spec section 9.2 states that, "If there are no attachments, rendering
// will be limited to a rectangle having a lower left of (0, 0) and an upper right of // will be limited to a rectangle having a lower left of (0, 0) and an upper right of
// (width, height), where width and height are the framebuffer object's default width // (width, height), where width and height are the framebuffer object's default width
...@@ -926,8 +983,16 @@ void StateManager11::syncViewport(const gl::Context *context) ...@@ -926,8 +983,16 @@ void StateManager11::syncViewport(const gl::Context *context)
dxViewport.Height = dxViewport.Height =
static_cast<GLfloat>(std::min(viewport.height, framebuffer->getDefaultHeight())); static_cast<GLfloat>(std::min(viewport.height, framebuffer->getDefaultHeight()));
} }
else
{
dxViewport.Width = static_cast<float>(dxViewportWidth);
dxViewport.Height = static_cast<float>(dxViewportHeight);
}
dxViewport.MinDepth = actualZNear;
dxViewport.MaxDepth = actualZFar;
}
mRenderer->getDeviceContext()->RSSetViewports(1, &dxViewport); mRenderer->getDeviceContext()->RSSetViewports(numRectangles, dxViewports.data());
mCurViewport = viewport; mCurViewport = viewport;
mCurNear = actualZNear; mCurNear = actualZNear;
...@@ -937,6 +1002,7 @@ void StateManager11::syncViewport(const gl::Context *context) ...@@ -937,6 +1002,7 @@ void StateManager11::syncViewport(const gl::Context *context)
// using viewAdjust (like the D3D9 renderer). // using viewAdjust (like the D3D9 renderer).
if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3)
{ {
const auto &dxViewport = dxViewports[0];
mVertexConstants.viewAdjust[0] = static_cast<float>((viewport.width - dxViewportWidth) + mVertexConstants.viewAdjust[0] = static_cast<float>((viewport.width - dxViewportWidth) +
2 * (viewport.x - dxViewportTopLeftX)) / 2 * (viewport.x - dxViewportTopLeftX)) /
dxViewport.Width; dxViewport.Width;
...@@ -1029,7 +1095,7 @@ void StateManager11::invalidateRenderTarget(const gl::Context *context) ...@@ -1029,7 +1095,7 @@ void StateManager11::invalidateRenderTarget(const gl::Context *context)
if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3) if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3)
{ {
auto *firstAttachment = fbo->getFirstNonNullAttachment(); const auto *firstAttachment = fbo->getFirstNonNullAttachment();
const auto &size = firstAttachment->getSize(); const auto &size = firstAttachment->getSize();
if (mViewportBounds.width != size.width || mViewportBounds.height != size.height) if (mViewportBounds.width != size.width || mViewportBounds.height != size.height)
{ {
...@@ -1242,7 +1308,7 @@ void StateManager11::unsetConflictingAttachmentResources( ...@@ -1242,7 +1308,7 @@ void StateManager11::unsetConflictingAttachmentResources(
} }
} }
gl::Error StateManager11::initialize(const gl::Caps &caps) gl::Error StateManager11::initialize(const gl::Caps &caps, const gl::Extensions &extensions)
{ {
mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits); mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits);
mCurPixelSRVs.initialize(caps.maxTextureImageUnits); mCurPixelSRVs.initialize(caps.maxTextureImageUnits);
...@@ -1264,6 +1330,9 @@ gl::Error StateManager11::initialize(const gl::Caps &caps) ...@@ -1264,6 +1330,9 @@ gl::Error StateManager11::initialize(const gl::Caps &caps)
mSamplerMetadataPS.initData(caps.maxTextureImageUnits); mSamplerMetadataPS.initData(caps.maxTextureImageUnits);
mSamplerMetadataCS.initData(caps.maxComputeTextureImageUnits); mSamplerMetadataCS.initData(caps.maxComputeTextureImageUnits);
mIsMultiviewEnabled = extensions.multiview;
mViewportOffsets.resize(1u);
ANGLE_TRY(mVertexDataManager.initialize()); ANGLE_TRY(mVertexDataManager.initialize());
mCurrentAttributes.reserve(gl::MAX_VERTEX_ATTRIBS); mCurrentAttributes.reserve(gl::MAX_VERTEX_ATTRIBS);
......
...@@ -84,7 +84,7 @@ class StateManager11 final : angle::NonCopyable ...@@ -84,7 +84,7 @@ class StateManager11 final : angle::NonCopyable
StateManager11(Renderer11 *renderer); StateManager11(Renderer11 *renderer);
~StateManager11(); ~StateManager11();
gl::Error initialize(const gl::Caps &caps); gl::Error initialize(const gl::Caps &caps, const gl::Extensions &extensions);
void deinitialize(); void deinitialize();
void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits); void syncState(const gl::Context *context, const gl::State::DirtyBits &dirtyBits);
...@@ -225,6 +225,7 @@ class StateManager11 final : angle::NonCopyable ...@@ -225,6 +225,7 @@ class StateManager11 final : angle::NonCopyable
// Faster than calling setTexture a jillion times // Faster than calling setTexture a jillion times
gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd); 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); gl::Error syncCurrentValueAttribs(const gl::State &state);
...@@ -272,6 +273,11 @@ class StateManager11 final : angle::NonCopyable ...@@ -272,6 +273,11 @@ class StateManager11 final : angle::NonCopyable
float mCurNear; float mCurNear;
float mCurFar; 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 // Things needed in viewport state
dx_VertexConstants11 mVertexConstants; dx_VertexConstants11 mVertexConstants;
dx_PixelConstants11 mPixelConstants; dx_PixelConstants11 mPixelConstants;
...@@ -375,6 +381,9 @@ class StateManager11 final : angle::NonCopyable ...@@ -375,6 +381,9 @@ class StateManager11 final : angle::NonCopyable
InputLayoutCache mInputLayoutCache; InputLayoutCache mInputLayoutCache;
std::vector<const TranslatedAttribute *> mCurrentAttributes; std::vector<const TranslatedAttribute *> mCurrentAttributes;
Optional<GLint> mLastFirstVertex; Optional<GLint> mLastFirstVertex;
// ANGLE_multiview.
bool mIsMultiviewEnabled;
}; };
} // namespace rx } // namespace rx
......
...@@ -23,7 +23,8 @@ VertexArray11::VertexArray11(const gl::VertexArrayState &data) ...@@ -23,7 +23,8 @@ VertexArray11::VertexArray11(const gl::VertexArrayState &data)
: VertexArrayImpl(data), : VertexArrayImpl(data),
mAttributeStorageTypes(data.getMaxAttribs(), VertexStorageType::CURRENT_VALUE), mAttributeStorageTypes(data.getMaxAttribs(), VertexStorageType::CURRENT_VALUE),
mTranslatedAttribs(data.getMaxAttribs()), mTranslatedAttribs(data.getMaxAttribs()),
mCurrentBuffers(data.getMaxAttribs()) mCurrentBuffers(data.getMaxAttribs()),
mAppliedNumViewsToDivisor(1)
{ {
for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex) for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex)
{ {
...@@ -180,6 +181,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context ...@@ -180,6 +181,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
const auto &activeLocations = program->getActiveAttribLocationsMask(); const auto &activeLocations = program->getActiveAttribLocationsMask();
const auto &attribs = mData.getVertexAttributes(); const auto &attribs = mData.getVertexAttributes();
const auto &bindings = mData.getVertexBindings(); const auto &bindings = mData.getVertexBindings();
mAppliedNumViewsToDivisor =
(program != nullptr && program->usesMultiview()) ? program->getNumViews() : 1;
if (mAttribsToTranslate.any()) if (mAttribsToTranslate.any())
{ {
...@@ -197,7 +200,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context ...@@ -197,7 +200,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
translatedAttrib->attribute = &attribs[dirtyAttribIndex]; translatedAttrib->attribute = &attribs[dirtyAttribIndex];
translatedAttrib->binding = &bindings[translatedAttrib->attribute->bindingIndex]; translatedAttrib->binding = &bindings[translatedAttrib->attribute->bindingIndex];
translatedAttrib->currentValueType = currentValue.Type; translatedAttrib->currentValueType = currentValue.Type;
translatedAttrib->divisor = translatedAttrib->binding->getDivisor(); translatedAttrib->divisor =
translatedAttrib->binding->getDivisor() * mAppliedNumViewsToDivisor;
switch (mAttributeStorageTypes[dirtyAttribIndex]) switch (mAttributeStorageTypes[dirtyAttribIndex])
{ {
...@@ -232,7 +236,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context ...@@ -232,7 +236,8 @@ gl::Error VertexArray11::updateDirtyAndDynamicAttribs(const gl::Context *context
dynamicAttrib->attribute = &attribs[dynamicAttribIndex]; dynamicAttrib->attribute = &attribs[dynamicAttribIndex];
dynamicAttrib->binding = &bindings[dynamicAttrib->attribute->bindingIndex]; dynamicAttrib->binding = &bindings[dynamicAttrib->attribute->bindingIndex];
dynamicAttrib->currentValueType = currentValue.Type; dynamicAttrib->currentValueType = currentValue.Type;
dynamicAttrib->divisor = dynamicAttrib->binding->getDivisor(); dynamicAttrib->divisor =
dynamicAttrib->binding->getDivisor() * mAppliedNumViewsToDivisor;
} }
ANGLE_TRY(vertexDataManager->storeDynamicAttribs(&mTranslatedAttribs, activeDynamicAttribs, ANGLE_TRY(vertexDataManager->storeDynamicAttribs(&mTranslatedAttribs, activeDynamicAttribs,
...@@ -265,4 +270,14 @@ void VertexArray11::clearDirtyAndPromoteDynamicAttribs(const gl::State &state, G ...@@ -265,4 +270,14 @@ void VertexArray11::clearDirtyAndPromoteDynamicAttribs(const gl::State &state, G
auto activeDynamicAttribs = (mDynamicAttribsMask & activeLocations); auto activeDynamicAttribs = (mDynamicAttribsMask & activeLocations);
VertexDataManager::PromoteDynamicAttribs(mTranslatedAttribs, activeDynamicAttribs, count); VertexDataManager::PromoteDynamicAttribs(mTranslatedAttribs, activeDynamicAttribs, count);
} }
void VertexArray11::markAllAttributeDivisorsForAdjustment(int numViews)
{
if (mAppliedNumViewsToDivisor != numViews)
{
mAppliedNumViewsToDivisor = numViews;
mAttribsToUpdate.set();
}
}
} // namespace rx } // namespace rx
...@@ -43,6 +43,10 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver ...@@ -43,6 +43,10 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
Serial getCurrentStateSerial() const { return mCurrentStateSerial; } 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: private:
void updateVertexAttribStorage(const gl::Context *context, size_t attribIndex); void updateVertexAttribStorage(const gl::Context *context, size_t attribIndex);
void flushAttribUpdates(const gl::Context *context); void flushAttribUpdates(const gl::Context *context);
...@@ -65,6 +69,9 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver ...@@ -65,6 +69,9 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
std::vector<OnBufferDataDirtyBinding> mOnBufferDataDirty; std::vector<OnBufferDataDirtyBinding> mOnBufferDataDirty;
Serial mCurrentStateSerial; Serial mCurrentStateSerial;
// The numViews value used to adjust the divisor.
int mAppliedNumViewsToDivisor;
}; };
} // namespace rx } // namespace rx
......
...@@ -1393,8 +1393,9 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons ...@@ -1393,8 +1393,9 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
if (extensions->multiview) if (extensions->multiview)
{ {
extensions->maxViews = extensions->maxViews =
std::min(static_cast<GLuint>(gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS),
std::min(static_cast<GLuint>(GetMaximum2DTextureArraySize(featureLevel)), std::min(static_cast<GLuint>(GetMaximum2DTextureArraySize(featureLevel)),
GetMaxViewportAndScissorRectanglesPerPipeline(featureLevel)); GetMaxViewportAndScissorRectanglesPerPipeline(featureLevel)));
} }
extensions->textureUsage = true; // This could be false since it has no effect in D3D11 extensions->textureUsage = true; // This could be false since it has no effect in D3D11
extensions->discardFramebuffer = true; extensions->discardFramebuffer = true;
......
...@@ -902,7 +902,9 @@ void GenerateCaps(const FunctionsGL *functions, ...@@ -902,7 +902,9 @@ void GenerateCaps(const FunctionsGL *functions,
const int maxLayers = QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS); const int maxLayers = QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS);
// GL_MAX_VIEWPORTS is guaranteed to be at least 16. // GL_MAX_VIEWPORTS is guaranteed to be at least 16.
const int maxViewports = QuerySingleGLInt(functions, GL_MAX_VIEWPORTS); 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; *multiviewImplementationType = MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2;
} }
......
...@@ -14,15 +14,18 @@ using namespace angle; ...@@ -14,15 +14,18 @@ using namespace angle;
namespace namespace
{ {
GLuint CreateSimplePassthroughProgram() GLuint CreateSimplePassthroughProgram(int numViews)
{ {
const std::string vsSource = const std::string vsSource =
"#version 300 es\n" "#version 300 es\n"
"#extension GL_OVR_multiview : require\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" "layout(location=0) in vec2 vPosition;\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" gl_PointSize = 1.;\n"
" gl_Position = vec4(vPosition.xy, 0.0, 1.0);\n" " gl_Position = vec4(vPosition.xy, 0.0, 1.0);\n"
"}\n"; "}\n";
...@@ -1383,7 +1386,7 @@ TEST_P(MultiviewRenderPrimitiveTest, Lines) ...@@ -1383,7 +1386,7 @@ TEST_P(MultiviewRenderPrimitiveTest, Lines)
return; return;
} }
GLuint program = CreateSimplePassthroughProgram(); GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u); ASSERT_NE(program, 0u);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -1420,7 +1423,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineStrip) ...@@ -1420,7 +1423,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineStrip)
return; return;
} }
GLuint program = CreateSimplePassthroughProgram(); GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u); ASSERT_NE(program, 0u);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -1457,7 +1460,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineLoop) ...@@ -1457,7 +1460,7 @@ TEST_P(MultiviewRenderPrimitiveTest, LineLoop)
return; return;
} }
GLuint program = CreateSimplePassthroughProgram(); GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u); ASSERT_NE(program, 0u);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -1491,7 +1494,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleStrip) ...@@ -1491,7 +1494,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleStrip)
return; return;
} }
GLuint program = CreateSimplePassthroughProgram(); GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u); ASSERT_NE(program, 0u);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -1522,7 +1525,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleFan) ...@@ -1522,7 +1525,7 @@ TEST_P(MultiviewRenderPrimitiveTest, TriangleFan)
return; return;
} }
GLuint program = CreateSimplePassthroughProgram(); GLuint program = CreateSimplePassthroughProgram(2);
ASSERT_NE(program, 0u); ASSERT_NE(program, 0u);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -1760,6 +1763,60 @@ TEST_P(MultiviewRenderTest, ProgramRelinkUpdatesAttribDivisor) ...@@ -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() MultiviewTestParams SideBySideOpenGL()
{ {
return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, egl_platform::OPENGL()); return MultiviewTestParams(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, egl_platform::OPENGL());
...@@ -1776,9 +1833,17 @@ MultiviewTestParams SideBySideD3D11() ...@@ -1776,9 +1833,17 @@ MultiviewTestParams SideBySideD3D11()
} }
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL()); ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewRenderDualViewTest, SideBySideOpenGL(), LayeredOpenGL()); ANGLE_INSTANTIATE_TEST(MultiviewRenderDualViewTest,
ANGLE_INSTANTIATE_TEST(MultiviewRenderTest, SideBySideOpenGL(), LayeredOpenGL()); SideBySideOpenGL(),
ANGLE_INSTANTIATE_TEST(MultiviewOcclusionQueryTest, SideBySideOpenGL(), LayeredOpenGL()); 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(MultiviewProgramGenerationTest, SideBySideOpenGL(), SideBySideD3D11());
ANGLE_INSTANTIATE_TEST(MultiviewRenderPrimitiveTest, SideBySideOpenGL(), LayeredOpenGL()); ANGLE_INSTANTIATE_TEST(MultiviewRenderPrimitiveTest,
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL()); 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