Condividi tramite


Leggere i dati tramite un buffer

Per leggere i dati dalla GPU (ad esempio, per acquisire uno screenshot), si usa un heap di readback. Questa tecnica è correlata al caricamento dei dati delle trame tramite un buffer, con alcune differenze.

  • Per leggere i dati, creare un heap con il D3D12_HEAP_TYPE impostato su D3D12_HEAP_TYPE_READBACK, anziché D3D12_HEAP_TYPE_UPLOAD.
  • La risorsa nell'heap readback deve essere sempre una D3D12_RESOURCE_DIMENSION_BUFFER.
  • Si usa un recinto per rilevare quando la GPU completa l'elaborazione di un frame (al termine della scrittura dei dati nel buffer di output). Questo è importante perché il metodo ID3D12Resource::Map non si sincronizza con la GPU (al contrario, l'equivalente Direct3D 11 esegue la sincronizzazione). Le chiamate mappa Direct3D 12 si comportano come se si chiamasse l'equivalente Direct3D 11 con il flag NO_OVERWRITE.
  • Dopo che i dati sono pronti (inclusa qualsiasi barriera di risorse necessaria), chiamare ID3D12Resource::Map per rendere i dati di readback visibili alla CPU.

Esempio di codice

L'esempio di codice seguente mostra la struttura generale del processo di lettura dei dati dalla GPU alla CPU tramite un 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
);

Per un'implementazione completa di una routine di screenshot che legge la trama di destinazione di rendering e la scrive su disco come file, vedere DirectX Tool Kit perScreenGrab di DX12.