Vulkan: Implement GL_EXT_multi_draw_indirect

* Optimized the implementations of multiDrawArraysIndirect()
and multiDrawElementsIndirect() for Vulkan
  * Added helper functions to support drawArraysIndirect() and
drawElementsIndirect() as special cases of multiDraw*Indirect
functions.
  * Added the flag to enable the multiDrawIndirect feature
(drawCount > 1). The generic implementation is used if the
flag is disabled.

Bug: angleproject:6439
Change-Id: Ibc653d93d355657f828de9c33da22428629e450f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3276044
Commit-Queue: Amirali Abdolrashidi <abdolrashidi@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Amirali Abdolrashidi
2021-11-15 08:51:41 -08:00
committed by Angle LUCI CQ
parent 6233dc7e8f
commit 79f9d163b4
6 changed files with 181 additions and 78 deletions

View File

@@ -193,6 +193,12 @@ struct FeaturesVk : FeatureSetBase
"VkDevice supports the VK_EXT_custom_border_color extension", &members,
"http://anglebug.com/3577"};
// Whether the VkDevice supports multiDrawIndirect (drawIndirect with drawCount > 1)
// http://anglebug.com/6439
Feature supportsMultiDrawIndirect = {
"supportsMultiDrawIndirect", FeatureCategory::VulkanFeatures,
"VkDevice supports the multiDrawIndirect extension", &members, "http://anglebug.com/6439"};
// Whether the VkDevice supports the VK_KHR_depth_stencil_resolve extension with the
// independentResolveNone feature.
// http://anglebug.com/4836

View File

