应用使用 ExecuteIndirect 方法执行间接绘制/调度。
语法
void ExecuteIndirect(
  [in]           ID3D12CommandSignature *pCommandSignature,
  [in]           UINT                   MaxCommandCount,
  [in]           ID3D12Resource         *pArgumentBuffer,
  [in]           UINT64                 ArgumentBufferOffset,
  [in, optional] ID3D12Resource         *pCountBuffer,
  [in]           UINT64                 CountBufferOffset
);
参数
[in] pCommandSignature
指定 ID3D12CommandSignature。 pArgumentBuffer 引用的数据将根据命令签名的内容进行解释。 有关用于创建命令签名的 API,请参阅 间接绘图 。
[in] MaxCommandCount
类型: UINT
可通过两种方式指定命令计数:
- 如果 pCountBuffer 不为 NULL,则 MaxCommandCount 指定将执行的最大操作数。 要执行的实际操作数由此值的最小值定义, pCountBuffer 中包含的 32 位无符号整数 (CountBufferOffset) 指定的字节偏移量。
- 如果 pCountBuffer 为 NULL, 则 MaxCommandCount 指定将执行的确切操作数。
[in] pArgumentBuffer
类型: ID3D12Resource*
指定一个或多个 ID3D12Resource 对象,其中包含命令参数。
[in] ArgumentBufferOffset
类型: UINT64
指定 pArgumentBuffer 中的偏移量,以标识第一个命令参数。
[in, optional] pCountBuffer
类型: ID3D12Resource*
指定指向 ID3D12Resource 的指针。
[in] CountBufferOffset
类型: UINT64
指定一个 UINT64,该 UINT64 是 pCountBuffer 的偏移量,用于标识参数计数。
返回值
无
备注
此 API 的语义使用以下伪代码定义:
非 NULL pCountBuffer:
// Read draw count out of count buffer
UINT CommandCount = pCountBuffer->ReadUINT32(CountBufferOffset);
CommandCount = min(CommandCount, MaxCommandCount)
// Get pointer to first Commanding argument
BYTE* Arguments = pArgumentBuffer->GetBase() + ArgumentBufferOffset;
for(UINT CommandIndex = 0; CommandIndex < CommandCount; CommandIndex++)
{
  // Interpret the data contained in *Arguments
  // according to the command signature
  pCommandSignature->Interpret(Arguments);
  Arguments += pCommandSignature->GetByteStride();
}
NULL pCountBuffer:
// Get pointer to first Commanding argument
BYTE* Arguments = pArgumentBuffer->GetBase() + ArgumentBufferOffset;
for(UINT CommandIndex = 0; CommandIndex < MaxCommandCount; CommandIndex++)
{
  // Interpret the data contained in *Arguments
  // according to the command signature
  pCommandSignature->Interpret(Arguments);
  Arguments += pCommandSignature->GetByteStride();
}
如果计数缓冲区或参数缓冲区不处于D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT状态,调试层将发出错误。 核心运行时将验证:
- CountBufferOffset 和 ArgumentBufferOffset 采用 4 字节对齐
- pCountBuffer 和 pArgumentBuffer 是任何堆类型 (缓冲区资源)
- MaxCommandCount、ArgumentBufferOffset 和绘图程序步幅隐含的偏移量不超过 pArgumentBuffer (类似的计数缓冲区)
- 命令列表是直接命令列表或计算命令列表, (不是复制或 JPEG 解码命令列表)
- 命令列表的根签名与命令签名的根签名匹配
DrawInstancedIndirect 和 DrawIndexedInstancedIndirect的两个 API 的功能包含在 ExecuteIndirect 中。
          
