I am pretty new to the dx12 world. As a sample project I am trying to add 2 buffers and store the output in yet another output buffer. As apart of this debugging I am trying to see if the buffers that I am loading actually have the right values. Thus, once the buffer is loaded, I want to put in a fence and "read back the data uploaded" into the CPU and print out the contents of the buffer. After a lot of googling I thought i should use the map function in DXD12.

But I am not entirely sure how I should go about doing this. Any thoughts?

  • $\begingroup$ I think you can also use ID3D12Resource::ReadFromSubresource it is a little simpler. $\endgroup$
    – Derag
    Apr 20, 2017 at 13:57
  • $\begingroup$ But I am trying to read out of a compute buffer. I was under the impression that ReadFromSubresource is used primarily on textures and layouts. $\endgroup$
    – Madhu
    Apr 20, 2017 at 16:37
  • $\begingroup$ I never used this with UAV but maybe it will work, just set DstRowPitch and DstDepthPitch to buffer size (in bytes) and you will see if its work. I don't see why it shouldn't work (but maybe it don't) $\endgroup$
    – Derag
    Apr 20, 2017 at 21:18
  • $\begingroup$ I created the Input buffer with the following properties: ThrowIfFailed(m_device->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_InputBuffer1[0]))); and later on when I try to read it back as follows, the compiler throws an exception! UINT8* pDataBegin; m_InputBuffer1[0]->ReadFromSubresource(reinterpret_cast<void**>(&pDataBegin),5,5,0,0); printf("The value pointed to is %d", *pDataBegin); any idea why this is happening ? $\endgroup$
    – Madhu
    Apr 21, 2017 at 0:43
  • $\begingroup$ ID3D12Resource::ReadFromSubresource doesn't allocate memory for you and it get pointer not pointer to pointer, so you need to allocate memory and cast pDataBegin to void* not void**. Remember to free allocated memory $\endgroup$
    – Derag
    Apr 21, 2017 at 9:07

1 Answer 1


Ok, so I made little example of how to do this.

Short version

You need to allocate additional buffer with heap type D3D12_HEAP_TYPE_READBACK and resource state set to D3D12_RESOURCE_STATE_COPY_DEST. Then create command list with type D3D12_COMMAND_LIST_TYPE_COPY. After dispatch compute shader use fence to check if buffer is ready, then copy from this buffer to buffer on D3D12_HEAP_TYPE_READBACK heap. Check again with fence if copying is finished and call Map on this buffer.

I would suggest that you stop reading here and try implement it from description above and check longer version if its still doesn't work. Copy/Past isn't the best way to learn :)

Longer version

First lets create new command list.

ID3D12CommandQueue* copyQueue;
ID3D12CommandAllocator* copyAllocator;
ID3D12GraphicsCommandList* copyList;

D3D12_COMMAND_QUEUE_DESC descCopyQueue = {};
descCopyQueue.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
descCopyQueue.Type = D3D12_COMMAND_LIST_TYPE_COPY;
device->CreateCommandQueue(&descCopyQueue, IID_PPV_ARGS(&copyQueue));
device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&copyAllocator));
device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, copyAllocator, nullptr, IID_PPV_ARGS(&copyList));


Next thing is new buffer.

D3D12_HEAP_PROPERTIES const heapPropertiesReadback =
    /*Type*/                    D3D12_HEAP_TYPE_READBACK
    /*CPUPageProperty*/         ,D3D12_CPU_PAGE_PROPERTY_UNKNOWN
    /*MemoryPoolPreference*/    ,D3D12_MEMORY_POOL_UNKNOWN
    /*CreationNodeMask*/        ,0
    /*VisibleNodeMask*/         ,0

D3D12_RESOURCE_DESC buffersDesc = {};
buffersDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffersDesc.Alignment = 0;
buffersDesc.Width = sizeof( unsigned int ) * 5;
buffersDesc.Height = 1;
buffersDesc.DepthOrArraySize = 1;
buffersDesc.MipLevels = 1;
buffersDesc.Format = DXGI_FORMAT_UNKNOWN;
buffersDesc.SampleDesc.Count = 1;
buffersDesc.SampleDesc.Quality = 0;
buffersDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffersDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

ID3D12Resource* bufferReadback;

device->CreateCommittedResource(&heapPropertiesReadback, D3D12_HEAP_FLAG_NONE, &buffersDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&bufferReadback));

Remember to set D3D12_RESOURCE_DESC ::Width to size of your buffers.

Then after you Dispatch compute list and call ExecuteCommandLists on compute queue you need to use fence, copy buffer and use fence again.

computeQueue->Signal(fence, fenceValue);
fence->SetEventOnCompletion(fenceValue, fenceEvent);
WaitForSingleObject(fenceEvent, INFINITE);

copyList->CopyResource(bufferReadback, bufferB);
copyQueue->ExecuteCommandLists(1, (ID3D12CommandList**)(&copyList));

copyQueue->Signal(fence, fenceValue);
fence->SetEventOnCompletion(fenceValue, fenceEvent);
WaitForSingleObject(fenceEvent, INFINITE);

After this you can call Map on resource.

unsigned int* data;
bufferReadback->Map(0, nullptr, reinterpret_cast<void**>(&data));

I hope this will help, let me know if you have any questions.

  • $\begingroup$ Thank you for you recommendation @Derag. However, the values in the readback buffer are incorrect. Can this be because of the readback buffer description, that the numbers are not being interpreted correctly? $\endgroup$
    – Madhu
    Apr 28, 2017 at 23:53
  • $\begingroup$ What values do you get? If you get only zeros you probably have some bug in code. If buffer you pass to Map have different type than UAV buffer that you read values can vary. And I don't think that D3D12_RESOURCE_DESC should be problem here as long as D3D12_RESOURCE_DESC::Width is set to right size and D3D12_RESOURCE_DESC::Format is set to DXGI_FORMAT_UNKNOWN $\endgroup$
    – Derag
    Apr 29, 2017 at 11:07
  • $\begingroup$ The definition of the readback buffer is the same as what you suggested, the buffer which the shader actually calls (or in the example above "bufferB) has the following properties: D3D12_HEAP_PROPERTIES defaultHeapProperties = { D3D12_HEAP_TYPE_DEFAULT,D3D12_CPU_PAGE_PROPERTY_UNKNOWN,D3D12_MEMORY_POOL_UNKNOWN,0,0 }; ThrowIfFailed(m_device->CreateCommittedResource(&defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &buffersDesc1, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_computeBuffer))); $\endgroup$
    – Madhu
    May 1, 2017 at 16:50
  • $\begingroup$ What size of buffers did you set? And what types of data? (int, float...) $\endgroup$
    – Derag
    May 2, 2017 at 9:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.