@@ -140,6 +140,30 @@ constexpr size_t kDefaultValueSize = sizeof(gl::VertexAttribCurrent
constexpr size_t kDefaultBufferSize = kDefaultValueSize * 16;
constexpr size_t kDriverUniformsAllocatorPageSize = 4 * 1024;
bool CanMultiDrawIndirectUseCmd(ContextVk *contextVk,
VertexArrayVk *vertexArray,
gl::PrimitiveMode mode,
GLsizei drawcount,
GLsizei stride)
{
// Use the generic implementation if multiDrawIndirect is disabled, if line loop is being used
// for multiDraw, if drawcount is greater than maxDrawIndirectCount, or if there are streaming
// vertex attributes.
const bool supportsMultiDrawIndirect =
contextVk->getFeatures().supportsMultiDrawIndirect.enabled;
const bool isMultiDrawLineLoop = (mode == gl::PrimitiveMode::LineLoop && drawcount > 1);
const bool isDrawCountBeyondLimit =
(static_cast<uint32_t>(drawcount) >
contextVk->getRenderer()->getPhysicalDeviceProperties().limits.maxDrawIndirectCount);
const bool isMultiDrawWithStreamingAttribs =
(vertexArray->getStreamingVertexAttribsMask().any() && drawcount > 1);
const bool canMultiDrawIndirectUseCmd = supportsMultiDrawIndirect && !isMultiDrawLineLoop &&
!isDrawCountBeyondLimit &&
!isMultiDrawWithStreamingAttribs;
return canMultiDrawIndirectUseCmd;
}
uint32_t GetCoverageSampleCount(const gl::State &glState, FramebufferVk *drawFramebuffer)
{
if (!glState.isSampleCoverageEnabled())
@@ -2832,6 +2856,61 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect)
{
return multiDrawArraysIndirectHelper(context, mode, indirect, 1, 0);
}
angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect)
{
return multiDrawElementsIndirectHelper(context, mode, type, indirect, 1, 0);
}
angle::Result ContextVk::multiDrawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
GLsizei drawcount)
{
return rx::MultiDrawArraysGeneral(this, context, mode, firsts, counts, drawcount);
}
angle::Result ContextVk::multiDrawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawArraysInstancedGeneral(this, context, mode, firsts, counts, instanceCounts,
drawcount);
}
angle::Result ContextVk::multiDrawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
return multiDrawArraysIndirectHelper(context, mode, indirect, drawcount, stride);
}
angle::Result ContextVk::multiDrawArraysIndirectHelper(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
if (!rx::CanMultiDrawIndirectUseCmd(this, mVertexArray, mode, drawcount, stride))
{
return rx::MultiDrawArraysIndirectGeneral(this, context, mode, indirect, drawcount, stride);
}
// Stride must be a multiple of the size of VkDrawIndirectCommand (stride = 0 is invalid when
// drawcount > 1).
uint32_t vkStride = (stride == 0 && drawcount > 1) ? sizeof(VkDrawIndirectCommand) : stride;
gl::Buffer *indirectBuffer = mState.getTargetBuffer(gl::BufferBinding::DrawIndirect);
VkDeviceSize indirectBufferOffset = 0;
vk::BufferHelper *currentIndirectBuf =
@@ -2841,6 +2920,9 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
if (mVertexArray->getStreamingVertexAttribsMask().any())
{
// Handling instanced vertex attributes is not covered for drawcount > 1.
ASSERT(drawcount <= 1);
// We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush.
@@ -2859,6 +2941,9 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
if (mode == gl::PrimitiveMode::LineLoop)
{
// Line loop only supports handling at most one indirect parameter.
ASSERT(drawcount <= 1);
ASSERT(indirectBuffer);
vk::BufferHelper *dstIndirectBuf = nullptr;
VkDeviceSize dstIndirectBufOffset = 0;
@@ -2868,7 +2953,7 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
&dstIndirectBufOffset));
mRenderPassCommandBuffer->drawIndexedIndirect(dstIndirectBuf->getBuffer(),
dstIndirectBufOffset, 1, 0);
dstIndirectBufOffset, drawcount, vkStride);
return angle::Result::Continue;
}
@@ -2876,15 +2961,60 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
currentIndirectBufOffset));
mRenderPassCommandBuffer->drawIndirect(currentIndirectBuf->getBuffer(),
currentIndirectBufOffset, 1, 0);
currentIndirectBufOffset, drawcount, vkStride);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect)
angle::Result ContextVk::multiDrawElements(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
GLsizei drawcount)
{
return rx::MultiDrawElementsGeneral(this, context, mode, counts, type, indices, drawcount);
}
angle::Result ContextVk::multiDrawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawElementsInstancedGeneral(this, context, mode, counts, type, indices,
instanceCounts, drawcount);
}
angle::Result ContextVk::multiDrawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
return multiDrawElementsIndirectHelper(context, mode, type, indirect, drawcount, stride);
}
angle::Result ContextVk::multiDrawElementsIndirectHelper(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
if (!rx::CanMultiDrawIndirectUseCmd(this, mVertexArray, mode, drawcount, stride))
{
return rx::MultiDrawElementsIndirectGeneral(this, context, mode, type, indirect, drawcount,
stride);
}
// Stride must be a multiple of the size of VkDrawIndexedIndirectCommand (stride = 0 is invalid
// when drawcount > 1).
uint32_t vkStride =
(stride == 0 && drawcount > 1) ? sizeof(VkDrawIndexedIndirectCommand) : stride;
gl::Buffer *indirectBuffer = mState.getTargetBuffer(gl::BufferBinding::DrawIndirect);
ASSERT(indirectBuffer);
VkDeviceSize indirectBufferOffset = 0;
@@ -2895,6 +3025,9 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
if (mVertexArray->getStreamingVertexAttribsMask().any())
{
// Handling instanced vertex attributes is not covered for drawcount > 1.
ASSERT(drawcount <= 1);
// We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush.
@@ -2932,6 +3065,9 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
if (mode == gl::PrimitiveMode::LineLoop)
{
// Line loop only supports handling at most one indirect parameter.
ASSERT(drawcount <= 1);
vk::BufferHelper *dstIndirectBuf;
VkDeviceSize dstIndirectBufOffset;
@@ -2949,72 +3085,10 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
}
mRenderPassCommandBuffer->drawIndexedIndirect(currentIndirectBuf->getBuffer(),
currentIndirectBufOffset, 1, 0);
currentIndirectBufOffset, drawcount, vkStride);
return angle::Result::Continue;
}
angle::Result ContextVk::multiDrawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
GLsizei drawcount)
{
return rx::MultiDrawArraysGeneral(this, context, mode, firsts, counts, drawcount);
}
angle::Result ContextVk::multiDrawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawArraysInstancedGeneral(this, context, mode, firsts, counts, instanceCounts,
drawcount);
}
angle::Result ContextVk::multiDrawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
return rx::MultiDrawArraysIndirectGeneral(this, context, mode, indirect, drawcount, stride);
}
angle::Result ContextVk::multiDrawElements(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
GLsizei drawcount)
{
return rx::MultiDrawElementsGeneral(this, context, mode, counts, type, indices, drawcount);
}
angle::Result ContextVk::multiDrawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawElementsInstancedGeneral(this, context, mode, counts, type, indices,
instanceCounts, drawcount);
}
angle::Result ContextVk::multiDrawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
return rx::MultiDrawElementsIndirectGeneral(this, context, mode, type, indirect, drawcount,
stride);
}
angle::Result ContextVk::multiDrawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,

View File

