1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "common/Math.h" 16 17 #include "dawn_native/d3d12/D3D12Error.h" 18 #include "dawn_native/d3d12/DeviceD3D12.h" 19 #include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" 20 21 namespace dawn_native { namespace d3d12 { 22 StagingDescriptorAllocator(Device * device,uint32_t descriptorCount,uint32_t heapSize,D3D12_DESCRIPTOR_HEAP_TYPE heapType)23 StagingDescriptorAllocator::StagingDescriptorAllocator(Device* device, 24 uint32_t descriptorCount, 25 uint32_t heapSize, 26 D3D12_DESCRIPTOR_HEAP_TYPE heapType) 27 : mDevice(device), 28 mSizeIncrement(device->GetD3D12Device()->GetDescriptorHandleIncrementSize(heapType)), 29 mBlockSize(descriptorCount * mSizeIncrement), 30 mHeapSize(RoundUp(heapSize, descriptorCount)), 31 mHeapType(heapType) { 32 ASSERT(descriptorCount <= heapSize); 33 } 34 ~StagingDescriptorAllocator()35 StagingDescriptorAllocator::~StagingDescriptorAllocator() { 36 const Index freeBlockIndicesSize = GetFreeBlockIndicesSize(); 37 for (auto& buffer : mPool) { 38 ASSERT(buffer.freeBlockIndices.size() == freeBlockIndicesSize); 39 } 40 ASSERT(mAvailableHeaps.size() == mPool.size()); 41 } 42 43 ResultOrError<CPUDescriptorHeapAllocation> AllocateCPUDescriptors()44 StagingDescriptorAllocator::AllocateCPUDescriptors() { 45 if (mAvailableHeaps.empty()) { 46 DAWN_TRY(AllocateCPUHeap()); 47 } 48 49 ASSERT(!mAvailableHeaps.empty()); 50 51 const uint32_t heapIndex = mAvailableHeaps.back(); 52 NonShaderVisibleBuffer& buffer = mPool[heapIndex]; 53 54 ASSERT(!buffer.freeBlockIndices.empty()); 55 56 const Index blockIndex = buffer.freeBlockIndices.back(); 57 58 buffer.freeBlockIndices.pop_back(); 59 60 if (buffer.freeBlockIndices.empty()) { 61 mAvailableHeaps.pop_back(); 62 } 63 64 const D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor = { 65 buffer.heap->GetCPUDescriptorHandleForHeapStart().ptr + (blockIndex * mBlockSize)}; 66 67 return CPUDescriptorHeapAllocation{baseCPUDescriptor, heapIndex}; 68 } 69 AllocateCPUHeap()70 MaybeError StagingDescriptorAllocator::AllocateCPUHeap() { 71 D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; 72 heapDescriptor.Type = mHeapType; 73 heapDescriptor.NumDescriptors = mHeapSize; 74 heapDescriptor.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 75 heapDescriptor.NodeMask = 0; 76 77 ComPtr<ID3D12DescriptorHeap> heap; 78 DAWN_TRY(CheckHRESULT( 79 mDevice->GetD3D12Device()->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&heap)), 80 "ID3D12Device::CreateDescriptorHeap")); 81 82 NonShaderVisibleBuffer newBuffer; 83 newBuffer.heap = std::move(heap); 84 85 const Index freeBlockIndicesSize = GetFreeBlockIndicesSize(); 86 newBuffer.freeBlockIndices.reserve(freeBlockIndicesSize); 87 88 for (Index blockIndex = 0; blockIndex < freeBlockIndicesSize; blockIndex++) { 89 newBuffer.freeBlockIndices.push_back(blockIndex); 90 } 91 92 mAvailableHeaps.push_back(mPool.size()); 93 mPool.emplace_back(std::move(newBuffer)); 94 95 return {}; 96 } 97 Deallocate(CPUDescriptorHeapAllocation * allocation)98 void StagingDescriptorAllocator::Deallocate(CPUDescriptorHeapAllocation* allocation) { 99 ASSERT(allocation->IsValid()); 100 101 const uint32_t heapIndex = allocation->GetHeapIndex(); 102 103 ASSERT(heapIndex < mPool.size()); 104 105 // Insert the deallocated block back into the free-list. Order does not matter. However, 106 // having blocks be non-contigious could slow down future allocations due to poor cache 107 // locality. 108 // TODO(dawn:155): Consider more optimization. 109 std::vector<Index>& freeBlockIndices = mPool[heapIndex].freeBlockIndices; 110 if (freeBlockIndices.empty()) { 111 mAvailableHeaps.emplace_back(heapIndex); 112 } 113 114 const D3D12_CPU_DESCRIPTOR_HANDLE heapStart = 115 mPool[heapIndex].heap->GetCPUDescriptorHandleForHeapStart(); 116 117 const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = allocation->OffsetFrom(0, 0); 118 119 const Index blockIndex = (baseDescriptor.ptr - heapStart.ptr) / mBlockSize; 120 121 freeBlockIndices.emplace_back(blockIndex); 122 123 // Invalidate the handle in case the developer accidentally uses it again. 124 allocation->Invalidate(); 125 } 126 GetSizeIncrement() const127 uint32_t StagingDescriptorAllocator::GetSizeIncrement() const { 128 return mSizeIncrement; 129 } 130 GetFreeBlockIndicesSize() const131 StagingDescriptorAllocator::Index StagingDescriptorAllocator::GetFreeBlockIndicesSize() const { 132 return ((mHeapSize * mSizeIncrement) / mBlockSize); 133 } 134 135 ResultOrError<CPUDescriptorHeapAllocation> AllocateTransientCPUDescriptors()136 StagingDescriptorAllocator::AllocateTransientCPUDescriptors() { 137 CPUDescriptorHeapAllocation allocation; 138 DAWN_TRY_ASSIGN(allocation, AllocateCPUDescriptors()); 139 mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial()); 140 return allocation; 141 } 142 Tick(ExecutionSerial completedSerial)143 void StagingDescriptorAllocator::Tick(ExecutionSerial completedSerial) { 144 for (CPUDescriptorHeapAllocation& allocation : 145 mAllocationsToDelete.IterateUpTo(completedSerial)) { 146 Deallocate(&allocation); 147 } 148 149 mAllocationsToDelete.ClearUpTo(completedSerial); 150 } 151 152 }} // namespace dawn_native::d3d12 153