Browse Source

Vulkan: Have more fine grained layouts

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
a3a2a46913

+ 1 - 0
shaders/FsCommonVert.glsl

@@ -14,6 +14,7 @@
 #define LIGHT_SS_BINDING 0
 #define LIGHT_TEX_BINDING 1
 #define LIGHT_UBO_BINDING 0
+#define LIGHT_MINIMAL
 #include "shaders/IsFsCommon.glsl"
 #undef LIGHT_SET
 #undef LIGHT_SS_BINDING

+ 2 - 4
shaders/Is.frag.glsl

@@ -11,11 +11,9 @@
 #define LIGHT_SS_BINDING 0
 #define LIGHT_UBO_BINDING 0
 #define LIGHT_TEX_BINDING 4
+#define LIGHT_INDIRECT
+#define LIGHT_DECALS
 #include "shaders/IsFsCommon.glsl"
-#undef LIGHT_SET
-#undef LIGHT_SS_BINDING
-#undef LIGHT_TEX_BINDING
-#undef LIGHT_UBO_BINDING
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msRt0;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_msRt1;

+ 6 - 2
shaders/IsFsCommon.glsl

@@ -72,15 +72,19 @@ layout(ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING + 2), std140, row_major) un
 	SpotLight u_spotLights[UBO_MAX_SIZE / (9 * 4 * 4)];
 };
 
+#ifdef LIGHT_INDIRECT
 layout(std140, row_major, ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING + 3)) uniform u3_
 {
 	ReflectionProbe u_reflectionProbes[UBO_MAX_SIZE / (2 * 4 * 4)];
 };
+#endif
 
+#ifdef LIGHT_DECALS
 layout(std140, row_major, ANKI_UBO_BINDING(LIGHT_SET, LIGHT_UBO_BINDING + 4)) uniform u4_
 {
 	Decal u_decals[UBO_MAX_SIZE / ((4 + 16) * 4)];
 };
+#endif
 
 layout(ANKI_SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 0), std430) readonly buffer s0_
 {
@@ -95,11 +99,11 @@ layout(std430, ANKI_SS_BINDING(LIGHT_SET, LIGHT_SS_BINDING + 1)) readonly buffer
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING)) uniform highp sampler2DArrayShadow u_spotMapArr;
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 1)) uniform highp samplerCubeArrayShadow u_omniMapArr;
 
+#ifdef LIGHT_INDIRECT
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 2)) uniform samplerCubeArray u_reflectionsTex;
-
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 3)) uniform samplerCubeArray u_irradianceTex;
-
 layout(ANKI_TEX_BINDING(LIGHT_SET, LIGHT_TEX_BINDING + 4)) uniform sampler2D u_integrationLut;
+#endif
 
 #endif // FRAGMENT_SHADER
 

+ 2 - 4
src/anki/Config.h.cmake

@@ -27,8 +27,7 @@
 #elif defined(__APPLE_CC__)
 #	if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) \
 		&& __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 40000) \
