Implement MapReadAsync on D3D12 backend

Buffers with MapRead allowed are created on the READBACK heap and always
add the D3D12_RESOURCE_STATE_COPY_DEST state (required by D3D12).
Likewise MapWrite adds the D3D12_RESOURCE_STATE_GENERIC_READ state and
places resources on the UPLOAD heap. Because these states are
required, transitions for mapped buffers do nothing.
This commit is contained in:
Austin Eng 2017-06-30 11:44:47 -04:00 committed by Austin Eng
parent 8fa550c015
commit 47261d4ecb
4 changed files with 100 additions and 3 deletions

View File

@ -43,6 +43,16 @@ namespace d3d12 {
return resourceState; return resourceState;
} }
D3D12_HEAP_TYPE D3D12HeapType(nxt::BufferUsageBit allowedUsage) {
if (allowedUsage & nxt::BufferUsageBit::MapRead) {
return D3D12_HEAP_TYPE_READBACK;
} else if (allowedUsage & nxt::BufferUsageBit::MapWrite) {
return D3D12_HEAP_TYPE_UPLOAD;
} else {
return D3D12_HEAP_TYPE_DEFAULT;
}
}
} }
Buffer::Buffer(Device* device, BufferBuilder* builder) Buffer::Buffer(Device* device, BufferBuilder* builder)
@ -61,7 +71,20 @@ namespace d3d12 {
resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE; resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;
resource = device->GetResourceAllocator()->Allocate(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor, D3D12BufferUsage(GetUsage())); auto heapType = D3D12HeapType(GetAllowedUsage());
auto bufferUsage = D3D12BufferUsage(GetUsage());
// D3D12 requires buffers on the READBACK heap to have the D3D12_RESOURCE_STATE_COPY_DEST state
if (heapType == D3D12_HEAP_TYPE_READBACK) {
bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST;
}
// D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ state
if (heapType == D3D12_HEAP_TYPE_UPLOAD) {
bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ;
}
resource = device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage);
} }
Buffer::~Buffer() { Buffer::~Buffer() {
@ -78,6 +101,12 @@ namespace d3d12 {
} }
bool Buffer::GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier) { bool Buffer::GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier) {
if (GetAllowedUsage() & (nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::MapWrite)) {
// Transitions are never needed for mapped buffers because they are created with and always need the Transfer(Dst|Src) state.
// Mapped buffers cannot have states outside of (MapRead|TransferDst) and (MapWrite|TransferSrc)
return false;
}
D3D12_RESOURCE_STATES stateBefore = D3D12BufferUsage(currentUsage); D3D12_RESOURCE_STATES stateBefore = D3D12BufferUsage(currentUsage);
D3D12_RESOURCE_STATES stateAfter = D3D12BufferUsage(targetUsage); D3D12_RESOURCE_STATES stateAfter = D3D12BufferUsage(targetUsage);
@ -99,16 +128,28 @@ namespace d3d12 {
return resource->GetGPUVirtualAddress(); return resource->GetGPUVirtualAddress();
} }
void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_READ_STATUS_SUCCESS, data);
}
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
device->GetResourceUploader()->BufferSubData(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), data); device->GetResourceUploader()->BufferSubData(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), data);
} }
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
// TODO(cwallez@chromium.org): Implement Map Read for the null backend D3D12_RANGE readRange = { start, start + count };
char* data = nullptr;
ASSERT_SUCCESS(resource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker();
tracker->Track(this, serial, data);
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
// TODO(cwallez@chromium.org): Implement Map Read for the null backend // TODO(enga@google.com): When MapWrite is implemented, this should state the range that was modified
D3D12_RANGE writeRange = {};
resource->Unmap(0, &writeRange);
device->GetResourceAllocator()->Release(resource);
} }
void Buffer::TransitionUsageImpl(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage) { void Buffer::TransitionUsageImpl(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage) {
@ -147,5 +188,29 @@ namespace d3d12 {
return uavDesc; return uavDesc;
} }
MapReadRequestTracker::MapReadRequestTracker(Device* device)
: device(device) {
}
MapReadRequestTracker::~MapReadRequestTracker() {
ASSERT(inflightRequests.Empty());
}
void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, const void* data) {
Request request;
request.buffer = buffer;
request.mapSerial = mapSerial;
request.data = data;
inflightRequests.Enqueue(std::move(request), device->GetSerial());
}
void MapReadRequestTracker::Tick(Serial finishedSerial) {
for (auto& request : inflightRequests.IterateUpTo(finishedSerial)) {
request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data);
}
inflightRequests.ClearUpTo(finishedSerial);
}
} }
} }

