Resource Management 8: placed resource sub-allocation.

- Adds d3d allocators (placed resource + heap).
- Support for heap tier 1 but only buffers.
- Suballocation optimization is enabled for allocations under 4MB.

BUG=dawn:27

Change-Id: I79177830670d1f322bbadf45f797415a3e9208d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/5680
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
This commit is contained in:
Bryan Bernhart 2019-10-17 17:25:28 +00:00 committed by Commit Bot service account
parent 8d000e0cc2
commit 154badfe2f
17 changed files with 415 additions and 167 deletions

View File

@ -256,8 +256,6 @@ source_set("libdawn_native_sources") {
"src/dawn_native/d3d12/CommandBufferD3D12.h", "src/dawn_native/d3d12/CommandBufferD3D12.h",
"src/dawn_native/d3d12/CommandRecordingContext.cpp", "src/dawn_native/d3d12/CommandRecordingContext.cpp",
"src/dawn_native/d3d12/CommandRecordingContext.h", "src/dawn_native/d3d12/CommandRecordingContext.h",
"src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp",
"src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h",
"src/dawn_native/d3d12/ComputePipelineD3D12.cpp", "src/dawn_native/d3d12/ComputePipelineD3D12.cpp",
"src/dawn_native/d3d12/ComputePipelineD3D12.h", "src/dawn_native/d3d12/ComputePipelineD3D12.h",
"src/dawn_native/d3d12/D3D12Error.cpp", "src/dawn_native/d3d12/D3D12Error.cpp",
@ -269,6 +267,10 @@ source_set("libdawn_native_sources") {
"src/dawn_native/d3d12/DeviceD3D12.cpp", "src/dawn_native/d3d12/DeviceD3D12.cpp",
"src/dawn_native/d3d12/DeviceD3D12.h", "src/dawn_native/d3d12/DeviceD3D12.h",
"src/dawn_native/d3d12/Forward.h", "src/dawn_native/d3d12/Forward.h",
"src/dawn_native/d3d12/HeapAllocatorD3D12.cpp",
"src/dawn_native/d3d12/HeapAllocatorD3D12.h",
"src/dawn_native/d3d12/HeapD3D12.cpp",
"src/dawn_native/d3d12/HeapD3D12.h",
"src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp", "src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp",
"src/dawn_native/d3d12/NativeSwapChainImplD3D12.h", "src/dawn_native/d3d12/NativeSwapChainImplD3D12.h",
"src/dawn_native/d3d12/PipelineLayoutD3D12.cpp", "src/dawn_native/d3d12/PipelineLayoutD3D12.cpp",

View File

@ -34,12 +34,18 @@ namespace dawn_native {
} }
ResultOrError<ResourceMemoryAllocation> BuddyMemoryAllocator::Allocate(uint64_t allocationSize, ResultOrError<ResourceMemoryAllocation> BuddyMemoryAllocator::Allocate(uint64_t allocationSize,
uint64_t alignment, uint64_t alignment) {
int memoryFlags) {
ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{}; ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{};
if (allocationSize == 0) {
return invalidAllocation;
}
// Round allocation size to nearest power-of-two.
allocationSize = NextPowerOfTwo(allocationSize);
// Allocation cannot exceed the memory size. // Allocation cannot exceed the memory size.
if (allocationSize == 0 || allocationSize > mMemorySize) { if (allocationSize > mMemorySize) {
return invalidAllocation; return invalidAllocation;
} }
@ -53,7 +59,7 @@ namespace dawn_native {
if (mTrackedSubAllocations[memoryIndex].refcount == 0) { if (mTrackedSubAllocations[memoryIndex].refcount == 0) {
// Transfer ownership to this allocator // Transfer ownership to this allocator
std::unique_ptr<ResourceHeapBase> memory; std::unique_ptr<ResourceHeapBase> memory;
DAWN_TRY_ASSIGN(memory, mClient->Allocate(mMemorySize, memoryFlags)); DAWN_TRY_ASSIGN(memory, mClient->Allocate(mMemorySize));
mTrackedSubAllocations[memoryIndex] = {/*refcount*/ 0, std::move(memory)}; mTrackedSubAllocations[memoryIndex] = {/*refcount*/ 0, std::move(memory)};
} }

View File

@ -44,8 +44,7 @@ namespace dawn_native {
~BuddyMemoryAllocator() = default; ~BuddyMemoryAllocator() = default;
ResultOrError<ResourceMemoryAllocation> Allocate(uint64_t allocationSize, ResultOrError<ResourceMemoryAllocation> Allocate(uint64_t allocationSize,
uint64_t alignment, uint64_t alignment);
int memoryFlags = 0);
void Deallocate(const ResourceMemoryAllocation& allocation); void Deallocate(const ResourceMemoryAllocation& allocation);
uint64_t GetMemorySize() const; uint64_t GetMemorySize() const;

View File

@ -24,8 +24,7 @@ namespace dawn_native {
public: public:
virtual ~MemoryAllocator() = default; virtual ~MemoryAllocator() = default;
virtual ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size, virtual ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size) = 0;
int memoryFlags) = 0;
virtual void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) = 0; virtual void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) = 0;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -111,8 +111,7 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY_ASSIGN( DAWN_TRY_ASSIGN(
mResourceAllocation, mResourceAllocation,
ToBackend(GetDevice()) ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage));
->AllocateMemory(heapType, resourceDescriptor, bufferUsage, D3D12_HEAP_FLAG_NONE));
return {}; return {};
} }

