Skip to content

Commit

Permalink
Added multi-draw commands (API254006)
Browse files Browse the repository at this point in the history
Implemented emulated multi-draw in D3D11, D3D12 and Vulkan
  • Loading branch information
TheMostDiligent committed Feb 23, 2024
1 parent a626300 commit d43ea76
Show file tree
Hide file tree
Showing 21 changed files with 443 additions and 24 deletions.
59 changes: 58 additions & 1 deletion Graphics/GraphicsEngine/include/DeviceContextBase.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2023 Diligent Graphics LLC
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -57,6 +57,8 @@ bool VerifyDrawAttribs (const DrawAttribs& Attribs)
bool VerifyDrawIndexedAttribs (const DrawIndexedAttribs& Attribs);
bool VerifyDrawIndirectAttribs (const DrawIndirectAttribs& Attribs);
bool VerifyDrawIndexedIndirectAttribs(const DrawIndexedIndirectAttribs& Attribs);
bool VerifyMultiDrawAttribs (const MultiDrawAttribs& Attribs);
bool VerifyMultiDrawIndexedAttribs (const MultiDrawIndexedAttribs& Attribs);

bool VerifyDispatchComputeAttribs (const DispatchComputeAttribs& Attribs);
bool VerifyDispatchComputeIndirectAttribs(const DispatchComputeIndirectAttribs& Attribs);
Expand Down Expand Up @@ -557,6 +559,8 @@ class DeviceContextBase : public ObjectBase<typename EngineImplTraits::DeviceCon
void DrawIndexedIndirect(const DrawIndexedIndirectAttribs& Attribs, int);
void DrawMesh(const DrawMeshAttribs& Attribs, int);
void DrawMeshIndirect(const DrawMeshIndirectAttribs& Attribs, int);
void MultiDraw(const MultiDrawAttribs& Attribs, int);
void MultiDrawIndexed(const MultiDrawIndexedAttribs& Attribs, int);
void DispatchCompute(const DispatchComputeAttribs& Attribs, int);
void DispatchComputeIndirect(const DispatchComputeIndirectAttribs& Attribs, int);

Expand Down Expand Up @@ -2363,6 +2367,59 @@ inline void DeviceContextBase<ImplementationTraits>::DrawMeshIndirect(const Draw
++m_Stats.CommandCounters.DrawMeshIndirect;
}

template <typename ImplementationTraits>
inline void DeviceContextBase<ImplementationTraits>::MultiDraw(const MultiDrawAttribs& Attribs, int)
{
#ifdef DILIGENT_DEVELOPMENT
if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) != 0)
{
DVP_CHECK_QUEUE_TYPE_COMPATIBILITY(COMMAND_QUEUE_TYPE_GRAPHICS, "MultiDraw");

DEV_CHECK_ERR(m_pPipelineState, "MultiDraw command arguments are invalid: no pipeline state is bound.");

DEV_CHECK_ERR(m_pPipelineState->GetDesc().PipelineType == PIPELINE_TYPE_GRAPHICS,
"MultiDraw command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline.");

DEV_CHECK_ERR(VerifyMultiDrawAttribs(Attribs), "MultiDrawAttribs are invalid");
}
#endif
if (m_pPipelineState)
{
const auto Topology = m_pPipelineState->GetGraphicsPipelineDesc().PrimitiveTopology;
for (Uint32 i = 0; i < Attribs.DrawCount; ++i)
m_Stats.PrimitiveCounts[Topology] += GetPrimitiveCount(Topology, Attribs.pDrawItems[i].NumVertices);
}
++m_Stats.CommandCounters.MultiDraw;
}

template <typename ImplementationTraits>
inline void DeviceContextBase<ImplementationTraits>::MultiDrawIndexed(const MultiDrawIndexedAttribs& Attribs, int)
{
#ifdef DILIGENT_DEVELOPMENT
if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) != 0)
{
DVP_CHECK_QUEUE_TYPE_COMPATIBILITY(COMMAND_QUEUE_TYPE_GRAPHICS, "MultiDrawIndexed");

DEV_CHECK_ERR(m_pPipelineState, "MultiDrawIndexed command arguments are invalid: no pipeline state is bound.");

DEV_CHECK_ERR(m_pPipelineState->GetDesc().PipelineType == PIPELINE_TYPE_GRAPHICS,
"MultiDrawIndexed command arguments are invalid: pipeline state '",
m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline.");

DEV_CHECK_ERR(m_pIndexBuffer, "MultiDrawIndexed command arguments are invalid: no index buffer is bound.");

DEV_CHECK_ERR(VerifyMultiDrawIndexedAttribs(Attribs), "MultiDrawIndexedAttribs are invalid");
}
#endif
if (m_pPipelineState)
{
const auto Topology = m_pPipelineState->GetGraphicsPipelineDesc().PrimitiveTopology;
for (Uint32 i = 0; i < Attribs.DrawCount; ++i)
m_Stats.PrimitiveCounts[Topology] += GetPrimitiveCount(Topology, Attribs.pDrawItems[i].NumIndices);
}
++m_Stats.CommandCounters.MultiDrawIndexed;
}

