Forráskód Böngészése

Nuke dynamic uniform and storage buffers from the Vulkan backend

Panagiotis Christopoulos Charitos 1 éve
szülő
commit
1f8e1b3768

+ 0 - 8
AnKi/Gr/CommandBuffer.h

@@ -185,14 +185,6 @@ public:
 	/// Set the line width. By default it's undefined.
 	void setLineWidth(F32 lineWidth);
 
-	/// Bind texture and sample.
-	/// @param set The set to bind to.
-	/// @param binding The binding to bind to.
-	/// @param texView The texture view to bind.
-	/// @param sampler The sampler to override the default sampler of the tex.
-	/// @param arrayIdx The array index if the binding is an array.
-	void bindTextureAndSampler(U32 set, U32 binding, TextureView* texView, Sampler* sampler, U32 arrayIdx = 0);
-
 	/// Bind sampler.
 	/// @param set The set to bind to.
 	/// @param binding The binding to bind to.

+ 0 - 20
AnKi/Gr/RenderGraph.h

@@ -134,16 +134,6 @@ public:
 		return GrManager::getSingleton().newTextureView(viewInit);
 	}
 
-	/// Convenience method.
-	void bindTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, const TextureSubresourceInfo& subresource, Sampler* sampler)
-	{
-		Texture* tex;
-		getRenderTargetState(handle, subresource, tex);
-		TextureViewInitInfo viewInit(tex, subresource, "TmpRenderGraph");
-		TextureViewPtr view = GrManager::getSingleton().newTextureView(viewInit);
-		m_commandBuffer->bindTextureAndSampler(set, binding, view.get(), sampler);
-	}
-
 	/// Convenience method.
 	void bindTexture(U32 set, U32 binding, RenderTargetHandle handle, const TextureSubresourceInfo& subresource)
 	{
@@ -154,16 +144,6 @@ public:
 		m_commandBuffer->bindTexture(set, binding, view.get());
 	}
 
-	/// Convenience method to bind the whole texture as color.
-	void bindColorTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, Sampler* sampler)
-	{
-		Texture* tex = &getTexture(handle);
-		TextureViewInitInfo viewInit(tex); // Use the whole texture
-		getRenderTargetState(handle, viewInit, tex);
-		TextureViewPtr view = GrManager::getSingleton().newTextureView(viewInit);
-		m_commandBuffer->bindTextureAndSampler(set, binding, view.get(), sampler);
-	}
-
 	/// Convenience method to bind the whole texture as color.
 	void bindColorTexture(U32 set, U32 binding, RenderTargetHandle handle, U32 arrayIdx = 0)
 	{

+ 1 - 1
AnKi/Gr/Vulkan/AccelerationStructureImpl.h

@@ -26,7 +26,7 @@ public:
 
 	Error init(const AccelerationStructureInitInfo& inf);
 
-	VkAccelerationStructureKHR getHandle() const
+	const VkAccelerationStructureKHR& getHandle() const
 	{
 		ANKI_ASSERT(m_handle);
 		return m_handle;

+ 3 - 2
AnKi/Gr/Vulkan/BufferImpl.cpp

@@ -242,6 +242,7 @@ void* BufferImpl::map(PtrSize offset, PtrSize range, [[maybe_unused]] BufferMapA
 
 VkPipelineStageFlags BufferImpl::computePplineStage(BufferUsageBit usage)
 {
+	const Bool rt = getGrManagerImpl().getDeviceCapabilities().m_rayTracingEnabled;
 	VkPipelineStageFlags stageMask = 0;
 
 	if(!!(usage & BufferUsageBit::kAllIndirect))
@@ -275,12 +276,12 @@ VkPipelineStageFlags BufferImpl::computePplineStage(BufferUsageBit usage)
 		stageMask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
 	}
 
-	if(!!(usage & (BufferUsageBit::kAccelerationStructureBuild | BufferUsageBit::kAccelerationStructureBuildScratch)))
+	if(!!(usage & (BufferUsageBit::kAccelerationStructureBuild | BufferUsageBit::kAccelerationStructureBuildScratch)) && rt)
 	{
 		stageMask |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR;
 	}
 
-	if(!!(usage & (BufferUsageBit::kAllTraceRays & ~BufferUsageBit::kIndirectTraceRays)))
+	if(!!(usage & (BufferUsageBit::kAllTraceRays & ~BufferUsageBit::kIndirectTraceRays)) && rt)
 	{
 		stageMask |= VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
 	}

+ 0 - 6
AnKi/Gr/Vulkan/CommandBuffer.cpp

@@ -177,12 +177,6 @@ void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, Bl
 	self.setBlendOperationInternal(attachment, funcRgb, funcA);
 }
 
-void CommandBuffer::bindTextureAndSampler(U32 set, U32 binding, TextureView* texView, Sampler* sampler, U32 arrayIdx)
-{
-	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindTextureAndSamplerInternal(set, binding, texView, sampler, arrayIdx);
-}
-
 void CommandBuffer::bindTexture(U32 set, U32 binding, TextureView* texView, U32 arrayIdx)
 {
 	ANKI_VK_SELF(CommandBufferImpl);

+ 4 - 0
AnKi/Gr/Vulkan/CommandBufferFactory.cpp

@@ -35,6 +35,8 @@ MicroCommandBuffer::~MicroCommandBuffer()
 {
 	reset();
 
+	m_dsAllocator.destroy();
+
 	if(m_handle)
 	{
 		vkFreeCommandBuffers(getVkDevice(), m_threadAlloc->m_pools[m_queue], 1, &m_handle);
@@ -56,6 +58,8 @@ void MicroCommandBuffer::reset()
 		m_objectRefs[type].destroy();
 	}
 
+	m_dsAllocator.reset();
+
 	m_fastPool.reset();
 }
 

+ 7 - 1
AnKi/Gr/Vulkan/CommandBufferFactory.h

@@ -8,6 +8,7 @@
 #include <AnKi/Gr/Vulkan/FenceFactory.h>
 #include <AnKi/Gr/CommandBuffer.h>
 #include <AnKi/Gr/Vulkan/MicroObjectRecycler.h>
+#include <AnKi/Gr/Vulkan/DescriptorSet.h>
 #include <AnKi/Util/List.h>
 
 namespace anki {
@@ -99,6 +100,11 @@ public:
 		return m_queue;
 	}
 
+	DSAllocator& getDSAllocator()
+	{
+		return m_dsAllocator;
+	}
+
 private:
 	static constexpr U32 kMaxRefObjectSearch = 16;
 
@@ -108,7 +114,7 @@ private:
 	MicroFencePtr m_fence;
 	Array<DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>, U(GrObjectType::kCount)> m_objectRefs;
 
-	// Cacheline boundary
+	DSAllocator m_dsAllocator;
 
 	CommandBufferThreadAllocator* m_threadAlloc;
 	mutable Atomic<I32> m_refcount = {0};

+ 1 - 1
AnKi/Gr/Vulkan/CommandBufferImpl.cpp

@@ -59,7 +59,7 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 		m_microCmdb->pushObjectRef(m_activeFb);
 	}
 
-	for(DescriptorSetState& state : m_dsetState)
+	for(DSStateTracker& state : m_dsetState)
 	{
 		state.init(m_pool);
 	}

+ 1 - 14
AnKi/Gr/Vulkan/CommandBufferImpl.h

@@ -205,19 +205,6 @@ public:
 		m_state.setBlendOperation(attachment, funcRgb, funcA);
 	}
 
-	ANKI_FORCE_INLINE void bindTextureAndSamplerInternal(U32 set, U32 binding, TextureView* texView, Sampler* sampler, U32 arrayIdx)
-	{
-		commandCommon();
-		const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
-		const TextureImpl& tex = view.getTextureImpl();
-		ANKI_ASSERT(tex.isSubresourceGoodForSampling(view.getSubresource()));
-		const VkImageLayout lay = tex.computeLayout(TextureUsageBit::kAllSampled & tex.getTextureUsage(), 0);
-
-		m_dsetState[set].bindTextureAndSampler(binding, arrayIdx, &view, sampler, lay);
-
-		m_microCmdb->pushObjectRef(sampler);
-	}
-
 	ANKI_FORCE_INLINE void bindTextureInternal(U32 set, U32 binding, TextureView* texView, U32 arrayIdx)
 	{
 		commandCommon();
@@ -554,7 +541,7 @@ private:
 
 	PipelineStateTracker m_state;
 
-	Array<DescriptorSetState, kMaxDescriptorSets> m_dsetState;
+	Array<DSStateTracker, kMaxDescriptorSets> m_dsetState;
 
 	ShaderProgramImpl* m_graphicsProg ANKI_DEBUG_CODE(= nullptr); ///< Last bound graphics program
 	ShaderProgramImpl* m_computeProg ANKI_DEBUG_CODE(= nullptr);

+ 16 - 70
AnKi/Gr/Vulkan/CommandBufferImpl.inl.h

@@ -147,29 +147,11 @@ ANKI_FORCE_INLINE void CommandBufferImpl::traceRaysInternal(Buffer* sbtBuffer, P
 	{
 		if(sprog.getReflectionInfo().m_descriptorSetMask.get(i))
 		{
-			DescriptorSet dset;
-			Bool dirty;
-			Array<PtrSize, kMaxBindingsPerDescriptorSet> dynamicOffsetsPtrSize;
-			U32 dynamicOffsetCount;
-			if(getGrManagerImpl().getDescriptorSetFactory().newDescriptorSet(*m_pool, m_dsetState[i], dset, dirty, dynamicOffsetsPtrSize,
-																			 dynamicOffsetCount))
+			VkDescriptorSet dset;
+			if(m_dsetState[i].flush(m_microCmdb->getDSAllocator(), dset))
 			{
-				ANKI_VK_LOGF("Cannot recover");
-			}
-
-			if(dirty)
-			{
-				// Vulkan should have had the dynamic offsets as VkDeviceSize and not U32. Workaround that.
-				Array<U32, kMaxBindingsPerDescriptorSet> dynamicOffsets;
-				for(U32 i = 0; i < dynamicOffsetCount; ++i)
-				{
-					dynamicOffsets[i] = U32(dynamicOffsetsPtrSize[i]);
-				}
-
-				VkDescriptorSet dsHandle = dset.getHandle();
-
-				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, sprog.getPipelineLayout().getHandle(), i, 1, &dsHandle,
-										dynamicOffsetCount, &dynamicOffsets[0]);
+				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, sprog.getPipelineLayout().getHandle(), i, 1, &dset, 0,
+										nullptr);
 			}
 		}
 	}
@@ -260,29 +242,11 @@ ANKI_FORCE_INLINE void CommandBufferImpl::drawcallCommon()
 	{
 		if(m_graphicsProg->getReflectionInfo().m_descriptorSetMask.get(i))
 		{
-			DescriptorSet dset;
-			Bool dirty;
-			Array<PtrSize, kMaxBindingsPerDescriptorSet> dynamicOffsetsPtrSize;
-			U32 dynamicOffsetCount;
-			if(getGrManagerImpl().getDescriptorSetFactory().newDescriptorSet(*m_pool, m_dsetState[i], dset, dirty, dynamicOffsetsPtrSize,
-																			 dynamicOffsetCount))
+			VkDescriptorSet dset;
+			if(m_dsetState[i].flush(m_microCmdb->getDSAllocator(), dset))
 			{
-				ANKI_VK_LOGF("Cannot recover");
-			}
-
-			if(dirty)
-			{
-				// Vulkan should have had the dynamic offsets as VkDeviceSize and not U32. Workaround that.
-				Array<U32, kMaxBindingsPerDescriptorSet> dynamicOffsets;
-				for(U32 i = 0; i < dynamicOffsetCount; ++i)
-				{
-					dynamicOffsets[i] = U32(dynamicOffsetsPtrSize[i]);
-				}
-
-				VkDescriptorSet dsHandle = dset.getHandle();
-
-				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsProg->getPipelineLayout().getHandle(), i, 1, &dsHandle,
-										dynamicOffsetCount, &dynamicOffsets[0]);
+				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsProg->getPipelineLayout().getHandle(), i, 1, &dset, 0,
+										nullptr);
 			}
 		}
 	}
