Browse Source

Vulkan: Some work on the state tracking

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
14348a7159

+ 1 - 1
src/anki/gr/Common.h

@@ -163,7 +163,7 @@ const U MAX_BINDINGS_PER_DESCRIPTOR_SET =
 	MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
 
 const U MAX_FRAMES_IN_FLIGHT = 3; ///< Triple buffering.
-const U MAX_BOUND_RESOURCE_GROUPS = 2; ///< Groups that can be bound at the same time.
+const U MAX_DESCRIPTOR_SETS = 2; ///< Groups that can be bound at the same time.
 const U MAX_DESCRIPTOR_SETS_PER_POOL = 1024; ///< An anoying limit for Vulkan.
 
 /// Compute max number of mipmaps for a 2D texture.

+ 1 - 0
src/anki/gr/Enums.h

@@ -238,6 +238,7 @@ enum class ShaderType : U8
 	COMPUTE,
 
 	COUNT,
+	FIRST = VERTEX,
 	FIRST_GRAPHICS = VERTEX,
 	LAST_GRAPHICS = FRAGMENT,
 };

+ 4 - 4
src/anki/gr/gl/StateTracker.h

@@ -427,7 +427,7 @@ public:
 		DepthStencilAspectBit m_aspect;
 	};
 
-	Array2d<TextureBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_TEXTURE_BINDINGS> m_textures;
+	Array2d<TextureBinding, MAX_DESCRIPTOR_SETS, MAX_TEXTURE_BINDINGS> m_textures;
 
 	Bool bindTexture(
 		U32 set, U32 binding, TexturePtr tex, DepthStencilAspectBit aspect, Bool& texChanged, Bool& samplerChanged)
@@ -471,7 +471,7 @@ public:
 		PtrSize m_range;
 	};
 
-	Array2d<ShaderBufferBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_UNIFORM_BUFFER_BINDINGS> m_ubos;
+	Array2d<ShaderBufferBinding, MAX_DESCRIPTOR_SETS, MAX_UNIFORM_BUFFER_BINDINGS> m_ubos;
 
 	Bool bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
 	{
@@ -482,7 +482,7 @@ public:
 		return true;
 	}
 
-	Array2d<ShaderBufferBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_STORAGE_BUFFER_BINDINGS> m_ssbos;
+	Array2d<ShaderBufferBinding, MAX_DESCRIPTOR_SETS, MAX_STORAGE_BUFFER_BINDINGS> m_ssbos;
 
 	Bool bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
 	{
@@ -500,7 +500,7 @@ public:
 		U8 m_level;
 	};
 
