ID3D12GraphicsCommandList::ExecuteIndirect 메서드(d3d12.h)

앱은 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*

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

인수 수를 식별하는 pCountBuffer의 오프셋인 UINT64를 지정합니다.

반환 값

없음

설명

이 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 상태가 아닌 경우 디버그 계층에서 오류가 발생합니다. 핵심 런타임은 다음의 유효성을 검사합니다.

  • CountBufferOffsetArgumentBufferOffset 은 4바이트로 정렬됩니다.
  • pCountBufferpArgumentBuffer 는 버퍼 리소스(모든 힙 유형)입니다.
  • MaxCommandCount, ArgumentBufferOffset 및 그리기 프로그램 보폭이 암시한 오프셋은 pArgumentBuffer의 범위를 초과하지 않습니다(마찬가지로 개수 버퍼의 경우).
  • 명령 목록은 직접 명령 목록 또는 컴퓨팅 명령 목록입니다(복사 또는 JPEG 디코딩 명령 목록이 아님).
  • 명령 목록의 루트 서명은 명령 서명의 루트 서명과 일치합니다.
이전 버전의 Direct3D DrawInstancedIndirectDrawIndexedInstancedIndirect의 두 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
라이브러리 D3d12.lib
DLL D3d12.dll

추가 정보

ID3D12GraphicsCommandList

간접 그리기