Add ResourceAllocationManager to create and track lifetimes of d3d12 resources

This commit is contained in:
Austin Eng 2017-06-15 13:32:51 -04:00 committed by Austin Eng
parent 78f1619446
commit f96ce23628
9 changed files with 195 additions and 70 deletions

View File

@ -233,6 +233,8 @@ if (WIN32)
${D3D12_DIR}/PipelineLayoutD3D12.h ${D3D12_DIR}/PipelineLayoutD3D12.h
${D3D12_DIR}/QueueD3D12.cpp ${D3D12_DIR}/QueueD3D12.cpp
${D3D12_DIR}/QueueD3D12.h ${D3D12_DIR}/QueueD3D12.h
${D3D12_DIR}/ResourceAllocator.cpp
${D3D12_DIR}/ResourceAllocator.h
${D3D12_DIR}/ResourceUploader.cpp ${D3D12_DIR}/ResourceUploader.cpp
${D3D12_DIR}/ResourceUploader.h ${D3D12_DIR}/ResourceUploader.h
${D3D12_DIR}/ShaderModuleD3D12.cpp ${D3D12_DIR}/ShaderModuleD3D12.cpp

View File

@ -15,6 +15,7 @@
#include "BufferD3D12.h" #include "BufferD3D12.h"
#include "D3D12Backend.h" #include "D3D12Backend.h"
#include "ResourceAllocator.h"
namespace backend { namespace backend {
namespace d3d12 { namespace d3d12 {
@ -59,22 +60,11 @@ 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;
D3D12_HEAP_PROPERTIES heapProperties; resource = device->GetResourceAllocator()->Allocate(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor, D3D12BufferUsage(GetUsage()));
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;
// TODO(enga@google.com): Use a ResourceAllocationManager Buffer::~Buffer() {
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource( device->GetResourceAllocator()->Release(resource);
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&resourceDescriptor,
D3D12BufferUsage(GetUsage()),
nullptr,
IID_PPV_ARGS(&resource)
));
} }
ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() { ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {

View File

@ -27,6 +27,7 @@ namespace d3d12 {
class Buffer : public BufferBase { class Buffer : public BufferBase {
public: public:
Buffer(Device* device, BufferBuilder* builder); Buffer(Device* device, BufferBuilder* builder);
~Buffer();
ComPtr<ID3D12Resource> GetD3D12Resource(); ComPtr<ID3D12Resource> GetD3D12Resource();
D3D12_GPU_VIRTUAL_ADDRESS GetVA() const; D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
@ -34,7 +35,6 @@ namespace d3d12 {
private: private:
Device* device; Device* device;
ComPtr<ID3D12Resource> uploadResource;
ComPtr<ID3D12Resource> resource; ComPtr<ID3D12Resource> resource;
// NXT API // NXT API

View File

@ -76,6 +76,7 @@ namespace d3d12 {
Device::Device(ComPtr<ID3D12Device> d3d12Device) Device::Device(ComPtr<ID3D12Device> d3d12Device)
: d3d12Device(d3d12Device), : d3d12Device(d3d12Device),
commandAllocatorManager(this), commandAllocatorManager(this),
resourceAllocator(this),
resourceUploader(this), resourceUploader(this),
pendingCommands{ commandAllocatorManager.ReserveCommandAllocator() } { pendingCommands{ commandAllocatorManager.ReserveCommandAllocator() } {
@ -109,6 +110,18 @@ namespace d3d12 {
return commandQueue; return commandQueue;
} }
CommandAllocatorManager* Device::GetCommandAllocatorManager() {
return &commandAllocatorManager;
}
ResourceAllocator* Device::GetResourceAllocator() {
return &resourceAllocator;
}
ResourceUploader* Device::GetResourceUploader() {
return &resourceUploader;
}
ComPtr<ID3D12GraphicsCommandList> Device::GetPendingCommandList() { ComPtr<ID3D12GraphicsCommandList> Device::GetPendingCommandList() {
// Callers of GetPendingCommandList do so to record commands. Only reserve a command allocator when it is needed so we don't submit empty command lists // Callers of GetPendingCommandList do so to record commands. Only reserve a command allocator when it is needed so we don't submit empty command lists
if (!pendingCommands.open) { if (!pendingCommands.open) {
@ -123,14 +136,6 @@ namespace d3d12 {
return renderTargetDescriptor; return renderTargetDescriptor;
} }
ResourceUploader* Device::GetResourceUploader() {
return &resourceUploader;
}
CommandAllocatorManager* Device::GetCommandAllocatorManager() {
return &commandAllocatorManager;
}
void Device::SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) { void Device::SetNextRenderTargetDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) {
this->renderTargetDescriptor = renderTargetDescriptor; this->renderTargetDescriptor = renderTargetDescriptor;
} }
@ -138,7 +143,7 @@ namespace d3d12 {
void Device::TickImpl() { void Device::TickImpl() {
// Perform cleanup operations to free unused objects // Perform cleanup operations to free unused objects
const uint64_t lastCompletedSerial = fence->GetCompletedValue(); const uint64_t lastCompletedSerial = fence->GetCompletedValue();
resourceUploader.FreeCompletedResources(lastCompletedSerial); resourceAllocator.FreeUnusedResources(lastCompletedSerial);
commandAllocatorManager.ResetCompletedAllocators(lastCompletedSerial); commandAllocatorManager.ResetCompletedAllocators(lastCompletedSerial);
} }

View File

@ -32,8 +32,9 @@
#include "common/ToBackend.h" #include "common/ToBackend.h"
#include "d3d12_platform.h" #include "d3d12_platform.h"
#include "ResourceUploader.h"
#include "CommandAllocatorManager.h" #include "CommandAllocatorManager.h"
#include "ResourceAllocator.h"
#include "ResourceUploader.h"
namespace backend { namespace backend {
namespace d3d12 { namespace d3d12 {
@ -110,7 +111,9 @@ namespace d3d12 {
ComPtr<ID3D12Device> GetD3D12Device(); ComPtr<ID3D12Device> GetD3D12Device();
ComPtr<ID3D12CommandQueue> GetCommandQueue(); ComPtr<ID3D12CommandQueue> GetCommandQueue();
CommandAllocatorManager* GetCommandAllocatorManager(); CommandAllocatorManager* GetCommandAllocatorManager();
ResourceAllocator* GetResourceAllocator();
ResourceUploader* GetResourceUploader(); ResourceUploader* GetResourceUploader();
ComPtr<ID3D12GraphicsCommandList> GetPendingCommandList(); ComPtr<ID3D12GraphicsCommandList> GetPendingCommandList();
@ -137,6 +140,7 @@ namespace d3d12 {
ComPtr<ID3D12CommandQueue> commandQueue; ComPtr<ID3D12CommandQueue> commandQueue;
CommandAllocatorManager commandAllocatorManager; CommandAllocatorManager commandAllocatorManager;
ResourceAllocator resourceAllocator;
ResourceUploader resourceUploader; ResourceUploader resourceUploader;
struct PendingCommandList { struct PendingCommandList {

View File

@ -0,0 +1,92 @@
// 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 "ResourceAllocator.h"
#include "D3D12Backend.h"
namespace backend {
namespace d3d12 {
namespace {
static constexpr D3D12_HEAP_PROPERTIES kDefaultHeapProperties = {
D3D12_HEAP_TYPE_DEFAULT,
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
D3D12_MEMORY_POOL_UNKNOWN,
0,
0
};
static constexpr D3D12_HEAP_PROPERTIES kUploadHeapProperties = {
D3D12_HEAP_TYPE_UPLOAD,
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
D3D12_MEMORY_POOL_UNKNOWN,
0,
0
};
static constexpr D3D12_HEAP_PROPERTIES kReadbackHeapProperties = {
D3D12_HEAP_TYPE_READBACK,
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
D3D12_MEMORY_POOL_UNKNOWN,
0,
0
};
}
ResourceAllocator::ResourceAllocator(Device* device) : device(device) {
}
ComPtr<ID3D12Resource> ResourceAllocator::Allocate(D3D12_HEAP_TYPE heapType, const D3D12_RESOURCE_DESC &resourceDescriptor, D3D12_RESOURCE_STATES initialUsage) {
const D3D12_HEAP_PROPERTIES* heapProperties = nullptr;
switch(heapType) {
case D3D12_HEAP_TYPE_DEFAULT:
heapProperties = &kDefaultHeapProperties;
break;
case D3D12_HEAP_TYPE_UPLOAD:
heapProperties = &kUploadHeapProperties;
break;
case D3D12_HEAP_TYPE_READBACK:
heapProperties = &kReadbackHeapProperties;
break;
default:
ASSERT(false);
}
ComPtr<ID3D12Resource> resource;
// TODO(enga@google.com): Use CreatePlacedResource
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommittedResource(
heapProperties,
D3D12_HEAP_FLAG_NONE,
&resourceDescriptor,
initialUsage,
nullptr,
IID_PPV_ARGS(&resource)
));
return resource;
}
void ResourceAllocator::Release(ComPtr<ID3D12Resource> resource) {
// Resources may still be in use on the GPU. Enqueue them so that we hold onto them until GPU execution has completed
releasedResources.Enqueue(resource, device->GetSerial());
}
void ResourceAllocator::FreeUnusedResources(uint64_t lastCompletedSerial) {
releasedResources.ClearUpTo(lastCompletedSerial);
}
}
}

View File

@ -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_RESOURCEALLOCATIONMANAGER_H_
#define BACKEND_D3D12_RESOURCEALLOCATIONMANAGER_H_
#include "d3d12_platform.h"
#include "common/SerialQueue.h"
#include <set>
namespace backend {
namespace d3d12 {
class Device;
class ResourceAllocator {
public:
ResourceAllocator(Device* device);
ComPtr<ID3D12Resource> Allocate(D3D12_HEAP_TYPE heapType, const D3D12_RESOURCE_DESC &resourceDescriptor, D3D12_RESOURCE_STATES initialUsage);
void Release(ComPtr<ID3D12Resource> resource);
void FreeUnusedResources(uint64_t lastCompletedSerial);
private:
Device* device;
SerialQueue<ComPtr<ID3D12Resource>> releasedResources;
};
}
}
#endif // BACKEND_D3D12_RESOURCEALLOCATIONMANAGER_H_

View File

@ -23,10 +23,20 @@ namespace d3d12 {
} }
void ResourceUploader::UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data) { void ResourceUploader::UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data) {
// TODO(enga@google.com): Use a handle to a subset of a large ring buffer. On Release, decrease reference count on the ring buffer and free when 0.
// Alternatively, the SerialQueue could be used to track which last point of the ringbuffer is in use, and start reusing chunks of it that aren't in flight.
UploadHandle uploadHandle = GetUploadBuffer(count);
memcpy(uploadHandle.mappedBuffer, data, count);
device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), start, uploadHandle.resource.Get(), 0, count);
Release(uploadHandle);
}
ResourceUploader::UploadHandle ResourceUploader::GetUploadBuffer(uint32_t requiredSize) {
// TODO(enga@google.com): This will find or create a mapped buffer of sufficient size and return a handle to a mapped range
D3D12_RESOURCE_DESC resourceDescriptor; D3D12_RESOURCE_DESC resourceDescriptor;
resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDescriptor.Alignment = 0; resourceDescriptor.Alignment = 0;
resourceDescriptor.Width = count; resourceDescriptor.Width = requiredSize;
resourceDescriptor.Height = 1; resourceDescriptor.Height = 1;
resourceDescriptor.DepthOrArraySize = 1; resourceDescriptor.DepthOrArraySize = 1;
resourceDescriptor.MipLevels = 1; resourceDescriptor.MipLevels = 1;
@ -36,45 +46,19 @@ 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;
ComPtr<ID3D12Resource> uploadResource; UploadHandle uploadHandle;
uploadHandle.resource = device->GetResourceAllocator()->Allocate(D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor, D3D12_RESOURCE_STATE_GENERIC_READ);
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; D3D12_RANGE readRange;
readRange.Begin = 0; readRange.Begin = 0;
readRange.End = 0; readRange.End = 0;
D3D12_RANGE writeRange; uploadHandle.resource->Map(0, &readRange, reinterpret_cast<void**>(&uploadHandle.mappedBuffer));
writeRange.Begin = 0; return uploadHandle;
writeRange.End = count;
uint8_t* mappedResource = nullptr;
ASSERT_SUCCESS(uploadResource->Map(0, &readRange, reinterpret_cast<void**>(&mappedResource)));
memcpy(mappedResource, data, count);
uploadResource->Unmap(0, &writeRange);
device->GetPendingCommandList()->CopyBufferRegion(resource.Get(), start, uploadResource.Get(), 0, count);
uploadingResources.Enqueue(std::move(uploadResource), device->GetSerial());
} }
void ResourceUploader::FreeCompletedResources(const uint64_t lastCompletedSerial) { void ResourceUploader::Release(UploadHandle uploadHandle) {
uploadingResources.ClearUpTo(lastCompletedSerial); uploadHandle.resource->Unmap(0, nullptr);
device->GetResourceAllocator()->Release(uploadHandle.resource);
} }
} }

View File

@ -17,10 +17,7 @@
#include "d3d12_platform.h" #include "d3d12_platform.h"
#include "common/SerialQueue.h" #include "common/Forward.h"
#include <map>
#include <vector>
namespace backend { namespace backend {
namespace d3d12 { namespace d3d12 {
@ -32,14 +29,18 @@ namespace d3d12 {
ResourceUploader(Device* device); ResourceUploader(Device* device);
void UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data); void UploadToBuffer(ComPtr<ID3D12Resource> resource, uint32_t start, uint32_t count, const uint8_t* data);
void FreeCompletedResources(const uint64_t lastCompletedSerial);
private: private:
Device* device; struct UploadHandle {
ComPtr<ID3D12Resource> resource;
SerialQueue<ComPtr<ID3D12Resource>> uploadingResources; uint8_t* mappedBuffer;
}; };
UploadHandle GetUploadBuffer(uint32_t requiredSize);
void Release(UploadHandle uploadHandle);
Device* device;
};
} }
} }