-	Array2d<ImageBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_IMAGE_BINDINGS> m_images;
+	Array2d<ImageBinding, MAX_DESCRIPTOR_SETS, MAX_IMAGE_BINDINGS> m_images;
 
 	Bool bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
 	{

+ 3 - 3
src/anki/gr/vulkan/BufferImpl.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/DescriptorObject.h>
 #include <anki/gr/vulkan/GpuMemoryManager.h>
 
 namespace anki
@@ -15,11 +15,11 @@ namespace anki
 /// @{
 
 /// Buffer implementation
-class BufferImpl : public VulkanObject
+class BufferImpl : public DescriptorObject
 {
 public:
 	BufferImpl(GrManager* manager)
-		: VulkanObject(manager)
+		: DescriptorObject(manager)
 	{
 	}
 

+ 1 - 1
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -227,7 +227,7 @@ private:
 		VkPipelineBindPoint m_bindPoint;
 		VkDescriptorSet m_dset;
 	};
-	Array<DeferredDsetBinding, MAX_BOUND_RESOURCE_GROUPS> m_deferredDsetBindings;
+	Array<DeferredDsetBinding, MAX_DESCRIPTOR_SETS> m_deferredDsetBindings;
 	U8 m_deferredDsetBindingMask = 0;
 	VkPipelineLayout m_crntPplineLayout = VK_NULL_HANDLE;
 

+ 1 - 1
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -588,7 +588,7 @@ inline void CommandBufferImpl::writeOcclusionQueryResultToBuffer(
 
 inline void CommandBufferImpl::flushDsetBindings()
 {
-	for(U i = 0; i < MAX_BOUND_RESOURCE_GROUPS; ++i)
+	for(U i = 0; i < MAX_DESCRIPTOR_SETS; ++i)
 	{
 		if(m_deferredDsetBindingMask & (1 << i))
 		{

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

@@ -19,6 +19,10 @@ public:
 	Mutex m_mtx;
 };
 
+DescriptorSetFactory::~DescriptorSetFactory()
+{
+}
+
 void DescriptorSetFactory::init(const GrAllocator<U8>& alloc, VkDevice dev)
 {
 	m_alloc = alloc;

+ 10 - 0
src/anki/gr/vulkan/DescriptorSet.h

@@ -43,6 +43,11 @@ public:
 		return m_handle;
 	}
 
+	Bool isCreated() const
+	{
+		return m_handle != VK_NULL_HANDLE;
+	}
+
 private:
 	VkDescriptorSetLayout m_handle = VK_NULL_HANDLE;
 	U32 m_cacheEntryIdx = MAX_U32;
@@ -124,6 +129,11 @@ public:
 
 	void init(const GrAllocator<U8>& alloc, VkDevice dev);
 
+	void destroy()
+	{
+		ANKI_ASSERT(!"TODO");
+	}
+
 	void newDescriptorSetLayout(const DescriptorSetLayoutInitInfo& init, DescriptorSetLayout& layout);
 
 	void newDescriptorSet(const DescriptorSetState& init, DescriptorSet& set);

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

@@ -84,6 +84,8 @@ GrManagerImpl::~GrManagerImpl()
 	{
 		vkDestroyInstance(m_instance, nullptr);
 	}
+
+	m_descrFactory.destroy();
 }
 
 GrAllocator<U8> GrManagerImpl::getAllocator() const
@@ -206,6 +208,7 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	}
 
 	m_descrGen.init(getAllocator());
+	m_descrFactory.init(getAllocator(), m_device);
 
 	return ErrorCode::NONE;
 }

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

@@ -11,6 +11,7 @@
 #include <anki/gr/vulkan/Fence.h>
 #include <anki/gr/vulkan/QueryExtra.h>
 #include <anki/gr/vulkan/DescriptorObject.h>
+#include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/gr/vulkan/CommandBufferExtra.h>
 #include <anki/util/HashMap.h>
 
@@ -169,6 +170,11 @@ public:
 		return m_descrGen;
 	}
 
+	DescriptorSetFactory& getDescriptorSetFactory()
+	{
+		return m_descrFactory;
+	}
+
 private:
 	GrManager* m_manager = nullptr;
 
@@ -260,6 +266,7 @@ private:
 	/// @}
 
 	DescriptorObjectTombstoneGenerator m_descrGen;
+	DescriptorSetFactory m_descrFactory;
 
 	QueryAllocator m_queryAlloc;
 

+ 11 - 0
src/anki/gr/vulkan/Pipeline.cpp

@@ -0,0 +1,11 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/Pipeline.h>
+
+namespace anki
+{
+
+} // end namespace anki

+ 364 - 0
src/anki/gr/vulkan/Pipeline.h