@@ -362,29 +326,11 @@ ANKI_FORCE_INLINE void CommandBufferImpl::dispatchCommon()
 	{
 		if(m_computeProg->getReflectionInfo().m_descriptorSetMask.get(i))
 		{
-			DescriptorSet dset;
-			Bool dirty;
-			Array<PtrSize, kMaxBindingsPerDescriptorSet> dynamicOffsetsPtrSize;
-			U32 dynamicOffsetCount;
-			if(getGrManagerImpl().getDescriptorSetFactory().newDescriptorSet(*m_pool, m_dsetState[i], dset, dirty, dynamicOffsetsPtrSize,
-																			 dynamicOffsetCount))
+			VkDescriptorSet dset;
+			if(m_dsetState[i].flush(m_microCmdb->getDSAllocator(), dset))
 			{
-				ANKI_VK_LOGF("Cannot recover");
-			}
-
-			if(dirty)
-			{
-				// Vulkan should have had the dynamic offsets as VkDeviceSize and not U32. Workaround that.
-				Array<U32, kMaxBindingsPerDescriptorSet> dynamicOffsets;
-				for(U32 i = 0; i < dynamicOffsetCount; ++i)
-				{
-					dynamicOffsets[i] = U32(dynamicOffsetsPtrSize[i]);
-				}
-
-				VkDescriptorSet dsHandle = dset.getHandle();
-
-				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_COMPUTE, m_computeProg->getPipelineLayout().getHandle(), i, 1, &dsHandle,
-										dynamicOffsetCount, &dynamicOffsets[0]);
+				vkCmdBindDescriptorSets(m_handle, VK_PIPELINE_BIND_POINT_COMPUTE, m_computeProg->getPipelineLayout().getHandle(), i, 1, &dset, 0,
+										nullptr);
 			}
 		}
 	}