@@ -207,6 +207,19 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
const GLuint *baseInstances,
GLsizei drawcount) override;
// MultiDrawIndirect helper functions
angle::Result multiDrawElementsIndirectHelper(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect,
GLsizei drawcount,
GLsizei stride);
angle::Result multiDrawArraysIndirectHelper(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect,
GLsizei drawcount,
GLsizei stride);
// ShareGroup
ShareGroupVk *getShareGroupVk() { return mShareGroupVk; }
PipelineLayoutCache &getPipelineLayoutCache()

View File

@@ -1949,6 +1949,8 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
mEnabledFeatures.features.imageCubeArray = getFeatures().supportsImageCubeArray.enabled;
// Used to support framebuffers with multiple attachments:
mEnabledFeatures.features.independentBlend = mPhysicalDeviceFeatures.independentBlend;
// Used to support multi_draw_indirect
mEnabledFeatures.features.multiDrawIndirect = mPhysicalDeviceFeatures.multiDrawIndirect;
// Used to support robust buffer access:
mEnabledFeatures.features.robustBufferAccess = mPhysicalDeviceFeatures.robustBufferAccess;
// Used to support Anisotropic filtering:
@@ -2687,6 +2689,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
mCustomBorderColorFeatures.customBorderColors == VK_TRUE &&
mCustomBorderColorFeatures.customBorderColorWithoutFormat == VK_TRUE && !isSwiftShader);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsMultiDrawIndirect,
mPhysicalDeviceFeatures.multiDrawIndirect == VK_TRUE);
ANGLE_FEATURE_CONDITION(&mFeatures, disableFifoPresentMode, IsLinux() && isIntel);
ANGLE_FEATURE_CONDITION(&mFeatures, bindEmptyForUnusedDescriptorSets,

View File

@@ -357,7 +357,8 @@ void SecondaryCommandBuffer::executeCommands(PrimaryCommandBuffer *primary)
{
const DrawIndexedIndirectParams *params =
getParamPtr<DrawIndexedIndirectParams>(currentCommand);
vkCmdDrawIndexedIndirect(cmdBuffer, params->buffer, params->offset, 1, 0);
vkCmdDrawIndexedIndirect(cmdBuffer, params->buffer, params->offset,
params->drawCount, params->stride);
break;
}
case CommandID::DrawIndexedInstanced:
@@ -389,7 +390,8 @@ void SecondaryCommandBuffer::executeCommands(PrimaryCommandBuffer *primary)
{
const DrawIndirectParams *params =
getParamPtr<DrawIndirectParams>(currentCommand);
vkCmdDrawIndirect(cmdBuffer, params->buffer, params->offset, 1, 0);
vkCmdDrawIndirect(cmdBuffer, params->buffer, params->offset, params->drawCount,
params->stride);
break;
}
case CommandID::DrawInstanced:

View File

@@ -263,6 +263,8 @@ struct DrawIndexedIndirectParams
{
VkBuffer buffer;
VkDeviceSize offset;
uint32_t drawCount;
uint32_t stride;
};
VERIFY_4_BYTE_ALIGNMENT(DrawIndexedIndirectParams)
@@ -295,6 +297,8 @@ struct DrawIndirectParams
{
VkBuffer buffer;
VkDeviceSize offset;
uint32_t drawCount;
uint32_t stride;
};
VERIFY_4_BYTE_ALIGNMENT(DrawIndirectParams)
@@ -1187,9 +1191,10 @@ ANGLE_INLINE void SecondaryCommandBuffer::drawIndexedIndirect(const Buffer &buff
{
DrawIndexedIndirectParams *paramStruct =
initCommand<DrawIndexedIndirectParams>(CommandID::DrawIndexedIndirect);
paramStruct->buffer = buffer.getHandle();
paramStruct->offset = offset;
ASSERT(drawCount == 1);
paramStruct->buffer = buffer.getHandle();
paramStruct->offset = offset;
paramStruct->drawCount = drawCount;
paramStruct->stride = stride;
mCommandTracker.onDraw();
}
@@ -1246,10 +1251,8 @@ ANGLE_INLINE void SecondaryCommandBuffer::drawIndirect(const Buffer &buffer,
DrawIndirectParams *paramStruct = initCommand<DrawIndirectParams>(CommandID::DrawIndirect);
paramStruct->buffer = buffer.getHandle();
paramStruct->offset = offset;
// OpenGL ES doesn't have a way to specify a drawCount or stride, throw assert if something
// changes.
ASSERT(drawCount == 1);
paramStruct->drawCount = drawCount;
paramStruct->stride = stride;
mCommandTracker.onDraw();
}