Ler dados de retorno por meio de um buffer

Para ler dados de volta da GPU (por exemplo, para capturar uma captura de tela), use um heap de readback. Essa técnica está relacionada ao carregamento de dados de textura por meio de um buffer, com algumas diferenças.

  • Para ler dados de volta, crie um heap com o D3D12_HEAP_TYPE definido como D3D12_HEAP_TYPE_READBACK, em vez de D3D12_HEAP_TYPE_UPLOAD.
  • O recurso no heap de leitura deve ser sempre um D3D12_RESOURCE_DIMENSION_BUFFER.
  • Você usa uma cerca para detectar quando a GPU conclui o processamento de um quadro (quando terminar de gravar dados no buffer de saída). Isso é importante, pois o método ID3D12Resource::Map não é sincronizado com a GPU (por outro lado, o equivalente do Direct3D 11 é sincronizado). As chamadas de mapa do Direct3D 12 se comportam como se você tivesse chamado o direct3D 11 equivalente com o sinalizador NO_OVERWRITE.
  • Depois que os dados estiverem prontos (incluindo qualquer barreira de recursos necessária), chame ID3D12Resource::Map para tornar os dados de readback visíveis para a CPU.

Exemplo de código

O exemplo de código abaixo mostra a estrutura de tópicos geral do processo de leitura de dados da GPU para a CPU por meio de um buffer.


// The output buffer (created below) is on a default heap, so only the GPU can access it.

D3D12_HEAP_PROPERTIES defaultHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT) };
D3D12_RESOURCE_DESC outputBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(outputBufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) };
winrt::com_ptr<::ID3D12Resource> outputBuffer;
winrt::check_hresult(d3d12Device->CreateCommittedResource(
    &defaultHeapProperties,
    D3D12_HEAP_FLAG_NONE,
    &outputBufferDesc,
    D3D12_RESOURCE_STATE_COPY_DEST,
    nullptr,
    __uuidof(outputBuffer),
    outputBuffer.put_void()));

// The readback buffer (created below) is on a readback heap, so that the CPU can access it.

D3D12_HEAP_PROPERTIES readbackHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK) };
D3D12_RESOURCE_DESC readbackBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(outputBufferSize) };
winrt::com_ptr<::ID3D12Resource> readbackBuffer;
winrt::check_hresult(d3d12Device->CreateCommittedResource(
    &readbackHeapProperties,
    D3D12_HEAP_FLAG_NONE,
    &readbackBufferDesc,
    D3D12_RESOURCE_STATE_COPY_DEST,
    nullptr,
    __uuidof(readbackBuffer),
    readbackBuffer.put_void()));

{
    D3D12_RESOURCE_BARRIER outputBufferResourceBarrier
    {
        CD3DX12_RESOURCE_BARRIER::Transition(
            outputBuffer.get(),
            D3D12_RESOURCE_STATE_COPY_DEST,
            D3D12_RESOURCE_STATE_COPY_SOURCE)
    };
    commandList->ResourceBarrier(1, &outputBufferResourceBarrier);
}

commandList->CopyResource(readbackBuffer.get(), outputBuffer.get());

// Code goes here to close, execute (and optionally reset) the command list, and also
// to use a fence to wait for the command queue.

// The code below assumes that the GPU wrote FLOATs to the buffer.

D3D12_RANGE readbackBufferRange{ 0, outputBufferSize };
FLOAT * pReadbackBufferData{};
winrt::check_hresult(
    readbackBuffer->Map
    (
        0,
        &readbackBufferRange,
        reinterpret_cast<void**>(&pReadbackBufferData)
    )
);

// Code goes here to access the data via pReadbackBufferData.

D3D12_RANGE emptyRange{ 0, 0 };
readbackBuffer->Unmap
(
    0,
    &emptyRange
);

Para obter uma implementação completa de uma rotina de captura de tela que lê a textura de destino de renderização e a grava no disco como um arquivo, consulte Kit de Ferramentas DirectX paraScreenGrab do DX12.