// Copyright 2017 The Dawn 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(dawn::BufferUsageBit usage) { D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE; if (usage & dawn::BufferUsageBit::Storage) { flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } return flags; } D3D12_RESOURCE_STATES D3D12BufferUsage(dawn::BufferUsageBit usage) { D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON; if (usage & dawn::BufferUsageBit::TransferSrc) { resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE; } if (usage & dawn::BufferUsageBit::TransferDst) { resourceState |= D3D12_RESOURCE_STATE_COPY_DEST; } if (usage & (dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Uniform)) { resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; } if (usage & dawn::BufferUsageBit::Index) { resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER; } if (usage & dawn::BufferUsageBit::Storage) { resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } return resourceState; } D3D12_HEAP_TYPE D3D12HeapType(dawn::BufferUsageBit allowedUsage) { if (allowedUsage & dawn::BufferUsageBit::MapRead) { return D3D12_HEAP_TYPE_READBACK; } else if (allowedUsage & dawn::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 = dawn::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 = dawn::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 Buffer::GetD3D12Resource() { return mResource; } void Buffer::TransitionUsageNow(ComPtr commandList, dawn::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, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); } else { CallMapReadCallback(mapSerial, DAWN_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(), dawn::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(&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(&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); } 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