-	|| (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \
-		&& __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
+	|| (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
 #		define ANKI_OS ANKI_OS_IOS
 #	else
 #		define ANKI_OS ANKI_OS_MACOS
@@ -40,8 +39,7 @@
 #endif
 
 // POSIX system or not
-#if ANKI_OS == ANKI_OS_LINUX || ANKI_OS == ANKI_OS_ANDROID \
-	|| ANKI_OS == ANKI_OS_MACOS || ANKI_OS == ANKI_OS_IOS
+#if ANKI_OS == ANKI_OS_LINUX || ANKI_OS == ANKI_OS_ANDROID || ANKI_OS == ANKI_OS_MACOS || ANKI_OS == ANKI_OS_IOS
 #	define ANKI_POSIX 1
 #else
 #	define ANKI_POSIX 0

+ 15 - 27
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -99,16 +99,6 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 	return ErrorCode::NONE;
 }
 
-void CommandBufferImpl::bindPipeline(PipelinePtr ppline)
-{
-	commandCommon();
-	ANKI_CMD(vkCmdBindPipeline(
-				 m_handle, ppline->getImplementation().getBindPoint(), ppline->getImplementation().getHandle()),
-		ANY_OTHER_COMMAND);
-
-	m_pplineList.pushBack(m_alloc, ppline);
-}
-
 void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
 {
 	commandCommon();
@@ -198,31 +188,29 @@ void CommandBufferImpl::endRecording()
 	m_finalized = true;
 }
 
-void CommandBufferImpl::bindResourceGroup(ResourceGroupPtr rc, U slot, const TransientMemoryInfo* dynInfo)
+void CommandBufferImpl::bindResourceGroup(ResourceGroupPtr rc, U set, const TransientMemoryInfo* dynInfo)
 {
 	commandCommon();
 	const ResourceGroupImpl& impl = rc->getImplementation();
 
-	if(impl.hasDescriptorSet())
+	if(impl.m_handle)
 	{
-		Array<U32, MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS> dynOffsets = {{}};
-
-		impl.setupDynamicOffsets(dynInfo, &dynOffsets[0]);
-
-		VkDescriptorSet dset = impl.getHandle();
-		ANKI_CMD(vkCmdBindDescriptorSets(m_handle,
-					 impl.getPipelineBindPoint(),
-					 getGrManagerImpl().getGlobalPipelineLayout(),
-					 slot,
-					 1,
-					 &dset,
-					 dynOffsets.getSize(),
-					 &dynOffsets[0]),
-			ANY_OTHER_COMMAND);
+		DeferredDsetBinding& binding = m_deferredDsetBindings[set];
+		m_deferredDsetBindingMask |= (1 << set);
+
+		/// Defer the dset bindings until you know the ppline layout
+		U dynOfsetCount = 0;
+		impl.setupDynamicOffsets(dynInfo, &binding.m_dynOffsets[0], dynOfsetCount);
+		binding.m_dynOffsetCount = dynOfsetCount;
+		binding.m_bindPoint = impl.m_bindPoint;
+		binding.m_dset = impl.m_handle;
+#if ANKI_ASSERTIONS
+		binding.m_dsetLayoutInfo = impl.m_descriptorSetLayoutInfo;
+#endif
 	}
 
 	// Bind vertex and index buffer only in the first set
-	if(slot == 0)
+	if(set == 0)
 	{
 		Array<VkBuffer, MAX_VERTEX_ATTRIBUTES> buffers = {{}};
 		Array<VkDeviceSize, MAX_VERTEX_ATTRIBUTES> offsets = {{}};

+ 24 - 0
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -7,6 +7,7 @@
 
 #include <anki/gr/vulkan/VulkanObject.h>
 #include <anki/gr/CommandBuffer.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 #include <anki/util/List.h>
 
 namespace anki
@@ -231,6 +232,27 @@ private:
 	U16 m_writeQueryAtomCount = 0;
 	/// @}
 
+	class DeferredDsetBinding
+	{
+	public:
+		Array<U32, MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS> m_dynOffsets;
+		U8 m_dynOffsetCount;
+		VkPipelineBindPoint m_bindPoint;
+		VkDescriptorSet m_dset;
+#if ANKI_ASSERTIONS
+		DescriptorSetLayoutInfo m_dsetLayoutInfo;
+#endif
+	};
+	Array<DeferredDsetBinding, MAX_BOUND_RESOURCE_GROUPS> m_deferredDsetBindings;
+	U8 m_deferredDsetBindingMask = 0;
+	VkPipelineLayout m_crntPplineLayout = VK_NULL_HANDLE;
+#if ANKI_ASSERTIONS
+	Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS> m_pplineDsetLayoutInfos;
+	U8 m_pplineDsetMask = 0;
+
+	U8 m_boundDsetsMask = 0;
+#endif
+
 	/// Some common operations per command.
 	void commandCommon();
 
@@ -259,6 +281,8 @@ private:
 
 	void flushWriteQueryResults();
 
+	void flushDsetBindings();
+
 	void clearTextureInternal(TexturePtr tex, const ClearValue& clearValue, const VkImageSubresourceRange& range);
 
 	void setTextureBarrierInternal(

+ 60 - 0
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -6,6 +6,8 @@
 #include <anki/gr/vulkan/CommandBufferImpl.h>
 #include <anki/gr/vulkan/GrManagerImpl.h>
 #include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/gr/Pipeline.h>
+#include <anki/gr/vulkan/PipelineImpl.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/vulkan/BufferImpl.h>
 #include <anki/gr/OcclusionQuery.h>
@@ -343,6 +345,7 @@ inline void CommandBufferImpl::drawElementsIndirect(U32 drawCount, PtrSize offse
 inline void CommandBufferImpl::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ)
 {
 	commandCommon();
+	flushDsetBindings();
 	ANKI_CMD(vkCmdDispatch(m_handle, groupCountX, groupCountY, groupCountZ), ANY_OTHER_COMMAND);
 }
 
@@ -503,6 +506,8 @@ inline void CommandBufferImpl::drawcallCommon()
 		beginRenderPassInternal();
 	}
 
+	flushDsetBindings();
+
 	++m_rpCommandCount;
 
 	ANKI_TRACE_INC_COUNTER(GR_DRAWCALLS, 1);
@@ -606,4 +611,59 @@ inline void CommandBufferImpl::writeOcclusionQueryResultToBuffer(
 	m_bufferList.pushBack(m_alloc, buff);
 }
 
+inline void CommandBufferImpl::flushDsetBindings()
+{
+	for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+	{
+		if(m_deferredDsetBindingMask & (1 << i))
+		{
+			ANKI_ASSERT(m_crntPplineLayout);
+			const DeferredDsetBinding& binding = m_deferredDsetBindings[i];
+
+			ANKI_CMD(vkCmdBindDescriptorSets(m_handle,
+						 binding.m_bindPoint,
+						 m_crntPplineLayout,
+						 i,
+						 1,
+						 &binding.m_dset,
+						 binding.m_dynOffsetCount,
+						 &binding.m_dynOffsets[0]),
+				ANY_OTHER_COMMAND);
+#if ANKI_DEBUG
+			m_boundDsetsMask |= (1 << i);
+#endif
+		}
+	}
+
+	m_deferredDsetBindingMask = 0;
+
+#if ANKI_DEBUG
+	ANKI_ASSERT((m_boundDsetsMask & m_pplineDsetMask) == m_pplineDsetMask);
+
+	for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+	{
+		if(m_pplineDsetMask & (1 << i))
+		{
+			ANKI_ASSERT(m_pplineDsetLayoutInfos[i] == m_deferredDsetBindings[i].m_dsetLayoutInfo);
+		}
+	}
+#endif
+}
+
+inline void CommandBufferImpl::bindPipeline(PipelinePtr ppline)
+{
+	commandCommon();
+	const PipelineImpl& impl = ppline->getImplementation();
+
+	ANKI_CMD(vkCmdBindPipeline(m_handle, impl.m_bindPoint, impl.m_handle), ANY_OTHER_COMMAND);
+
+	m_crntPplineLayout = impl.m_pipelineLayout;
+#if ANKI_ASSERTIONS
+	m_pplineDsetLayoutInfos = impl.m_descriptorSetLayoutInfos;
+	m_pplineDsetMask = impl.m_descriptorSetMask;
+#endif
+
+	m_pplineList.pushBack(m_alloc, ppline);
+}
+
 } // end namespace anki

+ 3 - 26
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -58,11 +58,7 @@ GrManagerImpl::~GrManagerImpl()
 	// THIRD THING: Continue with the rest
 	m_rpCreator.destroy();
 
-	if(m_globalPipelineLayout)
-	{
-		vkDestroyPipelineLayout(m_device, m_globalPipelineLayout, nullptr);
-	}
-
+	m_pplineLayFactory.destroy();
 	m_dsetAlloc.destroy();
 	m_transientMem.destroy();
 	m_gpuMemManager.destroy();
@@ -129,8 +125,8 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	}
 
 	ANKI_CHECK(initMemory(*init.m_config));
-	ANKI_CHECK(m_dsetAlloc.init(m_device));
-	ANKI_CHECK(initGlobalPplineLayout());
+	ANKI_CHECK(m_dsetAlloc.init(getAllocator(), m_device));
+	m_pplineLayFactory.init(getAllocator(), m_device, &m_dsetAlloc.getDescriptorSetLayoutFactory());
 
 	for(PerFrame& f : m_perFrame)
 	{
@@ -484,25 +480,6 @@ Error GrManagerImpl::initSwapchain(const GrManagerInitInfo& init)
 	return ErrorCode::NONE;
 }
 
-Error GrManagerImpl::initGlobalPplineLayout()
-{
-	Array<VkDescriptorSetLayout, MAX_BOUND_RESOURCE_GROUPS> sets = {
-		{m_dsetAlloc.getGlobalDescriptorSetLayout(), m_dsetAlloc.getGlobalDescriptorSetLayout()}};
-
-	VkPipelineLayoutCreateInfo ci;
-	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
-	ci.pNext = nullptr;
-	ci.flags = 0;
-	ci.setLayoutCount = MAX_BOUND_RESOURCE_GROUPS;
-	ci.pSetLayouts = &sets[0];
-	ci.pushConstantRangeCount = 0;
-	ci.pPushConstantRanges = nullptr;
-
-	ANKI_VK_CHECK(vkCreatePipelineLayout(m_device, &ci, nullptr, &m_globalPipelineLayout));
-
-	return ErrorCode::NONE;
-}
-
 Error GrManagerImpl::initMemory(const ConfigSet& cfg)
 {
 	vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);

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

@@ -69,12 +69,6 @@ public:
 		return m_physicalDevice;
 	}
 
-	VkPipelineLayout getGlobalPipelineLayout() const
-	{
-		ANKI_ASSERT(m_globalPipelineLayout);
-		return m_globalPipelineLayout;
-	}
-
 	/// @name object_creation
 	/// @{
 
@@ -189,6 +183,11 @@ public:
 		return *m_samplerCache;
 	}
 
+	PipelineLayoutFactory& getPipelineLayoutFactory()
+	{
+		return m_pplineLayFactory;
+	}
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -237,7 +236,7 @@ private:
 	/// @}
 
 	DescriptorSetAllocator m_dsetAlloc;
-	VkPipelineLayout m_globalPipelineLayout = VK_NULL_HANDLE;
+	PipelineLayoutFactory m_pplineLayFactory;
 
 	/// Map for compatible render passes.
 	CompatibleRenderPassCreator m_rpCreator;