@@ -0,0 +1,364 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/DescriptorSet.h>
+#include <anki/gr/ShaderProgram.h>
+#include <anki/gr/vulkan/ShaderProgramImpl.h>
+#include <anki/util/HashMap.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+class VertexBufferBinding
+{
+public:
+	PtrSize m_stride = MAX_PTR_SIZE; ///< Vertex stride.
+	VertexStepRate m_stepRate = VertexStepRate::VERTEX;
+
+	Bool operator==(const VertexBufferBinding& b) const
+	{
+		return m_stride == b.m_stride && m_stepRate == b.m_stepRate;
+	}
+
+	Bool operator!=(const VertexBufferBinding& b) const
+	{
+		return !(*this == b);
+	}
+};
+
+class VertexAttributeBinding
+{
+public:
+	PixelFormat m_format;
+	PtrSize m_offset = 0;
+	U8 m_binding = 0;
+
+	Bool operator==(const VertexAttributeBinding& b) const
+	{
+		return m_format == b.m_format && m_offset == b.m_offset && m_binding == b.m_binding;
+	}
+
+	Bool operator!=(const VertexAttributeBinding& b) const
+	{
+		return !(*this == b);
+	}
+};
+
+class VertexStateInfo
+{
+public:
+	U8 m_bindingCount = 0;
+	Array<VertexBufferBinding, MAX_VERTEX_ATTRIBUTES> m_bindings;
+	U8 m_attributeCount = 0;
+	Array<VertexAttributeBinding, MAX_VERTEX_ATTRIBUTES> m_attributes;
+};
+
+class InputAssemblerStateInfo
+{
+public:
+	PrimitiveTopology m_topology = PrimitiveTopology::TRIANGLES;
+	Bool8 m_primitiveRestartEnabled = false;
+};
+
+class TessellationStateInfo
+{
+public:
+	U32 m_patchControlPointCount = 3;
+};
+
+class ViewportStateInfo
+{
+public:
+	Bool8 m_scissorEnabled = false;
+};
+
+class RasterizerStateInfo
+{
+public:
+	FillMode m_fillMode = FillMode::SOLID;
+	FaceSelectionBit m_cullMode = FaceSelectionBit::BACK;
+	Bool8 m_depthBiasEnabled = false;
+};
+
+class DepthStateInfo
+{
+public:
+	Bool8 m_depthWriteEnabled = true;
+	CompareOperation m_depthCompareFunction = CompareOperation::LESS;
+};
+
+class StencilStateInfo
+{
+public:
+	class S
+	{
+	public:
+		StencilOperation m_stencilFailOperation = StencilOperation::KEEP;
+		StencilOperation m_stencilPassDepthFailOperation = StencilOperation::KEEP;
+		StencilOperation m_stencilPassDepthPassOperation = StencilOperation::KEEP;
+		CompareOperation m_compareFunction = CompareOperation::ALWAYS;
+	} m_face[2];
+};
+
+class ColorAttachmentStateInfo
+{
+public:
+	BlendFactor m_srcBlendMethod = BlendFactor::ONE;
+	BlendFactor m_dstBlendMethod = BlendFactor::ZERO;
+	BlendOperation m_blendFunction = BlendOperation::ADD;
+	ColorBit m_channelWriteMask = ColorBit::ALL;
+};
+
+class ColorStateInfo
+{
+public:
+	Bool8 m_alphaToCoverageEnabled = false;
+	U8 m_attachmentCount = 0;
+	Array<ColorAttachmentStateInfo, MAX_COLOR_ATTACHMENTS> m_attachments;
+};
+
+class PipelineInfoState
+{
+public:
+	PipelineInfoState()
+	{
+		// Do a special construction. The state will be hashed and the padding may contain garbage. With this trick
+		// zero the padding
+		memset(this, 0, sizeof(*this));
+
+#define ANKI_CONSTRUCT_AND_ZERO_PADDING(memb_) new(&memb_) decltype(memb_)()
+
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_vertex);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_inputAssembler);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_tessellation);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_viewport);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_rasterizer);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_depth);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_stencil);
+		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_color);
+
+#undef ANKI_CONSTRUCT_AND_ZERO_PADDING
+	}
+
+	VertexStateInfo m_vertex;
+	InputAssemblerStateInfo m_inputAssembler;
+	TessellationStateInfo m_tessellation;
+	ViewportStateInfo m_viewport;
+	RasterizerStateInfo m_rasterizer;
+	DepthStateInfo m_depth;
+	StencilStateInfo m_stencil;
+	ColorStateInfo m_color;
+};
+
+/// Track changes in the static state.
+class PipelineStateTracker
+{
+public:
+	void bindVertexBuffer(U32 binding, PtrSize stride)
+	{
+		VertexBufferBinding b;
+		b.m_stride = stride;
+		b.m_stepRate = VertexStepRate::VERTEX;
+		if(m_state.m_vertex.m_bindings[binding] != b)
+		{
+			m_state.m_vertex.m_bindings[binding].m_stride = stride;
+			m_dirty.m_vertBindings.set(binding);
+		}
+	}
+
+	void setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset)
+	{
+		VertexAttributeBinding b;
+		b.m_binding = buffBinding;
+		b.m_format = fmt;
+		b.m_offset = relativeOffset;
+		if(m_state.m_vertex.m_attributes[location] != b)
+		{
+			m_state.m_vertex.m_attributes[location] = b;
+			m_dirty.m_attribs.set(location);
+		}
+	}
+
+	void setPrimitiveRestart(Bool enable)
+	{
+		if(m_state.m_inputAssembler.m_primitiveRestartEnabled != enable)
+		{
+			m_state.m_inputAssembler.m_primitiveRestartEnabled = enable;
+			m_dirty.m_ia = true;
+		}
+	}
+
+	void setFillMode(FillMode mode)
+	{
+		if(m_state.m_rasterizer.m_fillMode != mode)
+		{
+			m_state.m_rasterizer.m_fillMode = mode;
+			m_dirty.m_raster = true;
+		}
+	}
+
+	void setCullMode(FaceSelectionBit mode)
+	{
+		if(m_state.m_rasterizer.m_cullMode != mode)
+		{
+			m_state.m_rasterizer.m_cullMode = mode;
+			m_dirty.m_raster = true;
+		}
+	}
+
+	void setPolygonOffset(F32 factor, F32 units)
+	{
+		const Bool enable = factor != 0.0 && units != 0.0;
+		if(m_state.m_rasterizer.m_depthBiasEnabled != enable)
+		{
+			m_state.m_rasterizer.m_depthBiasEnabled = enable;
+			m_dirty.m_raster = true;
+		}
+	}
+
+	void setStencilOperations(FaceSelectionBit face,
+		StencilOperation stencilFail,
+		StencilOperation stencilPassDepthFail,
+		StencilOperation stencilPassDepthPass)
+	{
+		if(!!(face & FaceSelectionBit::FRONT)
+			&& (m_state.m_stencil.m_face[0].m_stencilFailOperation != stencilFail
+				   || m_state.m_stencil.m_face[0].m_stencilPassDepthFailOperation != stencilPassDepthFail
+				   || m_state.m_stencil.m_face[0].m_stencilPassDepthPassOperation != stencilPassDepthPass))
+		{
+			m_state.m_stencil.m_face[0].m_stencilFailOperation = stencilFail;
+			m_state.m_stencil.m_face[0].m_stencilPassDepthFailOperation = stencilPassDepthFail;
+			m_state.m_stencil.m_face[0].m_stencilPassDepthPassOperation = stencilPassDepthPass;
+			m_dirty.m_stencil = true;
+		}
+
+		if(!!(face & FaceSelectionBit::BACK)
+			&& (m_state.m_stencil.m_face[1].m_stencilFailOperation != stencilFail
+				   || m_state.m_stencil.m_face[1].m_stencilPassDepthFailOperation != stencilPassDepthFail
+				   || m_state.m_stencil.m_face[1].m_stencilPassDepthPassOperation != stencilPassDepthPass))
+		{
+			m_state.m_stencil.m_face[1].m_stencilFailOperation = stencilFail;
+			m_state.m_stencil.m_face[1].m_stencilPassDepthFailOperation = stencilPassDepthFail;
+			m_state.m_stencil.m_face[1].m_stencilPassDepthPassOperation = stencilPassDepthPass;
+			m_dirty.m_stencil = true;
+		}
+	}
+
+	void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
+	{
+		if(!!(face & FaceSelectionBit::FRONT) && m_state.m_stencil.m_face[0].m_compareFunction != comp)
+		{
+			m_state.m_stencil.m_face[0].m_compareFunction = comp;
+			m_dirty.m_stencil = true;
+		}
+
+		if(!!(face & FaceSelectionBit::BACK) && m_state.m_stencil.m_face[1].m_compareFunction != comp)
+		{
+			m_state.m_stencil.m_face[1].m_compareFunction = comp;
+			m_dirty.m_stencil = true;
+		}
+	}
+
+	void setStencilCompareMask(FaceSelectionBit face, U32 mask);
+
+	void setStencilWriteMask(FaceSelectionBit face, U32 mask);
+
+	void setStencilReference(FaceSelectionBit face, U32 ref);
+
+	void setDepthWrite(Bool enable);
+
+	void setDepthCompareOperation(CompareOperation op);
+
+	void setAlphaToCoverage(Bool enable);
+
+	void setColorChannelWriteMask(U32 attachment, ColorBit mask);
+
+	void setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA);
+
+	void bindShaderProgram(const ShaderProgramPtr& prog)
+	{
+		const ShaderProgramImpl& impl = *prog->m_impl;
+		m_shaderColorAttachmentWritemask = impl.m_colorAttachmentWritemask;
+		m_shaderAttributeMask = impl.m_attributeMask;
+	}
+
+	void beginRenderPass(FramebufferPtr fb);
+
+private:
+	PipelineInfoState m_state;
+
+	class Hashes
+	{
+	public:
+		U64 m_vertex = 0;
+		BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_shaderAttributeMaskWhenHashed = {false};
+
+		U64 m_ia = 0;
+		U64 m_raster = 0;
+	} m_hashes;
+
+	class DirtyBits
+	{
+	public:
+		BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_attribs = {true};
+		BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_vertBindings = {true};
+
+		Bool8 m_ia = true;
+		Bool8 m_raster = true;
+		Bool8 m_stencil = true;
+	} m_dirty;
+
+	// Vertex
+	BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_shaderAttributeMask = {false};
+
+	// Color & blend
+	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_shaderColorAttachmentWritemask = {false};
+};
+
+/// Small wrapper on top of the pipeline.
+class Pipeline
+{
+	friend class PipelineFactory;
+
+public:
+	VkPipeline getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+private:
+	VkPipeline m_handle = VK_NULL_HANDLE;
+};
+
+/// Given some state it creates/hashes pipelines.
+class PipelineFactory
+{
+public:
+	void init(GrAllocator<U8> alloc, VkDevice dev, VkPipelineCache pplineCache);
+
+	void destroy();
+
+	/// @note Not thread-safe.
+	void newPipeline(PipelineStateTracker& state, Pipeline& ppline);
+
+private:
+	class Pipeline;
+	class Hasher;
+
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+	VkPipelineCache m_pplineCache = VK_NULL_HANDLE;
+
+	HashMap<U64, Pipeline, Hasher> m_pplines;
+};
+/// @}
+
+} // end namespace anki

+ 82 - 0
src/anki/gr/vulkan/PipelineLayout.cpp

@@ -0,0 +1,82 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/PipelineLayout.h>
+
+namespace anki
+{
+
+class PipelineLayoutFactory::Layout
+{
+public:
+	U64 m_hash;
+	VkPipelineLayout m_handle;
+};
+
+class PipelineLayoutFactory::Hasher
+{
+public:
+	U64 operator()(U64 k) const
+	{
+		return k;
+	}
+};
+
+void PipelineLayoutFactory::destroy()
+{
+	for(auto it : m_layouts)
+	{
+		Layout* l = it;
+		vkDestroyPipelineLayout(m_dev, l->m_handle, nullptr);
+		m_alloc.deleteInstance(l);
+	}
+
+	m_layouts.destroy(m_alloc);
+}
+
+void PipelineLayoutFactory::newPipelineLayout(const WeakArray<DescriptorSetLayout>& dsetLayouts, PipelineLayout& layout)
+{
+	U64 hash = 1;
+	Array<VkDescriptorSetLayout, MAX_DESCRIPTOR_SETS> vkDsetLayouts;
+	U dsetLayoutCount = 0;
+	for(const DescriptorSetLayout& dl : dsetLayouts)
+	{
+		vkDsetLayouts[dsetLayoutCount++] = dl.getHandle();
+	}
+
+	if(dsetLayoutCount > 0)
+	{
+		hash = computeHash(&vkDsetLayouts[0], sizeof(vkDsetLayouts[0]) * dsetLayoutCount);
+	}
+
+	LockGuard<Mutex> lock(m_layoutsMtx);
+
+	auto it = m_layouts.find(hash);
+	if(it != m_layouts.getEnd())
+	{
+		// Found it
+
+		layout.m_handle = (*it)->m_handle;
+	}
+	else
+	{
+		// Not found, create new
+
+		Layout* lay = m_alloc.newInstance<Layout>();
+		lay->m_hash = hash;
+
+		VkPipelineLayoutCreateInfo ci = {
+			VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+		};
+		ci.pSetLayouts = &vkDsetLayouts[0];
+		ci.setLayoutCount = dsetLayoutCount;
+
+		ANKI_VK_CHECKF(vkCreatePipelineLayout(m_dev, &ci, nullptr, &lay->m_handle));
+
+		layout.m_handle = lay->m_handle;
+	}
+}
+
+} // end namespace anki

