Draws indexed primitives as a more efficient alternative to DrawIndexedInstanced.
public:
void DrawIndexedX(
D3D12_GPU_VIRTUAL_ADDRESS IndexBufferLocation,
DXGI_FORMAT IndexFormat,
UINT IndexCount,
UINT IndexOffset
)
IndexBufferLocation
Type: D3D12_GPU_VIRTUAL_ADDRESS
[in] The GPU virtual address of the index buffer to be used for this draw.
IndexFormat
Type: DXGI_FORMAT
[in] A DXGI_FORMAT value for the index-buffer format. Can be either DXGI_FORMAT_R16_UINT or DXGI_FORMAT_R32_UINT.
IndexCount
Type: UINT
[in] The number of indices to read from the index buffer.
IndexOffset
Type: UINT
[in] A 32-bit offset that is added to every index as it is read from the index buffer. Its usage is similar to BaseVertexLocation as specified to DrawIndexedInstanced except that it also offsets the value returned by SV_VertexID. This latter property can be useful as a draw identifier when not using an input layout for the vertex shader.
Type: void
This API can achieve better performance than DrawIndexedInstanced by packing shader waves of consecutive DrawIndexedX calls. The wave packing happens automatically when no other state changes are done in between DrawIndexedX invocations; if state changes are done in between, then the draws execute as normal in isolation without wave packing. In order to benefit from wave packing, then, only the index buffer and index format can change from call to call.
The expected usage is to define some number of bits of SV_VertexID as a draw identifier, and to use IndexOffset to specify that identifier to the shader. This usage implies that input layouts cannot be used and the vertex shader will have to manually fetch the vertices. Intrinsics for unpacking on load of vertex formats can be used for this purpose.
Depth-only rendering is typically the most straightforward scenario to adapt to the wave packing property of DrawIndexedX. Vertex data must be accessible to all draws (as with a single big buffer spanning all vertex data), and per-draw arguments should be in global arrays. Color rendering can also benefit, but this most likely requires dynamic indexing of textures or use of texture atlases to avoid state changes between DrawIndexedX calls.
DrawIndexedX leaves the current index buffer in an undefined state so IASetIndexBuffer must be called before any subsequent DrawIndexedInstanced calls.
Note, this API enables the application to decide how many bits should be used for indexing vertices and how many bits should be used for the instance index. For example, you could do instancing with 16 bit indices by issuing a series of draw calls adding 65536 each time to IndexOffset, then in the shader:
instanceId = SV_VertexID >> 16;
realVertexId = SV_VertexID & 0xffff;
It’s up to the application how many bits to reserve for instances and how many vertices to pack into a vertex buffer. This creates a trade off, you can pack more meshes in but then you get a smaller maximum instance count. If you do go for more vertices however two things happen, you might need 32 bit indices, and you might have to loop N times because you overflow the maximum number of instances. Looping should not be a big issue, as you’ll still have the majority of the benefit from wave packing.
Another scenario is that you could pack even more information into IndexOffset, passing per instance data to the shader for example:
vertexShaderFog = SV_VertexId & 0x80000000;
instanceId = (SV_VertexID & 0x7fffffff) >> 16;
realVertexId = SV_VertexID & 0xffff;
Header: Declared in d3d12_x.h.
Library: Use d3d12_x.lib.