diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn index e4f03b5d1d..25f02f939c 100644 --- a/src/dawn_native/BUILD.gn +++ b/src/dawn_native/BUILD.gn @@ -312,6 +312,8 @@ source_set("dawn_native_sources") { "d3d12/HeapD3D12.h", "d3d12/NativeSwapChainImplD3D12.cpp", "d3d12/NativeSwapChainImplD3D12.h", + "d3d12/PageableD3D12.cpp", + "d3d12/PageableD3D12.h", "d3d12/PipelineLayoutD3D12.cpp", "d3d12/PipelineLayoutD3D12.h", "d3d12/PlatformFunctions.cpp", diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt index 6232477a0f..c51ae8263f 100644 --- a/src/dawn_native/CMakeLists.txt +++ b/src/dawn_native/CMakeLists.txt @@ -193,6 +193,8 @@ if (DAWN_ENABLE_D3D12) "d3d12/HeapD3D12.h" "d3d12/NativeSwapChainImplD3D12.cpp" "d3d12/NativeSwapChainImplD3D12.h" + "d3d12/PageableD3D12.cpp" + "d3d12/PageableD3D12.h" "d3d12/PipelineLayoutD3D12.cpp" "d3d12/PipelineLayoutD3D12.h" "d3d12/PlatformFunctions.cpp" diff --git a/src/dawn_native/d3d12/HeapD3D12.cpp b/src/dawn_native/d3d12/HeapD3D12.cpp index add21cfcce..ade5d4a5c1 100644 --- a/src/dawn_native/d3d12/HeapD3D12.cpp +++ b/src/dawn_native/d3d12/HeapD3D12.cpp @@ -16,16 +16,8 @@ namespace dawn_native { namespace d3d12 { Heap::Heap(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size) - : mD3d12Pageable(std::move(d3d12Pageable)), mMemorySegment(memorySegment), mSize(size) { - } - - Heap::~Heap() { - // When a heap is destroyed, it no longer resides in resident memory, so we must evict - // it from the LRU cache. If this heap is not manually removed from the LRU-cache, the - // ResidencyManager will attempt to use it after it has been deallocated. - if (IsInResidencyLRUCache()) { - RemoveFromList(); - } + : Pageable(std::move(d3d12Pageable), memorySegment, size) { + mD3d12Pageable.As(&mD3d12Heap); } // This function should only be used when mD3D12Pageable was initialized from a @@ -33,58 +25,7 @@ namespace dawn_native { namespace d3d12 { // ID3D12Pageable was initially created as an ID3D12Resource (i.e. DirectAllocation), then // use GetD3D12Pageable(). ID3D12Heap* Heap::GetD3D12Heap() const { - ComPtr heap; - HRESULT result = mD3d12Pageable.As(&heap); - ASSERT(SUCCEEDED(result)); - return heap.Get(); - } - - ID3D12Pageable* Heap::GetD3D12Pageable() const { - return mD3d12Pageable.Get(); - } - - MemorySegment Heap::GetMemorySegment() const { - return mMemorySegment; - } - - Serial Heap::GetLastUsage() const { - return mLastUsage; - } - - void Heap::SetLastUsage(Serial serial) { - mLastUsage = serial; - } - - uint64_t Heap::GetLastSubmission() const { - return mLastSubmission; - } - - void Heap::SetLastSubmission(Serial serial) { - mLastSubmission = serial; - } - - uint64_t Heap::GetSize() const { - return mSize; - } - - bool Heap::IsInResidencyLRUCache() const { - return IsInList(); - } - - void Heap::IncrementResidencyLock() { - mResidencyLockRefCount++; - } - - void Heap::DecrementResidencyLock() { - mResidencyLockRefCount--; - } - - bool Heap::IsResidencyLocked() const { - if (mResidencyLockRefCount == 0) { - return false; - } - - return true; + return mD3d12Heap.Get(); } }} // 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 71a4a0f204..715ffcdaf1 100644 --- a/src/dawn_native/d3d12/HeapD3D12.h +++ b/src/dawn_native/d3d12/HeapD3D12.h @@ -15,67 +15,25 @@ #ifndef DAWNNATIVE_D3D12_HEAPD3D12_H_ #define DAWNNATIVE_D3D12_HEAPD3D12_H_ -#include "common/LinkedList.h" -#include "common/Serial.h" -#include "dawn_native/D3D12Backend.h" #include "dawn_native/ResourceHeap.h" +#include "dawn_native/d3d12/PageableD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { class Device; - // This class is used to represent heap allocations, but also serves as a node within the - // ResidencyManager's LRU cache. This node is inserted into the LRU-cache when it is first - // allocated, and any time it is scheduled to be used by the GPU. This node is removed from the - // LRU cache when it is evicted from resident memory due to budget constraints, or when the heap - // is destroyed. - class Heap : public ResourceHeapBase, public LinkNode { + // This class is used to represent ID3D12Heap allocations, as well as an implicit heap + // representing a directly allocated resource. It inherits from Pageable because each Heap must + // be represented in the ResidencyManager. + class Heap : public ResourceHeapBase, public Pageable { public: Heap(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size); - ~Heap(); ID3D12Heap* GetD3D12Heap() const; - ID3D12Pageable* GetD3D12Pageable() const; - MemorySegment GetMemorySegment() const; - - // We set mLastRecordingSerial to denote the serial this heap was last recorded to be used. - // We must check this serial against the current serial when recording heap usages to ensure - // we do not process residency for this heap multiple times. - Serial GetLastUsage() const; - void SetLastUsage(Serial serial); - - // The residency manager must know the last serial that any portion of the heap was - // submitted to be used so that we can ensure this heap stays resident in memory at least - // until that serial has completed. - uint64_t GetLastSubmission() const; - void SetLastSubmission(Serial serial); - - uint64_t GetSize() const; - - bool IsInResidencyLRUCache() const; - - // In some scenarios, such as async buffer mapping, we must lock residency to ensure the - // heap cannot be evicted. Because multiple buffers may be mapped in a single heap, we must - // track the number of resources currently locked. - void IncrementResidencyLock(); - void DecrementResidencyLock(); - bool IsResidencyLocked() const; private: - ComPtr mD3d12Pageable; - MemorySegment mMemorySegment; - // mLastUsage denotes the last time this heap was recorded for use. - Serial mLastUsage = 0; - // mLastSubmission denotes the last time this heap was submitted to the GPU. Note that - // although this variable often contains the same value as mLastUsage, it can differ in some - // situations. When some asynchronous APIs (like SetSubData) are called, mLastUsage is - // updated upon the call, but the backend operation is deferred until the next submission - // to the GPU. This makes mLastSubmission unique from mLastUsage, and allows us to - // accurately identify when heaps are evictable. - Serial mLastSubmission = 0; - uint32_t mResidencyLockRefCount = 0; - uint64_t mSize = 0; + ComPtr mD3d12Heap; }; }} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/PageableD3D12.cpp b/src/dawn_native/d3d12/PageableD3D12.cpp new file mode 100644 index 0000000000..5884780864 --- /dev/null +++ b/src/dawn_native/d3d12/PageableD3D12.cpp @@ -0,0 +1,76 @@ +// Copyright 2020 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/PageableD3D12.h" + +namespace dawn_native { namespace d3d12 { + Pageable::Pageable(ComPtr d3d12Pageable, + MemorySegment memorySegment, + uint64_t size) + : mD3d12Pageable(std::move(d3d12Pageable)), mMemorySegment(memorySegment), mSize(size) { + } + + // When a pageable is destroyed, it no longer resides in resident memory, so we must evict + // it from the LRU cache. If this heap is not manually removed from the LRU-cache, the + // ResidencyManager will attempt to use it after it has been deallocated. + Pageable::~Pageable() { + if (IsInResidencyLRUCache()) { + RemoveFromList(); + } + } + + ID3D12Pageable* Pageable::GetD3D12Pageable() const { + return mD3d12Pageable.Get(); + } + + Serial Pageable::GetLastUsage() const { + return mLastUsage; + } + + void Pageable::SetLastUsage(Serial serial) { + mLastUsage = serial; + } + + uint64_t Pageable::GetLastSubmission() const { + return mLastSubmission; + } + + void Pageable::SetLastSubmission(Serial serial) { + mLastSubmission = serial; + } + + MemorySegment Pageable::GetMemorySegment() const { + return mMemorySegment; + } + + uint64_t Pageable::GetSize() const { + return mSize; + } + + bool Pageable::IsInResidencyLRUCache() const { + return IsInList(); + } + + void Pageable::IncrementResidencyLock() { + mResidencyLockRefCount++; + } + + void Pageable::DecrementResidencyLock() { + mResidencyLockRefCount--; + } + + bool Pageable::IsResidencyLocked() const { + return mResidencyLockRefCount != 0; + } +}} // namespace dawn_native::d3d12 \ No newline at end of file diff --git a/src/dawn_native/d3d12/PageableD3D12.h b/src/dawn_native/d3d12/PageableD3D12.h new file mode 100644 index 0000000000..4729b4b250 --- /dev/null +++ b/src/dawn_native/d3d12/PageableD3D12.h @@ -0,0 +1,80 @@ +// Copyright 2020 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_PAGEABLED3D12_H_ +#define DAWNNATIVE_D3D12_PAGEABLED3D12_H_ + +#include "common/LinkedList.h" +#include "common/Serial.h" +#include "dawn_native/D3D12Backend.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + // This class is used to represent ID3D12Pageable allocations, and also serves as a node within + // the ResidencyManager's LRU cache. This node is inserted into the LRU-cache when it is first + // allocated, and any time it is scheduled to be used by the GPU. This node is removed from the + // LRU cache when it is evicted from resident memory due to budget constraints, or when the + // pageable allocation is released. + class Pageable : public LinkNode { + public: + Pageable(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size); + ~Pageable(); + + ID3D12Pageable* GetD3D12Pageable() const; + + // We set mLastRecordingSerial to denote the serial this pageable was last recorded to be + // used. We must check this serial against the current serial when recording usages to + // ensure we do not process residency for this pageable multiple times. + Serial GetLastUsage() const; + void SetLastUsage(Serial serial); + + // The residency manager must know the last serial that any portion of the pageable was + // submitted to be used so that we can ensure this pageable stays resident in memory at + // least until that serial has completed. + uint64_t GetLastSubmission() const; + void SetLastSubmission(Serial serial); + + MemorySegment GetMemorySegment() const; + + uint64_t GetSize() const; + + bool IsInResidencyLRUCache() const; + + // In some scenarios, such as async buffer mapping or descriptor heaps, we must lock + // residency to ensure the pageable cannot be evicted. Because multiple buffers may be + // mapped in a single heap, we must track the number of resources currently locked. + void IncrementResidencyLock(); + void DecrementResidencyLock(); + bool IsResidencyLocked() const; + + protected: + ComPtr mD3d12Pageable; + + private: + // mLastUsage denotes the last time this pageable was recorded for use. + Serial mLastUsage = 0; + // mLastSubmission denotes the last time this pageable was submitted to the GPU. Note that + // although this variable often contains the same value as mLastUsage, it can differ in some + // situations. When some asynchronous APIs (like SetSubData) are called, mLastUsage is + // updated upon the call, but the backend operation is deferred until the next submission + // to the GPU. This makes mLastSubmission unique from mLastUsage, and allows us to + // accurately identify when a pageable can be evicted. + Serial mLastSubmission = 0; + MemorySegment mMemorySegment; + uint32_t mResidencyLockRefCount = 0; + uint64_t mSize = 0; + }; +}} // namespace dawn_native::d3d12 + +#endif \ No newline at end of file diff --git a/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp b/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp index c65d56a242..9ba15df581 100644 --- a/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp +++ b/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp @@ -144,12 +144,12 @@ namespace dawn_native { namespace d3d12 { // Removes a heap from the LRU and returns the least recently used heap when possible. Returns // nullptr when nothing further can be evicted. - ResultOrError ResidencyManager::RemoveSingleEntryFromLRU( + ResultOrError ResidencyManager::RemoveSingleEntryFromLRU( MemorySegmentInfo* memorySegment) { ASSERT(!memorySegment->lruCache.empty()); - Heap* heap = memorySegment->lruCache.head()->value(); + Pageable* pageable = memorySegment->lruCache.head()->value(); - Serial lastSubmissionSerial = heap->GetLastSubmission(); + Serial lastSubmissionSerial = pageable->GetLastSubmission(); // If the next candidate for eviction was inserted into the LRU during the current serial, // it is because more memory is being used in a single command list than is available. @@ -164,8 +164,8 @@ namespace dawn_native { namespace d3d12 { DAWN_TRY(mDevice->WaitForSerial(lastSubmissionSerial)); } - heap->RemoveFromList(); - return heap; + pageable->RemoveFromList(); + return pageable; } MaybeError ResidencyManager::EnsureCanAllocate(uint64_t allocationSize, @@ -197,16 +197,16 @@ namespace dawn_native { namespace d3d12 { uint64_t sizeNeededToBeUnderBudget = memoryUsageAfterMakeResident - memorySegment->budget; uint64_t sizeEvicted = 0; while (sizeEvicted < sizeNeededToBeUnderBudget) { - Heap* heap; - DAWN_TRY_ASSIGN(heap, RemoveSingleEntryFromLRU(memorySegment)); + Pageable* pageable; + DAWN_TRY_ASSIGN(pageable, RemoveSingleEntryFromLRU(memorySegment)); // If no heap was returned, then nothing more can be evicted. - if (heap == nullptr) { + if (pageable == nullptr) { break; } - sizeEvicted += heap->GetSize(); - resourcesToEvict.push_back(heap->GetD3D12Pageable()); + sizeEvicted += pageable->GetSize(); + resourcesToEvict.push_back(pageable->GetD3D12Pageable()); } if (resourcesToEvict.size() > 0) { @@ -287,13 +287,13 @@ namespace dawn_native { namespace d3d12 { // Inserts a heap at the bottom of the LRU. The passed heap must be resident or scheduled to // become resident within the current serial. - void ResidencyManager::TrackResidentAllocation(Heap* heap) { + void ResidencyManager::TrackResidentAllocation(Pageable* pageable) { if (!mResidencyManagementEnabled) { return; } - ASSERT(heap->IsInList() == false); - GetMemorySegmentInfo(heap->GetMemorySegment())->lruCache.Append(heap); + ASSERT(pageable->IsInList() == false); + GetMemorySegmentInfo(pageable->GetMemorySegment())->lruCache.Append(pageable); } // Places an artifical cap on Dawn's budget so we can test in a predictable manner. If used, diff --git a/src/dawn_native/d3d12/ResidencyManagerD3D12.h b/src/dawn_native/d3d12/ResidencyManagerD3D12.h index abd6add72e..1d97d4d593 100644 --- a/src/dawn_native/d3d12/ResidencyManagerD3D12.h +++ b/src/dawn_native/d3d12/ResidencyManagerD3D12.h @@ -27,6 +27,7 @@ namespace dawn_native { namespace d3d12 { class Device; class Heap; + class Pageable; class ResidencyManager { public: @@ -41,14 +42,14 @@ namespace dawn_native { namespace d3d12 { uint64_t SetExternalMemoryReservation(MemorySegment segment, uint64_t requestedReservationSize); - void TrackResidentAllocation(Heap* heap); + void TrackResidentAllocation(Pageable* pageable); void RestrictBudgetForTesting(uint64_t artificialBudgetCap); private: struct MemorySegmentInfo { const DXGI_MEMORY_SEGMENT_GROUP dxgiSegment; - LinkedList lruCache = {}; + LinkedList lruCache = {}; uint64_t budget = 0; uint64_t usage = 0; uint64_t externalReservation = 0; @@ -62,7 +63,7 @@ namespace dawn_native { namespace d3d12 { MemorySegmentInfo* GetMemorySegmentInfo(MemorySegment memorySegment); MaybeError EnsureCanMakeResident(uint64_t allocationSize, MemorySegmentInfo* memorySegment); - ResultOrError RemoveSingleEntryFromLRU(MemorySegmentInfo* memorySegment); + ResultOrError RemoveSingleEntryFromLRU(MemorySegmentInfo* memorySegment); void UpdateVideoMemoryInfo(); void UpdateMemorySegmentInfo(MemorySegmentInfo* segmentInfo); diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp index 9c36ac5030..5a3015bdbf 100644 --- a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp +++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp @@ -77,7 +77,7 @@ namespace dawn_native { namespace d3d12 { return false; } - ID3D12DescriptorHeap* descriptorHeap = mHeap.Get(); + ID3D12DescriptorHeap* descriptorHeap = mHeap->GetD3D12DescriptorHeap(); const uint64_t heapOffset = mSizeIncrement * startOffset; @@ -99,7 +99,7 @@ namespace dawn_native { namespace d3d12 { } ID3D12DescriptorHeap* ShaderVisibleDescriptorAllocator::GetShaderVisibleHeap() const { - return mHeap.Get(); + return mHeap->GetD3D12DescriptorHeap(); } void ShaderVisibleDescriptorAllocator::Tick(uint64_t completedSerial) { @@ -108,7 +108,7 @@ namespace dawn_native { namespace d3d12 { // Creates a GPU descriptor heap that manages descriptors in a FIFO queue. MaybeError ShaderVisibleDescriptorAllocator::AllocateAndSwitchShaderVisibleHeap() { - ComPtr heap; + std::unique_ptr descriptorHeap; // Return the switched out heap to the pool and retrieve the oldest heap that is no longer // used by GPU. This maintains a heap buffer to avoid frequently re-creating heaps for heavy // users. @@ -119,7 +119,7 @@ namespace dawn_native { namespace d3d12 { // Recycle existing heap if possible. if (!mPool.empty() && mPool.front().heapSerial <= mDevice->GetCompletedCommandSerial()) { - heap = std::move(mPool.front().heap); + descriptorHeap = std::move(mPool.front().heap); mPool.pop_front(); } @@ -129,19 +129,23 @@ namespace dawn_native { namespace d3d12 { const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize( mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); - if (heap == nullptr) { + if (descriptorHeap == nullptr) { + ComPtr d3d12DescriptorHeap; D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; heapDescriptor.Type = mHeapType; heapDescriptor.NumDescriptors = descriptorCount; heapDescriptor.Flags = GetD3D12HeapFlags(mHeapType); heapDescriptor.NodeMask = 0; - DAWN_TRY(CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap( - &heapDescriptor, IID_PPV_ARGS(&heap)), - "ID3D12Device::CreateDescriptorHeap")); + DAWN_TRY( + CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap( + &heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)), + "ID3D12Device::CreateDescriptorHeap")); + descriptorHeap = std::make_unique( + std::move(d3d12DescriptorHeap), mSizeIncrement * descriptorCount); } // Create a FIFO buffer from the recently created heap. - mHeap = std::move(heap); + mHeap = std::move(descriptorHeap); mAllocator = RingBufferAllocator(descriptorCount); // Invalidate all bindgroup allocations on previously bound heaps by incrementing the heap @@ -171,4 +175,15 @@ namespace dawn_native { namespace d3d12 { return (allocation.GetLastUsageSerial() > mDevice->GetCompletedCommandSerial() && allocation.GetHeapSerial() == mHeapSerial); } + + ShaderVisibleDescriptorHeap::ShaderVisibleDescriptorHeap( + ComPtr d3d12DescriptorHeap, + uint64_t size) + : Pageable(d3d12DescriptorHeap, MemorySegment::Local, size), + mD3d12DescriptorHeap(std::move(d3d12DescriptorHeap)) { + } + + ID3D12DescriptorHeap* ShaderVisibleDescriptorHeap::GetD3D12DescriptorHeap() const { + return mD3d12DescriptorHeap.Get(); + } }} // namespace dawn_native::d3d12 \ No newline at end of file diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h index be4e83974d..da8a5e36b7 100644 --- a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h +++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h @@ -17,6 +17,7 @@ #include "dawn_native/Error.h" #include "dawn_native/RingBufferAllocator.h" +#include "dawn_native/d3d12/PageableD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" #include @@ -32,6 +33,16 @@ namespace dawn_native { namespace d3d12 { class Device; class GPUDescriptorHeapAllocation; + class ShaderVisibleDescriptorHeap : public Pageable { + public: + ShaderVisibleDescriptorHeap(ComPtr d3d12DescriptorHeap, + uint64_t size); + ID3D12DescriptorHeap* GetD3D12DescriptorHeap() const; + + private: + ComPtr mD3d12DescriptorHeap; + }; + class ShaderVisibleDescriptorAllocator { public: static ResultOrError> Create( @@ -62,10 +73,10 @@ namespace dawn_native { namespace d3d12 { private: struct SerialDescriptorHeap { Serial heapSerial; - ComPtr heap; + std::unique_ptr heap; }; - ComPtr mHeap; + std::unique_ptr mHeap; RingBufferAllocator mAllocator; std::list mPool; D3D12_DESCRIPTOR_HEAP_TYPE mHeapType;