@@ -453,13 +399,13 @@ ANKI_FORCE_INLINE void CommandBufferImpl::bindShaderProgramInternal(ShaderProgra
 	{
 		if(impl.getReflectionInfo().m_descriptorSetMask.get(i))
 		{
-			m_dsetState[i].setLayout(impl.getDescriptorSetLayout(i));
+			m_dsetState[i].setLayout(&impl.getDescriptorSetLayout(i));
 		}
 		else
 		{
-			// According to the spec the bound DS may be disturbed if the ppline layout is not compatible. Play it safe
-			// and dirty the slot. That will force rebind of the DS at drawcall time.
-			m_dsetState[i].setLayout(DescriptorSetLayout());
+			// According to the spec the bound DS may be disturbed if the ppline layout is not compatible. Play it safe and dirty the slot. That will
+			// force rebind of the DS at drawcall time.
+			m_dsetState[i].setLayoutDirty();
 		}
 	}
 

+ 6 - 4
AnKi/Gr/Vulkan/Common.cpp

@@ -274,6 +274,8 @@ VkBufferUsageFlags convertBufferUsageBit(BufferUsageBit usageMask)
 {
 	VkBufferUsageFlags out = 0;
 
+	const Bool rt = GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled;
+
 	if(!!(usageMask & BufferUsageBit::kAllConstant))
 	{
 		out |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
@@ -319,22 +321,22 @@ VkBufferUsageFlags convertBufferUsageBit(BufferUsageBit usageMask)
 		out |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
 	}
 
-	if(!!(usageMask & BufferUsageBit::kAccelerationStructureBuild))
+	if(!!(usageMask & BufferUsageBit::kAccelerationStructureBuild) && rt)
 	{
 		out |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
 	}
 
-	if(!!(usageMask & BufferUsageBit::kAccelerationStructureBuildScratch))
+	if(!!(usageMask & BufferUsageBit::kAccelerationStructureBuildScratch) && rt)
 	{
 		out |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; // Spec says that this will be enough
 	}
 
-	if(!!(usageMask & PrivateBufferUsageBit::kAccelerationStructure))
+	if(!!(usageMask & PrivateBufferUsageBit::kAccelerationStructure) && rt)
 	{
 		out |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
 	}
 
-	if(!!(usageMask & BufferUsageBit::kShaderBindingTable))
+	if(!!(usageMask & BufferUsageBit::kShaderBindingTable) && rt)
 	{
 		out |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR;
 	}

+ 5 - 7
AnKi/Gr/Vulkan/Common.h

@@ -50,7 +50,6 @@ ANKI_PURE VkDevice getVkDevice();
 
 enum class DescriptorType : U8
 {
-	kCombinedTextureSampler,
 	kTexture,
 	kSampler,
 	kUniformBuffer,
@@ -60,8 +59,10 @@ enum class DescriptorType : U8
 	kReadWriteTextureBuffer,
 	kAccelerationStructure,
 
-	kCount
+	kCount,
+	kFirst = 0
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DescriptorType)
 
 enum class VulkanExtensions : U64
 {
@@ -271,9 +272,6 @@ static_assert(!(BufferUsageBit::kAll & PrivateBufferUsageBit::kAllPrivate), "Upd
 	VkDescriptorType out;
 	switch(ak)
 	{
-	case DescriptorType::kCombinedTextureSampler:
-		out = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-		break;
 	case DescriptorType::kTexture:
 		out = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
 		break;
@@ -281,7 +279,7 @@ static_assert(!(BufferUsageBit::kAll & PrivateBufferUsageBit::kAllPrivate), "Upd
 		out = VK_DESCRIPTOR_TYPE_SAMPLER;
 		break;
 	case DescriptorType::kUniformBuffer:
-		out = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+		out = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 		break;
 	case DescriptorType::kReadTextureBuffer:
 		out = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
@@ -290,7 +288,7 @@ static_assert(!(BufferUsageBit::kAll & PrivateBufferUsageBit::kAllPrivate), "Upd
 		out = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
 		break;
 	case DescriptorType::kStorageBuffer:
-		out = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+		out = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
 		break;
 	case DescriptorType::kImage:
 		out = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 245 - 676
AnKi/Gr/Vulkan/DescriptorSet.cpp


+ 206 - 272
AnKi/Gr/Vulkan/DescriptorSet.h

@@ -16,277 +16,211 @@
 
 namespace anki {
 
-// Forward
-class DSLayoutCacheEntry;
-
-/// @addtogroup vulkan
-/// @{
-
-class alignas(8) DescriptorBinding
+class DSLayout
 {
-public:
-	U32 m_arraySize = 0;
-	ShaderTypeBit m_stageMask = ShaderTypeBit::kNone;
-	DescriptorType m_type = DescriptorType::kCount;
-	U8 m_binding = kMaxU8;
-};
-static_assert(sizeof(DescriptorBinding) == 8, "Should be packed because it will be hashed");
+	friend class DSStateTracker;
+	friend class DSLayoutFactory;
+	friend class DSAllocator;
 
-class DescriptorSetLayoutInitInfo
-{
 public:
-	WeakArray<DescriptorBinding> m_bindings;
+	const VkDescriptorSetLayout& getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+private:
+	VkDescriptorSetLayout m_handle = VK_NULL_HANDLE;
+
+	U64 m_hash = 0;
+	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_descriptorCount = 0;
 };
 
-class DescriptorSetLayout
+/// Allocates descriptor sets.
+class DSAllocator
 {
-	friend class DescriptorSetFactory;
-	friend class DescriptorSetState;
+	friend class DSStateTracker;
 
 public:
-	VkDescriptorSetLayout getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
+	DSAllocator() = default;
 
-	Bool isCreated() const
-	{
-		return m_handle != VK_NULL_HANDLE;
-	}
+	~DSAllocator();
 
-	Bool operator==(const DescriptorSetLayout& b) const
+	void destroy()
 	{
-		return m_entry == b.m_entry;
-	}
+		for(Block& b : m_blocks)
+		{
+			ANKI_ASSERT(b.m_pool != VK_NULL_HANDLE);
+			vkDestroyDescriptorPool(getVkDevice(), b.m_pool, nullptr);
+		}
 
-	Bool operator!=(const DescriptorSetLayout& b) const
-	{
-		return !operator==(b);
+		m_blocks.destroy();
+		m_activeBlock = 0;
 	}
 
+	/// Reset for reuse.
+	void reset();
+
 private:
-	VkDescriptorSetLayout m_handle = VK_NULL_HANDLE;
-	DSLayoutCacheEntry* m_entry = nullptr;
-};
+	class Block
+	{
+	public:
+		VkDescriptorPool m_pool = VK_NULL_HANDLE;
+		U32 m_dsetsAllocatedCount = 0;
+	};
 
-class TextureSamplerBinding
-{
-public:
-	VkImageView m_imgViewHandle;
-	VkSampler m_samplerHandle;
-	VkImageLayout m_layout;
-};
+	static constexpr U32 kDescriptorSetGrowScale = 2;
 
-class TextureBinding
-{
-public:
-	VkImageView m_imgViewHandle;
-	VkImageLayout m_layout;
-};
+	GrDynamicArray<Block> m_blocks;
 
-class SamplerBinding
-{
-public:
-	VkSampler m_samplerHandle;
-};
+	U32 m_activeBlock = 0;
 
-class BufferBinding
-{
-public:
-	VkBuffer m_buffHandle;
-	PtrSize m_offset;
-	PtrSize m_range;
-};
+	void allocate(const DSLayout& layout, VkDescriptorSet& set);
 
-class ImageBinding
-{
-public:
-	VkImageView m_imgViewHandle;
+	void createNewBlock();
 };
 
-class AsBinding
+/// The bindless descriptor set.
+class DSBindless : public MakeSingleton<DSBindless>
 {
-public:
-	VkAccelerationStructureKHR m_accelerationStructureHandle;
-};
+	friend class DSStateTracker;
+	friend class DSLayoutFactory;
 
-class TextureBufferBinding
-{
 public:
-	VkBufferView m_buffView;
-};
+	~DSBindless();
 
-class AnyBinding
-{
-public:
-	Array<U64, 2> m_uuids;
+	Error init(U32 bindlessTextureCount, U32 bindlessTextureBuffers);
 
-	union
-	{
-		TextureSamplerBinding m_texAndSampler;
-		TextureBinding m_tex;
-		SamplerBinding m_sampler;
-		BufferBinding m_buff;
-		ImageBinding m_image;
-		AsBinding m_accelerationStructure;
-		TextureBufferBinding m_textureBuffer;
-	};
+	/// Bind a sampled image.
+	/// @note It's thread-safe.
+	U32 bindTexture(const VkImageView view, const VkImageLayout layout);
 
-	DescriptorType m_type;
-};
-static_assert(std::is_trivial<AnyBinding>::value, "Shouldn't have constructor for perf reasons");
+	/// Bind a uniform texel buffer.
+	/// @note It's thread-safe.
+	U32 bindUniformTexelBuffer(VkBufferView view);
 
-class AnyBindingExtended
-{
-public:
-	union
+	/// @note It's thread-safe.
+	void unbindTexture(U32 idx)
 	{
-		AnyBinding m_single;
-		AnyBinding* m_array;
-	};
+		unbindCommon(idx, m_freeTexIndices, m_freeTexIndexCount);
+	}
 
-	U32 m_arraySize;
-};
-static_assert(std::is_trivial<AnyBindingExtended>::value, "Shouldn't have constructor for perf reasons");
+	/// @note It's thread-safe.
+	void unbindUniformTexelBuffer(U32 idx)
+	{
+		unbindCommon(idx, m_freeTexelBufferIndices, m_freeTexelBufferIndexCount);
+	}
 
-/// Descriptor set thin wraper.
-class DescriptorSet
-{
-	friend class DescriptorSetFactory;
-	friend class BindlessDescriptorSet;
-	friend class DescriptorSetState;
+	U32 getMaxTextureCount() const
+	{
+		return m_freeTexIndices.getSize();
+	}
 
-public:
-	VkDescriptorSet getHandle() const
+	U32 getMaxTexelBufferCount() const
 	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
+		return m_freeTexelBufferIndices.getSize();
 	}
 
 private:
-	VkDescriptorSet m_handle = VK_NULL_HANDLE;
+	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);
 };
 
-/// A state tracker of descriptors.
-class DescriptorSetState
+/// A state tracker that creates descriptors at will.
+class DSStateTracker
 {
-	friend class DescriptorSetFactory;
-
 public:
 	void init(StackMemoryPool* pool)
 	{
-		m_pool = pool;
+		m_writeInfos = {pool};
 	}
 
-	void setLayout(const DescriptorSetLayout& layout)
+	void setLayout(const DSLayout* layout)
 	{
-		if(layout.isCreated())
-		{
-			m_layoutDirty = m_layout != layout;
-		}
-		else
+		ANKI_ASSERT(layout);
+		if(layout != m_layout)
 		{
 			m_layoutDirty = true;
+			m_layout = layout;
 		}
-
-		m_layout = layout;
 	}
 
-	void bindTextureAndSampler(U32 binding, U32 arrayIdx, const TextureView* texView, const Sampler* sampler, VkImageLayout layout)
+	void setLayoutDirty()
 	{
-		const TextureViewImpl& viewImpl = static_cast<const TextureViewImpl&>(*texView);
-		ANKI_ASSERT(viewImpl.getTextureImpl().isSubresourceGoodForSampling(viewImpl.getSubresource()));
-
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
-		b.m_type = DescriptorType::kCombinedTextureSampler;
-		b.m_uuids[0] = viewImpl.getHash();
-		b.m_uuids[1] = ptrToNumber(static_cast<const SamplerImpl*>(sampler)->m_sampler->getHandle());
-
-		b.m_texAndSampler.m_imgViewHandle = viewImpl.getHandle();
-		b.m_texAndSampler.m_samplerHandle = static_cast<const SamplerImpl*>(sampler)->m_sampler->getHandle();
-		b.m_texAndSampler.m_layout = layout;
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		m_layoutDirty = true;
 	}
 
 	void bindTexture(U32 binding, U32 arrayIdx, const TextureView* texView, VkImageLayout layout)
 	{
+		ANKI_ASSERT(texView);
 		const TextureViewImpl& viewImpl = static_cast<const TextureViewImpl&>(*texView);
 		ANKI_ASSERT(viewImpl.getTextureImpl().isSubresourceGoodForSampling(viewImpl.getSubresource()));
-
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kTexture;
-		b.m_uuids[0] = b.m_uuids[1] = viewImpl.getHash();
-
-		b.m_tex.m_imgViewHandle = viewImpl.getHandle();
-		b.m_tex.m_layout = layout;
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_image.imageView = viewImpl.getHandle();
+		b.m_image.imageLayout = layout;
+		b.m_image.sampler = VK_NULL_HANDLE;
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindSampler(U32 binding, U32 arrayIdx, const Sampler* sampler)
 	{
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		ANKI_ASSERT(sampler);
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kSampler;
-		b.m_uuids[0] = b.m_uuids[1] = ptrToNumber(static_cast<const SamplerImpl*>(sampler)->m_sampler->getHandle());
-		b.m_sampler.m_samplerHandle = static_cast<const SamplerImpl*>(sampler)->m_sampler->getHandle();
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_image.sampler = static_cast<const SamplerImpl*>(sampler)->m_sampler->getHandle();
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindConstantBuffer(U32 binding, U32 arrayIdx, const Buffer* buff, PtrSize offset, PtrSize range)
 	{
-		ANKI_ASSERT(range > 0);
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		ANKI_ASSERT(buff && range > 0);
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kUniformBuffer;
-		b.m_uuids[0] = b.m_uuids[1] = buff->getUuid();
-
-		b.m_buff.m_buffHandle = static_cast<const BufferImpl*>(buff)->getHandle();
-		b.m_buff.m_offset = offset;
-		b.m_buff.m_range = range;
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_buffer.buffer = static_cast<const BufferImpl*>(buff)->getHandle();
+		b.m_buffer.offset = offset;
+		b.m_buffer.range = (range == kMaxPtrSize) ? VK_WHOLE_SIZE : range;
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindUavBuffer(U32 binding, U32 arrayIdx, const Buffer* buff, PtrSize offset, PtrSize range)
 	{
-		ANKI_ASSERT(range > 0);
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		ANKI_ASSERT(buff && range > 0);
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kStorageBuffer;
-		b.m_uuids[0] = b.m_uuids[1] = buff->getUuid();
-
-		b.m_buff.m_buffHandle = static_cast<const BufferImpl*>(buff)->getHandle();
-		b.m_buff.m_offset = offset;
-		b.m_buff.m_range = range;
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_buffer.buffer = static_cast<const BufferImpl*>(buff)->getHandle();
+		b.m_buffer.offset = offset;
+		b.m_buffer.range = (range == kMaxPtrSize) ? VK_WHOLE_SIZE : range;
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindReadOnlyTextureBuffer(U32 binding, U32 arrayIdx, const Buffer* buff, PtrSize offset, PtrSize range, Format fmt)
 	{
-		ANKI_ASSERT(range > 0);
-		const VkBufferView view = static_cast<const BufferImpl*>(buff)->getOrCreateBufferView(fmt, offset, range);
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		ANKI_ASSERT(buff && range > 0);
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kReadTextureBuffer;
-		b.m_uuids[0] = ptrToNumber(view);
-		b.m_uuids[1] = buff->getUuid();
-
-		b.m_textureBuffer.m_buffView = view;
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_bufferView = static_cast<const BufferImpl*>(buff)->getOrCreateBufferView(fmt, offset, range);
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindUavTexture(U32 binding, U32 arrayIdx, const TextureView* texView)
@@ -294,120 +228,120 @@ public:
 		ANKI_ASSERT(texView);
 		const TextureViewImpl* impl = static_cast<const TextureViewImpl*>(texView);
 		ANKI_ASSERT(impl->getTextureImpl().isSubresourceGoodForImageLoadStore(impl->getSubresource()));
-
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kImage;
-		ANKI_ASSERT(impl->getHash());
-		b.m_uuids[0] = b.m_uuids[1] = impl->getHash();
-		b.m_image.m_imgViewHandle = impl->getHandle();
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_image.imageView = impl->getHandle();
+		b.m_image.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+		setBinding(binding, arrayIdx, b);
 	}
 
 	void bindAccelerationStructure(U32 binding, U32 arrayIdx, const AccelerationStructure* as)
 	{
-		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
-		b = {};
+		ANKI_ASSERT(as);
+		Binding b;
+		zeroMemory(b);
 		b.m_type = DescriptorType::kAccelerationStructure;
-		b.m_uuids[0] = b.m_uuids[1] = as->getUuid();
-		b.m_accelerationStructure.m_accelerationStructureHandle = static_cast<const AccelerationStructureImpl*>(as)->getHandle();
-
-		m_dirtyBindings.set(binding);
-		unbindBindlessDSet();
+		b.m_as.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
+		b.m_as.accelerationStructureCount = 1;
+		b.m_as.pAccelerationStructures = &static_cast<const AccelerationStructureImpl*>(as)->getHandle();
+		setBinding(binding, arrayIdx, b);
 	}
 
 	/// Forget all the rest of the bindings and bind the whole bindless descriptor set.
 	void bindBindlessDescriptorSet()
 	{
-		m_bindlessDSetBound = true;
-		m_bindlessDSetDirty = true;
+		if(!m_bindlessDSBound)
+		{
+			m_bindlessDSBound = true;
+			m_bindlessDSDirty = true;
+		}
 	}
 
-private:
-	StackMemoryPool* m_pool = nullptr;
-	DescriptorSetLayout m_layout;
-
-	Array<AnyBindingExtended, kMaxBindingsPerDescriptorSet> m_bindings;
+	/// Get all the state and create a descriptor set.
+	/// @param[out] dsHandle It can be VK_NULL_HANDLE if there is no need to re-bind the descriptor set.
+	Bool flush(DSAllocator& allocator, VkDescriptorSet& dsHandle);
 
-	U64 m_lastHash = 0;
+private:
+	class Binding
+	{
+	public:
+		union
+		{
+			VkDescriptorImageInfo m_image;
+			VkDescriptorBufferInfo m_buffer;
+			VkWriteDescriptorSetAccelerationStructureKHR m_as;
+			VkBufferView m_bufferView;
+		};
 
-	BitSet<kMaxBindingsPerDescriptorSet, U32> m_dirtyBindings = {true};
-	BitSet<kMaxBindingsPerDescriptorSet, U32> m_bindingSet = {false};
-	Bool m_layoutDirty = true;
-	Bool m_bindlessDSetDirty = true;
-	Bool m_bindlessDSetBound = false;
+		DescriptorType m_type;
 
-	/// Only DescriptorSetFactory should call this.
-	/// @param hash If hash is zero then the DS doesn't need rebind.
-	void flush(U64& hash, Array<PtrSize, kMaxBindingsPerDescriptorSet>& dynamicOffsets, U32& dynamicOffsetCount, Bool& bindlessDSet);
+		Binding()
+		{
+			// No init for perf reasons
+		}
+	};
 
-	void unbindBindlessDSet()
+	class BindingExtended
 	{
-		m_bindlessDSetBound = false;
-	}
-
-	AnyBinding& getBindingToPopulate(U32 bindingIdx, U32 arrayIdx);
-};
+	public:
+		union
+		{
+			Binding m_single;
+			Binding* m_array;
+		};
 
-/// Creates new descriptor set layouts and descriptor sets.
-class DescriptorSetFactory
-{
-	friend class DSLayoutCacheEntry;
+		U32 m_count;
 
-public:
-	DescriptorSetFactory() = default;
-	~DescriptorSetFactory();
+		BindingExtended()
+		{
+			// No init for perf reasons
+		}
+	};
 
-	Error init(U32 bindlessTextureCount, U32 bindlessTextureBuffers);
+	const DSLayout* m_layout = nullptr;
 
-	void destroy();
+	Array<BindingExtended, kMaxBindingsPerDescriptorSet> m_bindings;
 
-	/// @note It's thread-safe.
-	Error newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout);
+	BitSet<kMaxBindingsPerDescriptorSet, U32> m_bindingIsSetMask = {false};
+	BitSet<kMaxBindingsPerDescriptorSet, U32> m_bindingDirtyMask = {true};
+	Bool m_bindlessDSDirty = true;
+	Bool m_bindlessDSBound = false;
+	Bool m_layoutDirty = true;
 
-	/// @note It's thread-safe.
-	Error newDescriptorSet(StackMemoryPool& tmpPool, DescriptorSetState& state, DescriptorSet& set, Bool& dirty,
-						   Array<PtrSize, kMaxBindingsPerDescriptorSet>& dynamicOffsets, U32& dynamicOffsetCount);
+	DynamicArray<VkWriteDescriptorSet, MemoryPoolPtrWrapper<StackMemoryPool>> m_writeInfos;
 
-	void endFrame()
+	void unbindBindlessDSet()
 	{
-		++m_frameCount;
+		m_bindlessDSBound = false;
 	}
 
-	/// Bind a sampled image.
-	/// @note It's thread-safe.
-	U32 bindBindlessTexture(const VkImageView view, const VkImageLayout layout);
+	void setBinding(U32 bindingIdx, U32 arrayIdx, const Binding& b);
+};
 
-	/// Bind a uniform texel buffer.
-	/// @note It's thread-safe.
-	U32 bindBindlessUniformTexelBuffer(const VkBufferView view);
+class alignas(8) DSBinding
+{
+public:
+	U32 m_arraySize = 0;
+	ShaderTypeBit m_stageMask = ShaderTypeBit::kNone;
+	DescriptorType m_type = DescriptorType::kCount;
+	U8 m_binding = kMaxU8;
+};
+static_assert(sizeof(DSBinding) == 8, "Should be packed because it will be hashed");
 
-	/// @note It's thread-safe.
-	void unbindBindlessTexture(U32 idx);
+class DSLayoutFactory : public MakeSingleton<DSLayoutFactory>
+{
+public:
+	DSLayoutFactory();
+
+	~DSLayoutFactory();
 
 	/// @note It's thread-safe.
-	void unbindBindlessUniformTexelBuffer(U32 idx);
+	Error getOrCreateDescriptorSetLayout(const WeakArray<DSBinding>& bindings, const DSLayout*& layout);
 
 private:
-	class BindlessDescriptorSet;
-	class DSAllocator;
-	class ThreadLocal;
-
-	static thread_local ThreadLocal* m_threadLocal;
-	GrDynamicArray<ThreadLocal*> m_allThreadLocals;
-	Mutex m_allThreadLocalsMtx;
-
-	U64 m_frameCount = 0;
-
-	GrDynamicArray<DSLayoutCacheEntry*> m_caches;
-	SpinLock m_cachesMtx; ///< Not a mutex because after a while there will be no reason to lock
-
-	BindlessDescriptorSet* m_bindless = nullptr;
-	U32 m_bindlessTextureCount = kMaxU32;
-	U32 m_bindlessUniformTexelBufferCount = kMaxU32;
+	GrDynamicArray<DSLayout*> m_layouts;
+	Mutex m_mtx;
 };
-/// @}
 
 } // end namespace anki

+ 2 - 1
AnKi/Gr/Vulkan/FrameGarbageCollector.cpp

@@ -5,6 +5,7 @@
 
 #include <AnKi/Gr/Vulkan/FrameGarbageCollector.h>
 #include <AnKi/Gr/Vulkan/GrManagerImpl.h>
+#include <AnKi/Gr/Vulkan/DescriptorSet.h>
 #include <AnKi/Gr/Fence.h>
 
 namespace anki {
@@ -49,7 +50,7 @@ void FrameGarbageCollector::collectGarbage()
 
 			for(U32 bindlessIndex : textureGarbage->m_bindlessIndices)
 			{
-				getGrManagerImpl().getDescriptorSetFactory().unbindBindlessTexture(bindlessIndex);
+				DSBindless::getSingleton().unbindTexture(bindlessIndex);
 			}
 
 			if(textureGarbage->m_imageHandle)

+ 19 - 5
AnKi/Gr/Vulkan/GrManagerImpl.cpp

@@ -9,6 +9,7 @@
 #include <AnKi/Gr/CommandBuffer.h>
 #include <AnKi/Gr/Fence.h>
 #include <AnKi/Gr/Vulkan/FenceImpl.h>
+#include <AnKi/Gr/Vulkan/DescriptorSet.h>
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/StringList.h>
 #include <AnKi/Core/App.h>
@@ -16,6 +17,7 @@
 namespace anki {
 
 BoolCVar g_validationCVar(CVarSubsystem::kGr, "Validation", false, "Enable or not validation");
+static BoolCVar g_gpuValidationCVar(CVarSubsystem::kGr, "GpuValidation", false, "Enable or not GPU validation");
 static BoolCVar g_debugPrintfCVar(CVarSubsystem::kGr, "DebugPrintf", false, "Enable or not debug printf");
 BoolCVar g_debugMarkersCVar(CVarSubsystem::kGr, "DebugMarkers", false, "Enable or not debug markers");
 BoolCVar g_vsyncCVar(CVarSubsystem::kGr, "Vsync", false, "Enable or not vsync");
@@ -78,8 +80,10 @@ GrManagerImpl::~GrManagerImpl()
 
 	m_gpuMemManager.destroy();
 
-	m_pplineLayoutFactory.destroy();
-	m_descrFactory.destroy();
+	PipelineLayoutFactory::freeSingleton();
+
+	DSLayoutFactory::freeSingleton();
+	DSBindless::freeSingleton();
 
 	m_pplineCache.destroy();
 
@@ -213,7 +217,12 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 		}
 	}
 
-	ANKI_CHECK(m_descrFactory.init(kMaxBindlessTextures, kMaxBindlessReadonlyTextureBuffers));
+	DSBindless::allocateSingleton();
+	ANKI_CHECK(DSBindless::getSingleton().init(kMaxBindlessTextures, kMaxBindlessReadonlyTextureBuffers));
+
+	DSLayoutFactory::allocateSingleton();
+
+	PipelineLayoutFactory::allocateSingleton();
 
 	m_frameGarbageCollector.init();
 
@@ -300,7 +309,7 @@ Error GrManagerImpl::initInstance()
 		disabledValidationFeatures.emplaceBack(VK_VALIDATION_FEATURE_DISABLE_ALL_EXT);
 	}
 
-	if(g_validationCVar.get())
+	if(g_validationCVar.get() && g_gpuValidationCVar.get())
 	{
 		enabledValidationFeatures.emplaceBack(VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT);
 	}
@@ -1403,7 +1412,6 @@ void GrManagerImpl::endFrame()
 		ANKI_VK_CHECKF(res);
 	}
 
-	m_descrFactory.endFrame();
 	m_gpuMemManager.updateStats();
 
 	// Finalize
@@ -1570,6 +1578,12 @@ VkBool32 GrManagerImpl::debugReportCallbackEXT(VkDebugUtilsMessageSeverityFlagBi
 	}
 #endif
 
+	if(pCallbackData->messageIdNumber == 1944932341 || pCallbackData->messageIdNumber == 1303270965)
+	{
+		// Not sure why I'm getting that
+		return false;
+	}
+
 	// Get all names of affected objects
 	GrString objectNames;
 	if(pCallbackData->objectCount)

+ 0 - 15
AnKi/Gr/Vulkan/GrManagerImpl.h

@@ -18,7 +18,6 @@
 #include <AnKi/Gr/Vulkan/SwapchainFactory.h>
 #include <AnKi/Gr/Vulkan/PipelineLayout.h>
 #include <AnKi/Gr/Vulkan/PipelineCache.h>
-#include <AnKi/Gr/Vulkan/DescriptorSet.h>
 #include <AnKi/Gr/Vulkan/FrameGarbageCollector.h>
 #include <AnKi/Util/HashMap.h>
 #include <AnKi/Util/File.h>
@@ -142,22 +141,12 @@ public:
 		return m_pipelineQueryFactories[type];
 	}
 
-	DescriptorSetFactory& getDescriptorSetFactory()
-	{
-		return m_descrFactory;
-	}
-
 	VkPipelineCache getPipelineCache() const
 	{
 		ANKI_ASSERT(m_pplineCache.m_cacheHandle);
 		return m_pplineCache.m_cacheHandle;
 	}
 
-	PipelineLayoutFactory& getPipelineLayoutFactory()
-	{
-		return m_pplineLayoutFactory;
-	}
-
 	VulkanExtensions getExtensions() const
 	{
 		return m_extensions;
@@ -283,10 +272,6 @@ private:
 
 	SwapchainFactory m_swapchainFactory;
 
-	PipelineLayoutFactory m_pplineLayoutFactory;
-
-	DescriptorSetFactory m_descrFactory;
-
 	QueryFactory m_occlusionQueryFactory;
 	QueryFactory m_timestampQueryFactory;
 	Array<QueryFactory, U32(PipelineQueryType::kCount)> m_pipelineQueryFactories;

+ 3 - 3
AnKi/Gr/Vulkan/PipelineLayout.cpp

@@ -19,14 +19,14 @@ void PipelineLayoutFactory::destroy()
 	}
 }
 
-Error PipelineLayoutFactory::newPipelineLayout(const WeakArray<DescriptorSetLayout>& dsetLayouts, U32 pushConstantsSize, PipelineLayout& layout)
+Error PipelineLayoutFactory::newPipelineLayout(const WeakArray<const DSLayout*>& dsetLayouts, U32 pushConstantsSize, PipelineLayout& layout)
 {
 	U64 hash = computeHash(&pushConstantsSize, sizeof(pushConstantsSize));
 	Array<VkDescriptorSetLayout, kMaxDescriptorSets> vkDsetLayouts;
 	U32 dsetLayoutCount = 0;
-	for(const DescriptorSetLayout& dl : dsetLayouts)
+	for(const DSLayout* dl : dsetLayouts)
 	{
-		vkDsetLayouts[dsetLayoutCount++] = dl.getHandle();
+		vkDsetLayouts[dsetLayoutCount++] = dl->getHandle();
 	}
 
 	if(dsetLayoutCount > 0)

+ 8 - 4
AnKi/Gr/Vulkan/PipelineLayout.h

@@ -29,20 +29,24 @@ private:
 };
 
 /// Creator of pipeline layouts.
-class PipelineLayoutFactory
+class PipelineLayoutFactory : public MakeSingleton<PipelineLayoutFactory>
 {
 public:
 	PipelineLayoutFactory() = default;
-	~PipelineLayoutFactory() = default;
 
-	void destroy();
+	~PipelineLayoutFactory()
+	{
+		destroy();
+	}
 
 	/// @note It's thread-safe.
-	Error newPipelineLayout(const WeakArray<DescriptorSetLayout>& dsetLayouts, U32 pushConstantsSize, PipelineLayout& layout);
+	Error newPipelineLayout(const WeakArray<const DSLayout*>& dsetLayouts, U32 pushConstantsSize, PipelineLayout& layout);
 
 private:
 	GrHashMap<U64, VkPipelineLayout> m_layouts;
 	Mutex m_layoutsMtx;
+
+	void destroy();
 };
 /// @}
 

+ 3 - 4
AnKi/Gr/Vulkan/ShaderImpl.cpp

@@ -138,7 +138,7 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 	spirv_cross::ShaderResources rsrcActive = spvc.get_shader_resources(spvc.get_active_interface_variables());
 
 	Array<U32, kMaxDescriptorSets> counts = {};
-	Array2d<DescriptorBinding, kMaxDescriptorSets, kMaxBindingsPerDescriptorSet> descriptors;
+	Array2d<DSBinding, kMaxDescriptorSets, kMaxBindingsPerDescriptorSet> descriptors;
 
 	auto func = [&](const spirv_cross::SmallVector<spirv_cross::Resource>& resources, const DescriptorType origType) -> void {
 		for(const spirv_cross::Resource& r : resources)
@@ -189,7 +189,7 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 			if(foundIdx == kMaxU32)
 			{
 				// New binding, init it
-				DescriptorBinding& descriptor = descriptors[set][counts[set]++];
+				DSBinding& descriptor = descriptors[set][counts[set]++];
 				descriptor.m_binding = U8(binding);
 				descriptor.m_type = type;
 				descriptor.m_stageMask = ShaderTypeBit(1 << m_shaderType);
@@ -205,7 +205,6 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 	};
 
 	func(rsrc.uniform_buffers, DescriptorType::kUniformBuffer);
-	func(rsrc.sampled_images, DescriptorType::kCombinedTextureSampler);
 	func(rsrc.separate_images, DescriptorType::kTexture); // This also handles texture buffers
 	func(rsrc.separate_samplers, DescriptorType::kSampler);
 	func(rsrc.storage_buffers, DescriptorType::kStorageBuffer);
@@ -217,7 +216,7 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 		if(counts[set])
 		{
 			m_bindings[set].resize(counts[set]);
-			memcpy(&m_bindings[set][0], &descriptors[set][0], counts[set] * sizeof(DescriptorBinding));
+			memcpy(&m_bindings[set][0], &descriptors[set][0], counts[set] * sizeof(DSBinding));
 		}
 	}
 

+ 1 - 1
AnKi/Gr/Vulkan/ShaderImpl.h

@@ -20,7 +20,7 @@ class ShaderImpl final : public Shader
 public:
 	VkShaderModule m_handle = VK_NULL_HANDLE;
 
-	Array<GrDynamicArray<DescriptorBinding>, kMaxDescriptorSets> m_bindings;
+	Array<GrDynamicArray<DSBinding>, kMaxDescriptorSets> m_bindings;
 	BitSet<kMaxColorRenderTargets, U8> m_colorAttachmentWritemask = {false};
 	BitSet<kMaxVertexAttributes, U8> m_attributeMask = {false};
 	BitSet<kMaxDescriptorSets, U8> m_descriptorSetMask = {false};

+ 5 - 7
AnKi/Gr/Vulkan/ShaderProgramImpl.cpp

@@ -98,7 +98,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 	// Merge bindings
 	//
-	Array2d<DescriptorBinding, kMaxDescriptorSets, kMaxBindingsPerDescriptorSet> bindings;
+	Array2d<DSBinding, kMaxDescriptorSets, kMaxBindingsPerDescriptorSet> bindings;
 	Array<U32, kMaxDescriptorSets> counts = {};
 	U32 descriptorSetCount = 0;
 	for(U32 set = 0; set < kMaxDescriptorSets; ++set)
@@ -159,10 +159,8 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 	//
 	for(U32 set = 0; set < descriptorSetCount; ++set)
 	{
-		DescriptorSetLayoutInitInfo dsinf;
-		dsinf.m_bindings = WeakArray<DescriptorBinding>((counts[set]) ? &bindings[set][0] : nullptr, counts[set]);
-
-		ANKI_CHECK(getGrManagerImpl().getDescriptorSetFactory().newDescriptorSetLayout(dsinf, m_descriptorSetLayouts[set]));
+		ANKI_CHECK(DSLayoutFactory::getSingleton().getOrCreateDescriptorSetLayout(
+			WeakArray<DSBinding>((counts[set]) ? &bindings[set][0] : nullptr, counts[set]), m_descriptorSetLayouts[set]));
 
 		// Even if the dslayout is empty we will have to list it because we'll have to bind a DS for it.
 		m_refl.m_descriptorSetMask.set(set);
@@ -170,8 +168,8 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 	// Create the ppline layout
 	//
-	WeakArray<DescriptorSetLayout> dsetLayouts((descriptorSetCount) ? &m_descriptorSetLayouts[0] : nullptr, descriptorSetCount);
-	ANKI_CHECK(getGrManagerImpl().getPipelineLayoutFactory().newPipelineLayout(dsetLayouts, m_refl.m_pushConstantsSize, m_pplineLayout));
+	WeakArray<const DSLayout*> dsetLayouts((descriptorSetCount) ? &m_descriptorSetLayouts[0] : nullptr, descriptorSetCount);
+	ANKI_CHECK(PipelineLayoutFactory::getSingleton().newPipelineLayout(dsetLayouts, m_refl.m_pushConstantsSize, m_pplineLayout));
 
 	// Get some masks
 	//

+ 5 - 4
AnKi/Gr/Vulkan/ShaderProgramImpl.h

@@ -7,6 +7,7 @@
 
 #include <AnKi/Gr/ShaderProgram.h>
 #include <AnKi/Gr/Vulkan/PipelineLayout.h>
+#include <AnKi/Gr/Vulkan/DescriptorSet.h>
 
 namespace anki {
 
@@ -56,10 +57,10 @@ public:
 		return m_pplineLayout;
 	}
 
-	const DescriptorSetLayout& getDescriptorSetLayout(U32 set) const
+	const DSLayout& getDescriptorSetLayout(U32 set) const
 	{
-		ANKI_ASSERT(m_descriptorSetLayouts[set].isCreated());
-		return m_descriptorSetLayouts[set];
+		ANKI_ASSERT(m_descriptorSetLayouts[set]);
+		return *m_descriptorSetLayouts[set];
 	}
 
 	const ShaderProgramReflectionInfo& getReflectionInfo() const
@@ -113,7 +114,7 @@ private:
 	GrDynamicArray<ShaderPtr> m_shaders;
 
 	PipelineLayout m_pplineLayout = {};
-	Array<DescriptorSetLayout, kMaxDescriptorSets> m_descriptorSetLayouts;
+	Array<const DSLayout*, kMaxDescriptorSets> m_descriptorSetLayouts = {};
 
 	ShaderProgramReflectionInfo m_refl;
 

+ 5 - 4
AnKi/Gr/Vulkan/TextureImpl.cpp

@@ -42,7 +42,7 @@ static Bool isAstcSrgbFormat(const VkFormat format)
 	}
 }
 
-U32 MicroImageView::getOrCreateBindlessIndex(GrManagerImpl& gr) const
+U32 MicroImageView::getOrCreateBindlessIndex() const
 {
 	LockGuard<SpinLock> lock(m_bindlessIndexLock);
 
@@ -55,7 +55,7 @@ U32 MicroImageView::getOrCreateBindlessIndex(GrManagerImpl& gr) const
 	{
 		// Needs binding to the bindless descriptor set
 
-		outIdx = gr.getDescriptorSetFactory().bindBindlessTexture(m_handle, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+		outIdx = DSBindless::getSingleton().bindTexture(m_handle, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
 		m_bindlessIndex = outIdx;
 	}
 
@@ -339,6 +339,7 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit usage, Bool src, U32 level,
 	stages = 0;
 	accesses = 0;
 	const Bool depthStencil = !!m_aspect;
+	const Bool rt = getGrManagerImpl().getDeviceCapabilities().m_rayTracingEnabled;
 
 	if(!!(usage & (TextureUsageBit::kSampledGeometry | TextureUsageBit::kUavGeometryRead)))
 	{
@@ -378,13 +379,13 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit usage, Bool src, U32 level,
 		accesses |= VK_ACCESS_SHADER_WRITE_BIT;
 	}
 
-	if(!!(usage & (TextureUsageBit::kSampledTraceRays | TextureUsageBit::kUavTraceRaysRead)))
+	if(!!(usage & (TextureUsageBit::kSampledTraceRays | TextureUsageBit::kUavTraceRaysRead)) && rt)
 	{
 		stages |= VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
 		accesses |= VK_ACCESS_SHADER_READ_BIT;
 	}
 
-	if(!!(usage & TextureUsageBit::kUavTraceRaysWrite))
+	if(!!(usage & TextureUsageBit::kUavTraceRaysWrite) && rt)
 	{
 		stages |= VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
 		accesses |= VK_ACCESS_SHADER_WRITE_BIT;

+ 1 - 1
AnKi/Gr/Vulkan/TextureImpl.h

@@ -56,7 +56,7 @@ public:
 	}
 
 	/// @note It's thread-safe.
-	U32 getOrCreateBindlessIndex(GrManagerImpl& gr) const;
+	U32 getOrCreateBindlessIndex() const;
 
 	TextureType getDerivedTextureType() const
 	{

+ 1 - 1
AnKi/Gr/Vulkan/TextureViewImpl.cpp

@@ -40,7 +40,7 @@ U32 TextureViewImpl::getOrCreateBindlessIndex()
 {
 	if(m_bindlessIndex == kMaxU32)
 	{
-		m_bindlessIndex = m_microImageView->getOrCreateBindlessIndex(getGrManagerImpl());
+		m_bindlessIndex = m_microImageView->getOrCreateBindlessIndex();
 	}
 
 	return m_bindlessIndex;

+ 61 - 10
Tests/Gr/Gr.cpp

@@ -235,6 +235,8 @@ static RebarTransientMemoryPool* stagingMem = nullptr;
 static Input* input = nullptr;
 
 #define COMMON_BEGIN() \
+	DefaultMemoryPool::allocateSingleton(allocAligned, nullptr); \
+	ShaderCompilerMemoryPool::allocateSingleton(allocAligned, nullptr); \
 	g_windowWidthCVar.set(WIDTH); \
 	g_windowHeightCVar.set(HEIGHT); \
 	g_validationCVar.set(true); \
@@ -259,6 +261,8 @@ static Input* input = nullptr;
 	GrManager::freeSingleton(); \
 	Input::freeSingleton(); \
 	NativeWindow::freeSingleton(); \
+	ShaderCompilerMemoryPool::freeSingleton(); \
+	DefaultMemoryPool::freeSingleton(); \
 	g_win = nullptr; \
 	g_gr = nullptr;
 
@@ -739,7 +743,7 @@ void main()
 		cmdb->setScissor(0, 0, g_win->getWidth(), g_win->getHeight());
 		cmdb->bindShaderProgram(blitProg.get());
 		setTextureSurfaceBarrier(cmdb, rt, TextureUsageBit::kFramebufferWrite, TextureUsageBit::kSampledFragment, TextureSurfaceInfo(0, 0, 0, 0));
-		cmdb->bindTextureAndSampler(0, 0, texView.get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 0, texView.get(), sampler.get());
 		presentBarrierA(cmdb, presentTex);
 		cmdb->beginRenderPass(dfb.get(), {TextureUsageBit::kFramebufferWrite}, {});
 		cmdb->draw(PrimitiveTopology::kTriangles, 6);
@@ -806,8 +810,55 @@ ANKI_TEST(Gr, DrawWithUniforms)
 	ptr[2] = Vec4(0.0, 0.0, 1.0, 0.0);
 	b->unmap();
 
-	// Progm
-	ShaderProgramPtr prog = createProgram(VERT_UBO_SRC, FRAG_UBO_SRC, *g_gr);
+	// Prog
+	constexpr const char* kUboVert = R"(
+struct A
+{
+	float4 m_color[3];
+};
+[[vk::binding(0)]] ConstantBuffer<A> g_color;
+
+struct B
+{
+	float4 m_rotation2d;
+};
+[[vk::binding(1)]] ConstantBuffer<B> g_rotation2d;
+
+struct VertOut
+{
+	float4 m_svPosition : SV_POSITION;
+	float3 m_color : COLOR;
+};
+
+VertOut main(uint svVertexId : SV_VERTEXID)
+{
+	VertOut o;
+	o.m_color = g_color.m_color[svVertexId].xyz;
+
+	const float2 kPositions[3] = {float2(-1.0, 1.0), float2(0.0, -1.0), float2(1.0, 1.0)};
+
+	float2x2 rot = float2x2(
+		g_rotation2d.m_rotation2d.x, g_rotation2d.m_rotation2d.y, g_rotation2d.m_rotation2d.z, g_rotation2d.m_rotation2d.w);
+	float2 pos = mul(rot, kPositions[svVertexId % 3]);
+
+	o.m_svPosition = float4(pos, 0.0, 1.0);
+
+	return o;
+})";
+
+	constexpr const char* kUboFrag = R"(
+struct VertOut
+{
+	float4 m_svPosition : SV_POSITION;
+	float3 m_color : COLOR;
+};
+
+float4 main(VertOut i) : SV_TARGET0
+{
+	return float4(i.m_color, 1.0);
+})";
+
+	ShaderProgramPtr prog = createProgram(kUboVert, kUboFrag, *g_gr);
 
 	const U ITERATION_COUNT = 100;
 	U iterations = ITERATION_COUNT;
@@ -1134,8 +1185,8 @@ void main()
 		Vec4 pc(F32(g_win->getWidth()), F32(g_win->getHeight()), 0.0f, 0.0f);
 		cmdb->setPushConstants(&pc, sizeof(pc));
 
-		cmdb->bindTextureAndSampler(0, 0, aView.get(), sampler.get());
-		cmdb->bindTextureAndSampler(0, 1, bView.get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 0, aView.get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 1, bView.get(), sampler.get());
 		cmdb->draw(PrimitiveTopology::kTriangles, 6);
 		cmdb->endRenderPass();
 		presentBarrierB(cmdb, presentTex);
@@ -1305,8 +1356,8 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 		cmdb->beginRenderPass(dfb.get(), {TextureUsageBit::kFramebufferWrite}, {});
 		cmdb->bindShaderProgram(resolveProg.get());
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->bindTextureAndSampler(0, 0, col0View.get(), sampler.get());
-		cmdb->bindTextureAndSampler(0, 2, col1View.get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 0, col0View.get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 2, col1View.get(), sampler.get());
 		cmdb->draw(PrimitiveTopology::kTriangles, 6);
 		cmdb->endRenderPass();
 		presentBarrierB(cmdb, presentTex);
@@ -1427,7 +1478,7 @@ ANKI_TEST(Gr, ImageLoadStore)
 		FramebufferPtr dfb = createColorFb(*g_gr, presentTex);
 		presentBarrierA(cmdb, presentTex);
 		cmdb->beginRenderPass(dfb.get(), {TextureUsageBit::kFramebufferWrite}, {});
-		cmdb->bindTextureAndSampler(0, 0, g_gr->newTextureView(TextureViewInitInfo(tex.get())).get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 0, g_gr->newTextureView(TextureViewInitInfo(tex.get())).get(), sampler.get());
 		cmdb->draw(PrimitiveTopology::kTriangles, 6);
 		cmdb->endRenderPass();
 		presentBarrierB(cmdb, presentTex);
@@ -1537,7 +1588,7 @@ ANKI_TEST(Gr, 3DTextures)
 		U32 idx = U32((F32(ITERATION_COUNT - iterations - 1) / F32(ITERATION_COUNT)) * F32(TEX_COORDS_LOD.getSize()));
 		*uv = TEX_COORDS_LOD[idx];
 
-		cmdb->bindTextureAndSampler(0, 1, g_gr->newTextureView(TextureViewInitInfo(a.get())).get(), sampler.get());
+		// cmdb->bindTextureAndSampler(0, 1, g_gr->newTextureView(TextureViewInitInfo(a.get())).get(), sampler.get());
 		cmdb->draw(PrimitiveTopology::kTriangles, 6);
 
 		cmdb->endRenderPass();
@@ -1865,7 +1916,7 @@ void main()
 
 	setTextureBarrier(cmdb, tex, TextureUsageBit::kTransferDestination, TextureUsageBit::kSampledCompute, subresource);
 	cmdb->bindShaderProgram(prog.get());
-	cmdb->bindTextureAndSampler(0, 0, texView.get(), sampler.get());
+	// cmdb->bindTextureAndSampler(0, 0, texView.get(), sampler.get());
 	cmdb->bindUavBuffer(0, 1, resultBuff.get(), 0, resultBuff->getSize());
 	cmdb->dispatchCompute(1, 1, 1);
 

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott