diff --git a/include/platform/FeaturesVk.h b/include/platform/FeaturesVk.h index 58f1bf536..8d770ea61 100644 --- a/include/platform/FeaturesVk.h +++ b/include/platform/FeaturesVk.h @@ -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 diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp index eabc6f9ed..9d54d6669 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.cpp +++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp @@ -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(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, diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h index c0a98a82e..fac0552b8 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.h +++ b/src/libANGLE/renderer/vulkan/ContextVk.h @@ -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() diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp index 815dc265b..679f56485 100644 --- a/src/libANGLE/renderer/vulkan/RendererVk.cpp +++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp @@ -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, diff --git a/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp b/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp index 6ef776864..924c2b132 100644 --- a/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp +++ b/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp @@ -357,7 +357,8 @@ void SecondaryCommandBuffer::executeCommands(PrimaryCommandBuffer *primary) { const DrawIndexedIndirectParams *params = getParamPtr(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(currentCommand); - vkCmdDrawIndirect(cmdBuffer, params->buffer, params->offset, 1, 0); + vkCmdDrawIndirect(cmdBuffer, params->buffer, params->offset, params->drawCount, + params->stride); break; } case CommandID::DrawInstanced: diff --git a/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h b/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h index 6bc48940a..8b5b9ad06 100644 --- a/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h +++ b/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h @@ -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(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(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(); }