#ifdef DILIGENT_DEVELOPMENT
template <typename ImplementationTraits>
inline void DeviceContextBase<ImplementationTraits>::DvpVerifyRenderTargets() const
Expand Down
2 changes: 1 addition & 1 deletion Graphics/GraphicsEngine/interface/APIInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
/// \file
/// Diligent API information

#define DILIGENT_API_VERSION 254005
#define DILIGENT_API_VERSION 254006

#include "../../../Primitives/interface/BasicTypes.h"

Expand Down
145 changes: 144 additions & 1 deletion Graphics/GraphicsEngine/interface/DeviceContext.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2023 Diligent Graphics LLC
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -652,6 +652,117 @@ struct DrawMeshIndirectAttribs
typedef struct DrawMeshIndirectAttribs DrawMeshIndirectAttribs;


/// Multi-draw command item.
struct MultiDrawItem
{
/// The number of vertices to draw.
Uint32 NumVertices DEFAULT_INITIALIZER(0);

/// LOCATION (or INDEX, but NOT the byte offset) of the first vertex in the
/// vertex buffer to start reading vertices from.
Uint32 StartVertexLocation DEFAULT_INITIALIZER(0);
};
typedef struct MultiDrawItem MultiDrawItem;

/// MultiDraw command attributes.
struct MultiDrawAttribs
{
/// The number of draw items to execute.
Uint32 DrawCount DEFAULT_INITIALIZER(0);

/// A pointer to the array of DrawCount draw command items.
const MultiDrawItem* pDrawItems DEFAULT_INITIALIZER(nullptr);

/// Additional flags, see Diligent::DRAW_FLAGS.
DRAW_FLAGS Flags DEFAULT_INITIALIZER(DRAW_FLAG_NONE);

/// The number of instances to draw. If more than one instance is specified,
/// instanced draw call will be performed.
Uint32 NumInstances DEFAULT_INITIALIZER(1);

/// LOCATION (or INDEX, but NOT the byte offset) in the vertex buffer to start
/// reading instance data from.
Uint32 FirstInstanceLocation DEFAULT_INITIALIZER(0);

#if DILIGENT_CPP_INTERFACE
constexpr MultiDrawAttribs() noexcept {}

constexpr MultiDrawAttribs(Uint32 _DrawCount,
const MultiDrawItem* _pDrawItems,
DRAW_FLAGS _Flags,
Uint32 _NumInstances = 1,
Uint32 _FirstInstanceLocation = 0) noexcept :
DrawCount {_DrawCount },
pDrawItems {_pDrawItems },
Flags {_Flags },
NumInstances {_NumInstances },
FirstInstanceLocation{_FirstInstanceLocation}
{}
#endif
};
typedef struct MultiDrawAttribs MultiDrawAttribs;


/// Multi-draw indexed command item.
struct MultiDrawIndexedItem
{
/// The number of indices to draw.
Uint32 NumIndices DEFAULT_INITIALIZER(0);

/// LOCATION (NOT the byte offset) of the first index in
/// the index buffer to start reading indices from.
Uint32 FirstIndexLocation DEFAULT_INITIALIZER(0);

/// A constant which is added to each index before accessing the vertex buffer.
Uint32 BaseVertex DEFAULT_INITIALIZER(0);
};
typedef struct MultiDrawIndexedItem MultiDrawIndexedItem;

