Zurücklesen von Daten über einen Puffer

Zum Zurücklesen von Daten von der GPU (z. B. zum Erfassen eines Screenshots) verwenden Sie einen Readbackheap. Diese Technik bezieht sich auf das Hochladen von Texturdaten über einen Puffer mit einigen Unterschieden.

  • Um Daten zurückzulesen, erstellen Sie einen Heap mit dem D3D12_HEAP_TYPE , der auf D3D12_HEAP_TYPE_READBACK festgelegt ist, anstatt D3D12_HEAP_TYPE_UPLOAD.
  • Die Ressource auf dem Leseheap muss immer eine D3D12_RESOURCE_DIMENSION_BUFFER sein.
  • Sie verwenden einen Zaun, um zu erkennen, wann die GPU die Verarbeitung eines Frames abgeschlossen hat (wenn sie fertig ist, Daten in Ihren Ausgabepuffer zu schreiben). Dies ist wichtig, da die ID3D12Resource::Map-Methode nicht mit der GPU synchronisiert wird (umgekehrt wird das Direct3D 11-Äquivalent synchronisiert). Direct3D 12 Map-Aufrufe verhalten sich so, als ob Sie die Direct3D 11-Entsprechung mit dem flag NO_OVERWRITE aufgerufen haben.
  • Nachdem die Daten bereit sind (einschließlich aller erforderlichen Ressourcenbarrieren), rufen Sie ID3D12Resource::Map auf, um die Readbackdaten für die CPU sichtbar zu machen.

Codebeispiel

Das folgende Codebeispiel zeigt die allgemeine Gliederung des Prozesses zum Zurücklesen von Daten von der GPU an die CPU über einen Puffer.


// 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
);

Eine vollständige Implementierung einer Screenshotroutine, die die Renderzieltextur liest und als Datei auf den Datenträger schreibt, finden Sie unter DirectX Tool Kit für DX12ScreenGrab.