束
ID3D12GraphicsCommandList::ExecuteIndirect 仅当以下所有项均为 true 时,才允许在捆绑包命令列表中使用:- CountBuffer 为 NULL, (CPU 指定的计数仅) 。
- 命令签名只包含一个操作。 这意味着命令签名不包含根参数更改,也不包含 VB/IB 绑定更改。
获取缓冲区虚拟地址
ID3D12Resource::GetGPUVirtualAddress 方法使应用能够检索缓冲区的 GPU 虚拟地址。应用可以自由地将字节偏移量应用于虚拟地址,然后再将其置于间接参数缓冲区中。 请注意,VB/IB/CB 的所有 D3D12 对齐要求仍适用于生成的 GPU 虚拟地址。
示例
D3D12ExecuteIndirect 示例使用 ID3D12GraphicsCommandList::ExecuteIndirect,如下所示:
// Data structure to match the command signature used for ExecuteIndirect.
struct IndirectCommand
{
    D3D12_GPU_VIRTUAL_ADDRESS cbv;
    D3D12_DRAW_ARGUMENTS drawArguments;
};
对 ExecuteIndirect 的调用已接近此列表的末尾,位于注释“绘制尚未剔除的三角形”下方。
// Fill the command list with all the render commands and dependent state.
void D3D12ExecuteIndirect::PopulateCommandLists()
{
    // Command list allocators can only be reset when the associated 
    // command lists have finished execution on the GPU; apps should use 
    // fences to determine GPU execution progress.
    ThrowIfFailed(m_computeCommandAllocators[m_frameIndex]->Reset());
    ThrowIfFailed(m_commandAllocators[m_frameIndex]->Reset());
    // However, when ExecuteCommandList() is called on a particular command 
    // list, that command list can then be reset at any time and must be before 
    // re-recording.
    ThrowIfFailed(m_computeCommandList->Reset(m_computeCommandAllocators[m_frameIndex].Get(), m_computeState.Get()));
    ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), m_pipelineState.Get()));
    // Record the compute commands that will cull triangles and prevent them from being processed by the vertex shader.
    if (m_enableCulling)
    {
        UINT frameDescriptorOffset = m_frameIndex * CbvSrvUavDescriptorCountPerFrame;
        D3D12_GPU_DESCRIPTOR_HANDLE cbvSrvUavHandle = m_cbvSrvUavHeap->GetGPUDescriptorHandleForHeapStart();
        m_computeCommandList->SetComputeRootSignature(m_computeRootSignature.Get());
        ID3D12DescriptorHeap* ppHeaps[] = { m_cbvSrvUavHeap.Get() };
        m_computeCommandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
        m_computeCommandList->SetComputeRootDescriptorTable(
            SrvUavTable,
            CD3DX12_GPU_DESCRIPTOR_HANDLE(cbvSrvUavHandle, CbvSrvOffset + frameDescriptorOffset, m_cbvSrvUavDescriptorSize));
        m_computeCommandList->SetComputeRoot32BitConstants(RootConstants, 4, reinterpret_cast<void*>(&m_csRootConstants), 0);
        // Reset the UAV counter for this frame.
        m_computeCommandList->CopyBufferRegion(m_processedCommandBuffers[m_frameIndex].Get(), CommandBufferSizePerFrame, m_processedCommandBufferCounterReset.Get(), 0, sizeof(UINT));
        D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_processedCommandBuffers[m_frameIndex].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        m_computeCommandList->ResourceBarrier(1, &barrier);
        m_computeCommandList->Dispatch(static_cast<UINT>(ceil(TriangleCount / float(ComputeThreadBlockSize))), 1, 1);
    }
    ThrowIfFailed(m_computeCommandList->Close());
    // Record the rendering commands.
    {
        // Set necessary state.
        m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
        ID3D12DescriptorHeap* ppHeaps[] = { m_cbvSrvUavHeap.Get() };
        m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
        m_commandList->RSSetViewports(1, &m_viewport);
        m_commandList->RSSetScissorRects(1, m_enableCulling ? &m_cullingScissorRect : &m_scissorRect);
        // Indicate that the command buffer will be used for indirect drawing
        // and that the back buffer will be used as a render target.
        D3D12_RESOURCE_BARRIER barriers[2] = {
            CD3DX12_RESOURCE_BARRIER::Transition(
                m_enableCulling ? m_processedCommandBuffers[m_frameIndex].Get() : m_commandBuffer.Get(),
                m_enableCulling ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
                D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT),
            CD3DX12_RESOURCE_BARRIER::Transition(
                m_renderTargets[m_frameIndex].Get(),
                D3D12_RESOURCE_STATE_PRESENT,
                D3D12_RESOURCE_STATE_RENDER_TARGET)
        };
        m_commandList->ResourceBarrier(_countof(barriers), barriers);
        CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
        CD3DX12_CPU_DESCRIPTOR_HANDLE dsvHandle(m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
        m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
        // Record commands.
        const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
        m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
        m_commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
        m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
        if (m_enableCulling)
        {
            // Draw the triangles that have not been culled.
            m_commandList->ExecuteIndirect(
                m_commandSignature.Get(),
                TriangleCount,
                m_processedCommandBuffers[m_frameIndex].Get(),
                0,
                m_processedCommandBuffers[m_frameIndex].Get(),
                CommandBufferSizePerFrame);
        }
        else
        {
            // Draw all of the triangles.
            m_commandList->ExecuteIndirect(
                m_commandSignature.Get(),
                TriangleCount,
                m_commandBuffer.Get(),
                CommandBufferSizePerFrame * m_frameIndex,
                nullptr,
                0);
        }
        // Indicate that the command buffer may be used by the compute shader
        // and that the back buffer will now be used to present.
        barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
        barriers[0].Transition.StateAfter = m_enableCulling ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
        barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
        barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
        m_commandList->ResourceBarrier(_countof(barriers), barriers);
        ThrowIfFailed(m_commandList->Close());
    }
}
请参阅 D3D12 参考中的示例代码。
要求
| 目标平台 | Windows | 
| 标头 | d3d12.h | 
| Library | D3d12.lib | 
| DLL | D3d12.dll |