Residency 2: Create a d3d12::Heap During Direct Allocations
When creating a directly allocated resource in D3D12, also create a
dawn_native::d3d12::Heap to represent that allocation alongside the
ResourceHeapAllocation. This matches D3D12's allocation model when using
CreateCommittedResource and makes residency management much easier.
Bug: dawn:193
Change-Id: I2280863dcfca57bad72962a2b097f8f2d4cc7dad
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/16381
Commit-Queue: Brandon Jones <brandon1.jones@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/ResourceMemoryAllocation.h b/src/dawn_native/ResourceMemoryAllocation.h
index a66129a..1eb68e7 100644
--- a/src/dawn_native/ResourceMemoryAllocation.h
+++ b/src/dawn_native/ResourceMemoryAllocation.h
@@ -31,6 +31,9 @@
// Memory sub-divided using one or more blocks of various sizes.
kSubAllocated,
+ // Memory was allocated outside of Dawn.
+ kExternal,
+
// Memory not allocated or freed.
kInvalid
};
diff --git a/src/dawn_native/d3d12/Forward.h b/src/dawn_native/d3d12/Forward.h
index ade12e3..1143a4a 100644
--- a/src/dawn_native/d3d12/Forward.h
+++ b/src/dawn_native/d3d12/Forward.h
@@ -26,6 +26,7 @@
class CommandBuffer;
class ComputePipeline;
class Device;
+ class Heap;
class PipelineLayout;
class Queue;
class RenderPipeline;
@@ -47,6 +48,7 @@
using PipelineLayoutType = PipelineLayout;
using QueueType = Queue;
using RenderPipelineType = RenderPipeline;
+ using ResourceHeapType = Heap;
using SamplerType = Sampler;
using ShaderModuleType = ShaderModule;
using StagingBufferType = StagingBuffer;
diff --git a/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp b/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp
index e16f380..dd6c641 100644
--- a/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp
+++ b/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp
@@ -46,7 +46,7 @@
mDevice->GetD3D12Device()->CreateHeap(&heapDesc, IID_PPV_ARGS(&heap)),
"ID3D12Device::CreateHeap"));
- return {std::make_unique<Heap>(std::move(heap))};
+ return {std::make_unique<Heap>(std::move(heap), size)};
}
void HeapAllocator::DeallocateResourceHeap(std::unique_ptr<ResourceHeapBase> heap) {
diff --git a/src/dawn_native/d3d12/HeapD3D12.cpp b/src/dawn_native/d3d12/HeapD3D12.cpp
index 2e35bdf..2548ae0 100644
--- a/src/dawn_native/d3d12/HeapD3D12.cpp
+++ b/src/dawn_native/d3d12/HeapD3D12.cpp
@@ -15,11 +15,25 @@
#include "dawn_native/d3d12/HeapD3D12.h"
namespace dawn_native { namespace d3d12 {
-
- Heap::Heap(ComPtr<ID3D12Heap> heap) : mHeap(std::move(heap)) {
+ Heap::Heap(ComPtr<ID3D12Pageable> d3d12Pageable, uint64_t size)
+ : mD3d12Pageable(std::move(d3d12Pageable)), mSize(size) {
}
+ // This function should only be used when mD3D12Pageable was initialized from a ID3D12Pageable
+ // that was initially created as an ID3D12Heap (i.e. SubAllocation). If the ID3D12Pageable was
+ // initially created as an ID3D12Resource (i.e. DirectAllocation), then use GetD3D12Pageable().
ComPtr<ID3D12Heap> Heap::GetD3D12Heap() const {
- return mHeap;
+ ComPtr<ID3D12Heap> heap;
+ HRESULT result = mD3d12Pageable.As(&heap);
+ ASSERT(SUCCEEDED(result));
+ return heap;
+ }
+
+ ComPtr<ID3D12Pageable> Heap::GetD3D12Pageable() const {
+ return mD3d12Pageable;
+ }
+
+ uint64_t Heap::GetSize() const {
+ return mSize;
}
}} // namespace dawn_native::d3d12
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/HeapD3D12.h b/src/dawn_native/d3d12/HeapD3D12.h
index 834e42a..c3e648e 100644
--- a/src/dawn_native/d3d12/HeapD3D12.h
+++ b/src/dawn_native/d3d12/HeapD3D12.h
@@ -22,13 +22,17 @@
class Heap : public ResourceHeapBase {
public:
- Heap(ComPtr<ID3D12Heap> heap);
+ Heap(ComPtr<ID3D12Pageable> d3d12Pageable, uint64_t size);
~Heap() = default;
ComPtr<ID3D12Heap> GetD3D12Heap() const;
+ ComPtr<ID3D12Pageable> GetD3D12Pageable() const;
+
+ uint64_t GetSize() const;
private:
- ComPtr<ID3D12Heap> mHeap;
+ ComPtr<ID3D12Pageable> mD3d12Pageable;
+ uint64_t mSize = 0;
};
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
index 1fed89f..257b003 100644
--- a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
+++ b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
@@ -187,6 +187,13 @@
mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial());
+ // Directly allocated ResourceHeapAllocations are created with a heap object that must be
+ // manually deleted upon deallocation. See ResourceAllocatorManager::CreateCommittedResource
+ // for more information.
+ if (allocation.GetInfo().mMethod == AllocationMethod::kDirect) {
+ delete allocation.GetResourceHeap();
+ }
+
// Invalidate the allocation immediately in case one accidentally
// calls DeallocateMemory again using the same allocation.
allocation.Invalidate();
@@ -246,7 +253,7 @@
return ResourceHeapAllocation{}; // invalid
}
- ID3D12Heap* heap = static_cast<Heap*>(allocation.GetResourceHeap())->GetD3D12Heap().Get();
+ Heap* heap = ToBackend(allocation.GetResourceHeap());
// With placed resources, a single heap can be reused.
// The resource placed at an offset is only reclaimed
@@ -256,13 +263,14 @@
// barrier).
// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource
ComPtr<ID3D12Resource> placedResource;
- DAWN_TRY(CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreatePlacedResource(
- heap, allocation.GetOffset(), &resourceDescriptor,
- initialUsage, nullptr, IID_PPV_ARGS(&placedResource)),
- "ID3D12Device::CreatePlacedResource"));
+ DAWN_TRY(CheckOutOfMemoryHRESULT(
+ mDevice->GetD3D12Device()->CreatePlacedResource(
+ heap->GetD3D12Heap().Get(), allocation.GetOffset(), &resourceDescriptor,
+ initialUsage, nullptr, IID_PPV_ARGS(&placedResource)),
+ "ID3D12Device::CreatePlacedResource"));
return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(),
- std::move(placedResource)};
+ std::move(placedResource), heap};
}
ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::CreateCommittedResource(
@@ -297,11 +305,18 @@
initialUsage, nullptr, IID_PPV_ARGS(&committedResource)),
"ID3D12Device::CreateCommittedResource"));
+ // When using CreateCommittedResource, D3D12 creates an implicit heap that contains the
+ // resource allocation. Because Dawn's memory residency management occurs at the resource
+ // heap granularity, every directly allocated ResourceHeapAllocation also stores a Heap
+ // object. This object is created manually, and must be deleted manually upon deallocation
+ // of the committed resource.
+ Heap* heap = new Heap(committedResource, resourceInfo.SizeInBytes);
+
AllocationInfo info;
info.mMethod = AllocationMethod::kDirect;
return ResourceHeapAllocation{info,
- /*offset*/ 0, std::move(committedResource)};
+ /*offset*/ 0, std::move(committedResource), heap};
}
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp b/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp
index bf805cb..c3a89f0 100644
--- a/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp
+++ b/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp
@@ -14,13 +14,17 @@
#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
+#include "dawn_native/d3d12/HeapD3D12.h"
+
#include <utility>
namespace dawn_native { namespace d3d12 {
ResourceHeapAllocation::ResourceHeapAllocation(const AllocationInfo& info,
uint64_t offset,
- ComPtr<ID3D12Resource> resource)
- : ResourceMemoryAllocation(info, offset, nullptr), mResource(std::move(resource)) {
+ ComPtr<ID3D12Resource> resource,
+ Heap* heap)
+ : ResourceMemoryAllocation(info, offset, heap), mResource(std::move(resource)) {
+ ASSERT((info.mMethod == AllocationMethod::kExternal) == (heap == nullptr));
}
void ResourceHeapAllocation::Invalidate() {
diff --git a/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h b/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h
index d764a96..71b00fd 100644
--- a/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h
+++ b/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h
@@ -20,12 +20,15 @@
namespace dawn_native { namespace d3d12 {
+ class Heap;
+
class ResourceHeapAllocation : public ResourceMemoryAllocation {
public:
ResourceHeapAllocation() = default;
ResourceHeapAllocation(const AllocationInfo& info,
uint64_t offset,
- ComPtr<ID3D12Resource> resource);
+ ComPtr<ID3D12Resource> resource,
+ Heap* heap);
~ResourceHeapAllocation() override = default;
void Invalidate() override;
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index b07c3ed..84c0cb4 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -322,8 +322,11 @@
mDxgiKeyedMutex = std::move(dxgiKeyedMutex);
AllocationInfo info;
- info.mMethod = AllocationMethod::kDirect;
- mResourceAllocation = {info, 0, std::move(d3d12Resource)};
+ info.mMethod = AllocationMethod::kExternal;
+ // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
+ // texture is owned externally. The texture's owning entity must remain responsible for
+ // memory management.
+ mResourceAllocation = {info, 0, std::move(d3d12Resource), nullptr};
return {};
}
@@ -370,8 +373,11 @@
ComPtr<ID3D12Resource> nativeTexture)
: TextureBase(device, descriptor, TextureState::OwnedExternal) {
AllocationInfo info;
- info.mMethod = AllocationMethod::kDirect;
- mResourceAllocation = {info, 0, std::move(nativeTexture)};
+ info.mMethod = AllocationMethod::kExternal;
+ // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the
+ // texture is owned externally. The texture's owning entity must remain responsible for
+ // memory management.
+ mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
SetIsSubresourceContentInitialized(true, 0, descriptor->mipLevelCount, 0,
descriptor->arrayLayerCount);