@@ -302,7 +301,6 @@ private:
 	ANKI_USE_RESULT Error initDevice(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initSwapchain(const GrManagerInitInfo& init);
 	ANKI_USE_RESULT Error initFramebuffers(const GrManagerInitInfo& init);
-	ANKI_USE_RESULT Error initGlobalPplineLayout();
 	ANKI_USE_RESULT Error initMemory(const ConfigSet& cfg);
 
 	static void* allocateCallback(

+ 52 - 0
src/anki/gr/vulkan/PipelineExtra.cpp

@@ -154,4 +154,56 @@ VkRenderPass CompatibleRenderPassCreator::createNewRenderPass(const PipelineInit
 	return rpass;
 }
 
+void PipelineLayoutFactory::destroy()
+{
+	for(auto it : m_map)
+	{
+		VkPipelineLayout lay = it;
+		vkDestroyPipelineLayout(m_dev, lay, nullptr);
+	}
+
+	m_map.destroy(m_alloc);
+}
+
+Error PipelineLayoutFactory::getOrCreateLayout(
+	U8 setMask, const Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS>& dsinf, VkPipelineLayout& out)
+{
+	ANKI_ASSERT(setMask == 0 || setMask == 1 || setMask == 3);
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	Key key(setMask, dsinf);
+	auto it = m_map.find(key);
+
+	if(it != m_map.getEnd())
+	{
+		out = *it;
+	}
+	else
+	{
+		// Create the layout
+
+		Array<VkDescriptorSetLayout, MAX_BOUND_RESOURCE_GROUPS> sets;
+
+		VkPipelineLayoutCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+		ci.pSetLayouts = &sets[0];
+
+		for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+		{
+			if(setMask & (1 << i))
+			{
+				ANKI_CHECK(m_dsetLayFactory->getOrCreateLayout(dsinf[i], sets[i]));
+				++ci.setLayoutCount;
+			}
+		}
+
+		ANKI_VK_CHECK(vkCreatePipelineLayout(m_dev, &ci, nullptr, &out));
+
+		m_map.pushBack(m_alloc, key, out);
+	}
+
+	return ErrorCode::NONE;
+}
+
 } // end namespace anki

+ 70 - 1
src/anki/gr/vulkan/PipelineExtra.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/gr/vulkan/Common.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/Hash.h>
 
@@ -82,6 +82,75 @@ private:
 
 	VkRenderPass createNewRenderPass(const PipelineInitInfo& init);
 };
+
+/// Creator of pipeline layouts.
+class PipelineLayoutFactory
+{
+public:
+	PipelineLayoutFactory() = default;
+
+	void init(GrAllocator<U8> alloc, VkDevice dev, DescriptorSetLayoutFactory* dsetLayFactory)
+	{
+		ANKI_ASSERT(dev);
+		ANKI_ASSERT(dsetLayFactory);
+		m_alloc = alloc;
+		m_dev = dev;
+		m_dsetLayFactory = dsetLayFactory;
+	}
+
+	void destroy();
+
+	ANKI_USE_RESULT Error getOrCreateLayout(
+		U8 setMask, const Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS>& dsinf, VkPipelineLayout& out);
+
+private:
+	class Key
+	{
+	public:
+		U64 m_hash = 0;
+
+		Key(U8 setMask, const Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS>& dsinf)
+		{
+			ANKI_ASSERT(MAX_BOUND_RESOURCE_GROUPS == 2 && "The following lines make that assumption");
+			for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+			{
+				if(setMask & (1 << i))
+				{
+					U64 hash = dsinf[i].computeHash();
+					ANKI_ASSERT((hash >> 32) == 0);
+					m_hash = (m_hash << 32) | hash;
+				}
+			}
+		}
+	};
+
+	/// Hash the hash.
+	class Hasher
+	{
+	public:
+		U64 operator()(const Key& b) const
+		{
+			return b.m_hash;
+		}
+	};
+
+	/// Hash compare.
+	class Compare
+	{
+	public:
+		Bool operator()(const Key& a, const Key& b) const
+		{
+			return a.m_hash == b.m_hash;
+		}
+	};
+
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+	DescriptorSetLayoutFactory* m_dsetLayFactory = nullptr;
+
+	HashMap<Key, VkPipelineLayout, Hasher, Compare> m_map;
+	Mutex m_mtx;
+};
 /// @}
 
 } // end namespace anki

+ 38 - 2
src/anki/gr/vulkan/PipelineImpl.cpp

@@ -84,7 +84,8 @@ Error PipelineImpl::initGraphics(const PipelineInitInfo& init)
 	ci.pDynamicState = initDynamicState(ci.m_dyn);
 
 	// Finalize
-	ci.layout = getGrManagerImpl().getGlobalPipelineLayout();
+	ANKI_CHECK(createPplineLayout(init));
+	ci.layout = m_pipelineLayout;
 	ci.renderPass = getGrManagerImpl().getCompatibleRenderPassCreator().getOrCreateCompatibleRenderPass(init);
 	ci.basePipelineHandle = VK_NULL_HANDLE;
 
@@ -98,7 +99,9 @@ Error PipelineImpl::initCompute(const PipelineInitInfo& init)
 {
 	VkComputePipelineCreateInfo ci = {};
 	ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
-	ci.layout = getGrManagerImpl().getGlobalPipelineLayout();
+
+	ANKI_CHECK(createPplineLayout(init));
+	ci.layout = m_pipelineLayout;
 	ci.basePipelineHandle = VK_NULL_HANDLE;
 
 	VkPipelineShaderStageCreateInfo& stage = ci.stage;
@@ -313,4 +316,37 @@ Error PipelineImpl::init(const PipelineInitInfo& init)
 	return ErrorCode::NONE;
 }
 
+Error PipelineImpl::createPplineLayout(const PipelineInitInfo& init)
+{
+	// Merge the descriptor layout infos
+	for(U j = 0; j < MAX_BOUND_RESOURCE_GROUPS; ++j)
+	{
+		DescriptorSetLayoutInfo& dsetInfo = m_descriptorSetLayoutInfos[j];
+		memset(&dsetInfo, 0, sizeof(dsetInfo));
+
+		for(U i = 0; i < U(ShaderType::COUNT); ++i)
+		{
+			if(init.m_shaders[i] && (init.m_shaders[i]->getImplementation().m_descriptorSetMask & (1 << j)))
+			{
+				const DescriptorSetLayoutInfo& dsetInfo1 =
+					init.m_shaders[i]->getImplementation().m_descriptorSetLayoutInfos[j];
+
+				// Merge
+				dsetInfo.m_texCount = max(dsetInfo.m_texCount, dsetInfo1.m_texCount);
+				dsetInfo.m_uniCount = max(dsetInfo.m_uniCount, dsetInfo1.m_uniCount);
+				dsetInfo.m_storageCount = max(dsetInfo.m_storageCount, dsetInfo1.m_storageCount);
+				dsetInfo.m_imgCount = max(dsetInfo.m_imgCount, dsetInfo1.m_imgCount);
+
+				m_descriptorSetMask |= (1 << j);
+			}
+		}
+	}
+
+	// Create the layout
+	PipelineLayoutFactory& factory = getGrManagerImpl().getPipelineLayoutFactory();
+	ANKI_CHECK(factory.getOrCreateLayout(m_descriptorSetMask, m_descriptorSetLayoutInfos, m_pipelineLayout));
+
+	return ErrorCode::NONE;
+}
+
 } // end namespace anki

