Partilhar via


Ler dados através de uma memória intermédia

Para ler dados da GPU (por exemplo, para capturar uma captura de tela), use uma pilha de leitura. Esta técnica está relacionada com Upload de dados de textura através de um buffer, com algumas diferenças.

  • Para reler dados, crie uma pilha com o D3D12_HEAP_TYPE definido como D3D12_HEAP_TYPE_READBACK, em vez de D3D12_HEAP_TYPE_UPLOAD.
  • O recurso na pilha de read-back deve ser sempre um D3D12_RESOURCE_DIMENSION_BUFFER.
  • Você usa uma cerca para detetar quando a GPU conclui o processamento de um quadro (quando termina a gravação de dados no buffer de saída). Isso é importante, porque o método ID3D12Resource::Map não sincroniza com a GPU (inversamente, o equivalente ao Direct3D 11 sincroniza). As chamadas do Direct3D 12 Map se comportam como se você tivesse chamado o equivalente do Direct3D 11 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 o esboço geral do processo de leitura de dados da GPU para a CPU através 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 da renderização e a grava no disco como um arquivo, consulte DirectX Tool Kit for DX12ScreenGrab.