/// MultiDraw command attributes.
struct MultiDrawIndexedAttribs
{
/// The number of draw items to execute.
Uint32 DrawCount DEFAULT_INITIALIZER(0);

/// A pointer to the array of DrawCount draw command items.
const MultiDrawIndexedItem* pDrawItems DEFAULT_INITIALIZER(nullptr);

/// The type of elements in the index buffer.
/// Allowed values: VT_UINT16 and VT_UINT32.
VALUE_TYPE IndexType DEFAULT_INITIALIZER(VT_UNDEFINED);

/// Additional flags, see Diligent::DRAW_FLAGS.
DRAW_FLAGS Flags DEFAULT_INITIALIZER(DRAW_FLAG_NONE);

/// Number of instances to draw. If more than one instance is specified,
/// instanced draw call will be performed.
Uint32 NumInstances DEFAULT_INITIALIZER(1);

/// LOCATION (or INDEX, but NOT the byte offset) in the vertex
/// buffer to start reading instance data from.
Uint32 FirstInstanceLocation DEFAULT_INITIALIZER(0);

#if DILIGENT_CPP_INTERFACE
constexpr MultiDrawIndexedAttribs() noexcept {}

constexpr MultiDrawIndexedAttribs(Uint32 _DrawCount,
const MultiDrawIndexedItem* _pDrawItems,
VALUE_TYPE _IndexType,
DRAW_FLAGS _Flags,
Uint32 _NumInstances = 1,
Uint32 _FirstInstanceLocation = 0) noexcept :
DrawCount {_DrawCount },
pDrawItems {_pDrawItems },
IndexType {_IndexType },
Flags {_Flags },
NumInstances {_NumInstances },
FirstInstanceLocation{_FirstInstanceLocation}
{}
#endif
};
typedef struct MultiDrawIndexedAttribs MultiDrawIndexedAttribs;


/// Defines which parts of the depth-stencil buffer to clear.

/// These flags are used by IDeviceContext::ClearDepthStencil().
Expand Down Expand Up @@ -2126,6 +2237,12 @@ struct DeviceContextCommandCounters
/// The total number of indexed indirect DrawIndexedIndirect calls.
Uint32 DrawIndexedIndirect DEFAULT_INITIALIZER(0);

/// The total number of MultiDraw calls.
Uint32 MultiDraw DEFAULT_INITIALIZER(0);

/// The total number of MultiDrawIndexed calls.
Uint32 MultiDrawIndexed DEFAULT_INITIALIZER(0);

/// The total number of DispatchCompute calls.
Uint32 DispatchCompute DEFAULT_INITIALIZER(0);

Expand Down Expand Up @@ -2686,6 +2803,32 @@ DILIGENT_BEGIN_INTERFACE(IDeviceContext, IObject)
const DrawMeshIndirectAttribs REF Attribs) PURE;


/// Executes a multi-draw command.

/// \param [in] Attribs - Multi-draw command attributes, see Diligent::MultiDrawAttribs for details.
///
/// \remarks If the device does not support the NativeMultiDraw feature, the method will emulate it by
/// issuing a sequence of individual draw commands. Note that draw command index is only
/// available in the shader when the NativeMultiDraw feature is supported.
///
/// \remarks Supported contexts: graphics.
VIRTUAL void METHOD(MultiDraw)(THIS_
const MultiDrawAttribs REF Attribs) PURE;


/// Executes an indexed multi-draw command.

/// \param [in] Attribs - Multi-draw command attributes, see Diligent::MultiDrawIndexedAttribs for details.
///
/// \remarks If the device does not support the NativeMultiDraw feature, the method will emulate it by
/// issuing a sequence of individual draw commands. Note that draw command index is only
/// available in the shader when the NativeMultiDraw feature is supported.
///
/// \remarks Supported contexts: graphics.
VIRTUAL void METHOD(MultiDrawIndexed)(THIS_
const MultiDrawIndexedAttribs REF Attribs) PURE;


/// Executes a dispatch compute command.

/// \param [in] Attribs - Dispatch command attributes, see Diligent::DispatchComputeAttribs for details.
Expand Down
16 changes: 14 additions & 2 deletions Graphics/GraphicsEngine/interface/GraphicsTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,17 @@ struct DeviceFeatures
/// texture can be created.
DEVICE_FEATURE_STATE TextureSubresourceViews DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED);

/// Indicates if device supports native multi-draw commands.
///
/// \remarks When this feature is enabled, the GPU supports a dedicated command that
/// can be used to issue multiple draw calls with a single command (e.g. vkCmdDrawMultiEXT,
/// glMultiDrawElements, etc.). In OpenGL and Vulkan, the shader can access the draw
/// command index using the gl_DrawID built-in variable.
///
/// When this feature is disabled, the engine emulates multi-draw commands by issuing
/// multiple individual draw calls. The draw command index is unavailable.
DEVICE_FEATURE_STATE NativeMultiDraw DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED);

#if DILIGENT_CPP_INTERFACE
constexpr DeviceFeatures() noexcept {}

Expand Down Expand Up @@ -1842,11 +1853,12 @@ struct DeviceFeatures
Handler(SparseResources) \
Handler(SubpassFramebufferFetch) \
Handler(TextureComponentSwizzle) \
Handler(TextureSubresourceViews)
Handler(TextureSubresourceViews) \
Handler(NativeMultiDraw)

