通过缓冲区读回数据

若要从 GPU 读取回数据(例如捕获屏幕截图),请使用读回堆。 此方法与 通过缓冲区上传纹理数据相关,但存在一些差异。

  • 若要读回数据,请创建一个堆,并将 D3D12_HEAP_TYPE 设置为 D3D12_HEAP_TYPE_READBACK,而不是 D3D12_HEAP_TYPE_UPLOAD
  • 读回堆上的资源必须始终是 D3D12_RESOURCE_DIMENSION_BUFFER
  • 使用围栏检测 GPU 何时完成处理帧(在将数据写入输出缓冲区时)。 这一点很重要,因为 ID3D12Resource::Map 方法不会与 GPU 同步(相反,Direct3D 11 等效 同步)。 Direct3D 12 Map 调用的行为就像调用与 NO_OVERWRITE 标志等效的 Direct3D 11 一样。
  • 数据准备就绪(包括任何必要的资源屏障)后,调用 ID3D12Resource::Map 以使读回数据对 CPU 可见。

代码示例

下面的代码示例显示了通过缓冲区将数据从 GPU 读取回 CPU 的过程的一般概述。


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

有关读取呈现目标纹理并将其作为文件写入磁盘的屏幕截图例程的完整实现,请参阅 DirectX Tool Kit for DX12的 screenGrab

  • 在缓冲区 子分配