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(©Queue));
device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(©Allocator));
device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, copyAllocator, nullptr, IID_PPV_ARGS(©List));
Notice type D3D12_COMMAND_LIST_TYPE_COPY
.
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);
++fenceValue;
WaitForSingleObject(fenceEvent, INFINITE);
copyList->CopyResource(bufferReadback, bufferB);
copyList->Close();
copyQueue->ExecuteCommandLists(1, (ID3D12CommandList**)(©List));
copyQueue->Signal(fence, fenceValue);
fence->SetEventOnCompletion(fenceValue, fenceEvent);
++fenceValue;
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.
UAV
but maybe it will work, just setDstRowPitch
andDstDepthPitch
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$ID3D12Resource::ReadFromSubresource
doesn't allocate memory for you and it get pointer not pointer to pointer, so you need to allocate memory and castpDataBegin
tovoid*
notvoid**
. Remember to free allocated memory $\endgroup$