| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134 |
- // Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
- // All rights reserved.
- // Code licensed under the BSD License.
- // http://www.anki3d.org/LICENSE
- #include <AnKi/Gr/Vulkan/DescriptorSet.h>
- #include <AnKi/Gr/Buffer.h>
- #include <AnKi/Gr/Vulkan/BufferImpl.h>
- #include <AnKi/Util/List.h>
- #include <AnKi/Util/HashMap.h>
- #include <AnKi/Util/Tracer.h>
- #include <algorithm>
- namespace anki {
- thread_local DescriptorSetFactory::ThreadLocal* DescriptorSetFactory::m_threadLocal = nullptr;
- /// Wraps a global descriptor set that is used to store bindless textures.
- class DescriptorSetFactory::BindlessDescriptorSet
- {
- public:
- ~BindlessDescriptorSet();
- Error init(const U32 bindlessTextureCount, U32 bindlessImageCount);
- /// Bind a sampled image.
- /// @note It's thread-safe.
- U32 bindTexture(const VkImageView view, const VkImageLayout layout);
- /// Bind a uniform texel buffer.
- /// @note It's thread-safe.
- U32 bindUniformTexelBuffer(VkBufferView view);
- /// @note It's thread-safe.
- void unbindTexture(U32 idx)
- {
- unbindCommon(idx, m_freeTexIndices, m_freeTexIndexCount);
- }
- /// @note It's thread-safe.
- void unbindUniformTexelBuffer(U32 idx)
- {
- unbindCommon(idx, m_freeTexelBufferIndices, m_freeTexelBufferIndexCount);
- }
- DescriptorSet getDescriptorSet() const
- {
- ANKI_ASSERT(m_dset);
- DescriptorSet out;
- out.m_handle = m_dset;
- return out;
- }
- VkDescriptorSetLayout getDescriptorSetLayout() const
- {
- ANKI_ASSERT(m_layout);
- return m_layout;
- }
- private:
- VkDescriptorSetLayout m_layout = VK_NULL_HANDLE;
- VkDescriptorPool m_dsPool = VK_NULL_HANDLE;
- VkDescriptorSet m_dset = VK_NULL_HANDLE;
- Mutex m_mtx;
- GrDynamicArray<U16> m_freeTexIndices;
- GrDynamicArray<U16> m_freeTexelBufferIndices;
- U16 m_freeTexIndexCount = kMaxU16;
- U16 m_freeTexelBufferIndexCount = kMaxU16;
- void unbindCommon(U32 idx, GrDynamicArray<U16>& freeIndices, U16& freeIndexCount);
- };
- /// Descriptor set internal class.
- class DS : public IntrusiveListEnabled<DS>
- {
- public:
- VkDescriptorSet m_handle = {};
- U64 m_lastFrameUsed = kMaxU64;
- U64 m_hash;
- };
- /// Per thread allocator.
- class DescriptorSetFactory::DSAllocator
- {
- public:
- DSAllocator(const DSAllocator&) = delete; // Non-copyable
- DSAllocator& operator=(const DSAllocator&) = delete; // Non-copyable
- DSAllocator(const DSLayoutCacheEntry* layout)
- : m_layoutEntry(layout)
- {
- ANKI_ASSERT(m_layoutEntry);
- }
- ~DSAllocator();
- Error init();
- Error createNewPool();
- Error getOrCreateSet(U64 hash, const Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet>& bindings, StackMemoryPool& tmpPool, const DS*& out)
- {
- out = tryFindSet(hash);
- if(out == nullptr)
- {
- ANKI_CHECK(newSet(hash, bindings, tmpPool, out));
- }
- return Error::kNone;
- }
- private:
- const DSLayoutCacheEntry* m_layoutEntry; ///< Know your father.
- GrDynamicArray<VkDescriptorPool> m_pools;
- U32 m_lastPoolDSCount = 0;
- U32 m_lastPoolFreeDSCount = 0;
- IntrusiveList<DS> m_list; ///< At the left of the list are the least used sets.
- GrHashMap<U64, DS*> m_hashmap;
- [[nodiscard]] const DS* tryFindSet(U64 hash);
- Error newSet(U64 hash, const Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet>& bindings, StackMemoryPool& tmpPool, const DS*& out);
- void writeSet(const Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet>& bindings, const DS& set, StackMemoryPool& tmpPool);
- };
- class alignas(ANKI_CACHE_LINE_SIZE) DescriptorSetFactory::ThreadLocal
- {
- public:
- GrDynamicArray<DSAllocator*> m_allocators;
- };
- /// Cache entry. It's built around a specific descriptor set layout.
- class DSLayoutCacheEntry
- {
- public:
- DescriptorSetFactory* m_factory;
- U64 m_hash = 0; ///< Layout hash.
- VkDescriptorSetLayout m_layoutHandle = {};
- BitSet<kMaxBindingsPerDescriptorSet, U32> m_activeBindings = {false};
- Array<U32, kMaxBindingsPerDescriptorSet> m_bindingArraySize = {};
- Array<DescriptorType, kMaxBindingsPerDescriptorSet> m_bindingType = {};
- U32 m_minBinding = kMaxU32;
- U32 m_maxBinding = 0;
- U32 m_index = 0; ///< Index in DescriptorSetFactory::m_caches
- // Cache the create info
- Array<VkDescriptorPoolSize, U(DescriptorType::kCount)> m_poolSizesCreateInf = {};
- VkDescriptorPoolCreateInfo m_poolCreateInf = {};
- DSLayoutCacheEntry(DescriptorSetFactory* factory, U32 index)
- : m_factory(factory)
- , m_index(index)
- {
- }
- ~DSLayoutCacheEntry();
- Error init(const DescriptorBinding* bindings, U32 bindingCount, U64 hash);
- /// @note Thread-safe.
- Error getOrCreateDSAllocator(DescriptorSetFactory::DSAllocator*& alloc);
- };
- DescriptorSetFactory::BindlessDescriptorSet::~BindlessDescriptorSet()
- {
- ANKI_ASSERT(m_freeTexIndexCount == m_freeTexIndices.getSize() && "Forgot to unbind some textures");
- ANKI_ASSERT(m_freeTexelBufferIndexCount == m_freeTexelBufferIndices.getSize() && "Forgot to unbind some texel buffers");
- if(m_dsPool)
- {
- vkDestroyDescriptorPool(getVkDevice(), m_dsPool, nullptr);
- m_dsPool = VK_NULL_HANDLE;
- m_dset = VK_NULL_HANDLE;
- }
- if(m_layout)
- {
- vkDestroyDescriptorSetLayout(getVkDevice(), m_layout, nullptr);
- m_layout = VK_NULL_HANDLE;
- }
- }
- Error DescriptorSetFactory::BindlessDescriptorSet::init(U32 bindlessTextureCount, U32 bindlessTextureBuffers)
- {
- // Create the layout
- {
- Array<VkDescriptorSetLayoutBinding, 2> bindings = {};
- bindings[0].binding = 0;
- bindings[0].stageFlags = VK_SHADER_STAGE_ALL;
- bindings[0].descriptorCount = bindlessTextureCount;
- bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- bindings[1].binding = 1;
- bindings[1].stageFlags = VK_SHADER_STAGE_ALL;
- bindings[1].descriptorCount = bindlessTextureBuffers;
- bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
- Array<VkDescriptorBindingFlagsEXT, 2> bindingFlags = {};
- bindingFlags[0] = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT
- | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT;
- bindingFlags[1] = bindingFlags[0];
- VkDescriptorSetLayoutBindingFlagsCreateInfoEXT extraInfos = {};
- extraInfos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
- extraInfos.bindingCount = bindingFlags.getSize();
- extraInfos.pBindingFlags = &bindingFlags[0];
- VkDescriptorSetLayoutCreateInfo ci = {};
- ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- ci.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT;
- ci.bindingCount = bindings.getSize();
- ci.pBindings = &bindings[0];
- ci.pNext = &extraInfos;
- ANKI_VK_CHECK(vkCreateDescriptorSetLayout(getVkDevice(), &ci, nullptr, &m_layout));
- }
- // Create the pool
- {
- Array<VkDescriptorPoolSize, 2> sizes = {};
- sizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- sizes[0].descriptorCount = bindlessTextureCount;
- sizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
- sizes[1].descriptorCount = bindlessTextureBuffers;
- VkDescriptorPoolCreateInfo ci = {};
- ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
- ci.maxSets = 1;
- ci.poolSizeCount = sizes.getSize();
- ci.pPoolSizes = &sizes[0];
- ci.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
- ANKI_VK_CHECK(vkCreateDescriptorPool(getVkDevice(), &ci, nullptr, &m_dsPool));
- }
- // Create the descriptor set
- {
- VkDescriptorSetAllocateInfo ci = {};
- ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- ci.descriptorPool = m_dsPool;
- ci.descriptorSetCount = 1;
- ci.pSetLayouts = &m_layout;
- ANKI_VK_CHECK(vkAllocateDescriptorSets(getVkDevice(), &ci, &m_dset));
- }
- // Init the free arrays
- {
- m_freeTexIndices.resize(bindlessTextureCount);
- m_freeTexIndexCount = U16(m_freeTexIndices.getSize());
- for(U32 i = 0; i < m_freeTexIndices.getSize(); ++i)
- {
- m_freeTexIndices[i] = U16(m_freeTexIndices.getSize() - i - 1);
- }
- m_freeTexelBufferIndices.resize(bindlessTextureBuffers);
- m_freeTexelBufferIndexCount = U16(m_freeTexelBufferIndices.getSize());
- for(U32 i = 0; i < m_freeTexelBufferIndices.getSize(); ++i)
- {
- m_freeTexelBufferIndices[i] = U16(m_freeTexelBufferIndices.getSize() - i - 1);
- }
- }
- return Error::kNone;
- }
- U32 DescriptorSetFactory::BindlessDescriptorSet::bindTexture(const VkImageView view, const VkImageLayout layout)
- {
- ANKI_ASSERT(layout == VK_IMAGE_LAYOUT_GENERAL || layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
- ANKI_ASSERT(view);
- LockGuard<Mutex> lock(m_mtx);
- ANKI_ASSERT(m_freeTexIndexCount > 0 && "Out of indices");
- // Pop the index
- --m_freeTexIndexCount;
- const U16 idx = m_freeTexIndices[m_freeTexIndexCount];
- ANKI_ASSERT(idx < m_freeTexIndices.getSize());
- // Update the set
- VkDescriptorImageInfo imageInf = {};
- imageInf.imageView = view;
- imageInf.imageLayout = layout;
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.pNext = nullptr;
- write.dstSet = m_dset;
- write.dstBinding = 0;
- write.descriptorCount = 1;
- write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- write.dstArrayElement = idx;
- write.pImageInfo = &imageInf;
- vkUpdateDescriptorSets(getVkDevice(), 1, &write, 0, nullptr);
- return idx;
- }
- U32 DescriptorSetFactory::BindlessDescriptorSet::bindUniformTexelBuffer(VkBufferView view)
- {
- ANKI_ASSERT(view);
- LockGuard<Mutex> lock(m_mtx);
- ANKI_ASSERT(m_freeTexelBufferIndexCount > 0 && "Out of indices");
- // Pop the index
- --m_freeTexelBufferIndexCount;
- const U16 idx = m_freeTexelBufferIndices[m_freeTexelBufferIndexCount];
- ANKI_ASSERT(idx < m_freeTexelBufferIndices.getSize());
- // Update the set
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.pNext = nullptr;
- write.dstSet = m_dset;
- write.dstBinding = 1;
- write.descriptorCount = 1;
- write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
- write.dstArrayElement = idx;
- write.pTexelBufferView = &view;
- vkUpdateDescriptorSets(getVkDevice(), 1, &write, 0, nullptr);
- return idx;
- }
- void DescriptorSetFactory::BindlessDescriptorSet::unbindCommon(U32 idx, GrDynamicArray<U16>& freeIndices, U16& freeIndexCount)
- {
- LockGuard<Mutex> lock(m_mtx);
- ANKI_ASSERT(idx < freeIndices.getSize());
- ANKI_ASSERT(freeIndexCount < freeIndices.getSize());
- freeIndices[freeIndexCount] = U16(idx);
- ++freeIndexCount;
- // Sort the free indices to minimize fragmentation
- std::sort(&freeIndices[0], &freeIndices[0] + freeIndexCount, std::greater<U16>());
- // Make sure there are no duplicates
- for(U32 i = 1; i < freeIndexCount; ++i)
- {
- ANKI_ASSERT(freeIndices[i] != freeIndices[i - 1]);
- }
- }
- DescriptorSetFactory::DSAllocator::~DSAllocator()
- {
- while(!m_list.isEmpty())
- {
- DS* ds = &m_list.getFront();
- m_list.popFront();
- deleteInstance(GrMemoryPool::getSingleton(), ds);
- }
- for(VkDescriptorPool pool : m_pools)
- {
- vkDestroyDescriptorPool(getVkDevice(), pool, nullptr);
- }
- }
- Error DescriptorSetFactory::DSAllocator::init()
- {
- ANKI_CHECK(createNewPool());
- return Error::kNone;
- }
- Error DescriptorSetFactory::DSAllocator::createNewPool()
- {
- m_lastPoolDSCount = (m_lastPoolDSCount != 0) ? U32(F32(m_lastPoolDSCount) * kDescriptorPoolSizeScale) : kDescriptorPoolInitialSize;
- m_lastPoolFreeDSCount = m_lastPoolDSCount;
- // Set the create info
- Array<VkDescriptorPoolSize, U(DescriptorType::kCount)> poolSizes;
- memcpy(&poolSizes[0], &m_layoutEntry->m_poolSizesCreateInf[0], sizeof(poolSizes[0]) * m_layoutEntry->m_poolCreateInf.poolSizeCount);
- for(U i = 0; i < m_layoutEntry->m_poolCreateInf.poolSizeCount; ++i)
- {
- poolSizes[i].descriptorCount *= m_lastPoolDSCount;
- ANKI_ASSERT(poolSizes[i].descriptorCount > 0);
- }
- VkDescriptorPoolCreateInfo ci = m_layoutEntry->m_poolCreateInf;
- ci.pPoolSizes = &poolSizes[0];
- ci.maxSets = m_lastPoolDSCount;
- // Create
- VkDescriptorPool pool;
- ANKI_VK_CHECK(vkCreateDescriptorPool(getVkDevice(), &ci, nullptr, &pool));
- ANKI_TRACE_INC_COUNTER(VkDescriptorPoolCreate, 1);
- // Push back
- m_pools.resize(m_pools.getSize() + 1);
- m_pools[m_pools.getSize() - 1] = pool;
- return Error::kNone;
- }
- const DS* DescriptorSetFactory::DSAllocator::tryFindSet(U64 hash)
- {
- ANKI_ASSERT(hash > 0);
- auto it = m_hashmap.find(hash);
- if(it == m_hashmap.getEnd())
- {
- return nullptr;
- }
- else
- {
- DS* ds = *it;
- // Remove from the list and place at the end of the list
- m_list.erase(ds);
- m_list.pushBack(ds);
- ds->m_lastFrameUsed = m_layoutEntry->m_factory->m_frameCount;
- return ds;
- }
- }
- Error DescriptorSetFactory::DSAllocator::newSet(U64 hash, const Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet>& bindings,
- StackMemoryPool& tmpPool, const DS*& out_)
- {
- DS* out = nullptr;
- // First try to see if there are unused to recycle
- const U64 crntFrame = m_layoutEntry->m_factory->m_frameCount;
- auto it = m_list.getBegin();
- const auto end = m_list.getEnd();
- while(it != end)
- {
- DS* set = &(*it);
- U64 frameDiff = crntFrame - set->m_lastFrameUsed;
- if(frameDiff > kDescriptorBufferedFrameCount)
- {
- // Found something, recycle
- auto it2 = m_hashmap.find(set->m_hash);
- ANKI_ASSERT(it2 != m_hashmap.getEnd());
- m_hashmap.erase(it2);
- m_list.erase(set);
- m_list.pushBack(set);
- m_hashmap.emplace(hash, set);
- out = set;
- break;
- }
- ++it;
- }
- if(out == nullptr)
- {
- // Need to allocate one
- if(m_lastPoolFreeDSCount == 0)
- {
- // Can't allocate one from the current pool, create new
- ANKI_CHECK(createNewPool());
- }
- --m_lastPoolFreeDSCount;
- VkDescriptorSetAllocateInfo ci = {};
- ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- ci.descriptorPool = m_pools.getBack();
- ci.pSetLayouts = &m_layoutEntry->m_layoutHandle;
- ci.descriptorSetCount = 1;
- VkDescriptorSet handle;
- [[maybe_unused]] VkResult rez = vkAllocateDescriptorSets(getVkDevice(), &ci, &handle);
- ANKI_ASSERT(rez == VK_SUCCESS && "That allocation can't fail");
- ANKI_TRACE_INC_COUNTER(VkDescriptorSetCreate, 1);
- out = newInstance<DS>(GrMemoryPool::getSingleton());
- out->m_handle = handle;
- m_hashmap.emplace(hash, out);
- m_list.pushBack(out);
- }
- ANKI_ASSERT(out);
- out->m_lastFrameUsed = crntFrame;
- out->m_hash = hash;
- // Finally, write it
- writeSet(bindings, *out, tmpPool);
- out_ = out;
- return Error::kNone;
- }
- void DescriptorSetFactory::DSAllocator::writeSet(const Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet>& bindings, const DS& set,
- StackMemoryPool& tmpPool)
- {
- DynamicArray<VkWriteDescriptorSet, MemoryPoolPtrWrapper<StackMemoryPool>> writeInfos(&tmpPool);
- DynamicArray<VkDescriptorImageInfo, MemoryPoolPtrWrapper<StackMemoryPool>> texInfos(&tmpPool);
- DynamicArray<VkDescriptorBufferInfo, MemoryPoolPtrWrapper<StackMemoryPool>> buffInfos(&tmpPool);
- DynamicArray<VkWriteDescriptorSetAccelerationStructureKHR, MemoryPoolPtrWrapper<StackMemoryPool>> asInfos(&tmpPool);
- DynamicArray<VkBufferView, MemoryPoolPtrWrapper<StackMemoryPool>> bufferViews(&tmpPool);
- // First pass: Populate the VkDescriptorImageInfo and VkDescriptorBufferInfo
- for(U bindingIdx = m_layoutEntry->m_minBinding; bindingIdx <= m_layoutEntry->m_maxBinding; ++bindingIdx)
- {
- if(m_layoutEntry->m_activeBindings.get(bindingIdx))
- {
- for(U arrIdx = 0; arrIdx < m_layoutEntry->m_bindingArraySize[bindingIdx]; ++arrIdx)
- {
- ANKI_ASSERT(bindings[bindingIdx].m_arraySize >= m_layoutEntry->m_bindingArraySize[bindingIdx]);
- const AnyBinding& b = (bindings[bindingIdx].m_arraySize == 1) ? bindings[bindingIdx].m_single : bindings[bindingIdx].m_array[arrIdx];
- switch(b.m_type)
- {
- case DescriptorType::kCombinedTextureSampler:
- {
- VkDescriptorImageInfo& info = *texInfos.emplaceBack();
- info.sampler = b.m_texAndSampler.m_samplerHandle;
- info.imageView = b.m_texAndSampler.m_imgViewHandle;
- info.imageLayout = b.m_texAndSampler.m_layout;
- break;
- }
- case DescriptorType::kTexture:
- {
- VkDescriptorImageInfo& info = *texInfos.emplaceBack();
- info.sampler = VK_NULL_HANDLE;
- info.imageView = b.m_tex.m_imgViewHandle;
- info.imageLayout = b.m_tex.m_layout;
- break;
- }
- case DescriptorType::kSampler:
- {
- VkDescriptorImageInfo& info = *texInfos.emplaceBack();
- info.sampler = b.m_sampler.m_samplerHandle;
- info.imageView = VK_NULL_HANDLE;
- info.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- break;
- }
- case DescriptorType::kUniformBuffer:
- case DescriptorType::kStorageBuffer:
- {
- VkDescriptorBufferInfo& info = *buffInfos.emplaceBack();
- info.buffer = b.m_buff.m_buffHandle;
- info.offset = 0;
- info.range = (b.m_buff.m_range == kMaxPtrSize) ? VK_WHOLE_SIZE : b.m_buff.m_range;
- break;
- }
- case DescriptorType::kReadTextureBuffer:
- case DescriptorType::kReadWriteTextureBuffer:
- {
- VkBufferView& view = *bufferViews.emplaceBack();
- view = b.m_textureBuffer.m_buffView;
- break;
- }
- case DescriptorType::kImage:
- {
- VkDescriptorImageInfo& info = *texInfos.emplaceBack();
- info.sampler = VK_NULL_HANDLE;
- info.imageView = b.m_image.m_imgViewHandle;
- info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- break;
- }
- case DescriptorType::kAccelerationStructure:
- {
- VkWriteDescriptorSetAccelerationStructureKHR& info = *asInfos.emplaceBack();
- info.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
- info.pNext = nullptr;
- info.accelerationStructureCount = 1;
- info.pAccelerationStructures = &b.m_accelerationStructure.m_accelerationStructureHandle;
- break;
- }
- default:
- ANKI_ASSERT(0);
- }
- }
- }
- }
- // Second pass: Populate the VkWriteDescriptorSet with VkDescriptorImageInfo and VkDescriptorBufferInfo
- U32 texCounter = 0;
- U32 buffCounter = 0;
- U32 asCounter = 0;
- U32 buffViewsCounter = 0;
- VkWriteDescriptorSet writeTemplate = {};
- writeTemplate.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- writeTemplate.pNext = nullptr;
- writeTemplate.dstSet = set.m_handle;
- writeTemplate.descriptorCount = 1;
- for(U32 bindingIdx = m_layoutEntry->m_minBinding; bindingIdx <= m_layoutEntry->m_maxBinding; ++bindingIdx)
- {
- if(m_layoutEntry->m_activeBindings.get(bindingIdx))
- {
- for(U32 arrIdx = 0; arrIdx < m_layoutEntry->m_bindingArraySize[bindingIdx]; ++arrIdx)
- {
- const AnyBinding& b = (bindings[bindingIdx].m_arraySize == 1) ? bindings[bindingIdx].m_single : bindings[bindingIdx].m_array[arrIdx];
- VkWriteDescriptorSet& writeInfo = *writeInfos.emplaceBack(writeTemplate);
- writeInfo.descriptorType = convertDescriptorType(b.m_type);
- writeInfo.dstArrayElement = arrIdx;
- writeInfo.dstBinding = bindingIdx;
- switch(b.m_type)
- {
- case DescriptorType::kCombinedTextureSampler:
- case DescriptorType::kTexture:
- case DescriptorType::kSampler:
- case DescriptorType::kImage:
- writeInfo.pImageInfo = &texInfos[texCounter++];
- break;
- case DescriptorType::kUniformBuffer:
- case DescriptorType::kStorageBuffer:
- writeInfo.pBufferInfo = &buffInfos[buffCounter++];
- break;
- case DescriptorType::kReadTextureBuffer:
- case DescriptorType::kReadWriteTextureBuffer:
- writeInfo.pTexelBufferView = &bufferViews[buffViewsCounter++];
- break;
- case DescriptorType::kAccelerationStructure:
- writeInfo.pNext = &asInfos[asCounter++];
- break;
- default:
- ANKI_ASSERT(0);
- }
- }
- }
- }
- // Write
- vkUpdateDescriptorSets(getVkDevice(), writeInfos.getSize(), (writeInfos.getSize() > 0) ? &writeInfos[0] : nullptr, 0, nullptr);
- }
- DSLayoutCacheEntry::~DSLayoutCacheEntry()
- {
- if(m_layoutHandle)
- {
- vkDestroyDescriptorSetLayout(getVkDevice(), m_layoutHandle, nullptr);
- }
- }
- Error DSLayoutCacheEntry::init(const DescriptorBinding* bindings, U32 bindingCount, U64 hash)
- {
- ANKI_ASSERT(bindings);
- ANKI_ASSERT(hash > 0);
- m_hash = hash;
- // Create the VK layout
- Array<VkDescriptorSetLayoutBinding, kMaxBindingsPerDescriptorSet> vkBindings;
- VkDescriptorSetLayoutCreateInfo ci = {};
- ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- for(U i = 0; i < bindingCount; ++i)
- {
- VkDescriptorSetLayoutBinding& vk = vkBindings[i];
- const DescriptorBinding& ak = bindings[i];
- vk.binding = ak.m_binding;
- vk.descriptorCount = ak.m_arraySize;
- vk.descriptorType = convertDescriptorType(ak.m_type);
- vk.pImmutableSamplers = nullptr;
- vk.stageFlags = convertShaderTypeBit(ak.m_stageMask);
- ANKI_ASSERT(m_activeBindings.get(ak.m_binding) == false);
- m_activeBindings.set(ak.m_binding);
- m_bindingType[ak.m_binding] = ak.m_type;
- m_bindingArraySize[ak.m_binding] = ak.m_arraySize;
- m_minBinding = min<U32>(m_minBinding, ak.m_binding);
- m_maxBinding = max<U32>(m_maxBinding, ak.m_binding);
- }
- ci.bindingCount = bindingCount;
- ci.pBindings = &vkBindings[0];
- ANKI_VK_CHECK(vkCreateDescriptorSetLayout(getVkDevice(), &ci, nullptr, &m_layoutHandle));
- // Create the pool info
- U32 poolSizeCount = 0;
- for(U i = 0; i < bindingCount; ++i)
- {
- U j;
- for(j = 0; j < poolSizeCount; ++j)
- {
- if(m_poolSizesCreateInf[j].type == convertDescriptorType(bindings[i].m_type))
- {
- m_poolSizesCreateInf[j].descriptorCount += bindings[i].m_arraySize;
- break;
- }
- }
- if(j == poolSizeCount)
- {
- m_poolSizesCreateInf[poolSizeCount].type = convertDescriptorType(bindings[i].m_type);
- m_poolSizesCreateInf[poolSizeCount].descriptorCount = bindings[i].m_arraySize;
- ++poolSizeCount;
- }
- }
- if(poolSizeCount == 0)
- {
- // If the poolSizeCount it means that the DS layout has 0 descriptors. Since the pool sizes can't be zero put
- // something in them
- m_poolSizesCreateInf[0].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
- m_poolSizesCreateInf[0].descriptorCount = 1;
- ++poolSizeCount;
- }
- ANKI_ASSERT(poolSizeCount > 0);
- m_poolCreateInf.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
- m_poolCreateInf.poolSizeCount = poolSizeCount;
- return Error::kNone;
- }
- Error DSLayoutCacheEntry::getOrCreateDSAllocator(DescriptorSetFactory::DSAllocator*& alloc)
- {
- alloc = nullptr;
- // Get or create thread-local
- DescriptorSetFactory::ThreadLocal* threadLocal = DescriptorSetFactory::m_threadLocal;
- if(threadLocal == nullptr) [[unlikely]]
- {
- threadLocal = newInstance<DescriptorSetFactory::ThreadLocal>(GrMemoryPool::getSingleton());
- DescriptorSetFactory::m_threadLocal = threadLocal;
- LockGuard<Mutex> lock(m_factory->m_allThreadLocalsMtx);
- m_factory->m_allThreadLocals.emplaceBack(threadLocal);
- }
- // Get or create the allocator
- if(m_index >= threadLocal->m_allocators.getSize()) [[unlikely]]
- {
- threadLocal->m_allocators.resize(m_index + 1, nullptr);
- alloc = newInstance<DescriptorSetFactory::DSAllocator>(GrMemoryPool::getSingleton(), this);
- ANKI_CHECK(alloc->init());
- threadLocal->m_allocators[m_index] = alloc;
- }
- else if(threadLocal->m_allocators[m_index] == nullptr) [[unlikely]]
- {
- alloc = newInstance<DescriptorSetFactory::DSAllocator>(GrMemoryPool::getSingleton(), this);
- ANKI_CHECK(alloc->init());
- threadLocal->m_allocators[m_index] = alloc;
- }
- else
- {
- alloc = threadLocal->m_allocators[m_index];
- }
- ANKI_ASSERT(alloc);
- return Error::kNone;
- }
- AnyBinding& DescriptorSetState::getBindingToPopulate(U32 bindingIdx, U32 arrayIdx)
- {
- ANKI_ASSERT(bindingIdx < kMaxBindingsPerDescriptorSet);
- AnyBindingExtended& extended = m_bindings[bindingIdx];
- AnyBinding* out;
- const Bool bindingIsSet = m_bindingSet.get(bindingIdx);
- m_bindingSet.set(bindingIdx);
- extended.m_arraySize = (!bindingIsSet) ? 0 : extended.m_arraySize;
- if(arrayIdx == 0 && extended.m_arraySize <= 1) [[likely]]
- {
- // Array idx is zero, most common case
- out = &extended.m_single;
- extended.m_arraySize = 1;
- }
- else if(arrayIdx < extended.m_arraySize)
- {
- // It's (or was) an array and there enough space in thar array
- out = &extended.m_array[arrayIdx];
- }
- else
- {
- // Need to grow
- const U32 newSize = max(extended.m_arraySize * 2, arrayIdx + 1);
- AnyBinding* newArr = newArray<AnyBinding>(*m_pool, newSize);
- if(extended.m_arraySize == 1)
- {
- newArr[0] = extended.m_single;
- }
- else if(extended.m_arraySize > 1)
- {
- // Copy old to new.
- memcpy(newArr, extended.m_array, sizeof(AnyBinding) * extended.m_arraySize);
- }
- // Zero the rest
- memset(newArr + extended.m_arraySize, 0, sizeof(AnyBinding) * (newSize - extended.m_arraySize));
- extended.m_arraySize = newSize;
- extended.m_array = newArr;
- // Return
- out = &extended.m_array[arrayIdx];
- }
- ANKI_ASSERT(out);
- return *out;
- }
- void DescriptorSetState::flush(U64& hash, Array<PtrSize, kMaxBindingsPerDescriptorSet>& dynamicOffsets, U32& dynamicOffsetCount, Bool& bindlessDSet)
- {
- // Set some values
- hash = 0;
- dynamicOffsetCount = 0;
- bindlessDSet = false;
- // There is a chance where the bindless set is bound but the actual shaders have an empty DS layout (maybe because
- // the dead code elimination eliminated the bindless set). In that case we can't bind the bindless DS. We have to
- // treat it as regular set
- ANKI_ASSERT(!(m_layout.m_entry == nullptr && !m_bindlessDSetBound) && "DS layout points to bindless but no bindless is bound");
- const Bool reallyBindless = m_bindlessDSetBound && m_layout.m_entry == nullptr;
- if(!reallyBindless)
- {
- // Get cache entry
- ANKI_ASSERT(m_layout.m_entry);
- const DSLayoutCacheEntry& entry = *m_layout.m_entry;
- // Early out if nothing happened
- const Bool anyActiveBindingDirty = !!(entry.m_activeBindings & m_dirtyBindings);
- if(!anyActiveBindingDirty && !m_layoutDirty)
- {
- return;
- }
- Bool dynamicOffsetsDirty = false;
- // Compute the hash
- Array<U64, kMaxBindingsPerDescriptorSet * 2 * 2> toHash;
- U toHashCount = 0;
- const U minBinding = entry.m_minBinding;
- const U maxBinding = entry.m_maxBinding;
- for(U i = minBinding; i <= maxBinding; ++i)
- {
- if(entry.m_activeBindings.get(i))
- {
- ANKI_ASSERT(m_bindingSet.get(i) && "Forgot to bind");
- ANKI_ASSERT(m_bindings[i].m_arraySize >= entry.m_bindingArraySize[i] && "Bound less");
- const Bool crntBindingDirty = m_dirtyBindings.get(i);
- m_dirtyBindings.unset(i);
- for(U arrIdx = 0; arrIdx < entry.m_bindingArraySize[i]; ++arrIdx)
- {
- ANKI_ASSERT(arrIdx < m_bindings[i].m_arraySize);
- if(arrIdx > 1)
- {
- ANKI_ASSERT(m_bindings[i].m_array[arrIdx].m_type == m_bindings[i].m_array[arrIdx - 1].m_type);
- }
- const AnyBinding& anyBinding = (m_bindings[i].m_arraySize == 1) ? m_bindings[i].m_single : m_bindings[i].m_array[arrIdx];
- ANKI_ASSERT(anyBinding.m_uuids[0] != 0 && "Forgot to bind");
- toHash[toHashCount++] = anyBinding.m_uuids[0];
- switch(entry.m_bindingType[i])
- {
- case DescriptorType::kCombinedTextureSampler:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kCombinedTextureSampler && "Have bound the wrong type");
- toHash[toHashCount++] = anyBinding.m_uuids[1];
- toHash[toHashCount++] = U64(anyBinding.m_texAndSampler.m_layout);
- break;
- case DescriptorType::kTexture:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kTexture && "Have bound the wrong type");
- toHash[toHashCount++] = U64(anyBinding.m_tex.m_layout);
- break;
- case DescriptorType::kSampler:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kSampler && "Have bound the wrong type");
- break;
- case DescriptorType::kUniformBuffer:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kUniformBuffer && "Have bound the wrong type");
- toHash[toHashCount++] = anyBinding.m_buff.m_range;
- dynamicOffsets[dynamicOffsetCount++] = anyBinding.m_buff.m_offset;
- dynamicOffsetsDirty = dynamicOffsetsDirty || crntBindingDirty;
- break;
- case DescriptorType::kStorageBuffer:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kStorageBuffer && "Have bound the wrong type");
- toHash[toHashCount++] = anyBinding.m_buff.m_range;
- dynamicOffsets[dynamicOffsetCount++] = anyBinding.m_buff.m_offset;
- dynamicOffsetsDirty = dynamicOffsetsDirty || crntBindingDirty;
- break;
- case DescriptorType::kReadTextureBuffer:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kReadTextureBuffer && "Have bound the wrong type");
- toHash[toHashCount++] = anyBinding.m_uuids[1];
- break;
- case DescriptorType::kReadWriteTextureBuffer:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kReadWriteTextureBuffer && "Have bound the wrong type");
- toHash[toHashCount++] = anyBinding.m_uuids[1];
- break;
- case DescriptorType::kImage:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kImage && "Have bound the wrong type");
- break;
- case DescriptorType::kAccelerationStructure:
- ANKI_ASSERT(anyBinding.m_type == DescriptorType::kAccelerationStructure && "Have bound the wrong type");
- break;
- default:
- ANKI_ASSERT(0);
- }
- }
- }
- }
- const U64 newHash = computeHash(&toHash[0], toHashCount * sizeof(U64));
- if(newHash != m_lastHash || dynamicOffsetsDirty || m_layoutDirty)
- {
- // DS needs rebind
- m_lastHash = newHash;
- hash = newHash;
- }
- else
- {
- // All clean, keep hash equal to 0
- }
- m_layoutDirty = false;
- }
- else
- {
- // Custom set
- if(!m_bindlessDSetDirty && !m_layoutDirty)
- {
- return;
- }
- bindlessDSet = true;
- hash = 1;
- m_bindlessDSetDirty = false;
- m_layoutDirty = false;
- }
- }
- DescriptorSetFactory::~DescriptorSetFactory()
- {
- }
- Error DescriptorSetFactory::init(U32 bindlessTextureCount, U32 bindlessTextureBuffers)
- {
- m_bindless = newInstance<BindlessDescriptorSet>(GrMemoryPool::getSingleton());
- ANKI_CHECK(m_bindless->init(bindlessTextureCount, bindlessTextureBuffers));
- m_bindlessTextureCount = bindlessTextureCount;
- m_bindlessUniformTexelBufferCount = bindlessTextureBuffers;
- return Error::kNone;
- }
- void DescriptorSetFactory::destroy()
- {
- for(ThreadLocal* threadLocal : m_allThreadLocals)
- {
- for(DSAllocator* alloc : threadLocal->m_allocators)
- {
- deleteInstance(GrMemoryPool::getSingleton(), alloc);
- }
- deleteInstance(GrMemoryPool::getSingleton(), threadLocal);
- }
- m_allThreadLocals.destroy();
- for(DSLayoutCacheEntry* l : m_caches)
- {
- deleteInstance(GrMemoryPool::getSingleton(), l);
- }
- m_caches.destroy();
- if(m_bindless)
- {
- deleteInstance(GrMemoryPool::getSingleton(), m_bindless);
- }
- }
- Error DescriptorSetFactory::newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout)
- {
- // Compute the hash for the layout
- Array<DescriptorBinding, kMaxBindingsPerDescriptorSet> bindings;
- const U32 bindingCount = init.m_bindings.getSize();
- U64 hash;
- if(init.m_bindings.getSize() > 0)
- {
- memcpy(bindings.getBegin(), init.m_bindings.getBegin(), init.m_bindings.getSizeInBytes());
- std::sort(bindings.getBegin(), bindings.getBegin() + bindingCount, [](const DescriptorBinding& a, const DescriptorBinding& b) {
- return a.m_binding < b.m_binding;
- });
- hash = computeHash(&bindings[0], init.m_bindings.getSizeInBytes());
- ANKI_ASSERT(hash != 1);
- }
- else
- {
- hash = 1;
- }
- // Identify if the DS is the bindless one. It is if there is at least one binding that matches the criteria
- Bool isBindless = false;
- if(bindingCount > 0)
- {
- isBindless = true;
- for(U32 i = 0; i < bindingCount; ++i)
- {
- const DescriptorBinding& binding = bindings[i];
- if(binding.m_binding == 0 && binding.m_type == DescriptorType::kTexture && binding.m_arraySize == m_bindlessTextureCount)
- {
- // All good
- }
- else if(binding.m_binding == 1 && binding.m_type == DescriptorType::kReadTextureBuffer
- && binding.m_arraySize == m_bindlessUniformTexelBufferCount)
- {
- // All good
- }
- else
- {
- isBindless = false;
- }
- }
- }
- // Find or create the cache entry
- if(isBindless)
- {
- layout.m_handle = m_bindless->getDescriptorSetLayout();
- layout.m_entry = nullptr;
- }
- else
- {
- LockGuard<SpinLock> lock(m_cachesMtx);
- DSLayoutCacheEntry* cache = nullptr;
- U count = 0;
- for(DSLayoutCacheEntry* it : m_caches)
- {
- if(it->m_hash == hash)
- {
- cache = it;
- break;
- }
- ++count;
- }
- if(cache == nullptr)
- {
- cache = newInstance<DSLayoutCacheEntry>(GrMemoryPool::getSingleton(), this, m_caches.getSize());
- ANKI_CHECK(cache->init(bindings.getBegin(), bindingCount, hash));
- m_caches.emplaceBack(cache);
- }
- // Set the layout
- layout.m_handle = cache->m_layoutHandle;
- layout.m_entry = cache;
- }
- return Error::kNone;
- }
- Error DescriptorSetFactory::newDescriptorSet(StackMemoryPool& tmpPool, DescriptorSetState& state, DescriptorSet& set, Bool& dirty,
- Array<PtrSize, kMaxBindingsPerDescriptorSet>& dynamicOffsets, U32& dynamicOffsetCount)
- {
- ANKI_TRACE_SCOPED_EVENT(VkDescriptorSetGetOrCreate);
- U64 hash;
- Bool bindlessDSet;
- state.flush(hash, dynamicOffsets, dynamicOffsetCount, bindlessDSet);
- if(hash == 0)
- {
- dirty = false;
- return Error::kNone;
- }
- else
- {
- dirty = true;
- if(!bindlessDSet)
- {
- DescriptorSetLayout layout = state.m_layout;
- DSLayoutCacheEntry& entry = *layout.m_entry;
- // Get thread allocator
- DSAllocator* alloc;
- ANKI_CHECK(entry.getOrCreateDSAllocator(alloc));
- // Finally, allocate
- const DS* s;
- ANKI_CHECK(alloc->getOrCreateSet(hash, state.m_bindings, tmpPool, s));
- set.m_handle = s->m_handle;
- ANKI_ASSERT(set.m_handle != VK_NULL_HANDLE);
- }
- else
- {
- set = m_bindless->getDescriptorSet();
- }
- }
- return Error::kNone;
- }
- U32 DescriptorSetFactory::bindBindlessTexture(const VkImageView view, const VkImageLayout layout)
- {
- ANKI_ASSERT(m_bindless);
- return m_bindless->bindTexture(view, layout);
- }
- U32 DescriptorSetFactory::bindBindlessUniformTexelBuffer(const VkBufferView view)
- {
- ANKI_ASSERT(m_bindless);
- return m_bindless->bindUniformTexelBuffer(view);
- }
- void DescriptorSetFactory::unbindBindlessTexture(U32 idx)
- {
- ANKI_ASSERT(m_bindless);
- m_bindless->unbindTexture(idx);
- }
- void DescriptorSetFactory::unbindBindlessUniformTexelBuffer(U32 idx)
- {
- ANKI_ASSERT(m_bindless);
- m_bindless->unbindUniformTexelBuffer(idx);
- }
- } // end namespace anki
|