+ 61 - 0
src/anki/gr/vulkan/PipelineLayout.h

@@ -0,0 +1,61 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/DescriptorSet.h>
+#include <anki/util/HashMap.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+class PipelineLayout
+{
+	friend class PipelineLayoutFactory;
+
+public:
+	VkPipelineLayout getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+private:
+	VkPipelineLayout m_handle = VK_NULL_HANDLE;
+};
+
+/// Creator of pipeline layouts.
+class PipelineLayoutFactory
+{
+public:
+	PipelineLayoutFactory() = default;
+	~PipelineLayoutFactory() = default;
+
+	void init(GrAllocator<U8> alloc, VkDevice dev)
+	{
+		m_alloc = alloc;
+		m_dev = dev;
+	}
+
+	void destroy();
+
+	void newPipelineLayout(const WeakArray<DescriptorSetLayout>& dsetLayouts, PipelineLayout& layout);
+
+private:
+	class Layout;
+	class Hasher;
+
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+
+	HashMap<U64, Layout*, Hasher> m_layouts;
+	Mutex m_layoutsMtx;
+};
+/// @}
+
+} // end namespace anki

+ 17 - 4
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -315,10 +315,10 @@ void ShaderImpl::doReflection(const std::vector<unsigned int>& spirv)
 	spirv_cross::Compiler spvc(spirv);
 	spirv_cross::ShaderResources rsrc = spvc.get_shader_resources(spvc.get_active_interface_variables());
 