+ 9 - 15
src/anki/gr/vulkan/PipelineImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 
 namespace anki
 {
@@ -26,6 +27,12 @@ class ColorStateInfo;
 class PipelineImpl : public VulkanObject
 {
 public:
+	VkPipeline m_handle = VK_NULL_HANDLE;
+	VkPipelineBindPoint m_bindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
+	VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+	Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS> m_descriptorSetLayoutInfos;
+	U8 m_descriptorSetMask = 0;
+
 	PipelineImpl(GrManager* manager)
 		: VulkanObject(manager)
 	{
@@ -35,22 +42,7 @@ public:
 
 	ANKI_USE_RESULT Error init(const PipelineInitInfo& init);
 
-	VkPipeline getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
-
-	VkPipelineBindPoint getBindPoint() const
-	{
-		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
-		return m_bindPoint;
-	}
-
 private:
-	VkPipeline m_handle = VK_NULL_HANDLE;
-	VkPipelineBindPoint m_bindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
-
 	ANKI_USE_RESULT Error initGraphics(const PipelineInitInfo& init);
 
 	ANKI_USE_RESULT Error initCompute(const PipelineInitInfo& init);
@@ -80,6 +72,8 @@ private:
 		const ColorStateInfo& c, VkPipelineColorBlendStateCreateInfo& ci);
 
 	ANKI_USE_RESULT VkPipelineDynamicStateCreateInfo* initDynamicState(VkPipelineDynamicStateCreateInfo& ci);
+
+	ANKI_USE_RESULT Error createPplineLayout(const PipelineInitInfo& init);
 };
 /// @}
 

+ 77 - 129
src/anki/gr/vulkan/ResourceGroupExtra.cpp

@@ -8,134 +8,27 @@
 namespace anki
 {
 
-Error DescriptorSetAllocator::init(VkDevice dev)
-{
-	ANKI_ASSERT(dev);
-	m_dev = dev;
-	ANKI_CHECK(initGlobalDsetLayout());
-	ANKI_CHECK(initGlobalDsetPool());
-	return ErrorCode::NONE;
-}
-
-void DescriptorSetAllocator::destroy()
-{
-	if(m_globalDPool)
-	{
-		vkDestroyDescriptorPool(m_dev, m_globalDPool, nullptr);
-		m_globalDPool = VK_NULL_HANDLE;
-	}
-
-	if(m_globalDsetLayout)
-	{
-		vkDestroyDescriptorSetLayout(m_dev, m_globalDsetLayout, nullptr);
-		m_globalDsetLayout = VK_NULL_HANDLE;
-	}
-}
-
-Error DescriptorSetAllocator::initGlobalDsetLayout()
+void DescriptorSetLayoutFactory::destroy()
 {
-	VkDescriptorSetLayoutCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-
-	const U BINDING_COUNT =
-		MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
-	ci.bindingCount = BINDING_COUNT;
-
-	Array<VkDescriptorSetLayoutBinding, BINDING_COUNT> bindings;
-	memset(&bindings[0], 0, sizeof(bindings));
-	ci.pBindings = &bindings[0];
-
-	U count = 0;
-
-	// Combined image samplers
-	for(U i = 0; i < MAX_TEXTURE_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Uniform buffers
-	for(U i = 0; i < MAX_UNIFORM_BUFFER_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Storage buffers
-	for(U i = 0; i < MAX_STORAGE_BUFFER_BINDINGS; ++i)
-	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_ALL;
-
-		++count;
-	}
-
-	// Images
-	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
+	for(auto it : m_map)
 	{
-		VkDescriptorSetLayoutBinding& binding = bindings[count];
-		binding.binding = count;
-		binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
-		binding.descriptorCount = 1;
-		binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
-
-		++count;
+		VkDescriptorSetLayout dset = it;
+		vkDestroyDescriptorSetLayout(m_dev, dset, nullptr);
 	}
 
-	ANKI_ASSERT(count == BINDING_COUNT);
-
-	ANKI_VK_CHECK(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &m_globalDsetLayout));
-
-	return ErrorCode::NONE;
-}
-
-Error DescriptorSetAllocator::initGlobalDsetPool()
-{
-	Array<VkDescriptorPoolSize, 4> pools = {{}};
-	pools[0] =
-		VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_TEXTURE_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[1] = VkDescriptorPoolSize{
-		VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_UNIFORM_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[2] = VkDescriptorPoolSize{
-		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, MAX_STORAGE_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
-	pools[3] = VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_IMAGE_BINDINGS * MAX_RESOURCE_GROUPS};
-
-	VkDescriptorPoolCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
-	ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
-	ci.maxSets = MAX_RESOURCE_GROUPS;
-	ci.poolSizeCount = pools.getSize();
-	ci.pPoolSizes = &pools[0];
-
-	ANKI_VK_CHECK(vkCreateDescriptorPool(m_dev, &ci, nullptr, &m_globalDPool));
-
-	return ErrorCode::NONE;
+	m_map.destroy(m_alloc);
 }
 
-VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
-	U texBindingCount, U uniBindingCount, U storageBindingCount, U imageBindingCount)
+Error DescriptorSetLayoutFactory::getOrCreateLayout(const DescriptorSetLayoutInfo& dsinf, VkDescriptorSetLayout& out)
 {
+	out = VK_NULL_HANDLE;
 	LockGuard<Mutex> lock(m_mtx);
 
-	Key key(texBindingCount, uniBindingCount, storageBindingCount, imageBindingCount);
-	auto it = m_map.find(key);
+	auto it = m_map.find(dsinf);
 
 	if(it != m_map.getEnd())
 	{
-		return *it;
+		out = *it;
 	}
 	else
 	{
@@ -155,7 +48,7 @@ VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
 		U bindingIdx = 0;
 
 		// Combined image samplers
-		for(U i = 0; i < texBindingCount; ++i)
+		for(U i = 0; i < dsinf.m_texCount; ++i)
 		{
 			VkDescriptorSetLayoutBinding& binding = bindings[count++];
 			binding.binding = bindingIdx++;
@@ -166,7 +59,7 @@ VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
 
 		// Uniform buffers
 		bindingIdx = MAX_TEXTURE_BINDINGS;
-		for(U i = 0; i < uniBindingCount; ++i)
+		for(U i = 0; i < dsinf.m_uniCount; ++i)
 		{
 			VkDescriptorSetLayoutBinding& binding = bindings[count++];
 			binding.binding = bindingIdx++;
@@ -177,7 +70,7 @@ VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
 
 		// Storage buffers
 		bindingIdx = MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS;
-		for(U i = 0; i < storageBindingCount; ++i)
+		for(U i = 0; i < dsinf.m_storageCount; ++i)
 		{
 			VkDescriptorSetLayoutBinding& binding = bindings[count++];
 			binding.binding = bindingIdx++;
@@ -188,7 +81,7 @@ VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
 
 		// Images
 		bindingIdx = MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS;
-		for(U i = 0; i < imageBindingCount; ++i)
+		for(U i = 0; i < dsinf.m_imgCount; ++i)
 		{
 			VkDescriptorSetLayoutBinding& binding = bindings[count++];
 			binding.binding = bindingIdx++;
@@ -200,23 +93,78 @@ VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
 		ANKI_ASSERT(count <= BINDING_COUNT);
 		ci.bindingCount = count;
 
-		VkDescriptorSetLayout dset;
-		ANKI_VK_CHECKF(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &dset));
+		ANKI_VK_CHECK(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &out));
 
-		m_map.pushBack(m_alloc, key, dset);
-		return dset;
+		m_map.pushBack(m_alloc, dsinf, out);
 	}
+
+	return ErrorCode::NONE;
 }
 
-void DescriptorSetLayoutFactory::destroy()
+Error DescriptorSetAllocator::init(GrAllocator<U8> alloc, VkDevice dev)
 {
-	for(auto it : m_map)
+	ANKI_ASSERT(dev);
+	m_dev = dev;
+	ANKI_CHECK(initGlobalDsetPool());
+	m_layoutFactory.init(alloc, dev);
+
+	return ErrorCode::NONE;
+}
+
+void DescriptorSetAllocator::destroy()
+{
+	if(m_globalDPool)
 	{
-		VkDescriptorSetLayout dset = it;
-		vkDestroyDescriptorSetLayout(m_dev, dset, nullptr);
+		vkDestroyDescriptorPool(m_dev, m_globalDPool, nullptr);
+		m_globalDPool = VK_NULL_HANDLE;
 	}
 
-	m_map.destroy(m_alloc);
+	m_layoutFactory.destroy();
+}
+
+Error DescriptorSetAllocator::initGlobalDsetPool()
+{
+	Array<VkDescriptorPoolSize, 4> pools = {{}};
+	pools[0] =
+		VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_TEXTURE_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[1] = VkDescriptorPoolSize{
+		VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_UNIFORM_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[2] = VkDescriptorPoolSize{
+		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, MAX_STORAGE_BUFFER_BINDINGS * MAX_RESOURCE_GROUPS};
+	pools[3] = VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_IMAGE_BINDINGS * MAX_RESOURCE_GROUPS};
+
+	VkDescriptorPoolCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+	ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+	ci.maxSets = MAX_RESOURCE_GROUPS;
+	ci.poolSizeCount = pools.getSize();
+	ci.pPoolSizes = &pools[0];
+
+	ANKI_VK_CHECK(vkCreateDescriptorPool(m_dev, &ci, nullptr, &m_globalDPool));
+
+	return ErrorCode::NONE;
+}
+
+Error DescriptorSetAllocator::allocate(const DescriptorSetLayoutInfo& dsinf, VkDescriptorSet& out)
+{
+	VkDescriptorSetLayout layout;
+	ANKI_CHECK(m_layoutFactory.getOrCreateLayout(dsinf, layout));
+
+	out = VK_NULL_HANDLE;
+	VkDescriptorSetAllocateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+	ci.descriptorPool = m_globalDPool;
+	ci.descriptorSetCount = 1;
+	ci.pSetLayouts = &layout;
+
+	LockGuard<Mutex> lock(m_mtx);
+	if(++m_descriptorSetAllocationCount > MAX_RESOURCE_GROUPS)
+	{
+		ANKI_LOGE("Exceeded the MAX_RESOURCE_GROUPS");
+		return ErrorCode::OUT_OF_MEMORY;
+	}
+	ANKI_VK_CHECK(vkAllocateDescriptorSets(m_dev, &ci, &out));
+	return ErrorCode::NONE;
 }
 
 } // end namespace anki

+ 70 - 68
src/anki/gr/vulkan/ResourceGroupExtra.h

@@ -14,67 +14,36 @@ namespace anki
 /// @addtogroup vulkan
 /// @{
 
-/// Allocator of descriptor sets.
-class DescriptorSetAllocator : public NonCopyable
+class DescriptorSetLayoutInfo
 {
 public:
-	DescriptorSetAllocator()
+	U8 m_texCount = MAX_U8;
+	U8 m_uniCount = MAX_U8;
+	U8 m_storageCount = MAX_U8;
+	U8 m_imgCount = MAX_U8;
+
+	DescriptorSetLayoutInfo() = default;
+
+	DescriptorSetLayoutInfo(U texBindingCount, U uniBindingCount, U storageBindingCount, U imageBindingCount)
+		: m_texCount(texBindingCount)
+		, m_uniCount(uniBindingCount)
+		, m_storageCount(storageBindingCount)
+		, m_imgCount(imageBindingCount)
 	{
+		ANKI_ASSERT(m_texCount < MAX_U8 && m_uniCount < MAX_U8 && m_storageCount < MAX_U8 && m_imgCount < MAX_U8);
 	}
 
-	~DescriptorSetAllocator()
+	Bool operator==(const DescriptorSetLayoutInfo& b) const
 	{
-		ANKI_ASSERT(m_globalDPool == VK_NULL_HANDLE);
-		ANKI_ASSERT(m_globalDsetLayout == VK_NULL_HANDLE);
+		return m_texCount == b.m_texCount && m_uniCount == b.m_uniCount && m_storageCount == b.m_storageCount
+			&& m_imgCount == b.m_imgCount;
 	}
 
-	ANKI_USE_RESULT Error init(VkDevice dev);
-
-	void destroy();
-
-	ANKI_USE_RESULT Error allocate(VkDescriptorSet& out)
+	U64 computeHash() const
 	{
-		out = VK_NULL_HANDLE;
-		VkDescriptorSetAllocateInfo ci = {};
-		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-		ci.descriptorPool = m_globalDPool;
-		ci.descriptorSetCount = 1;
-		ci.pSetLayouts = &m_globalDsetLayout;
-
-		LockGuard<Mutex> lock(m_mtx);
-		if(++m_descriptorSetAllocationCount > MAX_RESOURCE_GROUPS)
-		{
-			ANKI_LOGE("Exceeded the MAX_RESOURCE_GROUPS");
-			return ErrorCode::OUT_OF_MEMORY;
-		}
-		ANKI_VK_CHECK(vkAllocateDescriptorSets(m_dev, &ci, &out));
-		return ErrorCode::NONE;
-	}
-
-	void free(VkDescriptorSet ds)
-	{
-		ANKI_ASSERT(ds);
-		LockGuard<Mutex> lock(m_mtx);
-		ANKI_ASSERT(m_descriptorSetAllocationCount > 0);
-		--m_descriptorSetAllocationCount;
-		ANKI_VK_CHECKF(vkFreeDescriptorSets(m_dev, m_globalDPool, 1, &ds));
+		ANKI_ASSERT(m_texCount < MAX_U8 && m_uniCount < MAX_U8 && m_storageCount < MAX_U8 && m_imgCount < MAX_U8);
+		return (m_texCount << 24) | (m_uniCount << 16) | (m_storageCount << 8) | m_imgCount;
 	}
-
-	VkDescriptorSetLayout getGlobalDescriptorSetLayout() const
-	{
-		ANKI_ASSERT(m_globalDsetLayout);
-		return m_globalDsetLayout;
-	}
-
-private:
-	VkDevice m_dev = VK_NULL_HANDLE;
-	VkDescriptorSetLayout m_globalDsetLayout = VK_NULL_HANDLE;
-	VkDescriptorPool m_globalDPool = VK_NULL_HANDLE;
-	Mutex m_mtx;
-	U32 m_descriptorSetAllocationCount = 0;
-
-	Error initGlobalDsetLayout();
-	Error initGlobalDsetPool();
 };
 
 /// Creates descriptor set layouts.
@@ -98,21 +67,10 @@ public:
 	void destroy();
 
 	/// If there is no bindings for a specific type then pass zero.
-	VkDescriptorSetLayout createLayout(
-		U texBindingCount, U uniBindingCount, U storageBindingCount, U imageBindingCount);
+	ANKI_USE_RESULT Error getOrCreateLayout(const DescriptorSetLayoutInfo& dsinf, VkDescriptorSetLayout& out);
 
 private:
-	class Key
-	{
-	public:
-		U64 m_hash;
-
-		Key(U a, U b, U c, U d)
-		{
-			ANKI_ASSERT(a < 0xFF && b < 0xFF && c < 0xFF && d < 0xFF);
-			m_hash = (a << 24) | (b << 16) | (c << 8) | d;
-		}
-	};
+	using Key = DescriptorSetLayoutInfo;
 
 	/// Hash the hash.
 	class Hasher
@@ -120,7 +78,7 @@ private:
 	public:
 		U64 operator()(const Key& b) const
 		{
-			return b.m_hash;
+			return b.computeHash();
 		}
 	};
 
@@ -130,16 +88,60 @@ private:
 	public:
 		Bool operator()(const Key& a, const Key& b) const
 		{
-			return a.m_hash == b.m_hash;
+			return a.computeHash() == b.computeHash();
 		}
 	};
 
-	HashMap<Key, VkDescriptorSetLayout, Hasher, Compare> m_map;
-
 	GrAllocator<U8> m_alloc;
 	VkDevice m_dev = VK_NULL_HANDLE;
 
+	HashMap<Key, VkDescriptorSetLayout, Hasher, Compare> m_map;
+
+	Mutex m_mtx;
+};
+
+/// Allocator of descriptor sets.
+class DescriptorSetAllocator : public NonCopyable
+{
+public:
+	DescriptorSetAllocator()
+	{
+	}
+
+	~DescriptorSetAllocator()
+	{
+		ANKI_ASSERT(m_globalDPool == VK_NULL_HANDLE);
+	}
+
+	ANKI_USE_RESULT Error init(GrAllocator<U8> alloc, VkDevice dev);
+
+	void destroy();
+
+	ANKI_USE_RESULT Error allocate(const DescriptorSetLayoutInfo& dsinf, VkDescriptorSet& out);
+
+	void free(VkDescriptorSet ds)
+	{
+		ANKI_ASSERT(ds);
+		LockGuard<Mutex> lock(m_mtx);
+		ANKI_ASSERT(m_descriptorSetAllocationCount > 0);
+		--m_descriptorSetAllocationCount;
+		ANKI_VK_CHECKF(vkFreeDescriptorSets(m_dev, m_globalDPool, 1, &ds));
+	}
+
+	DescriptorSetLayoutFactory& getDescriptorSetLayoutFactory()
+	{
+		return m_layoutFactory;
+	}
+
+private:
+	VkDevice m_dev = VK_NULL_HANDLE;
+	VkDescriptorPool m_globalDPool = VK_NULL_HANDLE;
 	Mutex m_mtx;
+	U32 m_descriptorSetAllocationCount = 0;
+
+	DescriptorSetLayoutFactory m_layoutFactory;
+
+	Error initGlobalDsetPool();
 };
 /// @}
 

