diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index 6177535e90..07672b9ea6 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -230,6 +230,8 @@ if (WIN32) ${D3D12_DIR}/PipelineLayoutD3D12.h ${D3D12_DIR}/QueueD3D12.cpp ${D3D12_DIR}/QueueD3D12.h + ${D3D12_DIR}/ResourceUploader.cpp + ${D3D12_DIR}/ResourceUploader.h ${D3D12_DIR}/ShaderModuleD3D12.cpp ${D3D12_DIR}/ShaderModuleD3D12.h ) diff --git a/src/backend/d3d12/BufferD3D12.cpp b/src/backend/d3d12/BufferD3D12.cpp index d837cba085..830a24255f 100644 --- a/src/backend/d3d12/BufferD3D12.cpp +++ b/src/backend/d3d12/BufferD3D12.cpp @@ -59,41 +59,22 @@ namespace d3d12 { resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE; - { - D3D12_HEAP_PROPERTIES heapProperties; - heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProperties.CreationNodeMask = 0; - heapProperties.VisibleNodeMask = 0; + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 0; + heapProperties.VisibleNodeMask = 0; - ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource( - &heapProperties, - D3D12_HEAP_FLAG_NONE, - &resourceDescriptor, - D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, - IID_PPV_ARGS(&uploadResource) - )); - } - - { - D3D12_HEAP_PROPERTIES heapProperties; - heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProperties.CreationNodeMask = 0; - heapProperties.VisibleNodeMask = 0; - - ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource( - &heapProperties, - D3D12_HEAP_FLAG_NONE, - &resourceDescriptor, - D3D12BufferUsage(GetUsage()), - nullptr, - IID_PPV_ARGS(&resource) - )); - } + // TODO(enga@google.com): Use a ResourceAllocationManager + ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &resourceDescriptor, + D3D12BufferUsage(GetUsage()), + nullptr, + IID_PPV_ARGS(&resource) + )); } ComPtr Buffer::GetD3D12Resource() { @@ -123,25 +104,7 @@ namespace d3d12 { } void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { - uint32_t begin = start * sizeof(uint32_t); - uint32_t end = (start + count) * sizeof(uint32_t); - - uint8_t* mappedResource = nullptr; - - D3D12_RANGE readRange; - readRange.Begin = 0; - readRange.End = 0; - - ASSERT_SUCCESS(uploadResource->Map(0, &readRange, reinterpret_cast(&mappedResource))); - memcpy(&mappedResource[begin], data, end - begin); - - D3D12_RANGE writeRange; - writeRange.Begin = begin; - writeRange.End = end; - - uploadResource->Unmap(0, &writeRange); - - device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), begin, uploadResource.Get(), begin, end - begin); + device->GetResourceUploader()->UploadToBuffer(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), reinterpret_cast(data)); } void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) { diff --git a/src/backend/d3d12/D3D12Backend.cpp b/src/backend/d3d12/D3D12Backend.cpp index 5e9a1422a0..ec9a087882 100644 --- a/src/backend/d3d12/D3D12Backend.cpp +++ b/src/backend/d3d12/D3D12Backend.cpp @@ -48,7 +48,7 @@ namespace d3d12 { ASSERT(SUCCEEDED(hr)); } - Device::Device(ComPtr d3d12Device) : d3d12Device(d3d12Device) { + Device::Device(ComPtr d3d12Device) : d3d12Device(d3d12Device), resourceUploader(this) { D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; @@ -62,6 +62,10 @@ namespace d3d12 { nullptr, IID_PPV_ARGS(&pendingCommandList) )); + + ASSERT_SUCCESS(d3d12Device->CreateFence(serial++, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); + fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + ASSERT(fenceEvent != nullptr); } Device::~Device() { @@ -87,10 +91,53 @@ namespace d3d12 { return renderTargetDescriptor; } + ResourceUploader* Device::GetResourceUploader() { + return &resourceUploader; + } + void Device::SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) { this->renderTargetDescriptor = renderTargetDescriptor; } + void Device::Tick() { + // Execute any pending commands + ASSERT_SUCCESS(pendingCommandList->Close()); + ID3D12CommandList* commandLists[] = { pendingCommandList.Get() }; + commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists); + + // Update state tracking for objects requiring the serial + resourceUploader.EnqueueUploadingResources(GetSerial() + 1); + + IncrementSerial(); + + // Signal when the pending commands have finished + ASSERT_SUCCESS(commandQueue->Signal(fence.Get(), GetSerial())); + + // Handle objects awaiting GPU execution + const uint64_t lastCompletedSerial = fence->GetCompletedValue(); + resourceUploader.FreeCompletedResources(lastCompletedSerial); + + // TODO(enga@google.com): This will stall on the submit because + // the commands must finish exeuting before the ID3D12CommandAllocator is reset. + // This should be fixed / optimized by using multiple command allocators. + const uint64_t currentFence = GetSerial(); + if (lastCompletedSerial < currentFence) { + ASSERT_SUCCESS(fence->SetEventOnCompletion(currentFence, fenceEvent)); + WaitForSingleObject(fenceEvent, INFINITE); + } + + ASSERT_SUCCESS(pendingCommandAllocator->Reset()); + ASSERT_SUCCESS(pendingCommandList->Reset(pendingCommandAllocator.Get(), NULL)); + } + + uint64_t Device::GetSerial() const { + return serial; + } + + void Device::IncrementSerial() { + serial++; + } + BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) { return new BindGroup(this, builder); } diff --git a/src/backend/d3d12/D3D12Backend.h b/src/backend/d3d12/D3D12Backend.h index cc0cd02b80..50fdb297e8 100644 --- a/src/backend/d3d12/D3D12Backend.h +++ b/src/backend/d3d12/D3D12Backend.h @@ -32,6 +32,7 @@ #include "common/ToBackend.h" #include "d3d12_platform.h" +#include "ResourceUploader.h" namespace backend { namespace d3d12 { @@ -109,9 +110,14 @@ namespace d3d12 { ComPtr GetPendingCommandAllocator(); ComPtr GetPendingCommandList(); D3D12_CPU_DESCRIPTOR_HANDLE GetCurrentRenderTargetDescriptor(); + ResourceUploader* GetResourceUploader(); void SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor); + void Tick(); + uint64_t GetSerial() const; + void IncrementSerial(); + // NXT API void Reference(); void Release(); @@ -122,6 +128,12 @@ namespace d3d12 { ComPtr pendingCommandAllocator; ComPtr pendingCommandList; D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor; + + ResourceUploader resourceUploader; + + uint64_t serial = 0; + ComPtr fence; + HANDLE fenceEvent; }; diff --git a/src/backend/d3d12/QueueD3D12.cpp b/src/backend/d3d12/QueueD3D12.cpp index 4a5ef0f328..633c937cb2 100644 --- a/src/backend/d3d12/QueueD3D12.cpp +++ b/src/backend/d3d12/QueueD3D12.cpp @@ -31,6 +31,7 @@ namespace d3d12 { nullptr, IID_PPV_ARGS(&commandList) )); + ASSERT_SUCCESS(commandList->Close()); ASSERT_SUCCESS(device->GetD3D12Device()->CreateFence(fenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); @@ -38,22 +39,12 @@ namespace d3d12 { } void Queue::Submit(uint32_t numCommands, CommandBuffer* const * commands) { - ComPtr pendingCommandAllocator = device->GetPendingCommandAllocator(); - ComPtr pendingCommandList = device->GetPendingCommandList(); - ASSERT_SUCCESS(pendingCommandList->Close()); + device->Tick(); - for (uint32_t i = 0; i < numCommands; ++i) { - commands[i]->FillCommands(commandList); - } - ASSERT_SUCCESS(commandList->Close()); - - ID3D12CommandList* commandLists[] = { pendingCommandList.Get(), commandList.Get() }; - device->GetCommandQueue()->ExecuteCommandLists(_countof(commandLists), commandLists); - - // TODO(enga@google.com): This will stall on the submit because + // TODO(enga@google.com): This will stall on the previous submit because // the commands must finish exeuting before the ID3D12CommandAllocator is reset. // This should be fixed / optimized by using multiple command allocators. - const uint64_t currentFence = ++fenceValue; + const uint64_t currentFence = fenceValue++; ASSERT_SUCCESS(device->GetCommandQueue()->Signal(fence.Get(), fenceValue)); if (fence->GetCompletedValue() < currentFence) { @@ -62,9 +53,15 @@ namespace d3d12 { } ASSERT_SUCCESS(commandAllocator->Reset()); - ASSERT_SUCCESS(pendingCommandAllocator->Reset()); - ASSERT_SUCCESS(pendingCommandList->Reset(pendingCommandAllocator.Get(), NULL)); ASSERT_SUCCESS(commandList->Reset(commandAllocator.Get(), NULL)); + + for (uint32_t i = 0; i < numCommands; ++i) { + commands[i]->FillCommands(commandList); + } + ASSERT_SUCCESS(commandList->Close()); + + ID3D12CommandList* commandLists[] = { commandList.Get() }; + device->GetCommandQueue()->ExecuteCommandLists(_countof(commandLists), commandLists); } } diff --git a/src/backend/d3d12/ResourceUploader.cpp b/src/backend/d3d12/ResourceUploader.cpp new file mode 100644 index 0000000000..0bf8ac4e8b --- /dev/null +++ b/src/backend/d3d12/ResourceUploader.cpp @@ -0,0 +1,96 @@ +// 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 "ResourceUploader.h" + +#include "D3D12Backend.h" + +namespace backend { +namespace d3d12 { + + ResourceUploader::ResourceUploader(Device* device) : device(device) { + } + + void ResourceUploader::UploadToBuffer(ComPtr resource, uint32_t start, uint32_t count, const uint8_t* data) { + D3D12_RESOURCE_DESC resourceDescriptor; + resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDescriptor.Alignment = 0; + resourceDescriptor.Width = count; + 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 = D3D12_RESOURCE_FLAG_NONE; + + ComPtr uploadResource; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 0; + heapProperties.VisibleNodeMask = 0; + + // TODO(enga@google.com): Use a ResourceAllocationManager + ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource( + &heapProperties, + D3D12_HEAP_FLAG_NONE, + &resourceDescriptor, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&uploadResource) + )); + + D3D12_RANGE readRange; + readRange.Begin = 0; + readRange.End = 0; + + D3D12_RANGE writeRange; + writeRange.Begin = 0; + writeRange.End = count; + + uint8_t* mappedResource = nullptr; + + ASSERT_SUCCESS(uploadResource->Map(0, &readRange, reinterpret_cast(&mappedResource))); + memcpy(mappedResource, data, count); + uploadResource->Unmap(0, &writeRange); + device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), start, uploadResource.Get(), 0, count); + + pendingResources.push_back(uploadResource); + } + + void ResourceUploader::EnqueueUploadingResources(const uint64_t serial) { + if (pendingResources.size() > 0) { + uploadingResources.push_back(std::make_pair(serial, std::move(pendingResources))); + pendingResources.clear(); + } + } + + void ResourceUploader::FreeCompletedResources(const uint64_t lastCompletedSerial) { + auto it = uploadingResources.begin(); + while (it != uploadingResources.end()) { + if (it->first < lastCompletedSerial) { + it++; + } else { + break; + } + } + uploadingResources.erase(uploadingResources.begin(), it); + } + +} +} diff --git a/src/backend/d3d12/ResourceUploader.h b/src/backend/d3d12/ResourceUploader.h new file mode 100644 index 0000000000..c6783bdec2 --- /dev/null +++ b/src/backend/d3d12/ResourceUploader.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef BACKEND_D3D12_RESOURCEUPLOADER_H_ +#define BACKEND_D3D12_RESOURCEUPLOADER_H_ + +#include "d3d12_platform.h" + +#include +#include + +namespace backend { +namespace d3d12 { + + class Device; + + class ResourceUploader { + public: + ResourceUploader(Device* device); + + void UploadToBuffer(ComPtr resource, uint32_t start, uint32_t count, const uint8_t* data); + void EnqueueUploadingResources(const uint64_t serial); + void FreeCompletedResources(const uint64_t lastCompletedSerial); + + private: + Device* device; + + std::vector> pendingResources; + std::vector>>> uploadingResources; + + }; + +} +} + +#endif // BACKEND_D3D12_RESOURCEUPLOADER_H_