diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 817c3f5840895158f621ae0c022df07c3c97978a..e20a34446d5d23ad0fa79946d2fdfa74a8177eba 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -233,16 +233,26 @@ namespace vkcv
 		[[nodiscard]]
 		vk::Format getImageFormat(const ImageHandle& image);
 
+		/** TODO:
+		 * @param bindings
+		 * @return
+		 */
+		[[nodiscard]]
+		DescriptorSetLayoutHandle createDescriptorSetLayout(const std::unordered_map<uint32_t, DescriptorBinding> &bindingsMap);
+		DescriptorSetLayout getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const;
+
+		// TODO: existsDescriptorSetLayout function that checks and returns fitting layout upon existence.
+
         /** TODO:
          *   @param setDescriptions
          *   @return
          */
         [[nodiscard]]
-        DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings);
+        DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &layoutHandle);
 		void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes);
-
 		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
 
+
 		/**
 		 * @brief start recording command buffers and increment frame index
 		*/
diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
index f2a089fd624c02c57db4a65c4a101c4acff371b1..2fac0a24e24260fee9b5936243535110cb228dbe 100644
--- a/include/vkcv/DescriptorConfig.hpp
+++ b/include/vkcv/DescriptorConfig.hpp
@@ -1,17 +1,13 @@
 #pragma once
 
+#include <unordered_map>
+
 #include "vkcv/Handles.hpp"
 #include "vkcv/ShaderStage.hpp"
+#include "vkcv/Logger.hpp"
 
 namespace vkcv
 {
-    struct DescriptorSet
-    {
-        vk::DescriptorSet       vulkanHandle;
-        vk::DescriptorSetLayout layout;
-        size_t                  poolIndex;
-    };
-
     /*
     * All the types of descriptors (resources) that can be retrieved by the shaders
     */
@@ -25,7 +21,34 @@ namespace vkcv
         UNIFORM_BUFFER_DYNAMIC,
         STORAGE_BUFFER_DYNAMIC
     };    
