dawn-cmake/src/backend/d3d12/BufferD3D12.cpp
Corentin Wallez a430a9a0aa Make the D3D12 backend support implicit barriers
With this the backend ignores explicit usage transition hints from the
frontent and generates all the barriers automatically based on resource
usage.

The current implementation is very naive and encodes a barrier
immediately just before a resource is used in a new state.
2018-07-13 13:31:14 +02:00

256 lines
9.8 KiB
C++

// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "backend/d3d12/BufferD3D12.h"
#include "backend/d3d12/DeviceD3D12.h"
#include "backend/d3d12/ResourceAllocator.h"
#include "backend/d3d12/ResourceUploader.h"
#include "common/Assert.h"
#include "common/Constants.h"
#include "common/Math.h"
namespace backend { namespace d3d12 {
namespace {
D3D12_RESOURCE_FLAGS D3D12ResourceFlags(nxt::BufferUsageBit usage) {
D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
if (usage & nxt::BufferUsageBit::Storage) {
flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
return flags;
}
D3D12_RESOURCE_STATES D3D12BufferUsage(nxt::BufferUsageBit usage) {
D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;
if (usage & nxt::BufferUsageBit::TransferSrc) {
resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
}
if (usage & nxt::BufferUsageBit::TransferDst) {
resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
}
if (usage & (nxt::BufferUsageBit::Vertex | nxt::BufferUsageBit::Uniform)) {
resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
}
if (usage & nxt::BufferUsageBit::Index) {
resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
}
if (usage & nxt::BufferUsageBit::Storage) {
resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
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;
}
}
} // namespace
Buffer::Buffer(BufferBuilder* builder) : BufferBase(builder) {
D3D12_RESOURCE_DESC resourceDescriptor;
resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDescriptor.Alignment = 0;
resourceDescriptor.Width = GetD3D12Size();
resourceDescriptor.Height = 1;
resourceDescriptor.DepthOrArraySize = 1;
resourceDescriptor.MipLevels = 1;
resourceDescriptor.Format = DXGI_FORMAT_UNKNOWN;
resourceDescriptor.SampleDesc.Count = 1;
resourceDescriptor.SampleDesc.Quality = 0;
resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
resourceDescriptor.Flags = D3D12ResourceFlags(GetAllowedUsage());
auto heapType = D3D12HeapType(GetAllowedUsage());
auto bufferUsage = D3D12_RESOURCE_STATE_COMMON;
// 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;
mFixedResourceState = true;
mLastUsage = nxt::BufferUsageBit::TransferDst;
}
// 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;
mFixedResourceState = true;
mLastUsage = nxt::BufferUsageBit::TransferSrc;
}
mResource = ToBackend(GetDevice())
->GetResourceAllocator()
->Allocate(heapType, resourceDescriptor, bufferUsage);
}
Buffer::~Buffer() {
ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
}
uint32_t Buffer::GetD3D12Size() const {
// TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
return Align(GetSize(), 256);
}
ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
return mResource;
}
void Buffer::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
nxt::BufferUsageBit usage) {
// Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state
if (mFixedResourceState) {
ASSERT(usage == mLastUsage);
return;
}
// We can skip transitions to already current usages.
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
bool lastIncludesTarget = (mLastUsage & usage) == usage;
if (lastIncludesTarget) {
return;
}
D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage);
D3D12_RESOURCE_STATES newState = D3D12BufferUsage(usage);
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = mResource.Get();
barrier.Transition.StateBefore = lastState;
barrier.Transition.StateAfter = newState;
barrier.Transition.Subresource = 0;
commandList->ResourceBarrier(1, &barrier);
mLastUsage = usage;
}
D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
return mResource->GetGPUVirtualAddress();
}
void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite) {
if (isWrite) {
CallMapWriteCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
} else {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
}
}
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) {
Device* device = ToBackend(GetDevice());
TransitionUsageNow(device->GetPendingCommandList(), nxt::BufferUsageBit::TransferDst);
device->GetResourceUploader()->BufferSubData(mResource, start, count, data);
}
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
D3D12_RANGE readRange = {start, start + count};
char* data = nullptr;
ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
// There is no need to transition the resource to a new state: D3D12 seems to make the GPU
// writes available when the fence is passed.
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, data + start, false);
}
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
D3D12_RANGE readRange = {start, start + count};
char* data = nullptr;
ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
// There is no need to transition the resource to a new state: D3D12 seems to make the CPU
// writes available on queue submission.
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, data + start, true);
}
void Buffer::UnmapImpl() {
// TODO(enga@google.com): When MapWrite is implemented, this should state the range that was
// modified
D3D12_RANGE writeRange = {};
mResource->Unmap(0, &writeRange);
ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
}
void Buffer::TransitionUsageImpl(nxt::BufferUsageBit, nxt::BufferUsageBit) {
}
BufferView::BufferView(BufferViewBuilder* builder) : BufferViewBase(builder) {
mCbvDesc.BufferLocation = ToBackend(GetBuffer())->GetVA() + GetOffset();
mCbvDesc.SizeInBytes = GetD3D12Size();
mUavDesc.Format = DXGI_FORMAT_UNKNOWN;
mUavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
mUavDesc.Buffer.FirstElement = GetOffset();
mUavDesc.Buffer.NumElements = GetD3D12Size();
mUavDesc.Buffer.StructureByteStride = 1;
mUavDesc.Buffer.CounterOffsetInBytes = 0;
mUavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
}
uint32_t BufferView::GetD3D12Size() const {
// TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
return Align(GetSize(), 256);
}
const D3D12_CONSTANT_BUFFER_VIEW_DESC& BufferView::GetCBVDescriptor() const {
return mCbvDesc;
}
const D3D12_UNORDERED_ACCESS_VIEW_DESC& BufferView::GetUAVDescriptor() const {
return mUavDesc;
}
MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
}
MapRequestTracker::~MapRequestTracker() {
ASSERT(mInflightRequests.Empty());
}
void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) {
Request request;
request.buffer = buffer;
request.mapSerial = mapSerial;
request.data = data;
request.isWrite = isWrite;
mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial());
}
void MapRequestTracker::Tick(Serial finishedSerial) {
for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.data,
request.isWrite);
}
mInflightRequests.ClearUpTo(finishedSerial);
}
}} // namespace backend::d3d12