+ 24 - 3
src/anki/gr/vulkan/ResourceGroupImpl.cpp

@@ -81,6 +81,7 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 	hasUploaded = false;
 	needsDSet = false;
 
+	m_descriptorSetLayoutInfo.m_texCount = 0;
 	for(U i = 0; i < MAX_TEXTURE_BINDINGS; ++i)
 	{
 		const TextureBinding& b = init.m_textures[i];
@@ -90,6 +91,8 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			needsDSet = true;
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(b.m_texture->getImplementation().usageValid(b.m_usage));
+
+			m_descriptorSetLayoutInfo.m_texCount = i + 1;
 		}
 
 		if(b.m_sampler)
@@ -99,6 +102,7 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 		}
 	}
 
+	m_descriptorSetLayoutInfo.m_uniCount = 0;
 	for(U i = 0; i < MAX_UNIFORM_BUFFER_BINDINGS; ++i)
 	{
 		const BufferBinding& b = init.m_uniformBuffers[i];
@@ -109,6 +113,8 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(!!(b.m_usage & BufferUsageBit::UNIFORM_ALL) && !(b.m_usage & ~BufferUsageBit::UNIFORM_ALL));
 			ANKI_ASSERT(b.m_buffer->getImplementation().usageValid(b.m_usage));
+
+			m_descriptorSetLayoutInfo.m_uniCount = i + 1;
 		}
 		else if(b.m_uploadedMemory)
 		{
@@ -116,9 +122,12 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			needsDSet = true;
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(!!(b.m_usage & BufferUsageBit::UNIFORM_ALL) && !(b.m_usage & ~BufferUsageBit::UNIFORM_ALL));
+
+			m_descriptorSetLayoutInfo.m_uniCount = i + 1;
 		}
 	}
 
