浏览代码

Vulkan: Some work on descriptors

Panagiotis Christopoulos Charitos 9 年之前
父节点
当前提交
bac1a475d8

+ 4 - 5
src/anki/gr/Common.h

@@ -158,14 +158,13 @@ const U MAX_UNIFORM_BUFFER_BINDINGS = 5;
 const U MAX_STORAGE_BUFFER_BINDINGS = 4;
 const U MAX_IMAGE_BINDINGS = 4;
 
-const U MAX_DESCRIPTORS_PER_SET =
+const U DESCRIPTOR_TYPE_COUNT = 4;
+const U MAX_BINDINGS_PER_DESCRIPTOR_SET =
 	MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
 
 const U MAX_FRAMES_IN_FLIGHT = 3; ///< Triple buffering.
-/// Groups that can be bound at the same time.
-const U MAX_BOUND_RESOURCE_GROUPS = 2;
-/// An anoying limit for Vulkan.
-const U MAX_RESOURCE_GROUPS = 1024;
+const U MAX_BOUND_RESOURCE_GROUPS = 2; ///< Groups that can be bound at the same time.
+const U MAX_DESCRIPTOR_SETS_PER_POOL = 1024; ///< An anoying limit for Vulkan.
 
 /// Compute max number of mipmaps for a 2D texture.
 inline U computeMaxMipmapCount2d(U w, U h, U minSizeOfLastMip = 1)

+ 6 - 0
src/anki/gr/vulkan/Common.h

@@ -107,6 +107,12 @@ ANKI_USE_RESULT VkImageViewType convertTextureViewType(TextureType ak);
 ANKI_USE_RESULT VkImageUsageFlags convertTextureUsage(TextureUsageBit ak, const PixelFormat& format);
 
 ANKI_USE_RESULT VkStencilOp convertStencilOp(StencilOperation ak);
+
+ANKI_USE_RESULT inline VkShaderStageFlagBits convertShaderTypeBit(ShaderTypeBit bit)
+{
+	ANKI_ASSERT(bit != ShaderTypeBit::NONE);
+	return static_cast<VkShaderStageFlagBits>(bit);
+}
 /// @}
 
 } // end namespace anki

+ 67 - 0
src/anki/gr/vulkan/DescriptorObject.cpp

@@ -0,0 +1,67 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/DescriptorObject.h>
+#include <anki/gr/GrManager.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
+
+namespace anki
+{
+
+DescriptorObject::DescriptorObject(GrManager* manager)
+	: VulkanObject(manager)
+{
+	m_tombstone = manager->getImplementation().getDescriptorObjectTombstoneGenerator().newTombstone();
+}
+
+DescriptorObject::~DescriptorObject()
+{
+	m_tombstone.killObject();
+}
+
+DescriptorObjectTombstoneGenerator::~DescriptorObjectTombstoneGenerator()
+{
+	auto it = m_blocks.getBegin();
+	auto end = m_blocks.getEnd();
+	while(it != end)
+	{
+		DescriptorTombstoneBlock* block = &(*it);
+		++it;
+		m_alloc.deleteInstance(block);
+	}
+}
+
+DescriptorObjectTombstone DescriptorObjectTombstoneGenerator::newTombstone()
+{
+	LockGuard<SpinLock> lock(m_blocksLock);
+
+	DescriptorTombstoneBlock* block;
+	if((m_tombstoneCount % DESCRIPTORS_PER_TOMBSTONE) != 0)
+	{
+		// Can use the last block
+		block = &m_blocks.getBack();
+	}
+	else
+	{
+		// Need new block
+
+		block = m_alloc.newInstance<DescriptorTombstoneBlock>();
+		memset(&block->m_descriptors[0], 0, sizeof(block->m_descriptors[0]));
+		m_blocks.pushBack(block);
+	}
+
+	U idx = m_tombstoneCount % DESCRIPTORS_PER_TOMBSTONE;
+	++m_tombstoneCount;
+
+	block->m_descriptors[idx].set(1); // Make it alive
+
+	DescriptorObjectTombstone out;
+	out.m_block = block;
+	out.m_idx = idx;
+
+	return out;
+}
+
+} // end namespace anki

+ 87 - 0
src/anki/gr/vulkan/DescriptorObject.h

@@ -0,0 +1,87 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/util/List.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+static const U DESCRIPTORS_PER_TOMBSTONE = 32;
+
+class DescriptorTombstoneBlock : public IntrusiveListEnabled<DescriptorTombstoneBlock>
+{
+public:
+	Array<Atomic<U8>, DESCRIPTORS_PER_TOMBSTONE> m_descriptors;
+};
+
+/// This object will tell you if the DescriptorObject is alive or not.
+class DescriptorObjectTombstone
+{
+	friend class DescriptorObject;
+	friend class DescriptorObjectTombstoneGenerator;
+
+public:
+	Bool isObjectAlive() const
+	{
+		return m_block->m_descriptors[m_idx].load() != 0;
+	}
+
+private:
+	U8 m_idx;
+	DescriptorTombstoneBlock* m_block;
+
+	void killObject()
+	{
+		m_block->m_descriptors[m_idx].store(0);
+	}
+};
+
+/// Contains special functionality for descriptor related types.
+class DescriptorObject : public VulkanObject
+{
+public:
+	DescriptorObject(GrManager* manager);
+
+	~DescriptorObject();
+
+	const DescriptorObjectTombstone& getDescriptorTombstone() const
+	{
+		return m_tombstone;
+	}
+
+private:
+	DescriptorObjectTombstone m_tombstone;
+};
+
+class DescriptorObjectTombstoneGenerator
+{
+public:
+	DescriptorObjectTombstoneGenerator() = default;
+
+	~DescriptorObjectTombstoneGenerator();
+
+	void init(GrAllocator<U8> alloc)
+	{
+		m_alloc = alloc;
+	}
+
+	/// @note It's thread-safe.
+	DescriptorObjectTombstone newTombstone();
+
+private:
+	GrAllocator<U8> m_alloc;
+	IntrusiveList<DescriptorTombstoneBlock> m_blocks;
+	SpinLock m_blocksLock;
+	U64 m_tombstoneCount = 0;
+};
+/// @}
+
+} // end namespace anki

+ 154 - 0
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -0,0 +1,154 @@
+// Copyright (C) 2009-2016, 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 <algorithm>
+
+namespace anki
+{
+
+/// Cache entry. It's built around a specific descriptor set layout.
+class DescriptorSetFactory::Cache
+{
+public:
+	U64 m_hash;
+	VkDescriptorSetLayout m_layoutHandle;
+	VkDescriptorPool m_poolHandle;
+	Mutex m_mtx;
+};
+
+void DescriptorSetFactory::init(const GrAllocator<U8>& alloc, VkDevice dev)
+{
+	m_alloc = alloc;
+	m_dev = dev;
+}
+
+void DescriptorSetFactory::newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout)
+{
+	// Compute the hash for the layout
+	Array<DescriptorBinding, MAX_BINDINGS_PER_DESCRIPTOR_SET> bindings;
+	U bindingCount = init.m_bindings.getSize();
+	U64 hash;
+
+	if(init.m_bindings.getSize() > 0)
+	{
+		memcpy(&bindings[0], &init.m_bindings[0], init.m_bindings.getSizeInBytes());
+		std::sort(&bindings[0],
+			&bindings[0] + bindingCount,
+			[](const DescriptorBinding& a, const DescriptorBinding& b) { return a.m_binding < b.m_binding; });
+
+		hash = computeHash(&bindings[0], init.m_bindings.getSizeInBytes());
+	}
+	else
+	{
+		hash = 0;
+	}
+
+	// Find or create the cache entry
+	LockGuard<Mutex> lock(m_mtx);
+
+	Cache* cache;
+	U cacheIdx = MAX_U;
+	U count = 0;
+	for(Cache* it : m_caches)
+	{
+		if(it->m_hash == hash)
+		{
+			cache = it;
+			cacheIdx = count;
+			break;
+		}
+		++count;
+	}
+
+	if(cache == nullptr)
+	{
+		cache = newCacheEntry(&bindings[0], bindingCount, hash);
+		m_caches.resize(m_alloc, m_caches.getSize() + 1);
+		m_caches[m_caches.getSize() - 1] = cache;
+		cacheIdx = m_caches.getSize() - 1;
+	}
+
+	// Set the layout
+	layout.m_handle = cache->m_layoutHandle;
+	layout.m_cacheEntryIdx = cacheIdx;
+}
+
+DescriptorSetFactory::Cache* DescriptorSetFactory::newCacheEntry(
+	const DescriptorBinding* bindings, U bindingCount, U64 hash)
+{
+	ANKI_ASSERT(bindings);
+
+	// Create
+	Cache* cache = m_alloc.newInstance<Cache>();
+	cache->m_hash = hash;
+
+	// Create the VK layout
+	Array<VkDescriptorSetLayoutBinding, MAX_BINDINGS_PER_DESCRIPTOR_SET> vkBindings;
+	VkDescriptorSetLayoutCreateInfo ci = {
+		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 = 1;
+		vk.descriptorType = static_cast<VkDescriptorType>(ak.m_type);
+		vk.pImmutableSamplers = nullptr;
+		vk.stageFlags = convertShaderTypeBit(ak.m_stageMask);
+	}
+
+	ci.bindingCount = bindingCount;
+	ci.pBindings = &vkBindings[0];
+
+	ANKI_VK_CHECKF(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &cache->m_layoutHandle));
+
+	// Create the pool
+	Array<VkDescriptorPoolSize, DESCRIPTOR_TYPE_COUNT> poolSizes;
+	U poolSizeCount = 0;
+	for(U i = 0; i < bindingCount; ++i)
+	{
+		U j;
+		for(j = 0; j < poolSizeCount; ++j)
+		{
+			if(poolSizes[j].type == bindings[i].m_type)
+			{
+				++poolSizes[j].descriptorCount;
+				break;
+			}
+		}
+
+		if(j == poolSizeCount)
+		{
+			poolSizes[poolSizeCount].type = static_cast<VkDescriptorType>(bindings[i].m_type);
+			poolSizes[poolSizeCount].descriptorCount = 1;
+			++poolSizeCount;
+		}
+	}
+
+	ANKI_ASSERT(poolSizeCount > 0);
+
+	for(U i = 0; i < poolSizeCount; ++i)
+	{
+		poolSizes[i].descriptorCount *= MAX_DESCRIPTOR_SETS_PER_POOL;
+	}
+
+	VkDescriptorPoolCreateInfo poolci = {
+		VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+	};
+	poolci.maxSets = MAX_DESCRIPTOR_SETS_PER_POOL;
+	poolci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+	poolci.poolSizeCount = poolSizeCount;
+	poolci.pPoolSizes = &poolSizes[0];
+
+	ANKI_VK_CHECKF(vkCreateDescriptorPool(m_dev, &poolci, nullptr, &cache->m_poolHandle));
+
+	return cache;
+}
+
+} // end namespace anki

+ 122 - 2
src/anki/gr/vulkan/DescriptorSet.h

@@ -6,6 +6,8 @@
 #pragma once
 
 #include <anki/gr/vulkan/Common.h>
+#include <anki/gr/Buffer.h>
+#include <anki/util/BitSet.h>
 
 namespace anki
 {
@@ -13,14 +15,132 @@ namespace anki
 /// @addtogroup vulkan
 /// @{
 
-class DescriptorBinding
+class alignas(4) DescriptorBinding
 {
 public:
-	VkDescriptorType m_type = VK_DESCRIPTOR_TYPE_MAX_ENUM;
+	U8 m_type = MAX_U8;
 	ShaderTypeBit m_stageMask = ShaderTypeBit::NONE;
 	U8 m_binding = MAX_U8;
+	U8 _m_padding = 0;
 };
 
+static_assert(sizeof(DescriptorBinding) == 4, "See file");
+
+class DescriptorSetLayoutInitInfo
+{
+public:
+	WeakArray<DescriptorBinding> m_bindings;
+};
+
+class DescriptorSetLayout
+{
+	friend class DescriptorSetFactory;
+
+public:
+	VkDescriptorSetLayout getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+private:
+	VkDescriptorSetLayout m_handle = VK_NULL_HANDLE;
+	U32 m_cacheEntryIdx = MAX_U32;
+};
+
+/// A state tracker of descriptors.
+class DescriptorSetState
+{
+public:
+	void bindUniformBuffer(U binding, const BufferPtr& buff, PtrSize offset, PtrSize range)
+	{
+		U realBinding = binding + MAX_TEXTURE_BINDINGS;
+		m_setBindings.set(realBinding);
+		m_uniformBuffers[binding].m_buff = buff;
+		m_uniformBuffers[binding].m_offset = offset;
+		m_uniformBuffers[binding].m_range = range;
+	}
+
+	void bindStorageBuffer(U binding, const BufferPtr& buff, PtrSize offset, PtrSize range)
+	{
+		U realBinding = binding + MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS;
+		m_setBindings.set(realBinding);
+		m_storageBuffers[binding].m_buff = buff;
+		m_storageBuffers[binding].m_offset = offset;
+		m_storageBuffers[binding].m_range = range;
+	}
+
+private:
+	class TextureBinding
+	{
+	public:
+		TexturePtr m_tex;
+		SamplerPtr m_sampler;
+	};
+
+	class BufferBinding
+	{
+	public:
+		BufferPtr m_buff;
+		PtrSize m_offset = MAX_PTR_SIZE;
+		PtrSize m_range = 0;
+	};
+
+	class ImageBinding
+	{
+	public:
+		TexturePtr m_tex;
+		U16 m_level = 0;
+	};
+
+	DescriptorSetLayout m_layout;
+	Array<BufferBinding, MAX_UNIFORM_BUFFER_BINDINGS> m_uniformBuffers;
+	Array<BufferBinding, MAX_STORAGE_BUFFER_BINDINGS> m_storageBuffers;
+	Array<TextureBinding, MAX_TEXTURE_BINDINGS> m_textures;
+	Array<ImageBinding, MAX_IMAGE_BINDINGS> m_images;
+
+	BitSet<MAX_BINDINGS_PER_DESCRIPTOR_SET, U8> m_setBindings = {false};
+};
+
+class DescriptorSet
+{
+public:
+	VkDescriptorSet getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+private:
+	VkDescriptorSet m_handle = VK_NULL_HANDLE;
+	DynamicArray<U64> m_resourcesUuids;
+};
+
+/// Creates new descriptor set layouts and descriptor sets.
+class DescriptorSetFactory
+{
+public:
+	DescriptorSetFactory() = default;
+	~DescriptorSetFactory();
+
+	void init(const GrAllocator<U8>& alloc, VkDevice dev);
+
+	void newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout);
+
+	void newDescriptorSet(const DescriptorSetState& init, DescriptorSet& set);
+
+	void collectGarbage();
+
+private:
+	class Cache;
+
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+	DynamicArray<Cache*> m_caches;
+	Mutex m_mtx;
+
+	Cache* newCacheEntry(const DescriptorBinding* bindings, U bindingCount, U64 hash);
+};
 /// @}
 
 } // end namespace anki

+ 2 - 0
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -205,6 +205,8 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 		}
 	}
 
+	m_descrGen.init(getAllocator());
+
 	return ErrorCode::NONE;
 }
 

+ 8 - 0
src/anki/gr/vulkan/GrManagerImpl.h

@@ -10,6 +10,7 @@
 #include <anki/gr/vulkan/GrSemaphore.h>
 #include <anki/gr/vulkan/Fence.h>
 #include <anki/gr/vulkan/QueryExtra.h>
+#include <anki/gr/vulkan/DescriptorObject.h>
 #include <anki/gr/vulkan/CommandBufferExtra.h>
 #include <anki/util/HashMap.h>
 
@@ -163,6 +164,11 @@ public:
 		return *m_samplerCache;
 	}
 
+	DescriptorObjectTombstoneGenerator& getDescriptorObjectTombstoneGenerator()
+	{
+		return m_descrGen;
+	}
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -253,6 +259,8 @@ private:
 	GrSemaphoreFactory m_semaphores;
 	/// @}
 
+	DescriptorObjectTombstoneGenerator m_descrGen;
+
 	QueryAllocator m_queryAlloc;
 
 	VkPipelineCache m_pplineCache = VK_NULL_HANDLE;

+ 1 - 1
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -318,7 +318,7 @@ void ShaderImpl::doReflection(const std::vector<unsigned int>& spirv)
 	Array<U, MAX_BOUND_RESOURCE_GROUPS> counts = {{
 		0,
 	}};
-	Array2d<DescriptorBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_DESCRIPTORS_PER_SET> descriptors;
+	Array2d<DescriptorBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_BINDINGS_PER_DESCRIPTOR_SET> descriptors;
 
 	auto func = [&](const std::vector<spirv_cross::Resource>& resources, VkDescriptorType type) -> void {
 		for(const spirv_cross::Resource& r : resources)

+ 29 - 0
src/anki/gr/vulkan/ShaderProgramImpl.cpp

@@ -0,0 +1,29 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/ShaderProgramImpl.h>
+#include <anki/gr/Shader.h>
+#include <anki/gr/vulkan/ShaderImpl.h>
+
+namespace anki
+{
+
+ShaderProgramImpl::ShaderProgramImpl(GrManager* manager)
+	: VulkanObject(manager)
+{
+}
+
+ShaderProgramImpl::~ShaderProgramImpl()
+{
+}
+
+Error ShaderProgramImpl::init(const Array<ShaderPtr, U(ShaderType::COUNT)>& shaders)
+{
+	m_shaders = shaders;
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki

+ 7 - 8
src/anki/gr/vulkan/ShaderProgramImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/DescriptorSet.h>
 
 namespace anki
 {
@@ -17,14 +18,12 @@ namespace anki
 class ShaderProgramImpl : public VulkanObject
 {
 public:
-	ShaderProgramImpl(GrManager* manager)
-		: VulkanObject(manager)
-	{
-	}
-
-	~ShaderProgramImpl()
-	{
-	}
+	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders;
+
+	ShaderProgramImpl(GrManager* manager);
+	~ShaderProgramImpl();
+
+	Error init(const Array<ShaderPtr, U(ShaderType::COUNT)>& shaders);
 };
 /// @}