다음을 통해 공유


버퍼를 통해 데이터 다시 읽기

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

렌더링 대상 텍스처를 읽고 디스크에 파일로 쓰는 스크린샷 루틴의 전체 구현은 DX12의 ScreenGrab용 DirectX 도구 키트를 참조하세요.