+	m_descriptorSetLayoutInfo.m_storageCount = 0;
 	for(U i = 0; i < MAX_STORAGE_BUFFER_BINDINGS; ++i)
 	{
 		const BufferBinding& b = init.m_storageBuffers[i];
@@ -129,6 +138,8 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(!!(b.m_usage & BufferUsageBit::STORAGE_ALL) && !(b.m_usage & ~BufferUsageBit::STORAGE_ALL));
 			ANKI_ASSERT(b.m_buffer->getImplementation().usageValid(b.m_usage));
+
+			m_descriptorSetLayoutInfo.m_storageCount = i + 1;
 		}
 		else if(b.m_uploadedMemory)
 		{
@@ -136,9 +147,12 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			needsDSet = true;
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(!!(b.m_usage & BufferUsageBit::STORAGE_ALL) && !(b.m_usage & ~BufferUsageBit::STORAGE_ALL));
+
+			m_descriptorSetLayoutInfo.m_storageCount = i + 1;
 		}
 	}
 
+	m_descriptorSetLayoutInfo.m_imgCount = 0;
 	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
 	{
 		const ImageBinding& b = init.m_images[i];
@@ -148,6 +162,8 @@ U ResourceGroupImpl::calcRefCount(const ResourceGroupInitInfo& init, Bool& hasUp
 			needsDSet = true;
 			updateBindPoint(b.m_usage);
 			ANKI_ASSERT(b.m_texture->getImplementation().usageValid(b.m_usage));
+
+			m_descriptorSetLayoutInfo.m_imgCount = i + 1;
 		}
 	}
 
@@ -191,7 +207,7 @@ Error ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
 	//
 	if(needsDSet)
 	{
-		ANKI_CHECK(getGrManagerImpl().getDescriptorSetAllocator().allocate(m_handle));
+		ANKI_CHECK(getGrManagerImpl().getDescriptorSetAllocator().allocate(m_descriptorSetLayoutInfo, m_handle));
 		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
 	}
 
@@ -205,6 +221,7 @@ Error ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
 	U writeCount = 0;
 	refCount = 0;
 	Bool hole = false;
+	(void)hole;
 	U count = 0;
 
 	// 1st the textures
@@ -454,8 +471,12 @@ Error ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
 	return ErrorCode::NONE;
 }
 