-	Array<U, MAX_BOUND_RESOURCE_GROUPS> counts = {{
+	Array<U, MAX_DESCRIPTOR_SETS> counts = {{
 		0,
 	}};
-	Array2d<DescriptorBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_BINDINGS_PER_DESCRIPTOR_SET> descriptors;
+	Array2d<DescriptorBinding, MAX_DESCRIPTOR_SETS, MAX_BINDINGS_PER_DESCRIPTOR_SET> descriptors;
 
 	auto func = [&](const std::vector<spirv_cross::Resource>& resources, VkDescriptorType type) -> void {
 		for(const spirv_cross::Resource& r : resources)
@@ -336,6 +336,7 @@ void ShaderImpl::doReflection(const std::vector<unsigned int>& spirv)
 			DescriptorBinding& descriptor = descriptors[set][counts[set]++];
 			descriptor.m_binding = binding;
 			descriptor.m_type = type;
+			descriptor.m_stageMask = static_cast<ShaderTypeBit>(1 << m_shaderType);
 		}
 	};
 
@@ -344,7 +345,7 @@ void ShaderImpl::doReflection(const std::vector<unsigned int>& spirv)
 	func(rsrc.storage_buffers, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
 	func(rsrc.storage_images, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
 
-	for(U set = 0; set < MAX_BOUND_RESOURCE_GROUPS; ++set)
+	for(U set = 0; set < MAX_DESCRIPTOR_SETS; ++set)
 	{
 		if(counts[set])
 		{
@@ -361,7 +362,19 @@ void ShaderImpl::doReflection(const std::vector<unsigned int>& spirv)
 			const U32 id = r.id;
 			const U32 location = spvc.get_decoration(id, spv::Decoration::DecorationLocation);
 
-			m_colorAttachmentWritemask |= 1 << location;
+			m_colorAttachmentWritemask.set(location);
+		}
+	}
+
+	// Attribs
+	if(m_shaderType == ShaderType::VERTEX)
+	{
+		for(const spirv_cross::Resource& r : rsrc.stage_inputs)
+		{
+			const U32 id = r.id;
+			const U32 location = spvc.get_decoration(id, spv::Decoration::DecorationLocation);
+
+			m_attributeMask.set(location);
 		}
 	}
 }

+ 4 - 2
src/anki/gr/vulkan/ShaderImpl.h

@@ -8,6 +8,7 @@
 #include <anki/gr/vulkan/VulkanObject.h>
 #include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/util/String.h>
+#include <anki/util/BitSet.h>
 #include <vector>
 
 namespace anki
@@ -23,8 +24,9 @@ public:
 	VkShaderModule m_handle = VK_NULL_HANDLE;
 	ShaderType m_shaderType = ShaderType::COUNT;
 
-	Array<DynamicArray<DescriptorBinding>, MAX_BOUND_RESOURCE_GROUPS> m_bindings;
-	U8 m_colorAttachmentWritemask = 0;
+	Array<DynamicArray<DescriptorBinding>, MAX_DESCRIPTOR_SETS> m_bindings;
+	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentWritemask = {false};
+	BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_attributeMask = {false};
 
 	ShaderImpl(GrManager* manager)
 		: VulkanObject(manager)

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

@@ -6,6 +6,7 @@
 #include <anki/gr/vulkan/ShaderProgramImpl.h>
 #include <anki/gr/Shader.h>
 #include <anki/gr/vulkan/ShaderImpl.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
 
 namespace anki
 {
@@ -21,8 +22,95 @@ ShaderProgramImpl::~ShaderProgramImpl()
 
 Error ShaderProgramImpl::init(const Array<ShaderPtr, U(ShaderType::COUNT)>& shaders)
 {
+	ShaderTypeBit shaderMask = ShaderTypeBit::NONE;
 	m_shaders = shaders;
 
+	// Merge bindings
+	Array2d<DescriptorBinding, MAX_BINDINGS_PER_DESCRIPTOR_SET, MAX_DESCRIPTOR_SETS> bindings;
+	Array<U, MAX_DESCRIPTOR_SETS> counts = {};
+	for(U set = 0; set < MAX_DESCRIPTOR_SETS; ++set)
+	{
+		for(ShaderType stype = ShaderType::FIRST; stype < ShaderType::COUNT; ++stype)
+		{
+			if(!shaders[stype].isCreated())
+			{
+				continue;
+			}
+
+			shaderMask |= static_cast<ShaderTypeBit>(1 << stype);
+
+			const ShaderImpl& simpl = *shaders[stype]->m_impl;
+
+			for(U i = 0; i < simpl.m_bindings[set].getSize(); ++i)
+			{
+				Bool bindingFound = false;
+				for(U j = 0; j < counts[set]; ++j)
+				{
+					if(bindings[set][j].m_binding == simpl.m_bindings[set][i].m_binding)
+					{
+						// Found the binding
+
+						ANKI_ASSERT(bindings[set][j].m_type == simpl.m_bindings[set][i].m_type);
+
+						bindings[set][j].m_stageMask |= simpl.m_bindings[set][i].m_stageMask;
+
+						bindingFound = true;
+						break;
+					}
+				}
+
+				if(!bindingFound)
+				{
+					// New binding
+
+					bindings[set][counts[set]++] = simpl.m_bindings[set][i];
+				}
+			}
+		}
+	}
+
+	// Create the descriptor set layouts
+	for(U set = 0; set < MAX_DESCRIPTOR_SETS; ++set)
+	{
+		if(counts[set] > 0)
+		{
+			DescriptorSetLayoutInitInfo inf;
+			inf.m_bindings = WeakArray<DescriptorBinding>(&bindings[set][0], counts[set]);
+
+			getGrManagerImpl().getDescriptorSetFactory().newDescriptorSetLayout(inf, m_descriptorSetLayouts[set]);
+		}
+	}
+
+	ANKI_ASSERT(!"TODO Pipeline layout");
+
+	// Get some masks
+	const Bool graphicsProg = !!(shaderMask & ShaderTypeBit::VERTEX);
+	if(graphicsProg)
+	{
+		m_attributeMask = shaders[ShaderType::VERTEX]->m_impl->m_attributeMask;
+		m_colorAttachmentWritemask = shaders[ShaderType::FRAGMENT]->m_impl->m_colorAttachmentWritemask;
+	}
+
+	// Cache some values
+	if(graphicsProg)
+	{
+		for(ShaderType stype = ShaderType::VERTEX; stype <= ShaderType::FRAGMENT; ++stype)
+		{
+			if(!shaders[stype].isCreated())
+			{
+				continue;
+			}
+
+			VkPipelineShaderStageCreateInfo& inf = m_shaderCreateInfos[m_shaderCreateInfoCount++];
+			inf = {
+				VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+			};
+			inf.stage = convertShaderTypeBit(static_cast<ShaderTypeBit>(1 << stype));
+			inf.pName = "main";
+			inf.module = shaders[stype]->m_impl->m_handle;
+		}
+	}
+
 	return ErrorCode::NONE;
 }
 

+ 9 - 1
src/anki/gr/vulkan/ShaderProgramImpl.h

@@ -6,7 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
-#include <anki/gr/vulkan/DescriptorSet.h>
+#include <anki/gr/vulkan/PipelineLayout.h>
 
 namespace anki
 {
@@ -20,6 +20,14 @@ class ShaderProgramImpl : public VulkanObject
 public:
 	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders;
 
+	Array<VkPipelineShaderStageCreateInfo, U(ShaderType::COUNT) - 1> m_shaderCreateInfos;
+	U32 m_shaderCreateInfoCount = 0;
+	PipelineLayout m_pplineLayout;
+
+	Array<DescriptorSetLayout, MAX_DESCRIPTOR_SETS> m_descriptorSetLayouts;
+	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentWritemask = {false};
+	BitSet<MAX_VERTEX_ATTRIBUTES, U8> m_attributeMask = {false};
+
 	ShaderProgramImpl(GrManager* manager);
 	~ShaderProgramImpl();
 

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

@@ -16,7 +16,7 @@ namespace anki
 {
 
 TextureImpl::TextureImpl(GrManager* manager)
-	: VulkanObject(manager)
+	: DescriptorObject(manager)
 {
 }
 

+ 2 - 2
src/anki/gr/vulkan/TextureImpl.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/DescriptorObject.h>
 #include <anki/gr/vulkan/GpuMemoryManager.h>
 #include <anki/gr/common/Misc.h>
 #include <anki/util/HashMap.h>
@@ -26,7 +26,7 @@ enum class TextureImplWorkaround : U8
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(TextureImplWorkaround, inline)
 
 /// Texture container.
-class TextureImpl : public VulkanObject
+class TextureImpl : public DescriptorObject
 {
 public:
 	SamplerPtr m_sampler;