-    
+
+    /**
+    * Converts the descriptor types from VulkanCV (vkcv) to native Vulkan (vk).
+    * @param[in] vkcv DescriptorType
+    * @return vk DescriptorType
+    */
+    constexpr vk::DescriptorType getVkDescriptorType(DescriptorType type) noexcept {
+        switch (type)
+        {
+            case DescriptorType::UNIFORM_BUFFER:
+                return vk::DescriptorType::eUniformBuffer;
+            case DescriptorType::UNIFORM_BUFFER_DYNAMIC:
+                return vk::DescriptorType::eUniformBufferDynamic;
+            case DescriptorType::STORAGE_BUFFER:
+                return vk::DescriptorType::eStorageBuffer;
+            case DescriptorType::STORAGE_BUFFER_DYNAMIC:
+                return vk::DescriptorType::eStorageBufferDynamic;
+            case DescriptorType::SAMPLER:
+                return vk::DescriptorType::eSampler;
+            case DescriptorType::IMAGE_SAMPLED:
+                return vk::DescriptorType::eSampledImage;
+            case DescriptorType::IMAGE_STORAGE:
+                return vk::DescriptorType::eStorageImage;
+            default:
+                return vk::DescriptorType::eMutableVALVE;
+        }
+    }
+
     /*
     * One binding for a descriptor set
     * @param[in] a unique binding ID
@@ -42,9 +65,26 @@ namespace vkcv
             ShaderStages shaderStages
         ) noexcept;
         
-        uint32_t bindingID;
-        DescriptorType descriptorType;
-        uint32_t descriptorCount;
-        ShaderStages shaderStages;
+        uint32_t        bindingID;
+        DescriptorType  descriptorType;
+        uint32_t        descriptorCount;
+        ShaderStages    shaderStages;
+
+        bool operator ==(const DescriptorBinding &other) const;
+    };
+    
+    typedef std::unordered_map<uint32_t, DescriptorBinding> DescriptorBindings;
+
+    struct DescriptorSetLayout
+    {
+        vk::DescriptorSetLayout vulkanHandle;
+        DescriptorBindings descriptorBindings;
+    };
+
+    struct DescriptorSet
+    {
+        vk::DescriptorSet           vulkanHandle;
+        DescriptorSetLayoutHandle   setLayoutHandle;
+        size_t                      poolIndex;
     };
 }
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index ea05bd212dd9314957e4771070bedb3963b1b2dc..1a88a85ee379c8e1999d13a0ec0f4c91a6abf420 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -84,6 +84,12 @@ namespace vkcv
 	private:
 		using Handle::Handle;
 	};
+
+	class DescriptorSetLayoutHandle : public Handle {
+	    friend class DescriptorManager;
+	private:
+	    using Handle::Handle;
+	};
 	
 	class SamplerHandle : public Handle {
 		friend class SamplerManager;
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index c7d67b19148b3c9ec19ce1b539f9661797d1b38f..d39f0da436089830fc90e5178b056d0be2cae910 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -51,7 +51,13 @@ namespace vkcv {
         const std::vector<VertexAttachment> &getVertexAttachments() const;
 		size_t getPushConstantSize() const;
 
-        const std::vector<std::vector<DescriptorBinding>>& getReflectedDescriptors() const;
+		/**
+		 * Returns the reflected descriptor sets/layouts/bindings in a map of maps.
+		 * First uint32_t serves as descriptor SET id.
+		 * Second uint32_t serves as the descriptor set's BINDING id.
+		 * @return
+		 */
+		const std::unordered_map<uint32_t, std::unordered_map<uint32_t, DescriptorBinding>>& getReflectedDescriptors() const;
 
 	private:
 	    /**
@@ -65,7 +71,7 @@ namespace vkcv {
 
         // contains all vertex input attachments used in the vertex buffer
         std::vector<VertexAttachment> m_VertexAttachments;
-        std::vector<std::vector<DescriptorBinding>> m_DescriptorSets;
+        std::unordered_map<uint32_t, std::unordered_map<uint32_t, DescriptorBinding>> m_DescriptorSets;
 		size_t m_pushConstantSize = 0;
 	};
 }
diff --git a/modules/material/include/vkcv/material/Material.hpp b/modules/material/include/vkcv/material/Material.hpp
index 9b54d99828eca3738fed9ff1c4078ca9f87eaefa..41ee8aecce98072981be80ccf9803c43c6464746 100644
--- a/modules/material/include/vkcv/material/Material.hpp
+++ b/modules/material/include/vkcv/material/Material.hpp
@@ -23,6 +23,7 @@ namespace vkcv::material {
 		
 		MaterialType m_Type;
 		DescriptorSetHandle m_DescriptorSet;
+		DescriptorSetLayoutHandle m_DescriptorSetLayout;
 		std::vector<Texture> m_Textures;
 		
 	public:
@@ -40,12 +41,15 @@ namespace vkcv::material {
 		
 		[[nodiscard]]
 		const DescriptorSetHandle& getDescriptorSet() const;
+
+		[[nodiscard]]
+		const DescriptorSetLayoutHandle& getDescriptorSetLayout() const;
 		
 		explicit operator bool() const;
 		
 		bool operator!() const;
 		
-		static const std::vector<DescriptorBinding>& getDescriptorBindings(MaterialType type);
+		static const std::unordered_map<uint32_t ,DescriptorBinding>& getDescriptorBindings(MaterialType type);
 		
 		static Material createPBR(Core &core,
 								  const ImageHandle &colorImg,
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index 409db0b9dd83f91b6a2afbb48d74933ab1a483fc..43a72e4cb6c936457f3723e32dc2715d9788ce08 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -14,6 +14,10 @@ namespace vkcv::material {
 	const DescriptorSetHandle & Material::getDescriptorSet() const {
 		return m_DescriptorSet;
 	}
+
+	const DescriptorSetLayoutHandle & Material::getDescriptorSetLayout() const {
+        return m_DescriptorSetLayout;
+	}
 	
 	Material::operator bool() const {
 		return (m_Type != MaterialType::UNKNOWN);
@@ -23,24 +27,25 @@ namespace vkcv::material {
 		return (m_Type == MaterialType::UNKNOWN);
 	}
 	
-	const std::vector<DescriptorBinding>& Material::getDescriptorBindings(MaterialType type)
+	const DescriptorBindings& Material::getDescriptorBindings(MaterialType type)
 	{
-		static std::vector<DescriptorBinding> pbr_bindings;
-		static std::vector<DescriptorBinding> default_bindings;
+		static DescriptorBindings pbr_bindings = {};
+		static DescriptorBindings default_bindings = {};
 		
 		switch (type) {
 			case MaterialType::PBR_MATERIAL:
-				if (pbr_bindings.empty()) {
-					pbr_bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-					pbr_bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+				if (pbr_bindings.empty())
+				{
+					pbr_bindings.insert(std::make_pair(0, DescriptorBinding(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(1, DescriptorBinding(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(2, DescriptorBinding(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(3, DescriptorBinding(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(4, DescriptorBinding(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(5, DescriptorBinding(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(6, DescriptorBinding(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(7, DescriptorBinding(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(8, DescriptorBinding(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(9, DescriptorBinding(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
 				}
 				
 				return pbr_bindings;
@@ -163,7 +168,8 @@ namespace vkcv::material {
 		material.m_Type = MaterialType::PBR_MATERIAL;
 		
 		const auto& bindings = getDescriptorBindings(material.m_Type);
-		material.m_DescriptorSet = core.createDescriptorSet(bindings);;
+		material.m_DescriptorSetLayout = core.createDescriptorSetLayout(bindings);
+		material.m_DescriptorSet = core.createDescriptorSet(material.m_DescriptorSetLayout);;
 		
 		material.m_Textures.reserve(bindings.size());
 		material.m_Textures.push_back({ images[0], samplers[0], std::vector<float>(baseColorFactor, baseColorFactor+4) });
diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
index 2a1338b85e3ee60a33215157aaaa15817f2db97f..241544c79545384d14a2599fb718cd3992455705 100644
--- a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
+++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
@@ -32,8 +32,11 @@ namespace vkcv::upscaling {
 	private:
 		PipelineHandle m_easuPipeline;
 		PipelineHandle m_rcasPipeline;
-		
+
+		DescriptorSetLayoutHandle m_easuDescriptorSetLayout;
 		DescriptorSetHandle m_easuDescriptorSet;
+
+		DescriptorSetLayoutHandle m_rcasDescriptorSetLayout;
 		DescriptorSetHandle m_rcasDescriptorSet;
 		
 		Buffer<FSRConstants> m_easuConstants;
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index b11051273ba6f9e56d3a537931f9d33fff657e43..88436c148d711638554f82a0cfb7be2dc81c998e 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -65,25 +65,44 @@ namespace vkcv::upscaling {
 		}
 	}
 	
-	static std::vector<DescriptorBinding> getDescriptorBindings() {
-		return std::vector<DescriptorBinding>({
-			DescriptorBinding(
-					0, DescriptorType::UNIFORM_BUFFER_DYNAMIC,
-					1, ShaderStage::COMPUTE
-			),
-			DescriptorBinding(
-					1, DescriptorType::IMAGE_SAMPLED,
-					1, ShaderStage::COMPUTE
-			),
-			DescriptorBinding(
-					2, DescriptorType::IMAGE_STORAGE,
-					1, ShaderStage::COMPUTE
-			),
-			DescriptorBinding(
-					3, DescriptorType::SAMPLER,
-					1, ShaderStage::COMPUTE
-			)
-		});
+	static DescriptorBindings getDescriptorBindings() {
+		DescriptorBindings descriptorBindings = {};
+
+	    auto binding_0 = DescriptorBinding(
+	            0,
+	            DescriptorType::UNIFORM_BUFFER_DYNAMIC,
+	            1,
+	            ShaderStage::COMPUTE
+		);
+
+	    auto binding_1 = DescriptorBinding(
+	            1,
+	            DescriptorType::IMAGE_SAMPLED,
+	            1,
+	            ShaderStage::COMPUTE
+		);
+
+	    auto binding_2 = DescriptorBinding(
+	            2,
+	            DescriptorType::IMAGE_STORAGE,
+	            1,
+	            ShaderStage::COMPUTE
+		);
+
+	    auto binding_3 = DescriptorBinding(
+	            3,
+	            DescriptorType::SAMPLER,
+	            1,
+	            ShaderStage::COMPUTE
+		);
+
+	    descriptorBindings.insert(std::make_pair(0, binding_0));
+	    descriptorBindings.insert(std::make_pair(1, binding_1));
+	    descriptorBindings.insert(std::make_pair(2, binding_2));
+	    descriptorBindings.insert(std::make_pair(3, binding_3));
+
+	    return descriptorBindings;
+
 	}
 	
 	template<typename T>
@@ -155,8 +174,13 @@ namespace vkcv::upscaling {
 	Upscaling(core),
 	m_easuPipeline(),
 	m_rcasPipeline(),
-	m_easuDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
-	m_rcasDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
+
+	m_easuDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())),
+	m_easuDescriptorSet(m_core.createDescriptorSet(m_easuDescriptorSetLayout)),
+
+	m_rcasDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())),
+	m_rcasDescriptorSet(m_core.createDescriptorSet(m_rcasDescriptorSetLayout)),
+
 	m_easuConstants(m_core.createBuffer<FSRConstants>(
 			BufferType::UNIFORM,1,
 			BufferMemoryType::HOST_VISIBLE
@@ -207,7 +231,7 @@ namespace vkcv::upscaling {
 			});
 			
 			m_easuPipeline = m_core.createComputePipeline(program, {
-				m_core.getDescriptorSet(m_easuDescriptorSet).layout
+				m_core.getDescriptorSetLayout(m_easuDescriptorSetLayout).vulkanHandle
 			});
 			
 			DescriptorWrites writes;
@@ -228,7 +252,7 @@ namespace vkcv::upscaling {
 			});
 			
 			m_rcasPipeline = m_core.createComputePipeline(program, {
-				m_core.getDescriptorSet(m_rcasDescriptorSet).layout
+			    m_core.getDescriptorSetLayout(m_rcasDescriptorSetLayout).vulkanHandle
 			});
 			
 			DescriptorWrites writes;
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 2ad76e8f78c5870f6b582a1970caac306026166f..0649bdd12ae50d34c4c9b37119a022ac9b59bafa 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -105,9 +105,16 @@ int main(int argc, const char** argv) {
 	
 	const vkcv::VertexLayout firstMeshLayout (bindings);
 
-	uint32_t setID = 0;
-	std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[setID] };
-	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings);
+
+	// since we only use one descriptor set (namely, desc set 0), directly address it
+	// recreate copies of the bindings and the handles (to check whether they are properly reused instead of actually recreated)
+	std::unordered_map<uint32_t, vkcv::DescriptorBinding> set0Bindings = firstMeshProgram.getReflectedDescriptors().at(0);
+    auto set0BindingsExplicitCopy = set0Bindings;
+
+	vkcv::DescriptorSetLayoutHandle setLayoutHandle = core.createDescriptorSetLayout(set0Bindings);
+	vkcv::DescriptorSetLayoutHandle setLayoutHandleCopy = core.createDescriptorSetLayout(set0BindingsExplicitCopy);
+
+	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(setLayoutHandle);
 
 	const vkcv::PipelineConfig firstMeshPipelineConfig {
         firstMeshProgram,
@@ -115,7 +122,7 @@ int main(int argc, const char** argv) {
         UINT32_MAX,
         firstMeshPass,
         {firstMeshLayout},
-		{ core.getDescriptorSet(descriptorSet).layout },
+		{ core.getDescriptorSetLayout(setLayoutHandle).vulkanHandle },
 		true
 	};
 	vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig);
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 21f8deacdb3f81fcd29c5f14cbe74485f36d13cd..cc026e4302e9970be58fc698e9148ea3c6602806 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -84,15 +84,15 @@ int main(int argc, const char** argv) {
 	
 	const auto& material0 = scene.getMaterial(0);
 
-	const vkcv::PipelineConfig scenePipelineDefsinition{
+	const vkcv::PipelineConfig scenePipelineDefinition{
 		sceneShaderProgram,
 		UINT32_MAX,
 		UINT32_MAX,
 		scenePass,
 		{sceneLayout},
-		{ core.getDescriptorSet(material0.getDescriptorSet()).layout },
+		{ core.getDescriptorSetLayout(material0.getDescriptorSetLayout()).vulkanHandle },
 		true };
-	vkcv::PipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefsinition);
+	vkcv::PipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefinition);
 	
 	if (!scenePipeline) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp
index 267ac6bd8ef44dcee9b3a05d7204e8d33fbe86a7..659ceffd55570db64cf26ed1597d8256c3b8efa0 100644
--- a/projects/indirect_dispatch/src/AppSetup.cpp
+++ b/projects/indirect_dispatch/src/AppSetup.cpp
@@ -122,12 +122,14 @@ bool loadGraphicPass(
 
 	const auto descriptorBindings = shaderProgram.getReflectedDescriptors();
 	const bool hasDescriptor = descriptorBindings.size() > 0;
+	std::vector<vk::DescriptorSetLayout> descriptorSetLayouts = {};
 	if (hasDescriptor)
-		outPassHandles->descriptorSet = core.createDescriptorSet(descriptorBindings[0]);
+	{
+	    outPassHandles->descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings.at(0));
+	    outPassHandles->descriptorSet = core.createDescriptorSet(outPassHandles->descriptorSetLayout);
+	    descriptorSetLayouts.push_back(core.getDescriptorSetLayout(outPassHandles->descriptorSetLayout).vulkanHandle);
+	}
 
-	std::vector<vk::DescriptorSetLayout> descriptorLayouts;
-	if (hasDescriptor)
-		descriptorLayouts.push_back(core.getDescriptorSet(outPassHandles->descriptorSet).layout);
 
 	vkcv::PipelineConfig pipelineConfig{
 		shaderProgram,
@@ -135,7 +137,7 @@ bool loadGraphicPass(
 		UINT32_MAX,
 		outPassHandles->renderPass,
 		{ vertexLayout },
-		descriptorLayouts,
+		descriptorSetLayouts,
 		true };
 	pipelineConfig.m_depthTest  = depthTest;
 	outPassHandles->pipeline    = core.createGraphicsPipeline(pipelineConfig);
@@ -254,11 +256,11 @@ bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, Comput
 		return false;
 	}
 
-	outComputePass->descriptorSet = core.createDescriptorSet(shaderProgram.getReflectedDescriptors()[0]);
-
+	outComputePass->descriptorSetLayout = core.createDescriptorSetLayout(shaderProgram.getReflectedDescriptors().at(0));
+	outComputePass->descriptorSet = core.createDescriptorSet(outComputePass->descriptorSetLayout);
 	outComputePass->pipeline = core.createComputePipeline(
 		shaderProgram,
-		{ core.getDescriptorSet(outComputePass->descriptorSet).layout });
+		{ core.getDescriptorSetLayout(outComputePass->descriptorSetLayout).vulkanHandle });
 
 	if (!outComputePass->pipeline) {
 		vkcv_log(vkcv::LogLevel::ERROR, "Compute shader pipeline creation failed");
diff --git a/projects/indirect_dispatch/src/AppSetup.hpp b/projects/indirect_dispatch/src/AppSetup.hpp
index 3125bc516b553de715d6e51bbda259e3e16f758f..162a6c6126f39adb85da7e419ef4e5b9793dc5ad 100644
--- a/projects/indirect_dispatch/src/AppSetup.hpp
+++ b/projects/indirect_dispatch/src/AppSetup.hpp
@@ -8,14 +8,16 @@ struct AppRenderTargets {
 };
 
 struct GraphicPassHandles {
-	vkcv::PipelineHandle        pipeline;
-	vkcv::PassHandle            renderPass;
-	vkcv::DescriptorSetHandle   descriptorSet;
+	vkcv::PipelineHandle                pipeline;
+	vkcv::PassHandle                    renderPass;
+	vkcv::DescriptorSetLayoutHandle     descriptorSetLayout;
+	vkcv::DescriptorSetHandle           descriptorSet;
 };
 
 struct ComputePassHandles {
-	vkcv::PipelineHandle        pipeline;
-	vkcv::DescriptorSetHandle   descriptorSet;
+	vkcv::PipelineHandle                pipeline;
+	vkcv::DescriptorSetLayoutHandle     descriptorSetLayout;
+	vkcv::DescriptorSetHandle           descriptorSet;
 };
 
 struct MeshResources {
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 15aaf527619b4fb06d89bccc78cffe843cc723b7..600de22a8f94b37ee8fdf74256acc51aea2e07a9 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -205,7 +205,8 @@ int main(int argc, const char** argv) {
     }
     const vkcv::VertexLayout bunnyLayout (bindings);
 
-	vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(bunnyShaderProgram.getReflectedDescriptors()[0]);
+    vkcv::DescriptorSetLayoutHandle vertexShaderDescriptorSetLayout = core.createDescriptorSetLayout(bunnyShaderProgram.getReflectedDescriptors().at(0));
+    vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(vertexShaderDescriptorSetLayout);
 
 	const vkcv::PipelineConfig bunnyPipelineDefinition {
 			bunnyShaderProgram,
@@ -213,7 +214,7 @@ int main(int argc, const char** argv) {
 			(uint32_t)windowHeight,
 			renderPass,
 			{ bunnyLayout },
-			{ core.getDescriptorSet(vertexShaderDescriptorSet).layout },
+			{ core.getDescriptorSetLayout(vertexShaderDescriptorSetLayout).vulkanHandle },
 			false
 	};
 
@@ -253,8 +254,8 @@ int main(int argc, const char** argv) {
 		meshShaderProgram.addShader(shaderStage, path);
 	});
 
-	uint32_t setID = 0;
-	vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet( meshShaderProgram.getReflectedDescriptors()[setID]);
+	vkcv::DescriptorSetLayoutHandle meshShaderDescriptorSetLayout = core.createDescriptorSetLayout(meshShaderProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet(meshShaderDescriptorSetLayout);
 	const vkcv::VertexLayout meshShaderLayout(bindings);
 
 	const vkcv::PipelineConfig meshShaderPipelineDefinition{
@@ -263,7 +264,7 @@ int main(int argc, const char** argv) {
 		(uint32_t)windowHeight,
 		renderPass,
 		{meshShaderLayout},
-		{core.getDescriptorSet(meshShaderDescriptorSet).layout},
+		{core.getDescriptorSetLayout(meshShaderDescriptorSetLayout).vulkanHandle},
 		false
 	};
 
diff --git a/projects/particle_simulation/src/BloomAndFlares.cpp b/projects/particle_simulation/src/BloomAndFlares.cpp
index 5961aae664a39dfb9bd597ffa7648c9b67999af4..68a553574eddd668f7c0360b91d4da448c5b8cf8 100644
--- a/projects/particle_simulation/src/BloomAndFlares.cpp
+++ b/projects/particle_simulation/src/BloomAndFlares.cpp
@@ -30,11 +30,14 @@ BloomAndFlares::BloomAndFlares(
                      });
     for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++)
     {
+        m_DownsampleDescSetLayouts.push_back(
+                p_Core->createDescriptorSetLayout(dsProg.getReflectedDescriptors().at(0)));
+
 		m_DownsampleDescSets.push_back(
-                p_Core->createDescriptorSet(dsProg.getReflectedDescriptors()[0]));
+		        p_Core->createDescriptorSet(m_DownsampleDescSetLayouts.back()));
     }
     m_DownsamplePipe = p_Core->createComputePipeline(
-            dsProg, { p_Core->getDescriptorSet(m_DownsampleDescSets[0]).layout });
+            dsProg, { p_Core->getDescriptorSetLayout(m_DownsampleDescSetLayouts[0]).vulkanHandle });
 
     // UPSAMPLE
     vkcv::ShaderProgram usProg;
@@ -46,11 +49,13 @@ BloomAndFlares::BloomAndFlares(
                      });
     for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++)
     {
+        m_UpsampleDescSetLayouts.push_back(
+                p_Core->createDescriptorSetLayout(usProg.getReflectedDescriptors().at(0)));
         m_UpsampleDescSets.push_back(
-                p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0]));
+                p_Core->createDescriptorSet(m_UpsampleDescSetLayouts.back()));
     }
     m_UpsamplePipe = p_Core->createComputePipeline(
-            usProg, { p_Core->getDescriptorSet(m_UpsampleDescSets[0]).layout });
+            usProg, { p_Core->getDescriptorSetLayout(m_UpsampleDescSetLayouts[0]).vulkanHandle });
 
     // LENS FEATURES
     vkcv::ShaderProgram lensProg;
@@ -60,9 +65,10 @@ BloomAndFlares::BloomAndFlares(
                      {
                          lensProg.addShader(shaderStage, path);
                      });
-    m_LensFlareDescSet = p_Core->createDescriptorSet(lensProg.getReflectedDescriptors()[0]);
+    m_LensFlareDescSetLayout = p_Core->createDescriptorSetLayout(lensProg.getReflectedDescriptors().at(0));
+    m_LensFlareDescSet = p_Core->createDescriptorSet(m_LensFlareDescSetLayout);
     m_LensFlarePipe = p_Core->createComputePipeline(
-            lensProg, { p_Core->getDescriptorSet(m_LensFlareDescSet).layout });
+            lensProg, { p_Core->getDescriptorSetLayout(m_LensFlareDescSetLayout).vulkanHandle });
 
     // COMPOSITE
     vkcv::ShaderProgram compProg;
@@ -72,9 +78,10 @@ BloomAndFlares::BloomAndFlares(
                      {
                          compProg.addShader(shaderStage, path);
                      });
-    m_CompositeDescSet = p_Core->createDescriptorSet(compProg.getReflectedDescriptors()[0]);
+    m_CompositeDescSetLayout = p_Core->createDescriptorSetLayout(compProg.getReflectedDescriptors().at(0));
+    m_CompositeDescSet = p_Core->createDescriptorSet(m_CompositeDescSetLayout);
     m_CompositePipe = p_Core->createComputePipeline(
-            compProg, { p_Core->getDescriptorSet(m_CompositeDescSet).layout });
+            compProg, { p_Core->getDescriptorSetLayout(m_CompositeDescSetLayout).vulkanHandle });
 }
 
 void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream,
diff --git a/projects/particle_simulation/src/BloomAndFlares.hpp b/projects/particle_simulation/src/BloomAndFlares.hpp
index 756b1ca154ea5232df04eb09a88bb743c5bd28aa..21f69f43511fabb920fd85c9e07f3fd0e5a355f3 100644
--- a/projects/particle_simulation/src/BloomAndFlares.hpp
+++ b/projects/particle_simulation/src/BloomAndFlares.hpp
@@ -25,17 +25,21 @@ private:
     vkcv::Image m_LensFeatures;
 
 
-    vkcv::PipelineHandle                     m_DownsamplePipe;
-    std::vector<vkcv::DescriptorSetHandle>   m_DownsampleDescSets; // per mip desc set
+    vkcv::PipelineHandle                            m_DownsamplePipe;
+    std::vector<vkcv::DescriptorSetLayoutHandle>    m_DownsampleDescSetLayouts;
+    std::vector<vkcv::DescriptorSetHandle>          m_DownsampleDescSets; // per mip desc set
 
-    vkcv::PipelineHandle                     m_UpsamplePipe;
-    std::vector<vkcv::DescriptorSetHandle>   m_UpsampleDescSets;   // per mip desc set
+    vkcv::PipelineHandle                            m_UpsamplePipe;
+    std::vector<vkcv::DescriptorSetLayoutHandle>    m_UpsampleDescSetLayouts;
+    std::vector<vkcv::DescriptorSetHandle>          m_UpsampleDescSets;   // per mip desc set
 
-    vkcv::PipelineHandle                     m_LensFlarePipe;
-    vkcv::DescriptorSetHandle                m_LensFlareDescSet;
+    vkcv::PipelineHandle                            m_LensFlarePipe;
+    vkcv::DescriptorSetLayoutHandle                 m_LensFlareDescSetLayout;
+    vkcv::DescriptorSetHandle                       m_LensFlareDescSet;
 
-    vkcv::PipelineHandle                     m_CompositePipe;
-    vkcv::DescriptorSetHandle                m_CompositeDescSet;
+    vkcv::PipelineHandle                            m_CompositePipe;
+    vkcv::DescriptorSetLayoutHandle                 m_CompositeDescSetLayout;
+    vkcv::DescriptorSetHandle                       m_CompositeDescSet;
 
     void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment);
     void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream);
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index 4de016aefd345a4231ea2dae0818b0839b163729..4027ce7b2d6aabd72653efd35d76db971b90d0ae 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -82,7 +82,8 @@ int main(int argc, const char **argv) {
         computeShaderProgram.addShader(shaderStage, path);
     });
 
-    vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]);
+    vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout = core.createDescriptorSetLayout(computeShaderProgram.getReflectedDescriptors().at(0));
+    vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeDescriptorSetLayout);
 
     const std::vector<vkcv::VertexAttachment> computeVertexAttachments = computeShaderProgram.getVertexAttachments();
 
@@ -100,8 +101,9 @@ int main(int argc, const char **argv) {
         particleShaderProgram.addShader(shaderStage, path);
     });
 
-    vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(
-            particleShaderProgram.getReflectedDescriptors()[0]);
+    vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(
+            particleShaderProgram.getReflectedDescriptors().at(0));
+    vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 
     vkcv::Buffer<glm::vec3> vertexBuffer = core.createBuffer<glm::vec3>(
             vkcv::BufferType::VERTEX,
@@ -125,7 +127,7 @@ int main(int argc, const char **argv) {
             UINT32_MAX,
             particlePass,
             {particleLayout},
-            {core.getDescriptorSet(descriptorSet).layout},
+            {core.getDescriptorSetLayout(descriptorSetLayout).vulkanHandle},
             true};
     particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
 
@@ -137,7 +139,7 @@ int main(int argc, const char **argv) {
 
     vkcv::PipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition);
 
-    vkcv::PipelineHandle computePipeline = core.createComputePipeline(computeShaderProgram, {core.getDescriptorSet(computeDescriptorSet).layout} );
+    vkcv::PipelineHandle computePipeline = core.createComputePipeline(computeShaderProgram, {core.getDescriptorSetLayout(computeDescriptorSetLayout).vulkanHandle} );
 
     vkcv::Buffer<glm::vec4> color = core.createBuffer<glm::vec4>(
             vkcv::BufferType::UNIFORM,
@@ -227,10 +229,11 @@ int main(int argc, const char **argv) {
         tonemappingShader.addShader(shaderStage, path);
     });
 
-    vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingShader.getReflectedDescriptors()[0]);
+    vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0));
+    vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout);
     vkcv::PipelineHandle tonemappingPipe = core.createComputePipeline(
         tonemappingShader, 
-        { core.getDescriptorSet(tonemappingDescriptor).layout });
+        { core.getDescriptorSetLayout(tonemappingDescriptorLayout).vulkanHandle });
 
     std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f);
     std::default_random_engine rdmEngine;
diff --git a/projects/voxelization/src/BloomAndFlares.cpp b/projects/voxelization/src/BloomAndFlares.cpp
index 6cb02e9035daf7abebc047d26137d0ba973bb4f1..1837ffe0f16500e18e2c013c5c74ac0e10ef6221 100644
--- a/projects/voxelization/src/BloomAndFlares.cpp
+++ b/projects/voxelization/src/BloomAndFlares.cpp
@@ -44,11 +44,13 @@ BloomAndFlares::BloomAndFlares(
                      });
     for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++)
     {
-		m_DownsampleDescSets.push_back(
-                p_Core->createDescriptorSet(dsProg.getReflectedDescriptors()[0]));
+        m_DownsampleDescSetLayouts.push_back(
+                p_Core->createDescriptorSetLayout(dsProg.getReflectedDescriptors().at(0))
+                );
+        m_DownsampleDescSets.push_back(p_Core->createDescriptorSet(m_DownsampleDescSetLayouts.back()));
     }
     m_DownsamplePipe = p_Core->createComputePipeline(
-            dsProg, { p_Core->getDescriptorSet(m_DownsampleDescSets[0]).layout });
+            dsProg, { p_Core->getDescriptorSetLayout(m_DownsampleDescSetLayouts[0]).vulkanHandle });
 
     // UPSAMPLE
     vkcv::ShaderProgram usProg;
@@ -60,16 +62,20 @@ BloomAndFlares::BloomAndFlares(
                      });
     for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++)
     {
+        m_UpsampleDescSetLayouts.push_back(
+                p_Core->createDescriptorSetLayout(usProg.getReflectedDescriptors().at(0)));
         m_UpsampleDescSets.push_back(
-                p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0]));
+                p_Core->createDescriptorSet(m_UpsampleDescSetLayouts.back()));
     }
     for (uint32_t mipLevel = 0; mipLevel < m_LensFeatures.getMipCount(); mipLevel++) {
+        m_UpsampleLensFlareDescSetLayouts.push_back(
+                p_Core->createDescriptorSetLayout(usProg.getReflectedDescriptors().at(0)));
         m_UpsampleLensFlareDescSets.push_back(
-            p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0]));
+                p_Core->createDescriptorSet(m_UpsampleLensFlareDescSetLayouts.back()));
     }
 
     m_UpsamplePipe = p_Core->createComputePipeline(
-            usProg, { p_Core->getDescriptorSet(m_UpsampleDescSets[0]).layout });
+            usProg, { p_Core->getDescriptorSetLayout(m_UpsampleDescSetLayouts[0]).vulkanHandle });
 
     // LENS FEATURES
     vkcv::ShaderProgram lensProg;
@@ -79,9 +85,10 @@ BloomAndFlares::BloomAndFlares(
                      {
                          lensProg.addShader(shaderStage, path);
                      });
-    m_LensFlareDescSet = p_Core->createDescriptorSet(lensProg.getReflectedDescriptors()[0]);
+    m_LensFlareDescSetLayout = p_Core->createDescriptorSetLayout(lensProg.getReflectedDescriptors().at(0));
+    m_LensFlareDescSet = p_Core->createDescriptorSet(m_LensFlareDescSetLayout);
     m_LensFlarePipe = p_Core->createComputePipeline(
-            lensProg, { p_Core->getDescriptorSet(m_LensFlareDescSet).layout });
+            lensProg, { p_Core->getDescriptorSetLayout(m_LensFlareDescSetLayout).vulkanHandle });
 
     // COMPOSITE
     vkcv::ShaderProgram compProg;
@@ -91,9 +98,11 @@ BloomAndFlares::BloomAndFlares(
                      {
                          compProg.addShader(shaderStage, path);
                      });
-    m_CompositeDescSet = p_Core->createDescriptorSet(compProg.getReflectedDescriptors()[0]);
+
+    m_CompositeDescSetLayout = p_Core->createDescriptorSetLayout(compProg.getReflectedDescriptors().at(0));
+    m_CompositeDescSet = p_Core->createDescriptorSet(m_CompositeDescSetLayout);
     m_CompositePipe = p_Core->createComputePipeline(
-            compProg, { p_Core->getDescriptorSet(m_CompositeDescSet).layout });
+            compProg, { p_Core->getDescriptorSetLayout(m_CompositeDescSetLayout).vulkanHandle });
 
     // radial LUT
     const auto texture = vkcv::asset::loadTexture("resources/RadialLUT.png");
diff --git a/projects/voxelization/src/BloomAndFlares.hpp b/projects/voxelization/src/BloomAndFlares.hpp
index 2b410e5b256c5820d908372d2e23fd495853274a..133a82cbe85a2fd4fd9965874a36faa5369013b9 100644
--- a/projects/voxelization/src/BloomAndFlares.hpp
+++ b/projects/voxelization/src/BloomAndFlares.hpp
@@ -29,18 +29,24 @@ private:
     vkcv::Image m_radialLut;
     vkcv::Image m_lensDirt;
 
-    vkcv::PipelineHandle                     m_DownsamplePipe;
-    std::vector<vkcv::DescriptorSetHandle>   m_DownsampleDescSets; // per mip desc set
-    std::vector<vkcv::DescriptorSetHandle>   m_UpsampleLensFlareDescSets; // per mip desc set
+    vkcv::PipelineHandle                            m_DownsamplePipe;
+    std::vector<vkcv::DescriptorSetLayoutHandle>    m_DownsampleDescSetLayouts;
+    std::vector<vkcv::DescriptorSetHandle>          m_DownsampleDescSets; // per mip desc set
 
-    vkcv::PipelineHandle                     m_UpsamplePipe;
-    std::vector<vkcv::DescriptorSetHandle>   m_UpsampleDescSets;   // per mip desc set
+    std::vector<vkcv::DescriptorSetLayoutHandle>    m_UpsampleLensFlareDescSetLayouts;
+    std::vector<vkcv::DescriptorSetHandle>          m_UpsampleLensFlareDescSets; // per mip desc set
 
-    vkcv::PipelineHandle                     m_LensFlarePipe;
-    vkcv::DescriptorSetHandle                m_LensFlareDescSet;
+    vkcv::PipelineHandle                            m_UpsamplePipe;
+    std::vector<vkcv::DescriptorSetLayoutHandle>    m_UpsampleDescSetLayouts;
+    std::vector<vkcv::DescriptorSetHandle>          m_UpsampleDescSets;   // per mip desc set
 
-    vkcv::PipelineHandle                     m_CompositePipe;
-    vkcv::DescriptorSetHandle                m_CompositeDescSet;
+    vkcv::PipelineHandle                            m_LensFlarePipe;
+    vkcv::DescriptorSetLayoutHandle                 m_LensFlareDescSetLayout;
+    vkcv::DescriptorSetHandle                       m_LensFlareDescSet;
+
+    vkcv::PipelineHandle                            m_CompositePipe;
+    vkcv::DescriptorSetLayoutHandle                 m_CompositeDescSetLayout;
+    vkcv::DescriptorSetHandle                       m_CompositeDescSet;
 
     void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment);
     void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream);
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index 32dd5457541f8f09f4d2711ea831e3c78de2303a..109da9a3b11023e9ba3734b09030d1b910050173 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -189,8 +189,9 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert
 
 	// depth to moments
 	vkcv::ShaderProgram depthToMomentsShader    = loadDepthToMomentsShader();
-	m_depthToMomentsDescriptorSet               = corePtr->createDescriptorSet(depthToMomentsShader.getReflectedDescriptors()[0]);
-	m_depthToMomentsPipe                        = corePtr->createComputePipeline(depthToMomentsShader, { corePtr->getDescriptorSet(m_depthToMomentsDescriptorSet).layout });
+	m_depthToMomentsDescriptorSetLayout         = corePtr->createDescriptorSetLayout(depthToMomentsShader.getReflectedDescriptors().at(0));
+	m_depthToMomentsDescriptorSet               = corePtr->createDescriptorSet(m_depthToMomentsDescriptorSetLayout);
+	m_depthToMomentsPipe                        = corePtr->createComputePipeline(depthToMomentsShader, { corePtr->getDescriptorSetLayout(m_depthToMomentsDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites depthToMomentDescriptorWrites;
 	depthToMomentDescriptorWrites.sampledImageWrites    = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapDepth.getHandle()) };
@@ -199,9 +200,10 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert
 	corePtr->writeDescriptorSet(m_depthToMomentsDescriptorSet, depthToMomentDescriptorWrites);
 
 	// shadow blur X
-	vkcv::ShaderProgram shadowBlurXShader    = loadShadowBlurXShader();
-	m_shadowBlurXDescriptorSet              = corePtr->createDescriptorSet(shadowBlurXShader.getReflectedDescriptors()[0]);
-	m_shadowBlurXPipe                       = corePtr->createComputePipeline(shadowBlurXShader, { corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).layout });
+	vkcv::ShaderProgram shadowBlurXShader   = loadShadowBlurXShader();
+	m_shadowBlurXDescriptorSetLayout        = corePtr->createDescriptorSetLayout(shadowBlurXShader.getReflectedDescriptors().at(0));
+	m_shadowBlurXDescriptorSet              = corePtr->createDescriptorSet(m_shadowBlurXDescriptorSetLayout);
+	m_shadowBlurXPipe                       = corePtr->createComputePipeline(shadowBlurXShader, { corePtr->getDescriptorSetLayout(m_shadowBlurXDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites shadowBlurXDescriptorWrites;
 	shadowBlurXDescriptorWrites.sampledImageWrites   = { vkcv::SampledImageDescriptorWrite(0, m_shadowMap.getHandle()) };
@@ -210,9 +212,10 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert
 	corePtr->writeDescriptorSet(m_shadowBlurXDescriptorSet, shadowBlurXDescriptorWrites);
 
 	// shadow blur Y
-	vkcv::ShaderProgram shadowBlurYShader = loadShadowBlurYShader();
-	m_shadowBlurYDescriptorSet = corePtr->createDescriptorSet(shadowBlurYShader.getReflectedDescriptors()[0]);
-	m_shadowBlurYPipe = corePtr->createComputePipeline(shadowBlurYShader, { corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).layout });
+	vkcv::ShaderProgram shadowBlurYShader   = loadShadowBlurYShader();
+	m_shadowBlurYDescriptorSetLayout        = corePtr->createDescriptorSetLayout(shadowBlurYShader.getReflectedDescriptors().at(0));
+	m_shadowBlurYDescriptorSet              = corePtr->createDescriptorSet(m_shadowBlurYDescriptorSetLayout);
+	m_shadowBlurYPipe                       = corePtr->createComputePipeline(shadowBlurYShader, { corePtr->getDescriptorSetLayout(m_shadowBlurYDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites shadowBlurYDescriptorWrites;
 	shadowBlurYDescriptorWrites.sampledImageWrites  = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapIntermediate.getHandle()) };
diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp
index 8066d5bdc90a66c0823be4dc23cf6a12729e32c7..3bd72581357510ae4ae9a581daeb23b60bb80e1c 100644
--- a/projects/voxelization/src/ShadowMapping.hpp
+++ b/projects/voxelization/src/ShadowMapping.hpp
@@ -36,21 +36,24 @@ public:
 private:
 	vkcv::Core* m_corePtr;
 
-	vkcv::Image                 m_shadowMap;
-	vkcv::Image                 m_shadowMapIntermediate;
-	vkcv::Image                 m_shadowMapDepth;
-	vkcv::SamplerHandle         m_shadowSampler;
-	vkcv::Buffer<LightInfo>     m_lightInfoBuffer;
-
-	vkcv::PassHandle            m_shadowMapPass;
-	vkcv::PipelineHandle        m_shadowMapPipe;
-
-	vkcv::PipelineHandle        m_depthToMomentsPipe;
-	vkcv::DescriptorSetHandle   m_depthToMomentsDescriptorSet;
-
-	vkcv::PipelineHandle        m_shadowBlurXPipe;
-	vkcv::DescriptorSetHandle   m_shadowBlurXDescriptorSet;
-
-	vkcv::PipelineHandle        m_shadowBlurYPipe;
-	vkcv::DescriptorSetHandle   m_shadowBlurYDescriptorSet;
+	vkcv::Image                         m_shadowMap;
+	vkcv::Image                         m_shadowMapIntermediate;
+	vkcv::Image                         m_shadowMapDepth;
+	vkcv::SamplerHandle                 m_shadowSampler;
+	vkcv::Buffer<LightInfo>             m_lightInfoBuffer;
+
+	vkcv::PassHandle                    m_shadowMapPass;
+	vkcv::PipelineHandle                m_shadowMapPipe;
+
+	vkcv::PipelineHandle                m_depthToMomentsPipe;
+	vkcv::DescriptorSetLayoutHandle     m_depthToMomentsDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_depthToMomentsDescriptorSet;
+
+	vkcv::PipelineHandle                m_shadowBlurXPipe;
+	vkcv::DescriptorSetLayoutHandle     m_shadowBlurXDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_shadowBlurXDescriptorSet;
+
+	vkcv::PipelineHandle                m_shadowBlurYPipe;
+	vkcv::DescriptorSetLayoutHandle     m_shadowBlurYDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_shadowBlurYDescriptorSet;
 };
\ No newline at end of file
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index f7e03709c6423ef0e3c43251afb28e887b9be61f..139851870a6dc3bb7a5aca9f926504cdfc571f40 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -98,12 +98,11 @@ Voxelization::Voxelization(
 		voxelizationDummyFormat) });
 	m_voxelizationPass = m_corePtr->createPass(voxelizationPassConfig);
 
-	std::vector<vkcv::DescriptorBinding> voxelizationDescriptorBindings = 
-	{ voxelizationShader.getReflectedDescriptors()[0] };
-	m_voxelizationDescriptorSet = m_corePtr->createDescriptorSet(voxelizationDescriptorBindings);
+	m_voxelizationDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelizationShader.getReflectedDescriptors().at(0));
+	m_voxelizationDescriptorSet = m_corePtr->createDescriptorSet(m_voxelizationDescriptorSetLayout);
 
-	vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet =
-		m_corePtr->createDescriptorSet({ voxelizationShader.getReflectedDescriptors()[1] });
+	vkcv::DescriptorSetLayoutHandle dummyPerMeshDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelizationShader.getReflectedDescriptors().at(1));
+	vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet = m_corePtr->createDescriptorSet(dummyPerMeshDescriptorSetLayout);
 
 	const vkcv::PipelineConfig voxelizationPipeConfig{
 		voxelizationShader,
@@ -112,8 +111,8 @@ Voxelization::Voxelization(
 		m_voxelizationPass,
 		dependencies.vertexLayout,
 		{ 
-			m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).layout,
-			m_corePtr->getDescriptorSet(dummyPerMeshDescriptorSet).layout},
+		    m_corePtr->getDescriptorSetLayout(m_voxelizationDescriptorSetLayout).vulkanHandle,
+		    m_corePtr->getDescriptorSetLayout(dummyPerMeshDescriptorSetLayout).vulkanHandle},
 		false,
 		true };
 	m_voxelizationPipe = m_corePtr->createGraphicsPipeline(voxelizationPipeConfig);
@@ -131,9 +130,8 @@ Voxelization::Voxelization(
 
 	vkcv::ShaderProgram voxelVisualisationShader = loadVoxelVisualisationShader();
 
-	const std::vector<vkcv::DescriptorBinding> voxelVisualisationDescriptorBindings = 
-		{ voxelVisualisationShader.getReflectedDescriptors()[0] };
-	m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(voxelVisualisationDescriptorBindings);
+	m_visualisationDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelVisualisationShader.getReflectedDescriptors().at(0));
+	m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(m_visualisationDescriptorSetLayout);
 
 	const vkcv::AttachmentDescription voxelVisualisationColorAttachments(
 		vkcv::AttachmentOperation::STORE,
@@ -158,7 +156,7 @@ Voxelization::Voxelization(
 		0,
 		m_visualisationPass,
 		{},
-		{ m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).layout },
+		{ m_corePtr->getDescriptorSetLayout(m_visualisationDescriptorSetLayout).vulkanHandle },
 		true,
 		false,
 		vkcv::PrimitiveTopology::PointList };	// points are extended to cubes in the geometry shader
@@ -174,10 +172,11 @@ Voxelization::Voxelization(
 
 	vkcv::ShaderProgram resetVoxelShader = loadVoxelResetShader();
 
-	m_voxelResetDescriptorSet = m_corePtr->createDescriptorSet(resetVoxelShader.getReflectedDescriptors()[0]);
+	m_voxelResetDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(resetVoxelShader.getReflectedDescriptors().at(0));
+	m_voxelResetDescriptorSet = m_corePtr->createDescriptorSet(m_voxelResetDescriptorSetLayout);
 	m_voxelResetPipe = m_corePtr->createComputePipeline(
 		resetVoxelShader,
-		{ m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).layout });
+		{ m_corePtr->getDescriptorSetLayout(m_voxelResetDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites resetVoxelWrites;
 	resetVoxelWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) };
@@ -186,10 +185,11 @@ Voxelization::Voxelization(
 	// buffer to image
 	vkcv::ShaderProgram bufferToImageShader = loadVoxelBufferToImageShader();
 
-	m_bufferToImageDescriptorSet = m_corePtr->createDescriptorSet(bufferToImageShader.getReflectedDescriptors()[0]);
+	m_bufferToImageDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(bufferToImageShader.getReflectedDescriptors().at(0));
+	m_bufferToImageDescriptorSet = m_corePtr->createDescriptorSet(m_bufferToImageDescriptorSetLayout);
 	m_bufferToImagePipe = m_corePtr->createComputePipeline(
 		bufferToImageShader,
-		{ m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).layout });
+		{ m_corePtr->getDescriptorSetLayout(m_bufferToImageDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites bufferToImageDescriptorWrites;
 	bufferToImageDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) };
@@ -199,10 +199,11 @@ Voxelization::Voxelization(
 	// secondary bounce
 	vkcv::ShaderProgram secondaryBounceShader = loadSecondaryBounceShader();
 
-	m_secondaryBounceDescriptorSet = m_corePtr->createDescriptorSet(secondaryBounceShader.getReflectedDescriptors()[0]);
+	m_secondaryBounceDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(secondaryBounceShader.getReflectedDescriptors().at(0));
+	m_secondaryBounceDescriptorSet = m_corePtr->createDescriptorSet(m_secondaryBounceDescriptorSetLayout);
 	m_secondaryBouncePipe = m_corePtr->createComputePipeline(
 		secondaryBounceShader,
-		{ m_corePtr->getDescriptorSet(m_secondaryBounceDescriptorSet).layout });
+		{ m_corePtr->getDescriptorSetLayout(m_secondaryBounceDescriptorSetLayout).vulkanHandle });
 
 	vkcv::DescriptorWrites secondaryBounceDescriptorWrites;
 	secondaryBounceDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) };
diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp
index 66c87acb3c13c0d950a28dc33e4084d728da5947..1ca25703361d11b0f3e43b8ce252a3cf21367b5f 100644
--- a/projects/voxelization/src/Voxelization.hpp
+++ b/projects/voxelization/src/Voxelization.hpp
@@ -53,24 +53,29 @@ private:
 	vkcv::Image                         m_voxelImage;
 	vkcv::Buffer<VoxelBufferContent>    m_voxelBuffer;
 
-	vkcv::Image                 m_dummyRenderTarget;
-	vkcv::PassHandle            m_voxelizationPass;
-	vkcv::PipelineHandle        m_voxelizationPipe;
-	vkcv::DescriptorSetHandle   m_voxelizationDescriptorSet;
+	vkcv::Image                         m_dummyRenderTarget;
+	vkcv::PassHandle                    m_voxelizationPass;
+	vkcv::PipelineHandle                m_voxelizationPipe;
+	vkcv::DescriptorSetLayoutHandle     m_voxelizationDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_voxelizationDescriptorSet;
 
-	vkcv::PipelineHandle        m_voxelResetPipe;
-	vkcv::DescriptorSetHandle   m_voxelResetDescriptorSet;
+	vkcv::PipelineHandle                m_voxelResetPipe;
+	vkcv::DescriptorSetLayoutHandle     m_voxelResetDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_voxelResetDescriptorSet;
 
-	vkcv::PipelineHandle        m_bufferToImagePipe;
-	vkcv::DescriptorSetHandle   m_bufferToImageDescriptorSet;
+	vkcv::PipelineHandle                m_bufferToImagePipe;
+	vkcv::DescriptorSetLayoutHandle     m_bufferToImageDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_bufferToImageDescriptorSet;
 
-	vkcv::PassHandle            m_visualisationPass;
-	vkcv::PipelineHandle        m_visualisationPipe;
+	vkcv::PassHandle                    m_visualisationPass;
+	vkcv::PipelineHandle                m_visualisationPipe;
 
-	vkcv::PipelineHandle        m_secondaryBouncePipe;
-	vkcv::DescriptorSetHandle   m_secondaryBounceDescriptorSet;
+	vkcv::PipelineHandle                m_secondaryBouncePipe;
+	vkcv::DescriptorSetLayoutHandle     m_secondaryBounceDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_secondaryBounceDescriptorSet;
 
-	vkcv::DescriptorSetHandle   m_visualisationDescriptorSet;
+	vkcv::DescriptorSetLayoutHandle     m_visualisationDescriptorSetLayout;
+	vkcv::DescriptorSetHandle           m_visualisationDescriptorSet;
 
 	struct VoxelizationInfo {
 		glm::vec3 offset;
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 3c23c7eabbbdc5d83d37bde79b43a2730b1f28b2..fe962ff3036c12734ab6d82e034499ab89312b37 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -5,9 +5,7 @@
 #include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
-#include <vkcv/Logger.hpp>
 #include "Voxelization.hpp"
-#include <glm/glm.hpp>
 #include "vkcv/gui/GUI.hpp"
 #include "ShadowMapping.hpp"
 #include "BloomAndFlares.hpp"
@@ -194,8 +192,8 @@ int main(int argc, const char** argv) {
 	}
 	const vkcv::VertexLayout vertexLayout (vertexBindings);
 
-	vkcv::DescriptorSetHandle forwardShadingDescriptorSet = 
-		core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] });
+	vkcv::DescriptorSetLayoutHandle forwardShadingDescriptorSetLayout = core.createDescriptorSetLayout(forwardProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle forwardShadingDescriptorSet = core.createDescriptorSet(forwardShadingDescriptorSetLayout);
 
 	// depth prepass config
 	vkcv::ShaderProgram depthPrepassShader;
@@ -232,6 +230,7 @@ int main(int argc, const char** argv) {
 		vkcv::SamplerAddressMode::REPEAT
 	);
 
+	std::vector<vkcv::DescriptorSetLayoutHandle> materialDescriptorSetLayouts;
 	std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets;
 	std::vector<vkcv::Image> sceneImages;
 
@@ -253,7 +252,8 @@ int main(int argc, const char** argv) {
 			specularIndex = 0;
 		}
 
-		materialDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1]));
+		materialDescriptorSetLayouts.push_back(core.createDescriptorSetLayout(forwardProgram.getReflectedDescriptors().at(1)));
+		materialDescriptorSets.push_back(core.createDescriptorSet(materialDescriptorSetLayouts.back()));
 
 		vkcv::asset::Texture& albedoTexture     = scene.textures[albedoIndex];
 		vkcv::asset::Texture& normalTexture     = scene.textures[normalIndex];
@@ -292,13 +292,16 @@ int main(int argc, const char** argv) {
 		core.writeDescriptorSet(materialDescriptorSets.back(), setWrites);
 	}
 
+	std::vector<vkcv::DescriptorSetLayoutHandle> perMeshDescriptorSetLayouts;
 	std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets;
 	for (const auto& vertexGroup : scene.vertexGroups) {
+	    perMeshDescriptorSetLayouts.push_back(materialDescriptorSetLayouts[vertexGroup.materialIndex]);
 		perMeshDescriptorSets.push_back(materialDescriptorSets[vertexGroup.materialIndex]);
 	}
 
 	// prepass pipeline
-	vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(std::vector<vkcv::DescriptorBinding>());
+	vkcv::DescriptorSetLayoutHandle prepassDescriptorSetLayout = core.createDescriptorSetLayout({});
+	vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(prepassDescriptorSetLayout);
 
 	vkcv::PipelineConfig prepassPipelineConfig{
 		depthPrepassShader,
@@ -307,8 +310,8 @@ int main(int argc, const char** argv) {
 		prepassPass,
 		vertexLayout,
 		{ 
-			core.getDescriptorSet(prepassDescriptorSet).layout,
-			core.getDescriptorSet(perMeshDescriptorSets[0]).layout },
+		    core.getDescriptorSetLayout(prepassDescriptorSetLayout).vulkanHandle,
+			core.getDescriptorSetLayout(perMeshDescriptorSetLayouts[0]).vulkanHandle },
 		true };
 	prepassPipelineConfig.m_culling         = vkcv::CullMode::Back;
 	prepassPipelineConfig.m_multisampling   = msaa;
@@ -325,8 +328,8 @@ int main(int argc, const char** argv) {
 		forwardPass,
 		vertexLayout,
 		{	
-			core.getDescriptorSet(forwardShadingDescriptorSet).layout, 
-			core.getDescriptorSet(perMeshDescriptorSets[0]).layout },
+		    core.getDescriptorSetLayout(forwardShadingDescriptorSetLayout).vulkanHandle,
+			core.getDescriptorSetLayout(perMeshDescriptorSetLayouts[0]).vulkanHandle },
 		true
 	};
     forwardPipelineConfig.m_culling         = vkcv::CullMode::Back;
@@ -425,11 +428,12 @@ int main(int argc, const char** argv) {
 		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		tonemappingProgram.addShader(shaderStage, path);
 	});
-	vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(
-		tonemappingProgram.getReflectedDescriptors()[0]);
+	vkcv::DescriptorSetLayoutHandle tonemappingDescriptorSetLayout = core.createDescriptorSetLayout(
+	        tonemappingProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingDescriptorSetLayout);
 	vkcv::PipelineHandle tonemappingPipeline = core.createComputePipeline(
 		tonemappingProgram,
-		{ core.getDescriptorSet(tonemappingDescriptorSet).layout });
+		{ core.getDescriptorSetLayout(tonemappingDescriptorSetLayout).vulkanHandle });
 	
 	// tonemapping compute shader
 	vkcv::ShaderProgram postEffectsProgram;
@@ -437,11 +441,12 @@ int main(int argc, const char** argv) {
 		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		postEffectsProgram.addShader(shaderStage, path);
 	});
-	vkcv::DescriptorSetHandle postEffectsDescriptorSet = core.createDescriptorSet(
-			postEffectsProgram.getReflectedDescriptors()[0]);
+	vkcv::DescriptorSetLayoutHandle postEffectsDescriptorSetLayout = core.createDescriptorSetLayout(
+	        postEffectsProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle postEffectsDescriptorSet = core.createDescriptorSet(postEffectsDescriptorSetLayout);
 	vkcv::PipelineHandle postEffectsPipeline = core.createComputePipeline(
 			postEffectsProgram,
-			{ core.getDescriptorSet(postEffectsDescriptorSet).layout });
+			{ core.getDescriptorSetLayout(postEffectsDescriptorSetLayout).vulkanHandle });
 
 	// resolve compute shader
 	vkcv::ShaderProgram resolveProgram;
@@ -449,11 +454,12 @@ int main(int argc, const char** argv) {
 		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		resolveProgram.addShader(shaderStage, path);
 	});
-	vkcv::DescriptorSetHandle resolveDescriptorSet = core.createDescriptorSet(
-		resolveProgram.getReflectedDescriptors()[0]);
+	vkcv::DescriptorSetLayoutHandle resolveDescriptorSetLayout = core.createDescriptorSetLayout(
+	        resolveProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle resolveDescriptorSet = core.createDescriptorSet(resolveDescriptorSetLayout);
 	vkcv::PipelineHandle resolvePipeline = core.createComputePipeline(
 		resolveProgram,
-		{ core.getDescriptorSet(resolveDescriptorSet).layout });
+		{ core.getDescriptorSetLayout(resolveDescriptorSetLayout).vulkanHandle });
 
 	vkcv::SamplerHandle resolveSampler = core.createSampler(
 		vkcv::SamplerFilterType::NEAREST,
@@ -941,7 +947,7 @@ int main(int argc, const char** argv) {
 				});
 				vkcv::PipelineHandle newPipeline = core.createComputePipeline(
 					newProgram,
-					{ core.getDescriptorSet(tonemappingDescriptorSet).layout });
+					{ core.getDescriptorSetLayout(tonemappingDescriptorSetLayout).vulkanHandle });
 
 				if (newPipeline) {
 					tonemappingPipeline = newPipeline;
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 095fdd2e98e7f48a14c3426c89e3f336b848b463..5413d1133f0348a97616d1001bcd4a763364bfb7 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -721,9 +721,19 @@ namespace vkcv
 		return m_ImageManager->getImageFormat(image);
 	}
 
-    DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings)
+	DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindingsMap)
+	{
+	    return m_DescriptorManager->createDescriptorSetLayout(bindingsMap);
+	}
+
+	DescriptorSetLayout Core::getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const
+	{
+	    return m_DescriptorManager->getDescriptorSetLayout(handle);
+	}
+
+	DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layoutHandle)
     {
-        return m_DescriptorManager->createDescriptorSet(bindings);
+        return m_DescriptorManager->createDescriptorSet(layoutHandle);
     }
 
 	void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp
index a9a127fe608472682c1cbc8d32ca466fba860c72..91a9c24dc1819b69223b6befa9b6d64409698c35 100644
--- a/src/vkcv/DescriptorConfig.cpp
+++ b/src/vkcv/DescriptorConfig.cpp
@@ -1,15 +1,23 @@
 #include "vkcv/DescriptorConfig.hpp"
 
-namespace vkcv {
+namespace vkcv
+{
 	DescriptorBinding::DescriptorBinding(
 		uint32_t bindingID,
 		DescriptorType descriptorType,
 		uint32_t descriptorCount,
-		ShaderStages shaderStages) noexcept
-		:
+		ShaderStages shaderStages) noexcept:
 		bindingID(bindingID),
 		descriptorType(descriptorType),
 		descriptorCount(descriptorCount),
-		shaderStages(shaderStages) {}
-	
+		shaderStages(shaderStages)
+		{}
+
+    bool DescriptorBinding::operator==(const DescriptorBinding &other) const
+    {
+	    return (this->bindingID == other.bindingID) &&
+	           (this->descriptorType == other.descriptorType) &&
+	           (this->descriptorCount == other.descriptorCount) &&
+	           (this->shaderStages == other.shaderStages);
+    }
 }
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index 76f2dae74420804c9ce168bea76e7c1bdef0fbc0..e7ae9ea2bda1164f5425e4324b3ff3969ff45847 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -1,7 +1,5 @@
 #include "DescriptorManager.hpp"
 
-#include "vkcv/Logger.hpp"
-
 namespace vkcv
 {
     DescriptorManager::DescriptorManager(vk::Device device) noexcept:
@@ -34,8 +32,13 @@ namespace vkcv
         for (uint64_t id = 0; id < m_DescriptorSets.size(); id++) {
 			destroyDescriptorSetById(id);
         }
+		
+		for (uint64_t id = 0; id < m_DescriptorSetLayouts.size(); id++) {
+			destroyDescriptorSetLayoutById(id);
+		}
         
 		m_DescriptorSets.clear();
+		m_DescriptorSetLayouts.clear();
   
 		for (const auto &pool : m_Pools) {
 			if (pool) {
@@ -44,55 +47,77 @@ namespace vkcv
 		}
     }
 
-    DescriptorSetHandle DescriptorManager::createDescriptorSet(const std::vector<DescriptorBinding>& bindings)
+    DescriptorSetLayoutHandle DescriptorManager::createDescriptorSetLayout(const DescriptorBindings &setBindingsMap)
     {
-        std::vector<vk::DescriptorSetLayoutBinding> setBindings = {};
+        for (auto i = 0; i < m_DescriptorSetLayouts.size(); i++)
+        {
+            if(m_DescriptorSetLayouts[i].descriptorBindings.size() != setBindingsMap.size())
+                continue;
 
-        //create each set's binding
-        for (auto binding : bindings) {
-            vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
-                binding.bindingID,
-                convertDescriptorTypeFlag(binding.descriptorType),
-                binding.descriptorCount,
-                getShaderStageFlags(binding.shaderStages));
-            setBindings.push_back(descriptorSetLayoutBinding);
+            if (m_DescriptorSetLayouts[i].descriptorBindings == setBindingsMap)
+            {
+                return DescriptorSetLayoutHandle(i, [&](uint64_t id) { destroyDescriptorSetLayoutById(id); });
+            }
         }
+        
+        //create the descriptor set's layout by iterating over its bindings
+        std::vector<vk::DescriptorSetLayoutBinding> bindingsVector = {};
+        for (auto bindingElem : setBindingsMap)
+        {
+            DescriptorBinding binding = bindingElem.second;
+            uint32_t bindingID = bindingElem.first;
 
-        DescriptorSet set;
+            vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
+                    bindingID,
+                    getVkDescriptorType(binding.descriptorType),
+                    binding.descriptorCount,
+                    getShaderStageFlags(binding.shaderStages),
+                    nullptr
+                    );
+
+            bindingsVector.push_back(descriptorSetLayoutBinding);
+        }
 
-        //create the descriptor set's layout from the bindings gathered above
-        vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings);
-        if (m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &set.layout) != vk::Result::eSuccess) {
-			vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
-            return DescriptorSetHandle();
+        //create the descriptor set's layout from the binding data gathered above
+        vk::DescriptorSetLayout vulkanHandle = VK_NULL_HANDLE;
+        vk::DescriptorSetLayoutCreateInfo layoutInfo({}, bindingsVector);
+        auto result = m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &vulkanHandle);
+        if (result != vk::Result::eSuccess) {
+            vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
+            return DescriptorSetLayoutHandle();
         };
-        
-        //create and allocate the set based on the layout that have been gathered above
-        vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &set.layout);
-        auto result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle);
+
+        const uint64_t id = m_DescriptorSetLayouts.size();
+        m_DescriptorSetLayouts.push_back({vulkanHandle, setBindingsMap});
+        return DescriptorSetLayoutHandle(id, [&](uint64_t id) { destroyDescriptorSetLayoutById(id); });
+    }
+
+    DescriptorSetHandle DescriptorManager::createDescriptorSet(const DescriptorSetLayoutHandle &setLayoutHandle)
+    {
+        //create and allocate the set based on the layout provided
+        DescriptorSetLayout setLayout = m_DescriptorSetLayouts[setLayoutHandle.getId()];
+        vk::DescriptorSet vulkanHandle = VK_NULL_HANDLE;
+        vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle);
+        auto result = m_Device.allocateDescriptorSets(&allocInfo, &vulkanHandle);
         if(result != vk::Result::eSuccess)
         {
 			//create a new descriptor pool if the previous one ran out of memory
 			if (result == vk::Result::eErrorOutOfPoolMemory) {
 				allocateDescriptorPool();
 				allocInfo.setDescriptorPool(m_Pools.back());
-				result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle);
+				result = m_Device.allocateDescriptorSets(&allocInfo, &vulkanHandle);
 			}
 			
 			if (result != vk::Result::eSuccess) {
 				vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)",
 						 vk::to_string(result).c_str());
-				
-				m_Device.destroy(set.layout);
 				return DescriptorSetHandle();
 			}
         };
 	
-		set.poolIndex = (m_Pools.size() - 1);
-
+		size_t poolIndex = (m_Pools.size() - 1);
         const uint64_t id = m_DescriptorSets.size();
-
-        m_DescriptorSets.push_back(set);
+        m_DescriptorSets.push_back({vulkanHandle, setLayoutHandle, poolIndex});
         return DescriptorSetHandle(id, [&](uint64_t id) { destroyDescriptorSetById(id); });
     }
     
@@ -247,33 +272,15 @@ namespace vkcv
 		m_Device.updateDescriptorSets(vulkanWrites, nullptr);
 	}
 
+	DescriptorSetLayout DescriptorManager::getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const
+	{
+	    return m_DescriptorSetLayouts[handle.getId()];
+	}
+
 	DescriptorSet DescriptorManager::getDescriptorSet(const DescriptorSetHandle handle) const {
 		return m_DescriptorSets[handle.getId()];
 	}
 
-    vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) {
-        switch (type)
-        {
-            case DescriptorType::UNIFORM_BUFFER:
-                return vk::DescriptorType::eUniformBuffer;
-			case DescriptorType::UNIFORM_BUFFER_DYNAMIC:
-				return vk::DescriptorType::eUniformBufferDynamic;
-            case DescriptorType::STORAGE_BUFFER:
-                return vk::DescriptorType::eStorageBuffer;
-			case DescriptorType::STORAGE_BUFFER_DYNAMIC:
-				return vk::DescriptorType::eStorageBufferDynamic;
-            case DescriptorType::SAMPLER:
-                return vk::DescriptorType::eSampler;
-            case DescriptorType::IMAGE_SAMPLED:
-                return vk::DescriptorType::eSampledImage;
-			case DescriptorType::IMAGE_STORAGE:
-				return vk::DescriptorType::eStorageImage;
-            default:
-				vkcv_log(LogLevel::ERROR, "Unknown DescriptorType");
-                return vk::DescriptorType::eUniformBuffer;
-        }
-    }
-    
     void DescriptorManager::destroyDescriptorSetById(uint64_t id) {
 		if (id >= m_DescriptorSets.size()) {
 			vkcv_log(LogLevel::ERROR, "Invalid id");
@@ -281,17 +288,26 @@ namespace vkcv
 		}
 		
 		auto& set = m_DescriptorSets[id];
-		if (set.layout) {
-			m_Device.destroyDescriptorSetLayout(set.layout);
-			set.layout = nullptr;
-		}
-		
 		if (set.vulkanHandle) {
 			m_Device.freeDescriptorSets(m_Pools[set.poolIndex], 1, &(set.vulkanHandle));
+			set.setLayoutHandle = DescriptorSetLayoutHandle();
 			set.vulkanHandle = nullptr;
 		}
 	}
 
+	void DescriptorManager::destroyDescriptorSetLayoutById(uint64_t id) {
+	    if (id >= m_DescriptorSets.size()) {
+	        vkcv_log(LogLevel::ERROR, "Invalid id");
+	        return;
+	    }
+
+	    auto layout = m_DescriptorSetLayouts[id];
+	    if (layout.vulkanHandle){
+	        m_Device.destroy(layout.vulkanHandle);
+	        layout.vulkanHandle = nullptr;
+	    }
+	}
+
 	vk::DescriptorPool DescriptorManager::allocateDescriptorPool() {
 		vk::DescriptorPool pool;
 		if (m_Device.createDescriptorPool(&m_PoolInfo, nullptr, &pool) != vk::Result::eSuccess) {
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
index df58daa56c26a0432f365a4ed0d9df56adc4cd0e..acc6edd9b704c377480e7acbbc40b3e763003d5f 100644
--- a/src/vkcv/DescriptorManager.hpp
+++ b/src/vkcv/DescriptorManager.hpp
@@ -21,7 +21,8 @@ namespace vkcv
 	    explicit DescriptorManager(vk::Device device) noexcept;
 	    ~DescriptorManager() noexcept;
 
-        DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings);
+	    DescriptorSetLayoutHandle createDescriptorSetLayout(const std::unordered_map<uint32_t, DescriptorBinding> &setBindingsMap);
+        DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &setLayoutHandle);
 
 		void writeDescriptorSet(
 			const DescriptorSetHandle	&handle,
@@ -30,6 +31,8 @@ namespace vkcv
 			const BufferManager     &bufferManager,
 			const SamplerManager    &samplerManager);
 
+		[[nodiscard]]
+		DescriptorSetLayout getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const;
 		[[nodiscard]]
 		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
 
@@ -39,25 +42,29 @@ namespace vkcv
 		std::vector<vk::DescriptorPoolSize> m_PoolSizes;
 		vk::DescriptorPoolCreateInfo m_PoolInfo;
 
-
 		/**
-		* Contains all the resource descriptions that were requested by the user in calls of createResourceDescription.
+        * Contains all the descriptor set layout descriptions
+        * that were requested by the user in calls of createDescriptorSetLayout.
+        */
+        std::vector<DescriptorSetLayout> m_DescriptorSetLayouts;
+
+        /**
+		* Contains all the descriptor sets that were created by the user in calls of createDescriptorSet.
 		*/
         std::vector<DescriptorSet> m_DescriptorSets;