View File

@ -1,52 +0,0 @@
// Copyright 2019 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 "dawn_native/d3d12/CommittedResourceAllocatorD3D12.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
namespace dawn_native { namespace d3d12 {
CommittedResourceAllocator::CommittedResourceAllocator(Device* device, D3D12_HEAP_TYPE heapType)
: mDevice(device), mHeapType(heapType) {
}
ResultOrError<ResourceHeapAllocation> CommittedResourceAllocator::Allocate(
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage,
D3D12_HEAP_FLAGS heapFlags) {
D3D12_HEAP_PROPERTIES heapProperties;
heapProperties.Type = mHeapType;
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProperties.CreationNodeMask = 0;
heapProperties.VisibleNodeMask = 0;
ComPtr<ID3D12Resource> committedResource;
if (FAILED(mDevice->GetD3D12Device()->CreateCommittedResource(
&heapProperties, heapFlags, &resourceDescriptor, initialUsage, nullptr,
IID_PPV_ARGS(&committedResource)))) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate resource");
}
AllocationInfo info;
info.mMethod = AllocationMethod::kDirect;
return ResourceHeapAllocation{info,
/*offset*/ 0, std::move(committedResource)};
}
void CommittedResourceAllocator::Deallocate(ResourceHeapAllocation& allocation) {
mDevice->ReferenceUntilUnused(allocation.GetD3D12Resource());
}
}} // namespace dawn_native::d3d12

View File