-void ResourceGroupImpl::setupDynamicOffsets(const TransientMemoryInfo* dynInfo, U32 dynOffsets[]) const
+void ResourceGroupImpl::setupDynamicOffsets(
+	const TransientMemoryInfo* dynInfo, U32 dynOffsets[], U& dynOffsetCount) const
 {
+	dynOffsetCount = m_uniBindingCount + m_storageBindingCount;
+	memset(&dynOffsets[0], 0, dynOffsetCount * sizeof(dynOffsets[0]));
+
 	if(m_dynamicBuffersMask.getAny())
 	{
 		// Has at least one uploaded buffer
@@ -489,7 +510,7 @@ void ResourceGroupImpl::setupDynamicOffsets(const TransientMemoryInfo* dynInfo,
 				if(!token.isUnused())
 				{
 					ANKI_ASSERT(!!(token.m_usage & BufferUsageBit::STORAGE_ALL));
-					dynOffsets[MAX_UNIFORM_BUFFER_BINDINGS + i] = token.m_offset;
+					dynOffsets[m_uniBindingCount + i] = token.m_offset;
 				}
 			}
 		}

+ 6 - 21
src/anki/gr/vulkan/ResourceGroupImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 #include <anki/util/BitSet.h>
 
 namespace anki
@@ -18,6 +19,10 @@ namespace anki
 class ResourceGroupImpl : public VulkanObject
 {
 public:
+	VkDescriptorSet m_handle = VK_NULL_HANDLE;
+	VkPipelineBindPoint m_bindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
+	DescriptorSetLayoutInfo m_descriptorSetLayoutInfo;
+
 	ResourceGroupImpl(GrManager* manager)
 		: VulkanObject(manager)
 	{
@@ -27,21 +32,10 @@ public:
 
 	ANKI_USE_RESULT Error init(const ResourceGroupInitInfo& init);
 
-	Bool hasDescriptorSet() const
-	{
-		return m_handle != VK_NULL_HANDLE;
-	}
-
-	VkDescriptorSet getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
-
 	void getVertexBindingInfo(
 		const TransientMemoryInfo* trans, VkBuffer buffers[], VkDeviceSize offsets[], U& bindingCount) const;
 
-	void setupDynamicOffsets(const TransientMemoryInfo* dynInfo, U32 dynOffsets[]) const;
+	void setupDynamicOffsets(const TransientMemoryInfo* dynInfo, U32 dynOffsets[], U& dynOffsetCount) const;
 
 	/// Get index buffer info.
 	/// @return false if there is no index buffer.
@@ -58,16 +52,7 @@ public:
 		return false;
 	}
 
-	VkPipelineBindPoint getPipelineBindPoint() const
-	{
-		ANKI_ASSERT(m_bindPoint != VK_PIPELINE_BIND_POINT_MAX_ENUM);
-		return m_bindPoint;
-	}
-
 private:
-	VkDescriptorSet m_handle = VK_NULL_HANDLE;
-	VkPipelineBindPoint m_bindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
-
 	class
 	{
 	public:

+ 14 - 0
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -5,6 +5,7 @@
 
 #include <anki/gr/vulkan/ShaderImpl.h>
 #include <anki/gr/vulkan/GrManagerImpl.h>
+#include <anki/gr/vulkan/SpirvReflection.h>
 #include <anki/gr/common/Misc.h>
 #include <glslang/Public/ShaderLang.h>
 #include <SPIRV/GlslangToSpv.h>
@@ -303,6 +304,19 @@ Error ShaderImpl::init(ShaderType shaderType, const CString& source)
 
 	ANKI_VK_CHECK(vkCreateShaderModule(getDevice(), &ci, nullptr, &m_handle));
 
+	// Get reflection info
+	SpirvReflection refl(getAllocator(), &spirv[0], &spirv[0] + spirv.size());
+	ANKI_CHECK(refl.parse());
+
+	m_descriptorSetMask = refl.getDescriptorSetMask();
+	for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+	{
+		if(m_descriptorSetMask & (1 << i))
+		{
+			m_descriptorSetLayoutInfos[i] = refl.getLayoutInfo(i);
+		}
+	}
+
 	return ErrorCode::NONE;
 }
 

+ 3 - 0
src/anki/gr/vulkan/ShaderImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 #include <anki/util/String.h>
 #include <vector>
 
@@ -21,6 +22,8 @@ class ShaderImpl : public VulkanObject
 public:
 	VkShaderModule m_handle = VK_NULL_HANDLE;
 	ShaderType m_shaderType = ShaderType::COUNT;
+	U8 m_descriptorSetMask = 0;
+	Array<DescriptorSetLayoutInfo, MAX_BOUND_RESOURCE_GROUPS> m_descriptorSetLayoutInfos;
 
 	ShaderImpl(GrManager* manager)
 		: VulkanObject(manager)

+ 48 - 41
src/anki/gr/vulkan/SpirvReflection.cpp

@@ -35,63 +35,68 @@ Error SpirvReflection::parse()
 	ListAuto<Variable> variables(m_alloc);
 
 	// For all instructions
-	U counter = 0;
+	U counter = sizeof(SpirvHeader) / sizeof(Word);
 	while(counter < m_spv.getSize())
 	{
 		Word combo = m_spv[counter];
-		Word instruction = combo & 0xFFFF;
-		Word instrctionWordCount = combo >> 16;
+		spv::Op instruction = spv::Op(combo & 0xFFFF);
+		Word instructionWordCount = combo >> 16;
 
 		// If decorate
 		if(instruction == spv::OpDecorate)
 		{
-			U c = counter + sizeof(Word);
-
-			// Find or create variable
+			U c = counter + 1;
 			Word rezId = m_spv[c++];
-			Variable* var = nullptr;
-			for(Variable& v : variables)
+			spv::Decoration decoration = spv::Decoration(m_spv[c++]);
+
+			if(decoration == spv::Decoration::DecorationDescriptorSet
+				|| decoration == spv::Decoration::DecorationBinding)
 			{
-				if(v.m_id == rezId)
+				// Find or create variable
+				Variable* var = nullptr;
+				for(Variable& v : variables)
 				{
-					var = &v;
-					break;
+					if(v.m_id == rezId)
+					{
+						var = &v;
+						break;
+					}
 				}
-			}
 
-			if(var == nullptr)
-			{
-				Variable v;
-				v.m_id = rezId;
-				variables.pushBack(v);
-				var = &variables.getBack();
-			}
-
-			// Check the decoration
-			Word decoration = m_spv[c++];
-			if(decoration == Word(spv::Decoration::DecorationDescriptorSet))
-			{
-				Word set = m_spv[c];
-				if(set >= MAX_RESOURCE_GROUPS)
+				if(var == nullptr)
 				{
-					ANKI_LOGE("Cannot accept shaders with descriptor set >= to %u", MAX_RESOURCE_GROUPS);
-					return ErrorCode::USER_DATA;
+					Variable v;
+					v.m_id = rezId;
+					variables.pushBack(v);
+					var = &variables.getBack();
 				}
 
-				m_setMask |= 1 << set;
-				ANKI_ASSERT(var.m_set == MAX_U8);
-				var->m_set = set;
-			}
-			else if(decoration == Word(spv::Decoration::DecorationBinding))
-			{
-				Word binding = m_spv[c];
+				// Check the decoration
+				if(decoration == spv::Decoration::DecorationDescriptorSet)
+				{
+					Word set = m_spv[c];
+					if(set >= MAX_BOUND_RESOURCE_GROUPS)
+					{
+						ANKI_LOGE("Cannot accept shaders with descriptor set >= to %u", MAX_BOUND_RESOURCE_GROUPS);
+						return ErrorCode::USER_DATA;
+					}
+
+					m_setMask |= 1 << set;
+					ANKI_ASSERT(var->m_set == MAX_U8);
+					var->m_set = set;
+				}
+				else
+				{
+					ANKI_ASSERT(decoration == spv::Decoration::DecorationBinding);
+					Word binding = m_spv[c];
 
-				ANKI_ASSERT(var.m_binding == MAX_U8);
-				var->m_binding = binding;
+					ANKI_ASSERT(var->m_binding == MAX_U8);
+					var->m_binding = binding;
+				}
 			}
 		}
 
-		counter += instrctionWordCount;
+		counter += instructionWordCount;
 	}
 
 	// Iterate the variables
@@ -111,17 +116,19 @@ Error SpirvReflection::parse()
 		}
 		else if(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS)
 		{
-			m_uniBindings[set] = max(m_uniBindings[set], binding + 1);
+			m_uniBindings[set] = max<U>(m_uniBindings[set], binding + 1 - MAX_TEXTURE_BINDINGS);
 		}
 		else if(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS)
 		{
-			m_storageBindings[set] = max(m_storageBindings[set], binding + 1);
+			m_storageBindings[set] =
+				max<U>(m_storageBindings[set], binding + 1 - (MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS));
 		}
 		else
 		{
 			ANKI_ASSERT(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS
 					+ MAX_IMAGE_BINDINGS);
-			m_imageBindings[set] = max(m_imageBindings[set], binding + 1);
+			m_imageBindings[set] = max<U>(m_imageBindings[set],
+				binding + 1 - (MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS));
 		}
 	}
 