-		
-		/**
-		* Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk).
-		* @param[in] vkcv flag of the DescriptorType (see DescriptorConfig.hpp)
-		* @return vk flag of the DescriptorType
-		*/
-		static vk::DescriptorType convertDescriptorTypeFlag(DescriptorType type);
-		
+
 		/**
-		* Destroys a specific resource description
-		* @param[in] the handle id of the respective resource description
+		* Destroys a specific descriptor set
+		* @param[in] the DescriptorSetHandle
 		*/
 		void destroyDescriptorSetById(uint64_t id);
 
+		/**
+        * Destroys a specific descriptor set LAYOUT (not the set)
+        * @param[in] the DescriptorSetLayoutHandle
+        */
+		void destroyDescriptorSetLayoutById(uint64_t id);
+
 		/**
 		* creates a descriptor pool based on the poolSizes and poolInfo defined in the constructor
 		* is called initially in the constructor and then every time the pool runs out memory
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 134ba7afd203d3222b37dfaf627a9d4fd3ede1c7..a1634c230907ffa741101710613aff73e1fb7b06 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -141,85 +141,124 @@ namespace vkcv {
 
 		//reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image)
         std::vector<std::pair<uint32_t, DescriptorBinding>> bindings;
-        int32_t maxSetID = -1;
-        for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) {
+
+        for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++)
+        {
             auto& u = resources.uniform_buffers[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::UNIFORM_BUFFER, base_type.vecsize, shaderStage));
-            bindings.push_back(descriptor);
-            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) 
-                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+
+            uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+            uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+            auto binding = DescriptorBinding(
+                    bindingID,
+                    DescriptorType::UNIFORM_BUFFER,
+                    base_type.vecsize,
+                    shaderStage);
+
+            auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
+            if(!insertionResult.second)
+            {
+                vkcv_log(LogLevel::WARNING,
+                         "Attempting to overwrite already existing binding %u at set ID %u.",
+                         bindingID,
+                         setID);
+            }
         }
 
-        for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) {
+        for (uint32_t i = 0; i < resources.storage_buffers.size(); i++)
+        {
             auto& u = resources.storage_buffers[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::STORAGE_BUFFER, base_type.vecsize, shaderStage));
-            bindings.push_back(descriptor);
-            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) 
-                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+
+            uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+            uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+            auto binding = DescriptorBinding(
+                    bindingID,
+                    DescriptorType::STORAGE_BUFFER,
+                    base_type.vecsize,
+                    shaderStage);
+
+            auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
+            if(!insertionResult.second)
+            {
+                vkcv_log(LogLevel::WARNING,
+                         "Attempting to overwrite already existing binding %u at set ID %u.",
+                         bindingID,
+                         setID);
+            }
         }
 
         for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) {
             auto& u = resources.separate_samplers[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::SAMPLER, base_type.vecsize, shaderStage));
-            bindings.push_back(descriptor);
-            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) 
-                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+
+            uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+            uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+            auto binding = DescriptorBinding(
+                    bindingID,
+                    DescriptorType::SAMPLER,
+                    base_type.vecsize,
+                    shaderStage);
+
+            auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
+            if(!insertionResult.second)
+            {
+                vkcv_log(LogLevel::WARNING,
+                         "Attempting to overwrite already existing binding %u at set ID %u.",
+                         bindingID,
+                         setID);
+            }
         }
 
         for (uint32_t i = 0; i < resources.separate_images.size(); i++) {
             auto& u = resources.separate_images[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_SAMPLED, base_type.vecsize, shaderStage));
-            bindings.push_back(descriptor);
-            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID)
-                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
 
+            uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+            uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+            auto binding = DescriptorBinding(
+                    bindingID,
+                    DescriptorType::IMAGE_SAMPLED,
+                    base_type.vecsize,
+                    shaderStage);
+
+            auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
+            if(!insertionResult.second)
+            {
+                vkcv_log(LogLevel::WARNING,
+                         "Attempting to overwrite already existing binding %u at set ID %u.",
+                         bindingID,
+                         setID);
+            }
         }
 
         for (uint32_t i = 0; i < resources.storage_images.size(); i++) {
             auto& u = resources.storage_images[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_STORAGE, base_type.vecsize, shaderStage));
-            bindings.push_back(descriptor);
-            if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID)
-                maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-        }
-        if (maxSetID != -1) {
-            if((int32_t)m_DescriptorSets.size() <= maxSetID) m_DescriptorSets.resize(maxSetID + 1);
-            for (const auto &binding : bindings) {
-                //checking if descriptor has already been reflected in another shader stage
-                bool bindingFound = false;
-                uint32_t pos = 0;
-                for (const auto& descriptor : m_DescriptorSets[binding.first]) {
-                    if (binding.second.bindingID == descriptor.bindingID) {
-                        if (binding.second.descriptorType == descriptor.descriptorType && binding.second.descriptorCount == descriptor.descriptorCount) {
-                            //updating descriptor binding with another shader stage
-                            ShaderStages updatedShaders = descriptor.shaderStages | shaderStage;
-                            DescriptorBinding newBinding = DescriptorBinding(binding.second.bindingID, binding.second.descriptorType, binding.second.descriptorCount, updatedShaders);
-                            m_DescriptorSets[binding.first][pos] = newBinding;
-                            bindingFound = true;
-                            break;
-                        }
-                        else vkcv_log(LogLevel::ERROR, "Included shaders contain resources with same identifier but different type or count");
-                    }
-                    pos++;
-                }
-                //append new descriptor if it has not been reflected yet
-                if(!bindingFound) m_DescriptorSets[binding.first].push_back(binding.second);
+
+            uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
+            uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+            auto binding = DescriptorBinding(
+                    bindingID,
+                    DescriptorType::IMAGE_STORAGE,
+                    base_type.vecsize,
+                    shaderStage);
+
+            auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
+            if(!insertionResult.second)
+            {
+                vkcv_log(LogLevel::WARNING,
+                         "Attempting to overwrite already existing binding %u at set ID %u.",
+                         bindingID,
+                         setID);
             }
         }
 
         //reflect push constants
-		for (const auto &pushConstantBuffer : resources.push_constant_buffers) {
-			for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id)) {
+		for (const auto &pushConstantBuffer : resources.push_constant_buffers)
+		{
+			for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id))
+			{
 				const size_t size = range.range + range.offset;
 				m_pushConstantSize = std::max(m_pushConstantSize, size);
 			}
@@ -231,7 +270,8 @@ namespace vkcv {
         return m_VertexAttachments;
 	}
 
-    const std::vector<std::vector<DescriptorBinding>>& ShaderProgram::getReflectedDescriptors() const {
+	const std::unordered_map<uint32_t, DescriptorBindings>& ShaderProgram::getReflectedDescriptors() const
+    {
         return m_DescriptorSets;
     }