Zurücklesen von Daten über einen Puffer

Zum Zurücklesen von Daten aus der GPU (z. B. zum Erfassen eines Screenshots) verwenden Sie einen Readback-Heap. Diese Technik bezieht sich auf das Hochladen von Texturdaten über einen Puffermit einigen Unterschieden.

  • Um Daten zurücklese zu können, erstellen Sie einen Heap, D3D12_HEAP_TYPE auf D3D12_HEAP_TYPE_READBACK undnicht auf D3D12_HEAP_TYPE_UPLOAD.
  • Die Ressource auf dem Zurück-Lese-Heap muss immer ein D3D12_RESOURCE_DIMENSION_BUFFER.
  • Sie verwenden einen Fence, um zu erkennen, wann die GPU die Verarbeitung eines Frames abgeschlossen hat (wenn das Schreiben von Daten in Ihren Ausgabepuffer abgeschlossen ist). Dies ist wichtig, da die ID3D12Resource::Map-Methode nicht mit der GPU synchronisiert wird (im Gegensatz dazu wird die Direct3D 11-Entsprechung synchronisiert). Direct3D 12 Map-Aufrufe verhalten sich so, als würden Sie das Direct3D 11-Äquivalent mit dem NO_OVERWRITE aufrufen.
  • Nachdem die Daten bereit sind (einschließlich aller erforderlichen Ressourcenbarrieren), rufen Sie ID3D12Resource::Map auf, um die Rücklesedaten für die CPU sichtbar zu machen.

Codebeispiel

Das folgende Codebeispiel zeigt die allgemeine Übersicht über den Prozess des Rücklesens von Daten aus der GPU in 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 DX12 es ScreenGrab.