+ 14 - 27
src/anki/gr/vulkan/SpirvReflection.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/gr/vulkan/Common.h>
+#include <anki/gr/vulkan/ResourceGroupExtra.h>
 
 namespace anki
 {
@@ -29,44 +29,31 @@ public:
 
 	ANKI_USE_RESULT Error parse();
 
-	U32 getDescriptorSetMask() const
+	U8 getDescriptorSetMask() const
 	{
 		return m_setMask;
 	}
 
-	U getTextureBindingCount(U set) const
+	DescriptorSetLayoutInfo getLayoutInfo(U set) const
 	{
 		ANKI_ASSERT(m_setMask & (1 << set));
-		return m_texBindings[set];
-	}
-
-	U getUniformBindingCount(U set) const
-	{
-		ANKI_ASSERT(m_setMask & (1 << set));
-		return m_uniBindings[set];
-	}
-
-	U getStorageBindingCount(U set) const
-	{
-		ANKI_ASSERT(m_setMask & (1 << set));
-		return m_storageBindings[set];
-	}
-
-	U getImageBindingCount(U set) const
-	{
-		ANKI_ASSERT(m_setMask & (1 << set));
-		return m_imageBindings[set];
+		DescriptorSetLayoutInfo out;
+		out.m_texCount = m_texBindings[set];
+		out.m_uniCount = m_uniBindings[set];
+		out.m_storageCount = m_storageBindings[set];
+		out.m_imgCount = m_imageBindings[set];
+		return out;
 	}
 
 private:
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	WeakArray<const U32> m_spv;
 
-	U32 m_setMask = 0;
-	Array<U32, MAX_RESOURCE_GROUPS> m_texBindings = {{}};
-	Array<U32, MAX_RESOURCE_GROUPS> m_uniBindings = {{}};
-	Array<U32, MAX_RESOURCE_GROUPS> m_storageBindings = {{}};
-	Array<U32, MAX_RESOURCE_GROUPS> m_imageBindings = {{}};
+	U8 m_setMask = 0;
+	Array<U32, MAX_BOUND_RESOURCE_GROUPS> m_texBindings = {{}};
+	Array<U32, MAX_BOUND_RESOURCE_GROUPS> m_uniBindings = {{}};
+	Array<U32, MAX_BOUND_RESOURCE_GROUPS> m_storageBindings = {{}};
+	Array<U32, MAX_BOUND_RESOURCE_GROUPS> m_imageBindings = {{}};
 };
 /// @}
 

+ 18 - 17
src/anki/renderer/Is.cpp

@@ -172,7 +172,12 @@ Error Is::initInternal(const ConfigSet& config)
 		m_rcGroup = getGrManager().newInstance<ResourceGroup>(init);
 	}
 
-	getGrManager().finish();
+	TextureInitInfo texinit;
+	texinit.m_width = texinit.m_height = 4;
+	texinit.m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
+	texinit.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
+	m_dummyTex = getGrManager().newInstance<Texture>(texinit);
+
 	return ErrorCode::NONE;
 }
 
@@ -194,22 +199,23 @@ Error Is::binLights(RenderingContext& ctx)
 		ctx.m_is.m_dynBufferInfo.m_storageBuffers[LIGHT_IDS_LOCATION],
 		diffDecalTex));
 
+	ResourceGroupInitInfo rcinit;
 	if(diffDecalTex)
 	{
-		ResourceGroupInitInfo rcinit;
 		rcinit.m_textures[0].m_texture = diffDecalTex;
-
-		U64 hash = rcinit.computeHash();
-		if(hash != m_rcGroup1Hash)
-		{
-			m_rcGroup1Hash = hash;
-
-			m_rcGroup1 = getGrManager().newInstance<ResourceGroup>(rcinit);
-		}
 	}
 	else
 	{
-		m_rcGroup1.reset(nullptr);
+		// Bind something because validation layers will complain
+		rcinit.m_textures[0].m_texture = m_dummyTex;
+	}
+
+	U64 hash = rcinit.computeHash();
+	if(hash != m_rcGroup1Hash)
+	{
+		m_rcGroup1Hash = hash;
+
+		m_rcGroup1 = getGrManager().newInstance<ResourceGroup>(rcinit);
 	}
 
 	return ErrorCode::NONE;
@@ -223,12 +229,7 @@ void Is::run(RenderingContext& ctx)
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 	cmdb->bindPipeline(m_lightPpline);
 	cmdb->bindResourceGroup(m_rcGroup, 0, &ctx.m_is.m_dynBufferInfo);
-
-	if(m_rcGroup1)
-	{
-		cmdb->bindResourceGroup(m_rcGroup1, 1, nullptr);
-	}
-
+	cmdb->bindResourceGroup(m_rcGroup1, 1, nullptr);
 	cmdb->drawArrays(4, m_r->getTileCount());
 	cmdb->endRenderPass();
 }

+ 2 - 0
src/anki/renderer/Is.h

@@ -66,6 +66,8 @@ private:
 	U64 m_rcGroup1Hash = 0;
 	ResourceGroupPtr m_rcGroup1;
 
+	TexturePtr m_dummyTex;
+
 	// Light shaders
 	ShaderResourcePtr m_lightVert;
 	ShaderResourcePtr m_lightFrag;

+ 9 - 2
src/anki/scene/RenderComponent.h

@@ -223,6 +223,8 @@ public:
 
 	Bool tryGetPipeline(U64 hash, PipelinePtr& ppline)
 	{
+		LockGuard<SpinLock> lock(m_localPplineCacheMtx);
+
 		auto it = m_localPplineCache.find(hash);
 		if(it != m_localPplineCache.getEnd())
 		{
@@ -237,8 +239,12 @@ public:
 
 	void storePipeline(U64 hash, PipelinePtr ppline)
 	{
-		ANKI_ASSERT(m_localPplineCache.find(hash) == m_localPplineCache.getEnd());
-		m_localPplineCache.pushBack(getAllocator(), hash, ppline);
+		LockGuard<SpinLock> lock(m_localPplineCacheMtx);
+
+		if(m_localPplineCache.find(hash) == m_localPplineCache.getEnd())
+		{
+			m_localPplineCache.pushBack(getAllocator(), hash, ppline);
+		}
 	}
 
 private:
@@ -269,6 +275,7 @@ private:
 
 	/// This is an optimization, a local hash of pipelines.
 	HashMap<U64, PipelinePtr, Hasher, Compare> m_localPplineCache;
+	SpinLock m_localPplineCacheMtx;
 };
 /// @}