バッファーを使用したデータの読み取り

(たとえば、スクリーンショットをキャプチャするために) GPU からデータを読み取るには、リードバック ヒープを使用します。 この手法は「バッファーを使用してテクスチャ データのアップロード」と関連していますが、いくつか違いがあります。

  • データを読み取り戻すには、D3D12_HEAP_TYPE_UPLOADではなく 、D3D12_HEAP_TYPED3D12_HEAP_TYPE_READBACKに設定してヒープ 作成します。
  • 読み取りバック ヒープ上のリソースは、常に 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'sScreenGrab」を参照してください。