View File

@ -16,6 +16,7 @@
#define BACKEND_D3D12_BUFFERD3D12_H_ #define BACKEND_D3D12_BUFFERD3D12_H_
#include "common/Buffer.h" #include "common/Buffer.h"
#include "common/SerialQueue.h"
#include "d3d12_platform.h" #include "d3d12_platform.h"
@ -33,6 +34,7 @@ namespace d3d12 {
ComPtr<ID3D12Resource> GetD3D12Resource(); ComPtr<ID3D12Resource> GetD3D12Resource();
D3D12_GPU_VIRTUAL_ADDRESS GetVA() const; D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
bool GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier); bool GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier);
void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data);
private: private:
Device* device; Device* device;
@ -59,6 +61,25 @@ namespace d3d12 {
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
}; };
class MapReadRequestTracker {
public:
MapReadRequestTracker(Device* device);
~MapReadRequestTracker();
void Track(Buffer* buffer, uint32_t mapSerial, const void* data);
void Tick(Serial finishedSerial);
private:
Device* device;
struct Request {
Ref<Buffer> buffer;
uint32_t mapSerial;
const void* data;
};
SerialQueue<Request> inflightRequests;
};
} }
} }

View File

@ -86,6 +86,7 @@ namespace d3d12 {
: d3d12Device(d3d12Device), : d3d12Device(d3d12Device),
commandAllocatorManager(new CommandAllocatorManager(this)), commandAllocatorManager(new CommandAllocatorManager(this)),
descriptorHeapAllocator(new DescriptorHeapAllocator(this)), descriptorHeapAllocator(new DescriptorHeapAllocator(this)),
mapReadRequestTracker(new MapReadRequestTracker(this)),
resourceAllocator(new ResourceAllocator(this)), resourceAllocator(new ResourceAllocator(this)),
resourceUploader(new ResourceUploader(this)) { resourceUploader(new ResourceUploader(this)) {
@ -114,6 +115,10 @@ namespace d3d12 {
return descriptorHeapAllocator; return descriptorHeapAllocator;
} }
MapReadRequestTracker* Device::GetMapReadRequestTracker() const {
return mapReadRequestTracker;
}
ResourceAllocator* Device::GetResourceAllocator() { ResourceAllocator* Device::GetResourceAllocator() {
return resourceAllocator; return resourceAllocator;
} }
@ -162,6 +167,9 @@ namespace d3d12 {
resourceAllocator->Tick(lastCompletedSerial); resourceAllocator->Tick(lastCompletedSerial);
commandAllocatorManager->Tick(lastCompletedSerial); commandAllocatorManager->Tick(lastCompletedSerial);
descriptorHeapAllocator->Tick(lastCompletedSerial); descriptorHeapAllocator->Tick(lastCompletedSerial);
mapReadRequestTracker->Tick(lastCompletedSerial);
ExecuteCommandLists({});
NextSerial();
} }
uint64_t Device::GetSerial() const { uint64_t Device::GetSerial() const {

View File

@ -56,6 +56,7 @@ namespace d3d12 {
class CommandAllocatorManager; class CommandAllocatorManager;
class DescriptorHeapAllocator; class DescriptorHeapAllocator;
class MapReadRequestTracker;
class ResourceAllocator; class ResourceAllocator;
class ResourceUploader; class ResourceUploader;
@ -115,6 +116,7 @@ namespace d3d12 {
ComPtr<ID3D12CommandQueue> GetCommandQueue(); ComPtr<ID3D12CommandQueue> GetCommandQueue();
DescriptorHeapAllocator* GetDescriptorHeapAllocator(); DescriptorHeapAllocator* GetDescriptorHeapAllocator();
MapReadRequestTracker* GetMapReadRequestTracker() const;
ResourceAllocator* GetResourceAllocator(); ResourceAllocator* GetResourceAllocator();
ResourceUploader* GetResourceUploader(); ResourceUploader* GetResourceUploader();
@ -144,6 +146,7 @@ namespace d3d12 {
CommandAllocatorManager* commandAllocatorManager; CommandAllocatorManager* commandAllocatorManager;
DescriptorHeapAllocator* descriptorHeapAllocator; DescriptorHeapAllocator* descriptorHeapAllocator;
MapReadRequestTracker* mapReadRequestTracker;
ResourceAllocator* resourceAllocator; ResourceAllocator* resourceAllocator;
ResourceUploader* resourceUploader; ResourceUploader* resourceUploader;