@ -1,47 +0,0 @@
// Copyright 2019 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.
#ifndef DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_
#define DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_
#include "common/SerialQueue.h"
#include "dawn_native/Error.h"
#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
#include "dawn_native/d3d12/d3d12_platform.h"
namespace dawn_native { namespace d3d12 {
class Device;
// Wrapper to allocate D3D12 committed resource.
// Committed resources are implicitly backed by a D3D12 heap.
class CommittedResourceAllocator {
public:
CommittedResourceAllocator(Device* device, D3D12_HEAP_TYPE heapType);
~CommittedResourceAllocator() = default;
ResultOrError<ResourceHeapAllocation> Allocate(
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage,
D3D12_HEAP_FLAGS heapFlags);
void Deallocate(ResourceHeapAllocation& allocation);
private:
Device* mDevice;
D3D12_HEAP_TYPE mHeapType;
};
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_

View File

@ -213,6 +213,7 @@ namespace dawn_native { namespace d3d12 {
mDynamicUploader->Deallocate(mCompletedSerial); mDynamicUploader->Deallocate(mCompletedSerial);
mResourceAllocator->Tick(mCompletedSerial); mResourceAllocator->Tick(mCompletedSerial);
mResourceAllocatorManager->Tick(mCompletedSerial);
DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial)); DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial));
mDescriptorHeapAllocator->Deallocate(mCompletedSerial); mDescriptorHeapAllocator->Deallocate(mCompletedSerial);
mMapRequestTracker->Tick(mCompletedSerial); mMapRequestTracker->Tick(mCompletedSerial);
@ -348,10 +349,9 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<ResourceHeapAllocation> Device::AllocateMemory( ResultOrError<ResourceHeapAllocation> Device::AllocateMemory(
D3D12_HEAP_TYPE heapType, D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage, D3D12_RESOURCE_STATES initialUsage) {
D3D12_HEAP_FLAGS heapFlags) { return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor,
return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor, initialUsage, initialUsage);
heapFlags);
} }
TextureBase* Device::WrapSharedHandle(const TextureDescriptor* descriptor, TextureBase* Device::WrapSharedHandle(const TextureDescriptor* descriptor,

View File

@ -90,8 +90,7 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<ResourceHeapAllocation> AllocateMemory( ResultOrError<ResourceHeapAllocation> AllocateMemory(
D3D12_HEAP_TYPE heapType, D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage, D3D12_RESOURCE_STATES initialUsage);
D3D12_HEAP_FLAGS heapFlags);
void DeallocateMemory(ResourceHeapAllocation& allocation); void DeallocateMemory(ResourceHeapAllocation& allocation);

View File

@ -0,0 +1,52 @@
// Copyright 2019 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 "dawn_native/d3d12/HeapAllocatorD3D12.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/HeapD3D12.h"
namespace dawn_native { namespace d3d12 {
HeapAllocator::HeapAllocator(Device* device,
D3D12_HEAP_TYPE heapType,
D3D12_HEAP_FLAGS heapFlags)
: mDevice(device), mHeapType(heapType), mHeapFlags(heapFlags) {
}
ResultOrError<std::unique_ptr<ResourceHeapBase>> HeapAllocator::Allocate(uint64_t size) {
D3D12_HEAP_DESC heapDesc;
heapDesc.SizeInBytes = size;
heapDesc.Properties.Type = mHeapType;
heapDesc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapDesc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapDesc.Properties.CreationNodeMask = 0;
heapDesc.Properties.VisibleNodeMask = 0;
// MSAA vs non-MSAA resources have separate heap alignments.
// TODO(bryan.bernhart@intel.com): Support heap creation containing MSAA resources.
heapDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
heapDesc.Flags = mHeapFlags;
ComPtr<ID3D12Heap> heap;
if (FAILED(mDevice->GetD3D12Device()->CreateHeap(&heapDesc, IID_PPV_ARGS(&heap)))) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate heap");
}
return {std::make_unique<Heap>(std::move(heap))};
}
void HeapAllocator::Deallocate(std::unique_ptr<ResourceHeapBase> heap) {
mDevice->ReferenceUntilUnused(static_cast<Heap*>(heap.get())->GetD3D12Heap());
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,42 @@
// Copyright 2019 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.
#ifndef DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_
#define DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_
#include "dawn_native/MemoryAllocator.h"
#include "dawn_native/d3d12/d3d12_platform.h"
namespace dawn_native { namespace d3d12 {
class Device;
// Wrapper to allocate a D3D12 heap.
class HeapAllocator : public MemoryAllocator {
public:
HeapAllocator(Device* device, D3D12_HEAP_TYPE heapType, D3D12_HEAP_FLAGS heapFlags);
~HeapAllocator() override = default;
ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size) override;
void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) override;
private:
Device* mDevice;
D3D12_HEAP_TYPE mHeapType;
D3D12_HEAP_FLAGS mHeapFlags;
};
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_

View File

@ -0,0 +1,25 @@
// Copyright 2019 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 "dawn_native/d3d12/HeapD3D12.h"
namespace dawn_native { namespace d3d12 {
Heap::Heap(ComPtr<ID3D12Heap> heap) : mHeap(std::move(heap)) {
}
ComPtr<ID3D12Heap> Heap::GetD3D12Heap() const {
return mHeap;
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,35 @@
// Copyright 2019 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.
#ifndef DAWNNATIVE_D3D12_HEAPD3D12_H_
#define DAWNNATIVE_D3D12_HEAPD3D12_H_
#include "dawn_native/ResourceHeap.h"
#include "dawn_native/d3d12/d3d12_platform.h"
namespace dawn_native { namespace d3d12 {
class Heap : public ResourceHeapBase {
public:
Heap(ComPtr<ID3D12Heap> heap);
~Heap() = default;
ComPtr<ID3D12Heap> GetD3D12Heap() const;
private:
ComPtr<ID3D12Heap> mHeap;
};
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_HEAPD3D12_H_

View File

@ -13,56 +13,218 @@
// limitations under the License. // limitations under the License.
#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h" #include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h"
#include "dawn_native/d3d12/Forward.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/HeapAllocatorD3D12.h"
#include "dawn_native/d3d12/HeapD3D12.h"
namespace dawn_native { namespace d3d12 { namespace dawn_native { namespace d3d12 {
namespace {
D3D12_HEAP_TYPE GetD3D12HeapType(ResourceHeapKind resourceHeapKind) {
switch (resourceHeapKind) {
case Readback_OnlyBuffers:
return D3D12_HEAP_TYPE_READBACK;
case Default_OnlyBuffers:
case Default_OnlyNonRenderableOrDepthTextures:
case Default_OnlyRenderableOrDepthTextures:
return D3D12_HEAP_TYPE_DEFAULT;
case Upload_OnlyBuffers:
return D3D12_HEAP_TYPE_UPLOAD;
default:
UNREACHABLE();
}
}
D3D12_HEAP_FLAGS GetD3D12HeapFlags(ResourceHeapKind resourceHeapKind) {
switch (resourceHeapKind) {
case Default_OnlyBuffers:
case Readback_OnlyBuffers:
case Upload_OnlyBuffers:
return D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
case Default_OnlyNonRenderableOrDepthTextures:
return D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
case Default_OnlyRenderableOrDepthTextures:
return D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
default:
UNREACHABLE();
}
}
ResourceHeapKind GetResourceHeapKind(D3D12_RESOURCE_DIMENSION dimension,
D3D12_HEAP_TYPE heapType,
D3D12_RESOURCE_FLAGS flags) {
switch (dimension) {
case D3D12_RESOURCE_DIMENSION_BUFFER: {
switch (heapType) {
case D3D12_HEAP_TYPE_UPLOAD:
return Upload_OnlyBuffers;
case D3D12_HEAP_TYPE_DEFAULT:
return Default_OnlyBuffers;
case D3D12_HEAP_TYPE_READBACK:
return Readback_OnlyBuffers;
default:
UNREACHABLE();
}
} break;
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
case D3D12_RESOURCE_DIMENSION_TEXTURE3D: {
switch (heapType) {
case D3D12_HEAP_TYPE_DEFAULT: {
if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
(flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) {
return Default_OnlyRenderableOrDepthTextures;
} else {
return Default_OnlyNonRenderableOrDepthTextures;
}
} break;
default:
UNREACHABLE();
}
} break;
default:
UNREACHABLE();
}
}
} // namespace
ResourceAllocatorManager::ResourceAllocatorManager(Device* device) : mDevice(device) { ResourceAllocatorManager::ResourceAllocatorManager(Device* device) : mDevice(device) {
for (uint32_t i = 0; i < ResourceHeapKind::EnumCount; i++) {
const ResourceHeapKind resourceHeapKind = static_cast<ResourceHeapKind>(i);
mSubAllocatedResourceAllocators[i] = std::make_unique<BuddyMemoryAllocator>(
kMaxHeapSize, kMinHeapSize,
std::make_unique<HeapAllocator>(mDevice, GetD3D12HeapType(resourceHeapKind),
GetD3D12HeapFlags(resourceHeapKind)));
}
} }
ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::AllocateMemory( ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::AllocateMemory(
D3D12_HEAP_TYPE heapType, D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage, D3D12_RESOURCE_STATES initialUsage) {
D3D12_HEAP_FLAGS heapFlags) { // TODO(bryan.bernhart@intel.com): Conditionally disable sub-allocation.
const size_t heapTypeIndex = GetD3D12HeapTypeToIndex(heapType); // For very large resources, there is no benefit to suballocate.
ASSERT(heapTypeIndex < kNumHeapTypes); // For very small resources, it is inefficent to suballocate given the min. heap
// size could be much larger then the resource allocation.
// Get the direct allocator using a tightly sized heap (aka CreateCommittedResource). // Attempt to satisfy the request using sub-allocation (placed resource in a heap).
CommittedResourceAllocator* allocator = mDirectResourceAllocators[heapTypeIndex].get(); ResourceHeapAllocation subAllocation;
if (allocator == nullptr) { DAWN_TRY_ASSIGN(subAllocation,
mDirectResourceAllocators[heapTypeIndex] = CreatePlacedResource(heapType, resourceDescriptor, initialUsage));
std::make_unique<CommittedResourceAllocator>(mDevice, heapType); if (subAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) {
allocator = mDirectResourceAllocators[heapTypeIndex].get(); return subAllocation;
} }
ResourceHeapAllocation allocation; // If sub-allocation fails, fall-back to direct allocation (committed resource).
DAWN_TRY_ASSIGN(allocation, ResourceHeapAllocation directAllocation;
allocator->Allocate(resourceDescriptor, initialUsage, heapFlags)); DAWN_TRY_ASSIGN(directAllocation,
CreateCommittedResource(heapType, resourceDescriptor, initialUsage));
return allocation; return directAllocation;
} }
size_t ResourceAllocatorManager::GetD3D12HeapTypeToIndex(D3D12_HEAP_TYPE heapType) const { void ResourceAllocatorManager::Tick(Serial completedSerial) {
ASSERT(heapType > 0); for (ResourceHeapAllocation& allocation :
ASSERT(static_cast<uint32_t>(heapType) <= kNumHeapTypes); mAllocationsToDelete.IterateUpTo(completedSerial)) {
return heapType - 1; if (allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated) {
FreeMemory(allocation);
}
}
mAllocationsToDelete.ClearUpTo(completedSerial);
} }
void ResourceAllocatorManager::DeallocateMemory(ResourceHeapAllocation& allocation) { void ResourceAllocatorManager::DeallocateMemory(ResourceHeapAllocation& allocation) {
if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) { if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
return; return;
} }
CommittedResourceAllocator* allocator = nullptr;
D3D12_HEAP_PROPERTIES heapProp;
allocation.GetD3D12Resource()->GetHeapProperties(&heapProp, nullptr);
const size_t heapTypeIndex = GetD3D12HeapTypeToIndex(heapProp.Type);
ASSERT(heapTypeIndex < kNumHeapTypes);
allocator = mDirectResourceAllocators[heapTypeIndex].get();
allocator->Deallocate(allocation);
// Invalidate the underlying resource heap in case the client accidentally mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial());
// Invalidate the allocation immediately in case one accidentally
// calls DeallocateMemory again using the same allocation. // calls DeallocateMemory again using the same allocation.
allocation.Invalidate(); allocation.Invalidate();
} }
void ResourceAllocatorManager::FreeMemory(ResourceHeapAllocation& allocation) {
ASSERT(allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated);
D3D12_HEAP_PROPERTIES heapProp;
allocation.GetD3D12Resource()->GetHeapProperties(&heapProp, nullptr);
const D3D12_RESOURCE_DESC resourceDescriptor = allocation.GetD3D12Resource()->GetDesc();
const size_t resourceHeapKindIndex = GetResourceHeapKind(
resourceDescriptor.Dimension, heapProp.Type, resourceDescriptor.Flags);
mSubAllocatedResourceAllocators[resourceHeapKindIndex]->Deallocate(allocation);
}
ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::CreatePlacedResource(
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage) {
const size_t resourceHeapKindIndex =
GetResourceHeapKind(resourceDescriptor.Dimension, heapType, resourceDescriptor.Flags);
BuddyMemoryAllocator* allocator =
mSubAllocatedResourceAllocators[resourceHeapKindIndex].get();
const D3D12_RESOURCE_ALLOCATION_INFO resourceInfo =
mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor);
ResourceMemoryAllocation allocation;
DAWN_TRY_ASSIGN(allocation,
allocator->Allocate(resourceInfo.SizeInBytes, resourceInfo.Alignment));
if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) {
return ResourceHeapAllocation{}; // invalid
}
ID3D12Heap* heap = static_cast<Heap*>(allocation.GetResourceHeap())->GetD3D12Heap().Get();
// With placed resources, a single heap can be reused.
// The resource placed at an offset is only reclaimed
// upon Tick or after the last command list using the resource has completed
// on the GPU. This means the same physical memory is not reused
// within the same command-list and does not require additional synchronization (aliasing
// barrier).
// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource
ComPtr<ID3D12Resource> placedResource;
if (FAILED(mDevice->GetD3D12Device()->CreatePlacedResource(
heap, allocation.GetOffset(), &resourceDescriptor, initialUsage, nullptr,
IID_PPV_ARGS(&placedResource)))) {
// Note: Heap must already exist before the resource is created. If CreatePlacedResource
// fails, it's unlikely to be OOM.
return DAWN_DEVICE_LOST_ERROR("Unable to allocate resource");
}
return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(),
std::move(placedResource)};
}
ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::CreateCommittedResource(
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage) {
D3D12_HEAP_PROPERTIES heapProperties;
heapProperties.Type = heapType;
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProperties.CreationNodeMask = 0;
heapProperties.VisibleNodeMask = 0;
// Note: Heap flags are inferred by the resource descriptor and do not need to be explicitly
// provided to CreateCommittedResource.
ComPtr<ID3D12Resource> committedResource;
if (FAILED(mDevice->GetD3D12Device()->CreateCommittedResource(
&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDescriptor, initialUsage, nullptr,
IID_PPV_ARGS(&committedResource)))) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate resource");
}
AllocationInfo info;
info.mMethod = AllocationMethod::kDirect;
return ResourceHeapAllocation{info,
/*offset*/ 0, std::move(committedResource)};
}
}} // namespace dawn_native::d3d12 }} // namespace dawn_native::d3d12

View File

@ -15,7 +15,10 @@
#ifndef DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_ #ifndef DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_
#define DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_ #define DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_
#include "dawn_native/d3d12/CommittedResourceAllocatorD3D12.h" #include "common/SerialQueue.h"
#include "dawn_native/BuddyMemoryAllocator.h"
#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
#include <array> #include <array>
@ -23,8 +26,23 @@ namespace dawn_native { namespace d3d12 {
class Device; class Device;
// Manages a list of resource allocators used by the device to create resources using multiple // Heap types + flags combinations are named after the D3D constants.
// allocation methods. // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_flags
// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_type
enum ResourceHeapKind {
Readback_OnlyBuffers,
Upload_OnlyBuffers,
Default_OnlyBuffers,
Default_OnlyNonRenderableOrDepthTextures,
Default_OnlyRenderableOrDepthTextures,
EnumCount,
InvalidEnum = EnumCount,
};
// Manages a list of resource allocators used by the device to create resources using
// multiple allocation methods.
class ResourceAllocatorManager { class ResourceAllocatorManager {
public: public:
ResourceAllocatorManager(Device* device); ResourceAllocatorManager(Device* device);
@ -32,29 +50,34 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<ResourceHeapAllocation> AllocateMemory( ResultOrError<ResourceHeapAllocation> AllocateMemory(
D3D12_HEAP_TYPE heapType, D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor, const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage, D3D12_RESOURCE_STATES initialUsage);
D3D12_HEAP_FLAGS heapFlags);
void DeallocateMemory(ResourceHeapAllocation& allocation); void DeallocateMemory(ResourceHeapAllocation& allocation);
void Tick(Serial lastCompletedSerial);
private: private:
size_t GetD3D12HeapTypeToIndex(D3D12_HEAP_TYPE heapType) const; void FreeMemory(ResourceHeapAllocation& allocation);
ResultOrError<ResourceHeapAllocation> CreatePlacedResource(
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage);
ResultOrError<ResourceHeapAllocation> CreateCommittedResource(
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage);
Device* mDevice; Device* mDevice;
static constexpr uint32_t kNumHeapTypes = 4u; // Number of D3D12_HEAP_TYPE static constexpr uint64_t kMaxHeapSize = 32ll * 1024ll * 1024ll * 1024ll; // 32GB
static constexpr uint64_t kMinHeapSize = 4ll * 1024ll * 1024ll; // 4MB
static_assert(D3D12_HEAP_TYPE_READBACK <= kNumHeapTypes, std::array<std::unique_ptr<BuddyMemoryAllocator>, ResourceHeapKind::EnumCount>
"Readback heap type enum exceeds max heap types"); mSubAllocatedResourceAllocators;
static_assert(D3D12_HEAP_TYPE_UPLOAD <= kNumHeapTypes,
"Upload heap type enum exceeds max heap types");
static_assert(D3D12_HEAP_TYPE_DEFAULT <= kNumHeapTypes,
"Default heap type enum exceeds max heap types");
static_assert(D3D12_HEAP_TYPE_CUSTOM <= kNumHeapTypes,
"Custom heap type enum exceeds max heap types");
std::array<std::unique_ptr<CommittedResourceAllocator>, kNumHeapTypes> SerialQueue<ResourceHeapAllocation> mAllocationsToDelete;
mDirectResourceAllocators;
}; };
}} // namespace dawn_native::d3d12 }} // namespace dawn_native::d3d12

View File

@ -35,9 +35,9 @@ namespace dawn_native { 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;
DAWN_TRY_ASSIGN(mUploadHeap, mDevice->AllocateMemory( DAWN_TRY_ASSIGN(mUploadHeap,
D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor, mDevice->AllocateMemory(D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor,
D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_FLAG_NONE)); D3D12_RESOURCE_STATE_GENERIC_READ));
if (FAILED(GetResource()->Map(0, nullptr, &mMappedPointer))) { if (FAILED(GetResource()->Map(0, nullptr, &mMappedPointer))) {
return DAWN_DEVICE_LOST_ERROR("Unable to map staging buffer."); return DAWN_DEVICE_LOST_ERROR("Unable to map staging buffer.");
@ -47,6 +47,11 @@ namespace dawn_native { namespace d3d12 {
} }
StagingBuffer::~StagingBuffer() { StagingBuffer::~StagingBuffer() {
// Always check if the allocation is valid before Unmap.
// The resource would not exist had it failed to allocate.
if (mUploadHeap.GetInfo().mMethod == AllocationMethod::kInvalid) {
return;
}
// Invalidate the CPU virtual address & flush cache (if needed). // Invalidate the CPU virtual address & flush cache (if needed).
GetResource()->Unmap(0, nullptr); GetResource()->Unmap(0, nullptr);
mMappedPointer = nullptr; mMappedPointer = nullptr;

View File

@ -20,8 +20,7 @@ using namespace dawn_native;
class DummyMemoryAllocator : public MemoryAllocator { class DummyMemoryAllocator : public MemoryAllocator {
public: public:
ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size, ResultOrError<std::unique_ptr<ResourceHeapBase>> Allocate(uint64_t size) override {
int memoryFlags = 0) override {
return std::make_unique<ResourceHeapBase>(); return std::make_unique<ResourceHeapBase>();
} }
void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) override { void Deallocate(std::unique_ptr<ResourceHeapBase> allocation) override {