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 "dawn_native/vulkan/DescriptorSetAllocator.h" 16 17 #include "dawn_native/vulkan/BindGroupLayoutVk.h" 18 #include "dawn_native/vulkan/DeviceVk.h" 19 #include "dawn_native/vulkan/FencedDeleter.h" 20 #include "dawn_native/vulkan/VulkanError.h" 21 22 namespace dawn_native { namespace vulkan { 23 24 // TODO(enga): Figure out this value. 25 static constexpr uint32_t kMaxDescriptorsPerPool = 512; 26 27 // static Create(BindGroupLayout * layout,std::map<VkDescriptorType,uint32_t> descriptorCountPerType)28 Ref<DescriptorSetAllocator> DescriptorSetAllocator::Create( 29 BindGroupLayout* layout, 30 std::map<VkDescriptorType, uint32_t> descriptorCountPerType) { 31 return AcquireRef(new DescriptorSetAllocator(layout, descriptorCountPerType)); 32 } 33 DescriptorSetAllocator(BindGroupLayout * layout,std::map<VkDescriptorType,uint32_t> descriptorCountPerType)34 DescriptorSetAllocator::DescriptorSetAllocator( 35 BindGroupLayout* layout, 36 std::map<VkDescriptorType, uint32_t> descriptorCountPerType) 37 : ObjectBase(layout->GetDevice()), mLayout(layout) { 38 ASSERT(layout != nullptr); 39 40 // Compute the total number of descriptors for this layout. 41 uint32_t totalDescriptorCount = 0; 42 mPoolSizes.reserve(descriptorCountPerType.size()); 43 for (const auto& it : descriptorCountPerType) { 44 ASSERT(it.second > 0); 45 totalDescriptorCount += it.second; 46 mPoolSizes.push_back(VkDescriptorPoolSize{it.first, it.second}); 47 } 48 49 if (totalDescriptorCount == 0) { 50 // Vulkan requires that valid usage of vkCreateDescriptorPool must have a non-zero 51 // number of pools, each of which has non-zero descriptor counts. 52 // Since the descriptor set layout is empty, we should be able to allocate 53 // |kMaxDescriptorsPerPool| sets from this 1-sized descriptor pool. 54 // The type of this descriptor pool doesn't matter because it is never used. 55 mPoolSizes.push_back(VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}); 56 mMaxSets = kMaxDescriptorsPerPool; 57 } else { 58 ASSERT(totalDescriptorCount <= kMaxBindingsPerPipelineLayout); 59 static_assert(kMaxBindingsPerPipelineLayout <= kMaxDescriptorsPerPool, ""); 60 61 // Compute the total number of descriptors sets that fits given the max. 62 mMaxSets = kMaxDescriptorsPerPool / totalDescriptorCount; 63 ASSERT(mMaxSets > 0); 64 65 // Grow the number of desciptors in the pool to fit the computed |mMaxSets|. 66 for (auto& poolSize : mPoolSizes) { 67 poolSize.descriptorCount *= mMaxSets; 68 } 69 } 70 } 71 ~DescriptorSetAllocator()72 DescriptorSetAllocator::~DescriptorSetAllocator() { 73 for (auto& pool : mDescriptorPools) { 74 ASSERT(pool.freeSetIndices.size() == mMaxSets); 75 if (pool.vkPool != VK_NULL_HANDLE) { 76 Device* device = ToBackend(GetDevice()); 77 device->GetFencedDeleter()->DeleteWhenUnused(pool.vkPool); 78 } 79 } 80 } 81 Allocate()82 ResultOrError<DescriptorSetAllocation> DescriptorSetAllocator::Allocate() { 83 if (mAvailableDescriptorPoolIndices.empty()) { 84 DAWN_TRY(AllocateDescriptorPool()); 85 } 86 87 ASSERT(!mAvailableDescriptorPoolIndices.empty()); 88 89 const PoolIndex poolIndex = mAvailableDescriptorPoolIndices.back(); 90 DescriptorPool* pool = &mDescriptorPools[poolIndex]; 91 92 ASSERT(!pool->freeSetIndices.empty()); 93 94 SetIndex setIndex = pool->freeSetIndices.back(); 95 pool->freeSetIndices.pop_back(); 96 97 if (pool->freeSetIndices.empty()) { 98 mAvailableDescriptorPoolIndices.pop_back(); 99 } 100 101 return DescriptorSetAllocation{pool->sets[setIndex], poolIndex, setIndex}; 102 } 103 Deallocate(DescriptorSetAllocation * allocationInfo)104 void DescriptorSetAllocator::Deallocate(DescriptorSetAllocation* allocationInfo) { 105 ASSERT(allocationInfo != nullptr); 106 ASSERT(allocationInfo->set != VK_NULL_HANDLE); 107 108 // We can't reuse the descriptor set right away because the Vulkan spec says in the 109 // documentation for vkCmdBindDescriptorSets that the set may be consumed any time between 110 // host execution of the command and the end of the draw/dispatch. 111 Device* device = ToBackend(GetDevice()); 112 const ExecutionSerial serial = device->GetPendingCommandSerial(); 113 mPendingDeallocations.Enqueue({allocationInfo->poolIndex, allocationInfo->setIndex}, 114 serial); 115 116 if (mLastDeallocationSerial != serial) { 117 device->EnqueueDeferredDeallocation(this); 118 mLastDeallocationSerial = serial; 119 } 120 121 // Clear the content of allocation so that use after frees are more visible. 122 *allocationInfo = {}; 123 } 124 FinishDeallocation(ExecutionSerial completedSerial)125 void DescriptorSetAllocator::FinishDeallocation(ExecutionSerial completedSerial) { 126 for (const Deallocation& dealloc : mPendingDeallocations.IterateUpTo(completedSerial)) { 127 ASSERT(dealloc.poolIndex < mDescriptorPools.size()); 128 129 auto& freeSetIndices = mDescriptorPools[dealloc.poolIndex].freeSetIndices; 130 if (freeSetIndices.empty()) { 131 mAvailableDescriptorPoolIndices.emplace_back(dealloc.poolIndex); 132 } 133 freeSetIndices.emplace_back(dealloc.setIndex); 134 } 135 mPendingDeallocations.ClearUpTo(completedSerial); 136 } 137 AllocateDescriptorPool()138 MaybeError DescriptorSetAllocator::AllocateDescriptorPool() { 139 VkDescriptorPoolCreateInfo createInfo; 140 createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 141 createInfo.pNext = nullptr; 142 createInfo.flags = 0; 143 createInfo.maxSets = mMaxSets; 144 createInfo.poolSizeCount = static_cast<PoolIndex>(mPoolSizes.size()); 145 createInfo.pPoolSizes = mPoolSizes.data(); 146 147 Device* device = ToBackend(GetDevice()); 148 149 VkDescriptorPool descriptorPool; 150 DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, 151 nullptr, &*descriptorPool), 152 "CreateDescriptorPool")); 153 154 std::vector<VkDescriptorSetLayout> layouts(mMaxSets, mLayout->GetHandle()); 155 156 VkDescriptorSetAllocateInfo allocateInfo; 157 allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; 158 allocateInfo.pNext = nullptr; 159 allocateInfo.descriptorPool = descriptorPool; 160 allocateInfo.descriptorSetCount = mMaxSets; 161 allocateInfo.pSetLayouts = AsVkArray(layouts.data()); 162 163 std::vector<VkDescriptorSet> sets(mMaxSets); 164 MaybeError result = 165 CheckVkSuccess(device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, 166 AsVkArray(sets.data())), 167 "AllocateDescriptorSets"); 168 if (result.IsError()) { 169 // On an error we can destroy the pool immediately because no command references it. 170 device->fn.DestroyDescriptorPool(device->GetVkDevice(), descriptorPool, nullptr); 171 DAWN_TRY(std::move(result)); 172 } 173 174 std::vector<SetIndex> freeSetIndices; 175 freeSetIndices.reserve(mMaxSets); 176 177 for (SetIndex i = 0; i < mMaxSets; ++i) { 178 freeSetIndices.push_back(i); 179 } 180 181 mAvailableDescriptorPoolIndices.push_back(mDescriptorPools.size()); 182 mDescriptorPools.emplace_back( 183 DescriptorPool{descriptorPool, std::move(sets), std::move(freeSetIndices)}); 184 185 return {}; 186 } 187 188 }} // namespace dawn_native::vulkan 189