explicit constexpr DeviceFeatures(DEVICE_FEATURE_STATE State) noexcept
{
static_assert(sizeof(*this) == 42, "Did you add a new feature to DeviceFeatures? Please add it to ENUMERATE_DEVICE_FEATURES.");
static_assert(sizeof(*this) == 43, "Did you add a new feature to DeviceFeatures? Please add it to ENUMERATE_DEVICE_FEATURES.");
#define INIT_FEATURE(Feature) Feature = State;
ENUMERATE_DEVICE_FEATURES(INIT_FEATURE)
#undef INIT_FEATURE
Expand Down
28 changes: 27 additions & 1 deletion Graphics/GraphicsEngine/src/DeviceContextBase.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2023 Diligent Graphics LLC
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -215,6 +215,32 @@ bool VerifyDrawMeshIndirectAttribs(const DrawMeshIndirectAttribs& Attribs, Uint3
return true;
}

bool VerifyMultiDrawAttribs(const MultiDrawAttribs& Attribs)
{
DEV_CHECK_ERR(Attribs.DrawCount == 0 || Attribs.pDrawItems != nullptr, "DrawCount is ", Attribs.DrawCount, ", but pDrawItems is null.");

if (Attribs.NumInstances == 0)
LOG_INFO_MESSAGE("MultiDrawAttribs.NumInstances is 0. This is OK as the draw command will be ignored, but may be unintentional.");

return true;
}

bool VerifyMultiDrawIndexedAttribs(const MultiDrawIndexedAttribs& Attribs)
{
DEV_CHECK_ERR(Attribs.DrawCount == 0 || Attribs.pDrawItems != nullptr, "DrawCount is ", Attribs.DrawCount, ", but pDrawItems is null.");

#define CHECK_MULTI_DRAW_INDEXED_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indexed attribs are invalid: ", __VA_ARGS__)

CHECK_MULTI_DRAW_INDEXED_ATTRIBS(Attribs.IndexType == VT_UINT16 || Attribs.IndexType == VT_UINT32,
"IndexType (", GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32.");

if (Attribs.NumInstances == 0)
LOG_INFO_MESSAGE("MultiDrawAttribs.NumInstances is 0. This is OK as the draw command will be ignored, but may be unintentional.");

#undef CHECK_MULTI_DRAW_INDEXED_ATTRIBS

return true;
}

bool VerifyDispatchComputeAttribs(const DispatchComputeAttribs& Attribs)
{
Expand Down
3 changes: 2 additions & 1 deletion Graphics/GraphicsEngine/src/RenderDeviceBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ DeviceFeatures EnableDeviceFeatures(const DeviceFeatures& SupportedFeatures,
ENABLE_FEATURE(SubpassFramebufferFetch, "Subpass framebuffer fetch is");
ENABLE_FEATURE(TextureComponentSwizzle, "Texture component swizzle is");
ENABLE_FEATURE(TextureSubresourceViews, "Texture subresource views are");
ENABLE_FEATURE(NativeMultiDraw, "Native multi-draw commands are");
// clang-format on
#undef ENABLE_FEATURE

ASSERT_SIZEOF(Diligent::DeviceFeatures, 42, "Did you add a new feature to DeviceFeatures? Please handle its status here (if necessary).");
ASSERT_SIZEOF(Diligent::DeviceFeatures, 43, "Did you add a new feature to DeviceFeatures? Please handle its status here (if necessary).");

return EnabledFeatures;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Diligent Graphics LLC
* Copyright 2019-2024 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -133,6 +133,10 @@ class DeviceContextD3D11Impl final : public DeviceContextBase<EngineD3D11ImplTra
virtual void DILIGENT_CALL_TYPE DrawMesh(const DrawMeshAttribs& Attribs) override final;
/// Implementation of IDeviceContext::DrawMeshIndirect() in Direct3D11 backend.
virtual void DILIGENT_CALL_TYPE DrawMeshIndirect(const DrawMeshIndirectAttribs& Attribs) override final;
/// Implementation of IDeviceContext::MultiDraw() in Direct3D11 backend.
virtual void DILIGENT_CALL_TYPE MultiDraw(const MultiDrawAttribs& Attribs) override final;
/// Implementation of IDeviceContext::MultiDrawIndexed() in Direct3D11 backend.
virtual void DILIGENT_CALL_TYPE MultiDrawIndexed(const MultiDrawIndexedAttribs& Attribs) override final;

/// Implementation of IDeviceContext::DispatchCompute() in Direct3D11 backend.
virtual void DILIGENT_CALL_TYPE DispatchCompute(const DispatchComputeAttribs& Attribs) override final;
Expand Down
Loading

0 comments on commit d43ea76

Please sign in to comment.