diff --git a/Doxyfile b/Doxyfile
index 706626db3e7d7710f4f6c9f1c6354e1dee087d32..a7c5c76bb52fe4a0cd4b2f982abff642af011f8f 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -874,8 +874,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = src \
-                         include \
+INPUT                  = include \
                          modules
 
 # This tag can be used to specify the character encoding of the source files
diff --git a/config/Sources.cmake b/config/Sources.cmake
index f0fd0ed758ee8b7f4d8ce0940babf0e1142e6b60..db4b200fa99597af4f96432e35e80af387ae881a 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -15,23 +15,31 @@ set(vkcv_sources
 		
 		${vkcv_include}/vkcv/File.hpp
 		${vkcv_source}/vkcv/File.cpp
+		
+		${vkcv_include}/vkcv/Pass.hpp
+		${vkcv_source}/vkcv/Pass.cpp
 
 		${vkcv_include}/vkcv/PassConfig.hpp
+		${vkcv_source}/vkcv/PassConfig.cpp
 
 		${vkcv_source}/vkcv/PassManager.hpp
 		${vkcv_source}/vkcv/PassManager.cpp
 
 		${vkcv_include}/vkcv/Handles.hpp
 		${vkcv_source}/vkcv/Handles.cpp
+		
+		${vkcv_source}/vkcv/HandleManager.hpp
 
 		${vkcv_include}/vkcv/Window.hpp
 		${vkcv_source}/vkcv/Window.cpp
-
+		
+		${vkcv_include}/vkcv/BufferTypes.hpp
 		${vkcv_include}/vkcv/Buffer.hpp
 		
 		${vkcv_include}/vkcv/PushConstants.hpp
+		${vkcv_source}/vkcv/PushConstants.cpp
 		
-		${vkcv_include}/vkcv/BufferManager.hpp
+		${vkcv_source}/vkcv/BufferManager.hpp
 		${vkcv_source}/vkcv/BufferManager.cpp
 
 		${vkcv_include}/vkcv/Image.hpp
@@ -40,13 +48,10 @@ set(vkcv_sources
 		${vkcv_source}/vkcv/ImageManager.hpp
 		${vkcv_source}/vkcv/ImageManager.cpp
 		
-		${vkcv_include}/vkcv/Logger.hpp
+		${vkcv_include}/vkcv/PipelineConfig.hpp
+		${vkcv_source}/vkcv/PipelineConfig.cpp
 		
-		${vkcv_include}/vkcv/Surface.hpp
-		${vkcv_source}/vkcv/Surface.cpp
-
-		${vkcv_include}/vkcv/Swapchain.hpp
-		${vkcv_source}/vkcv/Swapchain.cpp
+		${vkcv_include}/vkcv/Logger.hpp
 		
 		${vkcv_include}/vkcv/ShaderStage.hpp
 		
@@ -54,6 +59,8 @@ set(vkcv_sources
 		${vkcv_source}/vkcv/ShaderProgram.cpp
 
 		${vkcv_include}/vkcv/GraphicsPipelineConfig.hpp
+		${vkcv_source}/vkcv/GraphicsPipelineConfig.cpp
+		
 		${vkcv_include}/vkcv/ComputePipelineConfig.hpp
 
 		${vkcv_source}/vkcv/ComputePipelineManager.hpp
@@ -61,30 +68,35 @@ set(vkcv_sources
 
 		${vkcv_source}/vkcv/GraphicsPipelineManager.hpp
 		${vkcv_source}/vkcv/GraphicsPipelineManager.cpp
-
-        ${vkcv_include}/vkcv/CommandResources.hpp
-        ${vkcv_source}/vkcv/CommandResources.cpp
-        
-        ${vkcv_include}/vkcv/SyncResources.hpp
-        ${vkcv_source}/vkcv/SyncResources.cpp
         
         ${vkcv_include}/vkcv/QueueManager.hpp
         ${vkcv_source}/vkcv/QueueManager.cpp
 
 		${vkcv_include}/vkcv/VertexLayout.hpp
 		${vkcv_source}/vkcv/VertexLayout.cpp
+		
+		${vkcv_include}/vkcv/DispatchSize.hpp
+		${vkcv_source}/vkcv/DispatchSize.cpp
 
 		${vkcv_include}/vkcv/Event.hpp
+		
+		${vkcv_include}/vkcv/TypeGuard.hpp
+		${vkcv_source}/vkcv/TypeGuard.cpp
+		
+		${vkcv_include}/vkcv/DescriptorTypes.hpp
 
-		${vkcv_source}/vkcv/DescriptorManager.hpp
-		${vkcv_source}/vkcv/DescriptorManager.cpp
-
-		${vkcv_include}/vkcv/DescriptorConfig.hpp
-		${vkcv_source}/vkcv/DescriptorConfig.cpp
+		${vkcv_include}/vkcv/DescriptorBinding.hpp
+		${vkcv_source}/vkcv/DescriptorBinding.cpp
 		
 		${vkcv_include}/vkcv/DescriptorWrites.hpp
 		${vkcv_source}/vkcv/DescriptorWrites.cpp
 		
+		${vkcv_source}/vkcv/DescriptorSetLayoutManager.hpp
+		${vkcv_source}/vkcv/DescriptorSetLayoutManager.cpp
+		
+		${vkcv_source}/vkcv/DescriptorSetManager.hpp
+		${vkcv_source}/vkcv/DescriptorSetManager.cpp
+		
 		${vkcv_source}/vkcv/SamplerManager.hpp
 		${vkcv_source}/vkcv/SamplerManager.cpp
 
@@ -94,18 +106,13 @@ set(vkcv_sources
 		${vkcv_source}/vkcv/SwapchainManager.hpp
 		${vkcv_source}/vkcv/SwapchainManager.cpp
         
-        ${vkcv_include}/vkcv/DescriptorWrites.hpp
-        
-        ${vkcv_include}/vkcv/DrawcallRecording.hpp
-        ${vkcv_source}/vkcv/DrawcallRecording.cpp
-        
         ${vkcv_source}/vkcv/CommandStreamManager.hpp
         ${vkcv_source}/vkcv/CommandStreamManager.cpp
         
-        ${vkcv_include}/vkcv/CommandRecordingFunctionTypes.hpp
+        ${vkcv_include}/vkcv/EventFunctionTypes.hpp
         
-        ${vkcv_include}/vkcv/ImageConfig.hpp
-        ${vkcv_source}/vkcv/ImageConfig.cpp
+        ${vkcv_include}/vkcv/Multisampling.hpp
+        ${vkcv_source}/vkcv/Multisampling.cpp
 		
 		${vkcv_include}/vkcv/Downsampler.hpp
 		${vkcv_source}/vkcv/Downsampler.cpp
@@ -113,7 +120,19 @@ set(vkcv_sources
 		${vkcv_include}/vkcv/BlitDownsampler.hpp
 		${vkcv_source}/vkcv/BlitDownsampler.cpp
 		
+		${vkcv_include}/vkcv/SamplerTypes.hpp
+		
 		${vkcv_include}/vkcv/Sampler.hpp
+		${vkcv_source}/vkcv/Sampler.cpp
+		
+		${vkcv_include}/vkcv/DescriptorSetUsage.hpp
+		${vkcv_source}/vkcv/DescriptorSetUsage.cpp
+		
+		${vkcv_include}/vkcv/Drawcall.hpp
+		${vkcv_source}/vkcv/Drawcall.cpp
+		
+		${vkcv_include}/vkcv/VertexData.hpp
+		${vkcv_source}/vkcv/VertexData.cpp
 		
 		${vkcv_include}/vkcv/Result.hpp
 )
diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index 40821e68569397fce497426b66991fc38bb29859..5a4afb060c8ae9048fbd2edb65c6d8167976f810 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -7,8 +7,9 @@
 
 #include <vector>
 
+#include "BufferTypes.hpp"
+#include "Core.hpp"
 #include "Handles.hpp"
-#include "BufferManager.hpp"
 
 namespace vkcv {
 
@@ -19,10 +20,18 @@ namespace vkcv {
 	 */
 	template<typename T>
 	class Buffer {
-		friend class Core;
 	public:
-		// explicit destruction of default constructor
-		Buffer() = delete;
+		Buffer() : m_core(nullptr), m_handle() {}
+		
+		Buffer(Core* core, const BufferHandle& handle) : m_core(core), m_handle(handle) {}
+		
+		Buffer(const Buffer& other) = default;
+		Buffer(Buffer&& other) noexcept = default;
+		
+		~Buffer() = default;
+		
+		Buffer& operator=(const Buffer& other) = default;
+		Buffer& operator=(Buffer&& other) noexcept = default;
 		
 		/**
 		 * @brief Returns the buffers handle.
@@ -41,9 +50,14 @@ namespace vkcv {
 		 */
 		[[nodiscard]]
 		BufferType getType() const {
-			return m_type;
+			return m_core->getBufferType(m_handle);
 		};
 		
+		[[nodiscard]]
+		BufferMemoryType getMemoryType() const {
+			return m_core->getBufferMemoryType(m_handle);
+		}
+		
 		/**
 		 * @brief Returns the count of elements in the buffer.
 		 *
@@ -51,7 +65,7 @@ namespace vkcv {
 		 */
 		[[nodiscard]]
 		size_t getCount() const {
-			return m_count;
+			return m_core->getBufferSize(m_handle) / sizeof(T);
 		}
 		
 		/**
@@ -61,7 +75,7 @@ namespace vkcv {
 		 */
 		[[nodiscard]]
 		size_t getSize() const {
-			return m_count * sizeof(T);
+			return m_core->getBufferSize(m_handle);
 		}
 
 		/**
@@ -71,7 +85,7 @@ namespace vkcv {
 		 */
         [[nodiscard]]
 		vk::Buffer getVulkanHandle() const {
-            return m_manager->getBuffer(m_handle);
+            return m_core->getBuffer(m_handle);
         }
 
 		/**
@@ -84,7 +98,7 @@ namespace vkcv {
 		void fill(const T* data,
 				  size_t count = 0,
 				  size_t offset = 0) {
-			 m_manager->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
+			 m_core->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
 		}
 		
 		/**
@@ -108,7 +122,7 @@ namespace vkcv {
 		void read(T* data,
 				  size_t count = 0,
 				  size_t offset = 0) {
-			m_manager->readBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
+			m_core->readBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T));
 		}
 		
 		/**
@@ -132,77 +146,29 @@ namespace vkcv {
 		[[nodiscard]]
 		T* map(size_t offset = 0,
 			   size_t count = 0) {
-			return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T)));
+			return reinterpret_cast<T*>(m_core->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T)));
 		}
 
 		/**
 		 * @brief Unmaps the #Buffer, invalidates the pointer obtained by map().
 		 */
 		void unmap() {
-			m_manager->unmapBuffer(m_handle);
+			m_core->unmapBuffer(m_handle);
 		}
 
 	private:
-		BufferManager* const m_manager;
-		const BufferHandle m_handle;
-		const BufferType m_type;
-		const size_t m_count;
-		const BufferMemoryType m_memoryType;
-		
-		/**
-		 * @brief Constructor of the buffer object.
-		 *
-		 * @param[in,out] manager Buffer manager
-		 * @param[in] handle Buffer handle
-		 * @param[in] type Type of buffer
-		 * @param[in] count Count of elements
-		 * @param[in] memoryType Type of memory
-		 */
-		Buffer(BufferManager* manager,
-			   BufferHandle handle,
-			   BufferType type,
-			   size_t count,
-			   BufferMemoryType memoryType) :
-				m_manager(manager),
-				m_handle(handle),
-				m_type(type),
-				m_count(count),
-				m_memoryType(memoryType)
-		{}
-		
-		/**
-		 * @brief Creates a buffer object of type T with
-		 * a selected type, count of elements, memory type
-		 * and support of indirect usage.
-		 *
-		 * @param[in,out] manager Buffer manager
-		 * @param[in] type Buffer type
-		 * @param[in] count Count of elements
-		 * @param[in] memoryType Type of memory
-		 * @param[in] supportIndirect Support indirect usage
-		 * @return New buffer object
-		 */
-		[[nodiscard]]
-		static Buffer<T> create(BufferManager* manager,
-								BufferType type,
-								size_t count,
-								BufferMemoryType memoryType,
-								bool supportIndirect,
-								bool readable) {
-			return Buffer<T>(
-				manager,
-				manager->createBuffer(
-					type,
-					count * sizeof(T),
-					memoryType,
-					supportIndirect,
-					readable
-				),
-				type,
-				count,
-				memoryType
-			);
-		}
+		Core* m_core;
+		BufferHandle m_handle;
 		
 	};
+	
+	template<typename T>
+	Buffer<T> buffer(Core& core,
+					 BufferType type,
+					 size_t count,
+					 BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL,
+					 bool readable = false) {
+		return Buffer<T>(&core, core.createBuffer(type, typeGuard<T>(), count, memoryType, readable));
+	}
+	
 }
diff --git a/include/vkcv/BufferTypes.hpp b/include/vkcv/BufferTypes.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d2938af50efd1b57ddd131837cc2e1198edab4f
--- /dev/null
+++ b/include/vkcv/BufferTypes.hpp
@@ -0,0 +1,34 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/BufferTypes.hpp
+ * @brief Enum classes to specify attributes of buffers.
+ */
+
+namespace vkcv {
+	
+	/**
+	 * @brief Enum class to specify types of buffers.
+	 */
+	enum class BufferType {
+		INDEX,
+		VERTEX,
+		UNIFORM,
+		STORAGE,
+		STAGING,
+		INDIRECT,
+		
+		UNKNOWN
+	};
+	
+	/**
+	 * @brief Enum class to specify types of buffer memory.
+	 */
+	enum class BufferMemoryType {
+		DEVICE_LOCAL,
+		HOST_VISIBLE,
+		
+		UNKNOWN
+	};
+	
+}
diff --git a/include/vkcv/CommandResources.hpp b/include/vkcv/CommandResources.hpp
deleted file mode 100644
index 8ec868c0a5a353cba2efb2313d4168450f30844b..0000000000000000000000000000000000000000
--- a/include/vkcv/CommandResources.hpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#pragma once
-/**
- * @authors Alexander Gauggel, Tobias Frisch
- * @file vkcv/CommandResources.hpp
- * @brief Support functions to deal with command resources.
- */
-
-#include <vulkan/vulkan.hpp>
-#include <unordered_set>
-
-#include "QueueManager.hpp"
-
-namespace vkcv {
-	
-	/**
-	 * @brief Structure to store command pools for given queue families
-	 * of a device.
-	 */
-	struct CommandResources {
-		std::vector<vk::CommandPool> cmdPoolPerQueueFamily;
-	};
-
-	/**
-	 * @brief Generates a set of the family indices for all different kinds of
-	 * queues a given queue manager provides.
-	 *
-	 * @param[in] queueManager Queue manager
-	 * @return Set of queue family indices
-	 */
-	std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager);
-	
-	/**
-	 * @brief Creates and returns a new command resources instance containing
-	 * a vector of newly allocated command pools for each different queue family
-	 * index in a given set.
-	 *
-	 * @param[in,out] device Vulkan device
-	 * @param[in] familyIndexSet Set of queue family indices
-	 * @return New command resources
-	 */
-	CommandResources createCommandResources(const vk::Device &device,
-											const std::unordered_set<int> &familyIndexSet);
-	
-	/**
-	 * @brief Destroys a command resources instance and deallocates its command
-	 * pools. The command resources will be invalid to use afterwards.
-	 *
-	 * @param[in,out] device Vulkan device
-	 * @param[in,out] resources Command resources
-	 */
-	void destroyCommandResources(const vk::Device &device,
-								 const CommandResources &resources);
-	
-	/**
-	 * @brief Allocates and returns a new primary command buffer of a given
-	 * command pool.
-	 *
-	 * @param[in,out] device Vulkan device
-	 * @param[in,out] cmdPool Vulkan command pool
-	 * @return New vulkan command buffer
-	 */
-	vk::CommandBuffer allocateCommandBuffer(const vk::Device &device,
-											const vk::CommandPool &cmdPool);
-	
-	/**
-	 * @brief Returns the matching command pool of given command resources for
-	 * a specific queue.
-	 *
-	 * @param[in] queue Queue
-	 * @param[in] cmdResources Command resources
-	 * @return Command pool for a given queue
-	 */
-	vk::CommandPool chooseCmdPool(const Queue &queue,
-								  const CommandResources &cmdResources);
-	
-	/**
-	 * @brief Returns a queue of a given type from a queue manager.
-	 *
-	 * @param[in] type Type of queue
-	 * @param[in] queueManager Queue manager
-	 * @return Queue of a given type
-	 */
-	Queue getQueueForSubmit(QueueType type,
-							const QueueManager &queueManager);
-	
-	/**
-	 * @brief Begins the usage of a command buffer with given command buffer
-	 * usage flags.
-	 *
-	 * @param[in] cmdBuffer Vulkan command buffer
-	 * @param[in] flags Command buffer usage flags
-	 */
-	void beginCommandBuffer(const vk::CommandBuffer &cmdBuffer,
-							vk::CommandBufferUsageFlags flags);
-
-	/**
-	 * @brief Submits a command buffer into a queue with given fence and
-	 * semaphores to wait for or to signal after processing the command
-	 * buffer.
-	 *
-	 * @param[in,out] queue Vulkan queue
-	 * @param[in] cmdBuffer Vulkan command buffer
-	 * @param[in] fence Vulkan fence to wait for
-	 * @param[in] waitSemaphores Vector of semaphores to wait for
-	 * @param[in] signalSemaphores Vector of semaphores to signal
-	 */
-	void submitCommandBufferToQueue(vk::Queue queue,
-									const vk::CommandBuffer &cmdBuffer,
-									const vk::Fence &fence,
-									const std::vector<vk::Semaphore>& waitSemaphores,
-									const std::vector<vk::Semaphore>& signalSemaphores);
-	
-}
\ No newline at end of file
diff --git a/include/vkcv/ComputePipelineConfig.hpp b/include/vkcv/ComputePipelineConfig.hpp
index ab458aa53704ccc1d5dbe2f1024d826b0b337f78..82f3eedf54bb80f11576a2aafcc7ba3b244e61ce 100644
--- a/include/vkcv/ComputePipelineConfig.hpp
+++ b/include/vkcv/ComputePipelineConfig.hpp
@@ -5,19 +5,15 @@
  * @brief Compute pipeline config struct to hand over required information to pipeline creation.
  */
 
-#include <vector>
-
-#include "Handles.hpp"
-#include "ShaderProgram.hpp"
+#include "PipelineConfig.hpp"
 
 namespace vkcv {
 	
 	/**
-	 * @brief Structure to configure a compute pipeline before its creation.
+	 * @brief Class to configure a compute pipeline before its creation.
 	 */
-    struct ComputePipelineConfig {
-        ShaderProgram&                          m_ShaderProgram;
-        std::vector<DescriptorSetLayoutHandle>	m_DescriptorSetLayouts;
-    };
+    class ComputePipelineConfig : public PipelineConfig {
+		using PipelineConfig::PipelineConfig;
+	};
 	
 }
\ No newline at end of file
diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp
index d91855a54c83f17b8251aad7c6fe2cb2591f9add..5bdd354dcecd6375054537bdb4b4fc127b98d02e 100644
--- a/include/vkcv/Context.hpp
+++ b/include/vkcv/Context.hpp
@@ -8,8 +8,8 @@
 #include <vulkan/vulkan.hpp>
 #include <vk_mem_alloc.hpp>
 
+#include "Handles.hpp"
 #include "QueueManager.hpp"
-#include "DrawcallRecording.hpp"
 #include "Features.hpp"
 
 namespace vkcv
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index be7cb6097acf2aa163c6e1ec8218a793caa6bd73..2f158b4bd4a1e73d0933145a76686db2c6bca659 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -9,25 +9,22 @@
 #include <memory>
 #include <vulkan/vulkan.hpp>
 
+#include "BufferTypes.hpp"
 #include "Context.hpp"
-#include "Swapchain.hpp"
 #include "Window.hpp"
 #include "PassConfig.hpp"
 #include "Handles.hpp"
-#include "Buffer.hpp"
-#include "Image.hpp"
 #include "BlitDownsampler.hpp"
 #include "GraphicsPipelineConfig.hpp"
 #include "ComputePipelineConfig.hpp"
-#include "CommandResources.hpp"
-#include "SyncResources.hpp"
 #include "Result.hpp"
-#include "vkcv/DescriptorConfig.hpp"
-#include "Sampler.hpp"
+#include "SamplerTypes.hpp"
 #include "DescriptorWrites.hpp"
 #include "Event.hpp"
-#include "DrawcallRecording.hpp"
-#include "CommandRecordingFunctionTypes.hpp"
+#include "Drawcall.hpp"
+#include "PushConstants.hpp"
+#include "EventFunctionTypes.hpp"
+#include "DispatchSize.hpp"
 
 #define VKCV_FRAMEWORK_NAME "VkCV"
 #define VKCV_FRAMEWORK_VERSION (VK_MAKE_VERSION(0, 1, 0))
@@ -39,7 +36,8 @@ namespace vkcv
     class PassManager;
     class GraphicsPipelineManager;
     class ComputePipelineManager;
-    class DescriptorManager;
+    class DescriptorSetLayoutManager;
+	class DescriptorSetManager;
     class BufferManager;
     class SamplerManager;
     class ImageManager;
@@ -47,23 +45,13 @@ namespace vkcv
 	class WindowManager;
 	class SwapchainManager;
 
-	/**
-	 * @brief Structure to store details about a queue submission.
-	 */
-	struct SubmitInfo {
-		QueueType queueType;
-		std::vector<vk::Semaphore> waitSemaphores;
-		std::vector<vk::Semaphore> signalSemaphores;
-	};
-
 	/**
 	 * @brief Class to handle the core functionality of the framework.
 	 *
 	 * The class handles the core functionality of the framework with most
 	 * calls addressing resource management via more simplified abstraction.
 	 */
-    class Core final
-    {
+    class Core final {
     private:
 
         /**
@@ -71,7 +59,8 @@ namespace vkcv
          *
          * @param context encapsulates various Vulkan objects
          */
-        Core(Context &&context, const CommandResources& commandResources, const SyncResources& syncResources) noexcept;
+        explicit Core(Context &&context) noexcept;
+		
         // explicit destruction of default constructor
         Core() = delete;
 
@@ -79,20 +68,22 @@ namespace vkcv
 
         Context m_Context;
 
-        std::unique_ptr<PassManager>             m_PassManager;
-        std::unique_ptr<GraphicsPipelineManager> m_PipelineManager;
-        std::unique_ptr<ComputePipelineManager>  m_ComputePipelineManager;
-        std::unique_ptr<DescriptorManager>       m_DescriptorManager;
-        std::unique_ptr<BufferManager>           m_BufferManager;
-        std::unique_ptr<SamplerManager>          m_SamplerManager;
-        std::unique_ptr<ImageManager>            m_ImageManager;
-        std::unique_ptr<CommandStreamManager>    m_CommandStreamManager;
-        std::unique_ptr<WindowManager>           m_WindowManager;
-        std::unique_ptr<SwapchainManager>        m_SwapchainManager;
-
-		CommandResources    m_CommandResources;
-		SyncResources       m_SyncResources;
-		uint32_t            m_currentSwapchainImageIndex;
+        std::unique_ptr<PassManager>             	m_PassManager;
+        std::unique_ptr<GraphicsPipelineManager> 	m_GraphicsPipelineManager;
+        std::unique_ptr<ComputePipelineManager>  	m_ComputePipelineManager;
+        std::unique_ptr<DescriptorSetLayoutManager> m_DescriptorSetLayoutManager;
+		std::unique_ptr<DescriptorSetManager>       m_DescriptorSetManager;
+        std::unique_ptr<BufferManager>           	m_BufferManager;
+        std::unique_ptr<SamplerManager>          	m_SamplerManager;
+        std::unique_ptr<ImageManager>            	m_ImageManager;
+        std::unique_ptr<CommandStreamManager>    	m_CommandStreamManager;
+        std::unique_ptr<WindowManager>           	m_WindowManager;
+        std::unique_ptr<SwapchainManager>        	m_SwapchainManager;
+	
+		std::vector<vk::CommandPool> m_CommandPools;
+		vk::Semaphore m_RenderFinished;
+		vk::Semaphore m_SwapchainImageAcquired;
+		uint32_t m_currentSwapchainImageIndex;
 		
 		std::unique_ptr<Downsampler> m_downsampler;
 
@@ -192,43 +183,147 @@ namespace vkcv
          * Creates a basic vulkan render pass using @p config from the render pass config class and returns it.
          * Fixed Functions for pipeline are set with standard values.
          *
-         * @param config a render pass config object from the render pass config class
+         * @param[in] config a render pass config object from the render pass config class
          * @return A handle to represent the created pass
          */
         [[nodiscard]]
         PassHandle createPass(const PassConfig &config);
-
-        /**
-            * Creates a #Buffer with data-type T and @p bufferType
-            * @param type Type of Buffer created
-            * @param count Count of elements of type T
-            * @param memoryType Type of Buffers memory
-            * return Buffer-Object
-            */
-        template<typename T>
-        Buffer<T> createBuffer(vkcv::BufferType type,
-							   size_t count,
-							   BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL,
-							   bool supportIndirect = false,
-							   bool readable = false) {
-        	return Buffer<T>::create(
-					m_BufferManager.get(),
-					type,
-					count,
-					memoryType,
-					supportIndirect,
-					readable
-			);
-        }
+		
+		/**
+		 * Returns the used configuration for a created render pass which is
+		 * represented by the given handle.
+		 *
+		 * @param[in] pass Pass handle
+		 * @return Pass configuration
+		 */
+		[[nodiscard]]
+		const PassConfig& getPassConfiguration(const PassHandle &pass);
+	
+		/**
+		 * @brief Creates a buffer with given parameters and returns its handle.
+		 *
+		 * @param[in] type Type of buffer created
+		 * @param[in] typeGuard Type guard for the buffer
+		 * @param[in] count Count of elements of its guarded type
+		 * @param[in] memoryType Type of buffers memory
+		 * @param[in] readable Flag whether the buffer supports reading from it
+		 * @return A handle to represent the created buffer
+		 */
+		BufferHandle createBuffer(BufferType type,
+								  const TypeGuard& typeGuard,
+								  size_t count,
+								  BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL,
+								  bool readable = false);
+	
+		/**
+		 * @brief Creates a buffer with given parameters and returns its handle.
+		 *
+		 * @param[in] type Type of buffer created
+		 * @param[in] size Size of the buffer
+		 * @param[in] memoryType Type of buffers memory
+		 * @param[in] readable Flag whether the buffer supports reading from it
+		 * @return A handle to represent the created buffer
+		 */
+		BufferHandle createBuffer(BufferType type,
+								  size_t size,
+								  BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL,
+								  bool readable = false);
+	
+		/**
+		 * @brief Returns the vulkan buffer of a given buffer handle.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @return Vulkan buffer
+		 */
+		vk::Buffer getBuffer(const BufferHandle& buffer) const;
+	
+		/**
+		 * @brief Returns the buffer type of a buffer represented
+		 * by a given buffer handle.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @return Buffer type
+		 */
+		[[nodiscard]]
+		BufferType getBufferType(const BufferHandle& buffer) const;
+	
+		/**
+		 * @brief Returns the buffer memory type of a buffer
+		 * represented by a given buffer handle.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @return Buffer memory type
+		 */
+		[[nodiscard]]
+		BufferMemoryType getBufferMemoryType(const BufferHandle& buffer) const;
+	
+		/**
+		 * @brief Returns the size of a buffer represented
+		 * by a given buffer handle.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @return Size of the buffer
+		 */
+		[[nodiscard]]
+		size_t getBufferSize(const BufferHandle& buffer) const;
+	
+		/**
+		 * @brief Fills a buffer represented by a given buffer
+		 * handle with custom data.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @param[in] data Pointer to data
+		 * @param[in] size Size of data in bytes
+		 * @param[in] offset Offset to fill in data in bytes
+		 */
+		void fillBuffer(const BufferHandle& buffer,
+						const void* data,
+						size_t size,
+						size_t offset);
+	
+		/**
+		 * @brief Reads from a buffer represented by a given
+		 * buffer handle to some data pointer.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @param[in] data Pointer to data
+		 * @param[in] size Size of data to read in bytes
+		 * @param[in] offset Offset to read from buffer in bytes
+		 */
+		void readBuffer(const BufferHandle& buffer,
+						void* data,
+						size_t size,
+						size_t offset);
+	
+		/**
+		 * @brief Maps memory to a buffer represented by a given
+		 * buffer handle and returns it.
+		 *
+		 * @param[in] buffer Buffer handle
+		 * @param[in] offset Offset of mapping in bytes
+		 * @param[in] size Size of mapping in bytes
+		 * @return Pointer to mapped memory
+		 */
+		void* mapBuffer(const BufferHandle& buffer,
+						size_t offset,
+						size_t size);
+	
+		/**
+		 * @brief Unmaps memory from a buffer represented by a given
+		 * buffer handle.
+		 *
+		 * @param[in] buffer Buffer handle
+		 */
+		void unmapBuffer(const BufferHandle& buffer);
         
         /**
          * Creates a Sampler with given attributes.
          *
-         * @param magFilter Magnifying filter
-         * @param minFilter Minimizing filter
-         * @param mipmapMode Mipmapping filter
-         * @param addressMode Address mode
-         * @param mipLodBias Mip level of detail bias
+         * @param[in] magFilter Magnifying filter
+         * @param[in] minFilter Minimizing filter
+         * @param[in] mipmapMode Mipmapping filter
+         * @param[in] addressMode Address mode
+         * @param[in] mipLodBias Mip level of detail bias
          * @return Sampler handle
          */
         [[nodiscard]]
@@ -237,24 +332,49 @@ namespace vkcv
 									float mipLodBias = 0.0f, SamplerBorderColor borderColor = SamplerBorderColor::INT_ZERO_OPAQUE);
 
         /**
-         * Creates an #Image with a given format, width, height and depth.
+         * Creates an #Image with a given format, width, height, depth
+         * and a lot more optional parameters.
          *
-         * @param format Image format
-         * @param width Image width
-         * @param height Image height
-         * @param depth Image depth
-         * @return Image-Object
+         * @param[in] format Image format
+         * @param[in] width Image width
+         * @param[in] height Image height
+         * @param[in] depth Image depth
+         * @param[in] createMipChain Flag to create a mip chain
+         * @param[in] supportStorage Flag whether support storage
+         * @param[in] supportColorAttachment Flag whether attachment is supported
+         * @param[in] multisampling Multisampling
+         * @return Image handle
          */
         [[nodiscard]]
-        Image createImage(
-			vk::Format      format,
-			uint32_t        width,
-			uint32_t        height,
-			uint32_t        depth = 1,
-			bool            createMipChain = false,
-			bool            supportStorage = false,
-			bool            supportColorAttachment = false,
-			Multisampling   multisampling = Multisampling::None);
+        ImageHandle createImage(vk::Format format,
+								uint32_t width,
+								uint32_t height,
+								uint32_t depth=1,
+								bool createMipChain=false,
+								bool supportStorage=false,
+								bool supportColorAttachment=false,
+								Multisampling multisampling=Multisampling::None);
+		
+		/**
+		 * @brief Fills the image with given data of a specified size
+		 * in bytes.
+		 *
+		 * @param[in] image Image handle
+		 * @param[in] data Image data pointer
+		 * @param[in] size Size of data
+		 */
+		void fillImage(const ImageHandle& image,
+					   const void *data,
+					   size_t size);
+		
+		/**
+		 * @brief Switches the images layout synchronously if possible.
+		 *
+		 * @param[in] image Image handle
+		 * @param[in] layout New image layout
+		 */
+		void switchImageLayout(const ImageHandle &image,
+							   vk::ImageLayout layout);
 		
 		/**
 		 * @brief Returns the default blit-downsampler.
@@ -266,10 +386,10 @@ namespace vkcv
 
         /**
          * Creates a new window and returns it's handle
-         * @param applicationName window name
-         * @param windowWidth
-         * @param windowHeight
-         * @param resizeable resizeability bool
+         * @param[in] applicationName Window title
+         * @param[in] windowWidth Window width
+         * @param[in] windowHeight Window height
+         * @param[in] resizeable resizeability bool
          * @return windowHandle
          */
 		[[nodiscard]]
@@ -281,39 +401,45 @@ namespace vkcv
 
 		/**
 		 * Getter for window reference
-		 * @param handle of the window
+		 * @param[in] handle of the window
 		 * @return the window
 		 */
 		[[nodiscard]]
-		Window& getWindow(const WindowHandle& handle );
-
+		Window& getWindow(const WindowHandle& handle);
+	
 		/**
-		 * Gets the swapchain of the current focused window
-		 * @return swapchain
-		 */
+         * @brief Returns the image format for the current surface
+         * of the swapchain.
+         *
+         * @param[in] handle Swapchain handle
+         * @return Swapchain image format
+         */
 		[[nodiscard]]
-		Swapchain& getSwapchainOfCurrentWindow();
-
+		vk::Format getSwapchainFormat(const SwapchainHandle& swapchain) const;
+	
 		/**
-		 * Returns the swapchain reference
-		 * @param handle of the swapchain
-		 * @return swapchain
-		 */
+		 * @brief Returns the amount of images for the swapchain.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @return Number of images
+		*/
 		[[nodiscard]]
-		Swapchain& getSwapchain(const SwapchainHandle &handle);
-
+		uint32_t getSwapchainImageCount(const SwapchainHandle& swapchain) const;
+	
 		/**
-		 * Gets the swapchain handle from the window
-		 * @param handle of the window
-		 * @return the swapchain from getSwapchain( SwapchainHandle )
-		 */
+         * @brief Returns the extent from the current surface of
+         * the swapchain.
+         *
+         * @param[in] handle Swapchain handle
+         * @return Extent of the swapchains surface
+         */
 		[[nodiscard]]
-		Swapchain& getSwapchain(const WindowHandle &handle);
+		vk::Extent2D getSwapchainExtent(const SwapchainHandle& swapchain) const;
 
 		/**
 		 * @brief Returns the image width.
 		 *
-		 * @param image Image handle
+		 * @param[in] image Image handle
 		 * @return imageWidth
 		 */
         [[nodiscard]]
@@ -381,14 +507,6 @@ namespace vkcv
 		 */
 		[[nodiscard]]
 		DescriptorSetLayoutHandle createDescriptorSetLayout(const DescriptorBindings &bindings);
-		
-		/**
-		 * @brief Returns the descriptor set layout of a descriptor set layout handle.
-		 *
-		 * @param[in] handle Descriptor set layout handle
-		 * @return Descriptor set layout
-		 */
-		DescriptorSetLayout getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const;
 
 		/**
 		 * @brief Creates a new descriptor set
@@ -408,14 +526,6 @@ namespace vkcv
 		*/
 		void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes);
 
-		/**
-		 * @brief Returns information about a descriptor set
-		 * 
-		 * @param handle Handle of the descriptor set
-		 * @return Struct containing the descriptor set's vulkan handle, layout handle and descriptor pool index
-		*/
-		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
-
 
 		/**
 		 * @brief Start recording command buffers and increment frame index
@@ -426,7 +536,6 @@ namespace vkcv
 		 * @brief Records drawcalls to a command stream
 		 * 
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstants Push constants that are used for the drawcalls, ignored if constant size is set to 0
 		 * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor set bindings and instance count
@@ -434,60 +543,48 @@ namespace vkcv
 		 * @param windowHandle Window handle that is used to retrieve the corresponding swapchain
 		*/
 		void recordDrawcallsToCmdStream(
-			const CommandStreamHandle&      cmdStreamHandle,
-			const PassHandle&               renderpassHandle,
-			const GraphicsPipelineHandle    &pipelineHandle,
-			const PushConstants             &pushConstants,
-			const std::vector<DrawcallInfo> &drawcalls,
-			const std::vector<ImageHandle>  &renderTargets,
-			const WindowHandle              &windowHandle);
+			const CommandStreamHandle							&cmdStreamHandle,
+			const GraphicsPipelineHandle    					&pipelineHandle,
+			const PushConstants             					&pushConstants,
+			const std::vector<InstanceDrawcall>					&drawcalls,
+			const std::vector<ImageHandle>  					&renderTargets,
+			const WindowHandle              					&windowHandle);
 	
 		/**
 		 * @brief Records indirect drawcalls to a command stream
 		 *
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstantData Push constants that are used for the drawcalls, ignored if constant size is set to 0
-		 * @param compiledDescriptorSet TODO
-		 * @param compiledMesh TODO
-		 * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor set bindings and instance count
+		 * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor set bindings and draw count
 		 * @param renderTargets Image handles that are used as render targets
-		 * @param indirectBuffer TODO
-		 * @param drawCount TODO
 		 * @param windowHandle Window handle that is used to retrieve the corresponding swapchain
 		*/
-		void recordIndexedIndirectDrawcallsToCmdStream(
+		void recordIndirectDrawcallsToCmdStream(
 				const CommandStreamHandle                           cmdStreamHandle,
-				const PassHandle                                    renderpassHandle,
 				const GraphicsPipelineHandle                        &pipelineHandle,
 				const PushConstants                                 &pushConstantData,
-                const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
-				const vkcv::Mesh                                    &compiledMesh,
+				const std::vector<IndirectDrawcall>					&drawcalls,
 				const std::vector<ImageHandle>                      &renderTargets,
-				const vkcv::Buffer<vk::DrawIndexedIndirectCommand>  &indirectBuffer,
-				const uint32_t                                      drawCount,
 				const WindowHandle                                  &windowHandle);
 		
 		/**
 		 * @brief Records mesh shader drawcalls to a command stream
 		 *
 		 * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into
-		 * @param renderpassHandle Handle of the renderpass that is used for the drawcalls
 		 * @param pipelineHandle Handle of the pipeline that is used for the drawcalls
 		 * @param pushConstantData Push constants that are used for the drawcalls, ignored if constant size is set to 0
-		 * @param drawcalls Information about each drawcall, consisting of descriptor set bindings and task shader dispatch count
+		 * @param drawcalls Information about each drawcall, consisting of descriptor set bindings and task shader task count
 		 * @param renderTargets Image handles that are used as render targets
 		 * @param windowHandle Window handle that is used to retrieve the corresponding swapchain
 		*/
 		void recordMeshShaderDrawcalls(
-			const CommandStreamHandle&              cmdStreamHandle,
-			const PassHandle&                       renderpassHandle,
+			const CommandStreamHandle				&cmdStreamHandle,
 			const GraphicsPipelineHandle            &pipelineHandle,
-			const PushConstants&                    pushConstantData,
-            const std::vector<MeshShaderDrawcall>&  drawcalls,
-			const std::vector<ImageHandle>&         renderTargets,
-			const WindowHandle&                     windowHandle);
+			const PushConstants						&pushConstantData,
+            const std::vector<TaskDrawcall>			&drawcalls,
+			const std::vector<ImageHandle>			&renderTargets,
+			const WindowHandle						&windowHandle);
 		
         /**
          * Records the rtx ray generation to the @p cmdStreamHandle.
@@ -514,23 +611,22 @@ namespace vkcv
             vk::StridedDeviceAddressRegionKHR rcallRegion,
             const std::vector<DescriptorSetUsage>& descriptorSetUsages,
             const PushConstants& pushConstants,
-            const WindowHandle windowHandle);
+            const WindowHandle& windowHandle);
 
 		/**
 		 * @brief Record a compute shader dispatch into a command stream
 		 * 
 		 * @param cmdStream Handle of the command stream that the dispatch is recorded into
 		 * @param computePipeline Handle of the pipeline that is used for the dispatch
-		 * @param dispatchCount How many work groups are dispatched
-		 * @param descriptorSetUsages Descriptor set bindings of the dispatch
+		 * @param dispatchSize How many work groups are dispatched
+		 * @param descriptorSetUsages Descriptor set usages of the dispatch
 		 * @param pushConstants Push constant data for the dispatch
 		 */
-		void recordComputeDispatchToCmdStream(
-			CommandStreamHandle cmdStream,
-            ComputePipelineHandle computePipeline,
-			const uint32_t dispatchCount[3],
-			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
-			const PushConstants& pushConstants);
+		void recordComputeDispatchToCmdStream(const CommandStreamHandle& cmdStream,
+											  const ComputePipelineHandle& computePipeline,
+											  const DispatchSize& dispatchSize,
+											  const std::vector<DescriptorSetUsage> &descriptorSetUsages,
+											  const PushConstants& pushConstants);
 		
 		/**
 		 * @brief Record the start of a debug label into a command stream. 
@@ -573,20 +669,6 @@ namespace vkcv
 		 */
 		void endFrame( const WindowHandle& windowHandle );
 
-		/**
-		 * Submit a command buffer to any queue of selected type. The recording can be customized by a
-		 * custom record-command-function. If the command submission has finished, an optional finish-function
-		 * will be called.
-		 *
-		 * @param submitInfo Submit information
-		 * @param record Record-command-function
-		 * @param finish Finish-command-function or nullptr
-		 */
-		void recordAndSubmitCommandsImmediate(
-			const SubmitInfo            &submitInfo, 
-			const RecordCommandFunction &record, 
-			const FinishCommandFunction &finish);
-
 		/**
 		 * @brief Create a new command stream
 		 * 
@@ -603,7 +685,7 @@ namespace vkcv
 		 * @param finish Finish function, called after execution of commands is finished
 		 */
 		void recordCommandsToStream(
-			const CommandStreamHandle   cmdStreamHandle,
+			const CommandStreamHandle   &stream,
 			const RecordCommandFunction &record,
 			const FinishCommandFunction &finish);
 
@@ -613,7 +695,7 @@ namespace vkcv
 		 * @param[in] handle Command stream to submit
 		 * @param[in] signalRendering Flag to specify if the command stream finishes rendering
 		 */
-		void submitCommandStream(const CommandStreamHandle& handle,
+		void submitCommandStream(const CommandStreamHandle& stream,
 								 bool signalRendering = true);
 
 		/**
@@ -728,66 +810,185 @@ namespace vkcv
 		/**
 		 * @brief Sets a debug label to a buffer handle.
 		 *
-		 * @param handle Buffer handle
-		 * @param label Debug label
+		 * @param[in,out] handle Buffer handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const BufferHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a pass handle.
 		 *
-		 * @param handle Pass handle
-		 * @param label Debug label
+		 * @param[in,out] handle Pass handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const PassHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a graphics pipeline handle.
 		 *
-		 * @param handle Graphics pipeline handle
-		 * @param label Debug label
+		 * @param[in,out] handle Graphics pipeline handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const GraphicsPipelineHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a compute pipeline handle.
 		 *
-		 * @param handle Compute pipeline handle
-		 * @param label Debug label
+		 * @param[in,out] handle Compute pipeline handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const ComputePipelineHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a descriptor set handle.
 		 *
-		 * @param handle Descriptor set handle
-		 * @param label Debug label
+		 * @param[in,out] handle Descriptor set handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const DescriptorSetHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a sampler handle.
 		 *
-		 * @param handle Sampler handle
-		 * @param label Debug label
+		 * @param[in,out] handle Sampler handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const SamplerHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to an image handle.
 		 *
-		 * @param handle Image handle
-		 * @param label Debug label
+		 * @param[in,out] handle Image handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const ImageHandle &handle, const std::string &label);
 		
 		/**
 		 * @brief Sets a debug label to a command stream handle.
 		 *
-		 * @param handle Command stream handle
-		 * @param label Debug label
+		 * @param[in,out] handle Command stream handle
+		 * @param[in] label Debug label
 		 */
 		void setDebugLabel(const CommandStreamHandle &handle, const std::string &label);
 		
+		/**
+		 * @brief Runs the application in the current until all windows get closed.
+		 *
+		 * The frame callback will be called for each window every single frame.
+		 *
+		 * @param[in] frame Frame callback
+		 */
+		void run(const WindowFrameFunction &frame);
+		
+		/**
+		 * @brief Return the underlying vulkan handle for a render pass
+		 * by its given pass handle.
+		 *
+		 * @param[in] handle Pass handle
+		 * @return Vulkan render pass
+		 */
+		[[nodiscard]]
+		vk::RenderPass getVulkanRenderPass(const PassHandle &handle) const;
+		
+		/**
+		 * @brief Return the underlying vulkan handle for a pipeline
+		 * by its given graphics pipeline handle.
+		 *
+		 * @param[in] handle Graphics pipeline handle
+		 * @return Vulkan pipeline
+		 */
+		[[nodiscard]]
+		vk::Pipeline getVulkanPipeline(const GraphicsPipelineHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a pipeline
+		 * by its given compute pipeline handle.
+		 *
+		 * @param[in] handle Compute pipeline handle
+		 * @return Vulkan pipeline
+		 */
+		[[nodiscard]]
+		vk::Pipeline getVulkanPipeline(const ComputePipelineHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a descriptor set layout
+		 * by its given descriptor set layout handle.
+		 *
+		 * @param[in] handle Descriptor set layout handle
+		 * @return Vulkan descriptor set layout
+		 */
+		[[nodiscard]]
+		vk::DescriptorSetLayout getVulkanDescriptorSetLayout(const DescriptorSetLayoutHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a descriptor set
+		 * by its given descriptor set handle.
+		 *
+		 * @param[in] handle Descriptor set handle
+		 * @return Vulkan descriptor set
+		 */
+		[[nodiscard]]
+		vk::DescriptorSet getVulkanDescriptorSet(const DescriptorSetHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a buffer
+		 * by its given buffer handle.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @return Vulkan buffer
+		 */
+		[[nodiscard]]
+		vk::Buffer getVulkanBuffer(const BufferHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a sampler
+		 * by its given sampler handle.
+		 *
+		 * @param[in] handle Sampler handle
+		 * @return Vulkan sampler
+		 */
+		[[nodiscard]]
+		vk::Sampler getVulkanSampler(const SamplerHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a image
+		 * by its given image handle.
+		 *
+		 * @param[in] handle Image handle
+		 * @return Vulkan image
+		 */
+		[[nodiscard]]
+		vk::Image getVulkanImage(const ImageHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a image view
+		 * by its given image handle.
+		 *
+		 * @param[in] handle Image handle
+		 * @return Vulkan image view
+		 */
+		[[nodiscard]]
+		vk::ImageView getVulkanImageView(const ImageHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a device memory
+		 * by its given buffer handle.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @return Vulkan device memory
+		 */
+		[[nodiscard]]
+		vk::DeviceMemory getVulkanDeviceMemory(const BufferHandle &handle) const;
+	
+		/**
+		 * @brief Return the underlying vulkan handle for a device memory
+		 * by its given image handle.
+		 *
+		 * @param[in] handle Image handle
+		 * @return Vulkan device memory
+		 */
+		[[nodiscard]]
+		vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle &handle) const;
+		
     };
 }
diff --git a/include/vkcv/DescriptorBinding.hpp b/include/vkcv/DescriptorBinding.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0a8a670779bd8342995f9287770b857f3373bc8
--- /dev/null
+++ b/include/vkcv/DescriptorBinding.hpp
@@ -0,0 +1,31 @@
+#pragma once
+/**
+ * @authors Artur Wasmut, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Vanessa Karolek
+ * @file vkcv/DescriptorConfig.hpp
+ * @brief Structures to handle descriptor bindings.
+ */
+
+#include <unordered_map>
+
+#include "DescriptorTypes.hpp"
+#include "ShaderStage.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Structure to store details from a descriptor binding.
+	 */
+	struct DescriptorBinding {
+		uint32_t bindingID;
+		DescriptorType descriptorType;
+		uint32_t descriptorCount;
+		ShaderStages shaderStages;
+		bool variableCount;
+		bool partialBinding;
+		
+		bool operator ==(const DescriptorBinding &other) const;
+	};
+	
+	typedef std::unordered_map<uint32_t, DescriptorBinding> DescriptorBindings;
+	
+}
diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
deleted file mode 100644
index 8e4f3df2fad31f2a358f5f6b7f5ba4e0c14bd6e0..0000000000000000000000000000000000000000
--- a/include/vkcv/DescriptorConfig.hpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-/**
- * @authors Artur Wasmut, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Vanessa Karolek
- * @file vkcv/DescriptorConfig.hpp
- * @brief Structures to handle descriptor types and bindings.
- */
-
-#include <unordered_map>
-
-#include "Handles.hpp"
-#include "ShaderStage.hpp"
-#include "Logger.hpp"
-
-namespace vkcv
-{
-
-	/**
-	 * @brief Enum class to specify the type of a descriptor set binding.
-	 */
-    enum class DescriptorType {
-        UNIFORM_BUFFER,
-        STORAGE_BUFFER,
-        SAMPLER,
-        IMAGE_SAMPLED,
-		IMAGE_STORAGE,
-        UNIFORM_BUFFER_DYNAMIC,
-        STORAGE_BUFFER_DYNAMIC,
-        ACCELERATION_STRUCTURE_KHR
-    };
-
-    /**
-     * @brief Converts the descriptor type from the frameworks enumeration
-     * to the Vulkan type specifier.
-     *
-     * @param[in] type Descriptor type
-     * @return Vulkan descriptor type
-     */
-    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;
-            case DescriptorType::ACCELERATION_STRUCTURE_KHR:
-                return vk::DescriptorType::eAccelerationStructureKHR;
-            default:
-                return vk::DescriptorType::eMutableVALVE;
-        }
-    }
-	
-	/**
-	 * @brief Structure to store details from a descriptor binding.
-	 */
-    struct DescriptorBinding {
-        uint32_t bindingID;
-        DescriptorType descriptorType;
-        uint32_t descriptorCount;
-        ShaderStages shaderStages;
-        bool variableCount;
-		bool partialBinding;
-
-        bool operator ==(const DescriptorBinding &other) const;
-    };
-    
-    typedef std::unordered_map<uint32_t, DescriptorBinding> DescriptorBindings;
-	
-	/**
-	 * @brief Structure to store details about a descriptor set layout.
-	 */
-    struct DescriptorSetLayout {
-        vk::DescriptorSetLayout vulkanHandle;
-        DescriptorBindings descriptorBindings;
-        size_t layoutUsageCount;
-    };
-	
-	/**
-	 * @brief Structure to store details about a descriptor set.
-	 */
-    struct DescriptorSet {
-        vk::DescriptorSet vulkanHandle;
-        DescriptorSetLayoutHandle setLayoutHandle;
-        size_t poolIndex;
-    };
-	
-}
diff --git a/include/vkcv/DescriptorSetUsage.hpp b/include/vkcv/DescriptorSetUsage.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8bbf2c9625ae029665b20890a7978be867a81152
--- /dev/null
+++ b/include/vkcv/DescriptorSetUsage.hpp
@@ -0,0 +1,27 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/DescriptorUsage.hpp
+ * @brief Structures to handle descriptor usages.
+ */
+
+#include <vector>
+
+#include "Handles.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Structure to configure a descriptor set usage.
+	 */
+	struct DescriptorSetUsage {
+		uint32_t location;
+		DescriptorSetHandle descriptorSet;
+		std::vector<uint32_t> dynamicOffsets;
+	};
+	
+	DescriptorSetUsage useDescriptorSet(uint32_t location,
+										const DescriptorSetHandle &descriptorSet,
+										const std::vector<uint32_t> &dynamicOffsets = {});
+	
+}
diff --git a/include/vkcv/DescriptorTypes.hpp b/include/vkcv/DescriptorTypes.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eaa739d6d33359b7306254677e323fb1827ca5f0
--- /dev/null
+++ b/include/vkcv/DescriptorTypes.hpp
@@ -0,0 +1,57 @@
+#pragma once
+/**
+ * @authors Artur Wasmut, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Vanessa Karolek
+ * @file vkcv/DescriptorConfig.hpp
+ * @brief Enum classes to handle descriptor types.
+ */
+ 
+#include <vulkan/vulkan.hpp>
+ 
+namespace vkcv {
+	
+	/**
+	 * @brief Enum class to specify the type of a descriptor set binding.
+	 */
+	enum class DescriptorType {
+		UNIFORM_BUFFER,
+		STORAGE_BUFFER,
+		SAMPLER,
+		IMAGE_SAMPLED,
+		IMAGE_STORAGE,
+		UNIFORM_BUFFER_DYNAMIC,
+		STORAGE_BUFFER_DYNAMIC,
+		ACCELERATION_STRUCTURE_KHR
+	};
+	
+	/**
+     * @brief Converts the descriptor type from the frameworks enumeration
+     * to the Vulkan type specifier.
+     *
+     * @param[in] type Descriptor type
+     * @return Vulkan descriptor type
+     */
+	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;
+			case DescriptorType::ACCELERATION_STRUCTURE_KHR:
+				return vk::DescriptorType::eAccelerationStructureKHR;
+			default:
+				return vk::DescriptorType::eMutableVALVE;
+		}
+	}
+
+}
diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp
index eb532119fabe394202a410b9a166b0d190dabbf3..869b1061af8f8da217978f962fd68d41ba289cad 100644
--- a/include/vkcv/DescriptorWrites.hpp
+++ b/include/vkcv/DescriptorWrites.hpp
@@ -6,6 +6,7 @@
  */
 
 #include <vector>
+#include <vulkan/vulkan.hpp>
 
 #include "Handles.hpp"
 
@@ -60,6 +61,7 @@ namespace vkcv {
 	 */
 	struct AccelerationDescriptorWrite {
 	    uint32_t binding;
+		std::vector<vk::AccelerationStructureKHR> structures;
 	};
 
 	/**
@@ -162,9 +164,11 @@ namespace vkcv {
 		 * of a descriptor set.
 		 *
 		 * @param[in] binding Binding index
+		 * @param[in] structures Acceleration structures
 		 * @return Instance of descriptor writes
 		 */
-		DescriptorWrites& writeAcceleration(uint32_t binding);
+		DescriptorWrites& writeAcceleration(uint32_t binding,
+											const std::vector<vk::AccelerationStructureKHR> &structures);
 		
 		/**
 		 * @brief Returns the list of stored write entries for sampled images.
diff --git a/include/vkcv/DispatchSize.hpp b/include/vkcv/DispatchSize.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..39f1195046dc2d7c47c9994f5beb34b732ffd218
--- /dev/null
+++ b/include/vkcv/DispatchSize.hpp
@@ -0,0 +1,115 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/DispatchSize.hpp
+ * @brief Class to handle dispatch sizes.
+ */
+ 
+#include <array>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	
+	/**
+	 * @brief Class representing a dispatch size to invoke a compute pipeline with.
+	 */
+	class DispatchSize final {
+	private:
+		std::array<uint32_t, 3> m_Dispatch;
+		
+	public:
+		/**
+		 * Implicit constructor to convert an unsigned integer to a
+		 * one dimensional dispatch size.
+		 *
+		 * @param[in] count Count of invocations
+		 */
+		DispatchSize(uint32_t count);
+		
+		/**
+		 * Explicit constructor to create a dispatch size with two or
+		 * three dimensions setting each value specifically.
+		 *
+		 * @param[in] dimensionX Size of X dimension
+		 * @param[in] dimentionY Size of Y dimension
+		 * @param[in] dimensionZ Size of Z dimension (optional)
+		 */
+		DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ = 1);
+		
+		DispatchSize(const DispatchSize& other) = default;
+		DispatchSize(DispatchSize&& other) = default;
+		
+		~DispatchSize() = default;
+		
+		DispatchSize& operator=(const DispatchSize& other) = default;
+		DispatchSize& operator=(DispatchSize&& other) = default;
+		
+		/**
+		 * Returns the raw data of the dispatch size as readonly unsigned
+		 * integer pointer.
+		 *
+		 * @return Pointer to data
+		 */
+		[[nodiscard]]
+		const uint32_t* data() const;
+		
+		/**
+		 * Returns the size value of the dispatch size by a given index.
+		 *
+		 * @param[in] index Size index
+		 * @return Size value by index
+		 */
+		[[nodiscard]]
+		uint32_t operator[](size_t index) const;
+		
+		/**
+		 * Returns the value for the X dimension of the dispatch size.
+		 *
+		 * @return Size of X dimension
+		 */
+		[[nodiscard]]
+		uint32_t x() const;
+		
+		/**
+		 * Returns the value for the Y dimension of the dispatch size.
+		 *
+		 * @return Size of Y dimension
+		 */
+		[[nodiscard]]
+		uint32_t y() const;
+		
+		/**
+		 * Returns the value for the Z dimension of the dispatch size.
+		 *
+		 * @return Size of Z dimension
+		 */
+		[[nodiscard]]
+		uint32_t z() const;
+		
+		/**
+		 * Checks whether the dispatch size is valid for compute shader
+		 * invocations and returns the result as boolean value.
+		 *
+		 * @return True if the dispatch size is valid, otherwise false.
+		 */
+		bool check() const;
+		
+	};
+	
+	/**
+	 * Returns the proper dispatch size by dividing a global amount of invocations
+	 * as three dimensional dispatch size into invocations of a fixed group size
+	 * for the used work groups of the compute shader.
+	 *
+	 * This function will generate over fitted results to make sure all global
+	 * invocations get computed. So make sure the used compute shader handles those
+	 * additional invocations out of bounds from the original global invocations.
+	 *
+	 * @param[in] globalInvocations Size of planned global invocations
+	 * @param[in] groupSize Size of work group in compute stage
+	 * @return Dispatch size respecting the actual work group size
+	 */
+	[[nodiscard]]
+	DispatchSize dispatchInvocations(DispatchSize globalInvocations, DispatchSize groupSize);
+	
+}
diff --git a/include/vkcv/Drawcall.hpp b/include/vkcv/Drawcall.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..50ba78d223faaaafee51b9a0a0af0650babfa341
--- /dev/null
+++ b/include/vkcv/Drawcall.hpp
@@ -0,0 +1,103 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/Drawcall.hpp
+ * @brief Classes to define different drawcalls.
+ */
+
+#include <vector>
+
+#include "DescriptorSetUsage.hpp"
+#include "Handles.hpp"
+#include "VertexData.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Base class to store details for a general drawcall.
+	 */
+	class Drawcall {
+	private:
+		std::vector<DescriptorSetUsage> m_usages;
+	
+	public:
+		Drawcall() = default;
+		
+		Drawcall(const Drawcall& other) = default;
+		Drawcall(Drawcall&& other) noexcept = default;
+		
+		~Drawcall() = default;
+		
+		Drawcall& operator=(const Drawcall& other) = default;
+		Drawcall& operator=(Drawcall&& other) noexcept = default;
+		
+		[[nodiscard]]
+		const std::vector<DescriptorSetUsage>& getDescriptorSetUsages() const;
+		
+		void useDescriptorSet(uint32_t location,
+							  const DescriptorSetHandle &descriptorSet,
+							  const std::vector<uint32_t> &dynamicOffsets = {});
+		
+	};
+	
+	/**
+	 * @brief Class to store details for an instance drawcall.
+	 */
+	class InstanceDrawcall : public Drawcall {
+	private:
+		VertexData m_vertexData;
+		uint32_t m_instanceCount;
+		
+	public:
+		explicit InstanceDrawcall(const VertexData& vertexData,
+								  uint32_t instanceCount = 1);
+		
+		[[nodiscard]]
+		const VertexData& getVertexData() const;
+		
+		[[nodiscard]]
+		uint32_t getInstanceCount() const;
+	
+	};
+	
+	/**
+	 * @brief Class to store details for an indirect drawcall.
+	 */
+	class IndirectDrawcall : public Drawcall {
+	private:
+		BufferHandle m_indirectDrawBuffer;
+		VertexData m_vertexData;
+		uint32_t m_drawCount;
+	
+	public:
+		explicit IndirectDrawcall(const BufferHandle &indirectDrawBuffer,
+								  const VertexData& vertexData,
+								  uint32_t drawCount = 1);
+		
+		[[nodiscard]]
+		BufferHandle getIndirectDrawBuffer() const;
+		
+		[[nodiscard]]
+		const VertexData& getVertexData() const;
+		
+		[[nodiscard]]
+		uint32_t getDrawCount() const;
+		
+	};
+	
+	/**
+	 * @brief Class to store details for a task drawcall.
+	 */
+	class TaskDrawcall : public Drawcall {
+	private:
+		uint32_t m_taskCount;
+		
+	public:
+		explicit TaskDrawcall(uint32_t taskCount = 1);
+		
+		[[nodiscard]]
+		uint32_t getTaskCount() const;
+		
+	};
+	
+}
diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp
deleted file mode 100644
index 76b94dc8b4f8595d98e87920329eb616d703f0db..0000000000000000000000000000000000000000
--- a/include/vkcv/DrawcallRecording.hpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#pragma once
-/**
- * @authors Sebastian Gaida, Alexander Gauggel, Artur Wasmut, Tobias Frisch
- * @file vkcv/DrawcallRecording.hpp
- * @brief Structures and functions to record drawcalls.
- */
-
-#include <vulkan/vulkan.hpp>
-
-#include "Handles.hpp"
-#include "DescriptorConfig.hpp"
-#include "PushConstants.hpp"
-
-#include "Buffer.hpp"
-
-namespace vkcv {
-	
-	/**
-	 * @brief Structure to store details about a vertex buffer binding.
-	 */
-    struct VertexBufferBinding {
-        vk::DeviceSize offset;
-        vk::Buffer buffer;
-    };
-
-	/**
-	 * @brief Enum class to specify the size of indexes.
-	 */
-    enum class IndexBitCount {
-		Bit8,
-        Bit16,
-        Bit32
-    };
-	
-	/**
-	 * @brief Structure to configure a descriptor set usage.
-	 */
-    struct DescriptorSetUsage {
-        inline DescriptorSetUsage(uint32_t setLocation, DescriptorSetHandle descriptorSet,
-								  const std::vector<uint32_t>& dynamicOffsets = {}) noexcept :
-			setLocation(setLocation),
-			descriptorSet(descriptorSet),
-			dynamicOffsets(dynamicOffsets) {}
-
-        const uint32_t          	setLocation;
-        const DescriptorSetHandle 	descriptorSet;
-        const std::vector<uint32_t> dynamicOffsets;
-    };
-	
-	/**
-	 * @brief Structure to store details of a mesh to draw.
-	 */
-    struct Mesh {
-        inline Mesh() {}
-
-        inline Mesh(std::vector<VertexBufferBinding> vertexBufferBindings,
-					vk::Buffer indexBuffer,
-					size_t indexCount,
-					IndexBitCount indexBitCount = IndexBitCount::Bit16) noexcept :
-			vertexBufferBindings(vertexBufferBindings),
-			indexBuffer(indexBuffer),
-            indexCount(indexCount),
-            indexBitCount(indexBitCount) {}
-
-        std::vector<VertexBufferBinding> vertexBufferBindings;
-        vk::Buffer indexBuffer;
-        size_t indexCount;
-        IndexBitCount indexBitCount;
-
-    };
-	
-	/**
-	 * @brief Structure to store details for a drawcall.
-	 */
-    struct DrawcallInfo {
-        inline DrawcallInfo(const Mesh& mesh,
-							const std::vector<DescriptorSetUsage>& descriptorSets,
-							const uint32_t instanceCount = 1) :
-			mesh(mesh),
-			descriptorSets(descriptorSets),
-			instanceCount(instanceCount){}
-
-        Mesh mesh;
-        std::vector<DescriptorSetUsage> descriptorSets;
-        uint32_t instanceCount;
-    };
-	
-	/**
-	 * @brief Structure to store details for a mesh shader drawcall.
-	 */
-    struct MeshShaderDrawcall {
-        std::vector<DescriptorSetUsage> descriptorSets;
-        uint32_t taskCount;
-    };
-
-    void recordMeshShaderDrawcall(const Core& core,
-								  vk::CommandBuffer cmdBuffer,
-								  vk::PipelineLayout pipelineLayout,
-								  const PushConstants& pushConstantData,
-								  uint32_t pushConstantOffset,
-								  const MeshShaderDrawcall& drawcall,
-								  uint32_t firstTask);
-	
-}
diff --git a/include/vkcv/CommandRecordingFunctionTypes.hpp b/include/vkcv/EventFunctionTypes.hpp
similarity index 61%
rename from include/vkcv/CommandRecordingFunctionTypes.hpp
rename to include/vkcv/EventFunctionTypes.hpp
index d8cda4216e6fbd5686240f52fa0838650ef409df..5bfb29d6570c4666c13125fab0774ed264e20cb9 100644
--- a/include/vkcv/CommandRecordingFunctionTypes.hpp
+++ b/include/vkcv/EventFunctionTypes.hpp
@@ -2,12 +2,13 @@
 /**
  * @authors Alexander Gauggel, Tobias Frisch
  * @file vkcv/CommandRecordingFunctionTypes.hpp
- * @brief Abstract function types to handle command recording.
+ * @brief Abstract function types to handle command recording for example.
  */
 
 #include <vulkan/vulkan.hpp>
 
 #include "Event.hpp"
+#include "Handles.hpp"
 
 namespace vkcv {
 	
@@ -21,4 +22,10 @@ namespace vkcv {
 	 */
 	typedef typename event_function<>::type FinishCommandFunction;
 	
+	/**
+	 * @brief Function to be called each frame for every open window.
+	 */
+	typedef typename event_function<const WindowHandle&, double, double, uint32_t, uint32_t>::type 
+	        WindowFrameFunction;
+	
 }
\ No newline at end of file
diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp
index e15ad7670795d4ffaf879248612bbbc5cf8cfb74..0ed0d440e144032ec69f84f12d742d82a6849b8b 100644
--- a/include/vkcv/FeatureManager.hpp
+++ b/include/vkcv/FeatureManager.hpp
@@ -304,6 +304,16 @@ namespace vkcv {
 		[[nodiscard]]
 		bool checkSupport(const vk::PhysicalDeviceRayTracingPipelineFeaturesKHR& features, bool required) const;
 		
+		/**
+         * @brief Checks support of the @p vk::PhysicalDeviceVulkan13Features.
+         *
+         * @param[in] features The features
+         * @param[in] required True, if the @p features are required, else false
+         * @return @p True, if the @p features are supported, else @p false
+         */
+		[[nodiscard]]
+		bool checkSupport(const vk::PhysicalDeviceVulkan13Features& features, bool required) const;
+		
 		/**
 		 * @brief Searches for a base structure of a given structure type.
 		 *
@@ -411,7 +421,7 @@ namespace vkcv {
 			
 			if (!checkSupport(features, required)) {
 				if (required) {
-					throw std::runtime_error("Required feature is not supported!");
+					vkcv_log_throw_error("Required feature is not supported!");
 				}
 				
 				return false;
diff --git a/include/vkcv/GraphicsPipelineConfig.hpp b/include/vkcv/GraphicsPipelineConfig.hpp
index 85cabc135ddfa8e6e6762c16c269741e232cff4d..91e9fa75cb553ed2b98edfb363192d071ec1bd09 100644
--- a/include/vkcv/GraphicsPipelineConfig.hpp
+++ b/include/vkcv/GraphicsPipelineConfig.hpp
@@ -2,16 +2,15 @@
 /**
  * @authors Mara Vogt, Mark Mints, Tobias Frisch
  * @file vkcv/GraphicsPipelineConfig.hpp
- * @brief Graphics Pipeline Config Struct to hand over required information to Pipeline Creation
+ * @brief Graphics pipeline config struct to hand over required information to pipeline creation.
  */
 
 #include <vector>
 #include <cstdint>
 
-#include "Handles.hpp"
-#include "ShaderProgram.hpp"
+#include "PipelineConfig.hpp"
 #include "VertexLayout.hpp"
-#include "ImageConfig.hpp"
+#include "Multisampling.hpp"
 
 namespace vkcv {
 
@@ -58,26 +57,104 @@ namespace vkcv {
 	};
 	
 	/**
-	 * @brief Structure to configure a graphics pipeline before its creation.
+	 * @brief Class to configure a graphics pipeline before its creation.
 	 */
-    struct GraphicsPipelineConfig {
-        ShaderProgram                         	m_ShaderProgram;
-        uint32_t                              	m_Width;
-		uint32_t                              	m_Height;
-        PassHandle                            	m_PassHandle;
-        VertexLayout                          	m_VertexLayout;
-        std::vector<DescriptorSetLayoutHandle>	m_DescriptorLayouts;
-        bool                                  	m_UseDynamicViewport;
-        bool                                  	m_UseConservativeRasterization 	= false;
-        PrimitiveTopology                     	m_PrimitiveTopology 			= PrimitiveTopology::TriangleList;
-		BlendMode                             	m_blendMode 					= BlendMode::None;
-        bool                                    m_EnableDepthClamping           = false;
-        Multisampling                           m_multisampling                 = Multisampling::None;
-        CullMode                                m_culling                       = CullMode::None;
-        DepthTest                               m_depthTest                     = DepthTest::LessEqual;
-        bool                                    m_depthWrite                    = true;
-        bool                                    m_alphaToCoverage               = false;
-		uint32_t								m_tessellationControlPoints		= 0;
+    class GraphicsPipelineConfig : public PipelineConfig {
+	private:
+		PassHandle m_PassHandle;
+		VertexLayout m_VertexLayout;
+		
+        uint32_t m_Width;
+		uint32_t m_Height;
+		
+        bool 				m_UseConservativeRasterization 	= false;
+        PrimitiveTopology 	m_PrimitiveTopology 			= PrimitiveTopology::TriangleList;
+		BlendMode 			m_blendMode 					= BlendMode::None;
+        bool 				m_EnableDepthClamping 			= false;
+        CullMode 			m_Culling                       = CullMode::None;
+        DepthTest 			m_DepthTest                     = DepthTest::LessEqual;
+        bool 				m_DepthWrite 					= true;
+        bool 				m_AlphaToCoverage 				= false;
+		uint32_t 			m_TessellationControlPoints 	= 0;
+		
+	public:
+		GraphicsPipelineConfig();
+	
+		GraphicsPipelineConfig(const ShaderProgram& program,
+							   const PassHandle& pass,
+							   const VertexLayout& vertexLayout,
+							   const std::vector<DescriptorSetLayoutHandle>& layouts);
+	
+		GraphicsPipelineConfig(const GraphicsPipelineConfig &other) = default;
+		GraphicsPipelineConfig(GraphicsPipelineConfig &&other) = default;
+	
+		~GraphicsPipelineConfig() = default;
+	
+		GraphicsPipelineConfig& operator=(const GraphicsPipelineConfig &other) = default;
+		GraphicsPipelineConfig& operator=(GraphicsPipelineConfig &&other) = default;
+	
+		[[nodiscard]]
+		const PassHandle& getPass() const;
+	
+		[[nodiscard]]
+		const VertexLayout& getVertexLayout() const;
+	
+		[[nodiscard]]
+		uint32_t getWidth() const;
+	
+		[[nodiscard]]
+		uint32_t getHeight() const;
+		
+		void setResolution(uint32_t width, uint32_t height);
+		
+		[[nodiscard]]
+		bool isViewportDynamic() const;
+	
+		[[nodiscard]]
+		bool isUsingConservativeRasterization() const;
+		
+		void setUsingConservativeRasterization(bool conservativeRasterization);
+	
+		[[nodiscard]]
+		PrimitiveTopology getPrimitiveTopology() const;
+		
+		void setPrimitiveTopology(PrimitiveTopology primitiveTopology);
+	
+		[[nodiscard]]
+		BlendMode getBlendMode() const;
+		
+		void setBlendMode(BlendMode blendMode);
+	
+		[[nodiscard]]
+		bool isDepthClampingEnabled() const;
+		
+		void setDepthClampingEnabled(bool depthClamping);
+	
+		[[nodiscard]]
+		CullMode getCulling() const;
+		
+		void setCulling(CullMode cullMode);
+	
+		[[nodiscard]]
+		DepthTest getDepthTest() const;
+		
+		void setDepthTest(DepthTest depthTest);
+	
+		[[nodiscard]]
+		bool isWritingDepth() const;
+		
+		void setWritingDepth(bool writingDepth);
+	
+		[[nodiscard]]
+		bool isWritingAlphaToCoverage() const;
+		
+		void setWritingAlphaToCoverage(bool alphaToCoverage);
+		
+		[[nodiscard]]
+		uint32_t getTesselationControlPoints() const;
+		
+		void setTesselationControlPoints(uint32_t tessellationControlPoints);
+		
     };
 
 }
\ No newline at end of file
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index dea1793871a320016cf7c0da3f3877fc66a6f616..52ccb7a84a5d82bf51c3285c6536d6ce9aa0bc48 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -130,23 +130,23 @@ namespace vkcv
     private:
         using Handle::Handle;
     };
-
-    /**
-     * @brief Handle class for descriptor sets.
+	
+	/**
+     * @brief Handle class for descriptor set layouts.
      */
-	class DescriptorSetHandle : public Handle {
-		friend class DescriptorManager;
+	class DescriptorSetLayoutHandle : public Handle {
+		friend class DescriptorSetLayoutManager;
 	private:
 		using Handle::Handle;
 	};
 
     /**
-     * @brief Handle class for descriptor set layouts.
+     * @brief Handle class for descriptor sets.
      */
-	class DescriptorSetLayoutHandle : public Handle {
-	    friend class DescriptorManager;
+	class DescriptorSetHandle : public Handle {
+		friend class DescriptorSetManager;
 	private:
-	    using Handle::Handle;
+		using Handle::Handle;
 	};
 
     /**
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index 6e7b6e57950b3ccdbc341d660a223bd7885ddb1b..542658ae6aeb56b5221ca5f97504e955eaf0e478 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -7,13 +7,14 @@
  
 #include <vulkan/vulkan.hpp>
 
+#include "BufferTypes.hpp"
+#include "Core.hpp"
 #include "Handles.hpp"
-#include "ImageConfig.hpp"
+#include "Multisampling.hpp"
 
 namespace vkcv {
 	
 	class Downsampler;
-	class ImageManager;
 
 	/**
 	 * @brief Returns whether an image format is usable as depth buffer.
@@ -23,6 +24,15 @@ namespace vkcv {
 	 * otherwise false.
 	 */
 	bool isDepthFormat(const vk::Format format);
+	
+	/**
+	 * @brief Returns whether an image format is usable as stencil buffer.
+	 *
+	 * @param format Vulkan image format
+	 * @return True, if the format is valid to use as stencil buffer,
+	 * otherwise false.
+	 */
+	bool isStencilFormat(const vk::Format format);
 
     /**
      * @brief Class for image handling and filling data.
@@ -30,6 +40,17 @@ namespace vkcv {
 	class Image {
 		friend class Core;
 	public:
+		Image() : m_core(nullptr), m_handle() {};
+		
+		Image(Core* core, const ImageHandle& handle) : m_core(core), m_handle(handle) {}
+		
+		Image(const Image& other) = default;
+		Image(Image&& other) = default;
+		
+		~Image() = default;
+		
+		Image& operator=(const Image& other) = default;
+		Image& operator=(Image&& other) = default;
 		
 		/**
 		 * @brief Returns the format of the image.
@@ -77,7 +98,7 @@ namespace vkcv {
 		 * @return Number of mip levels
 		 */
 		[[nodiscard]]
-		uint32_t getMipCount() const;
+		uint32_t getMipLevels() const;
 
 		/**
 		 * @brief Switches the image layout,
@@ -107,39 +128,19 @@ namespace vkcv {
 									  Downsampler &downsampler);
 		
 	private:
-	    // TODO: const qualifier removed, very hacky!!!
-	    //  Else you cannot recreate an image. Pls fix.
-		ImageManager* m_manager;
+		Core* m_core;
 		ImageHandle m_handle;
 
-		Image(ImageManager* manager, const ImageHandle& handle);
-		
-		/**
-		 * @brief Creates an image with given parameters like width, height,
-		 * depth, amount of mip levels and others.
-		 *
-		 * @param[in,out] manager Image manager
-		 * @param[in] format Vulkan image format
-		 * @param[in] width Width of the image
-		 * @param[in] height Height of the image
-		 * @param[in] depth Depth of the image
-		 * @param[in] mipCount Amount of mip levels
-		 * @param[in] supportStorage Support of storage
-		 * @param[in] supportColorAttachment Support of color attachment
-		 * @param[in] msaa MSAA mode
-		 * @return New created image
-		 */
-		static Image create(
-			ImageManager*   manager,
-			vk::Format      format,
-			uint32_t        width,
-			uint32_t        height,
-			uint32_t        depth,
-			uint32_t        mipCount,
-			bool            supportStorage,
-			bool            supportColorAttachment,
-			Multisampling   msaa);
-
 	};
 	
+	Image image(Core &core,
+				vk::Format format,
+				uint32_t width,
+				uint32_t height,
+				uint32_t depth=1,
+				bool createMipChain=false,
+				bool supportStorage=false,
+				bool supportColorAttachment=false,
+				Multisampling multisampling=Multisampling::None);
+	
 }
diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp
index 9d4fa86f2b42e8190e7dc57c2ad897a76f945126..a1805651323f8c9200d16a6a7e999c82c3dc3c90 100644
--- a/include/vkcv/Logger.hpp
+++ b/include/vkcv/Logger.hpp
@@ -6,6 +6,7 @@
  */
 
 #include <cstdio>
+#include <exception>
 
 namespace vkcv {
 	
@@ -113,4 +114,39 @@ namespace vkcv {
 #define vkcv_log(level, ...) {}
 #endif
 
+/**
+ * @brief Macro-function to log the message of any error
+ * or an exception.
+ *
+ * @param[in] error Error or exception
+ */
+#define vkcv_log_error(error) {                    \
+  vkcv_log(LogLevel::ERROR, "%s", (error).what()); \
+}
+
+/**
+ * @brief Macro-function to throw and log any error or
+ * an exception.
+ *
+ * @param[in] error Error or exception
+ */
+#define vkcv_log_throw(error) {       \
+  try {                               \
+    throw error;                      \
+  } catch (const std::exception& e) { \
+    vkcv_log_error(e);                \
+    throw;                            \
+  }                                   \
+}
+
+/**
+ * @brief Macro-function to throw and log an error
+ * with its custom message.
+ *
+ * @param[in] message Error message
+ */
+#define vkcv_log_throw_error(message) {        \
+  vkcv_log_throw(std::runtime_error(message)); \
+}
+
 }
diff --git a/include/vkcv/ImageConfig.hpp b/include/vkcv/Multisampling.hpp
similarity index 81%
rename from include/vkcv/ImageConfig.hpp
rename to include/vkcv/Multisampling.hpp
index d031f790622f1f0595c540c5bbcf0d8795f5fd95..3bcf5d7b2a8dd838fcb18817a611086f33b06b68 100644
--- a/include/vkcv/ImageConfig.hpp
+++ b/include/vkcv/Multisampling.hpp
@@ -9,7 +9,12 @@
 
 namespace vkcv {
 	
-	enum class Multisampling { None, MSAA2X, MSAA4X, MSAA8X };
+	enum class Multisampling {
+		None,
+		MSAA2X,
+		MSAA4X,
+		MSAA8X
+	};
 
 	/**
 	 * @brief Returns the sample count flag bits of a given
@@ -18,7 +23,7 @@ namespace vkcv {
 	 * @param[in] msaa MSAA mode
 	 * @return Sample count flag bits
 	 */
-	vk::SampleCountFlagBits msaaToVkSampleCountFlag(Multisampling msaa);
+	vk::SampleCountFlagBits msaaToSampleCountFlagBits(Multisampling msaa);
 	
 	/**
 	 * @brief Returns the amount of samples of a given
diff --git a/include/vkcv/Pass.hpp b/include/vkcv/Pass.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f931eef50d0c78d015ef08472b5513942773edc3
--- /dev/null
+++ b/include/vkcv/Pass.hpp
@@ -0,0 +1,31 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/Pass.hpp
+ * @brief Support functions for basic pass creation.
+ */
+
+#include "Core.hpp"
+#include "Handles.hpp"
+#include "PassConfig.hpp"
+
+namespace vkcv
+{
+
+	PassHandle passFormats(Core &core,
+						   const std::vector<vk::Format> formats,
+						   bool clear = true,
+						   Multisampling multisampling = Multisampling::None);
+	
+	PassHandle passFormat(Core &core,
+						  vk::Format format,
+						  bool clear = true,
+						  Multisampling multisampling = Multisampling::None);
+	
+	PassHandle passSwapchain(Core &core,
+							 const SwapchainHandle &swapchain,
+							 const std::vector<vk::Format> formats,
+							 bool clear = true,
+							 Multisampling multisampling = Multisampling::None);
+	
+}
diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp
index ad3e3c0dff150f5ba41859477d4bb5a53c4365f8..849a6e837acd6321afed6c99dc3b608ba26766b7 100644
--- a/include/vkcv/PassConfig.hpp
+++ b/include/vkcv/PassConfig.hpp
@@ -8,29 +8,10 @@
 #include <vector>
 #include <vulkan/vulkan.hpp>
 
-#include "ImageConfig.hpp"
+#include "Multisampling.hpp"
 
 namespace vkcv
 {
-	
-	/**
-	 * @brief Enum class to specify kinds of attachment layouts.
-	 */
-    enum class AttachmentLayout {
-        UNDEFINED,
-        GENERAL,
-
-        COLOR_ATTACHMENT,
-        SHADER_READ_ONLY,
-
-        DEPTH_STENCIL_ATTACHMENT,
-        DEPTH_STENCIL_READ_ONLY,
-
-        TRANSFER_SRC,
-        TRANSFER_DST,
-
-        PRESENTATION
-    };
 
 	/**
 	 * @brief Enum class to specify types of attachment operations.
@@ -43,20 +24,82 @@ namespace vkcv
     };
 
 	/**
-	 * @brief Structure to store details about an attachment of a pass.
+	 * @brief Class to store details about an attachment of a pass.
 	 */
-    struct AttachmentDescription {
-        AttachmentOperation store_operation;
-        AttachmentOperation load_operation;
-        vk::Format format;
+    class AttachmentDescription {
+	private:
+		vk::Format m_format;
+	
+		AttachmentOperation m_load_op;
+        AttachmentOperation m_store_op;
+		
+		vk::ClearValue m_clear_value;
+	public:
+		AttachmentDescription(vk::Format format,
+							  AttachmentOperation load,
+							  AttachmentOperation store);
+		
+		AttachmentDescription(vk::Format format,
+							  AttachmentOperation load,
+							  AttachmentOperation store,
+							  const vk::ClearValue &clear);
+	
+		AttachmentDescription(const AttachmentDescription &other) = default;
+		AttachmentDescription(AttachmentDescription &&other) = default;
+		
+		~AttachmentDescription() = default;
+	
+		AttachmentDescription& operator=(const AttachmentDescription &other) = default;
+		AttachmentDescription& operator=(AttachmentDescription &&other) = default;
+	
+		[[nodiscard]]
+		vk::Format getFormat() const;
+	
+		[[nodiscard]]
+		AttachmentOperation getLoadOperation() const;
+	
+		[[nodiscard]]
+		AttachmentOperation getStoreOperation() const;
+		
+		void setClearValue(const vk::ClearValue &clear);
+	
+		[[nodiscard]]
+		const vk::ClearValue& getClearValue() const;
+		
     };
+	
+	using AttachmentDescriptions = std::vector<AttachmentDescription>;
 
 	/**
-	 * @brief Structure to configure a pass for usage.
+	 * @brief Class to configure a pass for usage.
 	 */
-    struct PassConfig {
-		std::vector<AttachmentDescription> attachments;
-        Multisampling msaa;
+    class PassConfig {
+	private:
+		AttachmentDescriptions m_attachments;
+        Multisampling m_multisampling;
+		
+	public:
+		PassConfig();
+		
+		explicit PassConfig(const AttachmentDescriptions &attachments,
+							Multisampling multisampling = Multisampling::None);
+		
+		PassConfig(const PassConfig &other) = default;
+		PassConfig(PassConfig &&other) = default;
+		
+		~PassConfig() = default;
+	
+		PassConfig& operator=(const PassConfig &other) = default;
+		PassConfig& operator=(PassConfig &&other) = default;
+		
+		[[nodiscard]]
+		const AttachmentDescriptions& getAttachments() const;
+		
+		void setMultisampling(Multisampling multisampling);
+	
+		[[nodiscard]]
+		Multisampling getMultisampling() const;
+		
     };
 	
 }
\ No newline at end of file
diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e53985f77a454891dd4d1fc69abcb2c0eb344912
--- /dev/null
+++ b/include/vkcv/PipelineConfig.hpp
@@ -0,0 +1,51 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/PipelineConfig.hpp
+ * @brief Pipeline config class to hand over required information to pipeline creation
+ */
+
+#include <vector>
+
+#include "Handles.hpp"
+#include "ShaderProgram.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Class to configure a general pipeline before its creation.
+	 */
+	class PipelineConfig {
+	private:
+		ShaderProgram m_ShaderProgram;
+		std::vector<DescriptorSetLayoutHandle> m_DescriptorSetLayouts;
+		
+	public:
+		PipelineConfig();
+		
+		PipelineConfig(const ShaderProgram& program,
+					   const std::vector<DescriptorSetLayoutHandle>& layouts);
+		
+		PipelineConfig(const PipelineConfig &other) = default;
+		PipelineConfig(PipelineConfig &&other) = default;
+		
+		~PipelineConfig() = default;
+		
+		PipelineConfig& operator=(const PipelineConfig &other) = default;
+		PipelineConfig& operator=(PipelineConfig &&other) = default;
+		
+		void setShaderProgram(const ShaderProgram& program);
+		
+		[[nodiscard]]
+		const ShaderProgram& getShaderProgram() const;
+		
+		void addDescriptorSetLayout(const DescriptorSetLayoutHandle& layout);
+		
+		void addDescriptorSetLayouts(const std::vector<DescriptorSetLayoutHandle>& layouts);
+		
+		[[nodiscard]]
+		const std::vector<DescriptorSetLayoutHandle>& getDescriptorSetLayouts() const;
+		
+	};
+	
+}
diff --git a/include/vkcv/PushConstants.hpp b/include/vkcv/PushConstants.hpp
index ca826ea52e9bcee72c14c26496c99937c27fb775..8d441bfcb57807eedc94d4cf839f6e99729d1cbe 100644
--- a/include/vkcv/PushConstants.hpp
+++ b/include/vkcv/PushConstants.hpp
@@ -9,24 +9,21 @@
 #include <vulkan/vulkan.hpp>
 
 #include "Logger.hpp"
+#include "TypeGuard.hpp"
 
 namespace vkcv {
 
     /**
      * @brief Class to handle push constants data per drawcall.
      */
-	class PushConstants {
+	class PushConstants final {
 	private:
+		TypeGuard m_typeGuard;
 		std::vector<uint8_t> m_data;
-		size_t m_sizePerDrawcall;
 		
 	public:
-		template<typename T>
-		PushConstants() : PushConstants(sizeof(T)) {}
-		
-		explicit PushConstants(size_t sizePerDrawcall) :
-		m_data(),
-		m_sizePerDrawcall(sizePerDrawcall) {}
+		explicit PushConstants(size_t sizePerDrawcall);
+		explicit PushConstants(const TypeGuard &guard);
 		
 		PushConstants(const PushConstants& other) = default;
 		PushConstants(PushConstants&& other) = default;
@@ -43,9 +40,7 @@ namespace vkcv {
 		 * @return Size of data per drawcall
 		 */
 		[[nodiscard]]
-		size_t getSizePerDrawcall() const {
-			return m_sizePerDrawcall;
-		}
+		size_t getSizePerDrawcall() const;
 		
 		/**
 		 * @brief Returns the size of total data stored for
@@ -54,9 +49,7 @@ namespace vkcv {
 		 * @return Total size of data
 		 */
 		[[nodiscard]]
-		size_t getFullSize() const {
-			return m_data.size();
-		}
+		size_t getFullSize() const;
 		
 		/**
 		 * @brief Returns the number of drawcalls that data
@@ -65,17 +58,13 @@ namespace vkcv {
 		 * @return Number of drawcalls
 		 */
 		[[nodiscard]]
-		size_t getDrawcallCount() const {
-			return (m_data.size() / m_sizePerDrawcall);
-		}
+		size_t getDrawcallCount() const;
 		
 		/**
 		 * @brief Clears the data for all drawcalls currently.
 		 * stored.
 		*/
-		void clear() {
-			m_data.clear();
-		}
+		void clear();
 		
 		/**
 		 * @brief Appends data for a single drawcall to the
@@ -87,9 +76,7 @@ namespace vkcv {
 		 */
 		template<typename T = uint8_t>
 		bool appendDrawcall(const T& value) {
-			if (sizeof(T) != m_sizePerDrawcall) {
-				vkcv_log(LogLevel::WARNING, "Size (%lu) of value does not match the specified size per drawcall (%lu)",
-						 sizeof(value), m_sizePerDrawcall);
+			if (!m_typeGuard.template check<T>()) {
 				return false;
 			}
 			
@@ -109,7 +96,7 @@ namespace vkcv {
 		 */
 		template<typename T = uint8_t>
 		T& getDrawcall(size_t index) {
-			const size_t offset = (index * m_sizePerDrawcall);
+			const size_t offset = (index * getSizePerDrawcall());
 			return *reinterpret_cast<T*>(m_data.data() + offset);
 		}
 		
@@ -123,7 +110,7 @@ namespace vkcv {
 		 */
 		template<typename T = uint8_t>
 		const T& getDrawcall(size_t index) const {
-			const size_t offset = (index * m_sizePerDrawcall);
+			const size_t offset = (index * getSizePerDrawcall());
 			return *reinterpret_cast<const T*>(m_data.data() + offset);
 		}
 		
@@ -135,10 +122,7 @@ namespace vkcv {
 		 * @return Drawcall data
 		 */
 		[[nodiscard]]
-		const void* getDrawcallData(size_t index) const {
-			const size_t offset = (index * m_sizePerDrawcall);
-			return reinterpret_cast<const void*>(m_data.data() + offset);
-		}
+		const void* getDrawcallData(size_t index) const;
 		
 		/**
 		 * @brief Returns the pointer to the entire drawcall data which
@@ -147,14 +131,33 @@ namespace vkcv {
 		 * @return Pointer to the data
 		 */
 		[[nodiscard]]
-		const void* getData() const {
-			if (m_data.empty()) {
-				return nullptr;
-			} else {
-				return m_data.data();
+		const void* getData() const;
+		
+	};
+	
+	template<typename T>
+	PushConstants pushConstants() {
+		return PushConstants(typeGuard<T>());
+	}
+	
+	template<typename T>
+	PushConstants pushConstants(const T& value) {
+		auto pc = pushConstants<T>();
+		pc.template appendDrawcall(value);
+		return pc;
+	}
+	
+	template<typename T>
+	PushConstants pushConstants(const std::vector<T>& values) {
+		auto pc = pushConstants<T>();
+		
+		for (const T& value : values) {
+			if (!(pc.template appendDrawcall(value))) {
+				break;
 			}
 		}
 		
-	};
+		return pc;
+	}
 	
 }
diff --git a/include/vkcv/Sampler.hpp b/include/vkcv/Sampler.hpp
index c65ef8218ff209e6eeaf0e45f9309e21d85e782e..601370c71bfaa3a8eaad023cae532b083d78fa90 100644
--- a/include/vkcv/Sampler.hpp
+++ b/include/vkcv/Sampler.hpp
@@ -2,50 +2,21 @@
 /**
  * @authors Tobias Frisch
  * @file vkcv/Sampler.hpp
- * @brief Enums for different sampler attributes.
+ * @brief Support functions for basic sampler creation.
  */
 
-namespace vkcv {
-
-    /**
-     * @brief Enum class to specify a samplers type to filter during access.
-     */
-	enum class SamplerFilterType {
-		NEAREST = 1,
-		LINEAR = 2
-	};
-
-    /**
-     * @brief Enum class to specify a samplers mode to access mipmaps.
-     */
-	enum class SamplerMipmapMode {
-		NEAREST = 1,
-		LINEAR = 2
-	};
+#include "Core.hpp"
+#include "SamplerTypes.hpp"
+#include "Handles.hpp"
 
-    /**
-     * @brief Enum class to specify a samplers mode to access via address space.
-     */
-	enum class SamplerAddressMode {
-		REPEAT = 1,
-		MIRRORED_REPEAT = 2,
-		CLAMP_TO_EDGE = 3,
-		MIRROR_CLAMP_TO_EDGE = 4,
-		CLAMP_TO_BORDER = 5
-	};
-
-    /**
-     * @brief Enum class to specify a samplers color beyond a textures border.
-     */
-	enum class SamplerBorderColor {
-		INT_ZERO_OPAQUE = 1,
-		INT_ZERO_TRANSPARENT = 2,
-		
-		FLOAT_ZERO_OPAQUE = 3,
-		FLOAT_ZERO_TRANSPARENT = 4,
-		
-		INT_ONE_OPAQUE = 5,
-		FLOAT_ONE_OPAQUE = 6
-	};
+namespace vkcv {
+	
+	[[nodiscard]]
+	SamplerHandle samplerLinear(Core &core,
+								bool clampToEdge = false);
+	
+	[[nodiscard]]
+	SamplerHandle samplerNearest(Core &core,
+								 bool clampToEdge = false);
 
 }
diff --git a/include/vkcv/SamplerTypes.hpp b/include/vkcv/SamplerTypes.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4dc68930050ff955a251f701657462a8b5ebbb1f
--- /dev/null
+++ b/include/vkcv/SamplerTypes.hpp
@@ -0,0 +1,51 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/SamplerTypes.hpp
+ * @brief Enums for different sampler attributes.
+ */
+
+namespace vkcv {
+	
+	/**
+     * @brief Enum class to specify a samplers type to filter during access.
+     */
+	enum class SamplerFilterType {
+		NEAREST = 1,
+		LINEAR = 2
+	};
+	
+	/**
+	 * @brief Enum class to specify a samplers mode to access mipmaps.
+	 */
+	enum class SamplerMipmapMode {
+		NEAREST = 1,
+		LINEAR = 2
+	};
+	
+	/**
+	 * @brief Enum class to specify a samplers mode to access via address space.
+	 */
+	enum class SamplerAddressMode {
+		REPEAT = 1,
+		MIRRORED_REPEAT = 2,
+		CLAMP_TO_EDGE = 3,
+		MIRROR_CLAMP_TO_EDGE = 4,
+		CLAMP_TO_BORDER = 5
+	};
+	
+	/**
+	 * @brief Enum class to specify a samplers color beyond a textures border.
+	 */
+	enum class SamplerBorderColor {
+		INT_ZERO_OPAQUE = 1,
+		INT_ZERO_TRANSPARENT = 2,
+		
+		FLOAT_ZERO_OPAQUE = 3,
+		FLOAT_ZERO_TRANSPARENT = 4,
+		
+		INT_ONE_OPAQUE = 5,
+		FLOAT_ONE_OPAQUE = 6
+	};
+	
+}
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index c815834a3d7a948df3307769f34abe94cbd2ff22..0cc4fed0d87195dec3b0eae345e206724d2f4060 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -13,8 +13,8 @@
 #include <vulkan/vulkan.hpp>
 #include <spirv_cross.hpp>
 
+#include "DescriptorBinding.hpp"
 #include "VertexLayout.hpp"
-#include "DescriptorConfig.hpp"
 #include "ShaderStage.hpp"
 
 namespace vkcv {
diff --git a/include/vkcv/ShaderStage.hpp b/include/vkcv/ShaderStage.hpp
index 39b250e80be79cbb0f39261e69ab0d80a71d4f31..d143b776c876f9a5cf6e298e485acc354fdf2b12 100644
--- a/include/vkcv/ShaderStage.hpp
+++ b/include/vkcv/ShaderStage.hpp
@@ -33,6 +33,9 @@ namespace vkcv {
 	
 }
 
+/**
+ * @cond VULKAN_TYPES
+ */
 namespace vk {
 	
 	template<>
@@ -58,6 +61,9 @@ namespace vk {
 	};
 	
 }
+/**
+ * @endcond
+ */
 
 namespace vkcv {
 	
diff --git a/include/vkcv/Surface.hpp b/include/vkcv/Surface.hpp
deleted file mode 100644
index 47862b9c49661f434b64f9def17037cc5bcc110c..0000000000000000000000000000000000000000
--- a/include/vkcv/Surface.hpp
+++ /dev/null
@@ -1,107 +0,0 @@
-#pragma once
-/**
- * @authors Tobias Frisch
- * @file vkcv/Surface.hpp
- * @brief Class to manage the surface used by a swapchain.
- */
-
-#include <vulkan/vulkan.hpp>
-
-#include "Context.hpp"
-#include "Window.hpp"
-
-namespace vkcv {
-	
-	const uint32_t MIN_SURFACE_SIZE = 2;
-
-    /**
-     * @brief Class to handle surfaces to use with a swapchain.
-     */
-	class Surface final {
-	private:
-		friend class Swapchain;
-		friend class SwapchainManager;
-		
-		const Context *m_Context;
-		vk::SurfaceKHR m_Handle;
-		uint32_t m_PresentQueueIndex;
-		vk::Extent2D m_Extent;
-		vk::Format m_Format;
-		vk::ColorSpaceKHR m_ColorSpace;
-		
-		Surface(const Context &context,
-				const vk::SurfaceKHR &handle,
-				uint32_t presentQueueIndex,
-				const vk::Extent2D &extent,
-				vk::Format format,
-				vk::ColorSpaceKHR colorSpace);
-		
-		vk::SwapchainKHR createVulkanSwapchain(const Window &window,
-											   const vk::SwapchainKHR &oldSwapchain);
-		
-	public:
-		Surface(const Surface& other) = default;
-		
-		Surface(Surface&& other) noexcept;
-		
-		Surface& operator=(const Surface& other) = default;
-		
-		Surface& operator=(Surface&& other) noexcept;
-		
-		~Surface();
-		
-		/**
-		 * @brief Creates a surface via a window and a current context.
-		 *
-		 * @param[in] window Window
-		 * @param[in] context Context
-		 * @return Created surface
-		 */
-		static Surface create(const Window &window,
-							  const Context &context);
-		
-		/**
-         * @brief Returns the Vulkan-Surface of the object.
-         *
-         * @return Current Vulkan-Surface
-         */
-		[[nodiscard]]
-		vk::SurfaceKHR getSurface() const;
-		
-		/**
-		 * @brief Returns the queue index of the present queue
-		 * for the surface.
-		 *
-		 * @return Present queue index
-		 */
-		[[nodiscard]]
-		uint32_t getPresentQueueIndex() const;
-		
-		/**
-		 * @brief Returns the extent of the surfaces resolution.
-		 *
-		 * @return Extent of surface
-		 */
-		[[nodiscard]]
-		const vk::Extent2D& getExtent() const;
-		
-		/**
-		 * @brief Returns the image format of the surface to
-		 * present an image.
-		 *
-		 * @return Vulkan image format
-		 */
-		[[nodiscard]]
-		vk::Format getFormat() const;
-		
-		/**
-		 * @brief Returns the color space of the surface.
-		 *
-		 * @return Color space
-		 */
-		[[nodiscard]]
-		vk::ColorSpaceKHR getColorSpace() const;
-		
-	};
-
-}
diff --git a/include/vkcv/Swapchain.hpp b/include/vkcv/Swapchain.hpp
deleted file mode 100644
index 9380b97ca73e46d53e7dcfbb8ba804004698b5b3..0000000000000000000000000000000000000000
--- a/include/vkcv/Swapchain.hpp
+++ /dev/null
@@ -1,136 +0,0 @@
-#pragma once
-/**
- * @authors Sebastian Gaida, Tobias Frisch
- * @file vkcv/Swapchain.hpp
- * @brief Class to manage the state of a swapchain and its transitions.
- */
-
-#include <atomic>
-#include <vulkan/vulkan.hpp>
-
-#include "Context.hpp"
-#include "Surface.hpp"
-#include "Window.hpp"
-
-namespace vkcv
-{
-
-    /**
-     * @brief Class to handle swapchains using a context.
-     */
-    class Swapchain final {
-    private:
-    	friend class Core;
-    	friend class Window;
-    	friend class SwapchainManager;
-     
-		const Context *m_Context;
-        Surface m_Surface;
-        vk::SwapchainKHR m_Swapchain;
-		std::atomic<bool> m_RecreationRequired;
-
-		/**
-		 * @brief Constructor of the swapchain with the current context,
-		 * a surface and a given vulkan swapchain object.
-		 *
-		 * @param[in,out] context Current context
-		 * @param[in] surface used by the swapchain
-		 * @param[in,out] swapchain to show images in the window
-		 */
-        Swapchain(const Context &context,
-				  const Surface &surface,
-                  vk::SwapchainKHR swapchain) noexcept;
-	
-		/**
-		 * @brief Checks whether the swapchain needs to be recreated.
-		 *
-		 * @return True, if the swapchain should be updated,
-		 * otherwise false.
-		 */
-		bool shouldUpdateSwapchain() const;
-	
-		/**
-		 * @brief Updates and recreates the swapchain.
-		 *
-		 * @param[in,out] context that holds the device to recreate the swapchain
-		 * @param[in] window that the new swapchain gets bound to
-		 */
-		void updateSwapchain(const Context &context, const Window &window);
-	
-		/**
-		 * @brief Signals the swapchain to be recreated.
-		 */
-		void signalSwapchainRecreation();
-
-    public:
-    	Swapchain(const Swapchain& other);
-
-        /**
-         * @brief Returns the vulkan swapchain object of the swapchain.
-         *
-         * @return The swapchain linked with the #SwapChain class
-         * @note The reference to our Swapchain variable is needed for the recreation step
-         */
-        [[nodiscard]]
-        const vk::SwapchainKHR& getSwapchain() const;
-
-        /**
-         * @brief Returns the current surface of the swapchain.
-         *
-         * @return Current surface
-         */
-        [[nodiscard]]
-        const Surface& getSurface() const;
-
-        /**
-         * @brief Returns the image format for the current surface
-         * of the swapchain.
-         *
-         * @return Swapchain image format
-         */
-        [[nodiscard]]
-        vk::Format getFormat() const;
-
-        /**
-         * @brief Creates a swapchain for a specific window and
-         * a given context.
-         *
-         * @param[in,out] window Window
-         * @param[in,out] context Context
-         * @return New created swapchain
-         */
-        static Swapchain create(const Window &window, const Context &context);
-
-        /**
-         * Destructor of thw swapchain.
-         */
-        virtual ~Swapchain();
-
-		/**
-		 * @brief Returns the amount of images for the swapchain.
-		 *
-		 * @return Number of images
-		*/
-		uint32_t getImageCount() const;
-	
-        /**
-         * @brief Returns the extent from the current surface of
-         * the swapchain.
-         *
-         * @return Extent of the swapchains surface
-         */
-        [[nodiscard]]
-		const vk::Extent2D& getExtent() const;
-
-		/**
-		 * @brief Returns the present queue index to be used with
-		 * the swapchain and its current surface.
-		 *
-		 * @return Present queue family index
-		 */
-		[[nodiscard]]
-		uint32_t getPresentQueueIndex() const;
-
-	};
-    
-}
diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp
deleted file mode 100644
index 1683855b6b4f9046b0af5fb2df65f3bca895da2a..0000000000000000000000000000000000000000
--- a/include/vkcv/SyncResources.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-/**
- * @authors Alexander Gauggel, Tobias Frisch
- * @file vkcv/SyncResources.hpp
- * @brief Support functions to deal with synchronization resources.
- */
-
-#include <vulkan/vulkan.hpp>
-
-namespace vkcv {
-	
-	/**
-	 * @brief Structure to store vulkan resources for presenting
-	 * with a pipeline.
-	 */
-	struct SyncResources {
-		vk::Semaphore renderFinished;
-		vk::Semaphore swapchainImageAcquired;
-		vk::Fence presentFinished;
-	};
-
-	/**
-	 * @brief Creates new synchronization resources for drawing
-	 * and presenting with a swapchain.
-	 *
-	 * @param[in,out] device Vulkan-Device
-	 * @return New created synchronization resources
-	 */
-	SyncResources createSyncResources(const vk::Device &device);
-	
-	/**
-	 * @brief Destroys the synchronization resources with a
-	 * given device.
-	 *
-	 * @param[in,out] device Vulkan-Device
-	 * @param[in,out] resources Synchronizazion resources
-	 */
-	void destroySyncResources(const vk::Device &device,
-							  const SyncResources &resources);
-	
-	/**
-	 * @brief Creates a new fence with a given device and
-	 * returns it.
-	 *
-	 * @param[in,out] device Vulkan-Device
-	 * @return New created fence
-	 */
-	vk::Fence createFence(const vk::Device &device);
-	
-	/**
-	 * @brief Calls a given device to wait for a specific fence.
-	 *
-	 * @param[in,out] device Vulkan-Device
-	 * @param[in] fence Vulkan-Fence
-	 */
-	void waitForFence(const vk::Device& device,
-					  const vk::Fence& fence);
-	
-}
\ No newline at end of file
diff --git a/include/vkcv/TypeGuard.hpp b/include/vkcv/TypeGuard.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cac170c0105c24c2c00758423e5c723758761ac7
--- /dev/null
+++ b/include/vkcv/TypeGuard.hpp
@@ -0,0 +1,154 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/TypeGuard.hpp
+ * @brief Support type safety for classes in debug compilation.
+ */
+
+#include <stdlib.h>
+#include <typeinfo>
+
+namespace vkcv {
+	
+	/**
+	 * @brief Class bringing type safety during runtime to other classes.
+	 */
+	class TypeGuard {
+	private:
+#ifndef NDEBUG
+		const char* m_typeName;
+		size_t m_typeHash;
+		
+		[[nodiscard]]
+		bool checkType(const char* name, size_t hash, size_t size) const;
+#endif
+		size_t m_typeSize;
+		
+		[[nodiscard]]
+		bool checkTypeSize(size_t size) const;
+		
+	public:
+		/**
+		 * Explicit constructor to create a type guard by a specific
+		 * size only. The guard will not verify an individual type but
+		 * whether its size matches the requirement.
+		 *
+		 * @param[in] size Size of type
+		 */
+		explicit TypeGuard(size_t size = 0);
+		
+		/**
+		 * Explicit constructor to create a type guard by a types
+		 * ID information and its size. The guard will verify a type
+		 * by all available information in debug mode.
+		 *
+		 * @param[in] info ID information of type
+		 * @param[in] size Size of type
+		 */
+		TypeGuard(const std::type_info &info, size_t size);
+		
+		TypeGuard(const TypeGuard &other) = default;
+		TypeGuard(TypeGuard &&other) noexcept = default;
+		
+		~TypeGuard() = default;
+		
+		TypeGuard& operator=(const TypeGuard &other) = default;
+		TypeGuard& operator=(TypeGuard &&other) noexcept = default;
+		
+		/**
+		 * Operator to compare two type guards and returns
+		 * whether their stored type information and size
+		 * match as boolean value.
+		 *
+		 * @param[in] other Other type guard
+		 * @return True if the details match, otherwise false.
+		 */
+		bool operator==(const TypeGuard &other) const;
+		
+		/**
+		 * Operator to compare two type guards and returns
+		 * whether their stored type information and size
+		 * do not match as boolean value.
+		 *
+		 * @param[in] other Other type guard
+		 * @return True if the details differ, otherwise false.
+		 */
+		bool operator!=(const TypeGuard &other) const;
+		
+		/**
+		 * Checks whether a type from a template parameter
+		 * matches with the used type by the given guard.
+		 *
+		 * @tparam T Type to check against
+		 * @return True if both types match, otherwise false.
+		 */
+		template<typename T>
+		[[nodiscard]]
+		bool check() const {
+#ifndef NDEBUG
+			return checkType(typeid(T).name(), typeid(T).hash_code(), sizeof(T));
+#else
+			return checkTypeSize(sizeof(T));
+#endif
+		}
+		
+		/**
+		 * Returns the size of this guards type in bytes.
+		 *
+		 * @return Size of type
+		 */
+		[[nodiscard]]
+		size_t typeSize() const;
+		
+	};
+	
+	/**
+	 * Creates a new type guard with a given type specified
+	 * as template parameter.
+	 *
+	 * @tparam T Type
+	 * @return New type guard
+	 */
+	template<typename T>
+	TypeGuard typeGuard() {
+		static TypeGuard guard (typeid(T), sizeof(T));
+		return guard;
+	}
+	
+	/**
+	 * Creates a new type guard with a given type specified
+	 * as template parameter by the passed parameter.
+	 *
+	 * @tparam T Type
+	 * @return New type guard
+	 */
+	template<typename T>
+	TypeGuard typeGuard(T) {
+		return typeGuard<T>();
+	}
+	
+	/**
+	 * Creates a new type guard with a given type specified
+	 * as template parameter by the passed parameter.
+	 *
+	 * @tparam T Type
+	 * @return New type guard
+	 */
+	template<typename T>
+	TypeGuard typeGuard(const T&) {
+		return typeGuard<T>();
+	}
+	
+	/**
+	 * Creates a new type guard with a given type specified
+	 * as template parameter by the passed parameter.
+	 *
+	 * @tparam T Type
+	 * @return New type guard
+	 */
+	template<typename T>
+	TypeGuard typeGuard(T&&) {
+		return typeGuard<T>();
+	}
+
+}
diff --git a/include/vkcv/VertexData.hpp b/include/vkcv/VertexData.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..001dcfcf6e96f1e356316ef56bd7810583531239
--- /dev/null
+++ b/include/vkcv/VertexData.hpp
@@ -0,0 +1,73 @@
+#pragma once
+/**
+ * @authors Sebastian Gaida, Alexander Gauggel, Artur Wasmut, Tobias Frisch
+ * @file vkcv/VertexData.hpp
+ * @brief Types to configure vertex data for drawcalls.
+ */
+
+#include <vector>
+
+#include "Handles.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Structure to store details about a vertex buffer binding.
+	 */
+	struct VertexBufferBinding {
+		BufferHandle buffer;
+		size_t offset;
+	};
+	
+	VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer,
+											size_t offset = 0);
+	
+	typedef std::vector<VertexBufferBinding> VertexBufferBindings;
+	
+	/**
+	 * @brief Enum class to specify the size of indexes.
+	 */
+	enum class IndexBitCount {
+		Bit8,
+		Bit16,
+		Bit32
+	};
+	
+	class VertexData {
+	private:
+		VertexBufferBindings m_bindings;
+		BufferHandle m_indices;
+		IndexBitCount m_indexBitCount;
+		size_t m_count;
+	
+	public:
+		explicit VertexData(const VertexBufferBindings &bindings = {});
+		
+		VertexData(const VertexData& other) = default;
+		VertexData(VertexData&& other) noexcept = default;
+		
+		~VertexData() = default;
+		
+		VertexData& operator=(const VertexData& other) = default;
+		VertexData& operator=(VertexData&& other) noexcept = default;
+		
+		[[nodiscard]]
+		const VertexBufferBindings& getVertexBufferBindings() const;
+		
+		void setIndexBuffer(const BufferHandle& indices,
+							IndexBitCount indexBitCount = IndexBitCount::Bit16);
+		
+		[[nodiscard]]
+		const BufferHandle& getIndexBuffer() const;
+		
+		[[nodiscard]]
+		IndexBitCount getIndexBitCount() const;
+		
+		void setCount(size_t count);
+		
+		[[nodiscard]]
+		size_t getCount() const;
+	
+	};
+
+}
diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp
index 330be708525b3e1a90e15919abb9f3f2f2ea1729..8e677d11dc49594416f7eedda9d2ef00edae830a 100644
--- a/include/vkcv/VertexLayout.hpp
+++ b/include/vkcv/VertexLayout.hpp
@@ -70,11 +70,20 @@ namespace vkcv {
 	 *
 	 * @param[in] bindingLocation Its entry in the buffers that make up the whole vertex buffer.
 	 * @param[in] attachments The vertex input attachments this specific buffer layout contains.
-	 * @return Vertex buffer binding with calculated stride
+	 * @return Vertex binding with calculated stride
 	 */
 	VertexBinding createVertexBinding(uint32_t bindingLocation, const VertexAttachments &attachments);
 	
 	typedef std::vector<VertexBinding> VertexBindings;
+	
+	/**
+	 * Creates vertex bindings in a very simplified way with one vertex binding for
+	 * each attachment.
+	 *
+	 * @param[in] attachments The vertex input attachments.
+	 * @return Vertex bindings with calculated stride
+	 */
+	VertexBindings createVertexBindings(const VertexAttachments &attachments);
 
 	/**
 	 * @brief Structure to store the details of a vertex layout.
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index d1db4bf438b95e9c96824bb6676a1d8bf020a35f..e7a27730140c50c791cd72b94d56488732770265 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -169,7 +169,8 @@ namespace vkcv {
          *
          * @return Swapchain handle
          */
-        SwapchainHandle getSwapchainHandle() const;
+        SwapchainHandle getSwapchain() const;
+		
     };
 
 }
diff --git a/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
index c2d9721d5db6791b82bcf5a7d0a832fd9ab5db77..a46ab4fc4e7ff237af52ba629455a06758e54e0f 100644
--- a/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
+++ b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
@@ -2,6 +2,7 @@
 
 #include <vector>
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
 #include <vkcv/Downsampler.hpp>
 #include <vkcv/ShaderProgram.hpp>
diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
index 0d344861824959eb482a076d7cc118841a434765..eee9f361493c35c5214ac661448f3cc80384e4ee 100644
--- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
+++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
@@ -14,6 +14,7 @@
 #include "SPDIntegration.glsl.hxx"
 #include "SPDIntegrationLinearSampler.glsl.hxx"
 
+#include <vkcv/ComputePipelineConfig.hpp>
 #include <vkcv/File.hpp>
 #include <vkcv/Logger.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -150,7 +151,8 @@ namespace vkcv::algorithm {
 		 m_descriptorSetLayout(),
 		 m_descriptorSets(),
 		
-		 m_globalCounter(m_core.createBuffer<uint32_t>(
+		 m_globalCounter(buffer<uint32_t>(
+				 m_core,
 				 vkcv::BufferType::STORAGE,
 				 6
 		 )),
@@ -231,10 +233,10 @@ namespace vkcv::algorithm {
 			);
 		}
 		
-		m_pipeline = m_core.createComputePipeline({
+		m_pipeline = m_core.createComputePipeline(ComputePipelineConfig(
 			program,
 			{ m_descriptorSetLayout }
-		});
+		));
 		
 		std::vector<uint32_t> zeroes;
 		zeroes.resize(m_globalCounter.getCount());
@@ -307,12 +309,16 @@ namespace vkcv::algorithm {
 			m_core.prepareImageForStorage(cmdStream, image);
 		}
 		
-		uint32_t dispatch [3];
-		dispatch[0] = dispatchThreadGroupCountXY[0];
-		dispatch[1] = dispatchThreadGroupCountXY[1];
-		dispatch[2] = m_core.getImageArrayLayers(image);
+		vkcv::DispatchSize dispatch (
+				dispatchThreadGroupCountXY[0],
+				dispatchThreadGroupCountXY[1],
+				m_core.getImageArrayLayers(image)
+		);
 		
-		vkcv::PushConstants pushConstants (m_sampler? sizeof(SPDConstantsSampler) : sizeof(SPDConstants));
+		vkcv::PushConstants pushConstants = (m_sampler?
+				vkcv::pushConstants<SPDConstantsSampler>() :
+				vkcv::pushConstants<SPDConstants>()
+		);
 		
 		if (m_sampler) {
 			SPDConstantsSampler data;
diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
index 2362e560516fee3bf392ef150a756a25aa2420a0..d6b5cf5d5382e0e03bd7950c1e87bf3c6a8ee445 100644
--- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
+++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
@@ -11,6 +11,8 @@
 #include <cstdint>
 #include <filesystem>
 
+#include "vkcv/VertexData.hpp"
+
 /* LOADING MESHES
  * The description of meshes is a hierarchy of structures with the Mesh at the
  * top.
@@ -279,8 +281,8 @@ struct Scene {
  * Note that for URIs only (local) filesystem paths are supported, no
  * URLs using network protocols etc.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene	is a reference to a Scene struct that will be filled with the
+ * @param[in] path must be the path to a glTF- or glb-file.
+ * @param[out] scene is a reference to a Scene struct that will be filled with the
  * 	meta-data of all objects described in the glTF file.
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
@@ -295,8 +297,8 @@ int probeScene(const std::filesystem::path &path, Scene &scene);
  * Besides the mesh, this function will also add any associated data to the
  * Scene struct such as Materials and Textures required by the Mesh.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene	is the scene struct to which the results will be written.
+ * @param[in,out] scene	is the scene struct to which the results will be written.
+ * @param[in] mesh_index Index of the mesh to load
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
 int loadMesh(Scene &scene, int mesh_index);
@@ -305,8 +307,8 @@ int loadMesh(Scene &scene, int mesh_index);
  * Load every mesh from the glTF file, as well as materials, textures and other
  * associated objects.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene is a reference to a Scene struct that will be filled with the
+ * @param[in] path	must be the path to a glTF- or glb-file.
+ * @param[out] scene is a reference to a Scene struct that will be filled with the
  * 	content of the glTF file being loaded.
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
@@ -322,11 +324,25 @@ int loadScene(const std::filesystem::path &path, Scene &scene);
  * will be cleared to all 0 with path and data being empty; make sure to always
  * check that !data.empty() before using the struct.
  *
- * @param path	must be the path to an image file.
+ * @param[in] path	must be the path to an image file.
  * @return	Texture struct describing the loaded image.
  */
 Texture loadTexture(const std::filesystem::path& path);
 
+/**
+ * Loads up the vertex attributes and creates usable vertex buffer bindings
+ * to match the desired order of primitive types as used in the vertex
+ * shader.
+ *
+ * @param[in] attributes Vertex attributes
+ * @param[in] buffer Buffer handle
+ * @param[in] types Primitive type order
+ * @return Vertex buffer bindings
+ */
+VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes,
+											  const BufferHandle &buffer,
+											  const std::vector<PrimitiveType> &types);
+
 /** @} */
 
 }	// end namespace vkcv::asset
diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
index 110a941ff6703e989174eaf311118b10b41491d4..d8f2563d867e5521bce29a3880650f0418ad9db8 100644
--- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
+++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
@@ -856,5 +856,31 @@ namespace vkcv::asset {
 		}
 		return texture;
 	}
+	
+	VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes,
+												  const BufferHandle &buffer,
+												  const std::vector<PrimitiveType> &types) {
+		VertexBufferBindings bindings;
+		
+		for (const auto& type : types) {
+			const VertexAttribute* attribute = nullptr;
+			
+			for (const auto& attr : attributes) {
+				if (type == attr.type) {
+					attribute = &(attr);
+					break;
+				}
+			}
+			
+			if (!attribute) {
+				vkcv_log(LogLevel::ERROR, "Missing primitive type in vertex attributes");
+				break;
+			}
+			
+			bindings.push_back(vkcv::vertexBufferBinding(buffer, attribute->offset));
+		}
+		
+		return bindings;
+	}
 
 }
diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
index c8e51fe9716af0304687d6ae60fc0bea2a5d2cc6..f31596ae80c86a432f62a50621de949961e83d56 100644
--- a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
+++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp
@@ -1,8 +1,9 @@
 
 #include "vkcv/effects/BloomAndFlaresEffect.hpp"
 
-#include <vkcv/DrawcallRecording.hpp>
 #include <vkcv/PushConstants.hpp>
+#include <vkcv/Image.hpp>
+#include <vkcv/Sampler.hpp>
 
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include <vkcv/asset/asset_loader.hpp>
@@ -125,7 +126,8 @@ namespace vkcv::effects {
 								   const std::string &texturePath) {
 		const auto texture = vkcv::asset::loadTexture(texturePath);
 		
-		Image image = core.createImage(
+		auto image = vkcv::image(
+				core,
 				vk::Format::eR8G8B8A8Unorm,
 				texture.width,
 				texture.height
@@ -188,12 +190,7 @@ namespace vkcv::effects {
 	m_blurImage(),
 	m_flaresImage(),
 	
-	m_linearSampler(m_core.createSampler(
-			vkcv::SamplerFilterType::LINEAR,
-			vkcv::SamplerFilterType::LINEAR,
-			vkcv::SamplerMipmapMode::LINEAR,
-			vkcv::SamplerAddressMode::CLAMP_TO_EDGE
-	)),
+	m_linearSampler(samplerLinear(m_core, true)),
 	
 	m_radialLutSampler(),
 	
@@ -236,13 +233,7 @@ namespace vkcv::effects {
 		);
 		
 		if (m_advanced) {
-			m_radialLutSampler = m_core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
-			
+			m_radialLutSampler = samplerLinear(m_core);
 			m_radialLut = loadTexture(m_core, "assets/RadialLUT.png");
 			m_lensDirt = loadTexture(m_core, "assets/lensDirt.jpg");
 		}
@@ -293,10 +284,10 @@ namespace vkcv::effects {
 			
 			static const uint32_t threadGroupWorkRegionDim = 8;
 			
-			uint32_t dispatch[3];
-			dispatch[0] = calcDispatchSize(downsampleSizeX, threadGroupWorkRegionDim);
-			dispatch[1] = calcDispatchSize(downsampleSizeY, threadGroupWorkRegionDim);
-			dispatch[2] = 1;
+			DispatchSize dispatch (
+					calcDispatchSize(downsampleSizeX, threadGroupWorkRegionDim),
+					calcDispatchSize(downsampleSizeY, threadGroupWorkRegionDim)
+			);
 			
 			// mip blur dispatch
 			m_core.recordComputeDispatchToCmdStream(
@@ -342,10 +333,10 @@ namespace vkcv::effects {
 			
 			static const uint32_t threadGroupWorkRegionDim = 8;
 			
-			uint32_t dispatch[3];
-			dispatch[0] = calcDispatchSize(upsampleSizeX, threadGroupWorkRegionDim);
-			dispatch[1] = calcDispatchSize(upsampleSizeY, threadGroupWorkRegionDim);
-			dispatch[2] = 1;
+			DispatchSize dispatch (
+					calcDispatchSize(upsampleSizeX, threadGroupWorkRegionDim),
+					calcDispatchSize(upsampleSizeY, threadGroupWorkRegionDim)
+			);
 			
 			m_core.recordComputeDispatchToCmdStream(
 					cmdStream,
@@ -385,13 +376,13 @@ namespace vkcv::effects {
 			mipDivisor *= 2.0f;
 		}
 		
-		static const uint32_t threadGroupWorkRegionDim = 8.0f;
+		static const uint32_t threadGroupWorkRegionDim = 8;
 		
 		// lens feature generation dispatch
-		uint32_t dispatch[3];
-		dispatch[0] = calcDispatchSize(sampleSizeX / mipDivisor, threadGroupWorkRegionDim);
-		dispatch[1] = calcDispatchSize(sampleSizeY / mipDivisor, threadGroupWorkRegionDim);
-		dispatch[2] = 1;
+		DispatchSize dispatch (
+				calcDispatchSize(sampleSizeX / mipDivisor, threadGroupWorkRegionDim),
+				calcDispatchSize(sampleSizeY / mipDivisor, threadGroupWorkRegionDim)
+		);
 		
 		m_core.recordComputeDispatchToCmdStream(
 				cmdStream,
@@ -451,12 +442,12 @@ namespace vkcv::effects {
 		
 		static const uint32_t threadGroupWorkRegionDim = 8;
 		
-		uint32_t dispatch[3];
-		dispatch[0] = calcDispatchSize(sampleWidth, threadGroupWorkRegionDim);
-		dispatch[1] = calcDispatchSize(sampleHeight, threadGroupWorkRegionDim);
-		dispatch[2] = 1;
+		DispatchSize dispatch (
+				calcDispatchSize(sampleWidth, threadGroupWorkRegionDim),
+				calcDispatchSize(sampleHeight, threadGroupWorkRegionDim)
+		);
 		
-		PushConstants pushConstants (sizeof(m_cameraDirection));
+		PushConstants pushConstants = vkcv::pushConstants<glm::vec3>();
 		pushConstants.appendDrawcall(m_cameraDirection);
 		
 		// bloom composite dispatch
@@ -494,7 +485,7 @@ namespace vkcv::effects {
 					1,
 					true,
 					true
-			).getHandle();
+			);
 			
 			m_downsampleDescriptorSets.clear();
 			m_upsampleDescriptorSets.clear();
@@ -524,7 +515,7 @@ namespace vkcv::effects {
 					1,
 					true,
 					true
-			).getHandle();
+			);
 			
 			m_flaresDescriptorSets.clear();
 			
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 7ee335379603b3e21ab4d95f0738097bd954cf71..15a1b049aa5496bdcb9ecb81c00f82b66ad86d71 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -71,7 +71,8 @@ namespace vkcv::gui {
 		m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo);
 		
 		const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice();
-		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
+		const SwapchainHandle& swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain();
+		const uint32_t swapchainImageCount = m_core.getSwapchainImageCount(swapchainHandle);
 		
 		const uint32_t graphicsQueueFamilyIndex = (
 				m_context.getQueueManager().getGraphicsQueues()[0].familyIndex
@@ -86,13 +87,13 @@ namespace vkcv::gui {
 		init_info.PipelineCache = 0;
 		init_info.DescriptorPool = static_cast<VkDescriptorPool>(m_descriptor_pool);
 		init_info.Allocator = nullptr;
-		init_info.MinImageCount = swapchain.getImageCount();
-		init_info.ImageCount = swapchain.getImageCount();
+		init_info.MinImageCount = swapchainImageCount;
+		init_info.ImageCount = swapchainImageCount;
 		init_info.CheckVkResultFn = checkVulkanResult;
 		
 		const vk::AttachmentDescription attachment (
 				vk::AttachmentDescriptionFlags(),
-				swapchain.getFormat(),
+				m_core.getSwapchainFormat(swapchainHandle),
 				vk::SampleCountFlagBits::e1,
 				vk::AttachmentLoadOp::eLoad,
 				vk::AttachmentStoreOp::eStore,
@@ -144,14 +145,15 @@ namespace vkcv::gui {
 		
 		ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(m_render_pass));
 		
-		const SubmitInfo submitInfo { QueueType::Graphics, {}, {} };
+		auto stream = m_core.createCommandStream(QueueType::Graphics);
 		
-		m_core.recordAndSubmitCommandsImmediate(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
+		m_core.recordCommandsToStream(stream, [](const vk::CommandBuffer& commandBuffer) {
 			ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(commandBuffer));
 		}, []() {
 			ImGui_ImplVulkan_DestroyFontUploadObjects();
 		});
 		
+		m_core.submitCommandStream(stream, false);
 		m_context.getDevice().waitIdle();
 	}
 	
@@ -177,11 +179,11 @@ namespace vkcv::gui {
 	}
 	
 	void GUI::beginGUI() {
-		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
-		const auto extent = swapchain.getExtent();
+		const auto swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain();
+		const auto& extent = m_core.getSwapchainExtent(swapchainHandle);
 		
 		if ((extent.width > 0) && (extent.height > 0)) {
-			ImGui_ImplVulkan_SetMinImageCount(swapchain.getImageCount());
+			ImGui_ImplVulkan_SetMinImageCount(m_core.getSwapchainImageCount(swapchainHandle));
 		}
 		
 		ImGui_ImplVulkan_NewFrame();
@@ -200,8 +202,8 @@ namespace vkcv::gui {
 			return;
 		}
 		
-		const Swapchain& swapchain = m_core.getSwapchain(m_windowHandle);
-		const auto extent = swapchain.getExtent();
+		const auto swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain();
+		const auto& extent = m_core.getSwapchainExtent(swapchainHandle);
 
 		const vk::ImageView swapchainImageView = m_core.getSwapchainImageView();
 
@@ -216,11 +218,9 @@ namespace vkcv::gui {
 		);
 		
 		const vk::Framebuffer framebuffer = m_context.getDevice().createFramebuffer(framebufferCreateInfo);
+		auto stream = m_core.createCommandStream(QueueType::Graphics);
 		
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Graphics;
-		
-		m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) {
+		m_core.recordCommandsToStream(stream, [&](const vk::CommandBuffer& commandBuffer) {
 
 			assert(initialImageLayout == vk::ImageLayout::eColorAttachmentOptimal);
 			m_core.prepareImageForAttachmentManually(commandBuffer, vkcv::ImageHandle::createSwapchainImageHandle());
@@ -251,6 +251,8 @@ namespace vkcv::gui {
 		}, [&]() {
 			m_context.getDevice().destroyFramebuffer(framebuffer);
 		});
+		
+		m_core.submitCommandStream(stream, false);
 	}
 	
 }
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index 8c3f3099cee731d24854d314fd7e3df4506a9aec..8187b5de9205ba18ca32cd23dba9d5b457696712 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -1,6 +1,9 @@
 
 #include "vkcv/material/Material.hpp"
 
+#include <vkcv/Image.hpp>
+#include <vkcv/Sampler.hpp>
+
 namespace vkcv::material {
 	
 	Material::Material() {
@@ -112,83 +115,58 @@ namespace vkcv::material {
 		};
 		
 		if (!colorImg) {
-			vkcv::Image defaultColor = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+			vkcv::Image defaultColor = image(core, vk::Format::eR8G8B8A8Srgb, 2, 2);
 			float colorData [4] = { 228, 51, 255, 1 };
 			fillImage(defaultColor, colorData);
 			images[0] = defaultColor.getHandle();
 		}
 		
 		if (!normalImg) {
-			vkcv::Image defaultNormal = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
+			vkcv::Image defaultNormal = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float normalData [4] = { 0, 0, 1, 0 };
 			fillImage(defaultNormal, normalData);
 			images[1] = defaultNormal.getHandle();
 		}
 		
 		if (!metRoughImg) {
-			vkcv::Image defaultRough = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
+			vkcv::Image defaultRough = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float roughData [4] = { 228, 51, 255, 1 };
 			fillImage(defaultRough, roughData);
 			images[2] = defaultRough.getHandle();
 		}
 		
 		if (!occlusionImg) {
-			vkcv::Image defaultOcclusion = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
+			vkcv::Image defaultOcclusion = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float occlusionData [4] = { 228, 51, 255, 1 };
 			fillImage(defaultOcclusion, occlusionData);
 			images[3] = defaultOcclusion.getHandle();
 		}
 		
 		if (!emissiveImg) {
-			vkcv::Image defaultEmissive = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+			vkcv::Image defaultEmissive = image(core, vk::Format::eR8G8B8A8Srgb, 2, 2);
 			float emissiveData [4] = { 0, 0, 0, 1 };
 			fillImage(defaultEmissive, emissiveData);
 			images[4] = defaultEmissive.getHandle();
 		}
 		
 		if (!colorSmp) {
-			samplers[0] = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			samplers[0] = samplerLinear(core);
 		}
 		
 		if (!normalSmp) {
-			samplers[1] = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			samplers[1] = samplerLinear(core);
 		}
 		
 		if (!metRoughSmp) {
-			samplers[2] = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			samplers[2] = samplerLinear(core);
 		}
 		
 		if (!occlusionSmp) {
-			samplers[3] = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			samplers[3] = samplerLinear(core);
 		}
 		
 		if (!emissiveSmp) {
-			samplers[4] = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			samplers[4] = samplerLinear(core);
 		}
 		
 		Material material;
diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp
index 3118696bdc270506bc6197e7d853148072294663..65cdb3a4ac7ebe69862874411ba38aae74d87a2f 100644
--- a/modules/scene/include/vkcv/scene/Mesh.hpp
+++ b/modules/scene/include/vkcv/scene/Mesh.hpp
@@ -17,7 +17,7 @@ namespace vkcv::scene {
      * An event function type to be called on per drawcall recording level to adjust data
      * like push constants with provided matrices.
      */
-	typedef typename event_function<const glm::mat4&, const glm::mat4&, PushConstants&, vkcv::DrawcallInfo&>::type RecordMeshDrawcallFunction;
+	typedef typename event_function<const glm::mat4&, const glm::mat4&, PushConstants&, vkcv::Drawcall&>::type RecordMeshDrawcallFunction;
 	
 	class Node;
 
@@ -41,7 +41,7 @@ namespace vkcv::scene {
         /**
          * List of the meshes drawcalls to render.
          */
-		std::vector<DrawcallInfo> m_drawcalls;
+		std::vector<InstanceDrawcall> m_drawcalls;
 
         /**
          * Local transformation matrix of the mesh.
@@ -55,6 +55,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new mesh with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit Mesh(Scene& scene);
@@ -62,14 +63,18 @@ namespace vkcv::scene {
         /**
          * Load mesh data from a scene structure from the asset loader
          * creating and loading all mesh parts being required.
+         *
          * @param[in] scene Scene structure from asset loader
          * @param[in] mesh Mesh structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          */
 		void load(const asset::Scene& scene,
-				  const asset::Mesh& mesh);
+				  const asset::Mesh& mesh,
+				  const std::vector<asset::PrimitiveType>& types);
 
         /**
          * Record drawcalls of the mesh, equally to all its visible parts.
+         *
          * @param[in] viewProjection View-transformation and projection as 4x4 matrix
          * @param[out] pushConstants Structure to store push constants per drawcall
          * @param[out] drawcalls List of drawcall structures
@@ -77,12 +82,13 @@ namespace vkcv::scene {
          */
 		void recordDrawcalls(const glm::mat4& viewProjection,
 							 PushConstants& pushConstants,
-							 std::vector<DrawcallInfo>& drawcalls,
+							 std::vector<InstanceDrawcall>& drawcalls,
 							 const RecordMeshDrawcallFunction& record);
 
         /**
          * Return the amount of drawcalls of the mesh
          * as sum of all its parts.
+         *
          * @return Amount of drawcalls
          */
 		[[nodiscard]]
@@ -96,18 +102,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a mesh.
+         *
          * @param[in] other Other mesh instance
          */
 		Mesh(const Mesh& other) = default;
 
         /**
          * Move-constructor of a mesh.
+         *
          * @param[in,out] other Other mesh instance
          */
         Mesh(Mesh&& other) = default;
 
         /**
          * Copy-operator of a mesh.
+         *
          * @param[in] other Other mesh instance
          * @return Reference to this mesh instance
          */
@@ -115,6 +124,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a mesh.
+         *
          * @param[in,out] other Other mesh instance
          * @return Reference to this mesh instance
          */
@@ -122,6 +132,7 @@ namespace vkcv::scene {
 
         /**
          * Return the axis aligned bounding box of the mesh.
+         *
          * @return Axis aligned bounding box of this mesh
          */
 		[[nodiscard]]
diff --git a/modules/scene/include/vkcv/scene/MeshPart.hpp b/modules/scene/include/vkcv/scene/MeshPart.hpp
index 8d77efd7a112ea92a6c4192e5a5cf43cea53e800..61ed8fc31dc1b6d17a4d2c4ef41db59f870e5ed6 100644
--- a/modules/scene/include/vkcv/scene/MeshPart.hpp
+++ b/modules/scene/include/vkcv/scene/MeshPart.hpp
@@ -64,6 +64,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new mesh part with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit MeshPart(Scene& scene);
@@ -71,13 +72,16 @@ namespace vkcv::scene {
         /**
          * Load vertex and index data from structures provided by the asset loader
          * and add a matching drawcall to the list if the loaded mesh part is valid.
+         *
          * @param[in] scene Scene structure from asset loader
          * @param[in] vertexGroup Vertex group structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          * @param[out] drawcalls List of drawcalls
          */
 		void load(const asset::Scene& scene,
 				  const asset::VertexGroup& vertexGroup,
-				  std::vector<DrawcallInfo>& drawcalls);
+				  const std::vector<asset::PrimitiveType>& types,
+				  std::vector<InstanceDrawcall>& drawcalls);
 	
 	public:
         /**
@@ -87,18 +91,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a mesh part.
+         *
          * @param[in] other Other mesh part
          */
 		MeshPart(const MeshPart& other);
 
         /**
          * Move-constructor of a mesh part.
+         *
          * @param[in,out] other Other mesh part
          */
         MeshPart(MeshPart&& other) noexcept;
 
         /**
          * Copy-operator of a mesh part.
+         *
          * @param[in] other Other mesh part
          * @return Reference to this mesh part
          */
@@ -106,6 +113,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a mesh part.
+         *
          * @param[in,out] other Other mesh part
          * @return Reference to this mesh part
          */
@@ -114,6 +122,7 @@ namespace vkcv::scene {
         /**
          * Get the material used by this specific part of
          * the mesh for rendering.
+         *
          * @return Material
          */
 		[[nodiscard]]
@@ -122,6 +131,7 @@ namespace vkcv::scene {
         /**
          * Return the axis aligned bounding box of this
          * specific part of the mesh.
+         *
          * @return Axis aligned bounding box of this mesh part
          */
 		[[nodiscard]]
@@ -130,6 +140,7 @@ namespace vkcv::scene {
         /**
          * Return the status if this part of the mesh is valid
          * as boolean value.
+         *
          * @return true if the mesh part is valid, otherwise false
          */
 		explicit operator bool() const;
@@ -137,6 +148,7 @@ namespace vkcv::scene {
         /**
          * Return the status if this part of the mesh is invalid
          * as boolean value.
+         *
          * @return true if the mesh part is invalid, otherwise false
          */
 		bool operator!() const;
diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp
index 51088f2564fb56a9d63d99238cb0665c6989bc9e..484e1c12caed05051598185fa284165624a0af17 100644
--- a/modules/scene/include/vkcv/scene/Node.hpp
+++ b/modules/scene/include/vkcv/scene/Node.hpp
@@ -45,25 +45,32 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new node with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit Node(Scene& scene);
 
         /**
          * Add a given mesh to this node for drawcall recording.
+         *
          * @param[in] mesh Mesh
          */
 		void addMesh(const Mesh& mesh);
 
         /**
          * Load and add a mesh from a scene preloaded with the asset loader.
+         *
          * @param[in] asset_scene Scene structure from asset loader
          * @param[in] asset_mesh Mesh structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          */
-		void loadMesh(const asset::Scene& asset_scene, const asset::Mesh& asset_mesh);
+		void loadMesh(const asset::Scene& asset_scene,
+					  const asset::Mesh& asset_mesh,
+					  const std::vector<asset::PrimitiveType>& types);
 
         /**
          * Record drawcalls of all meshes of this node and its child nodes.
+         *
          * @param[in] viewProjection View-transformation and projection as 4x4 matrix
          * @param[out] pushConstants Structure to store push constants per drawcall
          * @param[out] drawcalls List of drawcall structures
@@ -71,19 +78,21 @@ namespace vkcv::scene {
          */
 		void recordDrawcalls(const glm::mat4& viewProjection,
 							 PushConstants& pushConstants,
-							 std::vector<DrawcallInfo>& drawcalls,
+							 std::vector<InstanceDrawcall>& drawcalls,
 							 const RecordMeshDrawcallFunction& record);
 
         /**
          * Splits child nodes into tree based graphs of nodes
          * until all nodes contain an amount of meshes below
          * a given maximum.
+         *
          * @param[in] maxMeshesPerNode Maximum amount of meshes per node
          */
 		void splitMeshesToSubNodes(size_t maxMeshesPerNode);
 
         /**
          * Return the sum of drawcalls in the graph of this node.
+         *
          * @return Amount of drawcalls
          */
 		[[nodiscard]]
@@ -92,12 +101,14 @@ namespace vkcv::scene {
         /**
          * Add a new node as child to the scene graph with this node
          * as parent and return its index.
+         *
          * @return Index of the new node
          */
 		size_t addNode();
 
         /**
          * Get a reference to the child node with a given index.
+         *
          * @param[in] index Valid index of a child node
          * @return Matching child node
          */
@@ -105,6 +116,7 @@ namespace vkcv::scene {
 
         /**
          * Get a const reference to the child node with a given index.
+         *
          * @param[in] index Valid index of a child node
          * @return Matching child node
          */
@@ -119,18 +131,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a scene node.
+         *
          * @param[in] other Other scene node
          */
 		Node(const Node& other) = default;
 
         /**
          * Move-constructor of a scene node.
+         *
          * @param[in,out] other Other scene node
          */
 		Node(Node&& other) = default;
 
         /**
          * Copy-operator of a scene node.
+         *
          * @param[in] other Other scene node
          * @return Reference of this node
          */
@@ -138,6 +153,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a scene node.
+         *
          * @param[in,out] other Other scene node
          * @return Reference of this node
          */
@@ -146,6 +162,7 @@ namespace vkcv::scene {
         /**
          * Return the axis aligned bounding box of the
          * scene node.
+         *
          * @return Axis aligned bounding box of this node
          */
 		[[nodiscard]]
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index c482b464a8a24e83f1ba12d8294749d7b0cae48a..74c5a50fe1db7ec7ba19a0757d922580b66ca67c 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -57,6 +57,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a scene instance with a given Core instance.
+         *
          * @param[in,out] core Pointer to valid Core instance
          */
 		explicit Scene(Core* core);
@@ -64,12 +65,14 @@ namespace vkcv::scene {
         /**
          * Add a new node to the first level of the scene graph with
          * this scene as parent and return its index.
+         *
          * @return Index of the new node
          */
 		size_t addNode();
 
         /**
          * Get a reference to the first-level node with a given index.
+         *
          * @param[in] index Valid index of a first-level node
          * @return Matching first-level node
          */
@@ -77,6 +80,7 @@ namespace vkcv::scene {
 
         /**
         * Get a const reference to the first-level node with a given index.
+         *
         * @param[in] index Valid index of a first-level node
         * @return Matching first-level node
         */
@@ -85,12 +89,14 @@ namespace vkcv::scene {
 
         /**
          * Increase the amount of usages for a certain material via its index.
+         *
          * @param[in] index Index of a material
          */
 		void increaseMaterialUsage(size_t index);
 
         /**
          * Decrease the amount of usages for a certain material via its index.
+         *
          * @param[in] index Index of a material
          */
 		void decreaseMaterialUsage(size_t index);
@@ -98,6 +104,7 @@ namespace vkcv::scene {
         /**
          * Load a material from a scene preloaded with the asset loader via
          * its index.
+         *
          * @param[in] index Valid index of a material
          * @param[in] scene Scene structure from asset loader
          * @param[in] material Material structure from asset loader
@@ -113,18 +120,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a scene instance.
+         *
          * @param[in] other Other scene instance
          */
 		Scene(const Scene& other);
 
         /**
          * Move-constructor of a scene instance.
+         *
          * @param[in,out] other Other scene instance
          */
         Scene(Scene&& other) noexcept;
 
         /**
          * Copy-operator of a scene instance.
+         *
          * @param[in] other Other scene instance
          * @return Reference to this scene
          */
@@ -132,6 +142,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a scene instance.
+         *
          * @param[in,out] other Other scene instance
          * @return Reference to this scene
          */
@@ -139,6 +150,7 @@ namespace vkcv::scene {
 
         /**
          * Return the amount of materials managed by this scene.
+         *
          * @return Amount of materials
          */
         [[nodiscard]]
@@ -147,6 +159,7 @@ namespace vkcv::scene {
         /**
          * Get the material data by its certain index.
          * The material can still be invalid if it was not loaded properly.
+         *
          * @param[in] index Valid index of material
          * @return Material
          */
@@ -155,6 +168,7 @@ namespace vkcv::scene {
 
         /**
          * Record drawcalls of all meshes of this scene with CPU-side frustum culling.
+         *
          * @param cmdStream Command stream handle
          * @param camera Scene viewing camera
          * @param pass Render pass handle
@@ -175,7 +189,8 @@ namespace vkcv::scene {
 
         /**
          * Instantiation function to create a new scene instance.
-         * @param core Current Core instance
+         *
+         * @param[in,out] core Current Core instance
          * @return New scene instance
          */
 		static Scene create(Core& core);
@@ -183,11 +198,15 @@ namespace vkcv::scene {
         /**
          * Load function to create a new scene instance with materials and meshes
          * loaded from a file using the asset loader.
-         * @param core Current Core instance
-         * @param path Path of a valid file to load via asset loader
+         *
+         * @param[in,out] core Current Core instance
+         * @param[in] path Path of a valid file to load via asset loader
+         * @param[in] types Primitive type order of vertex attributes
          * @return New scene instance
          */
-		static Scene load(Core& core, const std::filesystem::path &path);
+		static Scene load(Core& core,
+						  const std::filesystem::path &path,
+						  const std::vector<asset::PrimitiveType>& types);
 		
 	};
 
diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp
index af02aedbd71ba4bdfcc30aa7fdcd82796af904f1..3c6249fb556c9c2d7e08b41636718be9ef80f829 100644
--- a/modules/scene/src/vkcv/scene/Mesh.cpp
+++ b/modules/scene/src/vkcv/scene/Mesh.cpp
@@ -20,7 +20,9 @@ namespace vkcv::scene {
 		return matrix;
 	}
 	
-	void Mesh::load(const asset::Scene &scene, const asset::Mesh &mesh) {
+	void Mesh::load(const asset::Scene &scene,
+					const asset::Mesh &mesh,
+					const std::vector<asset::PrimitiveType>& types) {
 		m_parts.clear();
 		m_drawcalls.clear();
 		
@@ -32,7 +34,7 @@ namespace vkcv::scene {
 			}
 			
 			MeshPart part (m_scene);
-			part.load(scene, scene.vertexGroups[vertexGroupIndex], m_drawcalls);
+			part.load(scene, scene.vertexGroups[vertexGroupIndex], types, m_drawcalls);
 			
 			if (!part) {
 				continue;
@@ -67,7 +69,7 @@ namespace vkcv::scene {
 			m_parts[i] = other.m_parts[i];
 		}
 		
-		m_drawcalls = std::vector<DrawcallInfo>(other.m_drawcalls);
+		m_drawcalls = std::vector<InstanceDrawcall>(other.m_drawcalls);
 		m_transform = other.m_transform;
 		m_bounds = other.m_bounds;
 		
@@ -90,7 +92,7 @@ namespace vkcv::scene {
 	
 	void Mesh::recordDrawcalls(const glm::mat4& viewProjection,
 							   PushConstants& pushConstants,
-							   std::vector<DrawcallInfo>& drawcalls,
+							   std::vector<InstanceDrawcall>& drawcalls,
 							   const RecordMeshDrawcallFunction& record) {
 		const glm::mat4 transform = viewProjection * m_transform;
 		
diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp
index 50d14ed49d43496ada3853034be1455b044bd902..d7ceacff91172fce1050ffe12aa250b89fcebf6a 100644
--- a/modules/scene/src/vkcv/scene/MeshPart.cpp
+++ b/modules/scene/src/vkcv/scene/MeshPart.cpp
@@ -2,6 +2,8 @@
 #include "vkcv/scene/MeshPart.hpp"
 #include "vkcv/scene/Scene.hpp"
 
+#include <vkcv/Buffer.hpp>
+
 namespace vkcv::scene {
 	
 	MeshPart::MeshPart(Scene& scene) :
@@ -15,28 +17,25 @@ namespace vkcv::scene {
 	
 	void MeshPart::load(const asset::Scene& scene,
 						const asset::VertexGroup &vertexGroup,
-						std::vector<DrawcallInfo>& drawcalls) {
+						const std::vector<asset::PrimitiveType>& types,
+						std::vector<InstanceDrawcall>& drawcalls) {
 		Core& core = *(m_scene.m_core);
 		
-		auto vertexBuffer = core.createBuffer<uint8_t>(
-				BufferType::VERTEX, vertexGroup.vertexBuffer.data.size()
+		auto vertexBuffer = buffer<uint8_t>(
+				core, BufferType::VERTEX, vertexGroup.vertexBuffer.data.size()
 		);
 		
 		vertexBuffer.fill(vertexGroup.vertexBuffer.data);
 		m_vertices = vertexBuffer.getHandle();
 		
-		auto attributes = vertexGroup.vertexBuffer.attributes;
-		
-		std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-			return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-		});
-		
-		for (const auto& attribute : attributes) {
-			m_vertexBindings.emplace_back(attribute.offset, vertexBuffer.getVulkanHandle());
-		}
+		m_vertexBindings = asset::loadVertexBufferBindings(
+				vertexGroup.vertexBuffer.attributes,
+				vertexBuffer.getHandle(),
+				types
+		);
 		
-		auto indexBuffer = core.createBuffer<uint8_t>(
-				BufferType::INDEX, vertexGroup.indexBuffer.data.size()
+		auto indexBuffer = buffer<uint8_t>(
+				core, BufferType::INDEX, vertexGroup.indexBuffer.data.size()
 		);
 		
 		indexBuffer.fill(vertexGroup.indexBuffer.data);
@@ -86,10 +85,14 @@ namespace vkcv::scene {
 					break;
 			}
 			
-			drawcalls.push_back(DrawcallInfo(
-					vkcv::Mesh(m_vertexBindings, indexBuffer.getVulkanHandle(), m_indexCount, indexBitCount),
-					{ DescriptorSetUsage(0, material.getDescriptorSet()) }
-			));
+			VertexData vertexData (m_vertexBindings);
+			vertexData.setIndexBuffer(indexBuffer.getHandle(), indexBitCount);
+			vertexData.setCount(m_indexCount);
+			
+			InstanceDrawcall drawcall (vertexData);
+			drawcall.useDescriptorSet(0, material.getDescriptorSet());
+			
+			drawcalls.push_back(drawcall);
 		}
 	}
 	
diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp
index 24f62d18e160c7d80f82384829a2130737737ba9..f5234dd461537c491c0fa5580c78fbfec695a310 100644
--- a/modules/scene/src/vkcv/scene/Node.cpp
+++ b/modules/scene/src/vkcv/scene/Node.cpp
@@ -69,9 +69,11 @@ namespace vkcv::scene {
 		m_meshes.push_back(mesh);
 	}
 	
-	void Node::loadMesh(const asset::Scene &asset_scene, const asset::Mesh &asset_mesh) {
+	void Node::loadMesh(const asset::Scene &asset_scene,
+						const asset::Mesh &asset_mesh,
+						const std::vector<asset::PrimitiveType>& types) {
 		Mesh mesh (m_scene);
-		mesh.load(asset_scene, asset_mesh);
+		mesh.load(asset_scene, asset_mesh, types);
 		addMesh(mesh);
 	}
 	
@@ -92,7 +94,7 @@ namespace vkcv::scene {
 	
 	void Node::recordDrawcalls(const glm::mat4& viewProjection,
 							   PushConstants& pushConstants,
-							   std::vector<DrawcallInfo>& drawcalls,
+							   std::vector<InstanceDrawcall>& drawcalls,
 							   const RecordMeshDrawcallFunction& record) {
 		if (!checkFrustum(viewProjection, m_bounds)) {
 			return;
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 9a8ded0f70880125ed1efeda9a6845e1dc735d22..90d457c9ea7d5c4a7a1e30dbddd7086ced496e31 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -1,7 +1,9 @@
 
 #include "vkcv/scene/Scene.hpp"
 
+#include <vkcv/Image.hpp>
 #include <vkcv/Logger.hpp>
+#include <vkcv/Sampler.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 
 #include <vkcv/algorithm/SinglePassDownsampler.hpp>
@@ -124,7 +126,7 @@ namespace vkcv::scene {
 		});
 		
 		PushConstants pushConstants (pushConstantsSizePerDrawcall);
-		std::vector<DrawcallInfo> drawcalls;
+		std::vector<InstanceDrawcall> drawcalls;
 		size_t count = 0;
 		
 		const glm::mat4 viewProjection = camera.getMVP();
@@ -138,7 +140,6 @@ namespace vkcv::scene {
 		
 		m_core->recordDrawcallsToCmdStream(
 				cmdStream,
-				pass,
 				pipeline,
 				pushConstants,
 				drawcalls,
@@ -157,26 +158,87 @@ namespace vkcv::scene {
 						  const asset::Texture& asset_texture,
 						  const vk::Format& format,
 						  ImageHandle& image, SamplerHandle& sampler) {
-		asset::Sampler* asset_sampler = nullptr;
+		const asset::Sampler* asset_sampler = nullptr;
 		
 		if ((asset_texture.sampler >= 0) && (asset_texture.sampler < asset_scene.samplers.size())) {
-			//asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); // TODO
+			asset_sampler = &(asset_scene.samplers[asset_texture.sampler]);
 		}
 		
-		Image img = core.createImage(format, asset_texture.w, asset_texture.h, 1, true);
+		Image img = vkcv::image(core, format, asset_texture.w, asset_texture.h, 1, true);
 		img.fill(asset_texture.data.data());
 		image = img.getHandle();
 		
+		SamplerFilterType magFilter = SamplerFilterType::LINEAR;
+		SamplerFilterType minFilter = SamplerFilterType::LINEAR;
+		SamplerMipmapMode mipmapMode = SamplerMipmapMode::LINEAR;
+		SamplerAddressMode addressMode = SamplerAddressMode::REPEAT;
+		
+		float mipLodBias = 0.0f;
+		
 		if (asset_sampler) {
-			//sampler = core.createSampler(asset_sampler) // TODO
-		} else {
-			sampler = core.createSampler(
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerFilterType::LINEAR,
-					vkcv::SamplerMipmapMode::LINEAR,
-					vkcv::SamplerAddressMode::REPEAT
-			);
+			switch (asset_sampler->magFilter) {
+				case VK_FILTER_NEAREST:
+					magFilter = SamplerFilterType::NEAREST;
+					break;
+				case VK_FILTER_LINEAR:
+					magFilter = SamplerFilterType::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->minFilter) {
+				case VK_FILTER_NEAREST:
+					minFilter = SamplerFilterType::NEAREST;
+					break;
+				case VK_FILTER_LINEAR:
+					minFilter = SamplerFilterType::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->mipmapMode) {
+				case VK_SAMPLER_MIPMAP_MODE_NEAREST:
+					mipmapMode = SamplerMipmapMode::NEAREST;
+					break;
+				case VK_SAMPLER_MIPMAP_MODE_LINEAR:
+					mipmapMode = SamplerMipmapMode::LINEAR;
+					break;
+				default:
+					break;
+			}
+			
+			switch (asset_sampler->addressModeU) {
+				case VK_SAMPLER_ADDRESS_MODE_REPEAT:
+					addressMode = SamplerAddressMode::REPEAT;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
+					addressMode = SamplerAddressMode::MIRRORED_REPEAT;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
+					addressMode = SamplerAddressMode::CLAMP_TO_EDGE;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
+					addressMode = SamplerAddressMode::MIRROR_CLAMP_TO_EDGE;
+					break;
+				case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
+					addressMode = SamplerAddressMode::CLAMP_TO_BORDER;
+					break;
+				default:
+					break;
+			}
+			
+			mipLodBias = asset_sampler->minLOD;
 		}
+		
+		sampler = core.createSampler(
+				magFilter,
+				minFilter,
+				mipmapMode,
+				addressMode,
+				mipLodBias
+		);
 	}
 	
 	void Scene::loadMaterial(size_t index, const asset::Scene& scene,
@@ -254,7 +316,9 @@ namespace vkcv::scene {
 		);
 	}
 	
-	Scene Scene::load(Core& core, const std::filesystem::path &path) {
+	Scene Scene::load(Core& core,
+					  const std::filesystem::path &path,
+					  const std::vector<asset::PrimitiveType>& types) {
 		asset::Scene asset_scene;
 		
 		if (!asset::loadScene(path.string(), asset_scene)) {
@@ -273,15 +337,10 @@ namespace vkcv::scene {
 		const size_t root = scene.addNode();
 		
 		for (const auto& mesh : asset_scene.meshes) {
-			scene.getNode(root).loadMesh(asset_scene, mesh);
+			scene.getNode(root).loadMesh(asset_scene, mesh, types);
 		}
 		
-		vkcv::SamplerHandle sampler = core.createSampler(
-				vkcv::SamplerFilterType::LINEAR,
-				vkcv::SamplerFilterType::LINEAR,
-				vkcv::SamplerMipmapMode::LINEAR,
-				vkcv::SamplerAddressMode::REPEAT
-		);
+		vkcv::SamplerHandle sampler = samplerLinear(core);
 		
 		const vkcv::FeatureManager& featureManager = core.getContext().getFeatureManager();
 		
diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
index fd5bbd3795d62aee7dac6e80a72066556a5ffe07..64bc78aca4fe42ff813d05fc016a70c7108bd557 100644
--- a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
+++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
@@ -2,6 +2,7 @@
 
 #include "Upscaling.hpp"
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/ShaderProgram.hpp>
 
 namespace vkcv::upscaling {
diff --git a/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp
index 9a35bfd57fd261587eea0d327d2eb3154b1cd38a..913983400808f3353adf3a1c653045345d36c6f1 100644
--- a/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp
+++ b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp
@@ -2,6 +2,7 @@
 
 #include "Upscaling.hpp"
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/ShaderProgram.hpp>
 
 namespace vkcv::upscaling {
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index a470cd21aba04faf7087e883cadfecb322876553..247161e35718cde352da5461b51f30c4fe8570c7 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -164,12 +164,16 @@ namespace vkcv::upscaling {
 	m_rcasDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())),
 	m_rcasDescriptorSet(m_core.createDescriptorSet(m_rcasDescriptorSetLayout)),
 
-	m_easuConstants(m_core.createBuffer<FSRConstants>(
-			BufferType::UNIFORM,1,
+	m_easuConstants(buffer<FSRConstants>(
+			m_core,
+			BufferType::UNIFORM,
+			1,
 			BufferMemoryType::HOST_VISIBLE
 	)),
-	m_rcasConstants(m_core.createBuffer<FSRConstants>(
-			BufferType::UNIFORM,1,
+	m_rcasConstants(buffer<FSRConstants>(
+			m_core,
+			BufferType::UNIFORM,
+			1,
 			BufferMemoryType::HOST_VISIBLE
 	)),
 	m_intermediateImage(),
@@ -275,7 +279,7 @@ namespace vkcv::upscaling {
 					outputWidth, outputHeight,1,
 					false,
 					true
-			).getHandle();
+			);
 			
 			m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
 		}
@@ -302,10 +306,10 @@ namespace vkcv::upscaling {
 		
 		static const uint32_t threadGroupWorkRegionDim = 16;
 		
-		uint32_t dispatch[3];
-		dispatch[0] = (outputWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
-		dispatch[1] = (outputHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
-		dispatch[2] = 1;
+		DispatchSize dispatch = dispatchInvocations(
+				DispatchSize(outputWidth, outputHeight),
+				DispatchSize(threadGroupWorkRegionDim, threadGroupWorkRegionDim)
+		);
 		
 		m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle());
 		
diff --git a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
index 2fd8e23f6c0a3c615ec6085ad1abfdad1a9794ab..2b25c9132b20368f37568d95608b23b89645016e 100644
--- a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp
@@ -7,6 +7,7 @@
 #include "NIS_Scaler.h.hxx"
 
 #include <vkcv/File.hpp>
+#include <vkcv/Image.hpp>
 #include <vkcv/Logger.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 
@@ -77,14 +78,14 @@ namespace vkcv::upscaling {
 		const size_t rowPitch = kFilterSize * 2;
 		const size_t imageSize = rowPitch * kPhaseCount;
 		
-		Image image = core.createImage(
+		Image image = vkcv::image(
+				core,
 				vk::Format::eR16G16B16A16Sfloat,
 				kFilterSize / 4,
 				kPhaseCount
 		);
 		
 		image.fill(data, imageSize);
-		
 		return image.getHandle();
 	}
 	
@@ -137,8 +138,10 @@ namespace vkcv::upscaling {
 	m_scalerDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())),
 	m_scalerDescriptorSet(m_core.createDescriptorSet(m_scalerDescriptorSetLayout)),
 	
-	m_scalerConstants(m_core.createBuffer<uint8_t>(
-			BufferType::UNIFORM, sizeof(NISConfig),
+	m_scalerConstants(buffer<uint8_t>(
+			m_core,
+			BufferType::UNIFORM,
+			sizeof(NISConfig),
 			BufferMemoryType::HOST_VISIBLE
 	)),
 	m_sampler(m_core.createSampler(
@@ -233,10 +236,10 @@ namespace vkcv::upscaling {
 				sizeof(config)
 		);
 		
-		uint32_t dispatch[3];
-		dispatch[0] = (outputWidth + (m_blockWidth - 1)) / m_blockWidth;
-		dispatch[1] = (outputHeight + (m_blockHeight - 1)) / m_blockHeight;
-		dispatch[2] = 1;
+		DispatchSize dispatch = dispatchInvocations(
+				DispatchSize(outputWidth, outputHeight),
+				DispatchSize(m_blockWidth, m_blockHeight)
+		);
 		
 		m_core.recordBufferMemoryBarrier(cmdStream, m_scalerConstants.getHandle());
 		
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index 811e1162588fa699b4d74045504dc9c3d4de4f4c..e4dd4916b6a83a35abb809ed30f38cec67a01062 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -1,5 +1,9 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Image.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
@@ -46,6 +50,7 @@ int main(int argc, const char** argv) {
 	);
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true);
+	vkcv::Window& window = core.getWindow(windowHandle);
 
 	vkcv::asset::Scene mesh;
 
@@ -62,9 +67,14 @@ int main(int argc, const char** argv) {
         std::filesystem::path grassPath(grassPaths[i]);
         vkcv::asset::Texture grassTexture = vkcv::asset::loadTexture(grassPath);
 
-        vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, grassTexture.width, grassTexture.height);
+        vkcv::Image texture = vkcv::image(
+				core,
+				vk::Format::eR8G8B8A8Srgb,
+				grassTexture.width,
+				grassTexture.height
+		);
+		
         texture.fill(grassTexture.data.data());
-
         texturesArray.push_back(texture);
     }
 
@@ -79,7 +89,8 @@ int main(int argc, const char** argv) {
 	}
 
 	assert(!mesh.vertexGroups.empty());
-	auto vertexBuffer = core.createBuffer<uint8_t>(
+	auto vertexBuffer = vkcv::buffer<uint8_t>(
+			core,
 			vkcv::BufferType::VERTEX,
 			mesh.vertexGroups[0].vertexBuffer.data.size(),
 			vkcv::BufferMemoryType::DEVICE_LOCAL
@@ -87,30 +98,21 @@ int main(int argc, const char** argv) {
 	
 	vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
 
-	auto indexBuffer = core.createBuffer<uint8_t>(
+	auto indexBuffer = vkcv::buffer<uint8_t>(
+			core,
 			vkcv::BufferType::INDEX,
 			mesh.vertexGroups[0].indexBuffer.data.size(),
 			vkcv::BufferMemoryType::DEVICE_LOCAL
 	);
 	
 	indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
-
-	// an example attachment for passes that output to the window
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat()
-	);
 	
-	const vkcv::AttachmentDescription depth_attachment(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			vk::Format::eD32Sfloat
+	vkcv::PassHandle firstMeshPass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
 	);
 
-	vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }, vkcv::Multisampling::None);
-	vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition);
-
 	if (!firstMeshPass) {
 		std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
@@ -123,18 +125,20 @@ int main(int argc, const char** argv) {
 		{ vkcv::ShaderStage::VERTEX, "resources/shaders/shader.vert" },
 		{ vkcv::ShaderStage::FRAGMENT, "resources/shaders/shader.frag" }
 	}, nullptr);
- 
-	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
 	
-	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-	});
-
-    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
-	std::vector<vkcv::VertexBinding> bindings;
-	for (size_t i = 0; i < vertexAttachments.size(); i++) {
-		bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] }));
-	}
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
+	
+	std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings(
+			firstMeshProgram.getVertexAttachments()
+	);
 	
 	const vkcv::VertexLayout firstMeshLayout { bindings };
 	const std::unordered_map<uint32_t, vkcv::DescriptorBinding> &descriptorBindings = firstMeshProgram.getReflectedDescriptors().at(0);
@@ -145,16 +149,14 @@ int main(int argc, const char** argv) {
     vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(adjustedBindings);
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 
-	const vkcv::GraphicsPipelineConfig firstMeshPipelineConfig {
-        firstMeshProgram,
-        UINT32_MAX,
-        UINT32_MAX,
-        firstMeshPass,
-        {firstMeshLayout},
-		{ descriptorSetLayout },
-		true
-	};
-	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig);
+	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					firstMeshProgram,
+					firstMeshPass,
+					{ firstMeshLayout },
+					{ descriptorSetLayout }
+			)
+	);
 	
 	if (!firstMeshPipeline) {
 		std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
@@ -168,7 +170,7 @@ int main(int argc, const char** argv) {
 	
 	{
 		vkcv::asset::Texture &tex = mesh.textures[0];
-		vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
+		vkcv::Image texture = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
 		texture.fill(tex.data.data());
 		texturesArray.push_back(texture);
 	}
@@ -181,18 +183,7 @@ int main(int argc, const char** argv) {
 	
 	core.submitCommandStream(downsampleStream, false);
 
-	vkcv::SamplerHandle sampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::REPEAT
-	);
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
-
+	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
 	vkcv::DescriptorWrites setWrites;
 	
 	for(uint32_t i = 0; i < 6; i++)
@@ -214,31 +205,22 @@ int main(int argc, const char** argv) {
 	vkcv::ImageHandle depthBuffer;
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+	
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(indexBuffer.getHandle());
+	vertexData.setCount(mesh.vertexGroups[0].numIndices);
+	
+	vkcv::InstanceDrawcall drawcall (vertexData);
+	drawcall.useDescriptorSet(0, descriptorSet);
 
-	const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices);
-
-	vkcv::DescriptorSetUsage    descriptorUsage(0, descriptorSet);
-	vkcv::DrawcallInfo          drawcall(renderMesh, { descriptorUsage },1);
-
-    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+    vkcv::camera::CameraManager cameraManager(window);
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
 	
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
-
-    auto start = std::chrono::system_clock::now();
-    
-	while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-		
-		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
-			continue;
-		}
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
@@ -246,17 +228,13 @@ int main(int argc, const char** argv) {
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 		}
-  
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-		
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+
+		cameraManager.update(dt);
         glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 
-		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
+		vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();
 		pushConstants.appendDrawcall(mvp);
 
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
@@ -264,16 +242,16 @@ int main(int argc, const char** argv) {
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			firstMeshPass,
 			firstMeshPipeline,
 			pushConstants,
 			{ drawcall },
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
+		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp
index d430216f864ac3e7c5e4d4fa1f6339710b64cb93..738723ff7ee7974b33bc13d0f825258fb9f8048b 100644
--- a/projects/fire_works/src/main.cpp
+++ b/projects/fire_works/src/main.cpp
@@ -1,8 +1,11 @@
 
 #include <array>
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
-#include <vkcv/DrawcallRecording.hpp>
+#include <vkcv/Image.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
 
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -186,7 +189,7 @@ void InitializeNestedFireworkEvents(std::vector<event_t>& events) {
 }
 
 void ChangeColor(std::vector<event_t>& events, glm::vec3 color) {
-	for (int i = 0; i < events.size(); i++) {
+	for (size_t i = 0; i < events.size(); i++) {
 		events [i].color = color;
 	}
 }
@@ -195,6 +198,9 @@ int main(int argc, const char **argv) {
 	vkcv::Features features;
 	
 	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+	features.requireFeature([](vk::PhysicalDeviceFeatures& features) {
+		features.setGeometryShader(true);
+	});
 	
 	vkcv::Core core = vkcv::Core::create(
 		"Firework",
@@ -318,7 +324,8 @@ int main(int argc, const char **argv) {
 		{ smokeDescriptorLayout }
 	});
 	
-	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	auto swapchainHandle = core.getWindow(windowHandle).getSwapchain();
+	auto swapchainExtent = core.getSwapchainExtent(swapchainHandle);
 	
 	const vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
 	
@@ -329,7 +336,7 @@ int main(int argc, const char **argv) {
 				swapchainExtent.width,
 				swapchainExtent.height,
 				1, false, true, true
-		).getHandle();
+		);
 	}
 	
 	vkcv::ShaderProgram particleShaderProgram;
@@ -361,18 +368,21 @@ int main(int argc, const char **argv) {
 	particles.resize(PARTICLE_COUNT);
 	InitializeParticles(particles);
 	
-	vkcv::Buffer<particle_t> particleBuffer = core.createBuffer<particle_t>(
-		vkcv::BufferType::STORAGE,
-		particles.size(),
-		vkcv::BufferMemoryType::DEVICE_LOCAL,
-		false,
-		true
+	auto particleBuffer = vkcv::buffer<particle_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			particles.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL,
+			true
 	);
 	
 	particleBuffer.fill(particles);
 	
-	vkcv::Buffer<particle_t> particleBufferCopy =
-		core.createBuffer<particle_t>(vkcv::BufferType::STORAGE, particles.size());
+	auto particleBufferCopy = vkcv::buffer<particle_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			particles.size()
+	);
 
 	particleBufferCopy.fill(particles);
 	
@@ -392,9 +402,10 @@ int main(int argc, const char **argv) {
 		);
 	}
 	
-	vkcv::Buffer<float> randomBuffer = core.createBuffer<float>(
-		vkcv::BufferType::STORAGE,
-		randomData.size()
+	auto randomBuffer = vkcv::buffer<float>(
+			core,
+			vkcv::BufferType::STORAGE,
+			randomData.size()
 	);
 	
 	randomBuffer.fill(randomData);
@@ -402,9 +413,10 @@ int main(int argc, const char **argv) {
 	std::vector<event_t> events;
 	InitializeFireworkEvents(events);
 	
-	vkcv::Buffer<event_t> eventBuffer = core.createBuffer<event_t>(
-		vkcv::BufferType::STORAGE,
-		events.size()
+	auto eventBuffer = vkcv::buffer<event_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			events.size()
 	);
 	
 	eventBuffer.fill(events);
@@ -416,8 +428,11 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(generationDescriptorSet, writes);
 	}
 
-	vkcv::Buffer<uint32_t> startIndexBuffer =
-		core.createBuffer<uint32_t>(vkcv::BufferType::STORAGE, eventBuffer.getCount());
+	auto startIndexBuffer = vkcv::buffer<uint32_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			eventBuffer.getCount()
+	);
 
 	{
 		vkcv::DescriptorWrites writes;
@@ -441,15 +456,19 @@ int main(int argc, const char **argv) {
 		smokes.push_back(smoke);
 	}
 	
-	vkcv::Buffer<smoke_t> smokeBuffer = core.createBuffer<smoke_t>(
-		vkcv::BufferType::STORAGE,
-		smokes.size()
+	auto smokeBuffer = vkcv::buffer<smoke_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			smokes.size()
 	);
 	
 	smokeBuffer.fill(smokes);
 	
-	vkcv::Buffer<uint32_t> smokeIndexBuffer = core.createBuffer<uint32_t>(
-		vkcv::BufferType::STORAGE, 3, vkcv::BufferMemoryType::HOST_VISIBLE
+	auto smokeIndexBuffer = vkcv::buffer<uint32_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			3,
+			vkcv::BufferMemoryType::HOST_VISIBLE
 	);
 	
 	uint32_t* smokeIndices = smokeIndexBuffer.map();
@@ -478,9 +497,10 @@ int main(int argc, const char **argv) {
 		trails.push_back(trail);
 	}
 	
-	vkcv::Buffer<trail_t> trailBuffer = core.createBuffer<trail_t>(
-		vkcv::BufferType::STORAGE,
-		trails.size()
+	auto trailBuffer = vkcv::buffer<trail_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			trails.size()
 	);
 	
 	trailBuffer.fill(trails);
@@ -499,9 +519,10 @@ int main(int argc, const char **argv) {
 		points.push_back(point);
 	}
 	
-	vkcv::Buffer<point_t> pointBuffer = core.createBuffer<point_t>(
-		vkcv::BufferType::STORAGE,
-		points.size()
+	auto pointBuffer = vkcv::buffer<point_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			points.size()
 	);
 	
 	pointBuffer.fill(points);
@@ -513,7 +534,12 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(trailDescriptorSet, writes);
 	}
 	
-	vkcv::Buffer<glm::vec3> cubePositions = core.createBuffer<glm::vec3>(vkcv::BufferType::VERTEX, 8);
+	auto cubePositions = vkcv::buffer<glm::vec3>(
+			core,
+			vkcv::BufferType::VERTEX,
+			8
+	);
+	
 	cubePositions.fill({
 		glm::vec3(-1.0f, -1.0f, -1.0f),
 		glm::vec3(+1.0f, -1.0f, -1.0f),
@@ -525,7 +551,12 @@ int main(int argc, const char **argv) {
 		glm::vec3(+1.0f, +1.0f, +1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> cubeIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 36);
+	auto cubeIndices = vkcv::buffer<uint16_t>(
+			core,
+			vkcv::BufferType::INDEX,
+			36
+	);
+	
 	cubeIndices.fill({
 		0, 2, 3,
 		0, 3, 1,
@@ -543,11 +574,9 @@ int main(int argc, const char **argv) {
 		1, 4, 0
 	});
 	
-	vkcv::Mesh cubeMesh (
-		{ vkcv::VertexBufferBinding(0, cubePositions.getVulkanHandle()) },
-		cubeIndices.getVulkanHandle(),
-		cubeIndices.getCount()
-	);
+	vkcv::VertexData cubeData ({ vkcv::vertexBufferBinding(cubePositions.getHandle()) });
+	cubeData.setIndexBuffer(cubeIndices.getHandle());
+	cubeData.setCount(cubeIndices.getCount());
 	
 	const std::vector<vkcv::VertexAttachment> vaSmoke = smokeShaderProgram.getVertexAttachments();
 	
@@ -558,28 +587,16 @@ int main(int argc, const char **argv) {
 	
 	const vkcv::VertexLayout smokeLayout { vbSmoke };
 	
-	vkcv::PassHandle renderPass = core.createPass(vkcv::PassConfig(
-		{
-			vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::CLEAR,
-				colorFormat
-				)
-		},
-		vkcv::Multisampling::None
-	));
-	
-	vkcv::GraphicsPipelineConfig smokePipelineDefinition{
+	vkcv::PassHandle renderPass = vkcv::passFormat(core, colorFormat);
+	
+	vkcv::GraphicsPipelineConfig smokePipelineDefinition (
 		smokeShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{smokeLayout},
-		{smokeDescriptorLayout, generationDescriptorLayout},
-		true
-	};
+		{smokeDescriptorLayout, generationDescriptorLayout}
+	);
 	
-	smokePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	smokePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle smokePipeline = core.createGraphicsPipeline(smokePipelineDefinition);
 	
@@ -592,67 +609,56 @@ int main(int argc, const char **argv) {
 	
 	const vkcv::VertexLayout trailLayout { vbTrail };
 	
-	vkcv::GraphicsPipelineConfig trailPipelineDefinition{
+	vkcv::GraphicsPipelineConfig trailPipelineDefinition (
 		trailShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{trailLayout},
-		{trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout},
-		true
-	};
+		{trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout}
+	);
 	
-	trailPipelineDefinition.m_PrimitiveTopology = vkcv::PrimitiveTopology::PointList;
-	trailPipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	trailPipelineDefinition.setPrimitiveTopology(vkcv::PrimitiveTopology::PointList);
+	trailPipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle trailPipeline = core.createGraphicsPipeline(trailPipelineDefinition);
 	
-	std::vector<vkcv::DrawcallInfo> drawcallsSmokes;
+	vkcv::InstanceDrawcall drawcallSmoke (cubeData, smokeBuffer.getCount());
+	drawcallSmoke.useDescriptorSet(0, smokeDescriptorSet);
+	drawcallSmoke.useDescriptorSet(1, generationDescriptorSet);
 	
-	drawcallsSmokes.push_back(vkcv::DrawcallInfo(
-		cubeMesh,
-		{
-			vkcv::DescriptorSetUsage(0, smokeDescriptorSet),
-			vkcv::DescriptorSetUsage(1, generationDescriptorSet),
-		},
-		smokeBuffer.getCount()
-	));
+	auto trianglePositions = vkcv::buffer<glm::vec2>(
+			core,
+			vkcv::BufferType::VERTEX,
+			3
+	);
 	
-	vkcv::Buffer<glm::vec2> trianglePositions = core.createBuffer<glm::vec2>(vkcv::BufferType::VERTEX, 3);
 	trianglePositions.fill({
 		glm::vec2(-1.0f, -1.0f),
 		glm::vec2(+0.0f, +1.5f),
 		glm::vec2(+1.0f, -1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> triangleIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3);
+	auto triangleIndices = vkcv::buffer<uint16_t>(
+			core,
+			vkcv::BufferType::INDEX,
+			3
+	);
+	
 	triangleIndices.fill({
 		0, 1, 2
 	});
 	
-	vkcv::Mesh triangleMesh (
-		{ vkcv::VertexBufferBinding(0, trianglePositions.getVulkanHandle()) },
-		triangleIndices.getVulkanHandle(),
-		triangleIndices.getCount()
-	);
-	
-	vkcv::Mesh trailMesh (
-		{},
-		triangleIndices.getVulkanHandle(),
-		1
-	);
+	vkcv::VertexData triangleData ({ vkcv::vertexBufferBinding(trianglePositions.getHandle()) });
+	triangleData.setIndexBuffer(triangleIndices.getHandle());
+	triangleData.setCount(triangleIndices.getCount());
 	
-	std::vector<vkcv::DrawcallInfo> drawcallsTrails;
+	vkcv::VertexData trailData;
+	triangleData.setIndexBuffer(triangleIndices.getHandle());
+	trailData.setCount(1);
 	
-	drawcallsTrails.push_back(vkcv::DrawcallInfo(
-		trailMesh,
-		{
-			vkcv::DescriptorSetUsage(0, trailDescriptorSet),
-			vkcv::DescriptorSetUsage(1, generationDescriptorSet),
-			vkcv::DescriptorSetUsage(2, descriptorSet)
-		},
-		trailBuffer.getCount()
-	));
+	vkcv::InstanceDrawcall drawcallTrail (trailData, trailBuffer.getCount());
+	drawcallTrail.useDescriptorSet(0, trailDescriptorSet);
+	drawcallTrail.useDescriptorSet(1, generationDescriptorSet);
+	drawcallTrail.useDescriptorSet(2, descriptorSet);
 	
 	const std::vector<vkcv::VertexAttachment> vaParticles = particleShaderProgram.getVertexAttachments();
 	
@@ -663,27 +669,19 @@ int main(int argc, const char **argv) {
 
 	const vkcv::VertexLayout particleLayout { vbParticles };
 
-	vkcv::GraphicsPipelineConfig particlePipelineDefinition{
+	vkcv::GraphicsPipelineConfig particlePipelineDefinition (
 		particleShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{particleLayout},
-		{descriptorSetLayout},
-		true
-	};
+		{descriptorSetLayout}
+	);
 	
-	particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition);
 	
-	std::vector<vkcv::DrawcallInfo> drawcallsParticles;
-	
-	drawcallsParticles.push_back(vkcv::DrawcallInfo(
-		triangleMesh,
-		{ vkcv::DescriptorSetUsage(0, descriptorSet) },
-		particleBuffer.getCount()
-	));
+	vkcv::InstanceDrawcall drawcallParticle (triangleData, particleBuffer.getCount());
+	drawcallParticle.useDescriptorSet(0, descriptorSet);
 	
 	vkcv::ShaderProgram motionShader;
 	compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/motion.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
@@ -702,36 +700,40 @@ int main(int argc, const char **argv) {
 	std::vector<uint32_t> zeroVoxel;
 	zeroVoxel.resize(voxelWidth * voxelHeight * voxelDepth, 0);
 	
-	vkcv::Image voxelRed = core.createImage(
-		vk::Format::eR32Uint,
-		voxelWidth,
-		voxelHeight,
-		voxelDepth,
-		false, true
+	vkcv::Image voxelRed = vkcv::image(
+			core,
+			vk::Format::eR32Uint,
+			voxelWidth,
+			voxelHeight,
+			voxelDepth,
+			false, true
 	);
 	
-	vkcv::Image voxelGreen = core.createImage(
-		vk::Format::eR32Uint,
-		voxelWidth,
-		voxelHeight,
-		voxelDepth,
-		false, true
+	vkcv::Image voxelGreen = vkcv::image(
+			core,
+			vk::Format::eR32Uint,
+			voxelWidth,
+			voxelHeight,
+			voxelDepth,
+			false, true
 	);
 	
-	vkcv::Image voxelBlue = core.createImage(
-		vk::Format::eR32Uint,
-		voxelWidth,
-		voxelHeight,
-		voxelDepth,
-		false, true
+	vkcv::Image voxelBlue = vkcv::image(
+			core,
+			vk::Format::eR32Uint,
+			voxelWidth,
+			voxelHeight,
+			voxelDepth,
+			false, true
 	);
 	
-	vkcv::Image voxelDensity = core.createImage(
-		vk::Format::eR32Uint,
-		voxelWidth,
-		voxelHeight,
-		voxelDepth,
-		false, true
+	vkcv::Image voxelDensity = vkcv::image(
+			core,
+			vk::Format::eR32Uint,
+			voxelWidth,
+			voxelHeight,
+			voxelDepth,
+			false, true
 	);
 	
 	std::array<vkcv::ImageHandle, 2> voxelData {
@@ -741,29 +743,25 @@ int main(int argc, const char **argv) {
 			voxelHeight,
 			voxelDepth,
 			false, true
-		).getHandle(),
+		),
 		core.createImage(
 			vk::Format::eR16G16B16A16Sfloat,
 			voxelWidth,
 			voxelHeight,
 			voxelDepth,
 			false, true
-		).getHandle()
+		)
 	};
 	
-	vkcv::Image voxelSamples = core.createImage(
-		colorFormat,
-		voxelWidth,
-		voxelHeight,
-		1, false, true
+	vkcv::Image voxelSamples = vkcv::image(
+			core,
+			colorFormat,
+			voxelWidth,
+			voxelHeight,
+			1, false, true
    	);
 	
-	vkcv::SamplerHandle voxelSampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::CLAMP_TO_EDGE
-	);
+	vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true);
 	
 	vkcv::ShaderProgram voxelClearShader;
 	compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/clear.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
@@ -944,7 +942,7 @@ int main(int argc, const char **argv) {
 					swapchainWidth,
 					swapchainHeight,
 					1, false, true, true
-				).getHandle();
+				);
 			}
 		}
 		
@@ -963,10 +961,10 @@ int main(int argc, const char **argv) {
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		uint32_t voxelDispatchCount[3];
-		voxelDispatchCount[0] = std::ceil(voxelWidth / 4.f);
-		voxelDispatchCount[1] = std::ceil(voxelHeight / 4.f);
-		voxelDispatchCount[2] = std::ceil(voxelDepth / 4.f);
+		auto voxelDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(voxelWidth, voxelHeight, voxelDepth),
+				vkcv::DispatchSize(4, 4, 4)
+		);
 		
 		core.recordBeginDebugLabel(cmdStream, "Voxel clear", { 0.5f, 0.25f, 0.8f, 1.0f });
 		core.prepareImageForStorage(cmdStream, voxelRed.getHandle());
@@ -978,7 +976,7 @@ int main(int argc, const char **argv) {
 			cmdStream,
 			voxelClearPipeline,
 			voxelDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, voxelDescriptorSet) },
+			{ vkcv::useDescriptorSet(0, voxelDescriptorSet) },
 			vkcv::PushConstants(0)
 		);
 		core.recordEndDebugLabel(cmdStream);
@@ -990,10 +988,7 @@ int main(int argc, const char **argv) {
 		core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle());
 		
-		uint32_t particleDispatchCount[3];
-		particleDispatchCount[0] = std::ceil(particleBuffer.getCount() / 256.f);
-		particleDispatchCount[1] = 1;
-		particleDispatchCount[2] = 1;
+		auto particleDispatchCount = vkcv::dispatchInvocations(particleBuffer.getCount(), 256);
 		
 		vkcv::PushConstants pushConstantsTime (2 * sizeof(float));
 		pushConstantsTime.appendDrawcall(time_values);
@@ -1004,10 +999,10 @@ int main(int argc, const char **argv) {
 			generationPipeline,
 			particleDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, descriptorSet),
-				vkcv::DescriptorSetUsage(1, generationDescriptorSet),
-				vkcv::DescriptorSetUsage(2, smokeDescriptorSet),
-				vkcv::DescriptorSetUsage(3, trailDescriptorSet)
+				vkcv::useDescriptorSet(0, descriptorSet),
+				vkcv::useDescriptorSet(1, generationDescriptorSet),
+				vkcv::useDescriptorSet(2, smokeDescriptorSet),
+				vkcv::useDescriptorSet(3, trailDescriptorSet)
 			},
 			pushConstantsTime
 		);
@@ -1015,17 +1010,14 @@ int main(int argc, const char **argv) {
 		
 		core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle());
 		
-		uint32_t smokeDispatchCount[3];
-		smokeDispatchCount[0] = std::ceil(smokeBuffer.getCount() / 256.f);
-		smokeDispatchCount[1] = 1;
-		smokeDispatchCount[2] = 1;
+		auto smokeDispatchCount = vkcv::dispatchInvocations(smokeBuffer.getCount(), 256);
 		
 		core.recordBeginDebugLabel(cmdStream, "Smoke scaling", { 0.0f, 0.0f, 1.0f, 1.0f });
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
 			scalePipeline,
 			smokeDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, smokeDescriptorSet) },
+			{ vkcv::useDescriptorSet(0, smokeDescriptorSet) },
 			pushConstantsTime
 		);
 		core.recordEndDebugLabel(cmdStream);
@@ -1037,7 +1029,7 @@ int main(int argc, const char **argv) {
 			cmdStream,
 			motionPipeline,
 			particleDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, descriptorSet) },
+			{ vkcv::useDescriptorSet(0, descriptorSet) },
 			pushConstantsTime
 		);
 		core.recordEndDebugLabel(cmdStream);
@@ -1046,10 +1038,7 @@ int main(int argc, const char **argv) {
 		core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle());
 		
-		uint32_t trailDispatchCount[3];
-		trailDispatchCount[0] = std::ceil(trailBuffer.getCount() / 256.f);
-		trailDispatchCount[1] = 1;
-		trailDispatchCount[2] = 1;
+		auto trailDispatchCount = vkcv::dispatchInvocations(trailBuffer.getCount(), 256);
 		
 		core.recordBeginDebugLabel(cmdStream, "Trail update", { 0.0f, 0.0f, 1.0f, 1.0f });
 		core.recordComputeDispatchToCmdStream(
@@ -1057,8 +1046,8 @@ int main(int argc, const char **argv) {
 			trailComputePipeline,
 			trailDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, descriptorSet),
-				vkcv::DescriptorSetUsage(1, trailDescriptorSet)
+				vkcv::useDescriptorSet(0, descriptorSet),
+				vkcv::useDescriptorSet(1, trailDescriptorSet)
 			},
 			pushConstantsTime
 		);
@@ -1082,10 +1071,9 @@ int main(int argc, const char **argv) {
 		core.recordBeginDebugLabel(cmdStream, "Draw particles", { 1.0f, 0.0f, 1.0f, 1.0f });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			particlePipeline,
 			pushConstantsDraw0,
-			{ drawcallsParticles },
+			{ drawcallParticle },
 			{ colorBuffers[0] },
 			windowHandle
 		);
@@ -1105,8 +1093,8 @@ int main(int argc, const char **argv) {
 			voxelParticlePipeline,
 			particleDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, descriptorSet),
-				vkcv::DescriptorSetUsage(1, voxelDescriptorSet)
+				vkcv::useDescriptorSet(0, descriptorSet),
+				vkcv::useDescriptorSet(1, voxelDescriptorSet)
 			},
 			pushConstantsVoxel
 		);
@@ -1125,10 +1113,9 @@ int main(int argc, const char **argv) {
 		
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			smokePipeline,
 			pushConstantsDraw1,
-			{ drawcallsSmokes },
+			{ drawcallSmoke },
 			{ colorBuffers[1] },
 			windowHandle
 		);
@@ -1145,8 +1132,8 @@ int main(int argc, const char **argv) {
 			voxelSmokePipeline,
 			smokeDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, smokeDescriptorSet),
-				vkcv::DescriptorSetUsage(1, voxelDescriptorSet)
+				vkcv::useDescriptorSet(0, smokeDescriptorSet),
+				vkcv::useDescriptorSet(1, voxelDescriptorSet)
 			},
 			pushConstantsVoxel
 		);
@@ -1158,10 +1145,9 @@ int main(int argc, const char **argv) {
 		core.recordBeginDebugLabel(cmdStream, "Draw trails", { 0.75f, 0.5f, 1.0f, 1.0f });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			trailPipeline,
 			pushConstantsDraw1,
-			{ drawcallsTrails },
+			{ drawcallTrail },
 			{ colorBuffers[2] },
 			windowHandle
 		);
@@ -1178,8 +1164,8 @@ int main(int argc, const char **argv) {
 			voxelTrailPipeline,
 			trailDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, trailDescriptorSet),
-				vkcv::DescriptorSetUsage(1, voxelDescriptorSet)
+				vkcv::useDescriptorSet(0, trailDescriptorSet),
+				vkcv::useDescriptorSet(1, voxelDescriptorSet)
 			},
 			pushConstantsVoxel
 		);
@@ -1199,8 +1185,8 @@ int main(int argc, const char **argv) {
 			voxelPipeline,
 			voxelDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, voxelDescriptorSet),
-				vkcv::DescriptorSetUsage(1, voxelOutDescriptorSet)
+				vkcv::useDescriptorSet(0, voxelDescriptorSet),
+				vkcv::useDescriptorSet(1, voxelOutDescriptorSet)
 			},
 			vkcv::PushConstants(0)
 		);
@@ -1217,7 +1203,7 @@ int main(int argc, const char **argv) {
 				cmdStream,
 				fluidPipeline,
 				voxelDispatchCount,
-				{ vkcv::DescriptorSetUsage(0, fluidDescriptorSet[i % 2]) },
+				{ vkcv::useDescriptorSet(0, fluidDescriptorSet[i % 2]) },
 				vkcv::PushConstants(0)
 			);
 		}
@@ -1229,18 +1215,18 @@ int main(int argc, const char **argv) {
 		core.prepareImageForStorage(cmdStream, voxelData[0]);
 		core.prepareImageForStorage(cmdStream, voxelSamples.getHandle());
 		
-		uint32_t sampleDispatchCount[3];
-		sampleDispatchCount[0] = std::ceil(voxelWidth / 8.f);
-		sampleDispatchCount[1] = std::ceil(voxelHeight / 8.f);
-		sampleDispatchCount[2] = 1;
+		auto sampleDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(voxelWidth, voxelHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 		
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
 			voxelSamplePipeline,
 			sampleDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, voxelOutDescriptorSet),
-				vkcv::DescriptorSetUsage(1, samplesDescriptorSet)
+				vkcv::useDescriptorSet(0, voxelOutDescriptorSet),
+				vkcv::useDescriptorSet(1, samplesDescriptorSet)
 			},
 			vkcv::PushConstants(0)
 		);
@@ -1261,18 +1247,18 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(addDescriptor, addDescriptorWrites);
 		core.prepareImageForSampling(cmdStream, voxelSamples.getHandle());
 		
-		uint32_t colorDispatchCount[3];
-		colorDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-		colorDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-		colorDispatchCount[2] = 1;
+		auto colorDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 		
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
 			addPipe,
 			colorDispatchCount,
 			{
-				vkcv::DescriptorSetUsage(0, addDescriptor),
-				vkcv::DescriptorSetUsage(1, generationDescriptorSet)
+				vkcv::useDescriptorSet(0, addDescriptor),
+				vkcv::useDescriptorSet(1, generationDescriptorSet)
 			},
 			vkcv::PushConstants(0)
 		);
@@ -1298,7 +1284,7 @@ int main(int argc, const char **argv) {
 			cmdStream,
 			tonemappingPipe,
 			colorDispatchCount,
-			{vkcv::DescriptorSetUsage(0, tonemappingDescriptor) },
+			{ vkcv::useDescriptorSet(0, tonemappingDescriptor) },
 			vkcv::PushConstants(0)
 		);
 		
@@ -1310,11 +1296,15 @@ int main(int argc, const char **argv) {
 		gui.beginGUI();
 		ImGui::Begin("Settings");
 		
-		bool listbox = ImGui::BeginListBox(" ");
-		bool firework = ImGui::Selectable("Firework");
-		bool sparkler = ImGui::Selectable("Sparkler");
-		bool nested = ImGui::Selectable("Nested Firework");
-		ImGui::EndListBox();
+		bool firework, sparkler, nested;
+		if (ImGui::BeginListBox(" ")) {
+			firework = ImGui::Selectable("Firework");
+			sparkler = ImGui::Selectable("Sparkler");
+			nested = ImGui::Selectable("Nested Firework");
+			
+			ImGui::EndListBox();
+		}
+		
 		bool resetTime = ImGui::Button("Reset");
 		auto color = glm::vec3(0.0f);
 		
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index fa671a5507a7621586497048128e8959f72cdf08..88ab4b70d72f30a3e622d8579cb75929039bc2cf 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -1,7 +1,10 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Image.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
 #include <vkcv/camera/CameraManager.hpp>
-#include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 
@@ -16,6 +19,7 @@ int main(int argc, const char** argv) {
 	);
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true);
+	vkcv::Window& window = core.getWindow(windowHandle);
 
 	vkcv::asset::Scene mesh;
 
@@ -30,7 +34,8 @@ int main(int argc, const char** argv) {
 	}
 
 	assert(!mesh.vertexGroups.empty());
-	auto vertexBuffer = core.createBuffer<uint8_t>(
+	auto vertexBuffer = vkcv::buffer<uint8_t>(
+			core,
 			vkcv::BufferType::VERTEX,
 			mesh.vertexGroups[0].vertexBuffer.data.size(),
 			vkcv::BufferMemoryType::DEVICE_LOCAL
@@ -38,7 +43,8 @@ int main(int argc, const char** argv) {
 	
 	vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
 
-	auto indexBuffer = core.createBuffer<uint8_t>(
+	auto indexBuffer = vkcv::buffer<uint8_t>(
+			core,
 			vkcv::BufferType::INDEX,
 			mesh.vertexGroups[0].indexBuffer.data.size(),
 			vkcv::BufferMemoryType::DEVICE_LOCAL
@@ -46,26 +52,12 @@ int main(int argc, const char** argv) {
 	
 	indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
 
-	// an example attachment for passes that output to the window
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat()
-	);
-	
-	const vkcv::AttachmentDescription depth_attachment(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			vk::Format::eD32Sfloat
-	);
-
-	vkcv::PassConfig firstMeshPassDefinition(
-			{ present_color_attachment, depth_attachment },
-			vkcv::Multisampling::None
+	vkcv::PassHandle firstMeshPass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
 	);
 
-	vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition);
-
 	if (!firstMeshPass) {
 		std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
@@ -78,18 +70,20 @@ int main(int argc, const char** argv) {
 		{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
 		{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" }
 	}, nullptr);
- 
-	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
 	
-	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-	});
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
 
-    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
-	std::vector<vkcv::VertexBinding> bindings;
-	for (size_t i = 0; i < vertexAttachments.size(); i++) {
-		bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] }));
-	}
+	std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings(
+			firstMeshProgram.getVertexAttachments()
+	);
 	
 	const vkcv::VertexLayout firstMeshLayout { bindings };
 
@@ -102,17 +96,15 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetLayoutHandle setLayoutHandleCopy = core.createDescriptorSetLayout(set0BindingsExplicitCopy);
 
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(setLayoutHandle);
-
-	const vkcv::GraphicsPipelineConfig firstMeshPipelineConfig {
-        firstMeshProgram,
-        UINT32_MAX,
-        UINT32_MAX,
-        firstMeshPass,
-        {firstMeshLayout},
-		{ setLayoutHandle },
-		true
-	};
-	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig);
+	
+	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					firstMeshProgram,
+					firstMeshPass,
+					{ firstMeshLayout },
+					{ setLayoutHandle }
+			)
+	);
 	
 	if (!firstMeshPipeline) {
 		std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
@@ -125,7 +117,7 @@ int main(int argc, const char** argv) {
 	}
 	
 	vkcv::asset::Texture &tex = mesh.textures[0];
-	vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
+	vkcv::Image texture = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
 	texture.fill(tex.data.data());
 	
 	{
@@ -134,17 +126,7 @@ int main(int argc, const char** argv) {
 		core.submitCommandStream(cmdStream, false);
 	}
 
-	vkcv::SamplerHandle sampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::REPEAT
-	);
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
+	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
 
 	vkcv::DescriptorWrites setWrites;
 	setWrites.writeSampledImage(0, texture.getHandle());
@@ -155,30 +137,21 @@ int main(int argc, const char** argv) {
 	vkcv::ImageHandle depthBuffer;
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+	
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(indexBuffer.getHandle());
+	vertexData.setCount(mesh.vertexGroups[0].numIndices);
+	
+	vkcv::InstanceDrawcall drawcall (vertexData);
+	drawcall.useDescriptorSet(0, descriptorSet);
 
-	const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices);
-
-	vkcv::DescriptorSetUsage    descriptorUsage(0, descriptorSet);
-	vkcv::DrawcallInfo          drawcall(renderMesh, { descriptorUsage },1);
-
-    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+    vkcv::camera::CameraManager cameraManager(window);
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
-
-    auto start = std::chrono::system_clock::now();
-    
-	while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-		
-		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
@@ -186,17 +159,13 @@ int main(int argc, const char** argv) {
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 		}
-  
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
 		
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
         glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 
-		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
+		vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();
 		pushConstants.appendDrawcall(mvp);
 
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
@@ -204,16 +173,16 @@ int main(int argc, const char** argv) {
 
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			firstMeshPass,
 			firstMeshPipeline,
 			pushConstants,
 			{ drawcall },
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
+		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index b09943e4b9432ac09c6eaaa5bbe06b0b28b59ab3..6b29b14bd92725af9439796c42012ba1d6bd4c80 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -1,5 +1,6 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/gui/GUI.hpp>
@@ -44,28 +45,21 @@ int main(int argc, const char** argv) {
 	
 	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
 
-	vkcv::scene::Scene scene = vkcv::scene::Scene::load(core, std::filesystem::path(
-			argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"
-	));
-
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat()
-	);
-
-	const vkcv::AttachmentDescription depth_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		vk::Format::eD32Sfloat
-	);
-
-	vkcv::PassConfig scenePassDefinition(
-			{ present_color_attachment, depth_attachment },
-			vkcv::Multisampling::None
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(
+			core,
+			std::filesystem::path(argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"),
+			{
+				vkcv::asset::PrimitiveType::POSITION,
+				vkcv::asset::PrimitiveType::NORMAL,
+				vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
 	);
 	
-	vkcv::PassHandle scenePass = core.createPass(scenePassDefinition);
+	vkcv::PassHandle scenePass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
+	);
 
 	if (!scenePass) {
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
@@ -89,17 +83,15 @@ int main(int argc, const char** argv) {
 
 	const vkcv::VertexLayout sceneLayout { bindings };
 	const auto& material0 = scene.getMaterial(0);
-
-	const vkcv::GraphicsPipelineConfig scenePipelineDefinition{
-		sceneShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
-		scenePass,
-		{sceneLayout},
-		{ material0.getDescriptorSetLayout() },
-		true
-	};
-	vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefinition);
+	
+	vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					sceneShaderProgram,
+					scenePass,
+					{ sceneLayout },
+					{ material0.getDescriptorSetLayout() }
+			)
+	);
 	
 	if (!scenePipeline) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
@@ -110,18 +102,8 @@ int main(int argc, const char** argv) {
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 	
-	auto start = std::chrono::system_clock::now();
-	while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-		
-		if(window.getHeight() == 0 || window.getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
-			continue;
-		}
-		
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
@@ -129,49 +111,42 @@ int main(int argc, const char** argv) {
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 		}
-  
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
 		
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
 
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
 		auto recordMesh = [](const glm::mat4& MVP, const glm::mat4& M,
 							 vkcv::PushConstants &pushConstants,
-							 vkcv::DrawcallInfo& drawcallInfo) {
+							 vkcv::Drawcall& drawcall) {
 			pushConstants.appendDrawcall(MVP);
 		};
 		
-		scene.recordDrawcalls(cmdStream,
-							  cameraManager.getActiveCamera(),
-							  scenePass,
-							  scenePipeline,
-							  sizeof(glm::mat4),
-							  recordMesh,
-							  renderTargets,
-							  windowHandle);
+		scene.recordDrawcalls(
+				cmdStream,
+				cameraManager.getActiveCamera(),
+				scenePass,
+				scenePipeline,
+				sizeof(glm::mat4),
+				recordMesh,
+				renderTargets,
+				windowHandle
+		);
 		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 
-        auto stop = std::chrono::system_clock::now();
-        auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
-
         gui.beginGUI();
 
         ImGui::Begin("Settings");
-        ImGui::Text("Deltatime %fms, %f", 0.001 * static_cast<double>(kektime.count()), 1/(0.000001 * static_cast<double>(kektime.count())));
+        ImGui::Text("Deltatime %fms, %f", dt * 1000, 1/dt);
         ImGui::End();
 
         gui.endGUI();
-
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index c2d8bf817dd1d6b2c7cc069f2037ee090abc4415..18c62285959775b2097f4b72fb13bf789480c914 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -1,9 +1,10 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
-#include <chrono>
 
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Triangle";
@@ -21,23 +22,19 @@ int main(int argc, const char** argv) {
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
 	vkcv::Window& window = core.getWindow(windowHandle);
 
-	auto triangleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, vkcv::BufferMemoryType::DEVICE_LOCAL);
+	auto triangleIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3);
 	uint16_t indices[3] = { 0, 1, 2 };
 	triangleIndexBuffer.fill(&indices[0], sizeof(indices));
 
 	core.setDebugLabel(triangleIndexBuffer.getHandle(), "Triangle Index Buffer");
 	
-	// an example attachment for passes that output to the window
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat());
-
-	vkcv::PassConfig trianglePassDefinition({ present_color_attachment }, vkcv::Multisampling::None);
-	vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition);
+	vkcv::PassHandle trianglePass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined }
+	);
 
-	if (!trianglePass)
-	{
+	if (!trianglePass) {
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
@@ -52,77 +49,56 @@ int main(int argc, const char** argv) {
 		{ vkcv::ShaderStage::FRAGMENT, "shaders/shader.frag" }
 	}, nullptr);
 
-	const vkcv::GraphicsPipelineConfig trianglePipelineDefinition {
-		triangleShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
-		trianglePass,
-		{},
-		{},
-		true
-	};
-
-	vkcv::GraphicsPipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition);
+	vkcv::GraphicsPipelineHandle trianglePipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					triangleShaderProgram,
+					trianglePass,
+					{},
+					{}
+			)
+	);
 
-	if (!trianglePipeline)
-	{
+	if (!trianglePipeline) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
 	
 	core.setDebugLabel(trianglePipeline, "Triangle Pipeline");
-	
-	auto start = std::chrono::system_clock::now();
 
-	const vkcv::Mesh renderMesh({}, triangleIndexBuffer.getVulkanHandle(), 3);
-	vkcv::DrawcallInfo drawcall(renderMesh, {},1);
+	vkcv::VertexData vertexData;
+	vertexData.setIndexBuffer(triangleIndexBuffer.getHandle());
+	vertexData.setCount(3);
+	
+	vkcv::InstanceDrawcall drawcall (vertexData);
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
-	core.setDebugLabel(swapchainInput, "Swapchain Image");
 
     vkcv::camera::CameraManager cameraManager(window);
-    uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
-    uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
+    uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	
-	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2));
-    cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
-    cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
+	cameraManager.getCamera(camIndex).setPosition(glm::vec3(0, 0, -2));
 
-	while (vkcv::Window::hasOpenWindow())
-	{
-        vkcv::Window::pollEvents();
-
-		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+			uint32_t swapchainWidth, uint32_t swapchainHeight) {
+		cameraManager.update(dt);
 		
-        auto end = std::chrono::system_clock::now();
-        auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-        start = end;
-		
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
-        glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
-
-		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
-		pushConstants.appendDrawcall(mvp);
+		glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		core.setDebugLabel(cmdStream, "Render Commands");
-
+		
 		core.recordDrawcallsToCmdStream(
-			cmdStream,
-			trianglePass,
-			trianglePipeline,
-			pushConstants,
-			{ drawcall },
-			{ swapchainInput },
-			windowHandle);
-
+				cmdStream,
+				trianglePipeline,
+				vkcv::pushConstants<glm::mat4>(mvp),
+				{ drawcall },
+				{ swapchainInput },
+				windowHandle
+		);
+		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-	    
-	    core.endFrame(windowHandle);
-	}
+	});
+	
 	return 0;
 }
diff --git a/projects/head_demo/src/main.cpp b/projects/head_demo/src/main.cpp
index 89fb5e08d1c4a5ceec8c43f3c4043f8978d5d404..69daa8ba7c98523f9b287b210d5144cb154ec40b 100644
--- a/projects/head_demo/src/main.cpp
+++ b/projects/head_demo/src/main.cpp
@@ -1,5 +1,7 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/gui/GUI.hpp>
@@ -36,49 +38,19 @@ int main(int argc, const char** argv) {
 	
 	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
 	
-	vkcv::scene::Scene scene = vkcv::scene::Scene::load(core, std::filesystem::path(
-			argc > 1 ? argv[1] : "assets/skull_scaled/scene.gltf"
-	));
-	
-	vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
-	
-	const vkcv::AttachmentDescription color_attachment0(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			colorFormat
-	);
-	
-	const vkcv::AttachmentDescription depth_attachment0(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			vk::Format::eD32Sfloat
-	);
-	
-	const vkcv::AttachmentDescription color_attachment1(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::LOAD,
-			colorFormat
-	);
-	
-	const vkcv::AttachmentDescription depth_attachment1(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::LOAD,
-			vk::Format::eD32Sfloat
-	);
-	
-	vkcv::PassConfig linePassDefinition(
-			{ color_attachment0, depth_attachment0 },
-			vkcv::Multisampling::None
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(
+			core,
+			std::filesystem::path(argc > 1 ? argv[1] : "assets/skull_scaled/scene.gltf"),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL
+			}
 	);
 	
-	vkcv::PassHandle linePass = core.createPass(linePassDefinition);
-	
-	vkcv::PassConfig scenePassDefinition(
-			{ color_attachment1, depth_attachment1 },
-			vkcv::Multisampling::None
-	);
+	vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
 	
-	vkcv::PassHandle scenePass = core.createPass(scenePassDefinition);
+	vkcv::PassHandle linePass = vkcv::passFormats(core, { colorFormat, vk::Format::eD32Sfloat });
+	vkcv::PassHandle scenePass = vkcv::passFormats(core, { colorFormat, vk::Format::eD32Sfloat }, false);
 	
 	if ((!scenePass) || (!linePass)) {
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
@@ -118,7 +90,7 @@ int main(int argc, const char** argv) {
 	float clipY = 0.0f;
 	float clipZ = 0.0f;
 	
-	auto clipBuffer = core.createBuffer<float>(vkcv::BufferType::UNIFORM, 4);
+	auto clipBuffer = vkcv::buffer<float>(core, vkcv::BufferType::UNIFORM, 4);
 	clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ });
 	
 	vkcv::DescriptorWrites clipWrites;
@@ -151,85 +123,66 @@ int main(int argc, const char** argv) {
 	const vkcv::VertexLayout sceneLayout { bindings };
 	const auto& material0 = scene.getMaterial(0);
 	
-	const vkcv::GraphicsPipelineConfig scenePipelineDefinition{
-			sceneShaderProgram,
-			UINT32_MAX,
-			UINT32_MAX,
-			scenePass,
-			{sceneLayout},
-			{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout },
-			true
-	};
-	
-	const vkcv::GraphicsPipelineConfig linePipelineDefinition{
-			lineShaderProgram,
-			UINT32_MAX,
-			UINT32_MAX,
-			linePass,
-			{sceneLayout},
-			{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout },
-			true
-	};
-	
-	vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefinition);
-	vkcv::GraphicsPipelineHandle linePipeline = core.createGraphicsPipeline(linePipelineDefinition);
+	vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+				sceneShaderProgram,
+				scenePass,
+				{ sceneLayout },
+				{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout }
+			)
+	);
+	
+	vkcv::GraphicsPipelineHandle linePipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					lineShaderProgram,
+					linePass,
+					{ sceneLayout },
+					{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout }
+			)
+	);
 	
 	if ((!scenePipeline) || (!linePipeline)) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
 	
-	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
 			swapchainExtent.width,
 			swapchainExtent.height
-	).getHandle();
+	);
 	
 	vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, true, true
-	).getHandle();
+	);
 	
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
 	vkcv::upscaling::FSRUpscaling upscaling (core);
 	
-	auto start = std::chrono::system_clock::now();
-	while (vkcv::Window::hasOpenWindow()) {
-		vkcv::Window::pollEvents();
-		
-		if(window.getHeight() == 0 || window.getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
-			continue;
-		}
-		
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) {
-			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight);
 			
 			colorBuffer = core.createImage(
 					colorFormat,
 					swapchainExtent.width,
 					swapchainExtent.height,
 					1, false, true, true
-			).getHandle();
+			);
 			
 			swapchainExtent.width = swapchainWidth;
 			swapchainExtent.height = swapchainHeight;
 		}
-		
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-		
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+
+		cameraManager.update(dt);
 		
 		clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ });
 		
@@ -238,32 +191,34 @@ int main(int argc, const char** argv) {
 		
 		auto recordMesh = [&](const glm::mat4& MVP, const glm::mat4& M,
 							 vkcv::PushConstants &pushConstants,
-							 vkcv::DrawcallInfo& drawcallInfo) {
+							 vkcv::Drawcall& drawcall) {
 			pushConstants.appendDrawcall(MVP);
-			drawcallInfo.descriptorSets.push_back(
-					vkcv::DescriptorSetUsage(1, clipDescriptorSet)
-			);
+			drawcall.useDescriptorSet(1, clipDescriptorSet);
 		};
 		
-		scene.recordDrawcalls(cmdStream,
-							  cameraManager.getActiveCamera(),
-							  linePass,
-							  linePipeline,
-							  sizeof(glm::mat4),
-							  recordMesh,
-							  renderTargets,
-							  windowHandle);
+		scene.recordDrawcalls(
+				cmdStream,
+				cameraManager.getActiveCamera(),
+				linePass,
+				linePipeline,
+				sizeof(glm::mat4),
+				recordMesh,
+				renderTargets,
+				windowHandle
+		);
 		
 		bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer);
 		
-		scene.recordDrawcalls(cmdStream,
-							  cameraManager.getActiveCamera(),
-							  scenePass,
-							  scenePipeline,
-							  sizeof(glm::mat4),
-							  recordMesh,
-							  renderTargets,
-							  windowHandle);
+		scene.recordDrawcalls(
+				cmdStream,
+				cameraManager.getActiveCamera(),
+				scenePass,
+				scenePipeline,
+				sizeof(glm::mat4),
+				recordMesh,
+				renderTargets,
+				windowHandle
+		);
 		
 		core.prepareImageForSampling(cmdStream, colorBuffer);
 		core.prepareImageForStorage(cmdStream, swapchainInput);
@@ -272,9 +227,6 @@ int main(int argc, const char** argv) {
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 		
-		auto stop = std::chrono::system_clock::now();
-		auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
-		
 		gui.beginGUI();
 		
 		ImGui::Begin("Settings");
@@ -285,9 +237,7 @@ int main(int argc, const char** argv) {
 		ImGui::End();
 		
 		gui.endGUI();
-		
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp
index e6885b8e7dfa71c00335dbb2cee5c5994ce4eccc..c52acec190bb1a6e405107b4b231922fc6bad846 100644
--- a/projects/indirect_dispatch/src/App.cpp
+++ b/projects/indirect_dispatch/src/App.cpp
@@ -1,7 +1,10 @@
 #include "App.hpp"
 #include "AppConfig.hpp"
-#include <chrono>
+
+#include <vkcv/Sampler.hpp>
 #include <vkcv/gui/GUI.hpp>
+
+#include <chrono>
 #include <functional>
 
 const char* MotionVectorVisualisationModeLabels[6] = {
@@ -60,12 +63,7 @@ bool App::initialize() {
 	if (!m_motionBlur.initialize(&m_core, m_windowWidth, m_windowHeight))
 		return false;
 
-	m_linearSampler = m_core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::CLAMP_TO_EDGE);
-
+	m_linearSampler = vkcv::samplerLinear(m_core, true);
 	m_renderTargets = createRenderTargets(m_core, m_windowWidth, m_windowHeight);
 
 	const int cameraIndex = m_cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
@@ -82,10 +80,10 @@ bool App::initialize() {
 
 void App::run() {
 
-	auto                        frameStartTime = std::chrono::system_clock::now();
-	const auto                  appStartTime   = std::chrono::system_clock::now();
-	const vkcv::ImageHandle     swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
-	const vkcv::DrawcallInfo    skyDrawcall(m_cubeMesh.mesh, {}, 1);
+	auto                         frameStartTime = std::chrono::system_clock::now();
+	const auto                   appStartTime   = std::chrono::system_clock::now();
+	const vkcv::ImageHandle      swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+	const vkcv::InstanceDrawcall skyDrawcall(m_cubeMesh.mesh);
 
 	vkcv::gui::GUI gui(m_core, m_windowHandle);
 
@@ -216,14 +214,13 @@ void App::run() {
 			m_renderTargets.motionBuffer,
 			m_renderTargets.depthBuffer };
 
-		std::vector<vkcv::DrawcallInfo> prepassSceneDrawcalls;
+		std::vector<vkcv::InstanceDrawcall> prepassSceneDrawcalls;
 		for (const Object& obj : sceneObjects) {
-			prepassSceneDrawcalls.push_back(vkcv::DrawcallInfo(obj.meshResources.mesh, {}));
+			prepassSceneDrawcalls.push_back(vkcv::InstanceDrawcall(obj.meshResources.mesh));
 		}
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_prePass.renderPass,
 			m_prePass.pipeline,
 			prepassPushConstants,
 			prepassSceneDrawcalls,
@@ -239,7 +236,6 @@ void App::run() {
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_skyPrePass.renderPass,
 			m_skyPrePass.pipeline,
 			skyPrepassPushConstants,
 			{ skyDrawcall },
@@ -257,16 +253,15 @@ void App::run() {
 			meshPushConstants.appendDrawcall(matrices);
 		}
 
-		std::vector<vkcv::DrawcallInfo> forwardSceneDrawcalls;
+		std::vector<vkcv::InstanceDrawcall> forwardSceneDrawcalls;
 		for (const Object& obj : sceneObjects) {
-			forwardSceneDrawcalls.push_back(vkcv::DrawcallInfo(
-				obj.meshResources.mesh, 
-				{ vkcv::DescriptorSetUsage(0, m_meshPass.descriptorSet) }));
+			vkcv::InstanceDrawcall drawcall (obj.meshResources.mesh);
+			drawcall.useDescriptorSet(0, m_meshPass.descriptorSet);
+			forwardSceneDrawcalls.push_back(drawcall);
 		}
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_meshPass.renderPass,
 			m_meshPass.pipeline,
 			meshPushConstants,
 			forwardSceneDrawcalls,
@@ -274,12 +269,11 @@ void App::run() {
 			m_windowHandle);
 
 		// sky
-		vkcv::PushConstants skyPushConstants(sizeof(glm::mat4));
+		vkcv::PushConstants skyPushConstants = vkcv::pushConstants<glm::mat4>();
 		skyPushConstants.appendDrawcall(viewProjection);
 
 		m_core.recordDrawcallsToCmdStream(
 			cmdStream,
-			m_skyPass.renderPass,
 			m_skyPass.pipeline,
 			skyPushConstants,
 			{ skyDrawcall },
@@ -303,7 +297,7 @@ void App::run() {
 				cameraNear,
 				cameraFar,
 				fDeltaTimeSeconds,
-				cameraShutterSpeedInverse,
+				static_cast<float>(cameraShutterSpeedInverse),
 				motionBlurTileOffsetLength,
 				motionBlurFastPathThreshold);
 		}
@@ -326,16 +320,16 @@ void App::run() {
 		m_core.prepareImageForSampling(cmdStream, motionBlurOutput);
 		m_core.prepareImageForStorage (cmdStream, swapchainInput);
 
-		const uint32_t fullScreenImageDispatch[3] = {
-			static_cast<uint32_t>((m_windowWidth  + 7) / 8),
-			static_cast<uint32_t>((m_windowHeight + 7) / 8),
-			static_cast<uint32_t>(1) };
+		const auto fullScreenImageDispatch = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(m_windowWidth, m_windowHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
 		m_core.recordComputeDispatchToCmdStream(
 			cmdStream,
 			m_gammaCorrectionPass.pipeline,
 			fullScreenImageDispatch,
-			{ vkcv::DescriptorSetUsage(0, m_gammaCorrectionPass.descriptorSet) },
+			{ vkcv::useDescriptorSet(0, m_gammaCorrectionPass.descriptorSet) },
 			vkcv::PushConstants(0));
 
 		m_core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp
index 933f20db73313e25bc9e4863f2bf4530df26a366..26cfbbc38e2a190326b8a17adde5b8e4f44ec1cc 100644
--- a/projects/indirect_dispatch/src/AppSetup.cpp
+++ b/projects/indirect_dispatch/src/AppSetup.cpp
@@ -1,5 +1,8 @@
 #include "AppSetup.hpp"
 #include "AppConfig.hpp"
+
+#include <vkcv/Buffer.hpp>
+#include <vkcv/Image.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 
@@ -23,12 +26,14 @@ bool loadMesh(vkcv::Core& core, const std::filesystem::path& path, MeshResources
 	auto& vertexData = scene.vertexGroups[0].vertexBuffer;
 	auto& indexData  = scene.vertexGroups[0].indexBuffer;
 
-	vkcv::Buffer vertexBuffer = core.createBuffer<uint8_t>(
+	vkcv::Buffer<uint8_t> vertexBuffer = vkcv::buffer<uint8_t>(
+		core,
 		vkcv::BufferType::VERTEX,
 		vertexData.data.size(),
 		vkcv::BufferMemoryType::DEVICE_LOCAL);
 
-	vkcv::Buffer indexBuffer = core.createBuffer<uint8_t>(
+	vkcv::Buffer<uint8_t> indexBuffer = vkcv::buffer<uint8_t>(
+		core,
 		vkcv::BufferType::INDEX,
 		indexData.data.size(),
 		vkcv::BufferMemoryType::DEVICE_LOCAL);
@@ -38,20 +43,20 @@ bool loadMesh(vkcv::Core& core, const std::filesystem::path& path, MeshResources
 
 	outMesh->vertexBuffer = vertexBuffer.getHandle();
 	outMesh->indexBuffer  = indexBuffer.getHandle();
-
-	auto& attributes = vertexData.attributes;
-
-	std::sort(attributes.begin(), attributes.end(),
-		[](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-	});
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
-
-	outMesh->mesh = vkcv::Mesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), scene.vertexGroups[0].numIndices);
+	
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			vertexData.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
+
+	outMesh->mesh = vkcv::VertexData(vertexBufferBindings);
+	outMesh->mesh.setIndexBuffer(indexBuffer.getHandle());
+	outMesh->mesh.setCount(scene.vertexGroups[0].numIndices);
 
 	return true;
 }
@@ -67,12 +72,14 @@ bool loadImage(vkcv::Core& core, const std::filesystem::path& path, vkcv::ImageH
 		return false;
 	}
 
-	vkcv::Image image = core.createImage(
-		vk::Format::eR8G8B8A8Srgb, 
-		textureData.width, 
-		textureData.height, 
-		1, 
-		true);
+	vkcv::Image image = vkcv::image(
+			core,
+			vk::Format::eR8G8B8A8Srgb,
+			textureData.width,
+			textureData.height,
+			1,
+			true
+	);
 
 	image.fill(textureData.data.data(), textureData.data.size());
 	
@@ -133,19 +140,16 @@ bool loadGraphicPass(
 	    outPassHandles->descriptorSet = core.createDescriptorSet(outPassHandles->descriptorSetLayout);
 	    descriptorSetLayouts.push_back(outPassHandles->descriptorSetLayout);
 	}
-
-
-	vkcv::GraphicsPipelineConfig pipelineConfig{
+	
+	vkcv::GraphicsPipelineConfig pipelineConfig(
 		shaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		outPassHandles->renderPass,
 		{ vertexLayout },
-		descriptorSetLayouts,
-		true
-	};
-	pipelineConfig.m_depthTest  = depthTest;
-	outPassHandles->pipeline    = core.createGraphicsPipeline(pipelineConfig);
+		descriptorSetLayouts
+	);
+	
+	pipelineConfig.setDepthTest(depthTest);
+	outPassHandles->pipeline = core.createGraphicsPipeline(pipelineConfig);
 
 	if (!outPassHandles->pipeline) {
 		vkcv_log(vkcv::LogLevel::ERROR, "Error: Could not create graphics pipeline");
@@ -160,14 +164,16 @@ bool loadMeshPass(vkcv::Core& core, GraphicPassHandles* outHandles) {
 	assert(outHandles);
 
 	vkcv::AttachmentDescription colorAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::DONT_CARE,
-		AppConfig::colorBufferFormat);
+			AppConfig::colorBufferFormat,
+			vkcv::AttachmentOperation::DONT_CARE,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	vkcv::AttachmentDescription depthAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		AppConfig::depthBufferFormat);
+			AppConfig::depthBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	return loadGraphicPass(
 		core,
@@ -186,14 +192,16 @@ bool loadSkyPass(vkcv::Core& core, GraphicPassHandles* outHandles) {
 	assert(outHandles);
 
 	vkcv::AttachmentDescription colorAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		AppConfig::colorBufferFormat);
+			AppConfig::colorBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	vkcv::AttachmentDescription depthAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		AppConfig::depthBufferFormat);
+			AppConfig::depthBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	return loadGraphicPass(
 		core,
@@ -211,14 +219,16 @@ bool loadPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) {
 	assert(outHandles);
 
 	vkcv::AttachmentDescription motionAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		AppConfig::motionBufferFormat);
+			AppConfig::motionBufferFormat,
+			vkcv::AttachmentOperation::CLEAR,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	vkcv::AttachmentDescription depthAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		AppConfig::depthBufferFormat);
+			AppConfig::depthBufferFormat,
+			vkcv::AttachmentOperation::CLEAR,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	return loadGraphicPass(
 		core,
@@ -236,14 +246,16 @@ bool loadSkyPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) {
 	assert(outHandles);
 
 	vkcv::AttachmentDescription motionAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		AppConfig::motionBufferFormat);
+			AppConfig::motionBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	vkcv::AttachmentDescription depthAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		AppConfig::depthBufferFormat);
+			AppConfig::depthBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
+	);
 
 	return loadGraphicPass(
 		core,
@@ -288,7 +300,6 @@ bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, Comput
 }
 
 AppRenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, const uint32_t height) {
-
 	AppRenderTargets targets;
 
 	targets.depthBuffer = core.createImage(
@@ -296,7 +307,8 @@ AppRenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, con
 		width,
 		height,
 		1,
-		false).getHandle();
+		false
+	);
 
 	targets.colorBuffer = core.createImage(
 		AppConfig::colorBufferFormat,
@@ -305,7 +317,8 @@ AppRenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, con
 		1,
 		false,
 		false,
-		true).getHandle();
+		true
+	);
 
 	targets.motionBuffer = core.createImage(
 		AppConfig::motionBufferFormat,
@@ -314,7 +327,8 @@ AppRenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, con
 		1,
 		false,
 		false,
-		true).getHandle();
+		true
+	);
 
 	return targets;
 }
\ No newline at end of file
diff --git a/projects/indirect_dispatch/src/AppSetup.hpp b/projects/indirect_dispatch/src/AppSetup.hpp
index b0ade4317b125fea581016bf9924cd740a443c35..41e020c357a3d868775a581170596e1748e39700 100644
--- a/projects/indirect_dispatch/src/AppSetup.hpp
+++ b/projects/indirect_dispatch/src/AppSetup.hpp
@@ -21,7 +21,7 @@ struct ComputePassHandles {
 };
 
 struct MeshResources {
-	vkcv::Mesh          mesh;
+	vkcv::VertexData    mesh;
 	vkcv::BufferHandle  vertexBuffer;
 	vkcv::BufferHandle  indexBuffer;
 };
diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp
index c9345684225e64f792a13ade2e5d11297ab7c444..fe55a5821b042feb709deffee806513b18ed06ba 100644
--- a/projects/indirect_dispatch/src/MotionBlur.cpp
+++ b/projects/indirect_dispatch/src/MotionBlur.cpp
@@ -1,19 +1,11 @@
 #include "MotionBlur.hpp"
 #include "MotionBlurConfig.hpp"
 #include "MotionBlurSetup.hpp"
-#include <array>
 
-std::array<uint32_t, 3> computeFullscreenDispatchSize(
-	const uint32_t imageWidth,
-	const uint32_t imageHeight,
-	const uint32_t localGroupSize) {
+#include <vkcv/Buffer.hpp>
+#include <vkcv/Sampler.hpp>
 
-	// optimized divide and ceil
-	return std::array<uint32_t, 3>{
-		static_cast<uint32_t>(imageWidth  + (localGroupSize - 1)) / localGroupSize,
-		static_cast<uint32_t>(imageHeight + (localGroupSize - 1)) / localGroupSize,
-		static_cast<uint32_t>(1) };
-}
+#include <array>
 
 bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, const uint32_t targetHeight) {
 
@@ -56,23 +48,23 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con
 		((MotionBlurConfig::maxWidth + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize) *
 		((MotionBlurConfig::maxHeight + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize));
 
-	m_copyPathWorkTileBuffer = m_core->createBuffer<uint32_t>(
-		vkcv::BufferType::STORAGE, 
+	m_copyPathWorkTileBuffer = vkcv::buffer<uint32_t>(
+		*m_core,
+		vkcv::BufferType::INDIRECT,
 		workTileBufferSize, 
-		vkcv::BufferMemoryType::DEVICE_LOCAL, 
-		true).getHandle();
+		vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle();
 
-	m_fullPathWorkTileBuffer = m_core->createBuffer<uint32_t>(
-		vkcv::BufferType::STORAGE, 
+	m_fullPathWorkTileBuffer = vkcv::buffer<uint32_t>(
+		*m_core,
+		vkcv::BufferType::INDIRECT,
 		workTileBufferSize, 
-		vkcv::BufferMemoryType::DEVICE_LOCAL, 
-		true).getHandle();
+		vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle();
 
-	m_fastPathWorkTileBuffer = m_core->createBuffer<uint32_t>(
-		vkcv::BufferType::STORAGE,
+	m_fastPathWorkTileBuffer = vkcv::buffer<uint32_t>(
+		*m_core,
+		vkcv::BufferType::INDIRECT,
 		workTileBufferSize,
-		vkcv::BufferMemoryType::DEVICE_LOCAL,
-		true).getHandle();
+		vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle();
 
 	vkcv::DescriptorWrites tileResetDescriptorWrites;
 	tileResetDescriptorWrites.writeStorageBuffer(
@@ -87,11 +79,7 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con
 
 	m_renderTargets = MotionBlurSetup::createRenderTargets(targetWidth, targetHeight, *m_core);
 
-	m_nearestSampler = m_core->createSampler(
-		vkcv::SamplerFilterType::NEAREST,
-		vkcv::SamplerFilterType::NEAREST,
-		vkcv::SamplerMipmapMode::NEAREST,
-		vkcv::SamplerAddressMode::CLAMP_TO_EDGE);
+	m_nearestSampler = vkcv::samplerNearest(*m_core, true);
 	
 	return true;
 }
@@ -115,15 +103,13 @@ vkcv::ImageHandle MotionBlur::render(
 
 	computeMotionTiles(cmdStream, motionBufferFullRes);
 
-	// work tile reset
-	const uint32_t dispatchSizeOne[3] = { 1, 1, 1 };
-
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_tileResetPass.pipeline,
-		dispatchSizeOne,
-		{ vkcv::DescriptorSetUsage(0, m_tileResetPass.descriptorSet) },
-		vkcv::PushConstants(0));
+		1,
+		{ vkcv::useDescriptorSet(0, m_tileResetPass.descriptorSet) },
+		vkcv::PushConstants(0)
+	);
 
 	m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer);
 	m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer);
@@ -148,10 +134,13 @@ vkcv::ImageHandle MotionBlur::render(
 
 	m_core->writeDescriptorSet(m_tileClassificationPass.descriptorSet, tileClassificationDescriptorWrites);
 
-	const auto tileClassificationDispatch = computeFullscreenDispatchSize(
-		m_core->getImageWidth(m_renderTargets.motionMaxNeighbourhood), 
-		m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood),
-		8);
+	const auto tileClassificationDispatch = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth(m_renderTargets.motionMaxNeighbourhood),
+					m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	struct ClassificationConstants {
 		uint32_t    width;
@@ -163,7 +152,7 @@ vkcv::ImageHandle MotionBlur::render(
 	classificationConstants.height              = m_core->getImageHeight(m_renderTargets.outputColor);
 	classificationConstants.fastPathThreshold   = fastPathThreshold;
 
-	vkcv::PushConstants classificationPushConstants(sizeof(ClassificationConstants));
+	vkcv::PushConstants classificationPushConstants = vkcv::pushConstants<ClassificationConstants>();
     classificationPushConstants.appendDrawcall(classificationConstants);
 
 	m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMaxNeighbourhood);
@@ -172,8 +161,8 @@ vkcv::ImageHandle MotionBlur::render(
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_tileClassificationPass.pipeline,
-		tileClassificationDispatch.data(),
-		{ vkcv::DescriptorSetUsage(0, m_tileClassificationPass.descriptorSet) },
+		tileClassificationDispatch,
+		{ vkcv::useDescriptorSet(0, m_tileClassificationPass.descriptorSet) },
 		classificationPushConstants);
 
 	m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer);
@@ -235,7 +224,7 @@ vkcv::ImageHandle MotionBlur::render(
 	motionBlurConstantData.cameraFarPlane           = cameraFar;
 	motionBlurConstantData.motionTileOffsetLength   = motionTileOffsetLength;
 
-	vkcv::PushConstants motionBlurPushConstants(sizeof(motionBlurConstantData));
+	vkcv::PushConstants motionBlurPushConstants = vkcv::pushConstants<MotionBlurConstantData>();
 	motionBlurPushConstants.appendDrawcall(motionBlurConstantData);
 
 	struct FastPathConstants {
@@ -244,7 +233,7 @@ vkcv::ImageHandle MotionBlur::render(
 	FastPathConstants fastPathConstants;
 	fastPathConstants.motionFactor = motionBlurConstantData.motionFactor;
 
-	vkcv::PushConstants fastPathPushConstants(sizeof(FastPathConstants));
+	vkcv::PushConstants fastPathPushConstants = vkcv::pushConstants<FastPathConstants>();
 	fastPathPushConstants.appendDrawcall(fastPathConstants);
 
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.outputColor);
@@ -258,7 +247,7 @@ vkcv::ImageHandle MotionBlur::render(
 			m_motionBlurPass.pipeline,
 			m_fullPathWorkTileBuffer,
 			0,
-			{ vkcv::DescriptorSetUsage(0, m_motionBlurPass.descriptorSet) },
+			{ vkcv::useDescriptorSet(0, m_motionBlurPass.descriptorSet) },
 			motionBlurPushConstants);
 
 		m_core->recordComputeIndirectDispatchToCmdStream(
@@ -266,7 +255,7 @@ vkcv::ImageHandle MotionBlur::render(
 			m_colorCopyPass.pipeline,
 			m_copyPathWorkTileBuffer,
 			0,
-			{ vkcv::DescriptorSetUsage(0, m_colorCopyPass.descriptorSet) },
+			{ vkcv::useDescriptorSet(0, m_colorCopyPass.descriptorSet) },
 			vkcv::PushConstants(0));
 
 		m_core->recordComputeIndirectDispatchToCmdStream(
@@ -274,7 +263,7 @@ vkcv::ImageHandle MotionBlur::render(
 			m_motionBlurFastPathPass.pipeline,
 			m_fastPathWorkTileBuffer,
 			0,
-			{ vkcv::DescriptorSetUsage(0, m_motionBlurFastPathPass.descriptorSet) },
+			{ vkcv::useDescriptorSet(0, m_motionBlurFastPathPass.descriptorSet) },
 			fastPathPushConstants);
 	}
 	else if(mode == eMotionBlurMode::Disabled) {
@@ -301,16 +290,11 @@ vkcv::ImageHandle MotionBlur::render(
 			(m_core->getImageWidth(m_renderTargets.outputColor)  + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize * 
 			(m_core->getImageHeight(m_renderTargets.outputColor) + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize;
 
-		const uint32_t dispatchCounts[3] = {
-			tileCount,
-			1,
-			1 };
-
 		m_core->recordComputeDispatchToCmdStream(
 			cmdStream,
 			m_tileVisualisationPass.pipeline,
-			dispatchCounts,
-			{ vkcv::DescriptorSetUsage(0, m_tileVisualisationPass.descriptorSet) },
+			tileCount,
+			{ vkcv::useDescriptorSet(0, m_tileVisualisationPass.descriptorSet) },
 			vkcv::PushConstants(0));
 	}
 	else {
@@ -361,19 +345,22 @@ vkcv::ImageHandle MotionBlur::renderMotionVectorVisualisation(
 	m_core->prepareImageForSampling(cmdStream, visualisationInput);
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.outputColor);
 
-	vkcv::PushConstants motionVectorVisualisationPushConstants(sizeof(float));
+	vkcv::PushConstants motionVectorVisualisationPushConstants = vkcv::pushConstants<float>();
 	motionVectorVisualisationPushConstants.appendDrawcall(velocityRange);
 
-	const auto dispatchSizes = computeFullscreenDispatchSize(
-		m_core->getImageWidth(m_renderTargets.outputColor), 
-		m_core->getImageHeight(m_renderTargets.outputColor), 
-		8);
+	const auto dispatchSizes = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth(m_renderTargets.outputColor),
+					m_core->getImageHeight(m_renderTargets.outputColor)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorVisualisationPass.pipeline,
-		dispatchSizes.data(),
-		{ vkcv::DescriptorSetUsage(0, m_motionVectorVisualisationPass.descriptorSet) },
+		dispatchSizes,
+		{ vkcv::useDescriptorSet(0, m_motionVectorVisualisationPass.descriptorSet) },
 		motionVectorVisualisationPushConstants);
 
 	return m_renderTargets.outputColor;
@@ -399,16 +386,19 @@ void MotionBlur::computeMotionTiles(
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMax);
 	m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMin);
 
-	const std::array<uint32_t, 3> motionTileDispatchCounts = computeFullscreenDispatchSize(
-		m_core->getImageWidth( m_renderTargets.motionMax),
-		m_core->getImageHeight(m_renderTargets.motionMax),
-		8);
+	const auto motionTileDispatchCounts = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(
+					m_core->getImageWidth( m_renderTargets.motionMax),
+					m_core->getImageHeight(m_renderTargets.motionMax)
+			),
+			vkcv::DispatchSize(8, 8)
+	);
 
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorMinMaxPass.pipeline,
-		motionTileDispatchCounts.data(),
-		{ vkcv::DescriptorSetUsage(0, m_motionVectorMinMaxPass.descriptorSet) },
+		motionTileDispatchCounts,
+		{ vkcv::useDescriptorSet(0, m_motionVectorMinMaxPass.descriptorSet) },
 		vkcv::PushConstants(0));
 
 	// motion vector min max neighbourhood
@@ -437,7 +427,7 @@ void MotionBlur::computeMotionTiles(
 	m_core->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_motionVectorMinMaxNeighbourhoodPass.pipeline,
-		motionTileDispatchCounts.data(),
-		{ vkcv::DescriptorSetUsage(0, m_motionVectorMinMaxNeighbourhoodPass.descriptorSet) },
+		motionTileDispatchCounts,
+		{ vkcv::useDescriptorSet(0, m_motionVectorMinMaxNeighbourhoodPass.descriptorSet) },
 		vkcv::PushConstants(0));
 }
\ No newline at end of file
diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.cpp b/projects/indirect_dispatch/src/MotionBlurSetup.cpp
index 82d2593a5b976f9389b58dddac43e3a45d1db303..0244c4ae9519ea6c6f818e024930e70c15e7a289 100644
--- a/projects/indirect_dispatch/src/MotionBlurSetup.cpp
+++ b/projects/indirect_dispatch/src/MotionBlurSetup.cpp
@@ -17,7 +17,8 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 		motionMaxheight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	targets.motionMaxNeighbourhood = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
@@ -25,7 +26,8 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 		motionMaxheight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	targets.motionMin = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
@@ -33,7 +35,8 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 		motionMaxheight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	targets.motionMinNeighbourhood = core.createImage(
 		MotionBlurConfig::motionVectorTileFormat,
@@ -41,7 +44,8 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 		motionMaxheight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	targets.outputColor = core.createImage(
 		MotionBlurConfig::outputColorFormat,
@@ -49,7 +53,8 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t
 		height,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	return targets;
 }
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
index 26f523958e9704d7fc090ceba818591d904a2e92..39c5621e2d1a062d86adbf77bef49cb3f639fdae 100644
--- a/projects/indirect_draw/src/main.cpp
+++ b/projects/indirect_draw/src/main.cpp
@@ -1,7 +1,10 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
+#include <vkcv/Image.hpp>
 #include <vkcv/camera/CameraManager.hpp>
-#include <chrono>
 #include <vkcv/gui/GUI.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -87,28 +90,33 @@ struct CompiledMaterial
 
 void interleaveScene(vkcv::asset::Scene scene,
                      std::vector<std::vector<Vertex>> &interleavedVertexBuffers,
-                     std::vector<glm::vec4> &boundingBoxBuffers)
-{
-    for(const auto &mesh : scene.meshes)
-    {
-        for(auto vertexGroupIndex : mesh.vertexGroups)
-        {
-			// Sort attributes to fix it!
-			auto& attributes = scene.vertexGroups[vertexGroupIndex].vertexBuffer.attributes;
+                     std::vector<glm::vec4> &boundingBoxBuffers) {
 	
-			std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-				return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-			});
-			
+    for(const auto &mesh : scene.meshes) {
+        for(auto vertexGroupIndex : mesh.vertexGroups) {
             const auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
 
-            const vkcv::asset::VertexAttribute positionAttribute = vertexGroup.vertexBuffer.attributes[0];
-            const vkcv::asset::VertexAttribute normalAttribute   = vertexGroup.vertexBuffer.attributes[1];
-            const vkcv::asset::VertexAttribute uvAttribute       = vertexGroup.vertexBuffer.attributes[2];
-
-            assert(positionAttribute.type   == vkcv::asset::PrimitiveType::POSITION);
-            assert(normalAttribute.type     == vkcv::asset::PrimitiveType::NORMAL);
-            assert(uvAttribute.type         == vkcv::asset::PrimitiveType::TEXCOORD_0);
+            const vkcv::asset::VertexAttribute* positionAttribute = nullptr;
+            const vkcv::asset::VertexAttribute* normalAttribute   = nullptr;
+            const vkcv::asset::VertexAttribute* uvAttribute       = nullptr;
+			
+			for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
+				switch (attribute.type) {
+					case vkcv::asset::PrimitiveType::POSITION:
+						positionAttribute = &attribute;
+						break;
+					case vkcv::asset::PrimitiveType::NORMAL:
+						normalAttribute = &attribute;
+						break;
+					case vkcv::asset::PrimitiveType::TEXCOORD_0:
+						uvAttribute = &attribute;
+						break;
+					default:
+						break;
+				}
+			}
+	
+			assert(positionAttribute && normalAttribute && uvAttribute);
 
             const uint64_t &verticesCount          = vertexGroup.numVertices;
             const std::vector<uint8_t> &vertexData = vertexGroup.vertexBuffer.data;
@@ -116,18 +124,18 @@ void interleaveScene(vkcv::asset::Scene scene,
             std::vector<Vertex> vertices;
             vertices.reserve(verticesCount);
 
-            const size_t positionStride = positionAttribute.stride == 0 ? sizeof(glm::vec3) : positionAttribute.stride;
-            const size_t normalStride   = normalAttribute.stride   == 0 ? sizeof(glm::vec3) : normalAttribute.stride;
-            const size_t uvStride       = uvAttribute.stride       == 0 ? sizeof(glm::vec2) : uvAttribute.stride;
+            const size_t positionStride = positionAttribute->stride == 0 ? sizeof(glm::vec3) : positionAttribute->stride;
+            const size_t normalStride   = normalAttribute->stride   == 0 ? sizeof(glm::vec3) : normalAttribute->stride;
+            const size_t uvStride       = uvAttribute->stride       == 0 ? sizeof(glm::vec2) : uvAttribute->stride;
 
             glm::vec3 max_pos(-std::numeric_limits<float>::max());
             glm::vec3 min_pos(std::numeric_limits<float>::max());
 
             for(size_t i = 0; i < verticesCount; i++)
             {
-                const size_t positionOffset = positionAttribute.offset + positionStride * i;
-                const size_t normalOffset   = normalAttribute.offset   + normalStride * i;
-                const size_t uvOffset       = uvAttribute.offset       + uvStride * i;
+                const size_t positionOffset = positionAttribute->offset + positionStride * i;
+                const size_t normalOffset   = normalAttribute->offset   + normalStride * i;
+                const size_t uvOffset       = uvAttribute->offset       + uvStride * i;
 
                 Vertex v;
 
@@ -169,7 +177,7 @@ void compileMeshForIndirectDraw(vkcv::Core &core,
                                 CompiledMaterial &compiledMat,
                                 std::vector<vk::DrawIndexedIndirectCommand> &indexedIndirectCommands)
 {
-    vkcv::Image pseudoImg = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+    vkcv::Image pseudoImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, 2, 2);
     std::vector<uint8_t> pseudoData = {0, 0, 0, 0};
     pseudoImg.fill(pseudoData.data());
 	
@@ -194,7 +202,7 @@ void compileMeshForIndirectDraw(vkcv::Core &core,
             {
                 auto &baseColor     = scene.textures[material.baseColor];
 
-                vkcv::Image baseColorImg = core.createImage(vk::Format::eR8G8B8A8Srgb, baseColor.w, baseColor.h);
+                vkcv::Image baseColorImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, baseColor.w, baseColor.h);
                 baseColorImg.fill(baseColor.data.data());
 				baseColorImg.recordMipChainGeneration(mipStream, core.getDownsampler());
 
@@ -306,6 +314,7 @@ int main(int argc, const char** argv) {
 	);
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName,800,600,true);
+	vkcv::Window& window = core.getWindow(windowHandle);
 
     vkcv::gui::GUI gui (core, windowHandle);
 
@@ -326,19 +335,12 @@ int main(int argc, const char** argv) {
         return EXIT_FAILURE;
     }
 
-    const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat()
-	);
-	const vkcv::AttachmentDescription depth_attachment(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			vk::Format::eD32Sfloat
+    vkcv::PassHandle passHandle = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
 	);
-
-	vkcv::PassConfig passDefinition({ present_color_attachment, depth_attachment }, vkcv::Multisampling::None);
-	vkcv::PassHandle passHandle = core.createPass(passDefinition);
+	
 	if (!passHandle) {
 		std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
@@ -393,26 +395,29 @@ int main(int argc, const char** argv) {
         compiledInterleavedVertexBuffer.insert(compiledInterleavedVertexBuffer.end(),vertexGroup.begin(),vertexGroup.end());
 	}
 
-	auto vkCompiledVertexBuffer = core.createBuffer<Vertex>(
+	auto vkCompiledVertexBuffer = vkcv::buffer<Vertex>(
+			core,
 			vkcv::BufferType::VERTEX,
             compiledInterleavedVertexBuffer.size(),
-			vkcv::BufferMemoryType::DEVICE_LOCAL
-			);
+			vkcv::BufferMemoryType::DEVICE_LOCAL);
     vkCompiledVertexBuffer.fill(compiledInterleavedVertexBuffer.data());
 
-    auto vkCompiledIndexBuffer = core.createBuffer<uint8_t>(
+    auto vkCompiledIndexBuffer = vkcv::buffer<uint8_t>(
+			core,
             vkcv::BufferType::INDEX,
             compiledIndexBuffer.size(),
             vkcv::BufferMemoryType::DEVICE_LOCAL);
     vkCompiledIndexBuffer.fill(compiledIndexBuffer.data());
 
-    vkcv::Buffer<vk::DrawIndexedIndirectCommand> indirectBuffer = core.createBuffer<vk::DrawIndexedIndirectCommand>(
+    vkcv::Buffer<vk::DrawIndexedIndirectCommand> indirectBuffer = vkcv::buffer<vk::DrawIndexedIndirectCommand>(
+			core,
             vkcv::BufferType::INDIRECT,
             indexedIndirectCommands.size(),
             vkcv::BufferMemoryType::DEVICE_LOCAL);
     indirectBuffer.fill(indexedIndirectCommands);
 
-    auto boundingBoxBuffer = core.createBuffer<glm::vec4>(
+    auto boundingBoxBuffer = vkcv::buffer<glm::vec4>(
+			core,
             vkcv::BufferType::STORAGE,
             compiledBoundingBoxBuffer.size());
     boundingBoxBuffer.fill(compiledBoundingBoxBuffer);
@@ -422,7 +427,8 @@ int main(int argc, const char** argv) {
 	{
 		modelMatrix.push_back(glm::make_mat4(mesh.modelMatrix.data()));
 	}
-	vkcv::Buffer<glm::mat4> modelBuffer = core.createBuffer<glm::mat4>(
+	vkcv::Buffer<glm::mat4> modelBuffer = vkcv::buffer<glm::mat4>(
+			core,
 			vkcv::BufferType::STORAGE,
 			modelMatrix.size(),
 			vkcv::BufferMemoryType::DEVICE_LOCAL
@@ -430,10 +436,11 @@ int main(int argc, const char** argv) {
 	modelBuffer.fill(modelMatrix);
 
 	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize> (0), vkCompiledVertexBuffer.getVulkanHandle() )
+			vkcv::vertexBufferBinding(vkCompiledVertexBuffer.getHandle())
 	};
-
-	const vkcv::Mesh compiledMesh(vertexBufferBindings, vkCompiledIndexBuffer.getVulkanHandle(), 0, vkcv::IndexBitCount::Bit32);
+	
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(vkCompiledIndexBuffer.getHandle(), vkcv::IndexBitCount::Bit32);
 
     //assert(compiledMaterial.baseColor.size() == compiledMaterial.metalRough.size());
 
@@ -443,12 +450,7 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings);
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 
-    vkcv::SamplerHandle standardSampler = core.createSampler(
-            vkcv::SamplerFilterType::LINEAR,
-            vkcv::SamplerFilterType::LINEAR,
-            vkcv::SamplerMipmapMode::LINEAR,
-            vkcv::SamplerAddressMode::REPEAT
-    );
+    vkcv::SamplerHandle standardSampler = vkcv::samplerLinear(core);
 	
 	vkcv::DescriptorWrites setWrites;
 	
@@ -462,16 +464,15 @@ int main(int argc, const char** argv) {
 	setWrites.writeStorageBuffer(1, modelBuffer.getHandle());
     core.writeDescriptorSet(descriptorSet, setWrites);
 
-	const vkcv::GraphicsPipelineConfig sponzaPipelineConfig {
-        sponzaProgram,
-        UINT32_MAX,
-        UINT32_MAX,
-        passHandle,
-        {sponzaVertexLayout},
-		{ descriptorSetLayout },
-		true
-	};
-	vkcv::GraphicsPipelineHandle sponzaPipelineHandle = core.createGraphicsPipeline(sponzaPipelineConfig);
+	vkcv::GraphicsPipelineHandle sponzaPipelineHandle = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					sponzaProgram,
+					passHandle,
+					{ sponzaVertexLayout },
+					{ descriptorSetLayout }
+			)
+	);
+	
 	if (!sponzaPipelineHandle) {
 		std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
 		return EXIT_FAILURE;
@@ -481,7 +482,8 @@ int main(int argc, const char** argv) {
     vkcv::DescriptorSetLayoutHandle cullingSetLayout = core.createDescriptorSetLayout(cullingBindings);
     vkcv::DescriptorSetHandle cullingDescSet = core.createDescriptorSet(cullingSetLayout);
 
-    vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(
+    vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = vkcv::buffer<CameraPlanes>(
+			core,
             vkcv::BufferType::UNIFORM,
             1);
 
@@ -502,7 +504,7 @@ int main(int argc, const char** argv) {
         return EXIT_FAILURE;
     }
 
-    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+    vkcv::camera::CameraManager cameraManager (window);
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
@@ -512,46 +514,30 @@ int main(int argc, const char** argv) {
 	
     const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
-    auto start = std::chrono::system_clock::now();
-
     float ceiledDispatchCount = static_cast<float>(indexedIndirectCommands.size()) / 64.0f;
     ceiledDispatchCount = std::ceil(ceiledDispatchCount);
-    const uint32_t dispatchCount[3] = {static_cast<uint32_t>(ceiledDispatchCount), 1, 1};
-
+    const vkcv::DispatchSize dispatchCount = static_cast<uint32_t>(ceiledDispatchCount);
 
-    vkcv::DescriptorSetUsage cullingUsage(0, cullingDescSet, {});
+    vkcv::DescriptorSetUsage cullingUsage (0, cullingDescSet, {});
     vkcv::PushConstants emptyPushConstant(0);
 
     bool updateFrustumPlanes    = true;
-
-    while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-		
-		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
-			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight);
 		}
   
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+		cameraManager.update(dt);
 		
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
         vkcv::camera::Camera cam = cameraManager.getActiveCamera();
-		vkcv::PushConstants pushConstants(sizeof(glm::mat4));
+		vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();
 		pushConstants.appendDrawcall(cam.getProjection() * cam.getView());
 
-        if(updateFrustumPlanes)
-        {
+        if (updateFrustumPlanes) {
             const CameraPlanes cameraPlanes = computeCameraPlanes(cam);
             cameraPlaneBuffer.fill({ cameraPlanes });
         }
@@ -559,43 +545,46 @@ int main(int argc, const char** argv) {
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              cullingPipelineHandle,
-                                              dispatchCount,
-                                              {cullingUsage},
-                                              emptyPushConstant);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				cullingPipelineHandle,
+				dispatchCount,
+				{cullingUsage},
+				emptyPushConstant
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, indirectBuffer.getHandle());
 
-		core.recordIndexedIndirectDrawcallsToCmdStream(
+		vkcv::IndirectDrawcall drawcall (
+				indirectBuffer.getHandle(),
+				vertexData,
+				indexedIndirectCommands.size()
+		);
+		
+		drawcall.useDescriptorSet(0, descriptorSet);
+		
+		core.recordIndirectDrawcallsToCmdStream(
 			cmdStream,
-			passHandle,
             sponzaPipelineHandle,
             pushConstants,
-            descriptorSet,
-            compiledMesh,
+			{ drawcall },
 			renderTargets,
-			indirectBuffer,
-            indexedIndirectCommands.size(),
-			windowHandle);
+			windowHandle
+		);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-
-		auto stop = std::chrono::system_clock::now();
-		auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+		
         gui.beginGUI();
 
         ImGui::Begin("Settings");
         ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
-		ImGui::Text("Deltatime %fms, %f", 0.001 * static_cast<double>(kektime.count()), 1/(0.000001 * static_cast<double>(kektime.count())));
+		ImGui::Text("Deltatime %fms, %f", dt * 1000, 1/dt);
 
         ImGui::End();
 
         gui.endGUI();
-
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index a74561ca07f7a052fe71c9e1d1efc751c2ab258c..034ae3c0de96d6ac434e3340cd6e4539626fbdba 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -1,5 +1,7 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
@@ -103,34 +105,57 @@ int main(int argc, const char** argv) {
 
     assert(!mesh.vertexGroups.empty());
 
-    auto vertexBuffer = core.createBuffer<uint8_t>(
+    auto vertexBuffer = vkcv::buffer<uint8_t>(
+			core,
             vkcv::BufferType::VERTEX,
             mesh.vertexGroups[0].vertexBuffer.data.size(),
             vkcv::BufferMemoryType::DEVICE_LOCAL
     );
     vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
 
-    auto indexBuffer = core.createBuffer<uint8_t>(
+    auto indexBuffer = vkcv::buffer<uint8_t>(
+			core,
             vkcv::BufferType::INDEX,
             mesh.vertexGroups[0].indexBuffer.data.size(),
             vkcv::BufferMemoryType::DEVICE_LOCAL
     );
     indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
-
-	// format data for mesh shader
-	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
-
-	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-	});
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
-			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
-			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
+	
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL
+			}
+	);
+	
+	const vkcv::asset::VertexAttribute* positionAttribute = nullptr;
+	const vkcv::asset::VertexAttribute* normalAttribute   = nullptr;
+	
+	for (const auto& attribute : mesh.vertexGroups[0].vertexBuffer.attributes) {
+		switch (attribute.type) {
+			case vkcv::asset::PrimitiveType::POSITION:
+				positionAttribute = &attribute;
+				break;
+			case vkcv::asset::PrimitiveType::NORMAL:
+				normalAttribute = &attribute;
+				break;
+			default:
+				break;
+		}
+	}
+	
+	assert(positionAttribute && normalAttribute);
 
 	const auto& bunny = mesh.vertexGroups[0];
-	std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(bunny.vertexBuffer.data, bunny.numVertices, attributes[0], attributes[1]);
+	std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(
+			bunny.vertexBuffer.data,
+			bunny.numVertices,
+			*positionAttribute,
+			*normalAttribute
+	);
+	
 	// mesh shader buffers
 	const auto& assetLoaderIndexBuffer                    = mesh.vertexGroups[0].indexBuffer;
 	std::vector<uint32_t> indexBuffer32Bit                = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type);
@@ -139,41 +164,31 @@ int main(int argc, const char** argv) {
 
     const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, forsythResult.indexBuffer, forsythResult.skippedIndices);
 
-	auto meshShaderVertexBuffer = core.createBuffer<vkcv::meshlet::Vertex>(
+	auto meshShaderVertexBuffer = vkcv::buffer<vkcv::meshlet::Vertex>(
+		core,
 		vkcv::BufferType::STORAGE,
 		meshShaderModelData.vertices.size());
 	meshShaderVertexBuffer.fill(meshShaderModelData.vertices);
 
-	auto meshShaderIndexBuffer = core.createBuffer<uint32_t>(
+	auto meshShaderIndexBuffer = vkcv::buffer<uint32_t>(
+		core,
 		vkcv::BufferType::STORAGE,
 		meshShaderModelData.localIndices.size());
 	meshShaderIndexBuffer.fill(meshShaderModelData.localIndices);
 
-	auto meshletBuffer = core.createBuffer<vkcv::meshlet::Meshlet>(
+	auto meshletBuffer = vkcv::buffer<vkcv::meshlet::Meshlet>(
+		core,
 		vkcv::BufferType::STORAGE,
 		meshShaderModelData.meshlets.size(),
 		vkcv::BufferMemoryType::DEVICE_LOCAL
 		);
 	meshletBuffer.fill(meshShaderModelData.meshlets);
-
-	// attachments
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat());
-
-    const vkcv::AttachmentDescription depth_attachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            vk::Format::eD32Sfloat
-    );
-
-	vkcv::PassConfig bunnyPassDefinition(
-			{ present_color_attachment, depth_attachment },
-			vkcv::Multisampling::None
-	);
 	
-	vkcv::PassHandle renderPass = core.createPass(bunnyPassDefinition);
+	vkcv::PassHandle renderPass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
+	);
 
 	if (!renderPass)
 	{
@@ -203,29 +218,26 @@ int main(int argc, const char** argv) {
 
     vkcv::DescriptorSetLayoutHandle vertexShaderDescriptorSetLayout = core.createDescriptorSetLayout(bunnyShaderProgram.getReflectedDescriptors().at(0));
     vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(vertexShaderDescriptorSetLayout);
-	
-	const vkcv::GraphicsPipelineConfig bunnyPipelineDefinition {
-			bunnyShaderProgram,
-			UINT32_MAX,
-			UINT32_MAX,
-			renderPass,
-			{ bunnyLayout },
-			{ vertexShaderDescriptorSetLayout },
-			true
-	};
 
 	struct ObjectMatrices {
 		glm::mat4 model;
 		glm::mat4 mvp;
 	};
 	const size_t objectCount = 1;
-	vkcv::Buffer<ObjectMatrices> matrixBuffer = core.createBuffer<ObjectMatrices>(vkcv::BufferType::STORAGE, objectCount);
+	vkcv::Buffer<ObjectMatrices> matrixBuffer = vkcv::buffer<ObjectMatrices>(core, vkcv::BufferType::STORAGE, objectCount);
 
 	vkcv::DescriptorWrites vertexShaderDescriptorWrites;
 	vertexShaderDescriptorWrites.writeStorageBuffer(0, matrixBuffer.getHandle());
 	core.writeDescriptorSet(vertexShaderDescriptorSet, vertexShaderDescriptorWrites);
 
-	vkcv::GraphicsPipelineHandle bunnyPipeline = core.createGraphicsPipeline(bunnyPipelineDefinition);
+	vkcv::GraphicsPipelineHandle bunnyPipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					bunnyShaderProgram,
+					renderPass,
+					{ bunnyLayout },
+					{ vertexShaderDescriptorSetLayout }
+			)
+	);
 
 	if (!bunnyPipeline)
 	{
@@ -254,17 +266,14 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet(meshShaderDescriptorSetLayout);
 	const vkcv::VertexLayout meshShaderLayout(bindings);
 
-	const vkcv::GraphicsPipelineConfig meshShaderPipelineDefinition{
-		meshShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
-		renderPass,
-		{meshShaderLayout},
-		{meshShaderDescriptorSetLayout},
-		true
-	};
-
-	vkcv::GraphicsPipelineHandle meshShaderPipeline = core.createGraphicsPipeline(meshShaderPipelineDefinition);
+	vkcv::GraphicsPipelineHandle meshShaderPipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					meshShaderProgram,
+					renderPass,
+					{ meshShaderLayout },
+					{ meshShaderDescriptorSetLayout }
+			)
+	);
 
 	if (!meshShaderPipeline)
 	{
@@ -272,7 +281,7 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
-	vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(vkcv::BufferType::UNIFORM, 1);
+	vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = vkcv::buffer<CameraPlanes>(core, vkcv::BufferType::UNIFORM, 1);
 
 	vkcv::DescriptorWrites meshShaderWrites;
 	meshShaderWrites.writeStorageBuffer(
@@ -292,12 +301,11 @@ int main(int argc, const char** argv) {
     core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites);
 
     vkcv::ImageHandle depthBuffer;
-
-    auto start = std::chrono::system_clock::now();
-
 	vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle();
 
-    const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices, vkcv::IndexBitCount::Bit32);
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(indexBuffer.getHandle(), vkcv::IndexBitCount::Bit32);
+	vertexData.setCount(mesh.vertexGroups[0].numIndices);
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
@@ -308,16 +316,9 @@ int main(int argc, const char** argv) {
 
 	bool useMeshShader          = true;
 	bool updateFrustumPlanes    = true;
-
-	while (vkcv::Window::hasOpenWindow())
-	{
-		vkcv::Window::pollEvents();
-
-		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
-			continue;
-		}
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			(swapchainHeight != core.getImageHeight(depthBuffer))) {
@@ -325,14 +326,10 @@ int main(int argc, const char** argv) {
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 		}
 		
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-		start = end;
-		
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
 
 		const vkcv::camera::Camera& camera = cameraManager.getActiveCamera();
 
@@ -356,35 +353,35 @@ int main(int argc, const char** argv) {
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		vkcv::PushConstants pushConstantData(sizeof(pushConstants));
+		vkcv::PushConstants pushConstantData = vkcv::pushConstants<PushConstants>();
 		pushConstantData.appendDrawcall(pushConstants);
 
 		if (useMeshShader) {
-
-			vkcv::DescriptorSetUsage descriptorUsage(0, meshShaderDescriptorSet);
 			const uint32_t taskCount = (meshShaderModelData.meshlets.size() + 31) / 32;
+			
+			vkcv::TaskDrawcall drawcall (taskCount);
+			drawcall.useDescriptorSet(0, meshShaderDescriptorSet);
 
 			core.recordMeshShaderDrawcalls(
 				cmdStream,
-				renderPass,
 				meshShaderPipeline,
 				pushConstantData,
-				{ vkcv::MeshShaderDrawcall({descriptorUsage}, taskCount)},
+				{ drawcall },
 				{ renderTargets },
-				windowHandle);
-		}
-		else {
-
-			vkcv::DescriptorSetUsage descriptorUsage(0, vertexShaderDescriptorSet);
+				windowHandle
+			);
+		} else {
+			vkcv::InstanceDrawcall drawcall (vertexData);
+			drawcall.useDescriptorSet(0, vertexShaderDescriptorSet);
 
 			core.recordDrawcallsToCmdStream(
 				cmdStream,
-				renderPass,
 				bunnyPipeline,
 				pushConstantData,
-				{ vkcv::DrawcallInfo(renderMesh, { descriptorUsage }) },
+				{ drawcall },
 				{ renderTargets },
-				windowHandle);
+				windowHandle
+			);
 		}
 
 		core.prepareSwapchainImageForPresent(cmdStream);
@@ -397,10 +394,8 @@ int main(int argc, const char** argv) {
 		ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
 
 		ImGui::End();
-		
 		gui.endGUI();
-
-		core.endFrame(windowHandle);
-	}
+	});
+	
 	return 0;
 }
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index db538796201ee4c1cf5830bfd82036f16a82e693..62a61806b26b9cfea89ab453b2c2291bd55cacef 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -1,5 +1,7 @@
 #include <iostream>
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
@@ -26,26 +28,15 @@ int main(int argc, const char **argv) {
     vkcv::Window& window = core.getWindow(windowHandle);
 	vkcv::camera::CameraManager cameraManager(window);
 
-    auto particleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3,
-                                                           vkcv::BufferMemoryType::DEVICE_LOCAL);
+    auto particleIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3,
+													  vkcv::BufferMemoryType::DEVICE_LOCAL);
     uint16_t indices[3] = {0, 1, 2};
     particleIndexBuffer.fill(&indices[0], sizeof(indices));
 
     vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
-    // an example attachment for passes that output to the window
-    const vkcv::AttachmentDescription present_color_attachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            colorFormat);
+    vkcv::PassHandle particlePass = vkcv::passFormat(core, colorFormat);
 
-
-    vkcv::PassConfig particlePassDefinition({present_color_attachment}, vkcv::Multisampling::None);
-    vkcv::PassHandle particlePass = core.createPass(particlePassDefinition);
-
-    vkcv::PassConfig computePassDefinition({});
-    vkcv::PassHandle computePass = core.createPass(computePassDefinition);
-
-    if (!particlePass || !computePass)
+    if (!particlePass)
     {
         std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
         return EXIT_FAILURE;
@@ -99,14 +90,16 @@ int main(int argc, const char **argv) {
             particleShaderProgram.getReflectedDescriptors().at(0));
     vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 
-    vkcv::Buffer<glm::vec3> vertexBuffer = core.createBuffer<glm::vec3>(
+    vkcv::Buffer<glm::vec3> vertexBuffer = vkcv::buffer<glm::vec3>(
+			core,
             vkcv::BufferType::VERTEX,
             3
     );
     const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments();
 
     const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-            vkcv::VertexBufferBinding(0, vertexBuffer.getVulkanHandle())};
+            vkcv::vertexBufferBinding(vertexBuffer.getHandle())
+	};
 
     std::vector<vkcv::VertexBinding> bindings;
     for (size_t i = 0; i < vertexAttachments.size(); i++) {
@@ -115,16 +108,14 @@ int main(int argc, const char **argv) {
 
     const vkcv::VertexLayout particleLayout { bindings };
 
-    vkcv::GraphicsPipelineConfig particlePipelineDefinition{
+    vkcv::GraphicsPipelineConfig particlePipelineDefinition (
             particleShaderProgram,
-            UINT32_MAX,
-            UINT32_MAX,
             particlePass,
             {particleLayout},
-            {descriptorSetLayout},
-            true
-	};
-    particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+            {descriptorSetLayout}
+	);
+	
+    particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 
     const std::vector<glm::vec3> vertices = {glm::vec3(-0.012, 0.012, 0),
                                              glm::vec3(0.012, 0.012, 0),
@@ -138,12 +129,14 @@ int main(int argc, const char **argv) {
 		computeShaderProgram, {computeDescriptorSetLayout}
 	});
 
-    vkcv::Buffer<glm::vec4> color = core.createBuffer<glm::vec4>(
+    vkcv::Buffer<glm::vec4> color = vkcv::buffer<glm::vec4>(
+			core,
             vkcv::BufferType::UNIFORM,
             1
     );
 
-    vkcv::Buffer<glm::vec2> position = core.createBuffer<glm::vec2>(
+    vkcv::Buffer<glm::vec2> position = vkcv::buffer<glm::vec2>(
+			core,
             vkcv::BufferType::UNIFORM,
             1
     );
@@ -153,7 +146,8 @@ int main(int argc, const char **argv) {
     glm::vec2 lifeTime = glm::vec2(-1.f,8.f);
     ParticleSystem particleSystem = ParticleSystem( 100000 , minVelocity, maxVelocity, lifeTime);
 
-    vkcv::Buffer<Particle> particleBuffer = core.createBuffer<Particle>(
+    vkcv::Buffer<Particle> particleBuffer = vkcv::buffer<Particle>(
+			core,
             vkcv::BufferType::STORAGE,
             particleSystem.getParticles().size()
     );
@@ -177,8 +171,10 @@ int main(int argc, const char **argv) {
 
     const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
-    const vkcv::Mesh renderMesh({vertexBufferBindings}, particleIndexBuffer.getVulkanHandle(),
-                                particleIndexBuffer.getCount());
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(particleIndexBuffer.getHandle());
+	vertexData.setCount(particleIndexBuffer.getCount());
+	
     vkcv::DescriptorSetUsage descriptorUsage(0, descriptorSet);
 
     auto pos = glm::vec2(0.f);
@@ -193,11 +189,10 @@ int main(int argc, const char **argv) {
     });
 
     std::vector<glm::mat4> modelMatrices;
-    std::vector<vkcv::DrawcallInfo> drawcalls;
-    drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage}, particleSystem.getParticles().size()));
-
-    auto start = std::chrono::system_clock::now();
-
+	
+	vkcv::InstanceDrawcall drawcall (vertexData, particleSystem.getParticles().size());
+	drawcall.useDescriptorSet(0, descriptorSet);
+	
     glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f);
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
     uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
@@ -211,14 +206,14 @@ int main(int argc, const char **argv) {
     cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, -2.0f));
     cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f));
 
-	const auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
     vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, true, true
-	).getHandle();
+	);
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
 	bloomAndFlares.setUpsamplingLimit(3);
@@ -237,14 +232,9 @@ int main(int argc, const char **argv) {
 
     std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f);
     std::default_random_engine rdmEngine;
-    while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-
-        uint32_t swapchainWidth, swapchainHeight;
-        if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-            continue;
-        }
 	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((core.getImageWidth(colorBuffer) != swapchainWidth) ||
 			(core.getImageHeight(colorBuffer) != swapchainHeight)) {
 			colorBuffer = core.createImage(
@@ -252,17 +242,13 @@ int main(int argc, const char **argv) {
 					swapchainWidth,
 					swapchainHeight,
 					1, false, true, true
-			).getHandle();
+			);
 		}
 
         color.fill(&colorData);
         position.fill(&pos);
 
-        auto end = std::chrono::system_clock::now();
-        float deltatime = 0.000001 * static_cast<float>( std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() );
-        start = end;
-
-        cameraManager.update(deltatime);
+        cameraManager.update(dt);
 
         // split view and projection to allow for easy billboarding in shader
         struct {
@@ -275,17 +261,18 @@ int main(int argc, const char **argv) {
 
         auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
         float random = rdm(rdmEngine);
-        glm::vec2 pushData = glm::vec2(deltatime, random);
+        glm::vec2 pushData = glm::vec2(dt, random);
 
-        vkcv::PushConstants pushConstantsCompute (sizeof(glm::vec2));
+        vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<glm::vec2>();
         pushConstantsCompute.appendDrawcall(pushData);
         
-        uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(particleSystem.getParticles().size()/256.f)),1,1};
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              computePipeline,
-                                              computeDispatchCount,
-                                              {vkcv::DescriptorSetUsage(0, computeDescriptorSet)},
-											  pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				computePipeline,
+				vkcv::dispatchInvocations(particleSystem.getParticles().size(), 256),
+				{vkcv::DescriptorSetUsage(0, computeDescriptorSet)},
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle());
 
@@ -294,12 +281,12 @@ int main(int argc, const char **argv) {
         
         core.recordDrawcallsToCmdStream(
                 cmdStream,
-                particlePass,
                 particlePipeline,
 				pushConstantsDraw,
-                {drawcalls},
+                { drawcall },
                 { colorBuffer },
-                windowHandle);
+                windowHandle
+		);
 	
 		bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer);
 
@@ -315,22 +302,22 @@ int main(int argc, const char **argv) {
 		
         core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
 
-        uint32_t tonemappingDispatchCount[3];
-        tonemappingDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-        tonemappingDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-        tonemappingDispatchCount[2] = 1;
+        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
         core.recordComputeDispatchToCmdStream(
             cmdStream, 
             tonemappingPipe, 
             tonemappingDispatchCount, 
-            {vkcv::DescriptorSetUsage(0, tonemappingDescriptor) },
-            vkcv::PushConstants(0));
+            { vkcv::useDescriptorSet(0, tonemappingDescriptor) },
+            vkcv::PushConstants(0)
+		);
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
-        core.endFrame(windowHandle);
-    }
+    });
 
     return 0;
 }
diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp
index 670986fff81d4f308aa063e6275f41872bc8e878..9a89696e82cf39bbb7281b6a489adc4afe66280a 100644
--- a/projects/path_tracer/src/main.cpp
+++ b/projects/path_tracer/src/main.cpp
@@ -1,4 +1,6 @@
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -69,7 +71,8 @@ int main(int argc, const char** argv) {
 		initialHeight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	vkcv::ImageHandle meanImage = core.createImage(
 		vk::Format::eR32G32B32A32Sfloat,
@@ -77,7 +80,8 @@ int main(int argc, const char** argv) {
 		initialHeight,
 		1,
 		false,
-		true).getHandle();
+		true
+	);
 
 	vkcv::shader::GLSLCompiler compiler;
 
@@ -174,17 +178,20 @@ int main(int argc, const char** argv) {
 	planes.emplace_back(Plane(glm::vec3( 0,  0,     2), glm::vec3( 0,  0, -1), glm::vec2(2), whiteMaterialIndex));
 	planes.emplace_back(Plane(glm::vec3( 0,  1.9,   0), glm::vec3( 0, -1,  0), glm::vec2(1), lightMaterialIndex));
 
-	vkcv::Buffer<Sphere> sphereBuffer = core.createBuffer<Sphere>(
+	vkcv::Buffer<Sphere> sphereBuffer = vkcv::buffer<Sphere>(
+		core,
 		vkcv::BufferType::STORAGE,
 		spheres.size());
 	sphereBuffer.fill(spheres);
 
-	vkcv::Buffer<Plane> planeBuffer = core.createBuffer<Plane>(
+	vkcv::Buffer<Plane> planeBuffer = vkcv::buffer<Plane>(
+		core,
 		vkcv::BufferType::STORAGE,
 		planes.size());
 	planeBuffer.fill(planes);
 
-	vkcv::Buffer<Material> materialBuffer = core.createBuffer<Material>(
+	vkcv::Buffer<Material> materialBuffer = vkcv::buffer<Material>(
+		core,
 		vkcv::BufferType::STORAGE,
 		materialSettings.size());
 
@@ -215,9 +222,7 @@ int main(int argc, const char** argv) {
 	uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2));
-
-	auto    startTime       = std::chrono::system_clock::now();
-	float   time            = 0;
+	
 	int     frameIndex      = 0;
 	bool    clearMeanImage  = true;
 	bool    updateMaterials = true;
@@ -240,16 +245,9 @@ int main(int argc, const char** argv) {
 
 	glm::vec3   skyColor            = glm::vec3(0.2, 0.7, 0.8);
 	float       skyColorMultiplier  = 1;
-
-	while (vkcv::Window::hasOpenWindow())
-	{
-		vkcv::Window::pollEvents();
-
-		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if (swapchainWidth != widthPrevious || swapchainHeight != heightPrevious) {
 
 			// resize images
@@ -259,7 +257,8 @@ int main(int argc, const char** argv) {
 				swapchainHeight,
 				1,
 				false,
-				true).getHandle();
+				true
+			);
 
 			meanImage = core.createImage(
 				vk::Format::eR32G32B32A32Sfloat,
@@ -267,7 +266,8 @@ int main(int argc, const char** argv) {
 				swapchainHeight,
 				1,
 				false,
-				true).getHandle();
+				true
+			);
 
 			// update descriptor sets
 			traceDescriptorWrites.writeStorageImage(3, outputImage);
@@ -292,20 +292,14 @@ int main(int argc, const char** argv) {
 			clearMeanImage = true;
 		}
 
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - startTime);
-		startTime = end;
-
-		time += 0.000001f * static_cast<float>(deltatime.count());
-
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
 
 		const vkcv::CommandStreamHandle cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		uint32_t fullscreenDispatchCount[3] = {
-			static_cast<uint32_t> (std::ceil(swapchainWidth  / 8.f)),
-			static_cast<uint32_t> (std::ceil(swapchainHeight / 8.f)),
-			1 };
+		const auto fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
 		if (updateMaterials) {
 			std::vector<Material> materials;
@@ -340,7 +334,7 @@ int main(int argc, const char** argv) {
 			core.recordComputeDispatchToCmdStream(cmdStream,
 				imageClearPipeline,
 				fullscreenDispatchCount,
-				{ vkcv::DescriptorSetUsage(0, imageClearDescriptorSet) },
+				{ vkcv::useDescriptorSet(0, imageClearDescriptorSet) },
 				vkcv::PushConstants(0));
 
 			clearMeanImage = false;
@@ -362,20 +356,20 @@ int main(int argc, const char** argv) {
 		raytracingPushData.planeCount   = planes.size();
 		raytracingPushData.frameIndex   = frameIndex;
 
-		vkcv::PushConstants pushConstantsCompute(sizeof(RaytracingPushConstantData));
+		vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<RaytracingPushConstantData>();
 		pushConstantsCompute.appendDrawcall(raytracingPushData);
-
-		uint32_t traceDispatchCount[3] = { 
-			static_cast<uint32_t> (std::ceil(swapchainWidth  / 16.f)),
-			static_cast<uint32_t> (std::ceil(swapchainHeight / 16.f)),
-			1 };
+		
+		const auto traceDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(16, 16)
+		);
 
 		core.prepareImageForStorage(cmdStream, outputImage);
 
 		core.recordComputeDispatchToCmdStream(cmdStream,
 			tracePipeline,
 			traceDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, traceDescriptorSet) },
+			{ vkcv::useDescriptorSet(0, traceDescriptorSet) },
 			pushConstantsCompute);
 
 		core.prepareImageForStorage(cmdStream, meanImage);
@@ -385,7 +379,7 @@ int main(int argc, const char** argv) {
 		core.recordComputeDispatchToCmdStream(cmdStream,
 			imageCombinePipeline,
 			fullscreenDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, imageCombineDescriptorSet) },
+			{ vkcv::useDescriptorSet(0, imageCombineDescriptorSet) },
 			vkcv::PushConstants(0));
 
 		core.recordImageMemoryBarrier(cmdStream, meanImage);
@@ -407,7 +401,7 @@ int main(int argc, const char** argv) {
 		core.recordComputeDispatchToCmdStream(cmdStream,
 			presentPipeline,
 			fullscreenDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, presentDescriptorSet) },
+			{ vkcv::useDescriptorSet(0, presentDescriptorSet) },
 			vkcv::PushConstants(0));
 
 		core.prepareSwapchainImageForPresent(cmdStream);
@@ -449,9 +443,8 @@ int main(int argc, const char** argv) {
 			gui.endGUI();
 		}
 
-		core.endFrame(windowHandle);
-
 		frameIndex++;
-	}
+	});
+
 	return 0;
 }
diff --git a/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp b/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
index 8e19caaeda878d8b37b24069bbf752bae6dc41d0..fefd468067658cde88eefe02834b2e7328672af6 100644
--- a/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
+++ b/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
@@ -67,8 +67,9 @@ namespace vkcv::rtx {
         if (result != vk::Result::eSuccess) {
             vkcv_log(LogLevel::ERROR, "ASManager: command buffer for Acceleration Strucutre Build could not be allocated! (%s)", vk::to_string(result).c_str());
         }
-
-        beginCommandBuffer(commandBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+		
+		const vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+		commandBuffer.begin(beginInfo);
         return commandBuffer;
     }
 
@@ -144,9 +145,6 @@ namespace vkcv::rtx {
     }
 
     void ASManager::copyFromCPUToGPU(RTXBuffer& cpuBuffer, RTXBuffer& gpuBuffer) {
-        SubmitInfo submitInfo;
-        submitInfo.queueType = QueueType::Graphics;
-
         vk::CommandPool commandPool= createCommandPool();
         vk::CommandBuffer commandBuffer= createAndBeginCommandBuffer(commandPool);
         vk::BufferCopy bufferCopy;
diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp b/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
index 47560b4d9fddb867dd5573c64fe57c24c69095ce..bd5453abae353eed6739c41dbff084a0c909aaca 100644
--- a/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
+++ b/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
@@ -104,7 +104,7 @@ namespace vkcv::rtx {
 
         vk::WriteDescriptorSet tlasWrite;
         tlasWrite.setPNext(&AccelerationDescriptor);
-        tlasWrite.setDstSet(m_core->getDescriptorSet(descriptorSetHandles[0]).vulkanHandle);
+        tlasWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0]));
         tlasWrite.setDstBinding(1);
         tlasWrite.setDstArrayElement(0);
         tlasWrite.setDescriptorCount(1);
@@ -123,7 +123,7 @@ namespace vkcv::rtx {
         vertexInfo.setRange(blas.vertexBuffer.deviceSize); //maybe check if size is correct
 
         vk::WriteDescriptorSet vertexWrite;
-        vertexWrite.setDstSet(m_core->getDescriptorSet(descriptorSetHandles[0]).vulkanHandle);
+        vertexWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0]));
         vertexWrite.setDstBinding(3);
         vertexWrite.setDescriptorCount(1);
         vertexWrite.setDescriptorType(vk::DescriptorType::eStorageBuffer);
@@ -137,7 +137,7 @@ namespace vkcv::rtx {
         indexInfo.setRange(blas.indexBuffer.deviceSize); //maybe check if size is correct
 
         vk::WriteDescriptorSet indexWrite;
-        indexWrite.setDstSet(m_core->getDescriptorSet(descriptorSetHandles[0]).vulkanHandle);
+        indexWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0]));
         indexWrite.setDstBinding(4);
         indexWrite.setDescriptorCount(1);
         indexWrite.setDescriptorType(vk::DescriptorType::eStorageBuffer);
@@ -245,7 +245,7 @@ namespace vkcv::rtx {
 
         std::vector<vk::DescriptorSetLayout> descriptorSetLayoutsVulkan;
         for (size_t i=0; i<descriptorSetLayouts.size(); i++) {
-            descriptorSetLayoutsVulkan.push_back(m_core->getDescriptorSetLayout(descriptorSetLayouts[i]).vulkanHandle);
+            descriptorSetLayoutsVulkan.push_back(m_core->getVulkanDescriptorSetLayout(descriptorSetLayouts[i]));
         }
 
         vk::PushConstantRange pushConstant(
diff --git a/projects/rtx_ambient_occlusion/src/main.cpp b/projects/rtx_ambient_occlusion/src/main.cpp
index e1bfd2231bc8047f12ba7d71e6a176f1fd85d92c..d3d29049b8f64df1e00523c7beb55bd65dd46496 100644
--- a/projects/rtx_ambient_occlusion/src/main.cpp
+++ b/projects/rtx_ambient_occlusion/src/main.cpp
@@ -93,19 +93,9 @@ int main(int argc, const char** argv) {
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
 	vkcv::DescriptorWrites rtxWrites;
-
-	auto start = std::chrono::system_clock::now();
-	while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-
-		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
-			continue;
-
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
-			continue;
-		}
-
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((!depthBuffer) ||
 			(swapchainWidth != core.getImageWidth(depthBuffer)) ||
 			((swapchainHeight != core.getImageHeight(depthBuffer)))) {
@@ -113,14 +103,10 @@ int main(int argc, const char** argv) {
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 		}
-
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		
+		cameraManager.update(dt);
 
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		
@@ -130,7 +116,7 @@ int main(int argc, const char** argv) {
 		raytracingPushData.camera_up = glm::vec4(cameraManager.getActiveCamera().getUp(),0);
 		raytracingPushData.camera_forward = glm::vec4(cameraManager.getActiveCamera().getFront(),0);
 
-		vkcv::PushConstants pushConstantsRTX(sizeof(RaytracingPushConstantData));
+		vkcv::PushConstants pushConstantsRTX = vkcv::pushConstants<RaytracingPushConstantData>();
 		pushConstantsRTX.appendDrawcall(raytracingPushData);
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
@@ -148,14 +134,13 @@ int main(int argc, const char** argv) {
 			rtxRegions.rmissRegion,
 			rtxRegions.rchitRegion,
 			rtxRegions.rcallRegion,
-			{	vkcv::DescriptorSetUsage(0, rtxShaderDescriptorSet)},
+			{ vkcv::useDescriptorSet(0, rtxShaderDescriptorSet) },
 			pushConstantsRTX,
 			windowHandle);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-		core.endFrame(windowHandle);
-	}
+	});
 
 	return 0;
 }
diff --git a/projects/saf_r/src/main.cpp b/projects/saf_r/src/main.cpp
index 68bc546ded40e7f45454d62bfc4e8cd7227da4b7..1f446a8f5a4cd1bf5946328b5fe7e144f12f0d5f 100644
--- a/projects/saf_r/src/main.cpp
+++ b/projects/saf_r/src/main.cpp
@@ -1,15 +1,19 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
-#include <GLFW/glfw3.h>
+#include <vkcv/Buffer.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
+
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
-#include <chrono>
+
 #include <cmath>
 #include <vector>
 #include <cstring>
-#include "safrScene.hpp"
+#include <GLFW/glfw3.h>
 
+#include "safrScene.hpp"
 
 void createQuadraticLightCluster(std::vector<safrScene::Light>& lights, int countPerDimension, float dimension, float height, float intensity) {
     float distance = dimension/countPerDimension;
@@ -39,16 +43,7 @@ int main(int argc, const char** argv) {
 	);
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
-
-	//configuring the compute Shader
-	vkcv::PassConfig computePassDefinition({});
-	vkcv::PassHandle computePass = core.createPass(computePassDefinition);
-
-	if (!computePass)
-	{
-		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
-		return EXIT_FAILURE;
-	}
+	vkcv::Window& window = core.getWindow(windowHandle);
 
 	std::string shaderPathCompute = "shaders/raytracing.comp";
 
@@ -121,24 +116,19 @@ int main(int argc, const char** argv) {
 	lights.push_back(safrScene::Light(glm::vec3(30,  20,  30), 1.7));
     */
     createQuadraticLightCluster(lights, 10, 2.5f, 20, 1.5f);
-
-
-	vkcv::SamplerHandle sampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::REPEAT
-	);
-
+	
+	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
 	
 	//create Buffer for compute shader
-	vkcv::Buffer<safrScene::Light> lightsBuffer = core.createBuffer<safrScene::Light>(
+	vkcv::Buffer<safrScene::Light> lightsBuffer = vkcv::buffer<safrScene::Light>(
+		core,
 		vkcv::BufferType::STORAGE,
 		lights.size()
 	);
 	lightsBuffer.fill(lights);
 
-	vkcv::Buffer<safrScene::Sphere> sphereBuffer = core.createBuffer<safrScene::Sphere>(
+	vkcv::Buffer<safrScene::Sphere> sphereBuffer = vkcv::buffer<safrScene::Sphere>(
+		core,
 		vkcv::BufferType::STORAGE,
 		spheres.size()
 	);
@@ -153,44 +143,37 @@ int main(int argc, const char** argv) {
 	
     core.writeDescriptorSet(computeDescriptorSet, computeWrites);
 
-	auto safrIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, vkcv::BufferMemoryType::DEVICE_LOCAL);
+	auto safrIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3);
 	uint16_t indices[3] = { 0, 1, 2 };
 	safrIndexBuffer.fill(&indices[0], sizeof(indices));
+	
+	vkcv::PassHandle safrPass = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined }
+	);
 
-	// an example attachment for passes that output to the window
-	const vkcv::AttachmentDescription present_color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		core.getSwapchain(windowHandle).getFormat());
-
-	vkcv::PassConfig safrPassDefinition({ present_color_attachment }, vkcv::Multisampling::None);
-	vkcv::PassHandle safrPass = core.createPass(safrPassDefinition);
-
-	if (!safrPass)
-	{
+	if (!safrPass) {
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
 	}
 
 	//create the render pipeline + compute pipeline
-	const vkcv::GraphicsPipelineConfig safrPipelineDefinition{
-			safrShaderProgram,
-			UINT32_MAX,
-			UINT32_MAX,
-			safrPass,
-			{},
-			{ descriptorSetLayout },
-			true
-	};
-
-	vkcv::GraphicsPipelineHandle safrPipeline = core.createGraphicsPipeline(safrPipelineDefinition);
-
-	const vkcv::ComputePipelineConfig computePipelineConfig{
-			computeShaderProgram,
-			{computeDescriptorSetLayout}
-	};
+	vkcv::GraphicsPipelineHandle safrPipeline = core.createGraphicsPipeline(
+			vkcv::GraphicsPipelineConfig(
+					safrShaderProgram,
+					safrPass,
+					{},
+					{ descriptorSetLayout }
+			)
+	);
 
-	vkcv::ComputePipelineHandle computePipeline = core.createComputePipeline(computePipelineConfig);
+	vkcv::ComputePipelineHandle computePipeline = core.createComputePipeline(
+			vkcv::ComputePipelineConfig(
+					computeShaderProgram,
+					{ computeDescriptorSetLayout }
+			)
+	);
 
 	if (!safrPipeline || !computePipeline)
 	{
@@ -198,39 +181,24 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 	
-	auto start = std::chrono::system_clock::now();
-
-	const vkcv::Mesh renderMesh({}, safrIndexBuffer.getVulkanHandle(), 3);
-	vkcv::DescriptorSetUsage descriptorUsage(0, descriptorSet);
-	vkcv::DrawcallInfo drawcall(renderMesh, { descriptorUsage }, 1);
+	vkcv::VertexData vertexData;
+	vertexData.setIndexBuffer(safrIndexBuffer.getHandle());
+	vertexData.setCount(3);
+	
+	vkcv::InstanceDrawcall drawcall (vertexData);
+	drawcall.useDescriptorSet(0, descriptorSet);
 
 	//create the camera
-	vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+	vkcv::camera::CameraManager cameraManager(window);
 	uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
 
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, 2));
 	cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
 	cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
-
-	float time = 0;
 	
-	while (vkcv::Window::hasOpenWindow())
-	{
-		vkcv::Window::pollEvents();
-
-		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-
-		//configure timer
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-		start = end;
-		
-		time += 0.000001f * static_cast<float>(deltatime.count());
-		
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		//adjust light position
 		/*
 		639a53157e7d3936caf7c3e40379159cbcf4c89e
@@ -240,13 +208,13 @@ int main(int argc, const char** argv) {
 		lightsBuffer.fill(lights);
 		*/
 
-		spheres[0].center.y += std::cos(time * 0.5f * 3.141f) * 0.25f;
-		spheres[1].center.x += std::cos(time * 2.f) * 0.25f;
-		spheres[1].center.z += std::cos(time * 2.f + 0.5f * 3.141f) * 0.25f;
+		spheres[0].center.y += std::cos(t * 0.5f * 3.141f) * 0.25f;
+		spheres[1].center.x += std::cos(t * 2.f) * 0.25f;
+		spheres[1].center.z += std::cos(t * 2.f + 0.5f * 3.141f) * 0.25f;
         sphereBuffer.fill(spheres);
 
 		//update camera
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
 		glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
 		glm::mat4 proj = cameraManager.getActiveCamera().getProjection();
 
@@ -273,25 +241,27 @@ int main(int argc, const char** argv) {
         raytracingPushData.sphereCount  = spheres.size();
         raytracingPushData.viewToWorld  = glm::inverse(cameraManager.getActiveCamera().getView());
 
-        vkcv::PushConstants pushConstantsCompute(sizeof(RaytracingPushConstantData));
+        vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<RaytracingPushConstantData>();
         pushConstantsCompute.appendDrawcall(raytracingPushData);
 
 		//dispatch compute shader
-		uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(swapchainWidth/16.f)),
-                                            static_cast<uint32_t> (std::ceil(swapchainHeight/16.f)),
-                                            1 }; // Anzahl workgroups
+		const auto computeDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(16, 16)
+		);
+		
 		core.recordComputeDispatchToCmdStream(cmdStream,
 			computePipeline,
 			computeDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, computeDescriptorSet) },
-			pushConstantsCompute);
+			{ vkcv::useDescriptorSet(0, computeDescriptorSet) },
+			pushConstantsCompute
+		);
 
 		core.recordBufferMemoryBarrier(cmdStream, lightsBuffer.getHandle());
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
-
-		core.endFrame(windowHandle);
-	}
+	});
+	
 	return 0;
 }
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 601075421a7bf2814e10514ae010fc83b7024f0a..4989b037375c3ca251411e957ea43030bba536c1 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -1,5 +1,7 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
+#include <vkcv/Buffer.hpp>
+#include <vkcv/Pass.hpp>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
 #include <random>
@@ -25,24 +27,12 @@ int main(int argc, const char **argv) {
 
     vkcv::camera::CameraManager cameraManager(window);
 
-    auto particleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3,
-                                                           vkcv::BufferMemoryType::DEVICE_LOCAL);
+    auto particleIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3);
     uint16_t indices[3] = {0, 1, 2};
     particleIndexBuffer.fill(&indices[0], sizeof(indices));
 
     vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
-    // an example attachment for passes that output to the window
-    const vkcv::AttachmentDescription present_color_attachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            colorFormat);
-
-
-    vkcv::PassConfig particlePassDefinition({present_color_attachment}, vkcv::Multisampling::None);
-    vkcv::PassHandle particlePass = core.createPass(particlePassDefinition);
-
-    vkcv::PassConfig computePassDefinition({});
-    vkcv::PassHandle computePass = core.createPass(computePassDefinition);
+    vkcv::PassHandle particlePass = vkcv::passFormat(core, colorFormat);
 
     //rotation
     float rotationx = 0;
@@ -58,7 +48,7 @@ int main(int argc, const char **argv) {
     float param_ABSORBTION = 0.5;
     float param_dt = 0.0005;
 
-    if (!particlePass || !computePass)
+    if (!particlePass)
     {
         std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
         return EXIT_FAILURE;
@@ -98,14 +88,16 @@ int main(int argc, const char **argv) {
             particleShaderProgram.getReflectedDescriptors().at(0));
     vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 
-    vkcv::Buffer<glm::vec3> vertexBuffer = core.createBuffer<glm::vec3>(
+    vkcv::Buffer<glm::vec3> vertexBuffer = vkcv::buffer<glm::vec3>(
+			core,
             vkcv::BufferType::VERTEX,
             3
     );
     const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments();
 
     const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-            vkcv::VertexBufferBinding(0, vertexBuffer.getVulkanHandle())};
+            vkcv::vertexBufferBinding(vertexBuffer.getHandle())
+	};
 
     std::vector<vkcv::VertexBinding> bindings;
     for (size_t i = 0; i < vertexAttachments.size(); i++) {
@@ -115,16 +107,14 @@ int main(int argc, const char **argv) {
     const vkcv::VertexLayout particleLayout { bindings };
 
     // initializing graphics pipeline
-    vkcv::GraphicsPipelineConfig particlePipelineDefinition{
+    vkcv::GraphicsPipelineConfig particlePipelineDefinition (
             particleShaderProgram,
-            UINT32_MAX,
-            UINT32_MAX,
             particlePass,
             {particleLayout},
-            {descriptorSetLayout},
-            true
-	};
-    particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+            {descriptorSetLayout}
+	);
+	
+    particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 
     const std::vector<glm::vec3> vertices = {glm::vec3(-0.012, 0.012, 0),
                                              glm::vec3(0.012, 0.012, 0),
@@ -134,12 +124,14 @@ int main(int argc, const char **argv) {
 
     vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition);
 
-    vkcv::Buffer<glm::vec4> color = core.createBuffer<glm::vec4>(
+    vkcv::Buffer<glm::vec4> color = vkcv::buffer<glm::vec4>(
+			core,
             vkcv::BufferType::UNIFORM,
             1
     );
 
-    vkcv::Buffer<glm::vec2> position = core.createBuffer<glm::vec2>(
+    vkcv::Buffer<glm::vec2> position = vkcv::buffer<glm::vec2>(
+			core,
             vkcv::BufferType::UNIFORM,
             1
     );
@@ -165,15 +157,16 @@ int main(int argc, const char **argv) {
     }
 
     // creating and filling particle buffer
-    vkcv::Buffer<Particle> particleBuffer1 = core.createBuffer<Particle>(
+    vkcv::Buffer<Particle> particleBuffer1 = vkcv::buffer<Particle>(
+			core,
             vkcv::BufferType::STORAGE,
-            numberParticles * sizeof(glm::vec4) * 3
-
+            numberParticles
     );
 
-    vkcv::Buffer<Particle> particleBuffer2 = core.createBuffer<Particle>(
-        vkcv::BufferType::STORAGE,
-        numberParticles * sizeof(glm::vec4) * 3
+    vkcv::Buffer<Particle> particleBuffer2 = vkcv::buffer<Particle>(
+			core,
+			vkcv::BufferType::STORAGE,
+			numberParticles
     );
 
     particleBuffer1.fill(particles);
@@ -205,17 +198,15 @@ int main(int argc, const char **argv) {
 
     const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
-    const vkcv::Mesh renderMesh({vertexBufferBindings}, particleIndexBuffer.getVulkanHandle(),
-                                particleIndexBuffer.getCount());
-    vkcv::DescriptorSetUsage descriptorUsage(0, descriptorSet);
+	vkcv::VertexData vertexData (vertexBufferBindings);
+	vertexData.setIndexBuffer(particleIndexBuffer.getHandle());
+	vertexData.setCount(particleIndexBuffer.getCount());
 
     auto pos = glm::vec2(0.f);
-
-    std::vector<vkcv::DrawcallInfo> drawcalls;
-    drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage}, numberParticles));
-
-    auto start = std::chrono::system_clock::now();
-
+	
+	vkcv::InstanceDrawcall drawcall (vertexData, numberParticles);
+	drawcall.useDescriptorSet(0, descriptorSet);
+	
     glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f);
     uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
     uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
@@ -229,14 +220,14 @@ int main(int argc, const char **argv) {
     cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, -2.5f));
     cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f));
 
-	const auto swapchainExtent = core.getSwapchain(window.getSwapchainHandle()).getExtent();
+	const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
     vkcv::ImageHandle colorBuffer = core.createImage(
 			colorFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, true, true
-	).getHandle();
+	);
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
 	bloomAndFlares.setUpsamplingLimit(3);
@@ -249,15 +240,9 @@ int main(int argc, const char **argv) {
 			"shaders/tonemapping.comp",
 			tonemappingPipe
 	);
-
-    while (vkcv::Window::hasOpenWindow()) {
-        vkcv::Window::pollEvents();
-
-        uint32_t swapchainWidth, swapchainHeight;
-        if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-            continue;
-        }
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((core.getImageWidth(colorBuffer) != swapchainWidth) ||
 			(core.getImageHeight(colorBuffer) != swapchainHeight)) {
 			colorBuffer = core.createImage(
@@ -265,17 +250,13 @@ int main(int argc, const char **argv) {
 					swapchainWidth,
 					swapchainHeight,
 					1, false, true, true
-			).getHandle();
+			);
 		}
 
         color.fill(&colorData);
         position.fill(&pos);
 
-        auto end = std::chrono::system_clock::now();
-        float deltatime = 0.000001 * static_cast<float>( std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() );
-        start = end;
-
-        cameraManager.update(deltatime);
+        cameraManager.update(dt);
 
         // split view and projection to allow for easy billboarding in shader
         struct {
@@ -293,35 +274,35 @@ int main(int argc, const char **argv) {
 
         // keybindings rotation
         if (glfwGetKey(window.getWindow(), GLFW_KEY_LEFT) == GLFW_PRESS)
-            rotationx += deltatime * 50;
+            rotationx += dt * 50;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_RIGHT) == GLFW_PRESS)
-            rotationx -= deltatime * 50;
+            rotationx -= dt * 50;
         
         if (glfwGetKey(window.getWindow(), GLFW_KEY_UP) == GLFW_PRESS)
-            rotationy += deltatime * 50;
+            rotationy += dt * 50;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_DOWN) == GLFW_PRESS)
-            rotationy -= deltatime * 50;
+            rotationy -= dt * 50;
 
         // keybindings params
         if (glfwGetKey(window.getWindow(), GLFW_KEY_T) == GLFW_PRESS)
-            param_h += deltatime * 0.2;
+            param_h += dt * 0.2;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_G) == GLFW_PRESS)
-            param_h -= deltatime * 0.2;
+            param_h -= dt * 0.2;
 
         if (glfwGetKey(window.getWindow(), GLFW_KEY_Y) == GLFW_PRESS)
-            param_mass += deltatime * 0.2;
+            param_mass += dt * 0.2;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_H) == GLFW_PRESS)
-            param_mass -= deltatime * 0.2;
+            param_mass -= dt * 0.2;
 
         if (glfwGetKey(window.getWindow(), GLFW_KEY_U) == GLFW_PRESS)
-            param_gasConstant += deltatime * 1500.0;
+            param_gasConstant += dt * 1500.0;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_J) == GLFW_PRESS)
-            param_gasConstant -= deltatime * 1500.0;
+            param_gasConstant -= dt * 1500.0;
 
         if (glfwGetKey(window.getWindow(), GLFW_KEY_I) == GLFW_PRESS)
-            param_offset += deltatime * 400.0;
+            param_offset += dt * 400.0;
         if (glfwGetKey(window.getWindow(), GLFW_KEY_K) == GLFW_PRESS)
-            param_offset -= deltatime * 400.0;
+            param_offset -= dt * 400.0;
 
         if (glfwGetKey(window.getWindow(), GLFW_KEY_O) == GLFW_PRESS)
             param_viscosity = 50;
@@ -342,44 +323,52 @@ int main(int argc, const char **argv) {
         vkcv::PushConstants pushConstantsCompute (sizeof(pushData));
         pushConstantsCompute.appendDrawcall(pushData);
 
-        uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil(numberParticles/256.f)),1,1};
+        const auto computeDispatchCount = vkcv::dispatchInvocations(numberParticles, 256);
 
         // computing pressure pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              pressurePipeline,
-                                              computeDispatchCount,
-                                              {vkcv::DescriptorSetUsage(0, pressureDescriptorSet)},
-											  pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				pressurePipeline,
+				computeDispatchCount,
+				{ vkcv::useDescriptorSet(0, pressureDescriptorSet) },
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing force pipeline
-		core.recordComputeDispatchToCmdStream(cmdStream,
-											  forcePipeline,
-											  computeDispatchCount,
-											  {vkcv::DescriptorSetUsage(0, forceDescriptorSet)},
-											  pushConstantsCompute);
+		core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				forcePipeline,
+				computeDispatchCount,
+				{ vkcv::useDescriptorSet(0, forceDescriptorSet) },
+				pushConstantsCompute
+		);
 
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing update data pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              updateDataPipeline,
-                                              computeDispatchCount,
-                                              { vkcv::DescriptorSetUsage(0, updateDataDescriptorSet) },
-                                              pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				updateDataPipeline,
+				computeDispatchCount,
+				{ vkcv::useDescriptorSet(0, updateDataDescriptorSet) },
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
 
         // computing flip pipeline
-        core.recordComputeDispatchToCmdStream(cmdStream,
-                                              flipPipeline,
-                                              computeDispatchCount,
-                                              { vkcv::DescriptorSetUsage(0, flipDescriptorSet) },
-                                              pushConstantsCompute);
+        core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				flipPipeline,
+				computeDispatchCount,
+				{ vkcv::useDescriptorSet(0, flipDescriptorSet) },
+				pushConstantsCompute
+		);
 
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle());
         core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle());
@@ -391,10 +380,9 @@ int main(int argc, const char **argv) {
         
         core.recordDrawcallsToCmdStream(
                 cmdStream,
-                particlePass,
                 particlePipeline,
 				pushConstantsDraw,
-                {drawcalls},
+                { drawcall },
                 { colorBuffer },
                 windowHandle
 		);
@@ -413,22 +401,22 @@ int main(int argc, const char **argv) {
 		
         core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
 
-        uint32_t tonemappingDispatchCount[3];
-        tonemappingDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-        tonemappingDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-        tonemappingDispatchCount[2] = 1;
+        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 
         core.recordComputeDispatchToCmdStream(
             cmdStream, 
             tonemappingPipe, 
             tonemappingDispatchCount, 
-            {vkcv::DescriptorSetUsage(0, tonemappingDescriptor) },
-            vkcv::PushConstants(0));
+            { vkcv::useDescriptorSet(0, tonemappingDescriptor) },
+            vkcv::PushConstants(0)
+		);
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
-        core.endFrame(windowHandle);
-    }
+    });
 
     return 0;
 }
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index a4b967b33f47110df42ca02849fbeb06b4a024f8..73f98cd3ea35d7f2bdf99d29f803df0abbc1adac 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -1,4 +1,6 @@
 #include "ShadowMapping.hpp"
+
+#include <vkcv/Pass.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 
 const vk::Format            shadowMapFormat         = vk::Format::eR16G16B16A16Unorm;
@@ -151,34 +153,28 @@ glm::mat4 computeShadowViewProjectionMatrix(
 
 ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vertexLayout) : 
 	m_corePtr(corePtr),
-	m_shadowMap(corePtr->createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, true, true)),
-	m_shadowMapIntermediate(corePtr->createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, false, true)),
-	m_shadowMapDepth(corePtr->createImage(shadowMapDepthFormat, shadowMapResolution, shadowMapResolution, 1, false, false, false, msaa)),
-	m_lightInfoBuffer(corePtr->createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3))){
+	m_shadowMap(vkcv::image(*corePtr, shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, true, true)),
+	m_shadowMapIntermediate(vkcv::image(*corePtr, shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, false, true)),
+	m_shadowMapDepth(vkcv::image(*corePtr, shadowMapDepthFormat, shadowMapResolution, shadowMapResolution, 1, false, false, false, msaa)),
+	m_lightInfoBuffer(buffer<LightInfo>(*corePtr, vkcv::BufferType::UNIFORM, 1)){
 
 	vkcv::ShaderProgram shadowShader = loadShadowShader();
 
 	// pass
-	const std::vector<vkcv::AttachmentDescription> shadowAttachments = {
-		vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapDepthFormat)
-	};
-	vkcv::PassConfig shadowPassConfig(shadowAttachments, msaa);
-	m_shadowMapPass = corePtr->createPass(shadowPassConfig);
+	m_shadowMapPass = vkcv::passFormat(*corePtr, shadowMapDepthFormat, true, msaa);
 
 	// pipeline
-	vkcv::GraphicsPipelineConfig shadowPipeConfig{
+	vkcv::GraphicsPipelineConfig shadowPipeConfig (
 		shadowShader,
-		shadowMapResolution,
-		shadowMapResolution,
 		m_shadowMapPass,
 		vertexLayout,
-		{},
-		false
-	};
-	shadowPipeConfig.m_multisampling        = msaa;
-	shadowPipeConfig.m_EnableDepthClamping  = true;
-	shadowPipeConfig.m_culling              = vkcv::CullMode::Front;
-	m_shadowMapPipe                         = corePtr->createGraphicsPipeline(shadowPipeConfig);
+		{}
+	);
+	
+	shadowPipeConfig.setResolution(shadowMapResolution, shadowMapResolution);
+	shadowPipeConfig.setDepthClampingEnabled(true);
+	shadowPipeConfig.setCulling(vkcv::CullMode::Front);
+	m_shadowMapPipe = corePtr->createGraphicsPipeline(shadowPipeConfig);
 
 	m_shadowSampler = corePtr->createSampler(
 		vkcv::SamplerFilterType::LINEAR,
@@ -226,18 +222,18 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert
 }
 
 void ShadowMapping::recordShadowMapRendering(
-	const vkcv::CommandStreamHandle&    cmdStream,
-	const glm::vec2&                    lightAngleRadian,
-	const glm::vec3&                    lightColor,
-	float                               lightStrength,
-	float                               maxShadowDistance,
-	const std::vector<vkcv::Mesh>&      meshes,
-	const std::vector<glm::mat4>&       modelMatrices,
-	const vkcv::camera::Camera&         camera,
-	const glm::vec3&                    voxelVolumeOffset,
-	float                               voxelVolumeExtent,
-	const vkcv::WindowHandle&           windowHandle,
-	vkcv::Downsampler&					downsampler) {
+	const vkcv::CommandStreamHandle&     cmdStream,
+	const glm::vec2&                     lightAngleRadian,
+	const glm::vec3&                     lightColor,
+	float                                lightStrength,
+	float                                maxShadowDistance,
+	const std::vector<vkcv::VertexData>& meshes,
+	const std::vector<glm::mat4>&        modelMatrices,
+	const vkcv::camera::Camera&          camera,
+	const glm::vec3&                     voxelVolumeOffset,
+	float                                voxelVolumeExtent,
+	const vkcv::WindowHandle&            windowHandle,
+	vkcv::Downsampler&					 downsampler) {
 
 	LightInfo lightInfo;
 	lightInfo.sunColor = lightColor;
@@ -255,38 +251,38 @@ void ShadowMapping::recordShadowMapRendering(
 		voxelVolumeExtent);
 	m_lightInfoBuffer.fill({ lightInfo });
 	
-	vkcv::PushConstants shadowPushConstants (sizeof(glm::mat4));
+	vkcv::PushConstants shadowPushConstants = vkcv::pushConstants<glm::mat4>();
 	
 	for (const auto& m : modelMatrices) {
 		shadowPushConstants.appendDrawcall(lightInfo.lightMatrix * m);
 	}
 	
-	std::vector<vkcv::DrawcallInfo> drawcalls;
+	std::vector<vkcv::InstanceDrawcall> drawcalls;
 	for (const auto& mesh : meshes) {
-		drawcalls.push_back(vkcv::DrawcallInfo(mesh, {}));
+		drawcalls.push_back(vkcv::InstanceDrawcall(mesh));
 	}
 
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Shadow map depth", {1, 1, 1, 1});
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_shadowMapPass,
 		m_shadowMapPipe,
 		shadowPushConstants,
 		drawcalls,
 		{ m_shadowMapDepth.getHandle() },
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
 	// depth to moments
-	uint32_t dispatchCount[3];
-	dispatchCount[0] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f));
-	dispatchCount[1] = static_cast<uint32_t>(std::ceil(shadowMapResolution / 8.f));
-	dispatchCount[2] = 1;
+	const auto dispatchCount = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(shadowMapResolution, shadowMapResolution),
+			vkcv::DispatchSize(8, 8)
+	);
 
-	const uint32_t msaaSampleCount = msaaToSampleCount(msaa);
+	const uint32_t msaaSampleCount = vkcv::msaaToSampleCount(msaa);
 	
-	vkcv::PushConstants msaaPushConstants (sizeof(msaaSampleCount));
+	vkcv::PushConstants msaaPushConstants = vkcv::pushConstants<uint32_t>();
 	msaaPushConstants.appendDrawcall(msaaSampleCount);
 
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Depth to moments", { 1, 1, 1, 1 });
@@ -296,8 +292,9 @@ void ShadowMapping::recordShadowMapRendering(
 		cmdStream,
 		m_depthToMomentsPipe,
 		dispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_depthToMomentsDescriptorSet) },
-		msaaPushConstants);
+		{ vkcv::useDescriptorSet(0, m_depthToMomentsDescriptorSet) },
+		msaaPushConstants
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
@@ -309,8 +306,9 @@ void ShadowMapping::recordShadowMapRendering(
 		cmdStream,
 		m_shadowBlurXPipe,
 		dispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_shadowBlurXDescriptorSet) },
-		vkcv::PushConstants(0));
+		{ vkcv::useDescriptorSet(0, m_shadowBlurXDescriptorSet) },
+		vkcv::PushConstants(0)
+	);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle());
 
 	// blur Y
@@ -319,8 +317,9 @@ void ShadowMapping::recordShadowMapRendering(
 		cmdStream,
 		m_shadowBlurYPipe,
 		dispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_shadowBlurYDescriptorSet) },
-		vkcv::PushConstants(0));
+		{ vkcv::useDescriptorSet(0, m_shadowBlurYDescriptorSet) },
+		vkcv::PushConstants(0)
+	);
 	m_shadowMap.recordMipChainGeneration(cmdStream, downsampler);
 
 	m_corePtr->recordEndDebugLabel(cmdStream);
diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp
index 14fef77df51632977f4c4eade4ebcdc28ba56b68..a843a7de9c9e4c731b16ae0c8d5bdfd2ac263711 100644
--- a/projects/voxelization/src/ShadowMapping.hpp
+++ b/projects/voxelization/src/ShadowMapping.hpp
@@ -1,7 +1,10 @@
 #pragma once
+
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
 #include <vkcv/camera/Camera.hpp>
 #include <vkcv/Downsampler.hpp>
+#include <vkcv/Image.hpp>
 
 #include <glm/glm.hpp>
 #define GLM_ENABLE_EXPERIMENTAL // use this before inclusion, else error!
@@ -20,18 +23,18 @@ public:
 	ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vertexLayout);
 
 	void recordShadowMapRendering(
-		const vkcv::CommandStreamHandle&    cmdStream,
-		const glm::vec2&                    lightAngleRadian,
-		const glm::vec3&                    lightColor,
-		float                               lightStrength,
-		float                               maxShadowDistance,
-		const std::vector<vkcv::Mesh>&      meshes,
-		const std::vector<glm::mat4>&       modelMatrices,
-		const vkcv::camera::Camera&         camera,
-		const glm::vec3&                    voxelVolumeOffset,
-		float                               voxelVolumeExtent,
-		const vkcv::WindowHandle&           windowHandle,
-		vkcv::Downsampler&					downsampler);
+		const vkcv::CommandStreamHandle&     cmdStream,
+		const glm::vec2&                     lightAngleRadian,
+		const glm::vec3&                     lightColor,
+		float                                lightStrength,
+		float                                maxShadowDistance,
+		const std::vector<vkcv::VertexData>& meshes,
+		const std::vector<glm::mat4>&        modelMatrices,
+		const vkcv::camera::Camera&          camera,
+		const glm::vec3&                     voxelVolumeOffset,
+		float                                voxelVolumeExtent,
+		const vkcv::WindowHandle&            windowHandle,
+		vkcv::Downsampler&					 downsampler);
 
 	vkcv::ImageHandle   getShadowMap();
 	vkcv::SamplerHandle getShadowSampler();
diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp
index b30d8a7fa1e9cb56158903de4b11c24ddc503b94..5c70b9dd0b3fe21118c4eddc32fca53e6e21c329 100644
--- a/projects/voxelization/src/Voxelization.cpp
+++ b/projects/voxelization/src/Voxelization.cpp
@@ -1,4 +1,6 @@
 #include "Voxelization.hpp"
+
+#include <vkcv/Pass.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include <glm/gtc/matrix_transform.hpp>
 #include <algorithm>
@@ -84,20 +86,20 @@ Voxelization::Voxelization(
 	vkcv::Multisampling msaa)
 	:
 	m_corePtr(corePtr),
-	m_voxelImageIntermediate(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)),
-	m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)),
-	m_voxelBuffer(m_corePtr->createBuffer<VoxelBufferContent>(vkcv::BufferType::STORAGE, voxelCount)),
-	m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)),
-	m_voxelInfoBuffer(m_corePtr->createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, 1)) {
+	m_voxelImageIntermediate(vkcv::image(*m_corePtr, vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)),
+	m_voxelImage(vkcv::image(*m_corePtr, vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)),
+	m_voxelBuffer(buffer<VoxelBufferContent>(*m_corePtr, vkcv::BufferType::STORAGE, voxelCount)),
+	m_dummyRenderTarget(vkcv::image(*m_corePtr, voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)),
+	m_voxelInfoBuffer(buffer<VoxelizationInfo>(*m_corePtr, vkcv::BufferType::UNIFORM, 1)) {
 
 	const vkcv::ShaderProgram voxelizationShader = loadVoxelizationShader();
 
 	const vkcv::PassConfig voxelizationPassConfig {{
-		  {
-				  vkcv::AttachmentOperation::DONT_CARE,
+		  vkcv::AttachmentDescription(
+				  voxelizationDummyFormat,
 				  vkcv::AttachmentOperation::DONT_CARE,
-				  voxelizationDummyFormat
-		  }
+				  vkcv::AttachmentOperation::DONT_CARE
+		  )
 	}, vkcv::Multisampling::None };
 	m_voxelizationPass = m_corePtr->createPass(voxelizationPassConfig);
 
@@ -107,16 +109,16 @@ Voxelization::Voxelization(
 	vkcv::DescriptorSetLayoutHandle dummyPerMeshDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelizationShader.getReflectedDescriptors().at(1));
 	vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet = m_corePtr->createDescriptorSet(dummyPerMeshDescriptorSetLayout);
 
-	const vkcv::GraphicsPipelineConfig voxelizationPipeConfig{
+	vkcv::GraphicsPipelineConfig voxelizationPipeConfig (
 		voxelizationShader,
-		voxelResolution,
-		voxelResolution,
 		m_voxelizationPass,
 		dependencies.vertexLayout,
-		{ m_voxelizationDescriptorSetLayout, dummyPerMeshDescriptorSetLayout },
-		false,
-		true
-	};
+		{ m_voxelizationDescriptorSetLayout, dummyPerMeshDescriptorSetLayout }
+	);
+	
+	voxelizationPipeConfig.setResolution(voxelResolution, voxelResolution);
+	voxelizationPipeConfig.setUsingConservativeRasterization(true);
+	
 	m_voxelizationPipe = m_corePtr->createGraphicsPipeline(voxelizationPipeConfig);
 
 	vkcv::DescriptorWrites voxelizationDescriptorWrites;
@@ -136,38 +138,22 @@ Voxelization::Voxelization(
 
 	m_visualisationDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelVisualisationShader.getReflectedDescriptors().at(0));
 	m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(m_visualisationDescriptorSetLayout);
-
-	const vkcv::AttachmentDescription voxelVisualisationColorAttachments(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		dependencies.colorBufferFormat
-	);
-
-	const vkcv::AttachmentDescription voxelVisualisationDepthAttachments(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		dependencies.depthBufferFormat
-	);
-
-	vkcv::PassConfig voxelVisualisationPassDefinition{
-		{ voxelVisualisationColorAttachments, voxelVisualisationDepthAttachments },
-		msaa
-	};
 	
-	m_visualisationPass = m_corePtr->createPass(voxelVisualisationPassDefinition);
+	m_visualisationPass = vkcv::passFormats(
+			*m_corePtr,
+			{ dependencies.colorBufferFormat, dependencies.depthBufferFormat },
+			false,
+			msaa
+	);
 
-	vkcv::GraphicsPipelineConfig voxelVisualisationPipeConfig{
+	vkcv::GraphicsPipelineConfig voxelVisualisationPipeConfig (
 		voxelVisualisationShader,
-		0,
-		0,
 		m_visualisationPass,
 		{},
-		{ m_visualisationDescriptorSetLayout },
-		true,
-		false,
-		vkcv::PrimitiveTopology::PointList
-	};	// points are extended to cubes in the geometry shader
-	voxelVisualisationPipeConfig.m_multisampling = msaa;
+		{ m_visualisationDescriptorSetLayout }
+	);	// points are extended to cubes in the geometry shader
+	
+	voxelVisualisationPipeConfig.setPrimitiveTopology(vkcv::PrimitiveTopology::PointList);
 	m_visualisationPipe = m_corePtr->createGraphicsPipeline(voxelVisualisationPipeConfig);
 
 	std::vector<uint16_t> voxelIndexData;
@@ -175,7 +161,7 @@ Voxelization::Voxelization(
 		voxelIndexData.push_back(static_cast<uint16_t>(i));
 	}
 
-	const vkcv::DescriptorSetUsage voxelizationDescriptorUsage(0, m_visualisationDescriptorSet);
+	const auto voxelizationDescriptorUsage = vkcv::useDescriptorSet(0, m_visualisationDescriptorSet);
 
 	vkcv::ShaderProgram resetVoxelShader = loadVoxelResetShader();
 
@@ -226,7 +212,7 @@ Voxelization::Voxelization(
 
 void Voxelization::voxelizeMeshes(
 	vkcv::CommandStreamHandle                       cmdStream,
-	const std::vector<vkcv::Mesh>&                  meshes,
+	const std::vector<vkcv::VertexData>&            meshes,
 	const std::vector<glm::mat4>&                   modelMatrices,
 	const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets,
 	const vkcv::WindowHandle&                       windowHandle,
@@ -254,61 +240,56 @@ void Voxelization::voxelizeMeshes(
 
 	// reset voxels
 	const uint32_t resetVoxelGroupSize = 64;
-	uint32_t resetVoxelDispatchCount[3];
-	resetVoxelDispatchCount[0] = glm::ceil(voxelCount / float(resetVoxelGroupSize));
-	resetVoxelDispatchCount[1] = 1;
-	resetVoxelDispatchCount[2] = 1;
 	
-	vkcv::PushConstants voxelCountPushConstants (sizeof(voxelCount));
+	vkcv::PushConstants voxelCountPushConstants = vkcv::pushConstants<uint32_t>();
 	voxelCountPushConstants.appendDrawcall(voxelCount);
 
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel reset", { 1, 1, 1, 1 });
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_voxelResetPipe,
-		resetVoxelDispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_voxelResetDescriptorSet) },
-		voxelCountPushConstants);
+		vkcv::dispatchInvocations(voxelCount, resetVoxelGroupSize),
+		{ vkcv::useDescriptorSet(0, m_voxelResetDescriptorSet) },
+		voxelCountPushConstants
+	);
 	m_corePtr->recordBufferMemoryBarrier(cmdStream, m_voxelBuffer.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
 	// voxelization
-	std::vector<vkcv::DrawcallInfo> drawcalls;
+	std::vector<vkcv::InstanceDrawcall> drawcalls;
 	for (size_t i = 0; i < meshes.size(); i++) {
-		drawcalls.push_back(vkcv::DrawcallInfo(
-			meshes[i],
-			{ 
-				vkcv::DescriptorSetUsage(0, m_voxelizationDescriptorSet),
-				vkcv::DescriptorSetUsage(1, perMeshDescriptorSets[i])
-			},1));
+		vkcv::InstanceDrawcall drawcall (meshes[i]);
+		drawcall.useDescriptorSet(0, m_voxelizationDescriptorSet);
+		drawcall.useDescriptorSet(1, perMeshDescriptorSets[i]);
+		drawcalls.push_back(drawcall);
 	}
 
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Voxelization", { 1, 1, 1, 1 });
 	m_corePtr->prepareImageForStorage(cmdStream, m_voxelImageIntermediate.getHandle());
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_voxelizationPass,
 		m_voxelizationPipe,
 		voxelizationPushConstants,
 		drawcalls,
 		{ m_dummyRenderTarget.getHandle() },
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->recordEndDebugLabel(cmdStream);
 
 	// buffer to image
-	const uint32_t bufferToImageGroupSize[3] = { 4, 4, 4 };
-	uint32_t bufferToImageDispatchCount[3];
-	for (int i = 0; i < 3; i++) {
-		bufferToImageDispatchCount[i] = glm::ceil(voxelResolution / float(bufferToImageGroupSize[i]));
-	}
-
+	const auto bufferToImageDispatchCount = vkcv::dispatchInvocations(
+			vkcv::DispatchSize(voxelResolution, voxelResolution, voxelResolution),
+			vkcv::DispatchSize(4, 4, 4)
+	);
+	
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel buffer to image", { 1, 1, 1, 1 });
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_bufferToImagePipe,
 		bufferToImageDispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_bufferToImageDescriptorSet) },
-		vkcv::PushConstants(0));
+		{ vkcv::useDescriptorSet(0, m_bufferToImageDescriptorSet) },
+		vkcv::PushConstants(0)
+	);
 
 	m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImageIntermediate.getHandle());
 	m_corePtr->recordEndDebugLabel(cmdStream);
@@ -325,7 +306,7 @@ void Voxelization::voxelizeMeshes(
 		cmdStream,
 		m_secondaryBouncePipe,
 		bufferToImageDispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_secondaryBounceDescriptorSet) },
+		{ vkcv::useDescriptorSet(0, m_secondaryBounceDescriptorSet) },
 		vkcv::PushConstants(0));
 	m_voxelImage.recordMipChainGeneration(cmdStream, downsampler);
 	m_corePtr->recordEndDebugLabel(cmdStream);
@@ -343,10 +324,10 @@ void Voxelization::renderVoxelVisualisation(
 	uint32_t                                mipLevel,
 	const vkcv::WindowHandle&               windowHandle) {
 
-	vkcv::PushConstants voxelVisualisationPushConstants (sizeof(glm::mat4));
+	vkcv::PushConstants voxelVisualisationPushConstants = vkcv::pushConstants<glm::mat4>();
 	voxelVisualisationPushConstants.appendDrawcall(viewProjectin);
 
-	mipLevel = std::clamp(mipLevel, (uint32_t)0, m_voxelImage.getMipCount()-1);
+	mipLevel = std::clamp(mipLevel, (uint32_t) 0, m_voxelImage.getMipLevels() - 1);
 
 	// write descriptor set
 	vkcv::DescriptorWrites voxelVisualisationDescriptorWrite;
@@ -355,21 +336,23 @@ void Voxelization::renderVoxelVisualisation(
 	m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite);
 
 	uint32_t drawVoxelCount = voxelCount / exp2(mipLevel);
-
-	const auto drawcall = vkcv::DrawcallInfo(
-		vkcv::Mesh({}, nullptr, drawVoxelCount),
-		{ vkcv::DescriptorSetUsage(0, m_visualisationDescriptorSet) },1);
+	
+	vkcv::VertexData voxelData;
+	voxelData.setCount(drawVoxelCount);
+	
+	vkcv::InstanceDrawcall drawcall (voxelData);
+	drawcall.useDescriptorSet(0, m_visualisationDescriptorSet);
 
 	m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel visualisation", { 1, 1, 1, 1 });
 	m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle());
 	m_corePtr->recordDrawcallsToCmdStream(
 		cmdStream,
-		m_visualisationPass,
 		m_visualisationPipe,
 		voxelVisualisationPushConstants,
 		{ drawcall },
 		renderTargets,
-		windowHandle);
+		windowHandle
+	);
 	m_corePtr->recordEndDebugLabel(cmdStream);
 }
 
diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp
index 866efaf47354bc0a53ebd500d06dc4a49b0ed690..f624d03503b67cc2de0632367d90742fb975473e 100644
--- a/projects/voxelization/src/Voxelization.hpp
+++ b/projects/voxelization/src/Voxelization.hpp
@@ -1,5 +1,8 @@
 #pragma once
+
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Image.hpp>
 #include <glm/glm.hpp>
 #include <vkcv/camera/Camera.hpp>
 #include <vkcv/Downsampler.hpp>
@@ -22,7 +25,7 @@ public:
 
 	void voxelizeMeshes(
 		vkcv::CommandStreamHandle                       cmdStream,
-		const std::vector<vkcv::Mesh>&                  meshes,
+		const std::vector<vkcv::VertexData>&            meshes,
 		const std::vector<glm::mat4>&                   modelMatrices,
 		const std::vector<vkcv::DescriptorSetHandle>&   perMeshDescriptorSets,
 		const vkcv::WindowHandle&                       windowHandle,
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 2868a4cc8901a346e5ee723444ace1ec83ae0b25..da2be34b3e174467392817efb6a4d4d0437b2257 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -1,5 +1,7 @@
 #include <iostream>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Sampler.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
@@ -135,25 +137,17 @@ int main(int argc, const char** argv) {
 	std::vector<std::vector<uint8_t>> vBuffers;
 	std::vector<std::vector<uint8_t>> iBuffers;
 
-	std::vector<vkcv::VertexBufferBinding> vBufferBindings;
 	std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings;
-	std::vector<vkcv::asset::VertexAttribute> vAttributes;
 
 	for (size_t i = 0; i < scene.vertexGroups.size(); i++) {
-
 		vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data);
 		iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data);
-
-		auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes;
-
-		std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-			return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-		});
 	}
 
 	std::vector<vkcv::Buffer<uint8_t>> vertexBuffers;
 	for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) {
-		vertexBuffers.push_back(core.createBuffer<uint8_t>(
+		vertexBuffers.push_back(buffer<uint8_t>(
+			core,
 			vkcv::BufferType::VERTEX,
 			group.vertexBuffer.data.size()));
 		vertexBuffers.back().fill(group.vertexBuffer.data);
@@ -161,35 +155,38 @@ int main(int argc, const char** argv) {
 
 	std::vector<vkcv::Buffer<uint8_t>> indexBuffers;
 	for (const auto& dataBuffer : iBuffers) {
-		indexBuffers.push_back(core.createBuffer<uint8_t>(
+		indexBuffers.push_back(buffer<uint8_t>(
+			core,
 			vkcv::BufferType::INDEX,
 			dataBuffer.size()));
 		indexBuffers.back().fill(dataBuffer);
 	}
 
-	int vertexBufferIndex = 0;
-	for (const auto& vertexGroup : scene.vertexGroups) {
-		for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
-			vAttributes.push_back(attribute);
-			vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle()));
-		}
-		vertexBufferBindings.push_back(vBufferBindings);
-		vBufferBindings.clear();
-		vertexBufferIndex++;
+	for (size_t i = 0; i < scene.vertexGroups.size(); i++) {
+		vertexBufferBindings.push_back(vkcv::asset::loadVertexBufferBindings(
+				scene.vertexGroups[i].vertexBuffer.attributes,
+				vertexBuffers[i].getHandle(),
+				{
+						vkcv::asset::PrimitiveType::POSITION,
+						vkcv::asset::PrimitiveType::NORMAL,
+						vkcv::asset::PrimitiveType::TEXCOORD_0,
+						vkcv::asset::PrimitiveType::TANGENT
+				}
+		));
 	}
 
 	const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32;
-	const vkcv::AttachmentDescription color_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		colorBufferFormat
+	const vkcv::AttachmentDescription color_attachment (
+			colorBufferFormat,
+			vkcv::AttachmentOperation::CLEAR,
+			vkcv::AttachmentOperation::STORE
 	);
 	
 	const vk::Format depthBufferFormat = vk::Format::eD32Sfloat;
-	const vkcv::AttachmentDescription depth_attachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		depthBufferFormat
+	const vkcv::AttachmentDescription depth_attachment (
+			depthBufferFormat,
+			vkcv::AttachmentOperation::LOAD,
+			vkcv::AttachmentOperation::STORE
 	);
 	
 	// forward shading config
@@ -237,22 +234,11 @@ int main(int argc, const char** argv) {
 		prepassVertexBindings.push_back(vkcv::createVertexBinding(i, { prepassVertexAttachments[i] }));
 	}
 	const vkcv::VertexLayout prepassVertexLayout { prepassVertexBindings };
-
-	const vkcv::AttachmentDescription prepassAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::CLEAR,
-		depthBufferFormat);
-
-	vkcv::PassConfig prepassPassDefinition({ prepassAttachment }, msaa);
-	vkcv::PassHandle prepassPass = core.createPass(prepassPassDefinition);
+	
+	vkcv::PassHandle prepassPass = vkcv::passFormat(core, depthBufferFormat, true, msaa);
 
 	// create descriptor sets
-	vkcv::SamplerHandle colorSampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::REPEAT
-	);
+	vkcv::SamplerHandle colorSampler = vkcv::samplerLinear(core);
 
 	std::vector<vkcv::DescriptorSetLayoutHandle> materialDescriptorSetLayouts;
 	std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets;
@@ -288,19 +274,19 @@ int main(int argc, const char** argv) {
 		vkcv::asset::Texture& specularTexture   = scene.textures[specularIndex];
 
 		// albedo texture
-		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true));
+		sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true));
 		sceneImages.back().fill(albedoTexture.data.data());
 		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle albedoHandle = sceneImages.back().getHandle();
 
 		// normal texture
-		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true, true));
+		sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true, true));
 		sceneImages.back().fill(normalTexture.data.data());
 		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle normalHandle = sceneImages.back().getHandle();
 
 		// specular texture
-		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true, true));
+		sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true, true));
 		sceneImages.back().fill(specularTexture.data.data());
 		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle specularHandle = sceneImages.back().getHandle();
@@ -331,38 +317,32 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetLayoutHandle prepassDescriptorSetLayout = core.createDescriptorSetLayout({});
 	vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(prepassDescriptorSetLayout);
 
-	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
-	vkcv::GraphicsPipelineConfig prepassPipelineConfig{
+	vkcv::GraphicsPipelineConfig prepassPipelineConfig (
 		depthPrepassShader,
-		swapchainExtent.width,
-		swapchainExtent.height,
 		prepassPass,
 		vertexLayout,
-		{ prepassDescriptorSetLayout, perMeshDescriptorSetLayouts[0] },
-		true
-	};
-	prepassPipelineConfig.m_culling         = vkcv::CullMode::Back;
-	prepassPipelineConfig.m_multisampling   = msaa;
-	prepassPipelineConfig.m_depthTest       = vkcv::DepthTest::LessEqual;
-	prepassPipelineConfig.m_alphaToCoverage = true;
+		{ prepassDescriptorSetLayout, perMeshDescriptorSetLayouts[0] }
+	);
+	
+	prepassPipelineConfig.setCulling(vkcv::CullMode::Back);
+	prepassPipelineConfig.setDepthTest(vkcv::DepthTest::LessEqual);
+	prepassPipelineConfig.setWritingAlphaToCoverage(true);
 
 	vkcv::GraphicsPipelineHandle prepassPipeline = core.createGraphicsPipeline(prepassPipelineConfig);
 
 	// forward pipeline
-	vkcv::GraphicsPipelineConfig forwardPipelineConfig {
+	vkcv::GraphicsPipelineConfig forwardPipelineConfig (
 		forwardProgram,
-		swapchainExtent.width,
-		swapchainExtent.height,
 		forwardPass,
 		vertexLayout,
-		{ forwardShadingDescriptorSetLayout, perMeshDescriptorSetLayouts[0] },
-		true
-	};
-    forwardPipelineConfig.m_culling         = vkcv::CullMode::Back;
-	forwardPipelineConfig.m_multisampling   = msaa;
-	forwardPipelineConfig.m_depthTest       = vkcv::DepthTest::Equal;
-	forwardPipelineConfig.m_depthWrite      = false;
+		{ forwardShadingDescriptorSetLayout, perMeshDescriptorSetLayouts[0] }
+	);
+	
+	forwardPipelineConfig.setCulling(vkcv::CullMode::Back);
+	forwardPipelineConfig.setDepthTest(vkcv::DepthTest::Equal);
+	forwardPipelineConfig.setWritingDepth(false);
 	
 	vkcv::GraphicsPipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig);
 	
@@ -379,19 +359,13 @@ int main(int argc, const char** argv) {
 	SkySettings skySettings;
 	skySettings.color       = glm::vec3(0.15, 0.65, 1);
 	skySettings.strength    = 5;
-
-	const vkcv::AttachmentDescription skyColorAttachment(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		colorBufferFormat);
-
-	const vkcv::AttachmentDescription skyDepthAttachments(
-		vkcv::AttachmentOperation::STORE,
-		vkcv::AttachmentOperation::LOAD,
-		depthBufferFormat);
-
-	vkcv::PassConfig skyPassConfig({ skyColorAttachment, skyDepthAttachments }, msaa);
-	vkcv::PassHandle skyPass = core.createPass(skyPassConfig);
+	
+	vkcv::PassHandle skyPass = vkcv::passFormats(
+			core,
+			{ colorBufferFormat, depthBufferFormat },
+			false,
+			msaa
+	);
 
 	vkcv::ShaderProgram skyShader;
 	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/sky.vert"),
@@ -403,34 +377,32 @@ int main(int argc, const char** argv) {
 		skyShader.addShader(shaderStage, path);
 	});
 
-	vkcv::GraphicsPipelineConfig skyPipeConfig;
-	skyPipeConfig.m_ShaderProgram       = skyShader;
-	skyPipeConfig.m_Width               = swapchainExtent.width;
-	skyPipeConfig.m_Height              = swapchainExtent.height;
-	skyPipeConfig.m_PassHandle          = skyPass;
-	skyPipeConfig.m_VertexLayout        = vkcv::VertexLayout();
-	skyPipeConfig.m_DescriptorLayouts   = {};
-	skyPipeConfig.m_UseDynamicViewport  = true;
-	skyPipeConfig.m_multisampling       = msaa;
-	skyPipeConfig.m_depthWrite          = false;
+	vkcv::GraphicsPipelineConfig skyPipeConfig (
+			skyShader,
+			skyPass,
+			{},
+			{}
+	);
+	
+	skyPipeConfig.setWritingDepth(false);
 
 	vkcv::GraphicsPipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig);
 
 	// render targets
-	vkcv::ImageHandle depthBuffer           = core.createImage(
+	vkcv::ImageHandle depthBuffer = core.createImage(
 			depthBufferFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, false, false, msaa
-	).getHandle();
+	);
 
-    const bool colorBufferRequiresStorage   = !usingMsaa;
-	vkcv::ImageHandle colorBuffer           = core.createImage(
+    const bool colorBufferRequiresStorage = !usingMsaa;
+	vkcv::ImageHandle colorBuffer = core.createImage(
 			colorBufferFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, colorBufferRequiresStorage, true, msaa
-	).getHandle();
+	);
 
 	vkcv::ImageHandle resolvedColorBuffer;
 	if (usingMsaa) {
@@ -439,7 +411,7 @@ int main(int argc, const char** argv) {
 				swapchainExtent.width,
 				swapchainExtent.height,
 				1, false, true, true
-		).getHandle();
+		);
 	}
 	else {
 		resolvedColorBuffer = colorBuffer;
@@ -450,14 +422,14 @@ int main(int argc, const char** argv) {
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, true
-	).getHandle();
+	);
 	
 	vkcv::ImageHandle swapBuffer2 = core.createImage(
 			colorBufferFormat,
 			swapchainExtent.width,
 			swapchainExtent.height,
 			1, false, true
-	).getHandle();
+	);
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
@@ -520,12 +492,7 @@ int main(int argc, const char** argv) {
 		{ resolveDescriptorSetLayout }
 	});
 
-	vkcv::SamplerHandle resolveSampler = core.createSampler(
-		vkcv::SamplerFilterType::NEAREST,
-		vkcv::SamplerFilterType::NEAREST,
-		vkcv::SamplerMipmapMode::NEAREST,
-		vkcv::SamplerAddressMode::CLAMP_TO_EDGE
-	);
+	vkcv::SamplerHandle resolveSampler = vkcv::samplerNearest(core, true);
 
 	// model matrices per mesh
 	std::vector<glm::mat4> modelMatrices;
@@ -538,30 +505,30 @@ int main(int argc, const char** argv) {
 	}
 
 	// prepare meshes
-	std::vector<vkcv::Mesh> meshes;
+	std::vector<vkcv::VertexData> meshes;
 	for (size_t i = 0; i < scene.vertexGroups.size(); i++) {
-		vkcv::Mesh mesh(vertexBufferBindings[i], indexBuffers[i].getVulkanHandle(), scene.vertexGroups[i].numIndices);
+		vkcv::VertexData mesh (vertexBufferBindings[i]);
+		mesh.setIndexBuffer(indexBuffers[i].getHandle());
+		mesh.setCount(scene.vertexGroups[i].numIndices);
 		meshes.push_back(mesh);
 	}
 
-	std::vector<vkcv::DrawcallInfo> drawcalls;
-	std::vector<vkcv::DrawcallInfo> prepassDrawcalls;
+	std::vector<vkcv::InstanceDrawcall> drawcalls;
+	std::vector<vkcv::InstanceDrawcall> prepassDrawcalls;
 	for (size_t i = 0; i < meshes.size(); i++) {
-
-		drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { 
-			vkcv::DescriptorSetUsage(0, forwardShadingDescriptorSet),
-			vkcv::DescriptorSetUsage(1, perMeshDescriptorSets[i]) }));
-		prepassDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {
-			vkcv::DescriptorSetUsage(0, prepassDescriptorSet),
-			vkcv::DescriptorSetUsage(1, perMeshDescriptorSets[i]) }));
+		vkcv::InstanceDrawcall drawcall (meshes[i]);
+		drawcall.useDescriptorSet(0, forwardShadingDescriptorSet);
+		drawcall.useDescriptorSet(1, perMeshDescriptorSets[i]);
+		
+		vkcv::InstanceDrawcall prepassDrawcall (meshes[i]);
+		prepassDrawcall.useDescriptorSet(0, prepassDescriptorSet);
+		prepassDrawcall.useDescriptorSet(1, perMeshDescriptorSets[i]);
+		
+		drawcalls.push_back(drawcall);
+		prepassDrawcalls.push_back(prepassDrawcall);
 	}
 
-	vkcv::SamplerHandle voxelSampler = core.createSampler(
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerFilterType::LINEAR,
-		vkcv::SamplerMipmapMode::LINEAR,
-		vkcv::SamplerAddressMode::CLAMP_TO_EDGE
-	);
+	vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true);
 
 	ShadowMapping shadowMapping(&core, vertexLayout);
 
@@ -579,7 +546,7 @@ int main(int argc, const char** argv) {
 		msaa);
 
 	vkcv::effects::BloomAndFlaresEffect bloomFlares (core, true);
-	vkcv::Buffer<glm::vec3> cameraPosBuffer = core.createBuffer<glm::vec3>(vkcv::BufferType::UNIFORM, 1);
+	vkcv::Buffer<glm::vec3> cameraPosBuffer = buffer<glm::vec3>(core, vkcv::BufferType::UNIFORM, 1);
 
 	struct VolumetricSettings {
 		glm::vec3   scatteringCoefficient;
@@ -587,7 +554,7 @@ int main(int argc, const char** argv) {
 		glm::vec3   absorptionCoefficient;
 	};
 	vkcv::Buffer<VolumetricSettings> volumetricSettingsBuffer
-		= core.createBuffer<VolumetricSettings>(vkcv::BufferType::UNIFORM ,1);
+		= buffer<VolumetricSettings>(core, vkcv::BufferType::UNIFORM ,1);
 
 	// write forward pass descriptor set
 	vkcv::DescriptorWrites forwardDescriptorWrites;
@@ -660,17 +627,9 @@ int main(int argc, const char** argv) {
 	glm::vec3   absorptionColor     = glm::vec3(1);
 	float       absorptionDensity   = 0.005;
 	float       volumetricAmbient   = 0.2;
-
-	auto start = std::chrono::system_clock::now();
-	const auto appStartTime = start;
-	while (vkcv::Window::hasOpenWindow()) {
-		vkcv::Window::pollEvents();
-
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-		
+	
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		uint32_t width, height;
 		vkcv::upscaling::getFSRResolution(
 				fsrMode,
@@ -702,21 +661,21 @@ int main(int argc, const char** argv) {
 					fsrWidth, fsrHeight, 1,
 					false, false, false,
 					msaa
-			).getHandle();
+			);
 			
 			colorBuffer = core.createImage(
 					colorBufferFormat,
 					fsrWidth, fsrHeight, 1,
 					false, colorBufferRequiresStorage, true,
 					msaa
-			).getHandle();
+			);
 
 			if (usingMsaa) {
 				resolvedColorBuffer = core.createImage(
 						colorBufferFormat,
 						fsrWidth, fsrHeight, 1,
 						false, true, true
-				).getHandle();
+				);
 			} else {
 				resolvedColorBuffer = colorBuffer;
 			}
@@ -725,18 +684,15 @@ int main(int argc, const char** argv) {
 					colorBufferFormat,
 					fsrWidth, fsrHeight, 1,
 					false, true
-			).getHandle();
+			);
 			
 			swapBuffer2 = core.createImage(
 					colorBufferFormat,
 					swapchainWidth, swapchainHeight, 1,
 					false, true
-			).getHandle();
+			);
 		}
 
-		auto end = std::chrono::system_clock::now();
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
-
 		// update descriptor sets which use swapchain image
 		vkcv::DescriptorWrites tonemappingDescriptorWrites;
 		tonemappingDescriptorWrites.writeSampledImage(0, resolvedColorBuffer);
@@ -760,8 +716,7 @@ int main(int argc, const char** argv) {
 		resolveDescriptorWrites.writeStorageImage(2, resolvedColorBuffer);
 		core.writeDescriptorSet(resolveDescriptorSet, resolveDescriptorWrites);
 
-		start = end;
-		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraManager.update(dt);
 		cameraPosBuffer.fill({ cameraManager.getActiveCamera().getPosition() });
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
@@ -799,25 +754,24 @@ int main(int argc, const char** argv) {
 		// depth prepass
 		const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP();
 		
-		vkcv::PushConstants prepassPushConstants (sizeof(glm::mat4));
+		vkcv::PushConstants prepassPushConstants = vkcv::pushConstants<glm::mat4>();
 		
 		std::vector<glm::mat4> prepassMatrices;
 		for (const auto& m : modelMatrices) {
 			prepassPushConstants.appendDrawcall(viewProjectionCamera * m);
 		}
-
 		
 		const std::vector<vkcv::ImageHandle>    prepassRenderTargets = { depthBuffer };
 
 		core.recordBeginDebugLabel(cmdStream, "Depth prepass", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			prepassPass,
 			prepassPipeline,
 			prepassPushConstants,
 			prepassDrawcalls,
 			prepassRenderTargets,
-			windowHandle);
+			windowHandle
+		);
 
 		core.recordImageMemoryBarrier(cmdStream, depthBuffer);
 		core.recordEndDebugLabel(cmdStream);
@@ -840,46 +794,41 @@ int main(int argc, const char** argv) {
 		core.recordBeginDebugLabel(cmdStream, "Forward rendering", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			forwardPass,
 			forwardPipeline,
 			pushConstants,
 			drawcalls,
 			renderTargets,
-			windowHandle);
+			windowHandle
+		);
 		core.recordEndDebugLabel(cmdStream);
 
 		if (renderVoxelVis) {
 			voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip, windowHandle);
 		}
 		
-		vkcv::PushConstants skySettingsPushConstants (sizeof(skySettings));
+		vkcv::PushConstants skySettingsPushConstants = vkcv::pushConstants<SkySettings>();
 		skySettingsPushConstants.appendDrawcall(skySettings);
+		
+		vkcv::VertexData skyData;
+		skyData.setCount(3);
 
 		// sky
 		core.recordBeginDebugLabel(cmdStream, "Sky", { 1, 1, 1, 1 });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			skyPass,
 			skyPipe,
 			skySettingsPushConstants,
-			{ vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) },
+			{ vkcv::InstanceDrawcall(skyData) },
 			renderTargets,
-			windowHandle);
-		core.recordEndDebugLabel(cmdStream);
-
-		const uint32_t fullscreenLocalGroupSize = 8;
-		
-		uint32_t fulsscreenDispatchCount [3];
-		
-		fulsscreenDispatchCount[0] = static_cast<uint32_t>(
-				glm::ceil(fsrWidth  / static_cast<float>(fullscreenLocalGroupSize))
+			windowHandle
 		);
+		core.recordEndDebugLabel(cmdStream);
 		
-		fulsscreenDispatchCount[1] = static_cast<uint32_t>(
-				glm::ceil(fsrHeight / static_cast<float>(fullscreenLocalGroupSize))
+		const uint32_t fullscreenLocalGroupSize = 8;
+		auto fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(fsrWidth, fsrHeight),
+				vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize)
 		);
-		
-		fulsscreenDispatchCount[2] = 1;
 
 		if (usingMsaa) {
 			core.recordBeginDebugLabel(cmdStream, "MSAA resolve", { 1, 1, 1, 1 });
@@ -889,11 +838,12 @@ int main(int argc, const char** argv) {
 
 				assert(msaa == vkcv::Multisampling::MSAA4X);	// shaders is written for msaa 4x
 				core.recordComputeDispatchToCmdStream(
-					cmdStream,
-					resolvePipeline,
-					fulsscreenDispatchCount,
-					{ vkcv::DescriptorSetUsage(0, resolveDescriptorSet) },
-					vkcv::PushConstants(0));
+						cmdStream,
+						resolvePipeline,
+						fullscreenDispatchCount,
+						{ vkcv::useDescriptorSet(0, resolveDescriptorSet) },
+						vkcv::PushConstants(0)
+				);
 
 				core.recordImageMemoryBarrier(cmdStream, resolvedColorBuffer);
 			}
@@ -911,11 +861,11 @@ int main(int argc, const char** argv) {
 		
 		core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 1, 1, 1, 1 });
 		core.recordComputeDispatchToCmdStream(
-			cmdStream, 
-			tonemappingPipeline, 
-			fulsscreenDispatchCount,
-			{ vkcv::DescriptorSetUsage(0, tonemappingDescriptorSet) },
-			vkcv::PushConstants(0)
+				cmdStream,
+				tonemappingPipeline,
+				fullscreenDispatchCount,
+				{ vkcv::useDescriptorSet(0, tonemappingDescriptorSet) },
+				vkcv::PushConstants(0)
 		);
 		
 		core.prepareImageForStorage(cmdStream, swapBuffer2);
@@ -939,26 +889,20 @@ int main(int argc, const char** argv) {
 		core.prepareImageForStorage(cmdStream, swapchainInput);
 		core.prepareImageForSampling(cmdStream, swapBuffer2);
 		
-		auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime);
-		float timeF         = static_cast<float>(timeSinceStart.count()) * 0.01f;
-		
-		vkcv::PushConstants timePushConstants (sizeof(timeF));
-		timePushConstants.appendDrawcall(timeF);
-		
-		fulsscreenDispatchCount[0] = static_cast<uint32_t>(
-				glm::ceil(swapchainWidth  / static_cast<float>(fullscreenLocalGroupSize))
-		);
+		vkcv::PushConstants timePushConstants = vkcv::pushConstants<float>();
+		timePushConstants.appendDrawcall(static_cast<float>(t * 100000.0));
 		
-		fulsscreenDispatchCount[1] = static_cast<uint32_t>(
-				glm::ceil(swapchainHeight / static_cast<float>(fullscreenLocalGroupSize))
+		fullscreenDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize)
 		);
 		
 		core.recordBeginDebugLabel(cmdStream, "Post Processing", { 1, 1, 1, 1 });
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				postEffectsPipeline,
-				fulsscreenDispatchCount,
-				{ vkcv::DescriptorSetUsage(0, postEffectsDescriptorSet) },
+				fullscreenDispatchCount,
+				{ vkcv::useDescriptorSet(0, postEffectsDescriptorSet) },
 				timePushConstants
 		);
 		core.recordEndDebugLabel(cmdStream);
@@ -1021,7 +965,8 @@ int main(int argc, const char** argv) {
 					[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 					newForwardProgram.addShader(shaderStage, path);
 				});
-				forwardPipelineConfig.m_ShaderProgram = newForwardProgram;
+				
+				forwardPipelineConfig.setShaderProgram(newForwardProgram);
 				vkcv::GraphicsPipelineHandle newPipeline = core.createGraphicsPipeline(forwardPipelineConfig);
 
 				if (newPipeline) {
@@ -1049,9 +994,7 @@ int main(int argc, const char** argv) {
 		}
 
 		gui.endGUI();
-
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	return 0;
 }
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index 7c865fac55cc9c4a7185006b3b74b205f9ac9105..e4f02691f9a7d97c355b276c389296e486221f4a 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -1,5 +1,8 @@
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
+#include <vkcv/Pass.hpp>
+#include <vkcv/Image.hpp>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/gui/GUI.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -238,7 +241,8 @@ vkcv::ComputePipelineHandle createComputePipeline(vkcv::Core& core, vkcv::shader
 
 vkcv::BufferHandle resetParticles(vkcv::Core& core, size_t count, const glm::vec3& velocity,
 					float density, float size, int form, int mode) {
-	vkcv::Buffer<Particle> particles = core.createBuffer<Particle>(
+	vkcv::Buffer<Particle> particles = vkcv::buffer<Particle>(
+			core,
 			vkcv::BufferType::STORAGE,
 			count
 	);
@@ -301,15 +305,16 @@ int main(int argc, const char **argv) {
 	cameraManager.getCamera(trackballIdx).setCenter(glm::vec3(0.5f, 0.5f, 0.5f));   // set camera to look at the center of the particle volume
 	cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	
-	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain());
 	
 	vkcv::ImageHandle depthBuffer = core.createImage(
 			vk::Format::eD32Sfloat,
 			swapchainExtent.width,
 			swapchainExtent.height
-	).getHandle();
+	);
 	
-	vkcv::Image grid = core.createImage(
+	vkcv::Image grid = vkcv::image(
+			core,
 			vk::Format::eR16G16B16A16Sfloat,
 			32,
 			32,
@@ -327,8 +332,8 @@ int main(int argc, const char **argv) {
 			vkcv::SamplerBorderColor::FLOAT_ZERO_TRANSPARENT
 	);
 	
-	vkcv::Buffer<Simulation> simulation = core.createBuffer<Simulation>(
-			vkcv::BufferType::UNIFORM, 1, vkcv::BufferMemoryType::HOST_VISIBLE
+	vkcv::Buffer<Simulation> simulation = vkcv::buffer<Simulation>(
+			core, vkcv::BufferType::UNIFORM, 1, vkcv::BufferMemoryType::HOST_VISIBLE
 	);
 	
 	Simulation* sim = simulation.map();
@@ -452,45 +457,6 @@ int main(int argc, const char **argv) {
 			{ vkcv::ShaderStage::FRAGMENT, "shaders/lines.frag" }
 	}, nullptr);
 	
-	vkcv::PassConfig passConfigGrid {{
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::CLEAR,
-				core.getSwapchain(windowHandle).getFormat()
-		),
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::CLEAR,
-				vk::Format::eD32Sfloat
-		)
-	}, vkcv::Multisampling::None };
-	
-	vkcv::PassConfig passConfigParticles {{
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::CLEAR,
-				core.getSwapchain(windowHandle).getFormat()
-		),
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::CLEAR,
-				vk::Format::eD32Sfloat
-		)
-	}, vkcv::Multisampling::None };
-	
-	vkcv::PassConfig passConfigLines {{
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::LOAD,
-				core.getSwapchain(windowHandle).getFormat()
-		),
-		vkcv::AttachmentDescription(
-				vkcv::AttachmentOperation::STORE,
-				vkcv::AttachmentOperation::LOAD,
-				vk::Format::eD32Sfloat
-		)
-	}, vkcv::Multisampling::None };
-	
 	vkcv::DescriptorSetLayoutHandle gfxSetLayoutGrid = core.createDescriptorSetLayout(
 			gfxProgramGrid.getReflectedDescriptors().at(0)
 	);
@@ -517,73 +483,75 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(gfxSetParticles, writes);
 	}
 	
-	vkcv::PassHandle gfxPassGrid = core.createPass(passConfigGrid);
-	vkcv::PassHandle gfxPassParticles = core.createPass(passConfigParticles);
-	vkcv::PassHandle gfxPassLines = core.createPass(passConfigLines);
+	vkcv::PassHandle gfxPassGrid = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
+	);
+	
+	vkcv::PassHandle gfxPassParticles = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
+	);
+	
+	vkcv::PassHandle gfxPassLines = vkcv::passSwapchain(
+			core,
+			window.getSwapchain(),
+			{ vk::Format::eUndefined, vk::Format::eD32Sfloat },
+			false
+	);
 	
 	vkcv::VertexLayout vertexLayoutGrid ({
 		vkcv::createVertexBinding(0, gfxProgramGrid.getVertexAttachments())
 	});
 	
-	vkcv::GraphicsPipelineConfig gfxPipelineConfigGrid;
-	gfxPipelineConfigGrid.m_ShaderProgram = gfxProgramGrid;
-	gfxPipelineConfigGrid.m_Width = windowWidth;
-	gfxPipelineConfigGrid.m_Height = windowHeight;
-	gfxPipelineConfigGrid.m_PassHandle = gfxPassGrid;
-	gfxPipelineConfigGrid.m_VertexLayout = vertexLayoutGrid;
-	gfxPipelineConfigGrid.m_DescriptorLayouts = { gfxSetLayoutGrid };
-	gfxPipelineConfigGrid.m_UseDynamicViewport = true;
+	vkcv::GraphicsPipelineConfig gfxPipelineConfigGrid (
+			gfxProgramGrid,
+			gfxPassGrid,
+			vertexLayoutGrid,
+			{ gfxSetLayoutGrid }
+	);
 	
 	vkcv::VertexLayout vertexLayoutParticles ({
 		vkcv::createVertexBinding(0, gfxProgramParticles.getVertexAttachments())
 	});
 	
-	vkcv::GraphicsPipelineConfig gfxPipelineConfigParticles;
-	gfxPipelineConfigParticles.m_ShaderProgram = gfxProgramParticles;
-	gfxPipelineConfigParticles.m_Width = windowWidth;
-	gfxPipelineConfigParticles.m_Height = windowHeight;
-	gfxPipelineConfigParticles.m_PassHandle = gfxPassParticles;
-	gfxPipelineConfigParticles.m_VertexLayout = vertexLayoutParticles;
-	gfxPipelineConfigParticles.m_DescriptorLayouts = { gfxSetLayoutParticles };
-	gfxPipelineConfigParticles.m_UseDynamicViewport = true;
+	vkcv::GraphicsPipelineConfig gfxPipelineConfigParticles (
+			gfxProgramParticles,
+			gfxPassParticles,
+			vertexLayoutParticles,
+			{ gfxSetLayoutParticles }
+	);
 	
 	vkcv::VertexLayout vertexLayoutLines ({
 		vkcv::createVertexBinding(0, gfxProgramLines.getVertexAttachments())
 	});
 	
-	vkcv::GraphicsPipelineConfig gfxPipelineConfigLines;
-	gfxPipelineConfigLines.m_ShaderProgram = gfxProgramLines;
-	gfxPipelineConfigLines.m_Width = windowWidth;
-	gfxPipelineConfigLines.m_Height = windowHeight;
-	gfxPipelineConfigLines.m_PassHandle = gfxPassLines;
-	gfxPipelineConfigLines.m_VertexLayout = vertexLayoutLines;
-	gfxPipelineConfigLines.m_DescriptorLayouts = {};
-	gfxPipelineConfigLines.m_UseDynamicViewport = true;
-	gfxPipelineConfigLines.m_PrimitiveTopology = vkcv::PrimitiveTopology::LineList;
+	vkcv::GraphicsPipelineConfig gfxPipelineConfigLines (
+			gfxProgramLines,
+			gfxPassLines,
+			vertexLayoutLines,
+			{}
+	);
+	
+	gfxPipelineConfigLines.setPrimitiveTopology(vkcv::PrimitiveTopology::LineList);
 	
 	vkcv::GraphicsPipelineHandle gfxPipelineGrid = core.createGraphicsPipeline(gfxPipelineConfigGrid);
 	vkcv::GraphicsPipelineHandle gfxPipelineParticles = core.createGraphicsPipeline(gfxPipelineConfigParticles);
 	vkcv::GraphicsPipelineHandle gfxPipelineLines = core.createGraphicsPipeline(gfxPipelineConfigLines);
 	
-	vkcv::Buffer<glm::vec2> trianglePositions = core.createBuffer<glm::vec2>(vkcv::BufferType::VERTEX, 3);
+	vkcv::Buffer<glm::vec2> trianglePositions = vkcv::buffer<glm::vec2>(core, vkcv::BufferType::VERTEX, 3);
 	trianglePositions.fill({
 		glm::vec2(-1.0f, -1.0f),
 		glm::vec2(+0.0f, +1.5f),
 		glm::vec2(+1.0f, -1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> triangleIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3);
-	triangleIndices.fill({
-		0, 1, 2
-	});
-	
-	vkcv::Mesh triangleMesh (
-			{ vkcv::VertexBufferBinding(0, trianglePositions.getVulkanHandle()) },
-			triangleIndices.getVulkanHandle(),
-			triangleIndices.getCount()
-	);
+	vkcv::VertexData triangleData ({ vkcv::vertexBufferBinding(trianglePositions.getHandle()) });
+	triangleData.setCount(trianglePositions.getCount());
 	
-	vkcv::Buffer<glm::vec3> linesPositions = core.createBuffer<glm::vec3>(vkcv::BufferType::VERTEX, 8);
+	vkcv::Buffer<glm::vec3> linesPositions = vkcv::buffer<glm::vec3>(core, vkcv::BufferType::VERTEX, 8);
 	linesPositions.fill({
 		glm::vec3(0.0f, 0.0f, 0.0f),
 		glm::vec3(1.0f, 0.0f, 0.0f),
@@ -595,7 +563,7 @@ int main(int argc, const char **argv) {
 		glm::vec3(1.0f, 1.0f, 1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> linesIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 24);
+	vkcv::Buffer<uint16_t> linesIndices = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 24);
 	linesIndices.fill({
 		0, 1,
 		1, 3,
@@ -613,90 +581,60 @@ int main(int argc, const char **argv) {
 		3, 7
 	});
 	
-	vkcv::Mesh linesMesh (
-			{ vkcv::VertexBufferBinding(0, linesPositions.getVulkanHandle()) },
-			linesIndices.getVulkanHandle(),
-			linesIndices.getCount()
-	);
-	
-	std::vector<vkcv::DrawcallInfo> drawcallsGrid;
+	vkcv::VertexData linesData ({ vkcv::vertexBufferBinding(linesPositions.getHandle()) });
+	linesData.setIndexBuffer(linesIndices.getHandle());
+	linesData.setCount(linesIndices.getCount());
 	
-	drawcallsGrid.push_back(vkcv::DrawcallInfo(
-			triangleMesh,
-			{ vkcv::DescriptorSetUsage(0, gfxSetGrid) },
+	vkcv::InstanceDrawcall drawcallGrid (
+			triangleData,
 			grid.getWidth() * grid.getHeight() * grid.getDepth()
-	));
-	
-	std::vector<vkcv::DrawcallInfo> drawcallsParticles;
+	);
 	
-	drawcallsParticles.push_back(vkcv::DrawcallInfo(
-			triangleMesh,
-			{ vkcv::DescriptorSetUsage(0, gfxSetParticles) },
-			sim->count
-	));
+	drawcallGrid.useDescriptorSet(0, gfxSetGrid);
 	
-	std::vector<vkcv::DrawcallInfo> drawcallsLines;
+	vkcv::InstanceDrawcall drawcallParticle (triangleData, sim->count);
+	drawcallParticle.useDescriptorSet(0, gfxSetParticles);
 	
-	drawcallsLines.push_back(vkcv::DrawcallInfo(
-			linesMesh,
-			{},
-			1
-	));
+	vkcv::InstanceDrawcall drawcallLines (linesData);
 	
 	bool renderGrid = true;
-	
 	float speed_factor = 1.0f;
 	
-	auto start = std::chrono::system_clock::now();
-	auto current = start;
-	
-	while (vkcv::Window::hasOpenWindow()) {
-		vkcv::Window::pollEvents();
-		
-		if (window.getHeight() == 0 || window.getWidth() == 0)
-			continue;
-		
-		uint32_t swapchainWidth, swapchainHeight;
-		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
-			continue;
-		}
-		
+	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
+				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
 		if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) {
 			depthBuffer = core.createImage(
 					vk::Format::eD32Sfloat,
 					swapchainWidth,
 					swapchainHeight
-			).getHandle();
+			);
 			
 			swapchainExtent.width = swapchainWidth;
 			swapchainExtent.height = swapchainHeight;
 		}
 		
-		auto next = std::chrono::system_clock::now();
-		
-		auto time = std::chrono::duration_cast<std::chrono::microseconds>(next - start);
-		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(next - current);
-		
-		current = next;
-		
 		Physics physics;
-		physics.t = static_cast<float>(0.000001 * static_cast<double>(time.count()));
-		physics.dt = static_cast<float>(0.000001 * static_cast<double>(deltatime.count()));
+		physics.t = static_cast<float>(t);
+		physics.dt = static_cast<float>(dt);
 		physics.speedfactor = speed_factor;
 		
-		vkcv::PushConstants physicsPushConstants(sizeof(physics));
+		vkcv::PushConstants physicsPushConstants = vkcv::pushConstants<Physics>();
 		physicsPushConstants.appendDrawcall(physics);
 		
 		cameraManager.update(physics.dt);
 		
 		glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
-		vkcv::PushConstants cameraPushConstants(sizeof(glm::mat4));
+		vkcv::PushConstants cameraPushConstants = vkcv::pushConstants<glm::mat4>();
 		cameraPushConstants.appendDrawcall(mvp);
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		const uint32_t dispatchSizeGrid[3] = {grid.getWidth() / 4, grid.getHeight() / 4, grid.getDepth() / 4};
-		const uint32_t dispatchSizeParticles[3] = {static_cast<uint32_t>(sim->count + 63) / 64, 1, 1};
+		const auto dispatchSizeGrid = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(grid.getWidth(), grid.getHeight(), grid.getDepth()),
+				vkcv::DispatchSize(4, 4, 4)
+		);
+		
+		const auto dispatchSizeParticles = vkcv::dispatchInvocations(sim->count, 64);
 		
 		for (int step = 0; step < 1; step++) {
 			core.recordBeginDebugLabel(cmdStream, "INIT PARTICLE WEIGHTS", {0.78f, 0.89f, 0.94f, 1.0f});
@@ -708,10 +646,10 @@ int main(int argc, const char **argv) {
 					initParticleWeightsPipeline,
 					dispatchSizeParticles,
 					{
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								0, initParticleWeightsSets[0]
 						),
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								1, initParticleWeightsSets[1]
 						)
 					},
@@ -730,13 +668,13 @@ int main(int argc, const char **argv) {
 					transformParticlesToGridPipeline,
 					dispatchSizeGrid,
 					{
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								0, transformParticlesToGridSets[0]
 						),
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								1, transformParticlesToGridSets[1]
 						),
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								2, transformParticlesToGridSets[2]
 						)
 					},
@@ -756,13 +694,13 @@ int main(int argc, const char **argv) {
 					updateParticleVelocitiesPipeline,
 					dispatchSizeParticles,
 					{
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								0, updateParticleVelocitiesSets[0]
 						),
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								1, updateParticleVelocitiesSets[1]
 						),
-						vkcv::DescriptorSetUsage(
+						vkcv::useDescriptorSet(
 								2, updateParticleVelocitiesSets[2]
 						)
 					},
@@ -785,10 +723,9 @@ int main(int argc, const char **argv) {
 			
 			core.recordDrawcallsToCmdStream(
 					cmdStream,
-					gfxPassGrid,
 					gfxPipelineGrid,
 					cameraPushConstants,
-					drawcallsGrid,
+					{ drawcallGrid },
 					renderTargets,
 					windowHandle
 			);
@@ -800,10 +737,9 @@ int main(int argc, const char **argv) {
 			
 			core.recordDrawcallsToCmdStream(
 					cmdStream,
-					gfxPassParticles,
 					gfxPipelineParticles,
 					cameraPushConstants,
-					drawcallsParticles,
+					{ drawcallParticle },
 					renderTargets,
 					windowHandle
 			);
@@ -815,10 +751,9 @@ int main(int argc, const char **argv) {
 		
 		core.recordDrawcallsToCmdStream(
 				cmdStream,
-				gfxPassLines,
 				gfxPipelineLines,
 				cameraPushConstants,
-				drawcallsLines,
+				{ drawcallLines },
 				renderTargets,
 				windowHandle
 		);
@@ -896,9 +831,7 @@ int main(int argc, const char **argv) {
 		
 		ImGui::End();
 		gui.endGUI();
-		
-		core.endFrame(windowHandle);
-	}
+	});
 	
 	simulation.unmap();
 	return 0;
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index e1b1a5097be2bd088d1ec1bd39b78695331899f3..da2a370fac7a6d72c762ef47f8316519264a8457 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -3,41 +3,62 @@
  * @file vkcv/BufferManager.cpp
  */
 
-#include "vkcv/BufferManager.hpp"
+#include "BufferManager.hpp"
 #include "vkcv/Core.hpp"
 #include <vkcv/Logger.hpp>
 
 namespace vkcv {
 	
-	BufferManager::BufferManager() noexcept :
-		m_core(nullptr), m_buffers(), m_stagingBuffer(BufferHandle())
-	{
-	}
-	
-	void BufferManager::init() {
-		if (!m_core) {
-			return;
+	bool BufferManager::init(Core& core) {
+		if (!HandleManager<BufferEntry, BufferHandle>::init(core)) {
+			return false;
 		}
 		
 		m_stagingBuffer = createBuffer(
+				TypeGuard(1),
 				BufferType::STAGING,
-				1024 * 1024,
 				BufferMemoryType::HOST_VISIBLE,
-				false,
+				1024 * 1024,
 				false
 		);
+		
+		return true;
 	}
 	
-	BufferManager::~BufferManager() noexcept {
-		for (uint64_t id = 0; id < m_buffers.size(); id++) {
-			destroyBufferById(id);
+	uint64_t BufferManager::getIdFrom(const BufferHandle &handle) const {
+		return handle.getId();
+	}
+	
+	BufferHandle BufferManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return BufferHandle(id, destroy);
+	}
+	
+	void BufferManager::destroyById(uint64_t id) {
+		auto& buffer = getById(id);
+		
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
+		
+		if (buffer.m_handle) {
+			allocator.destroyBuffer(buffer.m_handle, buffer.m_allocation);
+			
+			buffer.m_handle = nullptr;
+			buffer.m_allocation = nullptr;
 		}
 	}
 	
-	BufferHandle BufferManager::createBuffer(BufferType type,
-											 size_t size,
+	BufferManager::BufferManager() noexcept :
+			HandleManager<BufferEntry, BufferHandle>(),
+			m_stagingBuffer(BufferHandle())
+	{}
+	
+	BufferManager::~BufferManager() noexcept {
+		clear();
+	}
+	
+	BufferHandle BufferManager::createBuffer(const TypeGuard &typeGuard,
+											 BufferType type,
 											 BufferMemoryType memoryType,
-											 bool supportIndirect,
+											 size_t size,
 											 bool readable) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
@@ -60,7 +81,8 @@ namespace vkcv {
 				usageFlags = vk::BufferUsageFlagBits::eIndexBuffer;
 				break;
             case BufferType::INDIRECT:
-                usageFlags = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer ;
+                usageFlags = vk::BufferUsageFlagBits::eStorageBuffer |
+							 vk::BufferUsageFlagBits::eIndirectBuffer ;
                 break;
 			default:
 				vkcv_log(LogLevel::WARNING, "Unknown buffer type");
@@ -71,15 +93,11 @@ namespace vkcv {
 			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
 		}
 		
-		if (supportIndirect) {
-			usageFlags |= vk::BufferUsageFlagBits::eIndirectBuffer;
-		}
-		
 		if (readable) {
 			usageFlags |= vk::BufferUsageFlagBits::eTransferSrc;
 		}
 		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		vk::MemoryPropertyFlags memoryTypeFlags;
 		vma::MemoryUsage memoryUsage;
@@ -123,9 +141,15 @@ namespace vkcv {
 		vk::Buffer buffer = bufferAllocation.first;
 		vma::Allocation allocation = bufferAllocation.second;
 		
-		const uint64_t id = m_buffers.size();
-		m_buffers.push_back({ buffer, allocation, size, mappable });
-		return BufferHandle(id, [&](uint64_t id) { destroyBufferById(id); });
+		return add({
+			typeGuard,
+			type,
+			memoryType,
+			size,
+			buffer,
+			allocation,
+			mappable
+		});
 	}
 	
 	/**
@@ -154,21 +178,20 @@ namespace vkcv {
 	 * @param core Core instance
 	 * @param info Staging-info structure
 	 */
-	static void fillFromStagingBuffer(Core* core, StagingWriteInfo& info) {
+	static void fillFromStagingBuffer(Core& core, StagingWriteInfo& info) {
 		const size_t remaining = info.size - info.stagingPosition;
 		const size_t mapped_size = std::min(remaining, info.stagingLimit);
 		
-		const vma::Allocator& allocator = core->getContext().getAllocator();
+		const vma::Allocator& allocator = core.getContext().getAllocator();
 		
 		void* mapped = allocator.mapMemory(info.stagingAllocation);
 		memcpy(mapped, reinterpret_cast<const char*>(info.data) + info.stagingPosition, mapped_size);
 		allocator.unmapMemory(info.stagingAllocation);
 		
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Transfer;
+		auto stream = core.createCommandStream(QueueType::Transfer);
 		
-		core->recordAndSubmitCommandsImmediate(
-				submitInfo,
+		core.recordCommandsToStream(
+				stream,
 				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
 					const vk::BufferCopy region (
 							0,
@@ -189,6 +212,8 @@ namespace vkcv {
 					}
 				}
 		);
+		
+		core.submitCommandStream(stream, false);
 	}
 	
 	/**
@@ -217,15 +242,14 @@ namespace vkcv {
 	 * @param core Core instance
 	 * @param info Staging-info structure
 	 */
-	static void readToStagingBuffer(Core* core, StagingReadInfo& info) {
+	static void readToStagingBuffer(Core& core, StagingReadInfo& info) {
 		const size_t remaining = info.size - info.stagingPosition;
 		const size_t mapped_size = std::min(remaining, info.stagingLimit);
 		
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Transfer;
+		auto stream = core.createCommandStream(QueueType::Transfer);
 		
-		core->recordAndSubmitCommandsImmediate(
-				submitInfo,
+		core.recordCommandsToStream(
+				stream,
 				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
 					const vk::BufferCopy region (
 							info.offset + info.stagingPosition,
@@ -236,7 +260,7 @@ namespace vkcv {
 					commandBuffer.copyBuffer(info.buffer, info.stagingBuffer, 1, &region);
 				},
 				[&core, &info, &mapped_size, &remaining]() {
-					const vma::Allocator& allocator = core->getContext().getAllocator();
+					const vma::Allocator& allocator = core.getContext().getAllocator();
 					
 					const void* mapped = allocator.mapMemory(info.stagingAllocation);
 					memcpy(reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped, mapped_size);
@@ -252,42 +276,44 @@ namespace vkcv {
 					}
 				}
 		);
+		
+		core.submitCommandStream(stream, false);
 	}
 	
 	vk::Buffer BufferManager::getBuffer(const BufferHandle& handle) const {
-		const uint64_t id = handle.getId();
-		
-		if (id >= m_buffers.size()) {
-			return nullptr;
-		}
-		
-		auto& buffer = m_buffers[id];
+		auto& buffer = (*this)[handle];
 		
 		return buffer.m_handle;
 	}
 	
-	size_t BufferManager::getBufferSize(const BufferHandle &handle) const {
-		const uint64_t id = handle.getId();
+	TypeGuard BufferManager::getTypeGuard(const BufferHandle &handle) const {
+		auto& buffer = (*this)[handle];
 		
-		if (id >= m_buffers.size()) {
-			return 0;
-		}
+		return buffer.m_typeGuard;
+	}
+	
+	BufferType BufferManager::getBufferType(const BufferHandle &handle) const {
+		auto& buffer = (*this)[handle];
+		
+		return buffer.m_type;
+	}
+	
+	BufferMemoryType BufferManager::getBufferMemoryType(const BufferHandle &handle) const {
+		auto& buffer = (*this)[handle];
 		
-		auto& buffer = m_buffers[id];
+		return buffer.m_memoryType;
+	}
+	
+	size_t BufferManager::getBufferSize(const BufferHandle &handle) const {
+		auto& buffer = (*this)[handle];
 		
 		return buffer.m_size;
 	}
 	
 	vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle& handle) const {
-		const uint64_t id = handle.getId();
-		
-		if (id >= m_buffers.size()) {
-			return nullptr;
-		}
+		auto& buffer = (*this)[handle];
 		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		auto info = allocator.getAllocationInfo(
 				buffer.m_allocation
@@ -300,19 +326,13 @@ namespace vkcv {
 								   const void *data,
 								   size_t size,
 								   size_t offset) {
-		const uint64_t id = handle.getId();
+		auto& buffer = (*this)[handle];
 		
 		if (size == 0) {
 			size = SIZE_MAX;
 		}
 		
-		if (id >= m_buffers.size()) {
-			return;
-		}
-		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		if (offset > buffer.m_size) {
 			return;
@@ -325,7 +345,7 @@ namespace vkcv {
 			memcpy(reinterpret_cast<char*>(mapped) + offset, data, max_size);
 			allocator.unmapMemory(buffer.m_allocation);
 		} else {
-			auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ];
+			auto& stagingBuffer = (*this)[ m_stagingBuffer ];
 			
 			StagingWriteInfo info;
 			info.data = data;
@@ -339,7 +359,7 @@ namespace vkcv {
 			info.stagingLimit = stagingBuffer.m_size;
 			info.stagingPosition = 0;
 			
-			fillFromStagingBuffer(m_core, info);
+			fillFromStagingBuffer(getCore(), info);
 		}
 	}
 	
@@ -347,19 +367,13 @@ namespace vkcv {
 								   void *data,
 								   size_t size,
 								   size_t offset) {
-		const uint64_t id = handle.getId();
+		auto& buffer = (*this)[handle];
 		
 		if (size == 0) {
 			size = SIZE_MAX;
 		}
 		
-		if (id >= m_buffers.size()) {
-			return;
-		}
-		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		if (offset > buffer.m_size) {
 			return;
@@ -372,7 +386,7 @@ namespace vkcv {
 			memcpy(data, reinterpret_cast<const char*>(mapped) + offset, max_size);
 			allocator.unmapMemory(buffer.m_allocation);
 		} else {
-			auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ];
+			auto& stagingBuffer = (*this)[ m_stagingBuffer ];
 			
 			StagingReadInfo info;
 			info.data = data;
@@ -386,24 +400,18 @@ namespace vkcv {
 			info.stagingLimit = stagingBuffer.m_size;
 			info.stagingPosition = 0;
 			
-			readToStagingBuffer(m_core, info);
+			readToStagingBuffer(getCore(), info);
 		}
 	}
 	
 	void* BufferManager::mapBuffer(const BufferHandle& handle, size_t offset, size_t size) {
-		const uint64_t id = handle.getId();
+		auto& buffer = (*this)[handle];
 		
 		if (size == 0) {
 			size = SIZE_MAX;
 		}
 		
-		if (id >= m_buffers.size()) {
-			return nullptr;
-		}
-		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		if (offset > buffer.m_size) {
 			return nullptr;
@@ -413,46 +421,15 @@ namespace vkcv {
 	}
 	
 	void BufferManager::unmapBuffer(const BufferHandle& handle) {
-		const uint64_t id = handle.getId();
+		auto& buffer = (*this)[handle];
 		
-		if (id >= m_buffers.size()) {
-			return;
-		}
-		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		allocator.unmapMemory(buffer.m_allocation);
 	}
-	
-	void BufferManager::destroyBufferById(uint64_t id) {
-		if (id >= m_buffers.size()) {
-			return;
-		}
-		
-		auto& buffer = m_buffers[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
-		
-		if (buffer.m_handle) {
-			allocator.destroyBuffer(buffer.m_handle, buffer.m_allocation);
-			
-			buffer.m_handle = nullptr;
-			buffer.m_allocation = nullptr;
-		}
-	}
 
 	void BufferManager ::recordBufferMemoryBarrier(const BufferHandle& handle, vk::CommandBuffer cmdBuffer) {
-
-		const uint64_t id = handle.getId();
-
-		if (id >= m_buffers.size()) {
-			vkcv_log(vkcv::LogLevel::ERROR, "Invalid buffer handle");
-			return;
-		}
-
-		auto& buffer = m_buffers[id];
+		auto& buffer = (*this)[handle];
 		
 		vk::BufferMemoryBarrier memoryBarrier(
 			vk::AccessFlagBits::eMemoryWrite, 
diff --git a/include/vkcv/BufferManager.hpp b/src/vkcv/BufferManager.hpp
similarity index 69%
rename from include/vkcv/BufferManager.hpp
rename to src/vkcv/BufferManager.hpp
index 8da11cfea885471f490836bf24c8b5169d1ce401..e86438126fd6775c241fbcfb7a0e493d0885c775 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/src/vkcv/BufferManager.hpp
@@ -9,57 +9,42 @@
 #include <vulkan/vulkan.hpp>
 #include <vk_mem_alloc.hpp>
 
-#include "Handles.hpp"
+#include "vkcv/BufferTypes.hpp"
+#include "vkcv/TypeGuard.hpp"
 
-namespace vkcv
-{
-	
-	/**
-	 * @brief Enum class to specify types of buffers.
-	 */
-	enum class BufferType {
-		INDEX,
-		VERTEX,
-		UNIFORM,
-		STORAGE,
-		STAGING,
-		INDIRECT
-	};
+#include "HandleManager.hpp"
+
+namespace vkcv {
 	
-	/**
-	 * @brief Enum class to specify types of buffer memory.
-	 */
-	enum class BufferMemoryType {
-		DEVICE_LOCAL,
-		HOST_VISIBLE
+	struct BufferEntry {
+		TypeGuard m_typeGuard;
+		
+		BufferType m_type;
+		BufferMemoryType m_memoryType;
+		size_t m_size;
+		
+		vk::Buffer m_handle;
+		vma::Allocation m_allocation;
+		
+		bool m_mappable;
 	};
 	
-	class Core;
-	
 	/**
 	 * @brief Class to manage the creation, destruction, allocation
 	 * and filling of buffers.
 	 */
-	class BufferManager
-	{
+	class BufferManager : public HandleManager<BufferEntry, BufferHandle> {
 		friend class Core;
 	private:
-		
-		struct Buffer
-		{
-			vk::Buffer m_handle;
-			vma::Allocation m_allocation;
-			size_t m_size = 0;
-			bool m_mappable = false;
-		};
-		
-		Core* m_core;
-		std::vector<Buffer> m_buffers;
 		BufferHandle m_stagingBuffer;
 		
-		BufferManager() noexcept;
+		bool init(Core& core) override;
+		
+		[[nodiscard]]
+		uint64_t getIdFrom(const BufferHandle& handle) const override;
 		
-		void init();
+		[[nodiscard]]
+		BufferHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
 		
 		/**
 		 * Destroys and deallocates buffer represented by a given
@@ -67,32 +52,30 @@ namespace vkcv
 		 *
 		 * @param id Buffer handle id
 		 */
-		void destroyBufferById(uint64_t id);
+		void destroyById(uint64_t id) override;
 		
 	public:
-		~BufferManager() noexcept;
-		
-		BufferManager(BufferManager&& other) = delete;
-		BufferManager(const BufferManager& other) = delete;
+		BufferManager() noexcept;
 		
-		BufferManager& operator=(BufferManager&& other) = delete;
-		BufferManager& operator=(const BufferManager& other) = delete;
+		~BufferManager() noexcept override;
 		
 		/**
 		 * @brief Creates and allocates a new buffer and returns its
 		 * unique buffer handle.
 		 *
+		 * @param[in] typeGuard Type guard
 		 * @param[in] type Type of buffer
-		 * @param[in] size Size of buffer in bytes
 		 * @param[in] memoryType Type of buffers memory
+		 * @param[in] size Size of buffer in bytes
 		 * @param[in] supportIndirect Support of indirect usage
 		 * @param[in] readable Support read functionality
 		 * @return New buffer handle
 		 */
-		BufferHandle createBuffer(BufferType type,
-								  size_t size,
+		[[nodiscard]]
+		BufferHandle createBuffer(const TypeGuard &typeGuard,
+								  BufferType type,
 								  BufferMemoryType memoryType,
-								  bool supportIndirect,
+								  size_t size,
 								  bool readable);
 		
 		/**
@@ -105,6 +88,36 @@ namespace vkcv
 		[[nodiscard]]
 		vk::Buffer getBuffer(const BufferHandle& handle) const;
 		
+		/**
+		 * @brief Returns the type guard of a buffer represented
+		 * by a given buffer handle.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @return Type guard
+		 */
+		[[nodiscard]]
+		TypeGuard getTypeGuard(const BufferHandle& handle) const;
+		
+		/**
+		 * @brief Returns the buffer type of a buffer represented
+		 * by a given buffer handle.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @return Buffer type
+		 */
+		[[nodiscard]]
+		BufferType getBufferType(const BufferHandle& handle) const;
+		
+		/**
+		 * @brief Returns the buffer memory type of a buffer
+		 * represented by a given buffer handle.
+		 *
+		 * @param[in] handle Buffer handle
+		 * @return Buffer memory type
+		 */
+		[[nodiscard]]
+		BufferMemoryType getBufferMemoryType(const BufferHandle& handle) const;
+		
 		/**
 		 * @brief Returns the size of a buffer represented
 		 * by a given buffer handle.
diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp
deleted file mode 100644
index d5c97946aa97ba897710dda412b6f71bd2ed54c8..0000000000000000000000000000000000000000
--- a/src/vkcv/CommandResources.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "vkcv/CommandResources.hpp"
-#include <iostream>
-
-#include "vkcv/Logger.hpp"
-
-namespace vkcv {
-
-	std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) {
-		std::unordered_set<int> indexSet;
-		for (const auto& queue : queueManager.getGraphicsQueues()) {
-			indexSet.insert(queue.familyIndex);
-		}
-		for (const auto& queue : queueManager.getComputeQueues()) {
-			indexSet.insert(queue.familyIndex);
-		}
-		for (const auto& queue : queueManager.getTransferQueues()) {
-			indexSet.insert(queue.familyIndex);
-		}
-		indexSet.insert(queueManager.getPresentQueue().familyIndex);
-		return indexSet;
-	}
-
-	CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int>& familyIndexSet) {
-		CommandResources resources;
-		const size_t queueFamiliesCount = familyIndexSet.size();
-		resources.cmdPoolPerQueueFamily.resize(queueFamiliesCount);
-
-		const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient;
-		for (const int familyIndex : familyIndexSet) {
-			const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex);
-			resources.cmdPoolPerQueueFamily[familyIndex] = device.createCommandPool(poolCreateInfo, nullptr, {});
-		}
-
-		return resources;
-	}
-
-	void destroyCommandResources(const vk::Device& device, const CommandResources& resources) {
-		for (const vk::CommandPool &pool : resources.cmdPoolPerQueueFamily) {
-			device.destroyCommandPool(pool);
-		}
-	}
-
-	vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool &cmdPool) {
-		const vk::CommandBufferAllocateInfo info(cmdPool, vk::CommandBufferLevel::ePrimary, 1);
-		return device.allocateCommandBuffers(info).front();
-	}
-
-	vk::CommandPool chooseCmdPool(const Queue& queue, const CommandResources& cmdResources) {
-		return cmdResources.cmdPoolPerQueueFamily[queue.familyIndex];
-	}
-
-	Queue getQueueForSubmit(QueueType type, const QueueManager& queueManager) {
-		if (type == QueueType::Graphics) {
-			return queueManager.getGraphicsQueues().front();
-		}
-		else if (type == QueueType::Compute) {
-			return queueManager.getComputeQueues().front();
-		}
-		else if (type == QueueType::Transfer) {
-			return queueManager.getTransferQueues().front();
-		}
-		else if (type == QueueType::Present) {
-			return queueManager.getPresentQueue();
-		}
-		else {
-			vkcv_log(LogLevel::ERROR, "Unknown queue type");
-			return queueManager.getGraphicsQueues().front();	// graphics is the most general queue
-		}
-	}
-
-	void beginCommandBuffer(const vk::CommandBuffer &cmdBuffer, vk::CommandBufferUsageFlags flags) {
-		const vk::CommandBufferBeginInfo beginInfo(flags);
-		cmdBuffer.begin(beginInfo);
-	}
-
-	void submitCommandBufferToQueue(
-		vk::Queue							queue,
-		const vk::CommandBuffer				&cmdBuffer,
-		const vk::Fence						&fence,
-		const std::vector<vk::Semaphore>	&waitSemaphores,
-		const std::vector<vk::Semaphore>	&signalSemaphores) {
-
-		const std::vector<vk::PipelineStageFlags> waitDstStageMasks(waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands);
-		const vk::SubmitInfo queueSubmitInfo(waitSemaphores, waitDstStageMasks, cmdBuffer, signalSemaphores);
-		queue.submit(queueSubmitInfo, fence);
-	}
-}
\ No newline at end of file
diff --git a/src/vkcv/CommandStreamManager.cpp b/src/vkcv/CommandStreamManager.cpp
index 872e91c54b1b64616ad5867926e6f656f44ccded..5086d0248f87f91778aa7fcbb6f6a22326666227 100644
--- a/src/vkcv/CommandStreamManager.cpp
+++ b/src/vkcv/CommandStreamManager.cpp
@@ -4,105 +4,95 @@
 #include "vkcv/Logger.hpp"
 
 namespace vkcv {
-	CommandStreamManager::CommandStreamManager() noexcept : m_core(nullptr){}
-
-	CommandStreamManager::~CommandStreamManager() noexcept {
-		for (const auto& stream : m_commandStreams) {
-			if (stream.cmdBuffer && stream.cmdBuffer) {
-				m_core->getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer);
-			}
-		}
+	
+	uint64_t CommandStreamManager::getIdFrom(const CommandStreamHandle &handle) const {
+		return handle.getId();
 	}
-
-	void CommandStreamManager::init(Core* core) {
-		if (!core) {
-			vkcv_log(LogLevel::ERROR, "Requires valid core pointer");
+	
+	CommandStreamHandle CommandStreamManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return CommandStreamHandle(id, destroy);
+	}
+	
+	void CommandStreamManager::destroyById(uint64_t id) {
+		auto& stream = getById(id);
+		
+		if (stream.cmdBuffer) {
+			getCore().getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer);
+			stream.cmdBuffer = nullptr;
+			stream.callbacks.clear();
 		}
-		m_core = core;
 	}
+	
+	CommandStreamManager::CommandStreamManager() noexcept :
+		HandleManager<CommandStreamEntry, CommandStreamHandle>() {}
 
-	CommandStreamHandle CommandStreamManager::createCommandStream(
-		const vk::Queue &queue,
-		vk::CommandPool cmdPool) {
-
-		const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(m_core->getContext().getDevice(), cmdPool);
-
-		CommandStream stream(cmdBuffer, queue, cmdPool);
-		beginCommandBuffer(stream.cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+	CommandStreamManager::~CommandStreamManager() noexcept {
+		clear();
+	}
 
-		// find unused stream
-		int unusedStreamIndex = -1;
-		for (size_t i = 0; i < m_commandStreams.size(); i++) {
-			if (m_commandStreams[i].cmdBuffer) {
-				// still in use
-			} else {
-				unusedStreamIndex = i;
-				break;
+	CommandStreamHandle CommandStreamManager::createCommandStream(const vk::Queue &queue,
+																  vk::CommandPool cmdPool) {
+		const vk::CommandBufferAllocateInfo info (cmdPool, vk::CommandBufferLevel::ePrimary, 1);
+		auto& device = getCore().getContext().getDevice();
+		
+		const vk::CommandBuffer cmdBuffer = device.allocateCommandBuffers(info).front();
+		
+		const vk::CommandBufferBeginInfo beginInfo (vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+		cmdBuffer.begin(beginInfo);
+		
+		for (uint64_t id = 0; id < getCount(); id++) {
+			auto& stream = getById(id);
+			
+			if (!(stream.cmdBuffer)) {
+				stream.cmdBuffer = cmdBuffer;
+				stream.cmdPool = cmdPool;
+				stream.queue = queue;
+				
+				return createById(id, [&](uint64_t id) { destroyById(id); });
 			}
 		}
-
-        const bool foundUnusedStream = unusedStreamIndex >= 0;
-        if (foundUnusedStream) {
-            m_commandStreams[unusedStreamIndex] = stream;
-            return CommandStreamHandle(unusedStreamIndex);
-        }
-
-        CommandStreamHandle handle(m_commandStreams.size());
-        m_commandStreams.push_back(stream);
-        return handle;
+		
+        return add({ cmdBuffer, cmdPool, queue, {} });
     }
 
-	void CommandStreamManager::recordCommandsToStream(
-		const CommandStreamHandle   &handle,
-		const RecordCommandFunction &record) {
-
-		const size_t id = handle.getId();
-		if (id >= m_commandStreams.size()) {
-			vkcv_log(LogLevel::ERROR, "Requires valid handle");
-			return;
-		}
-
-		CommandStream& stream = m_commandStreams[id];
+	void CommandStreamManager::recordCommandsToStream(const CommandStreamHandle &handle,
+													  const RecordCommandFunction &record) {
+		auto& stream = (*this)[handle];
 		record(stream.cmdBuffer);
 	}
 
-	void CommandStreamManager::addFinishCallbackToStream(
-		const CommandStreamHandle   &handle,
-		const FinishCommandFunction &finish) {
-
-		const size_t id = handle.getId();
-		if (id >= m_commandStreams.size()) {
-			vkcv_log(LogLevel::ERROR, "Requires valid handle");
-			return;
-		}
-
-		CommandStream& stream = m_commandStreams[id];
+	void CommandStreamManager::addFinishCallbackToStream(const CommandStreamHandle &handle,
+														 const FinishCommandFunction &finish) {
+		auto& stream = (*this)[handle];
 		stream.callbacks.push_back(finish);
 	}
 
-	void CommandStreamManager::submitCommandStreamSynchronous(
-		const CommandStreamHandle   &handle,
-		std::vector<vk::Semaphore>  &waitSemaphores,
-		std::vector<vk::Semaphore>  &signalSemaphores) {
-
-		const size_t id = handle.getId();
-		if (id >= m_commandStreams.size()) {
-			vkcv_log(LogLevel::ERROR, "Requires valid handle");
-			return;
-		}
-		CommandStream& stream = m_commandStreams[id];
+	void CommandStreamManager::submitCommandStreamSynchronous(const CommandStreamHandle &handle,
+															  std::vector<vk::Semaphore> &waitSemaphores,
+															  std::vector<vk::Semaphore> &signalSemaphores) {
+		auto& stream = (*this)[handle];
 		stream.cmdBuffer.end();
 
-		const auto device = m_core->getContext().getDevice();
-		const vk::Fence waitFence = createFence(device);
-		submitCommandBufferToQueue(stream.queue, stream.cmdBuffer, waitFence, waitSemaphores, signalSemaphores);
-		waitForFence(device, waitFence);
+		const auto device = getCore().getContext().getDevice();
+		const vk::Fence waitFence = device.createFence({});
+		
+		const std::vector<vk::PipelineStageFlags> waitDstStageMasks (
+				waitSemaphores.size(),
+				vk::PipelineStageFlagBits::eAllCommands
+		);
+		
+		const vk::SubmitInfo queueSubmitInfo(
+				waitSemaphores,
+				waitDstStageMasks,
+				stream.cmdBuffer,
+				signalSemaphores
+		);
+		
+		stream.queue.submit(queueSubmitInfo, waitFence);
+		assert(device.waitForFences(waitFence, true, UINT64_MAX) == vk::Result::eSuccess);
+		
 		device.destroyFence(waitFence);
-
-		device.freeCommandBuffers(stream.cmdPool, stream.cmdBuffer);
-		stream.cmdBuffer    = nullptr;
-		stream.cmdPool      = nullptr;
-		stream.queue        = nullptr;
+		stream.queue = nullptr;
 
 		for (const auto& finishCallback : stream.callbacks) {
 			finishCallback();
@@ -110,11 +100,8 @@ namespace vkcv {
 	}
 
 	vk::CommandBuffer CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle &handle) {
-		const size_t id = handle.getId();
-		if (id >= m_commandStreams.size()) {
-			vkcv_log(LogLevel::ERROR, "Requires valid handle");
-			return nullptr;
-		}
-		return m_commandStreams[id].cmdBuffer;
+		auto& stream = (*this)[handle];
+		return stream.cmdBuffer;
 	}
+	
 }
\ No newline at end of file
diff --git a/src/vkcv/CommandStreamManager.hpp b/src/vkcv/CommandStreamManager.hpp
index 9a8eb3d02143c20532275d5a61ca3cc68aa03778..7913ca223ff78fdc3afb1f41f83d1b31728864df 100644
--- a/src/vkcv/CommandStreamManager.hpp
+++ b/src/vkcv/CommandStreamManager.hpp
@@ -1,49 +1,44 @@
 #pragma once
+
 #include <vulkan/vulkan.hpp>
 #include <vector>
+
 #include "vkcv/Event.hpp"
-#include "vkcv/Handles.hpp"
-#include "vkcv/CommandRecordingFunctionTypes.hpp"
+#include "vkcv/EventFunctionTypes.hpp"
+
+#include "HandleManager.hpp"
 
 namespace vkcv {
 	
-	class Core;
+	/**
+	 * @brief Represents one command stream, into which commands can be recorded into.
+	 * Consists of a command buffer, the command buffer's command pool and a queue, as well as some callbacks.
+	 */
+	struct CommandStreamEntry {
+		vk::CommandBuffer                   cmdBuffer;
+		vk::CommandPool                     cmdPool;
+		vk::Queue                           queue;
+		std::vector<FinishCommandFunction>  callbacks;
+	};
 
 	/**
 	 * @brief Responsible for creation, deletion, callbacks and recording of command streams
 	*/
-	class CommandStreamManager
-	{
+	class CommandStreamManager : public HandleManager<CommandStreamEntry, CommandStreamHandle> {
 		friend class Core;
 	private:
-		/**
-		 * @brief Represents one command stream, into which commands can be recorded into.
-		 * Consists of a command buffer, the command buffer's command pool and a queue, as well as some callbacks.
-		*/
-		struct CommandStream {
-			inline CommandStream(vk::CommandBuffer cmdBuffer, vk::Queue queue, vk::CommandPool cmdPool) 
-				: cmdBuffer(cmdBuffer), cmdPool(cmdPool), queue(queue) {};
-			vk::CommandBuffer                   cmdBuffer;
-			vk::CommandPool                     cmdPool;
-			vk::Queue                           queue;
-			std::vector<FinishCommandFunction>  callbacks;
-		};
-
-		Core* m_core;
-		std::vector<CommandStream> m_commandStreams;
-
-		CommandStreamManager() noexcept;
-
-		void init(Core* core);
+		[[nodiscard]]
+		uint64_t getIdFrom(const CommandStreamHandle& handle) const override;
+		
+		[[nodiscard]]
+		CommandStreamHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+		
+		void destroyById(uint64_t id) override;
 
 	public:
-		~CommandStreamManager() noexcept;
-
-		CommandStreamManager(CommandStreamManager&& other) = delete;
-		CommandStreamManager(const CommandStreamManager& other) = delete;
-
-		CommandStreamManager& operator=(CommandStreamManager&& other) = delete;
-		CommandStreamManager& operator=(const CommandStreamManager& other) = delete;
+		CommandStreamManager() noexcept;
+		
+		~CommandStreamManager() noexcept override;
 
 		/**
 		 * @brief Creates a new command stream
@@ -52,9 +47,8 @@ namespace vkcv {
 		 * @param cmdPool Command pool the command buffer will be allocated from
 		 * @return Handle that represents the #CommandStream
 		*/
-		CommandStreamHandle createCommandStream(
-			const vk::Queue &queue,
-			vk::CommandPool cmdPool);
+		CommandStreamHandle createCommandStream(const vk::Queue &queue,
+												vk::CommandPool cmdPool);
 
 		/**
 		 * @brief Record vulkan commands to a #CommandStream, using a record function
@@ -62,7 +56,8 @@ namespace vkcv {
 		 * @param handle Command stream handle
 		 * @param record Function that records the vulkan commands
 		*/
-		void recordCommandsToStream(const CommandStreamHandle &handle, const RecordCommandFunction &record);
+		void recordCommandsToStream(const CommandStreamHandle &handle,
+									const RecordCommandFunction &record);
 
 		/**
 		 * @brief Add a callback to a #CommandStream that is called 
@@ -71,7 +66,8 @@ namespace vkcv {
 		 * @param handle Command stream handle
 		 * @param finish Callback that is called when a command stream submission is finished
 		*/
-		void addFinishCallbackToStream(const CommandStreamHandle &handle, const FinishCommandFunction &finish);
+		void addFinishCallbackToStream(const CommandStreamHandle &handle,
+									   const FinishCommandFunction &finish);
 
 		/**
 		 * @brief Submits a #CommandStream to it's queue and returns after execution is finished
@@ -80,10 +76,9 @@ namespace vkcv {
 		 * @param waitSemaphores Semaphores that are waited upon before executing the recorded commands
 		 * @param signalSemaphores Semaphores that are signaled when execution of the recorded commands is finished
 		*/
-		void submitCommandStreamSynchronous(
-			const CommandStreamHandle   &handle,
-			std::vector<vk::Semaphore>  &waitSemaphores,
-			std::vector<vk::Semaphore>  &signalSemaphores);
+		void submitCommandStreamSynchronous(const CommandStreamHandle   &handle,
+											std::vector<vk::Semaphore>  &waitSemaphores,
+											std::vector<vk::Semaphore>  &signalSemaphores);
 
 		/**
 		 * @brief Returns the underlying vulkan handle of a #CommandStream to be used for manual command recording
@@ -92,6 +87,7 @@ namespace vkcv {
 		 * @return Vulkan handle of the #CommandStream
 		*/
 		vk::CommandBuffer getStreamCommandBuffer(const CommandStreamHandle &handle);
+		
 	};
 
 }
\ No newline at end of file
diff --git a/src/vkcv/ComputePipelineManager.cpp b/src/vkcv/ComputePipelineManager.cpp
index 264389ca9b5ed118666911b07158ab589a4ca0ee..68836cdba92e4a28d6edfe4ee4e142043cde2c44 100644
--- a/src/vkcv/ComputePipelineManager.cpp
+++ b/src/vkcv/ComputePipelineManager.cpp
@@ -1,53 +1,62 @@
 #include "ComputePipelineManager.hpp"
 
-namespace vkcv
-{
-
-    ComputePipelineManager::ComputePipelineManager(vk::Device device) noexcept :
-            m_Device{device},
-            m_Pipelines{}
-    {}
-
-    ComputePipelineManager::~ComputePipelineManager() noexcept
-    {
-        for (uint64_t id = 0; id < m_Pipelines.size(); id++) {
-            destroyPipelineById(id);
-        }
+#include "vkcv/Core.hpp"
+
+namespace vkcv {
+	
+	uint64_t ComputePipelineManager::getIdFrom(const ComputePipelineHandle &handle) const {
+		return handle.getId();
+	}
+	
+	ComputePipelineHandle ComputePipelineManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return ComputePipelineHandle(id, destroy);
+	}
+	
+	void ComputePipelineManager::destroyById(uint64_t id) {
+		auto& pipeline = getById(id);
+		
+		if (pipeline.m_handle) {
+			getCore().getContext().getDevice().destroy(pipeline.m_handle);
+			pipeline.m_handle = nullptr;
+		}
+		
+		if (pipeline.m_layout) {
+			getCore().getContext().getDevice().destroy(pipeline.m_layout);
+			pipeline.m_layout = nullptr;
+		}
+	}
+	
+	ComputePipelineManager::ComputePipelineManager() noexcept :
+		HandleManager<ComputePipelineEntry, ComputePipelineHandle>() {}
+	
+	vk::Result ComputePipelineManager::createShaderModule(vk::ShaderModule &module,
+														  const ShaderProgram &shaderProgram,
+														  const ShaderStage stage) {
+		std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage);
+		vk::ShaderModuleCreateInfo moduleInfo({}, code.size() * sizeof(uint32_t), code.data());
+		return getCore().getContext().getDevice().createShaderModule(&moduleInfo, nullptr, &module);
+	}
+
+    ComputePipelineManager::~ComputePipelineManager() noexcept {
+        clear();
     }
 
-    vk::Pipeline ComputePipelineManager::getVkPipeline(const ComputePipelineHandle &handle) const
-    {
-        const uint64_t id = handle.getId();
-
-        if (id >= m_Pipelines.size()) {
-            return nullptr;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
+    vk::Pipeline ComputePipelineManager::getVkPipeline(const ComputePipelineHandle &handle) const {
+        auto& pipeline = (*this)[handle];
         return pipeline.m_handle;
     }
 
-    vk::PipelineLayout ComputePipelineManager::getVkPipelineLayout(const ComputePipelineHandle &handle) const
-    {
-        const uint64_t id = handle.getId();
-
-        if (id >= m_Pipelines.size()) {
-            return nullptr;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
+    vk::PipelineLayout ComputePipelineManager::getVkPipelineLayout(const ComputePipelineHandle &handle) const {
+		auto& pipeline = (*this)[handle];
         return pipeline.m_layout;
     }
 
     ComputePipelineHandle ComputePipelineManager::createComputePipeline(const ShaderProgram& shaderProgram,
 																		const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) {
-
         // Temporally handing over the Shader Program instead of a pipeline config
         vk::ShaderModule computeModule{};
         if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) != vk::Result::eSuccess)
-            return ComputePipelineHandle();
+            return {};
 
         vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo(
                 {},
@@ -67,10 +76,11 @@ namespace vkcv
         }
 
         vk::PipelineLayout vkPipelineLayout{};
-        if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) !=
-            vk::Result::eSuccess) {
-            m_Device.destroy(computeModule);
-            return ComputePipelineHandle();
+        if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo,
+																	nullptr,
+																	&vkPipelineLayout) != vk::Result::eSuccess) {
+			getCore().getContext().getDevice().destroy(computeModule);
+            return {};
         }
 
         vk::ComputePipelineCreateInfo computePipelineCreateInfo{};
@@ -78,42 +88,17 @@ namespace vkcv
         computePipelineCreateInfo.layout = vkPipelineLayout;
 
         vk::Pipeline vkPipeline;
-        if (m_Device.createComputePipelines(nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline) !=
-            vk::Result::eSuccess) {
-            m_Device.destroy(computeModule);
+        if (getCore().getContext().getDevice().createComputePipelines(nullptr,
+																	  1,
+																	  &computePipelineCreateInfo,
+																	  nullptr,
+																	  &vkPipeline) != vk::Result::eSuccess) {
+			getCore().getContext().getDevice().destroy(computeModule);
             return ComputePipelineHandle();
         }
-
-        m_Device.destroy(computeModule);
-
-        const uint64_t id = m_Pipelines.size();
-        m_Pipelines.push_back({vkPipeline, vkPipelineLayout});
-
-        return ComputePipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); });
+	
+		getCore().getContext().getDevice().destroy(computeModule);
+        return add({ vkPipeline, vkPipelineLayout });
     }
 
-    void vkcv::ComputePipelineManager::destroyPipelineById(uint64_t id) {
-        if (id >= m_Pipelines.size()) {
-            return;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
-        if (pipeline.m_handle) {
-            m_Device.destroy(pipeline.m_handle);
-            pipeline.m_handle = nullptr;
-        }
-
-        if (pipeline.m_layout) {
-            m_Device.destroy(pipeline.m_layout);
-            pipeline.m_layout = nullptr;
-        }
-    }
-
-    vk::Result ComputePipelineManager::createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, const ShaderStage stage)
-    {
-        std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage);
-        vk::ShaderModuleCreateInfo moduleInfo({}, code.size() * sizeof(uint32_t), code.data());
-        return m_Device.createShaderModule(&moduleInfo, nullptr, &module);
-    }
 }
\ No newline at end of file
diff --git a/src/vkcv/ComputePipelineManager.hpp b/src/vkcv/ComputePipelineManager.hpp
index 4c186ac7508a0cb8ff98d691d25cd8c3b1726f47..860181c8745756bab37790dadfa37b13d9ac0eab 100644
--- a/src/vkcv/ComputePipelineManager.hpp
+++ b/src/vkcv/ComputePipelineManager.hpp
@@ -9,28 +9,45 @@
 #include <vulkan/vulkan.hpp>
 #include <vector>
 
-#include "vkcv/Handles.hpp"
+#include "HandleManager.hpp"
+
 #include "vkcv/ShaderProgram.hpp"
 #include "vkcv/ComputePipelineConfig.hpp"
 
-namespace vkcv
-{
+namespace vkcv {
+	
+	struct ComputePipelineEntry {
+		vk::Pipeline m_handle;
+		vk::PipelineLayout m_layout;
+	};
 
 	/**
 	 * @brief Class to manage compute pipelines.
 	 */
-    class ComputePipelineManager
-    {
+    class ComputePipelineManager : public HandleManager<ComputePipelineEntry, ComputePipelineHandle> {
+	private:
+		[[nodiscard]]
+		uint64_t getIdFrom(const ComputePipelineHandle& handle) const override;
+	
+		[[nodiscard]]
+		ComputePipelineHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+	
+		/**
+		 * Destroys and deallocates compute pipeline represented by a given
+		 * compute pipeline handle id.
+		 *
+		 * @param id Compute pipeline handle id
+		 */
+		void destroyById(uint64_t id) override;
+	
+		vk::Result createShaderModule(vk::ShaderModule &module,
+									  const ShaderProgram &shaderProgram,
+									  ShaderStage stage);
+		
     public:
-        ComputePipelineManager() = delete; // no default ctor
-        explicit ComputePipelineManager(vk::Device device) noexcept; // ctor
-        ~ComputePipelineManager() noexcept; // dtor
-
-        ComputePipelineManager(const ComputePipelineManager &other) = delete; // copy-ctor
-        ComputePipelineManager(ComputePipelineManager &&other) = delete; // move-ctor;
-
-        ComputePipelineManager & operator=(const ComputePipelineManager &other) = delete; // copy-assign op
-        ComputePipelineManager & operator=(ComputePipelineManager &&other) = delete; // move-assign op
+		ComputePipelineManager() noexcept;
+		
+        ~ComputePipelineManager() noexcept override; // dtor
 
         /**
         * Returns a vk::Pipeline object by handle.
@@ -58,20 +75,7 @@ namespace vkcv
          */
         ComputePipelineHandle createComputePipeline(const ShaderProgram& shaderProgram,
 													const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts);
-
-    private:
-        struct ComputePipeline {
-            vk::Pipeline m_handle;
-            vk::PipelineLayout m_layout;
-        };
-
-        vk::Device m_Device;
-        std::vector<ComputePipeline> m_Pipelines;
-
-        void destroyPipelineById(uint64_t id);
-
-        vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, ShaderStage stage);
-
+		
     };
 
 }
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index f0520f0505b193e0e13d7f724242f1286f84634e..d6cc00f964f9f9549ae7d80e61de98932e00e30a 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -352,7 +352,7 @@ namespace vkcv
 		};
 		
 		if (!checkSupport(supportedLayers, validationLayers)) {
-			throw std::runtime_error("Validation layers requested but not available!");
+			vkcv_log_throw_error("Validation layers requested but not available!");
 		}
 #endif
 		
@@ -377,7 +377,7 @@ namespace vkcv
 		requiredExtensions.insert(requiredExtensions.end(), instanceExtensions.begin(), instanceExtensions.end());
 		
 		if (!checkSupport(supportedExtensions, requiredExtensions)) {
-			throw std::runtime_error("The requested instance extensions are not supported!");
+			vkcv_log_throw_error("The requested instance extensions are not supported!");
 		}
 		
 		const vk::ApplicationInfo applicationInfo(
@@ -408,7 +408,7 @@ namespace vkcv
 		vk::PhysicalDevice physicalDevice;
 		
 		if (!pickPhysicalDevice(instance, physicalDevice)) {
-			throw std::runtime_error("Picking suitable GPU as physical device failed!");
+			vkcv_log_throw_error("Picking suitable GPU as physical device failed!");
 		}
 		
 		FeatureManager featureManager (physicalDevice);
@@ -467,15 +467,14 @@ namespace vkcv
 				nullptr,
 				extensions.size(),
 				extensions.data(),
-				nullptr
+				nullptr,
+				&(featureManager.getFeatures())
 		);
 
 #ifndef NDEBUG
 		deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 		deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
 #endif
-
-		deviceCreateInfo.setPNext(&(featureManager.getFeatures()));
 		
 		vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
 		
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 22cc66e7cb9c31d569f5d56fab59a7b17cd840a4..576ddc4d4126f258b82f008c23635d32ff374922 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -1,27 +1,78 @@
 /**
- * @authors Artur Wasmut
+ * @authors Artur Wasmut, Alexander Gauggel, Tobias Frisch
  * @file src/vkcv/Core.cpp
  * @brief Handling of global states regarding dependencies
  */
 
 #include <GLFW/glfw3.h>
+#include <cmath>
 
 #include "vkcv/Core.hpp"
 #include "PassManager.hpp"
 #include "GraphicsPipelineManager.hpp"
 #include "ComputePipelineManager.hpp"
-#include "vkcv/BufferManager.hpp"
+#include "BufferManager.hpp"
 #include "SamplerManager.hpp"
 #include "ImageManager.hpp"
-#include "DescriptorManager.hpp"
+#include "DescriptorSetLayoutManager.hpp"
+#include "DescriptorSetManager.hpp"
 #include "WindowManager.hpp"
 #include "CommandStreamManager.hpp"
-#include <cmath>
+#include "vkcv/Image.hpp"
 #include "vkcv/Logger.hpp"
 #include "vkcv/BlitDownsampler.hpp"
 
 namespace vkcv
 {
+	
+	/**
+	 * @brief Generates a set of the family indices for all different kinds of
+	 * queues a given queue manager provides.
+	 *
+	 * @param[in] queueManager Queue manager
+	 * @return Set of queue family indices
+	 */
+	static std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) {
+		std::unordered_set<int> indexSet;
+		
+		for (const auto& queue : queueManager.getGraphicsQueues()) {
+			indexSet.insert(queue.familyIndex);
+		}
+		
+		for (const auto& queue : queueManager.getComputeQueues()) {
+			indexSet.insert(queue.familyIndex);
+		}
+		
+		for (const auto& queue : queueManager.getTransferQueues()) {
+			indexSet.insert(queue.familyIndex);
+		}
+		
+		indexSet.insert(queueManager.getPresentQueue().familyIndex);
+		return indexSet;
+	}
+	
+	/**
+	 * @brief Creates and returns a vector of newly allocated command pools
+	 * for each different queue family index in a given set.
+	 *
+	 * @param[in,out] device Vulkan device
+	 * @param[in] familyIndexSet Set of queue family indices
+	 * @return New command pools
+	 */
+	static std::vector<vk::CommandPool> createCommandPools(const vk::Device& device,
+														   const std::unordered_set<int>& familyIndexSet) {
+		std::vector<vk::CommandPool> commandPoolsPerQueueFamily;
+		commandPoolsPerQueueFamily.resize(familyIndexSet.size());
+		
+		const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient;
+		for (const int familyIndex : familyIndexSet) {
+			const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex);
+			commandPoolsPerQueueFamily[familyIndex] = device.createCommandPool(poolCreateInfo, nullptr, {});
+		}
+		
+		return commandPoolsPerQueueFamily;
+	}
+	
     Core Core::create(const char *applicationName,
                       uint32_t applicationVersion,
                       const std::vector<vk::QueueFlagBits>& queueFlags,
@@ -35,82 +86,156 @@ namespace vkcv
         		instanceExtensions
 		);
 
-        const auto& queueManager = context.getQueueManager();
-        
-		const std::unordered_set<int>	queueFamilySet			= generateQueueFamilyIndexSet(queueManager);
-		const auto						commandResources		= createCommandResources(context.getDevice(), queueFamilySet);
-		const auto						defaultSyncResources	= createSyncResources(context.getDevice());
-
-        return Core(std::move(context) , commandResources, defaultSyncResources);
+        return Core(std::move(context));
     }
 
-    const Context &Core::getContext() const
-    {
+    const Context &Core::getContext() const {
         return m_Context;
     }
 
-    Core::Core(Context &&context, const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
-            m_Context(std::move(context)),
-            m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
-            m_PipelineManager{std::make_unique<GraphicsPipelineManager>(m_Context.m_Device, m_Context.m_PhysicalDevice)},
-            m_ComputePipelineManager{std::make_unique<ComputePipelineManager>(m_Context.m_Device)},
-            m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)),
-            m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())},
-            m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))),
-            m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))},
-            m_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)},
+    Core::Core(Context &&context) noexcept :
+			m_Context(std::move(context)),
+			m_PassManager(std::make_unique<PassManager>()),
+			m_GraphicsPipelineManager(std::make_unique<GraphicsPipelineManager>()),
+			m_ComputePipelineManager(std::make_unique<ComputePipelineManager>()),
+			m_DescriptorSetLayoutManager(std::make_unique<DescriptorSetLayoutManager>()),
+			m_DescriptorSetManager(std::make_unique<DescriptorSetManager>()),
+			m_BufferManager(std::make_unique<BufferManager>()),
+			m_SamplerManager(std::make_unique<SamplerManager>()),
+			m_ImageManager(std::make_unique<ImageManager>()),
+			m_CommandStreamManager{std::make_unique<CommandStreamManager>()},
 			m_WindowManager(std::make_unique<WindowManager>()),
 			m_SwapchainManager(std::make_unique<SwapchainManager>()),
-            m_CommandResources(commandResources),
-            m_SyncResources(syncResources),
+			m_CommandPools(),
+			m_RenderFinished(),
+			m_SwapchainImageAcquired(),
 			m_downsampler(nullptr)
 	{
-		m_BufferManager->m_core = this;
-		m_BufferManager->init();
-		m_CommandStreamManager->init(this);
-		m_SwapchainManager->m_context = &m_Context;
-		m_ImageManager->m_core = this;
+		m_CommandPools = createCommandPools(
+				m_Context.getDevice(),
+				generateQueueFamilyIndexSet(m_Context.getQueueManager())
+		);
+		
+		m_RenderFinished = m_Context.getDevice().createSemaphore({});
+		m_SwapchainImageAcquired = m_Context.getDevice().createSemaphore({});
+		
+		m_PassManager->init(*this);
+		m_GraphicsPipelineManager->init(*this);
+		m_ComputePipelineManager->init(*this);
+		m_DescriptorSetLayoutManager->init(*this);
+		m_DescriptorSetManager->init(*this, *m_DescriptorSetLayoutManager);
+		m_BufferManager->init(*this);
+		m_SamplerManager->init(*this);
+		m_ImageManager->init(*this, *m_BufferManager);
+		m_CommandStreamManager->init(*this);
+		m_SwapchainManager->init(*this);
 		m_downsampler = std::unique_ptr<Downsampler>(new BlitDownsampler(*this, *m_ImageManager));
 	}
 
 	Core::~Core() noexcept {
 		m_Context.getDevice().waitIdle();
-
-		destroyCommandResources(m_Context.getDevice(), m_CommandResources);
-		destroySyncResources(m_Context.getDevice(), m_SyncResources);
+		
+		for (const vk::CommandPool &pool : m_CommandPools) {
+			m_Context.getDevice().destroyCommandPool(pool);
+		}
+		
+		m_Context.getDevice().destroySemaphore(m_RenderFinished);
+		m_Context.getDevice().destroySemaphore(m_SwapchainImageAcquired);
 	}
 	
-	GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config)
-    {
-        return m_PipelineManager->createPipeline(config, *m_PassManager, *m_DescriptorManager);
+	GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config) {
+        return m_GraphicsPipelineManager->createPipeline(config, *m_PassManager, *m_DescriptorSetLayoutManager);
     }
 
-    ComputePipelineHandle Core::createComputePipeline(const ComputePipelineConfig &config)
-    {
+    ComputePipelineHandle Core::createComputePipeline(const ComputePipelineConfig &config) {
 		std::vector<vk::DescriptorSetLayout> layouts;
-		layouts.resize(config.m_DescriptorSetLayouts.size());
+		layouts.resize(config.getDescriptorSetLayouts().size());
 	
 		for (size_t i = 0; i < layouts.size(); i++) {
-			layouts[i] = getDescriptorSetLayout(config.m_DescriptorSetLayouts[i]).vulkanHandle;
+			layouts[i] = m_DescriptorSetLayoutManager->getDescriptorSetLayout(
+					config.getDescriptorSetLayouts()[i]
+			).vulkanHandle;
 		}
 		
-        return m_ComputePipelineManager->createComputePipeline(config.m_ShaderProgram, layouts);
+        return m_ComputePipelineManager->createComputePipeline(config.getShaderProgram(), layouts);
     }
 
-    PassHandle Core::createPass(const PassConfig &config)
-    {
+    PassHandle Core::createPass(const PassConfig &config) {
         return m_PassManager->createPass(config);
     }
-
+	
+	const PassConfig &Core::getPassConfiguration(const vkcv::PassHandle &pass) {
+		return m_PassManager->getPassConfig(pass);
+	}
+	
+	BufferHandle Core::createBuffer(BufferType type,
+									const TypeGuard &typeGuard,
+									size_t count,
+									BufferMemoryType memoryType,
+									bool readable) {
+		return m_BufferManager->createBuffer(
+				typeGuard,
+				type,
+				memoryType,
+				count * typeGuard.typeSize(),
+				readable
+		);
+	}
+	
+	BufferHandle Core::createBuffer(BufferType type,
+									size_t size,
+									BufferMemoryType memoryType,
+									bool readable) {
+		return m_BufferManager->createBuffer(
+				TypeGuard(1),
+				type,
+				memoryType,
+				size,
+				readable
+		);
+	}
+	
+	vk::Buffer Core::getBuffer(const BufferHandle &buffer) const {
+		return m_BufferManager->getBuffer(buffer);
+	}
+	
+	BufferType Core::getBufferType(const BufferHandle &handle) const {
+		return m_BufferManager->getBufferType(handle);
+	}
+	
+	BufferMemoryType Core::getBufferMemoryType(const BufferHandle &handle) const {
+		return m_BufferManager->getBufferMemoryType(handle);
+	}
+	
+	size_t Core::getBufferSize(const BufferHandle &handle) const {
+		return m_BufferManager->getBufferSize(handle);
+	}
+	
+	void Core::fillBuffer(const BufferHandle &handle, const void *data, size_t size, size_t offset) {
+		m_BufferManager->fillBuffer(handle, data, size, offset);
+	}
+	
+	void Core::readBuffer(const BufferHandle &handle, void *data, size_t size, size_t offset) {
+		m_BufferManager->readBuffer(handle, data, size, offset);
+	}
+	
+	void* Core::mapBuffer(const BufferHandle &handle, size_t offset, size_t size) {
+		return m_BufferManager->mapBuffer(handle, offset, size);
+	}
+	
+	void Core::unmapBuffer(const BufferHandle &handle) {
+		m_BufferManager->unmapBuffer(handle);
+	}
+	
 	Result Core::acquireSwapchainImage(const SwapchainHandle &swapchainHandle) {
     	uint32_t imageIndex;
     	vk::Result result;
     	
 		try {
 			result = m_Context.getDevice().acquireNextImageKHR(
-					m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain(),
+					m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain,
 					std::numeric_limits<uint64_t>::max(),
-					m_SyncResources.swapchainImageAcquired,
+					m_SwapchainImageAcquired,
 					nullptr,
 					&imageIndex, {}
 			);
@@ -127,7 +252,7 @@ namespace vkcv
 		} else
 		if (result == vk::Result::eSuboptimalKHR) {
 			vkcv_log(LogLevel::WARNING, "Acquired image is suboptimal");
-			m_SwapchainManager->getSwapchain(swapchainHandle).signalSwapchainRecreation();
+			m_SwapchainManager->signalRecreation(swapchainHandle);
 		}
 		
 		m_currentSwapchainImageIndex = imageIndex;
@@ -135,21 +260,22 @@ namespace vkcv
 	}
 
 	bool Core::beginFrame(uint32_t& width, uint32_t& height, const WindowHandle &windowHandle) {
-		const SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+		const Window& window = m_WindowManager->getWindow(windowHandle);
+		const SwapchainHandle swapchainHandle = window.getSwapchain();
 
-		if (m_SwapchainManager->getSwapchain(swapchainHandle).shouldUpdateSwapchain()) {
+		if (m_SwapchainManager->shouldUpdateSwapchain(swapchainHandle)) {
 			m_Context.getDevice().waitIdle();
 
-			m_SwapchainManager->getSwapchain(swapchainHandle).updateSwapchain(m_Context, m_WindowManager->getWindow(windowHandle));
+			m_SwapchainManager->updateSwapchain(swapchainHandle, window);
 			
-			if (!m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain()) {
+			if (!m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain) {
 				return false;
 			}
 
 			setSwapchainImages(swapchainHandle);
 		}
 		
-		const auto& extent = m_SwapchainManager->getSwapchain(swapchainHandle).getExtent();
+		const auto& extent = m_SwapchainManager->getExtent(swapchainHandle);
 		
 		width = extent.width;
 		height = extent.height;
@@ -171,21 +297,18 @@ namespace vkcv
 		return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max());
 	}
 
-	std::array<uint32_t, 2> getWidthHeightFromRenderTargets(
-		const std::vector<ImageHandle>& renderTargets,
-		const Swapchain& swapchain,
-		const ImageManager& imageManager) {
+	static std::array<uint32_t, 2> getWidthHeightFromRenderTargets(const std::vector<ImageHandle>& renderTargets,
+																   const vk::Extent2D& swapchainExtent,
+																   const ImageManager& imageManager) {
 
 		std::array<uint32_t, 2> widthHeight;
 
 		if (renderTargets.size() > 0) {
 			const vkcv::ImageHandle firstImage = renderTargets[0];
 			if (firstImage.isSwapchainImage()) {
-				const auto& swapchainExtent = swapchain.getExtent();
 				widthHeight[0] = swapchainExtent.width;
 				widthHeight[1] = swapchainExtent.height;
-			}
-			else {
+			} else {
 				widthHeight[0] = imageManager.getImageWidth(firstImage);
 				widthHeight[1] = imageManager.getImageHeight(firstImage);
 			}
@@ -198,28 +321,26 @@ namespace vkcv
 		return widthHeight;
 	}
 
-	vk::Framebuffer createFramebuffer(
-		const std::vector<ImageHandle>& renderTargets,
-		const ImageManager&             imageManager,
-		const Swapchain&                swapchain,
-		vk::RenderPass                  renderpass,
-		vk::Device                      device) {
+	static vk::Framebuffer createFramebuffer(const std::vector<ImageHandle> &renderTargets,
+											 const ImageManager &imageManager,
+											 const vk::Extent2D &renderExtent,
+											 const vk::RenderPass &renderpass,
+											 const vk::Device &device) {
 
 		std::vector<vk::ImageView> attachmentsViews;
 		for (const ImageHandle& handle : renderTargets) {
 			attachmentsViews.push_back(imageManager.getVulkanImageView(handle));
 		}
 
-		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, swapchain, imageManager);
-
 		const vk::FramebufferCreateInfo createInfo(
 			{},
 			renderpass,
 			static_cast<uint32_t>(attachmentsViews.size()),
 			attachmentsViews.data(),
-			widthHeight[0],
-			widthHeight[1],
-			1);
+			renderExtent.width,
+			renderExtent.height,
+			1
+		);
 
 		return device.createFramebuffer(createInfo);
 	}
@@ -240,19 +361,8 @@ namespace vkcv
 	std::vector<vk::ClearValue> createAttachmentClearValues(const std::vector<AttachmentDescription>& attachments) {
 		std::vector<vk::ClearValue> clearValues;
 		for (const auto& attachment : attachments) {
-			if (attachment.load_operation == AttachmentOperation::CLEAR) {
-				float clear = 0.0f;
-
-				if (isDepthFormat(attachment.format)) {
-					clear = 1.0f;
-				}
-
-				clearValues.emplace_back(std::array<float, 4>{
-					clear,
-					clear,
-					clear,
-					0.f
-				});
+			if (attachment.getLoadOperation() == AttachmentOperation::CLEAR) {
+				clearValues.push_back(attachment.getClearValue());
 			}
 		}
 		return clearValues;
@@ -285,26 +395,34 @@ namespace vkcv
 		}
 	}
 	
-	void recordDrawcall(
-			const Core				&core,
-			const DrawcallInfo      &drawcall,
-			vk::CommandBuffer       cmdBuffer,
-			vk::PipelineLayout      pipelineLayout,
-			const PushConstants     &pushConstants,
-			const size_t            drawcallIndex) {
+	static void recordDrawcall(const DescriptorSetManager &descriptorSetManager,
+							   const BufferManager &bufferManager,
+							   const InstanceDrawcall &drawcall,
+							   vk::CommandBuffer cmdBuffer,
+							   vk::PipelineLayout pipelineLayout,
+							   const PushConstants &pushConstants,
+							   size_t drawcallIndex) {
+		
+		const auto& vertexData = drawcall.getVertexData();
 		
-		for (uint32_t i = 0; i < drawcall.mesh.vertexBufferBindings.size(); i++) {
-			const auto& vertexBinding = drawcall.mesh.vertexBufferBindings[i];
-			cmdBuffer.bindVertexBuffers(i, vertexBinding.buffer, vertexBinding.offset);
+		for (uint32_t i = 0; i < vertexData.getVertexBufferBindings().size(); i++) {
+			const auto& vertexBinding = vertexData.getVertexBufferBindings()[i];
+			
+			cmdBuffer.bindVertexBuffers(
+					i,
+					bufferManager.getBuffer(vertexBinding.buffer),
+					vertexBinding.offset
+			);
 		}
 		
-		for (const auto& descriptorUsage : drawcall.descriptorSets) {
+		for (const auto& usage : drawcall.getDescriptorSetUsages()) {
 			cmdBuffer.bindDescriptorSets(
 					vk::PipelineBindPoint::eGraphics,
 					pipelineLayout,
-					descriptorUsage.setLocation,
-					core.getDescriptorSet(descriptorUsage.descriptorSet).vulkanHandle,
-					nullptr);
+					usage.location,
+					descriptorSetManager.getDescriptorSet(usage.descriptorSet).vulkanHandle,
+					usage.dynamicOffsets
+			);
 		}
 		
 		if (pushConstants.getSizePerDrawcall() > 0) {
@@ -313,271 +431,363 @@ namespace vkcv
 					vk::ShaderStageFlagBits::eAll,
 					0,
 					pushConstants.getSizePerDrawcall(),
-					pushConstants.getDrawcallData(drawcallIndex));
+					pushConstants.getDrawcallData(drawcallIndex)
+			);
 		}
 		
-		if (drawcall.mesh.indexBuffer) {
-			cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, getIndexType(drawcall.mesh.indexBitCount));
-			cmdBuffer.drawIndexed(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {});
-		}
-		else {
-			cmdBuffer.draw(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {});
+		if (vertexData.getIndexBuffer()) {
+			cmdBuffer.bindIndexBuffer(
+					bufferManager.getBuffer(vertexData.getIndexBuffer()),
+					0,
+					getIndexType(vertexData.getIndexBitCount())
+			);
+			
+			cmdBuffer.drawIndexed(vertexData.getCount(), drawcall.getInstanceCount(), 0, 0, {});
+		} else {
+			cmdBuffer.draw(vertexData.getCount(), drawcall.getInstanceCount(), 0, 0, {});
 		}
 	}
-
-	void Core::recordDrawcallsToCmdStream(
-		const CommandStreamHandle&      cmdStreamHandle,
-		const PassHandle&               renderpassHandle,
-		const GraphicsPipelineHandle    &pipelineHandle,
-        const PushConstants             &pushConstantData,
-        const std::vector<DrawcallInfo> &drawcalls,
-		const std::vector<ImageHandle>  &renderTargets,
-		const WindowHandle              &windowHandle) {
-
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
-
-		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+	
+	static void recordGraphicsPipeline(Core& core,
+									   CommandStreamManager &cmdStreamManager,
+									   GraphicsPipelineManager &pipelineManager,
+									   PassManager &passManager,
+									   ImageManager &imageManager,
+									   const CommandStreamHandle &cmdStreamHandle,
+									   const GraphicsPipelineHandle &pipelineHandle,
+									   const PushConstants &pushConstants,
+									   const std::vector<ImageHandle> &renderTargets,
+									   const WindowHandle &windowHandle,
+									   const RecordCommandFunction &record) {
+		
+		const SwapchainHandle swapchainHandle = core.getWindow(windowHandle).getSwapchain();
+		
+		const std::array<uint32_t, 2> extent = getWidthHeightFromRenderTargets(
+				renderTargets,
+				core.getSwapchainExtent(swapchainHandle),
+				imageManager
+		);
+		
+		const auto width = extent[0];
+		const auto height = extent[1];
+		
+		const PassHandle &passHandle = pipelineManager.getPipelineConfig(pipelineHandle).getPass();
+		
+		const vk::RenderPass renderPass = passManager.getVkPass(passHandle);
+		const PassConfig passConfig = passManager.getPassConfig(passHandle);
+		
+		const auto& attachments = passConfig.getAttachments();
+		const auto& layouts = passManager.getLayouts(passHandle);
+		
+		if (renderTargets.size() != layouts.size()) {
+			vkcv_log(LogLevel::ERROR, "Amount of render targets does not match specified pipeline");
 			return;
 		}
-
-		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
-		const auto width  = widthHeight[0];
-		const auto height = widthHeight[1];
-
-		const vk::RenderPass        renderpass      = m_PassManager->getVkPass(renderpassHandle);
-		const PassConfig            passConfig      = m_PassManager->getPassConfig(renderpassHandle);
-
-		const vk::Pipeline          pipeline        = m_PipelineManager->getVkPipeline(pipelineHandle);
-		const vk::PipelineLayout    pipelineLayout  = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
-		const vk::Rect2D            renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
-
-		vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
-		transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
-
-		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
-
+		
+		const vk::Pipeline pipeline = pipelineManager.getVkPipeline(pipelineHandle);
+		const vk::Rect2D renderArea (vk::Offset2D(0, 0), vk::Extent2D(width, height));
+		
+		vk::CommandBuffer cmdBuffer = cmdStreamManager.getStreamCommandBuffer(cmdStreamHandle);
+		transitionRendertargetsToAttachmentLayout(renderTargets, imageManager, cmdBuffer);
+		
+		for (size_t i = 0; i < layouts.size(); i++) {
+			imageManager.recordImageLayoutTransition(renderTargets[i], 0, 0, layouts[i], cmdBuffer);
+		}
+		
+		const vk::Framebuffer framebuffer = createFramebuffer(
+				renderTargets,
+				imageManager,
+				renderArea.extent,
+				renderPass,
+				core.getContext().getDevice()
+		);
+		
 		if (!framebuffer) {
 			vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
 			return;
 		}
-
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Graphics;
-		submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
-
-		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
-			const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
-
-			const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data());
+		
+		auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) {
+			const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(attachments);
+			
+			const vk::RenderPassBeginInfo beginInfo(
+					renderPass,
+					framebuffer,
+					renderArea,
+					clearValues.size(),
+					clearValues.data()
+			);
+			
 			cmdBuffer.beginRenderPass(beginInfo, {}, {});
-
 			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
-
-			const GraphicsPipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
-			if (pipeConfig.m_UseDynamicViewport) {
+			
+			const GraphicsPipelineConfig &pipeConfig = pipelineManager.getPipelineConfig(pipelineHandle);
+			
+			if (pipeConfig.isViewportDynamic()) {
 				recordDynamicViewport(cmdBuffer, width, height);
 			}
-
-			for (size_t i = 0; i < drawcalls.size(); i++) {
-				recordDrawcall(*this, drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i);
+			
+			if (record) {
+				record(cmdBuffer);
 			}
-
+			
 			cmdBuffer.endRenderPass();
 		};
-
-		auto finishFunction = [framebuffer, this]()
-		{
-			m_Context.m_Device.destroy(framebuffer);
+		
+		auto finishFunction = [framebuffer, &core]() {
+			core.getContext().getDevice().destroy(framebuffer);
 		};
-
-		recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
+		
+		core.recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
 	}
 
-    void Core::recordIndexedIndirectDrawcallsToCmdStream(
-            const CommandStreamHandle                           cmdStreamHandle,
-            const PassHandle                                    renderpassHandle,
-            const GraphicsPipelineHandle                        &pipelineHandle,
-            const PushConstants                                 &pushConstantData,
-            const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
-            const vkcv::Mesh                                    &compiledMesh,
-            const std::vector<ImageHandle>                      &renderTargets,
-            const vkcv::Buffer<vk::DrawIndexedIndirectCommand>  &indirectBuffer,
-            const uint32_t                                      drawCount,
-			const WindowHandle                                  &windowHandle) {
-
-        if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
-            return;
-        }
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
-        const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle),
-                                                                                    *m_ImageManager);
-        const auto width = widthHeight[0];
-        const auto height = widthHeight[1];
-
-        const vk::RenderPass        renderpass      = m_PassManager->getVkPass(renderpassHandle);
-        const PassConfig            passConfig      = m_PassManager->getPassConfig(renderpassHandle);
-
-        const vk::Pipeline          pipeline        = m_PipelineManager->getVkPipeline(pipelineHandle);
-        const vk::PipelineLayout    pipelineLayout  = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
-        const vk::Rect2D            renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
-
-        vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
-        transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
-
-        const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass,
-                                                              m_Context.m_Device);
-
-        if (!framebuffer) {
-            vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
-            return;
-        }
-
-        SubmitInfo submitInfo;
-        submitInfo.queueType = QueueType::Graphics;
-        submitInfo.signalSemaphores = {m_SyncResources.renderFinished};
-
-        auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) {
-
-            const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
-
-            const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(),
-                                                    clearValues.data());
-            cmdBuffer.beginRenderPass(beginInfo, {}, {});
-
-            cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
+	void Core::recordDrawcallsToCmdStream(const CommandStreamHandle &cmdStreamHandle,
+										  const GraphicsPipelineHandle &pipelineHandle,
+										  const PushConstants &pushConstantData,
+										  const std::vector<InstanceDrawcall> &drawcalls,
+										  const std::vector<ImageHandle> &renderTargets,
+										  const WindowHandle &windowHandle) {
+		
+		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+			return;
+		}
+		
+		const vk::PipelineLayout pipelineLayout = m_GraphicsPipelineManager->getVkPipelineLayout(
+				pipelineHandle
+		);
 
-            const GraphicsPipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
-            if (pipeConfig.m_UseDynamicViewport) {
-                recordDynamicViewport(cmdBuffer, width, height);
-            }
+		auto recordFunction = [&](const vk::CommandBuffer& cmdBuffer) {
+			for (size_t i = 0; i < drawcalls.size(); i++) {
+				recordDrawcall(
+						*m_DescriptorSetManager,
+						*m_BufferManager,
+						drawcalls[i],
+						cmdBuffer,
+						pipelineLayout,
+						pushConstantData,
+						i
+				);
+			}
+		};
 
-			if (pushConstantData.getSizePerDrawcall() > 0)
-			{
-				cmdBuffer.pushConstants(
+		recordGraphicsPipeline(
+				*this,
+				*m_CommandStreamManager,
+				*m_GraphicsPipelineManager,
+				*m_PassManager,
+				*m_ImageManager,
+				cmdStreamHandle,
+				pipelineHandle,
+				pushConstantData,
+				renderTargets,
+				windowHandle,
+				recordFunction
+		);
+	}
+	
+	static void recordIndirectDrawcall(const Core& core,
+									   const DescriptorSetManager &descriptorSetManager,
+									   const BufferManager &bufferManager,
+									   vk::CommandBuffer cmdBuffer,
+									   vk::PipelineLayout pipelineLayout,
+									   const PushConstants& pushConstantData,
+									   size_t drawcallIndex,
+									   const IndirectDrawcall& drawcall) {
+		for (const auto& usage : drawcall.getDescriptorSetUsages()) {
+			cmdBuffer.bindDescriptorSets(
+					vk::PipelineBindPoint::eGraphics,
+					pipelineLayout,
+					usage.location,
+					descriptorSetManager.getDescriptorSet(usage.descriptorSet).vulkanHandle,
+					usage.dynamicOffsets
+			);
+		}
+		
+		const auto& vertexData = drawcall.getVertexData();
+		
+		for (uint32_t i = 0; i < vertexData.getVertexBufferBindings().size(); i++) {
+			const auto& vertexBinding = vertexData.getVertexBufferBindings()[i];
+			
+			cmdBuffer.bindVertexBuffers(
+					i,
+					bufferManager.getBuffer(vertexBinding.buffer),
+					vertexBinding.offset
+			);
+		}
+		
+		if (pushConstantData.getSizePerDrawcall() > 0) {
+			cmdBuffer.pushConstants(
 					pipelineLayout,
 					vk::ShaderStageFlagBits::eAll,
 					0,
 					pushConstantData.getSizePerDrawcall(),
-					pushConstantData.getDrawcallData(0));
-			}
-
-            vkcv::DescriptorSet descSet = m_DescriptorManager->getDescriptorSet(compiledDescriptorSet);
-
-            cmdBuffer.bindDescriptorSets(
-                    vk::PipelineBindPoint::eGraphics,
-                    pipelineLayout,
-                    0,
-                    descSet.vulkanHandle,
-                    nullptr);
-
-			vk::DeviceSize deviceSize = 0;
-			cmdBuffer.bindVertexBuffers(0, 1, &compiledMesh.vertexBufferBindings[0].buffer,&deviceSize);
-            cmdBuffer.bindIndexBuffer(compiledMesh.indexBuffer, 0, getIndexType(compiledMesh.indexBitCount));
-
-            cmdBuffer.drawIndexedIndirect(
-                    indirectBuffer.getVulkanHandle(),
-                    0,
-                    drawCount,
-                    sizeof(vk::DrawIndexedIndirectCommand));
-
-            cmdBuffer.endRenderPass();
-        };
+					pushConstantData.getDrawcallData(0)
+			);
+		}
+		
+		if (vertexData.getIndexBuffer()) {
+			cmdBuffer.bindIndexBuffer(
+					bufferManager.getBuffer(vertexData.getIndexBuffer()),
+					0,
+					getIndexType(vertexData.getIndexBitCount())
+			);
+			
+			cmdBuffer.drawIndexedIndirect(
+					bufferManager.getBuffer(drawcall.getIndirectDrawBuffer()),
+					0,
+					drawcall.getDrawCount(),
+					sizeof(vk::DrawIndexedIndirectCommand)
+			);
+		} else {
+			cmdBuffer.drawIndirect(
+					bufferManager.getBuffer(drawcall.getIndirectDrawBuffer()),
+					0,
+					drawcall.getDrawCount(),
+					sizeof(vk::DrawIndirectCommand)
+			);
+		}
+	}
 
-        auto finishFunction = [framebuffer, this]() {
-            m_Context.m_Device.destroy(framebuffer);
-        };
+	void Core::recordIndirectDrawcallsToCmdStream(const vkcv::CommandStreamHandle cmdStreamHandle,
+												  const vkcv::GraphicsPipelineHandle &pipelineHandle,
+												  const vkcv::PushConstants &pushConstantData,
+												  const std::vector<IndirectDrawcall> &drawcalls,
+												  const std::vector<ImageHandle> &renderTargets,
+												  const vkcv::WindowHandle &windowHandle) {
 
-        recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
+        if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+            return;
+        }
+	
+		const vk::PipelineLayout pipelineLayout = m_GraphicsPipelineManager->getVkPipelineLayout(
+				pipelineHandle
+		);
+	
+		auto recordFunction = [&](const vk::CommandBuffer& cmdBuffer) {
+			for (size_t i = 0; i < drawcalls.size(); i++) {
+				recordIndirectDrawcall(
+						*this,
+						*m_DescriptorSetManager,
+						*m_BufferManager,
+						cmdBuffer,
+						pipelineLayout,
+						pushConstantData,
+						i,
+						drawcalls[i]
+				);
+			}
+		};
+	
+		recordGraphicsPipeline(
+				*this,
+				*m_CommandStreamManager,
+				*m_GraphicsPipelineManager,
+				*m_PassManager,
+				*m_ImageManager,
+				cmdStreamHandle,
+				pipelineHandle,
+				pushConstantData,
+				renderTargets,
+				windowHandle,
+				recordFunction
+		);
     }
-
-	void Core::recordMeshShaderDrawcalls(
-		const CommandStreamHandle&                          cmdStreamHandle,
-		const PassHandle&                                   renderpassHandle,
-		const GraphicsPipelineHandle                        &pipelineHandle,
-		const PushConstants&                                pushConstantData,
-		const std::vector<MeshShaderDrawcall>&              drawcalls,
-		const std::vector<ImageHandle>&                     renderTargets,
-		const WindowHandle&                                 windowHandle) {
-
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
-
-		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+	
+	static void recordMeshShaderDrawcall(const Core& core,
+										 const DescriptorSetManager &descriptorSetManager,
+										 vk::CommandBuffer cmdBuffer,
+										 vk::PipelineLayout pipelineLayout,
+										 const PushConstants& pushConstantData,
+										 size_t drawcallIndex,
+										 const TaskDrawcall& drawcall) {
+		
+		static PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks = reinterpret_cast<PFN_vkCmdDrawMeshTasksNV>(
+				core.getContext().getDevice().getProcAddr("vkCmdDrawMeshTasksNV")
+		);
+		
+		if (!cmdDrawMeshTasks) {
+			vkcv_log(LogLevel::ERROR, "Mesh shader drawcalls are not supported");
 			return;
 		}
+		
+		for (const auto& descriptorUsage : drawcall.getDescriptorSetUsages()) {
+			cmdBuffer.bindDescriptorSets(
+					vk::PipelineBindPoint::eGraphics,
+					pipelineLayout,
+					descriptorUsage.location,
+					descriptorSetManager.getDescriptorSet(descriptorUsage.descriptorSet).vulkanHandle,
+					descriptorUsage.dynamicOffsets
+			);
+		}
+		
+		if (pushConstantData.getData()) {
+			cmdBuffer.pushConstants(
+					pipelineLayout,
+					vk::ShaderStageFlagBits::eAll,
+					0,
+					pushConstantData.getSizePerDrawcall(),
+					pushConstantData.getDrawcallData(drawcallIndex)
+			);
+		}
+		
+		cmdDrawMeshTasks(VkCommandBuffer(cmdBuffer), drawcall.getTaskCount(), 0);
+	}
 
-		const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
-		const auto width  = widthHeight[0];
-		const auto height = widthHeight[1];
-
-		const vk::RenderPass        renderpass = m_PassManager->getVkPass(renderpassHandle);
-		const PassConfig            passConfig = m_PassManager->getPassConfig(renderpassHandle);
-
-		const vk::Pipeline          pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
-		const vk::PipelineLayout    pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
-		const vk::Rect2D            renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
-
-		vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
-		transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
-
-		const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
-
-		if (!framebuffer) {
-			vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
+	void Core::recordMeshShaderDrawcalls(const CommandStreamHandle &cmdStreamHandle,
+										 const GraphicsPipelineHandle &pipelineHandle,
+										 const PushConstants &pushConstantData,
+										 const std::vector<TaskDrawcall> &drawcalls,
+										 const std::vector<ImageHandle> &renderTargets,
+										 const WindowHandle &windowHandle) {
+		
+		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
+		
+		const vk::PipelineLayout pipelineLayout = m_GraphicsPipelineManager->getVkPipelineLayout(
+				pipelineHandle
+		);
 
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Graphics;
-		submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
-
-		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
-			const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
-
-			const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data());
-			cmdBuffer.beginRenderPass(beginInfo, {}, {});
-
-			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
-
-			const GraphicsPipelineConfig& pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
-			if (pipeConfig.m_UseDynamicViewport) {
-				recordDynamicViewport(cmdBuffer, width, height);
-			}
-
+		auto recordFunction = [&](const vk::CommandBuffer& cmdBuffer) {
 			for (size_t i = 0; i < drawcalls.size(); i++) {
-                const uint32_t pushConstantOffset = i * pushConstantData.getSizePerDrawcall();
                 recordMeshShaderDrawcall(
 					*this,
+					*m_DescriptorSetManager,
                     cmdBuffer,
                     pipelineLayout,
                     pushConstantData,
-                    pushConstantOffset,
-                    drawcalls[i],
-                    0
+                    i,
+                    drawcalls[i]
 				);
 			}
-
-			cmdBuffer.endRenderPass();
 		};
-
-		auto finishFunction = [framebuffer, this]() {
-			m_Context.m_Device.destroy(framebuffer);
-		};
-
-		recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
+		
+		recordGraphicsPipeline(
+				*this,
+				*m_CommandStreamManager,
+				*m_GraphicsPipelineManager,
+				*m_PassManager,
+				*m_ImageManager,
+				cmdStreamHandle,
+				pipelineHandle,
+				pushConstantData,
+				renderTargets,
+				windowHandle,
+				recordFunction
+		);
 	}
 
 
-	void Core::recordRayGenerationToCmdStream(
-		CommandStreamHandle cmdStreamHandle,
-		vk::Pipeline rtxPipeline,
-		vk::PipelineLayout rtxPipelineLayout,
-		vk::StridedDeviceAddressRegionKHR rgenRegion,
-		vk::StridedDeviceAddressRegionKHR rmissRegion,
-		vk::StridedDeviceAddressRegionKHR rchitRegion,
-		vk::StridedDeviceAddressRegionKHR rcallRegion,
-		const std::vector<DescriptorSetUsage>& descriptorSetUsages,
-        const PushConstants& pushConstants,
-		const WindowHandle windowHandle) {
+	void Core::recordRayGenerationToCmdStream(CommandStreamHandle cmdStreamHandle,
+											  vk::Pipeline rtxPipeline,
+											  vk::PipelineLayout rtxPipelineLayout,
+											  vk::StridedDeviceAddressRegionKHR rgenRegion,
+											  vk::StridedDeviceAddressRegionKHR rmissRegion,
+											  vk::StridedDeviceAddressRegionKHR rchitRegion,
+											  vk::StridedDeviceAddressRegionKHR rcallRegion,
+											  const std::vector<DescriptorSetUsage>& descriptorSetUsages,
+											  const PushConstants& pushConstants,
+											  const WindowHandle& windowHandle) {
 
 		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
 			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, rtxPipeline);
@@ -585,8 +795,8 @@ namespace vkcv
 				cmdBuffer.bindDescriptorSets(
 					vk::PipelineBindPoint::eRayTracingKHR,
 					rtxPipelineLayout,
-					usage.setLocation,
-					{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
+					usage.location,
+					{ m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle },
 					usage.dynamicOffsets
 				);
 			}
@@ -610,15 +820,12 @@ namespace vkcv
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
     }
 
-	void Core::recordComputeDispatchToCmdStream(
-		CommandStreamHandle cmdStreamHandle,
-		ComputePipelineHandle computePipeline,
-		const uint32_t dispatchCount[3],
-		const std::vector<DescriptorSetUsage>& descriptorSetUsages,
-		const PushConstants& pushConstants) {
-
+	void Core::recordComputeDispatchToCmdStream(const CommandStreamHandle& cmdStreamHandle,
+												const ComputePipelineHandle& computePipeline,
+												const DispatchSize& dispatchSize,
+												const std::vector<DescriptorSetUsage>& descriptorSetUsages,
+												const PushConstants& pushConstants) {
 		auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
-
 			const auto pipelineLayout = m_ComputePipelineManager->getVkPipelineLayout(computePipeline);
 
 			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_ComputePipelineManager->getVkPipeline(computePipeline));
@@ -626,8 +833,8 @@ namespace vkcv
 				cmdBuffer.bindDescriptorSets(
 					vk::PipelineBindPoint::eCompute,
 					pipelineLayout,
-					usage.setLocation,
-					{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
+					usage.location,
+					{ m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle },
 					usage.dynamicOffsets
 				);
 			}
@@ -639,7 +846,8 @@ namespace vkcv
 					pushConstants.getSizePerDrawcall(),
 					pushConstants.getData());
 			}
-			cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]);
+			
+			cmdBuffer.dispatch(dispatchSize.x(), dispatchSize.y(), dispatchSize.z());
 		};
 
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
@@ -705,8 +913,8 @@ namespace vkcv
 				cmdBuffer.bindDescriptorSets(
 					vk::PipelineBindPoint::eCompute,
 					pipelineLayout,
-					usage.setLocation,
-					{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
+					usage.location,
+					{ m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle },
 					usage.dynamicOffsets
 				);
 			}
@@ -725,19 +933,18 @@ namespace vkcv
 	}
 
 	void Core::endFrame(const WindowHandle& windowHandle) {
-
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchain();
 
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
 		
-		std::array<vk::Semaphore, 2> waitSemaphores{
-			m_SyncResources.renderFinished,
-			m_SyncResources.swapchainImageAcquired
+		const std::array<vk::Semaphore, 2> waitSemaphores {
+			m_RenderFinished,
+			m_SwapchainImageAcquired
 		};
 
-		const vk::SwapchainKHR& swapchain = m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain();
+		const vk::SwapchainKHR& swapchain = m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain;
 		const vk::PresentInfoKHR presentInfo(
 			waitSemaphores,
 			swapchain,
@@ -747,7 +954,7 @@ namespace vkcv
 		vk::Result result;
 		
 		try {
-			result = m_Context.getDevice().getQueue(m_SwapchainManager->getSwapchain(swapchainHandle).getPresentQueueIndex(),0).presentKHR(presentInfo);
+			result = m_Context.getDevice().getQueue(m_SwapchainManager->getPresentQueueIndex(swapchainHandle),0).presentKHR(presentInfo);
 		} catch (const vk::OutOfDateKHRError& e) {
 			result = vk::Result::eErrorOutOfDateKHR;
 		} catch (const vk::DeviceLostError& e) {
@@ -764,67 +971,60 @@ namespace vkcv
 		}
 	}
 	
-	void Core::recordAndSubmitCommandsImmediate(
-		const SubmitInfo &submitInfo, 
-		const RecordCommandFunction &record, 
-		const FinishCommandFunction &finish)
-	{
-		const vk::Device& device = m_Context.getDevice();
-
-		const vkcv::Queue		queue		= getQueueForSubmit(submitInfo.queueType, m_Context.getQueueManager());
-		const vk::CommandPool	cmdPool		= chooseCmdPool(queue, m_CommandResources);
-		const vk::CommandBuffer	cmdBuffer	= allocateCommandBuffer(device, cmdPool);
-
-		beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
-		record(cmdBuffer);
-		cmdBuffer.end();
-		
-		vk::Fence waitFence = createFence(device);
-
-		submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores);
-		waitForFence(device, waitFence);
-		
-		device.destroyFence(waitFence);
-		
-		device.freeCommandBuffers(cmdPool, cmdBuffer);
-		
-		if (finish) {
-			finish();
+	/**
+	 * @brief Returns a queue of a given type from a queue manager.
+	 *
+	 * @param[in] type Type of queue
+	 * @param[in] queueManager Queue manager
+	 * @return Queue of a given type
+	 */
+	static Queue getQueueForSubmit(QueueType type, const QueueManager& queueManager) {
+		switch (type) {
+			case QueueType::Graphics:
+				return queueManager.getGraphicsQueues().front();
+			case QueueType::Compute:
+				return queueManager.getComputeQueues().front();
+			case QueueType::Transfer:
+				return queueManager.getTransferQueues().front();
+			case QueueType::Present:
+				return queueManager.getPresentQueue();
+			default: {
+				vkcv_log(LogLevel::ERROR, "Unknown queue type");
+				return queueManager.getGraphicsQueues().front(); // graphics is the most general queue
+			}
 		}
 	}
-
+	
 	CommandStreamHandle Core::createCommandStream(QueueType queueType) {
-		const vkcv::Queue       queue   = getQueueForSubmit(queueType, m_Context.getQueueManager());
-		const vk::CommandPool   cmdPool = chooseCmdPool(queue, m_CommandResources);
+		const vkcv::Queue     queue   = getQueueForSubmit(queueType, m_Context.getQueueManager());
+		const vk::CommandPool cmdPool = m_CommandPools[queue.familyIndex];
 
 		return m_CommandStreamManager->createCommandStream(queue.handle, cmdPool);
 	}
 
-    void Core::recordCommandsToStream(
-		const CommandStreamHandle   cmdStreamHandle,
-		const RecordCommandFunction &record, 
-		const FinishCommandFunction &finish) {
-
+    void Core::recordCommandsToStream(const CommandStreamHandle &stream,
+									  const RecordCommandFunction &record,
+									  const FinishCommandFunction &finish) {
 		if (record) {
-			m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record);
+			m_CommandStreamManager->recordCommandsToStream(stream, record);
 		}
 		
 		if (finish) {
-			m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish);
+			m_CommandStreamManager->addFinishCallbackToStream(stream, finish);
 		}
 	}
 
-	void Core::submitCommandStream(const CommandStreamHandle& handle,
+	void Core::submitCommandStream(const CommandStreamHandle& stream,
 								   bool signalRendering) {
 		std::vector<vk::Semaphore> waitSemaphores;
 		
 		// FIXME: add proper user controllable sync
 		std::vector<vk::Semaphore> signalSemaphores;
 		if (signalRendering) {
-			signalSemaphores.push_back(m_SyncResources.renderFinished);
+			signalSemaphores.push_back(m_RenderFinished);
 		}
 		
-		m_CommandStreamManager->submitCommandStreamSynchronous(handle, waitSemaphores, signalSemaphores);
+		m_CommandStreamManager->submitCommandStreamSynchronous(stream, waitSemaphores, signalSemaphores);
 	}
 
 	SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter,
@@ -833,46 +1033,52 @@ namespace vkcv
 		return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode, mipLodBias, borderColor);
 	}
 
-	Image Core::createImage(
-		vk::Format      format,
-		uint32_t        width,
-		uint32_t        height,
-		uint32_t        depth,
-		bool            createMipChain,
-		bool            supportStorage,
-		bool            supportColorAttachment,
-		Multisampling   multisampling)
-	{
-
+	ImageHandle Core::createImage(vk::Format format,
+								  uint32_t width,
+								  uint32_t height,
+								  uint32_t depth,
+								  bool createMipChain,
+								  bool supportStorage,
+								  bool supportColorAttachment,
+								  Multisampling multisampling) {
 		uint32_t mipCount = 1;
 		if (createMipChain) {
 			mipCount = 1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth))));
 		}
-
-		return Image::create(
-			m_ImageManager.get(), 
-			format,
-			width,
-			height,
-			depth,
-			mipCount,
-			supportStorage,
-			supportColorAttachment,
-			multisampling);
+		
+		return m_ImageManager->createImage(
+				width,
+				height,
+				depth,
+				format,
+				mipCount,
+				supportStorage,
+				supportColorAttachment,
+				multisampling
+		);
+	}
+	
+	void Core::fillImage(const ImageHandle &image,
+						 const void *data,
+						 size_t size) {
+		m_ImageManager->fillImage(image, data, size);
+	}
+	
+	void Core::switchImageLayout(const ImageHandle &image,
+								 vk::ImageLayout layout) {
+		m_ImageManager->switchImageLayoutImmediate(image, layout);
 	}
 	
 	Downsampler &Core::getDownsampler() {
 		return *m_downsampler;
 	}
 
-	WindowHandle Core::createWindow(
-			const char *applicationName,
-			uint32_t windowWidth,
-			uint32_t windowHeight,
-			bool resizeable) {
-
+	WindowHandle Core::createWindow(const char *applicationName,
+									uint32_t windowWidth,
+									uint32_t windowHeight,
+									bool resizeable) {
 		WindowHandle windowHandle = m_WindowManager->createWindow(*m_SwapchainManager ,applicationName, windowWidth, windowHeight, resizeable);
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchain();
 		setSwapchainImages( swapchainHandle );
 		return windowHandle;
 	}
@@ -880,7 +1086,19 @@ namespace vkcv
 	Window& Core::getWindow(const WindowHandle& handle) {
 		return m_WindowManager->getWindow(handle);
 	}
-
+	
+	vk::Format Core::getSwapchainFormat(const SwapchainHandle &swapchain) const {
+		return m_SwapchainManager->getFormat(swapchain);
+	}
+	
+	uint32_t Core::getSwapchainImageCount(const SwapchainHandle &swapchain) const {
+		return m_SwapchainManager->getImageCount(swapchain);
+	}
+	
+	vk::Extent2D Core::getSwapchainExtent(const SwapchainHandle& swapchain) const {
+		return m_SwapchainManager->getExtent(swapchain);
+	}
+	
 	uint32_t Core::getImageWidth(const ImageHandle& image)
 	{
 		return m_ImageManager->getImageWidth(image);
@@ -912,45 +1130,22 @@ namespace vkcv
 		return m_ImageManager->getImageArrayLayers(image);
 	}
 
-	Swapchain& Core::getSwapchainOfCurrentWindow() {
-		return m_SwapchainManager->getSwapchain(Window::getFocusedWindow().getSwapchainHandle());
-	}
-
-	Swapchain& Core::getSwapchain(const SwapchainHandle& handle) {
-		return m_SwapchainManager->getSwapchain(handle);
-	}
-
-	Swapchain& Core::getSwapchain(const WindowHandle& handle) {
-		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(handle).getSwapchainHandle();
-		return getSwapchain(swapchainHandle);
-	}
-
-	DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindings)
-	{
-	    return m_DescriptorManager->createDescriptorSetLayout(bindings);
-	}
-
-	DescriptorSetLayout Core::getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const
-	{
-	    return m_DescriptorManager->getDescriptorSetLayout(handle);
+	DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindings) {
+	    return m_DescriptorSetLayoutManager->createDescriptorSetLayout(bindings);
 	}
 
-	DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layout)
-    {
-        return m_DescriptorManager->createDescriptorSet(layout);
+	DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layout) {
+        return m_DescriptorSetManager->createDescriptorSet(layout);
     }
 
 	void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
-		m_DescriptorManager->writeDescriptorSet(
+		m_DescriptorSetManager->writeDescriptorSet(
 			handle,
 			writes, 
 			*m_ImageManager, 
 			*m_BufferManager, 
-			*m_SamplerManager);
-	}
-
-	DescriptorSet Core::getDescriptorSet(const DescriptorSetHandle handle) const {
-		return m_DescriptorManager->getDescriptorSet(handle);
+			*m_SamplerManager
+		);
 	}
 
 	void Core::prepareSwapchainImageForPresent(const CommandStreamHandle& cmdStream) {
@@ -1108,16 +1303,16 @@ namespace vkcv
 	}
 
 	void Core::setSwapchainImages( SwapchainHandle handle ) {
-		Swapchain swapchain = m_SwapchainManager->getSwapchain(handle);
+		const auto& swapchain = m_SwapchainManager->getSwapchain(handle);
 		const auto swapchainImages = m_SwapchainManager->getSwapchainImages(handle);
 		const auto swapchainImageViews = m_SwapchainManager->createSwapchainImageViews(handle);
 
 		m_ImageManager->setSwapchainImages(
 				swapchainImages,
 				swapchainImageViews,
-				swapchain.getExtent().width,
-				swapchain.getExtent().height,
-				swapchain.getFormat()
+				swapchain.m_Extent.width,
+				swapchain.m_Extent.height,
+				swapchain.m_Format
 		);
 	}
 	
@@ -1184,7 +1379,7 @@ namespace vkcv
 				m_Context.getDevice(),
 				vk::ObjectType::ePipeline,
 				uint64_t(static_cast<VkPipeline>(
-						m_PipelineManager->getVkPipeline(handle)
+						m_GraphicsPipelineManager->getVkPipeline(handle)
 				)),
 				label
 		);
@@ -1216,7 +1411,7 @@ namespace vkcv
 				m_Context.getDevice(),
 				vk::ObjectType::eDescriptorSet,
 				uint64_t(static_cast<VkDescriptorSet>(
-						m_DescriptorManager->getDescriptorSet(handle).vulkanHandle
+						m_DescriptorSetManager->getDescriptorSet(handle).vulkanHandle
 				)),
 				label
 		);
@@ -1273,4 +1468,79 @@ namespace vkcv
 				label
 		);
 	}
+	
+	void Core::run(const vkcv::WindowFrameFunction &frame) {
+		auto start = std::chrono::system_clock::now();
+		double t = 0.0;
+		
+		if (!frame)
+			return;
+		
+		while (Window::hasOpenWindow()) {
+			vkcv::Window::pollEvents();
+			
+			auto end = std::chrono::system_clock::now();
+			auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+			start = end;
+			
+			double dt = 0.000001 * static_cast<double>(deltatime.count());
+			
+			for (const auto &window : m_WindowManager->getWindowHandles()) {
+				uint32_t swapchainWidth, swapchainHeight;
+				if (!beginFrame(swapchainWidth, swapchainHeight, window)) {
+					continue;
+				}
+				
+				frame(window, t, dt, swapchainWidth, swapchainHeight);
+				endFrame(window);
+			}
+			
+			t += dt;
+		}
+	}
+	
+	vk::RenderPass Core::getVulkanRenderPass(const PassHandle &handle) const {
+		return m_PassManager->getVkPass(handle);
+	}
+	
+	vk::Pipeline Core::getVulkanPipeline(const GraphicsPipelineHandle &handle) const {
+		return m_GraphicsPipelineManager->getVkPipeline(handle);
+	}
+	
+	vk::Pipeline Core::getVulkanPipeline(const ComputePipelineHandle &handle) const {
+		return m_ComputePipelineManager->getVkPipeline(handle);
+	}
+	
+	vk::DescriptorSetLayout Core::getVulkanDescriptorSetLayout(const DescriptorSetLayoutHandle &handle) const {
+		return m_DescriptorSetLayoutManager->getDescriptorSetLayout(handle).vulkanHandle;
+	}
+	
+	vk::DescriptorSet Core::getVulkanDescriptorSet(const DescriptorSetHandle &handle) const {
+		return m_DescriptorSetManager->getDescriptorSet(handle).vulkanHandle;
+	}
+	
+	vk::Buffer Core::getVulkanBuffer(const BufferHandle &handle) const {
+		return m_BufferManager->getBuffer(handle);
+	}
+	
+	vk::Sampler Core::getVulkanSampler(const SamplerHandle &handle) const {
+		return m_SamplerManager->getVulkanSampler(handle);
+	}
+	
+	vk::Image Core::getVulkanImage(const ImageHandle &handle) const {
+		return m_ImageManager->getVulkanImage(handle);
+	}
+	
+	vk::ImageView Core::getVulkanImageView(const vkcv::ImageHandle &handle) const {
+		return m_ImageManager->getVulkanImageView(handle);
+	}
+	
+	vk::DeviceMemory Core::getVulkanDeviceMemory(const BufferHandle &handle) const {
+		return m_BufferManager->getDeviceMemory(handle);
+	}
+	
+	vk::DeviceMemory Core::getVulkanDeviceMemory(const ImageHandle &handle) const {
+		return m_ImageManager->getVulkanDeviceMemory(handle);
+	}
+	
 }
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorBinding.cpp
similarity index 86%
rename from src/vkcv/DescriptorConfig.cpp
rename to src/vkcv/DescriptorBinding.cpp
index 15bb05fd9cb3e5430b2a9909a682c468dfcde342..0ed0144e29942630bd3627a2eac086e82a8b3da6 100644
--- a/src/vkcv/DescriptorConfig.cpp
+++ b/src/vkcv/DescriptorBinding.cpp
@@ -1,7 +1,7 @@
-#include "vkcv/DescriptorConfig.hpp"
+#include "vkcv/DescriptorBinding.hpp"
 
-namespace vkcv
-{
+namespace vkcv {
+	
     bool DescriptorBinding::operator==(const DescriptorBinding &other) const
     {
 	    return (this->bindingID == other.bindingID) &&
@@ -10,4 +10,5 @@ namespace vkcv
 	           (this->shaderStages == other.shaderStages) &&
 	           (this->variableCount == other.variableCount);
     }
+	
 }
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
deleted file mode 100644
index a9cabbd4b65bd8e460bc3506338497992ded890a..0000000000000000000000000000000000000000
--- a/src/vkcv/DescriptorManager.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-#include "DescriptorManager.hpp"
-
-namespace vkcv
-{
-    DescriptorManager::DescriptorManager(vk::Device device) noexcept:
-        m_Device{ device }
-    {
-        /**
-         * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type below.
-		 * Finally, create an initial pool.
-         */
-		m_PoolSizes = {
-				vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000),
-				vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000),
-				vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000),
-				vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000),
-				vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000),
-				vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000),    // for RTX
-				vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1000) // for RTX
-		};
-
-		m_PoolInfo = vk::DescriptorPoolCreateInfo(
-				vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
-			1000,
-			static_cast<uint32_t>(m_PoolSizes.size()),
-			m_PoolSizes.data());
-
-		allocateDescriptorPool();
-    }
-
-    DescriptorManager::~DescriptorManager() noexcept
-    {
-        for (uint64_t id = 0; id < m_DescriptorSets.size(); id++) {
-			destroyDescriptorSetById(id);
-        }
-		
-		for (uint64_t id = 0; id < m_DescriptorSetLayouts.size(); id++) {
-			// Resets the usage count to zero for destruction.
-			m_DescriptorSetLayouts[id].layoutUsageCount = 0;
-			destroyDescriptorSetLayoutById(id);
-		}
-        
-		m_DescriptorSets.clear();
-		m_DescriptorSetLayouts.clear();
-  
-		for (const auto &pool : m_Pools) {
-			if (pool) {
-				m_Device.destroy(pool);
-			}
-		}
-    }
-
-    DescriptorSetLayoutHandle DescriptorManager::createDescriptorSetLayout(const DescriptorBindings &bindings)
-    {
-        for (size_t i = 0; i < m_DescriptorSetLayouts.size(); i++)
-        {
-            if(m_DescriptorSetLayouts[i].descriptorBindings.size() != bindings.size())
-                continue;
-
-            if (m_DescriptorSetLayouts[i].descriptorBindings == bindings)
-            {
-				m_DescriptorSetLayouts[i].layoutUsageCount++;
-                return DescriptorSetLayoutHandle(i, [&](uint64_t id) { destroyDescriptorSetLayoutById(id); });
-            }
-        }
-        
-        //create the descriptor set's layout and binding flags by iterating over its bindings
-        std::vector<vk::DescriptorSetLayoutBinding> bindingsVector = {};
-		std::vector<vk::DescriptorBindingFlags> bindingsFlags = {};
-		
-        for (auto bindingElem : bindings)
-        {
-            DescriptorBinding binding = bindingElem.second;
-            uint32_t bindingID = bindingElem.first;
-	
-			bindingsVector.emplace_back(
-					bindingID,
-					getVkDescriptorType(binding.descriptorType),
-					binding.descriptorCount,
-					getShaderStageFlags(binding.shaderStages),
-					nullptr
-			);
-			
-			vk::DescriptorBindingFlags flags;
-			
-			if (binding.variableCount)
-				flags |= vk::DescriptorBindingFlagBits::eVariableDescriptorCount;
-			
-			if (binding.partialBinding)
-				flags |= vk::DescriptorBindingFlagBits::ePartiallyBound;
-	
-			bindingsFlags.push_back(flags);
-        }
-		
-        vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo (
-				bindingsFlags.size(), bindingsFlags.data()
-		);
-
-        //create the descriptor set's layout from the binding data gathered above
-        vk::DescriptorSetLayout vulkanHandle;
-        vk::DescriptorSetLayoutCreateInfo layoutInfo(vk::DescriptorSetLayoutCreateFlags(), bindingsVector);
-		layoutInfo.setPNext(&bindingFlagsInfo);
-		
-        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();
-        };
-
-        const uint64_t id = m_DescriptorSetLayouts.size();
-        m_DescriptorSetLayouts.push_back({vulkanHandle, bindings, 1});
-        return DescriptorSetLayoutHandle(id, [&](uint64_t id) { destroyDescriptorSetLayoutById(id); });
-    }
-
-    DescriptorSetHandle DescriptorManager::createDescriptorSet(const DescriptorSetLayoutHandle &layout)
-    {
-        //create and allocate the set based on the layout provided
-        DescriptorSetLayout setLayout = m_DescriptorSetLayouts[layout.getId()];
-        vk::DescriptorSet vulkanHandle;
-        vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle);
-
-        uint32_t sumVariableDescriptorCounts = 0;
-        for (auto bindingElem : setLayout.descriptorBindings)
-        {
-            auto binding = bindingElem.second;
-            if(binding.variableCount)
-                sumVariableDescriptorCounts += binding.descriptorCount;
-        }
-
-        vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo(1, &sumVariableDescriptorCounts);
-
-        if (sumVariableDescriptorCounts > 0) {
-            allocInfo.setPNext(&variableAllocInfo);
-        }
-
-        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, &vulkanHandle);
-			}
-			
-			if (result != vk::Result::eSuccess) {
-				vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)",
-						 vk::to_string(result).c_str());
-				return DescriptorSetHandle();
-			}
-        };
-	
-		size_t poolIndex = (m_Pools.size() - 1);
-        const uint64_t id = m_DescriptorSets.size();
-        m_DescriptorSets.push_back({ vulkanHandle, layout, poolIndex });
-        return DescriptorSetHandle(id, [&](uint64_t id) { destroyDescriptorSetById(id); });
-    }
-    
-	/**
-	 * @brief Structure to store details to write to a descriptor set.
-	 */
-    struct WriteDescriptorSetInfo {
-		size_t imageInfoIndex;
-		size_t bufferInfoIndex;
-		uint32_t binding;
-		uint32_t arrayElementIndex;
-		uint32_t descriptorCount;
-		vk::DescriptorType type;
-    };
-
-	void DescriptorManager::writeDescriptorSet(
-		const DescriptorSetHandle	&handle,
-		const DescriptorWrites	&writes,
-		const ImageManager		&imageManager, 
-		const BufferManager		&bufferManager,
-		const SamplerManager	&samplerManager) {
-		vk::DescriptorSet set = m_DescriptorSets[handle.getId()].vulkanHandle;
-
-		std::vector<vk::DescriptorImageInfo> imageInfos;
-		std::vector<vk::DescriptorBufferInfo> bufferInfos;
-		
-		std::vector<WriteDescriptorSetInfo> writeInfos;
-
-		for (const auto& write : writes.getSampledImageWrites())
-		{
-		    const vk::ImageLayout layout = (write.useGeneralLayout?
-					vk::ImageLayout::eGeneral :
-					vk::ImageLayout::eShaderReadOnlyOptimal
-			);
-			
-			for (uint32_t i = 0; i < write.mipCount; i++) {
-				const vk::DescriptorImageInfo imageInfo(
-						nullptr,
-						imageManager.getVulkanImageView(
-								write.image,
-								write.mipLevel + i,
-								write.arrayView
-						),
-						layout
-				);
-				
-				imageInfos.push_back(imageInfo);
-			}
-			
-			WriteDescriptorSetInfo vulkanWrite = {
-					imageInfos.size() + 1 - write.mipCount,
-					0,
-					write.binding,
-					write.arrayIndex,
-					write.mipCount,
-					vk::DescriptorType::eSampledImage,
-			};
-			
-			writeInfos.push_back(vulkanWrite);
-		}
-
-		for (const auto& write : writes.getStorageImageWrites()) {
-			for (uint32_t i = 0; i < write.mipCount; i++) {
-				const vk::DescriptorImageInfo imageInfo(
-						nullptr,
-						imageManager.getVulkanImageView(
-								write.image,
-								write.mipLevel + i,
-								write.arrayView
-						),
-						vk::ImageLayout::eGeneral
-				);
-				
-				imageInfos.push_back(imageInfo);
-			}
-			
-			WriteDescriptorSetInfo vulkanWrite = {
-					imageInfos.size() + 1 - write.mipCount,
-					0,
-					write.binding,
-					0,
-					write.mipCount,
-					vk::DescriptorType::eStorageImage
-			};
-			
-			writeInfos.push_back(vulkanWrite);
-		}
-
-		for (const auto& write : writes.getUniformBufferWrites()) {
-			const size_t size = bufferManager.getBufferSize(write.buffer);
-			const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size);
-			
-			const vk::DescriptorBufferInfo bufferInfo(
-				bufferManager.getBuffer(write.buffer),
-				offset,
-				write.size == 0? size : std::min<uint32_t>(
-						write.size, size - offset
-				)
-			);
-			
-			bufferInfos.push_back(bufferInfo);
-
-			WriteDescriptorSetInfo vulkanWrite = {
-					0,
-					bufferInfos.size(),
-					write.binding,
-					0,
-					1,
-					write.dynamic?
-					vk::DescriptorType::eUniformBufferDynamic :
-					vk::DescriptorType::eUniformBuffer
-			};
-			
-			writeInfos.push_back(vulkanWrite);
-		}
-
-		for (const auto& write : writes.getStorageBufferWrites()) {
-			const size_t size = bufferManager.getBufferSize(write.buffer);
-			const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size);
-			
-			const vk::DescriptorBufferInfo bufferInfo(
-				bufferManager.getBuffer(write.buffer),
-				offset,
-				write.size == 0? size : std::min<uint32_t>(
-						write.size, size - offset
-				)
-			);
-			
-			bufferInfos.push_back(bufferInfo);
-			
-			WriteDescriptorSetInfo vulkanWrite = {
-					0,
-					bufferInfos.size(),
-					write.binding,
-					0,
-					1,
-					write.dynamic?
-					vk::DescriptorType::eStorageBufferDynamic :
-					vk::DescriptorType::eStorageBuffer
-			};
-			
-			writeInfos.push_back(vulkanWrite);
-		}
-
-		for (const auto& write : writes.getSamplerWrites()) {
-			const vk::Sampler& sampler = samplerManager.getVulkanSampler(write.sampler);
-			
-			const vk::DescriptorImageInfo imageInfo(
-				sampler,
-				nullptr,
-				vk::ImageLayout::eGeneral
-			);
-			
-			imageInfos.push_back(imageInfo);
-
-			WriteDescriptorSetInfo vulkanWrite = {
-					imageInfos.size(),
-					0,
-					write.binding,
-					0,
-					1,
-					vk::DescriptorType::eSampler
-			};
-			
-			writeInfos.push_back(vulkanWrite);
-		}
-		
-		std::vector<vk::WriteDescriptorSet> vulkanWrites;
-		
-		for (const auto& write : writeInfos) {
-			vk::WriteDescriptorSet vulkanWrite(
-					set,
-					write.binding,
-					write.arrayElementIndex,
-					write.descriptorCount,
-					write.type,
-					(write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr),
-					(write.bufferInfoIndex > 0? &(bufferInfos[write.bufferInfoIndex - 1]) : nullptr)
-			);
-			
-			vulkanWrites.push_back(vulkanWrite);
-		}
-		
-		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()];
-	}
-
-    void DescriptorManager::destroyDescriptorSetById(uint64_t id) {
-		if (id >= m_DescriptorSets.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid id");
-			return;
-		}
-		
-		auto& set = m_DescriptorSets[id];
-		
-		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_DescriptorSetLayouts.size()) {
-	        vkcv_log(LogLevel::ERROR, "Invalid id");
-	        return;
-	    }
-
-	    auto& layout = m_DescriptorSetLayouts[id];
-
-		if (layout.layoutUsageCount > 1) {
-			layout.layoutUsageCount--;
-			return;
-		} else {
-			layout.layoutUsageCount = 0;
-		}
-		
-	    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) {
-			vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool");
-			pool = nullptr;
-		} else {
-			m_Pools.push_back(pool);
-		}
-		
-		return pool;
-	}
-
-}
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
deleted file mode 100644
index c996e8c94f6dbb6f45f173f60f59f344a9a58623..0000000000000000000000000000000000000000
--- a/src/vkcv/DescriptorManager.hpp
+++ /dev/null
@@ -1,120 +0,0 @@
-#pragma once
-
-/**
- * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann, Tobias Frisch
- * @file src/vkcv/DescriptorManager.cpp
- * @brief Creation and handling of descriptor sets and the respective descriptor pools
- */
-#include <vulkan/vulkan.hpp>
-
-#include "vkcv/Handles.hpp"
-#include "vkcv/DescriptorConfig.hpp"
-#include "vkcv/DescriptorWrites.hpp"
-
-#include "ImageManager.hpp"
-#include "vkcv/BufferManager.hpp"
-#include "SamplerManager.hpp"
-
-namespace vkcv
-{
-	
-	/**
-	 * @brief Class to manage descriptor sets and descriptor set layouts.
-	 */
-	class DescriptorManager
-	{
-	public:
-		/**
-		 * @brief Constructor of the descriptor manager
-		 *
-		 * @param[in,out] device Vulkan device
-		 */
-	    explicit DescriptorManager(vk::Device device) noexcept;
-		
-		/**
-		 * @brief Destructor of the descriptor manager
-		 */
-	    ~DescriptorManager() noexcept;
-
-		/**
-		 * @brief Creates a descriptor set layout with given descriptor bindings
-		 * or returns a matching handle.
-		 *
-		 * @param[in] bindings Descriptor bindings
-		 * @return Handle of descriptor set layout
-		 */
-	    DescriptorSetLayoutHandle createDescriptorSetLayout(const DescriptorBindings &bindings);
-		
-		/**
-		 * @brief Creates a descriptor set using a given descriptor set layout.
-		 *
-		 * @param[in] layout Handle of descriptor set layout
-		 * @return Handle of descriptor set
-		 */
-        DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &layout);
-
-		/**
-		 * @brief Writes to a descriptor set using writes and all required managers.
-		 *
-		 * @param[in] handle Handle of descriptor set
-		 * @param[in] writes Descriptor set writes
-		 * @param[in] imageManager Image manager
-		 * @param[in] bufferManager Buffer manager
-		 * @param[in] samplerManager Sampler manager
-		 */
-		void writeDescriptorSet(const DescriptorSetHandle &handle,
-								const DescriptorWrites &writes,
-								const ImageManager &imageManager,
-								const BufferManager &bufferManager,
-								const SamplerManager &samplerManager);
-
-		[[nodiscard]]
-		DescriptorSetLayout getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const;
-		
-		[[nodiscard]]
-		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
-
-	private:
-		vk::Device m_Device;
-		std::vector<vk::DescriptorPool>	m_Pools;
-		std::vector<vk::DescriptorPoolSize> m_PoolSizes;
-		vk::DescriptorPoolCreateInfo m_PoolInfo;
-
-		/**
-         * 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;
-
-		/**
-		 * @brief Destroys a specific descriptor set.
-		 *
-		 * @param[in] the DescriptorSetHandle
-		 */
-		void destroyDescriptorSetById(uint64_t id);
-
-		/**
-         * @brief Revokes the usage of a specific descriptor set layout and
-         * destroys it once the usage count is at zero.
-         *
-         * @param[in] the DescriptorSetLayoutHandle
-         */
-		void destroyDescriptorSetLayoutById(uint64_t id);
-
-		/**
-		 * @brief 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.
-		 *
-		 * @return a DescriptorPool object
-		 */
-		vk::DescriptorPool allocateDescriptorPool();
-		
-	};
-	
-}
\ No newline at end of file
diff --git a/src/vkcv/DescriptorSetLayoutManager.cpp b/src/vkcv/DescriptorSetLayoutManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3122964036e793f1f49f23fd56253e7fc3c9464
--- /dev/null
+++ b/src/vkcv/DescriptorSetLayoutManager.cpp
@@ -0,0 +1,109 @@
+#include "DescriptorSetLayoutManager.hpp"
+
+#include "vkcv/Core.hpp"
+
+namespace vkcv {
+	
+	uint64_t DescriptorSetLayoutManager::getIdFrom(const DescriptorSetLayoutHandle &handle) const {
+		return handle.getId();
+	}
+	
+	DescriptorSetLayoutHandle DescriptorSetLayoutManager::createById(uint64_t id,
+																	 const HandleDestroyFunction &destroy) {
+		return DescriptorSetLayoutHandle(id, destroy);
+	}
+	
+	void DescriptorSetLayoutManager::destroyById(uint64_t id) {
+		auto& layout = getById(id);
+		
+		if (layout.layoutUsageCount > 1) {
+			layout.layoutUsageCount--;
+			return;
+		} else {
+			layout.layoutUsageCount = 0;
+		}
+		
+		if (layout.vulkanHandle){
+			getCore().getContext().getDevice().destroy(layout.vulkanHandle);
+			layout.vulkanHandle = nullptr;
+		}
+	}
+	
+	DescriptorSetLayoutManager::DescriptorSetLayoutManager() noexcept :
+			HandleManager<DescriptorSetLayoutEntry, DescriptorSetLayoutHandle>() {}
+	
+	DescriptorSetLayoutManager::~DescriptorSetLayoutManager() noexcept {
+		for (uint64_t id = 0; id < getCount(); id++) {
+			// Resets the usage count to zero for destruction.
+			getById(id).layoutUsageCount = 0;
+		}
+		
+		clear();
+	}
+	
+	DescriptorSetLayoutHandle DescriptorSetLayoutManager::createDescriptorSetLayout(const DescriptorBindings &bindings) {
+		for (uint64_t id = 0; id < getCount(); id++) {
+			auto& layout = getById(id);
+			
+			if (layout.descriptorBindings.size() != bindings.size())
+				continue;
+			
+			if (layout.descriptorBindings == bindings) {
+				layout.layoutUsageCount++;
+				return createById(id, [&](uint64_t id) { destroyById(id); });
+			}
+		}
+		
+		//create the descriptor set's layout and binding flags by iterating over its bindings
+		std::vector<vk::DescriptorSetLayoutBinding> bindingsVector = {};
+		std::vector<vk::DescriptorBindingFlags> bindingsFlags = {};
+		
+		for (auto bindingElem : bindings)
+		{
+			DescriptorBinding binding = bindingElem.second;
+			uint32_t bindingID = bindingElem.first;
+			
+			bindingsVector.emplace_back(
+					bindingID,
+					getVkDescriptorType(binding.descriptorType),
+					binding.descriptorCount,
+					getShaderStageFlags(binding.shaderStages),
+					nullptr
+			);
+			
+			vk::DescriptorBindingFlags flags;
+			
+			if (binding.variableCount)
+				flags |= vk::DescriptorBindingFlagBits::eVariableDescriptorCount;
+			
+			if (binding.partialBinding)
+				flags |= vk::DescriptorBindingFlagBits::ePartiallyBound;
+			
+			bindingsFlags.push_back(flags);
+		}
+		
+		vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo (
+				bindingsFlags.size(), bindingsFlags.data()
+		);
+		
+		//create the descriptor set's layout from the binding data gathered above
+		vk::DescriptorSetLayout vulkanHandle;
+		vk::DescriptorSetLayoutCreateInfo layoutInfo(vk::DescriptorSetLayoutCreateFlags(), bindingsVector);
+		layoutInfo.setPNext(&bindingFlagsInfo);
+		
+		auto result = getCore().getContext().getDevice().createDescriptorSetLayout(&layoutInfo,
+																				   nullptr,
+																				   &vulkanHandle);
+		if (result != vk::Result::eSuccess) {
+			vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
+			return DescriptorSetLayoutHandle();
+		};
+		
+		return add({ vulkanHandle, bindings, 1 });
+	}
+	
+	const DescriptorSetLayoutEntry& DescriptorSetLayoutManager::getDescriptorSetLayout(const DescriptorSetLayoutHandle& handle) const {
+		return (*this)[handle];
+	}
+	
+}
diff --git a/src/vkcv/DescriptorSetLayoutManager.hpp b/src/vkcv/DescriptorSetLayoutManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0dd2598c70fdeb6aeb3e9da1adc48b1a480e71c5
--- /dev/null
+++ b/src/vkcv/DescriptorSetLayoutManager.hpp
@@ -0,0 +1,70 @@
+#pragma once
+/**
+ * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann, Tobias Frisch
+ * @file src/vkcv/DescriptorManager.cpp
+ * @brief Creation and handling of descriptor set layouts.
+ */
+#include <vulkan/vulkan.hpp>
+
+#include "vkcv/DescriptorBinding.hpp"
+
+#include "HandleManager.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Structure to store details about a descriptor set layout.
+	 */
+	struct DescriptorSetLayoutEntry {
+		vk::DescriptorSetLayout vulkanHandle;
+		DescriptorBindings descriptorBindings;
+		size_t layoutUsageCount;
+	};
+	
+	/**
+	 * @brief Class to manage descriptor set layouts.
+	 */
+	class DescriptorSetLayoutManager : public HandleManager<DescriptorSetLayoutEntry, DescriptorSetLayoutHandle> {
+		friend class Core;
+	private:
+		[[nodiscard]]
+		uint64_t getIdFrom(const DescriptorSetLayoutHandle& handle) const override;
+		
+		[[nodiscard]]
+		DescriptorSetLayoutHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+		
+		/**
+		 * Destroys and deallocates descriptor set layout represented by a given
+		 * descriptor set layout handle id.
+		 *
+		 * @param id Descriptor set layout handle id
+		 */
+		void destroyById(uint64_t id) override;
+	
+	public:
+		/**
+		 * @brief Constructor of the descriptor set layout manager
+		 */
+		DescriptorSetLayoutManager() noexcept;
+		
+		/**
+		 * @brief Destructor of the descriptor set layout manager
+		 */
+		~DescriptorSetLayoutManager() noexcept override;
+		
+		/**
+		 * @brief Creates a descriptor set layout with given descriptor bindings
+		 * or returns a matching handle.
+		 *
+		 * @param[in] bindings Descriptor bindings
+		 * @return Handle of descriptor set layout
+		 */
+		[[nodiscard]]
+		DescriptorSetLayoutHandle createDescriptorSetLayout(const DescriptorBindings &bindings);
+		
+		[[nodiscard]]
+		const DescriptorSetLayoutEntry& getDescriptorSetLayout(const DescriptorSetLayoutHandle& handle) const;
+		
+	};
+	
+}
diff --git a/src/vkcv/DescriptorSetManager.cpp b/src/vkcv/DescriptorSetManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d7d680d9a3d72d694bd1cafa83c7c4f92bf2825f
--- /dev/null
+++ b/src/vkcv/DescriptorSetManager.cpp
@@ -0,0 +1,347 @@
+#include "DescriptorSetManager.hpp"
+
+#include "vkcv/Core.hpp"
+
+namespace vkcv {
+	
+	bool DescriptorSetManager::init(Core &core, DescriptorSetLayoutManager& descriptorSetLayoutManager) {
+		if (!HandleManager<DescriptorSetEntry, DescriptorSetHandle>::init(core)) {
+			return false;
+		}
+		
+		m_DescriptorSetLayoutManager = &descriptorSetLayoutManager;
+		
+		/**
+         * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type below.
+		 * Finally, create an initial pool.
+         */
+		m_PoolSizes = {
+				vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000),
+				vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000),
+				vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000),
+				vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000),
+				vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000),
+				vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000),    // for RTX
+				vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1000) // for RTX
+		};
+		
+		m_PoolInfo = vk::DescriptorPoolCreateInfo(
+				vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
+				1000,
+				static_cast<uint32_t>(m_PoolSizes.size()),
+				m_PoolSizes.data()
+		);
+		
+		return allocateDescriptorPool();
+	}
+	
+	uint64_t DescriptorSetManager::getIdFrom(const DescriptorSetHandle &handle) const {
+		return handle.getId();
+	}
+	
+	DescriptorSetHandle DescriptorSetManager::createById(uint64_t id,
+														 const HandleDestroyFunction &destroy) {
+		return DescriptorSetHandle(id, destroy);
+	}
+	
+	void DescriptorSetManager::destroyById(uint64_t id) {
+		auto& set = getById(id);
+		
+		if (set.vulkanHandle) {
+			getCore().getContext().getDevice().freeDescriptorSets(m_Pools[set.poolIndex], 1,
+																  &(set.vulkanHandle));
+			set.setLayoutHandle = DescriptorSetLayoutHandle();
+			set.vulkanHandle = nullptr;
+		}
+	}
+	
+	vk::DescriptorPool DescriptorSetManager::allocateDescriptorPool() {
+		vk::DescriptorPool pool;
+		if (getCore().getContext().getDevice().createDescriptorPool(&m_PoolInfo,
+																	nullptr,
+																	&pool) != vk::Result::eSuccess) {
+			vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool");
+			pool = nullptr;
+		} else {
+			m_Pools.push_back(pool);
+		}
+		
+		return pool;
+	}
+	
+	DescriptorSetManager::DescriptorSetManager() noexcept :
+			HandleManager<DescriptorSetEntry, DescriptorSetHandle>() {
+		
+	}
+	
+	DescriptorSetManager::~DescriptorSetManager() noexcept {
+		clear();
+		
+		for (const auto &pool : m_Pools) {
+			if (pool) {
+				getCore().getContext().getDevice().destroy(pool);
+			}
+		}
+	}
+	
+	DescriptorSetHandle DescriptorSetManager::createDescriptorSet(const DescriptorSetLayoutHandle &layout) {
+		//create and allocate the set based on the layout provided
+		const auto& setLayout = m_DescriptorSetLayoutManager->getDescriptorSetLayout(layout);
+		
+		vk::DescriptorSet vulkanHandle;
+		vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle);
+		
+		uint32_t sumVariableDescriptorCounts = 0;
+		for (auto bindingElem : setLayout.descriptorBindings) {
+			auto binding = bindingElem.second;
+			
+			if(binding.variableCount)
+				sumVariableDescriptorCounts += binding.descriptorCount;
+		}
+		
+		vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo(1, &sumVariableDescriptorCounts);
+		
+		if (sumVariableDescriptorCounts > 0) {
+			allocInfo.setPNext(&variableAllocInfo);
+		}
+		
+		auto result = getCore().getContext().getDevice().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 = getCore().getContext().getDevice().allocateDescriptorSets(&allocInfo, &vulkanHandle);
+			}
+			
+			if (result != vk::Result::eSuccess) {
+				vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)",
+						 vk::to_string(result).c_str());
+				return {};
+			}
+		};
+		
+		size_t poolIndex = (m_Pools.size() - 1);
+		return add({ vulkanHandle, layout, poolIndex });
+	}
+	
+	/**
+	 * @brief Structure to store details to write to a descriptor set.
+	 */
+	struct WriteDescriptorSetInfo {
+		size_t imageInfoIndex;
+		size_t bufferInfoIndex;
+		size_t structureIndex;
+		uint32_t binding;
+		uint32_t arrayElementIndex;
+		uint32_t descriptorCount;
+		vk::DescriptorType type;
+	};
+	
+	void DescriptorSetManager::writeDescriptorSet(const DescriptorSetHandle	&handle,
+												  const DescriptorWrites	&writes,
+												  const ImageManager		&imageManager,
+												  const BufferManager		&bufferManager,
+												  const SamplerManager	&samplerManager) {
+		auto& set = (*this)[handle];
+		
+		std::vector<vk::DescriptorImageInfo> imageInfos;
+		std::vector<vk::DescriptorBufferInfo> bufferInfos;
+		
+		std::vector<vk::WriteDescriptorSetAccelerationStructureKHR> writeStructures;
+		
+		std::vector<WriteDescriptorSetInfo> writeInfos;
+		
+		for (const auto& write : writes.getSampledImageWrites()) {
+			const vk::ImageLayout layout = (write.useGeneralLayout?
+											vk::ImageLayout::eGeneral :
+											vk::ImageLayout::eShaderReadOnlyOptimal
+			);
+			
+			for (uint32_t i = 0; i < write.mipCount; i++) {
+				const vk::DescriptorImageInfo imageInfo(
+						nullptr,
+						imageManager.getVulkanImageView(
+								write.image,
+								write.mipLevel + i,
+								write.arrayView
+						),
+						layout
+				);
+				
+				imageInfos.push_back(imageInfo);
+			}
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					imageInfos.size() + 1 - write.mipCount,
+					0,
+					0,
+					write.binding,
+					write.arrayIndex,
+					write.mipCount,
+					vk::DescriptorType::eSampledImage,
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		for (const auto& write : writes.getStorageImageWrites()) {
+			for (uint32_t i = 0; i < write.mipCount; i++) {
+				const vk::DescriptorImageInfo imageInfo(
+						nullptr,
+						imageManager.getVulkanImageView(
+								write.image,
+								write.mipLevel + i,
+								write.arrayView
+						),
+						vk::ImageLayout::eGeneral
+				);
+				
+				imageInfos.push_back(imageInfo);
+			}
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					imageInfos.size() + 1 - write.mipCount,
+					0,
+					0,
+					write.binding,
+					0,
+					write.mipCount,
+					vk::DescriptorType::eStorageImage
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		for (const auto& write : writes.getUniformBufferWrites()) {
+			const size_t size = bufferManager.getBufferSize(write.buffer);
+			const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size);
+			
+			const vk::DescriptorBufferInfo bufferInfo(
+					bufferManager.getBuffer(write.buffer),
+					offset,
+					write.size == 0? size : std::min<uint32_t>(
+							write.size, size - offset
+					)
+			);
+			
+			bufferInfos.push_back(bufferInfo);
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					0,
+					bufferInfos.size(),
+					0,
+					write.binding,
+					0,
+					1,
+					write.dynamic?
+					vk::DescriptorType::eUniformBufferDynamic :
+					vk::DescriptorType::eUniformBuffer
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		for (const auto& write : writes.getStorageBufferWrites()) {
+			const size_t size = bufferManager.getBufferSize(write.buffer);
+			const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size);
+			
+			const vk::DescriptorBufferInfo bufferInfo(
+					bufferManager.getBuffer(write.buffer),
+					offset,
+					write.size == 0? size : std::min<uint32_t>(
+							write.size, size - offset
+					)
+			);
+			
+			bufferInfos.push_back(bufferInfo);
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					0,
+					bufferInfos.size(),
+					0,
+					write.binding,
+					0,
+					1,
+					write.dynamic?
+					vk::DescriptorType::eStorageBufferDynamic :
+					vk::DescriptorType::eStorageBuffer
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		for (const auto& write : writes.getSamplerWrites()) {
+			const vk::Sampler& sampler = samplerManager.getVulkanSampler(write.sampler);
+			
+			const vk::DescriptorImageInfo imageInfo(
+					sampler,
+					nullptr,
+					vk::ImageLayout::eGeneral
+			);
+			
+			imageInfos.push_back(imageInfo);
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					imageInfos.size(),
+					0,
+					0,
+					write.binding,
+					0,
+					1,
+					vk::DescriptorType::eSampler
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		for (const auto& write : writes.getAccelerationWrites()) {
+			const vk::WriteDescriptorSetAccelerationStructureKHR structureWrite (
+					write.structures.size(),
+					write.structures.data()
+			);
+			
+			writeStructures.push_back(structureWrite);
+			
+			WriteDescriptorSetInfo vulkanWrite = {
+					0,
+					0,
+					writeStructures.size(),
+					write.binding,
+					0,
+					1,
+					vk::DescriptorType::eAccelerationStructureKHR
+			};
+			
+			writeInfos.push_back(vulkanWrite);
+		}
+		
+		std::vector<vk::WriteDescriptorSet> vulkanWrites;
+		
+		for (const auto& write : writeInfos) {
+			vk::WriteDescriptorSet vulkanWrite (
+					set.vulkanHandle,
+					write.binding,
+					write.arrayElementIndex,
+					write.descriptorCount,
+					write.type,
+					(write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr),
+					(write.bufferInfoIndex > 0? &(bufferInfos[write.bufferInfoIndex - 1]) : nullptr)
+			);
+			
+			if (write.structureIndex > 0) {
+				vulkanWrite.setPNext(&(writeStructures[write.structureIndex - 1]));
+			}
+			
+			vulkanWrites.push_back(vulkanWrite);
+		}
+		
+		getCore().getContext().getDevice().updateDescriptorSets(vulkanWrites, nullptr);
+	}
+	
+	const DescriptorSetEntry& DescriptorSetManager::getDescriptorSet(const DescriptorSetHandle& handle) const {
+		return (*this)[handle];
+	}
+	
+}
diff --git a/src/vkcv/DescriptorSetManager.hpp b/src/vkcv/DescriptorSetManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3c13aff26832888b2f43e33a03feb1ace77fb93
--- /dev/null
+++ b/src/vkcv/DescriptorSetManager.hpp
@@ -0,0 +1,106 @@
+#pragma once
+/**
+ * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann, Tobias Frisch
+ * @file src/vkcv/DescriptorManager.cpp
+ * @brief Creation and handling of descriptor sets and the respective descriptor pools.
+ */
+#include <vulkan/vulkan.hpp>
+
+#include "vkcv/DescriptorBinding.hpp"
+#include "vkcv/DescriptorWrites.hpp"
+
+#include "HandleManager.hpp"
+#include "BufferManager.hpp"
+#include "DescriptorSetLayoutManager.hpp"
+#include "ImageManager.hpp"
+#include "SamplerManager.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Structure to store details about a descriptor set.
+	 */
+	struct DescriptorSetEntry {
+		vk::DescriptorSet vulkanHandle;
+		DescriptorSetLayoutHandle setLayoutHandle;
+		size_t poolIndex;
+	};
+	
+	/**
+	 * @brief Class to manage descriptor sets.
+	 */
+	class DescriptorSetManager : public HandleManager<DescriptorSetEntry, DescriptorSetHandle> {
+		friend class Core;
+	private:
+		DescriptorSetLayoutManager* m_DescriptorSetLayoutManager;
+		
+		std::vector<vk::DescriptorPool>	m_Pools;
+		std::vector<vk::DescriptorPoolSize> m_PoolSizes;
+		vk::DescriptorPoolCreateInfo m_PoolInfo;
+		
+		bool init(Core& core, DescriptorSetLayoutManager& descriptorSetLayoutManager);
+		
+		[[nodiscard]]
+		uint64_t getIdFrom(const DescriptorSetHandle& handle) const override;
+		
+		[[nodiscard]]
+		DescriptorSetHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+		
+		/**
+		 * Destroys and deallocates descriptor set represented by a given
+		 * descriptor set handle id.
+		 *
+		 * @param id Descriptor set handle id
+		 */
+		void destroyById(uint64_t id) override;
+		
+		/**
+		 * @brief 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.
+		 *
+		 * @return a DescriptorPool object
+		 */
+		vk::DescriptorPool allocateDescriptorPool();
+	
+	public:
+		/**
+		 * @brief Constructor of the descriptor set manager
+		 */
+		DescriptorSetManager() noexcept;
+		
+		/**
+		 * @brief Destructor of the descriptor set manager
+		 */
+		~DescriptorSetManager() noexcept override;
+		
+		/**
+		 * @brief Creates a descriptor set using a given descriptor set layout.
+		 *
+		 * @param[in] layout Handle of descriptor set layout
+		 * @return Handle of descriptor set
+		 */
+		[[nodiscard]]
+		DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &layout);
+		
+		/**
+		 * @brief Writes to a descriptor set using writes and all required managers.
+		 *
+		 * @param[in] handle Handle of descriptor set
+		 * @param[in] writes Descriptor set writes
+		 * @param[in] imageManager Image manager
+		 * @param[in] bufferManager Buffer manager
+		 * @param[in] samplerManager Sampler manager
+		 */
+		void writeDescriptorSet(const DescriptorSetHandle &handle,
+								const DescriptorWrites &writes,
+								const ImageManager &imageManager,
+								const BufferManager &bufferManager,
+								const SamplerManager &samplerManager);
+		
+		[[nodiscard]]
+		const DescriptorSetEntry& getDescriptorSet(const DescriptorSetHandle& handle) const;
+		
+	};
+	
+}
diff --git a/src/vkcv/DescriptorSetUsage.cpp b/src/vkcv/DescriptorSetUsage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d1ab2ec752dde1047fd736fd6e300bdb586b238
--- /dev/null
+++ b/src/vkcv/DescriptorSetUsage.cpp
@@ -0,0 +1,12 @@
+
+#include "vkcv/DescriptorSetUsage.hpp"
+
+namespace vkcv {
+	
+	DescriptorSetUsage useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet,
+										const std::vector<uint32_t> &dynamicOffsets) {
+		DescriptorSetUsage usage (location, descriptorSet, dynamicOffsets);
+		return usage;
+	}
+	
+}
diff --git a/src/vkcv/DescriptorWrites.cpp b/src/vkcv/DescriptorWrites.cpp
index c64eeb5c4037018e949436d984c112d10e44a434..4f9c50ea1ac4628659b18decab6573b73fbdcb38 100644
--- a/src/vkcv/DescriptorWrites.cpp
+++ b/src/vkcv/DescriptorWrites.cpp
@@ -63,8 +63,9 @@ namespace vkcv {
 		return *this;
 	}
 	
-	DescriptorWrites &DescriptorWrites::writeAcceleration(uint32_t binding) {
-		m_accelerationWrites.emplace_back(binding);
+	DescriptorWrites &DescriptorWrites::writeAcceleration(uint32_t binding,
+														  const std::vector<vk::AccelerationStructureKHR> &structures) {
+		m_accelerationWrites.emplace_back(binding, structures);
 		return *this;
 	}
 	
diff --git a/src/vkcv/DispatchSize.cpp b/src/vkcv/DispatchSize.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf34d2de9668fb90a61208cce9f10abe57a64682
--- /dev/null
+++ b/src/vkcv/DispatchSize.cpp
@@ -0,0 +1,65 @@
+
+#include "vkcv/DispatchSize.hpp"
+#include "vkcv/Logger.hpp"
+
+#include <cmath>
+
+namespace vkcv {
+	
+	DispatchSize::DispatchSize(uint32_t count)
+	: DispatchSize(count, 1, 1) {}
+	
+	DispatchSize::DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ)
+	: m_Dispatch({ dimensionX, dimentionY, dimensionZ }) {
+		check();
+	}
+	
+	const uint32_t *DispatchSize::data() const {
+		return m_Dispatch.data();
+	}
+	
+	uint32_t DispatchSize::operator[](size_t index) const {
+		return m_Dispatch.at(index);
+	}
+	
+	uint32_t DispatchSize::x() const {
+		return m_Dispatch[0];
+	}
+	
+	uint32_t DispatchSize::y() const {
+		return m_Dispatch[1];
+	}
+	
+	uint32_t DispatchSize::z() const {
+		return m_Dispatch[2];
+	}
+	
+	bool DispatchSize::check() const {
+		const uint32_t dimensionX = x();
+		const uint32_t dimensionY = y();
+		const uint32_t dimensionZ = z();
+		
+		if ((dimensionX <= 0) || (dimensionY <= 0) || (dimensionZ <= 0)) {
+			vkcv_log(
+					LogLevel::WARNING,
+					"Dispatch size invalid: x = %u, y = %u, z = %u",
+					dimensionX,
+					dimensionY,
+					dimensionZ
+			);
+			
+			return false;
+		} else {
+			return true;
+		}
+	}
+	
+	DispatchSize dispatchInvocations(DispatchSize globalInvocations, DispatchSize groupSize) {
+		const uint32_t dimensionX = std::ceil(1.0f * globalInvocations.x() / groupSize.x());
+		const uint32_t dimensionY = std::ceil(1.0f * globalInvocations.y() / groupSize.y());
+		const uint32_t dimensionZ = std::ceil(1.0f * globalInvocations.z() / groupSize.z());
+		
+		return DispatchSize(dimensionX, dimensionY, dimensionZ);
+	}
+	
+}
diff --git a/src/vkcv/Drawcall.cpp b/src/vkcv/Drawcall.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..657481eb4648602e81f554292abaeec4a70c509d
--- /dev/null
+++ b/src/vkcv/Drawcall.cpp
@@ -0,0 +1,60 @@
+/**
+ * @authors Tobias Frisch
+ * @file src/vkcv/Drawcall.cpp
+ */
+
+#include "vkcv/Drawcall.hpp"
+
+namespace vkcv {
+	
+	const std::vector<DescriptorSetUsage> &Drawcall::getDescriptorSetUsages() const {
+		return m_usages;
+	}
+	
+	void Drawcall::useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet,
+									const std::vector<uint32_t>& dynamicOffsets) {
+		m_usages.emplace_back(location, descriptorSet, dynamicOffsets);
+	}
+	
+	InstanceDrawcall::InstanceDrawcall(const VertexData &vertexData,
+									   uint32_t instanceCount)
+	: Drawcall(),
+	m_vertexData(vertexData),
+	m_instanceCount(instanceCount) {}
+	
+	const VertexData &InstanceDrawcall::getVertexData() const {
+		return m_vertexData;
+	}
+	
+	uint32_t InstanceDrawcall::getInstanceCount() const {
+		return m_instanceCount;
+	}
+	
+	IndirectDrawcall::IndirectDrawcall(const BufferHandle &indirectDrawBuffer,
+									   const VertexData &vertexData,
+									   uint32_t drawCount)
+	: Drawcall(),
+	m_indirectDrawBuffer(indirectDrawBuffer),
+	m_vertexData(vertexData),
+	m_drawCount(drawCount) {}
+	
+	BufferHandle IndirectDrawcall::getIndirectDrawBuffer() const {
+		return m_indirectDrawBuffer;
+	}
+	
+	const VertexData &IndirectDrawcall::getVertexData() const {
+		return m_vertexData;
+	}
+	
+	uint32_t IndirectDrawcall::getDrawCount() const {
+		return m_drawCount;
+	}
+	
+	TaskDrawcall::TaskDrawcall(uint32_t taskCount)
+	: Drawcall(), m_taskCount(taskCount) {}
+	
+	uint32_t TaskDrawcall::getTaskCount() const {
+		return m_taskCount;
+	}
+	
+}
diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp
deleted file mode 100644
index eef87ca65d5f461f1c37b678634cf3e6f8973397..0000000000000000000000000000000000000000
--- a/src/vkcv/DrawcallRecording.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-
-#include "vkcv/DrawcallRecording.hpp"
-#include "vkcv/Logger.hpp"
-#include "vkcv/Core.hpp"
-
-namespace vkcv {
-
-    void recordMeshShaderDrawcall(const Core& core,
-                                  vk::CommandBuffer cmdBuffer,
-                                  vk::PipelineLayout pipelineLayout,
-                                  const PushConstants& pushConstantData,
-                                  uint32_t pushConstantOffset,
-                                  const MeshShaderDrawcall& drawcall,
-                                  uint32_t firstTask) {
-        static PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks = reinterpret_cast<PFN_vkCmdDrawMeshTasksNV>(
-                core.getContext().getDevice().getProcAddr("vkCmdDrawMeshTasksNV")
-        );
-	
-		if (!cmdDrawMeshTasks) {
-			vkcv_log(LogLevel::ERROR, "Mesh shader drawcalls are not supported");
-			return;
-		}
-
-        for (const auto& descriptorUsage : drawcall.descriptorSets) {
-            cmdBuffer.bindDescriptorSets(
-                vk::PipelineBindPoint::eGraphics,
-                pipelineLayout,
-                descriptorUsage.setLocation,
-				core.getDescriptorSet(descriptorUsage.descriptorSet).vulkanHandle,
-                nullptr);
-        }
-
-        // char* cast because void* does not support pointer arithmetic
-        const void* drawcallPushConstantData = pushConstantOffset + (char*)pushConstantData.getData();
-
-        if (pushConstantData.getData()) {
-            cmdBuffer.pushConstants(
-                pipelineLayout,
-                vk::ShaderStageFlagBits::eAll,
-                0,
-                pushConstantData.getSizePerDrawcall(),
-                drawcallPushConstantData);
-        }
-    
-        cmdDrawMeshTasks(VkCommandBuffer(cmdBuffer), drawcall.taskCount, firstTask);
-    }
-}
diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp
index bfceeb6300ece6dd3387d0785d8611a7289aa82c..502b07971bf0e20a3f02d94506a55ab0dcfcd5cf 100644
--- a/src/vkcv/FeatureManager.cpp
+++ b/src/vkcv/FeatureManager.cpp
@@ -431,6 +431,28 @@ m_physicalDevice.getFeatures2(&query)
 	    return true;
 	}
 	
+	bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkan13Features &features, bool required) const {
+		vkcv_check_init_features2(vk::PhysicalDeviceVulkan13Features);
+		
+		vkcv_check_feature(robustImageAccess);
+		vkcv_check_feature(inlineUniformBlock);
+		vkcv_check_feature(descriptorBindingInlineUniformBlockUpdateAfterBind);
+		vkcv_check_feature(pipelineCreationCacheControl);
+		vkcv_check_feature(privateData);
+		vkcv_check_feature(shaderDemoteToHelperInvocation);
+		vkcv_check_feature(shaderTerminateInvocation);
+		vkcv_check_feature(subgroupSizeControl);
+		vkcv_check_feature(computeFullSubgroups);
+		vkcv_check_feature(synchronization2);
+		vkcv_check_feature(textureCompressionASTC_HDR);
+		vkcv_check_feature(shaderZeroInitializeWorkgroupMemory);
+		vkcv_check_feature(dynamicRendering);
+		vkcv_check_feature(shaderIntegerDotProduct);
+		vkcv_check_feature(maintenance4);
+		
+		return true;
+	}
+	
 	vk::BaseOutStructure* FeatureManager::findFeatureStructure(vk::StructureType type) const {
 		for (auto& base : m_featuresExtensions) {
 			if (base->sType == type) {
@@ -535,7 +557,7 @@ m_physicalDevice.getFeatures2(&query)
 			
 			delete[] clone;
 			if (required) {
-				throw std::runtime_error("Required extension is not supported!");
+				vkcv_log_throw_error("Required extension is not supported!");
 			}
 			
 			return false;
diff --git a/src/vkcv/GraphicsPipelineConfig.cpp b/src/vkcv/GraphicsPipelineConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..37e86240e782460fe317c91e71c1ddcb5880fff0
--- /dev/null
+++ b/src/vkcv/GraphicsPipelineConfig.cpp
@@ -0,0 +1,121 @@
+
+#include "vkcv/GraphicsPipelineConfig.hpp"
+
+namespace vkcv {
+	
+	GraphicsPipelineConfig::GraphicsPipelineConfig()
+	: PipelineConfig(),
+	m_PassHandle(),
+	m_VertexLayout(),
+	m_Width(std::numeric_limits<uint32_t>::max()),
+	m_Height(std::numeric_limits<uint32_t>::max()) {}
+	
+	GraphicsPipelineConfig::GraphicsPipelineConfig(const ShaderProgram &program,
+												   const PassHandle &pass,
+												   const VertexLayout &vertexLayout,
+												   const std::vector<DescriptorSetLayoutHandle> &layouts)
+												   : PipelineConfig(program, layouts),
+												   m_PassHandle(pass),
+												   m_VertexLayout(vertexLayout),
+												   m_Width(std::numeric_limits<uint32_t>::max()),
+												   m_Height(std::numeric_limits<uint32_t>::max()) {}
+	
+	const PassHandle &GraphicsPipelineConfig::getPass() const {
+		return m_PassHandle;
+	}
+	
+	const VertexLayout &GraphicsPipelineConfig::getVertexLayout() const {
+		return m_VertexLayout;
+	}
+	
+	uint32_t GraphicsPipelineConfig::getWidth() const {
+		return m_Width;
+	}
+	
+	uint32_t GraphicsPipelineConfig::getHeight() const {
+		return m_Height;
+	}
+	
+	void GraphicsPipelineConfig::setResolution(uint32_t width, uint32_t height) {
+		m_Width = width;
+		m_Height = height;
+	}
+	
+	bool GraphicsPipelineConfig::isViewportDynamic() const {
+		return ((m_Width == std::numeric_limits<uint32_t>::max()) &&
+				(m_Height == std::numeric_limits<uint32_t>::max()));
+	}
+	
+	bool GraphicsPipelineConfig::isUsingConservativeRasterization() const {
+		return m_UseConservativeRasterization;
+	}
+	
+	void GraphicsPipelineConfig::setUsingConservativeRasterization(bool conservativeRasterization) {
+		m_UseConservativeRasterization = conservativeRasterization;
+	}
+	
+	PrimitiveTopology GraphicsPipelineConfig::getPrimitiveTopology() const {
+		return m_PrimitiveTopology;
+	}
+	
+	void GraphicsPipelineConfig::setPrimitiveTopology(PrimitiveTopology primitiveTopology) {
+		m_PrimitiveTopology = primitiveTopology;
+	}
+	
+	BlendMode GraphicsPipelineConfig::getBlendMode() const {
+		return m_blendMode;
+	}
+	
+	void GraphicsPipelineConfig::setBlendMode(BlendMode blendMode) {
+		m_blendMode = blendMode;
+	}
+	
+	bool GraphicsPipelineConfig::isDepthClampingEnabled() const {
+		return m_EnableDepthClamping;
+	}
+	
+	void GraphicsPipelineConfig::setDepthClampingEnabled(bool depthClamping) {
+		m_EnableDepthClamping = depthClamping;
+	}
+	
+	CullMode GraphicsPipelineConfig::getCulling() const {
+		return m_Culling;
+	}
+	
+	void GraphicsPipelineConfig::setCulling(CullMode cullMode) {
+		m_Culling = cullMode;
+	}
+	
+	DepthTest GraphicsPipelineConfig::getDepthTest() const {
+		return m_DepthTest;
+	}
+	
+	void GraphicsPipelineConfig::setDepthTest(DepthTest depthTest) {
+		m_DepthTest = depthTest;
+	}
+	
+	bool GraphicsPipelineConfig::isWritingDepth() const {
+		return m_DepthWrite;
+	}
+	
+	void GraphicsPipelineConfig::setWritingDepth(bool writingDepth) {
+		m_DepthWrite = writingDepth;
+	}
+	
+	bool GraphicsPipelineConfig::isWritingAlphaToCoverage() const {
+		return m_AlphaToCoverage;
+	}
+	
+	void GraphicsPipelineConfig::setWritingAlphaToCoverage(bool alphaToCoverage) {
+		m_AlphaToCoverage = alphaToCoverage;
+	}
+	
+	uint32_t GraphicsPipelineConfig::getTesselationControlPoints() const {
+		return m_TessellationControlPoints;
+	}
+	
+	void GraphicsPipelineConfig::setTesselationControlPoints(uint32_t tessellationControlPoints) {
+		m_TessellationControlPoints = tessellationControlPoints;
+	}
+	
+}
diff --git a/src/vkcv/GraphicsPipelineManager.cpp b/src/vkcv/GraphicsPipelineManager.cpp
index 2ebc84ad476bf911e42fc71bbc575db6b16db081..2a495fa18950f7b48e2b082853d1b31b5a7cf9c9 100644
--- a/src/vkcv/GraphicsPipelineManager.cpp
+++ b/src/vkcv/GraphicsPipelineManager.cpp
@@ -1,21 +1,39 @@
 #include "GraphicsPipelineManager.hpp"
+
+#include "vkcv/Core.hpp"
 #include "vkcv/Image.hpp"
 #include "vkcv/Logger.hpp"
+#include "vkcv/Multisampling.hpp"
 
-namespace vkcv
-{
+namespace vkcv {
 	
-	GraphicsPipelineManager::GraphicsPipelineManager(vk::Device device, vk::PhysicalDevice physicalDevice) noexcept :
-            m_Device(device),
-            m_physicalDevice(physicalDevice),
-            m_Pipelines{}
-    {}
+	uint64_t GraphicsPipelineManager::getIdFrom(const GraphicsPipelineHandle &handle) const {
+		return handle.getId();
+	}
 	
-	GraphicsPipelineManager::~GraphicsPipelineManager() noexcept
-    {
-        for (uint64_t id = 0; id < m_Pipelines.size(); id++) {
-            destroyPipelineById(id);
-        }
+	GraphicsPipelineHandle GraphicsPipelineManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return GraphicsPipelineHandle(id, destroy);
+	}
+	
+	void GraphicsPipelineManager::destroyById(uint64_t id) {
+		auto& pipeline = getById(id);
+		
+		if (pipeline.m_handle) {
+			getCore().getContext().getDevice().destroy(pipeline.m_handle);
+			pipeline.m_handle = nullptr;
+		}
+		
+		if (pipeline.m_layout) {
+			getCore().getContext().getDevice().destroy(pipeline.m_layout);
+			pipeline.m_layout = nullptr;
+		}
+	}
+	
+	GraphicsPipelineManager::GraphicsPipelineManager() noexcept :
+		HandleManager<GraphicsPipelineEntry, GraphicsPipelineHandle>() {}
+	
+	GraphicsPipelineManager::~GraphicsPipelineManager() noexcept {
+		clear();
     }
 
     // currently assuming default 32 bit formats, no lower precision or normalized variants supported
@@ -141,7 +159,7 @@ namespace vkcv
 			const GraphicsPipelineConfig &config) {
 		
 		if (existsVertexShader) {
-			const VertexLayout& layout = config.m_VertexLayout;
+			const VertexLayout& layout = config.getVertexLayout();
 			
 			// iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer
 			for (const auto& vertexBinding : layout.vertexBindings)
@@ -190,7 +208,7 @@ namespace vkcv
 	vk::PipelineInputAssemblyStateCreateInfo createPipelineInputAssemblyStateCreateInfo(const GraphicsPipelineConfig &config) {
 		vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
 				{},
-				primitiveTopologyToVulkanPrimitiveTopology(config.m_PrimitiveTopology),
+				primitiveTopologyToVulkanPrimitiveTopology(config.getPrimitiveTopology()),
 				false
 		);
 		
@@ -200,7 +218,7 @@ namespace vkcv
 	vk::PipelineTessellationStateCreateInfo createPipelineTessellationStateCreateInfo(const GraphicsPipelineConfig &config) {
 		vk::PipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(
 				{},
-				config.m_tessellationControlPoints
+				config.getTesselationControlPoints()
 		);
 		
 		return pipelineTessellationStateCreateInfo;
@@ -217,14 +235,14 @@ namespace vkcv
 		
 		viewport = vk::Viewport(
 				0.f, 0.f,
-				static_cast<float>(config.m_Width),
-				static_cast<float>(config.m_Height),
+				static_cast<float>(config.getWidth()),
+				static_cast<float>(config.getHeight()),
 				0.f, 1.f
 		);
 		
 		scissor = vk::Rect2D(
 				{ 0,0 },
-				{ config.m_Width, config.m_Height }
+				{ config.getWidth(), config.getHeight() }
 		);
 		
 		vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
@@ -253,7 +271,7 @@ namespace vkcv
 		const GraphicsPipelineConfig &config,
 		const vk::PhysicalDeviceConservativeRasterizationPropertiesEXT& conservativeRasterProperties) {
 		vk::CullModeFlags cullMode;
-		switch (config.m_culling) {
+		switch (config.getCulling()) {
 			case CullMode::None:
 				cullMode = vk::CullModeFlagBits::eNone;
 				break;
@@ -273,7 +291,7 @@ namespace vkcv
 		
 		vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo (
 				{},
-				config.m_EnableDepthClamping,
+				config.isDepthClampingEnabled(),
 				false,
 				vk::PolygonMode::eFill,
 				cullMode,
@@ -287,7 +305,7 @@ namespace vkcv
 
 		static vk::PipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterization;
 		
-		if (config.m_UseConservativeRasterization) {
+		if (config.isUsingConservativeRasterization()) {
             const float overestimationSize = 1.0f - conservativeRasterProperties.primitiveOverestimationSize;
             const float maxOverestimationSize = conservativeRasterProperties.maxExtraPrimitiveOverestimationSize;
 
@@ -308,14 +326,15 @@ namespace vkcv
 	 * @param config set MSAA Sample Count Flag
 	 * @return Pipeline Multisample State Create Info Struct
 	 */
-	vk::PipelineMultisampleStateCreateInfo createPipelineMultisampleStateCreateInfo(const GraphicsPipelineConfig &config) {
+	vk::PipelineMultisampleStateCreateInfo createPipelineMultisampleStateCreateInfo(const GraphicsPipelineConfig &config,
+																					const PassConfig &passConfig) {
 		vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
 				{},
-				msaaToVkSampleCountFlag(config.m_multisampling),
+				msaaToSampleCountFlagBits(passConfig.getMultisampling()),
 				false,
 				0.f,
 				nullptr,
-				config.m_alphaToCoverage,
+				config.isWritingAlphaToCoverage(),
 				false
 		);
 		
@@ -332,7 +351,7 @@ namespace vkcv
 		// currently set to additive, if not disabled
 		// BlendFactors must be set as soon as additional BlendModes are added
 		static vk::PipelineColorBlendAttachmentState colorBlendAttachmentState (
-				config.m_blendMode != BlendMode::None,
+				config.getBlendMode() != BlendMode::None,
 				vk::BlendFactor::eOne,
 				vk::BlendFactor::eOne,
 				vk::BlendOp::eAdd,
@@ -368,7 +387,7 @@ namespace vkcv
 																const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) {
 		static vk::PushConstantRange pushConstantRange;
 		
-		const size_t pushConstantsSize = config.m_ShaderProgram.getPushConstantsSize();
+		const size_t pushConstantsSize = config.getShaderProgram().getPushConstantsSize();
 		pushConstantRange = vk::PushConstantRange(
 				vk::ShaderStageFlagBits::eAll, 0, pushConstantsSize
 		);
@@ -394,9 +413,9 @@ namespace vkcv
 	vk::PipelineDepthStencilStateCreateInfo createPipelineDepthStencilStateCreateInfo(const GraphicsPipelineConfig &config) {
 		const vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilCreateInfo(
 				vk::PipelineDepthStencilStateCreateFlags(),
-				config.m_depthTest != DepthTest::None,
-				config.m_depthWrite,
-				depthTestToVkCompareOp(config.m_depthTest),
+				config.getDepthTest() != DepthTest::None,
+				config.isWritingDepth(),
+				depthTestToVkCompareOp(config.getDepthTest()),
 				false,
 				false,
 				{},
@@ -417,7 +436,7 @@ namespace vkcv
 		static std::vector<vk::DynamicState> dynamicStates;
 		dynamicStates.clear();
 		
-		if(config.m_UseDynamicViewport) {
+		if(config.isViewportDynamic()) {
 			dynamicStates.push_back(vk::DynamicState::eViewport);
 			dynamicStates.push_back(vk::DynamicState::eScissor);
 		}
@@ -433,37 +452,38 @@ namespace vkcv
 
     GraphicsPipelineHandle GraphicsPipelineManager::createPipeline(const GraphicsPipelineConfig &config,
 																   const PassManager& passManager,
-																   const DescriptorManager& descriptorManager) {
-        const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle);
-
-        const bool existsTaskShader     = config.m_ShaderProgram.existsShader(ShaderStage::TASK);
-        const bool existsMeshShader     = config.m_ShaderProgram.existsShader(ShaderStage::MESH);
-        const bool existsVertexShader   = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX);
-        const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT);
-        const bool existsGeometryShader = config.m_ShaderProgram.existsShader(ShaderStage::GEOMETRY);
-		const bool existsTessellationControlShader = config.m_ShaderProgram.existsShader(ShaderStage::TESS_CONTROL);
-		const bool existsTessellationEvaluationShader = config.m_ShaderProgram.existsShader(ShaderStage::TESS_EVAL);
+																   const DescriptorSetLayoutManager& descriptorManager) {
+        const vk::RenderPass &pass = passManager.getVkPass(config.getPass());
+
+		const auto& program = config.getShaderProgram();
+		
+        const bool existsTaskShader     = program.existsShader(ShaderStage::TASK);
+        const bool existsMeshShader     = program.existsShader(ShaderStage::MESH);
+        const bool existsVertexShader   = program.existsShader(ShaderStage::VERTEX);
+        const bool existsFragmentShader = program.existsShader(ShaderStage::FRAGMENT);
+        const bool existsGeometryShader = program.existsShader(ShaderStage::GEOMETRY);
+		const bool existsTessellationControlShader = program.existsShader(ShaderStage::TESS_CONTROL);
+		const bool existsTessellationEvaluationShader = program.existsShader(ShaderStage::TESS_EVAL);
 		
         const bool validGeometryStages  = (
 				(existsVertexShader && (existsTessellationControlShader == existsTessellationEvaluationShader)) ||
 				(existsTaskShader && existsMeshShader)
 		);
 
-        if (!validGeometryStages)
-        {
+        if (!validGeometryStages) {
             vkcv_log(LogLevel::ERROR, "Requires vertex or task and mesh shader");
-            return GraphicsPipelineHandle();
+            return {};
         }
 
         if (!existsFragmentShader) {
             vkcv_log(LogLevel::ERROR, "Requires fragment shader code");
-            return GraphicsPipelineHandle();
+            return {};
         }
 
         std::vector<vk::PipelineShaderStageCreateInfo> shaderStages;
         auto destroyShaderModules = [&shaderStages, this] {
             for (auto stage : shaderStages) {
-                m_Device.destroyShaderModule(stage.module);
+				getCore().getContext().getDevice().destroyShaderModule(stage.module);
             }
             shaderStages.clear();
         };
@@ -471,121 +491,116 @@ namespace vkcv
         if (existsVertexShader) {
             vk::PipelineShaderStageCreateInfo createInfo;
             const bool success = createPipelineShaderStageCreateInfo(
-                    config.m_ShaderProgram,
+					program,
                     ShaderStage::VERTEX,
-                    m_Device,
+					getCore().getContext().getDevice(),
                     &createInfo);
 
             if (success) {
                 shaderStages.push_back(createInfo);
-            }
-            else {
+            } else {
                 destroyShaderModules();
-                return GraphicsPipelineHandle();
+                return {};
             }
         }
 
         if (existsTaskShader) {
             vk::PipelineShaderStageCreateInfo createInfo;
             const bool success = createPipelineShaderStageCreateInfo(
-                    config.m_ShaderProgram,
+					program,
                     ShaderStage::TASK,
-                    m_Device,
+					getCore().getContext().getDevice(),
                     &createInfo);
 
             if (success) {
                 shaderStages.push_back(createInfo);
-            }
-            else {
+            } else {
                 destroyShaderModules();
-                return GraphicsPipelineHandle();
+                return {};
             }
         }
 
         if (existsMeshShader) {
             vk::PipelineShaderStageCreateInfo createInfo;
             const bool success = createPipelineShaderStageCreateInfo(
-                    config.m_ShaderProgram,
+					program,
                     ShaderStage::MESH,
-                    m_Device,
+					getCore().getContext().getDevice(),
                     &createInfo);
 
             if (success) {
                 shaderStages.push_back(createInfo);
-            }
-            else {
+            } else {
                 destroyShaderModules();
-                return GraphicsPipelineHandle();
+                return {};
             }
         }
 
         {
             vk::PipelineShaderStageCreateInfo createInfo;
             const bool success = createPipelineShaderStageCreateInfo(
-                    config.m_ShaderProgram,
+					program,
                     ShaderStage::FRAGMENT,
-                    m_Device,
+					getCore().getContext().getDevice(),
                     &createInfo);
 
             if (success) {
                 shaderStages.push_back(createInfo);
-            }
-            else {
+            } else {
                 destroyShaderModules();
-                return GraphicsPipelineHandle();
+                return {};
             }
         }
 
         if (existsGeometryShader) {
             vk::PipelineShaderStageCreateInfo createInfo;
             const bool success = createPipelineShaderStageCreateInfo(
-                    config.m_ShaderProgram,
+					program,
                     ShaderStage::GEOMETRY,
-                    m_Device,
+					getCore().getContext().getDevice(),
                     &createInfo);
 
             if (success) {
                 shaderStages.push_back(createInfo);
-            }
-            else {
+            } else {
                 destroyShaderModules();
-                return GraphicsPipelineHandle();
+                return {};
             }
         }
 	
 		if (existsTessellationControlShader) {
 			vk::PipelineShaderStageCreateInfo createInfo;
 			const bool success = createPipelineShaderStageCreateInfo(
-					config.m_ShaderProgram,
+					program,
 					ShaderStage::TESS_CONTROL,
-					m_Device,
+					getCore().getContext().getDevice(),
 					&createInfo);
 		
 			if (success) {
 				shaderStages.push_back(createInfo);
-			}
-			else {
+			} else {
 				destroyShaderModules();
-				return GraphicsPipelineHandle();
+				return {};
 			}
 		}
 	
 		if (existsTessellationEvaluationShader) {
 			vk::PipelineShaderStageCreateInfo createInfo;
 			const bool success = createPipelineShaderStageCreateInfo(
-					config.m_ShaderProgram,
+					program,
 					ShaderStage::TESS_EVAL,
-					m_Device,
+					getCore().getContext().getDevice(),
 					&createInfo);
 		
 			if (success) {
 				shaderStages.push_back(createInfo);
-			}
-			else {
+			} else {
 				destroyShaderModules();
-				return GraphicsPipelineHandle();
+				return {};
 			}
 		}
+	
+		const PassConfig& passConfig = passManager.getPassConfig(config.getPass());
 
         // vertex input state
         // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers
@@ -615,13 +630,13 @@ namespace vkcv
         vk::PhysicalDeviceProperties                                deviceProperties;
         vk::PhysicalDeviceProperties2                               deviceProperties2(deviceProperties);
         deviceProperties2.pNext = &conservativeRasterProperties;
-        m_physicalDevice.getProperties2(&deviceProperties2);
+		getCore().getContext().getPhysicalDevice().getProperties2(&deviceProperties2);
         vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo =
                 createPipelineRasterizationStateCreateInfo(config, conservativeRasterProperties);
 
         // multisample state
         vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo =
-                createPipelineMultisampleStateCreateInfo(config);
+                createPipelineMultisampleStateCreateInfo(config, passConfig);
 
         // color blend state
         vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo =
@@ -632,8 +647,8 @@ namespace vkcv
                 createPipelineDynamicStateCreateInfo(config);
 
 		std::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
-		descriptorSetLayouts.reserve(config.m_DescriptorLayouts.size());
-		for (const auto& handle : config.m_DescriptorLayouts) {
+		descriptorSetLayouts.reserve(config.getDescriptorSetLayouts().size());
+		for (const auto& handle : config.getDescriptorSetLayouts()) {
 			descriptorSetLayouts.push_back(descriptorManager.getDescriptorSetLayout(handle).vulkanHandle);
 		}
 		
@@ -642,9 +657,11 @@ namespace vkcv
                 createPipelineLayoutCreateInfo(config, descriptorSetLayouts);
 
         vk::PipelineLayout vkPipelineLayout{};
-        if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) {
+        if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo,
+																	nullptr,
+																	&vkPipelineLayout) != vk::Result::eSuccess) {
             destroyShaderModules();
-            return GraphicsPipelineHandle();
+            return {};
         }
 
         // Depth Stencil
@@ -652,10 +669,10 @@ namespace vkcv
                 createPipelineDepthStencilStateCreateInfo(config);
 
         const vk::PipelineDepthStencilStateCreateInfo* p_depthStencilCreateInfo = nullptr;
-        const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle);
 
-        for (const auto& attachment : passConfig.attachments) {
-            if (isDepthFormat(attachment.format)) {
+        for (const auto& attachment : passConfig.getAttachments()) {
+            if ((isDepthFormat(attachment.getFormat())) ||
+				(isStencilFormat(attachment.getFormat()))) {
                 p_depthStencilCreateInfo = &depthStencilCreateInfo;
                 break;
             }
@@ -683,77 +700,38 @@ namespace vkcv
         );
 
         vk::Pipeline vkPipeline{};
-        if (m_Device.createGraphicsPipelines(nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) != vk::Result::eSuccess)
+        if (getCore().getContext().getDevice().createGraphicsPipelines(nullptr,
+																	   1,
+																	   &graphicsPipelineCreateInfo,
+																	   nullptr,
+																	   &vkPipeline) != vk::Result::eSuccess)
         {
             // Catch runtime error if the creation of the pipeline fails.
             // Destroy everything to keep the memory clean.
             destroyShaderModules();
-            return GraphicsPipelineHandle();
+            return {};
         }
 
         // Clean Up
         destroyShaderModules();
 
         // Hand over Handler to main Application
-        const uint64_t id = m_Pipelines.size();
-        m_Pipelines.push_back({ vkPipeline, vkPipelineLayout, config });
-        return GraphicsPipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); });
+        return add({ vkPipeline, vkPipelineLayout, config });
     }
 
-    vk::Pipeline GraphicsPipelineManager::getVkPipeline(const GraphicsPipelineHandle &handle) const
-    {
-        const uint64_t id = handle.getId();
-
-        if (id >= m_Pipelines.size()) {
-            return nullptr;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
+    vk::Pipeline GraphicsPipelineManager::getVkPipeline(const GraphicsPipelineHandle &handle) const {
+        auto& pipeline = (*this)[handle];
         return pipeline.m_handle;
     }
 
-    vk::PipelineLayout GraphicsPipelineManager::getVkPipelineLayout(const GraphicsPipelineHandle &handle) const
-    {
-        const uint64_t id = handle.getId();
-
-        if (id >= m_Pipelines.size()) {
-            return nullptr;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
+    vk::PipelineLayout GraphicsPipelineManager::getVkPipelineLayout(const GraphicsPipelineHandle &handle) const {
+		auto& pipeline = (*this)[handle];
         return pipeline.m_layout;
     }
 
-    void GraphicsPipelineManager::destroyPipelineById(uint64_t id) {
-        if (id >= m_Pipelines.size()) {
-            return;
-        }
-
-        auto& pipeline = m_Pipelines[id];
-
-        if (pipeline.m_handle) {
-            m_Device.destroy(pipeline.m_handle);
-            pipeline.m_handle = nullptr;
-        }
-
-        if (pipeline.m_layout) {
-            m_Device.destroy(pipeline.m_layout);
-            pipeline.m_layout = nullptr;
-        }
-    }
-
-    const GraphicsPipelineConfig& GraphicsPipelineManager::getPipelineConfig(const GraphicsPipelineHandle &handle) const
-    {
-        const uint64_t id = handle.getId();
-
-        if (id >= m_Pipelines.size()) {
-            static GraphicsPipelineConfig dummyConfig;
-            vkcv_log(LogLevel::ERROR, "Invalid handle");
-            return dummyConfig;
-        }
-
-        return m_Pipelines[id].m_config;
+    const GraphicsPipelineConfig& GraphicsPipelineManager::getPipelineConfig(const GraphicsPipelineHandle &handle) const {
+		auto& pipeline = (*this)[handle];
+        return pipeline.m_config;
     }
+	
 }
diff --git a/src/vkcv/GraphicsPipelineManager.hpp b/src/vkcv/GraphicsPipelineManager.hpp
index 511dcf47988028617712c191f79bdf1f292a200a..be18a4c983696eea177fbe6842e6f6ff57426b3a 100644
--- a/src/vkcv/GraphicsPipelineManager.hpp
+++ b/src/vkcv/GraphicsPipelineManager.hpp
@@ -10,23 +10,42 @@
 
 #include <vulkan/vulkan.hpp>
 #include <vector>
-#include "vkcv/Handles.hpp"
+#include "HandleManager.hpp"
 #include "vkcv/GraphicsPipelineConfig.hpp"
 #include "PassManager.hpp"
-#include "DescriptorManager.hpp"
+#include "DescriptorSetLayoutManager.hpp"
 
-namespace vkcv
-{
+namespace vkcv {
+	
+	struct GraphicsPipelineEntry {
+		vk::Pipeline m_handle;
+		vk::PipelineLayout m_layout;
+		GraphicsPipelineConfig m_config;
+	};
 	
 	/**
 	 * @brief Class to manage graphics pipelines.
 	 */
-    class GraphicsPipelineManager
-    {
+    class GraphicsPipelineManager : public HandleManager<GraphicsPipelineEntry, GraphicsPipelineHandle> {
+	private:
+		[[nodiscard]]
+		uint64_t getIdFrom(const GraphicsPipelineHandle& handle) const override;
+	
+		[[nodiscard]]
+		GraphicsPipelineHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+	
+		/**
+		 * Destroys and deallocates graphics pipeline represented by a given
+		 * graphics pipeline handle id.
+		 *
+		 * @param id Graphics pipeline handle id
+		 */
+		void destroyById(uint64_t id) override;
+		
     public:
-		GraphicsPipelineManager() = delete; // no default ctor
-        explicit GraphicsPipelineManager(vk::Device device, vk::PhysicalDevice physicalDevice) noexcept; // ctor
-        ~GraphicsPipelineManager() noexcept; // dtor
+        GraphicsPipelineManager() noexcept;
+		
+        ~GraphicsPipelineManager() noexcept override; // dtor
 	
 		GraphicsPipelineManager(const GraphicsPipelineManager &other) = delete; // copy-ctor
 		GraphicsPipelineManager(GraphicsPipelineManager &&other) = delete; // move-ctor;
@@ -46,7 +65,7 @@ namespace vkcv
          */
 		GraphicsPipelineHandle createPipeline(const GraphicsPipelineConfig &config,
 											  const PassManager& passManager,
-											  const DescriptorManager& descriptorManager);
+											  const DescriptorSetLayoutManager& descriptorManager);
 
         /**
          * Returns a vk::Pipeline object by handle.
@@ -71,20 +90,7 @@ namespace vkcv
          */
         [[nodiscard]]
         const GraphicsPipelineConfig &getPipelineConfig(const GraphicsPipelineHandle &handle) const;
-
-    private:
-        struct GraphicsPipeline {
-            vk::Pipeline m_handle;
-            vk::PipelineLayout m_layout;
-			GraphicsPipelineConfig m_config;
-        };
-
-        vk::Device                      m_Device;
-        vk::PhysicalDevice              m_physicalDevice; // needed to get infos to configure conservative rasterization
-        std::vector<GraphicsPipeline>   m_Pipelines;
-
-        void destroyPipelineById(uint64_t id);
-
+		
     };
 	
 }
diff --git a/src/vkcv/HandleManager.hpp b/src/vkcv/HandleManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..13213dc2a4ff5975458690ddc41e5a879580ee16
--- /dev/null
+++ b/src/vkcv/HandleManager.hpp
@@ -0,0 +1,116 @@
+#pragma once
+
+#include <optional>
+#include <vector>
+
+#include "vkcv/Handles.hpp"
+#include "vkcv/Logger.hpp"
+
+namespace vkcv {
+	
+	class Core;
+
+	template<typename T, typename H = Handle>
+	class HandleManager {
+		friend class Core;
+	private:
+		Core* m_core;
+		std::vector<T> m_entries;
+		
+	protected:
+		HandleManager() noexcept : m_core(nullptr), m_entries() {}
+		
+		virtual bool init(Core& core) {
+			if (m_core) {
+				vkcv_log(vkcv::LogLevel::ERROR, "Manager is already initialized");
+				return false;
+			}
+			
+			if (!m_entries.empty()) {
+				vkcv_log(vkcv::LogLevel::WARNING, "Entries added before initialization will be erased");
+			}
+			
+			m_core = &core;
+			m_entries.clear();
+			return true;
+		}
+		
+		[[nodiscard]]
+		const Core& getCore() const {
+			return *m_core;
+		}
+		
+		[[nodiscard]]
+		Core& getCore() {
+			return *m_core;
+		}
+		
+		[[nodiscard]]
+		size_t getCount() const {
+			return m_entries.size();
+		}
+		
+		[[nodiscard]]
+		const T& getById(uint64_t id) const {
+			if (id >= m_entries.size()) {
+				static T invalid;
+				vkcv_log(vkcv::LogLevel::ERROR, "Invalid handle id");
+				return invalid;
+			}
+			
+			return m_entries[id];
+		}
+		
+		[[nodiscard]]
+		T& getById(uint64_t id) {
+			if (id >= m_entries.size()) {
+				static T invalid;
+				vkcv_log(vkcv::LogLevel::ERROR, "Invalid handle id");
+				return invalid;
+			}
+			
+			return m_entries[id];
+		}
+		
+		virtual uint64_t getIdFrom(const H& handle) const = 0;
+		
+		[[nodiscard]]
+		virtual const T& operator[](const H& handle) const {
+			const Handle& _handle = handle;
+			return getById(getIdFrom(static_cast<const H&>(_handle)));
+		}
+		
+		[[nodiscard]]
+		virtual T& operator[](const H& handle) {
+			const Handle& _handle = handle;
+			return getById(getIdFrom(static_cast<const H&>(_handle)));
+		}
+		
+		H add(const T &entry) {
+			const uint64_t id = m_entries.size();
+			m_entries.push_back(entry);
+			return createById(id, [&](uint64_t id) { destroyById(id); });
+		}
+		
+		virtual H createById(uint64_t id, const HandleDestroyFunction& destroy) = 0;
+		
+		virtual void destroyById(uint64_t id) = 0;
+		
+		void clear() {
+			for (uint64_t id = 0; id < m_entries.size(); id++) {
+				destroyById(id);
+			}
+		}
+		
+	public:
+		HandleManager(HandleManager&& other) = delete;
+		HandleManager(const HandleManager& other) = delete;
+		
+		HandleManager& operator=(HandleManager&& other) = delete;
+		HandleManager& operator=(const HandleManager& other) = delete;
+		
+		virtual ~HandleManager() noexcept = default;
+		
+	};
+
+}
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 84a706f83a8f256b976f7b58546fbd7f2580f9f5..e17b95fe719805faa5b200686e529fc700400dd4 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -12,62 +12,59 @@ namespace vkcv{
 	
 	bool isDepthFormat(const vk::Format format) {
 		switch (format) {
-			case(vk::Format::eD16Unorm):        return true;
-			case(vk::Format::eD16UnormS8Uint):  return true;
-			case(vk::Format::eD24UnormS8Uint):  return true;
-			case(vk::Format::eD32Sfloat):       return true;
-			case(vk::Format::eD32SfloatS8Uint): return true;
-			default:                            return false;
+			case(vk::Format::eD16Unorm):
+			case(vk::Format::eD16UnormS8Uint):
+			case(vk::Format::eD24UnormS8Uint):
+			case(vk::Format::eD32Sfloat):
+			case(vk::Format::eD32SfloatS8Uint):
+				return true;
+			default:
+				return false;
 		}
 	}
-
-	Image Image::create(
-		ImageManager*   manager,
-		vk::Format      format,
-		uint32_t        width,
-		uint32_t        height,
-		uint32_t        depth,
-		uint32_t        mipCount,
-		bool            supportStorage,
-		bool            supportColorAttachment,
-		Multisampling   msaa)
-	{
-		return Image(
-			manager, 
-			manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment, msaa));
+	
+	bool isStencilFormat(const vk::Format format) {
+		switch (format) {
+			case(vk::Format::eS8Uint):
+			case(vk::Format::eD16UnormS8Uint):
+			case(vk::Format::eD24UnormS8Uint):
+			case(vk::Format::eD32SfloatS8Uint):
+				return true;
+			default:
+				return false;
+		}
 	}
 	
 	vk::Format Image::getFormat() const {
-		return m_manager->getImageFormat(m_handle);
+		return m_core->getImageFormat(m_handle);
 	}
 	
 	uint32_t Image::getWidth() const {
-		return m_manager->getImageWidth(m_handle);
+		return m_core->getImageWidth(m_handle);
 	}
 	
 	uint32_t Image::getHeight() const {
-		return m_manager->getImageHeight(m_handle);
+		return m_core->getImageHeight(m_handle);
 	}
 	
 	uint32_t Image::getDepth() const {
-		return m_manager->getImageDepth(m_handle);
+		return m_core->getImageDepth(m_handle);
 	}
 
-	void Image::switchLayout(vk::ImageLayout newLayout)
-	{
-		m_manager->switchImageLayoutImmediate(m_handle, newLayout);
+	void Image::switchLayout(vk::ImageLayout newLayout) {
+		m_core->switchImageLayout(m_handle, newLayout);
 	}
 
 	const vkcv::ImageHandle& Image::getHandle() const {
 		return m_handle;
 	}
 
-	uint32_t Image::getMipCount() const {
-		return m_manager->getImageMipCount(m_handle);
+	uint32_t Image::getMipLevels() const {
+		return m_core->getImageMipLevels(m_handle);
 	}
 
 	void Image::fill(const void *data, size_t size) {
-		m_manager->fillImage(m_handle, data, size);
+		m_core->fillImage(m_handle, data, size);
 	}
 
 	void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream,
@@ -75,9 +72,25 @@ namespace vkcv{
 		downsampler.recordDownsampling(cmdStream, m_handle);
 	}
 	
-	Image::Image(ImageManager* manager, const ImageHandle& handle) :
-		m_manager(manager),
-		m_handle(handle)
-	{}
+	Image image(Core &core,
+				vk::Format format,
+				uint32_t width,
+				uint32_t height,
+				uint32_t depth,
+				bool createMipChain,
+				bool supportStorage,
+				bool supportColorAttachment,
+				Multisampling multisampling) {
+		return Image(&core, core.createImage(
+				format,
+				width,
+				height,
+				depth,
+				createMipChain,
+				supportStorage,
+				supportColorAttachment,
+				multisampling
+		));
+	}
 
 }
diff --git a/src/vkcv/ImageConfig.cpp b/src/vkcv/ImageConfig.cpp
deleted file mode 100644
index 80aeac3d0f702467c8edc1b0f7378d0d7f15b3db..0000000000000000000000000000000000000000
--- a/src/vkcv/ImageConfig.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <vkcv/ImageConfig.hpp>
-#include <vkcv/Logger.hpp>
-
-namespace vkcv {
-	vk::SampleCountFlagBits msaaToVkSampleCountFlag(Multisampling msaa) {
-		switch (msaa) {
-		case Multisampling::None:   return vk::SampleCountFlagBits::e1;
-		case Multisampling::MSAA2X: return vk::SampleCountFlagBits::e2;
-		case Multisampling::MSAA4X: return vk::SampleCountFlagBits::e4;
-		case Multisampling::MSAA8X: return vk::SampleCountFlagBits::e8;
-		default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting"); return vk::SampleCountFlagBits::e1;
-		}
-	}
-
-	uint32_t msaaToSampleCount(Multisampling msaa) {
-		switch (msaa) {
-		case Multisampling::None:   return 1;
-		case Multisampling::MSAA2X: return 2;
-		case Multisampling::MSAA4X: return 4;
-		case Multisampling::MSAA8X: return 8;
-		default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting"); return 1;
-		}
-	}
-}
\ No newline at end of file
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 8783752834e95747ff462dfb1a3bec40d91bc175..fbe109da5ee10d43d011c0120abcdff78934a7d7 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -5,51 +5,147 @@
  */
 #include "ImageManager.hpp"
 #include "vkcv/Core.hpp"
+#include "vkcv/Image.hpp"
 #include "vkcv/Logger.hpp"
+#include "vkcv/Multisampling.hpp"
+#include "vkcv/TypeGuard.hpp"
 
 #include <algorithm>
 
 namespace vkcv {
-
-	/**
-	 * @brief searches memory type index for image allocation, combines requirements of image and application
-	 * @param physicalMemoryProperties Memory Properties of physical device
-	 * @param typeBits Bit field for suitable memory types
-	 * @param requirements Property flags that are required
-	 * @return memory type index for image
-	 */
-	uint32_t searchImageMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) {
-		const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount;
-		for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) {
-			const uint32_t memoryTypeBits = (1 << memoryIndex);
-			const bool isRequiredMemoryType = typeBits & memoryTypeBits;
-
-			const vk::MemoryPropertyFlags properties =
-				physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
-			const bool hasRequiredProperties =
-				(properties & requirements) == requirements;
-
-			if (isRequiredMemoryType && hasRequiredProperties)
-				return static_cast<int32_t>(memoryIndex);
+	
+	bool ImageManager::init(Core &core, BufferManager &bufferManager) {
+		if (!HandleManager<ImageEntry, ImageHandle>::init(core)) {
+			return false;
 		}
-
-		// failed to find memory type
-		return -1;
+		
+		m_bufferManager = &bufferManager;
+		m_swapchainImages.clear();
+		return true;
 	}
-
-	ImageManager::ImageManager(BufferManager& bufferManager) noexcept :
-		m_core(nullptr), m_bufferManager(bufferManager), m_images()
-	{
+	
+	uint64_t ImageManager::getIdFrom(const ImageHandle &handle) const {
+		return handle.getId();
 	}
-
-	ImageManager::~ImageManager() noexcept {
-		for (uint64_t id = 0; id < m_images.size(); id++) {
-			destroyImageById(id);
+	
+	ImageHandle ImageManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return ImageHandle(id, destroy);
+	}
+	
+	void ImageManager::destroyById(uint64_t id) {
+		auto& image = getById(id);
+		
+		const vk::Device& device = getCore().getContext().getDevice();
+		
+		for (auto& view : image.m_viewPerMip) {
+			if (view) {
+				device.destroyImageView(view);
+				view = nullptr;
+			}
+		}
+		
+		for (auto& view : image.m_arrayViewPerMip) {
+			if (view) {
+				device.destroyImageView(view);
+				view = nullptr;
+			}
+		}
+		
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
+		
+		if (image.m_handle) {
+			allocator.destroyImage(image.m_handle, image.m_allocation);
+			
+			image.m_handle = nullptr;
+			image.m_allocation = nullptr;
+		}
+	}
+	
+	const BufferManager &ImageManager::getBufferManager() const {
+		return *m_bufferManager;
+	}
+	
+	BufferManager &ImageManager::getBufferManager() {
+		return *m_bufferManager;
+	}
+	
+	void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer,
+														   const ImageHandle& handle) {
+		auto& image = (*this)[handle];
+		recordImageLayoutTransition(handle, 0, 0, vk::ImageLayout::eGeneral, cmdBuffer);
+		
+		vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ?
+										  vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor;
+		
+		uint32_t srcWidth = image.m_width;
+		uint32_t srcHeight = image.m_height;
+		uint32_t srcDepth = image.m_depth;
+		
+		auto half = [](uint32_t in) {
+			return std::max<uint32_t>(in / 2, 1);
+		};
+		
+		uint32_t dstWidth = half(srcWidth);
+		uint32_t dstHeight = half(srcHeight);
+		uint32_t dstDepth = half(srcDepth);
+		
+		for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) {
+			uint32_t dstMip = srcMip + 1;
+			vk::ImageBlit region(
+					vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) },
+					vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1),
+					{ vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) });
+			
+			cmdBuffer.blitImage(
+					image.m_handle,
+					vk::ImageLayout::eGeneral,
+					image.m_handle,
+					vk::ImageLayout::eGeneral,
+					region,
+					vk::Filter::eLinear);
+			
+			srcWidth = dstWidth;
+			srcHeight = dstHeight;
+			srcDepth = dstDepth;
+			
+			dstWidth = half(dstWidth);
+			dstHeight = half(dstHeight);
+			dstDepth = half(dstDepth);
+			
+			recordImageMemoryBarrier(handle, cmdBuffer);
+		}
+	}
+	
+	const ImageEntry &ImageManager::operator[](const ImageHandle &handle) const {
+		if (handle.isSwapchainImage()) {
+			return m_swapchainImages[m_currentSwapchainInputImage];
 		}
 		
+		return HandleManager<ImageEntry, ImageHandle>::operator[](handle);
+	}
+	
+	ImageEntry &ImageManager::operator[](const ImageHandle &handle) {
+		if (handle.isSwapchainImage()) {
+			return m_swapchainImages[m_currentSwapchainInputImage];
+		}
+		
+		return HandleManager<ImageEntry, ImageHandle>::operator[](handle);
+	}
+	
+	ImageManager::ImageManager() noexcept :
+			HandleManager<ImageEntry, ImageHandle>(),
+			m_bufferManager(nullptr),
+			m_swapchainImages(),
+			m_currentSwapchainInputImage(0)
+	{}
+	
+	ImageManager::~ImageManager() noexcept {
+		clear();
+		
 		for (const auto& swapchainImage : m_swapchainImages) {
 			for (const auto view : swapchainImage.m_viewPerMip) {
-				m_core->getContext().getDevice().destroy(view);
+				getCore().getContext().getDevice().destroy(view);
 			}
 		}
 	}
@@ -72,9 +168,8 @@ namespace vkcv {
 		uint32_t        mipCount,
 		bool            supportStorage, 
 		bool            supportColorAttachment,
-		Multisampling   msaa)
-	{
-		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
+		Multisampling   msaa) {
+		const vk::PhysicalDevice& physicalDevice = getCore().getContext().getPhysicalDevice();
 		
 		const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format);
 		
@@ -94,7 +189,7 @@ namespace vkcv {
 				imageTiling = vk::ImageTiling::eLinear;
 				
 				if (!(formatProperties.linearTilingFeatures & vk::FormatFeatureFlagBits::eStorageImage))
-					return ImageHandle();
+					return {};
 			}
 		}
 		
@@ -108,7 +203,7 @@ namespace vkcv {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
 		}
 
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 
 		vk::ImageType imageType = vk::ImageType::e3D;
 		vk::ImageViewType imageViewType = vk::ImageViewType::e3D;
@@ -134,7 +229,7 @@ namespace vkcv {
 		
 		if (!formatProperties.optimalTilingFeatures) {
 			if (!formatProperties.linearTilingFeatures)
-				return ImageHandle();
+				return {};
 			
 			imageTiling = vk::ImageTiling::eLinear;
 		}
@@ -148,8 +243,6 @@ namespace vkcv {
 		
 		const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers);
 
-		vk::SampleCountFlagBits sampleCountFlag = msaaToVkSampleCountFlag(msaa);
-
 		const vk::ImageCreateInfo imageCreateInfo (
 			createFlags,
 			imageType,
@@ -157,7 +250,7 @@ namespace vkcv {
 			vk::Extent3D(width, height, depth),
 			mipCount,
 			arrayLayers,
-			sampleCountFlag,
+			msaaToSampleCountFlagBits(msaa),
 			imageTiling,
 			imageUsageFlags,
 			vk::SharingMode::eExclusive,
@@ -189,7 +282,7 @@ namespace vkcv {
 			aspectFlags = vk::ImageAspectFlagBits::eColor;
 		}
 		
-		const vk::Device& device = m_core->getContext().getDevice();
+		const vk::Device& device = getCore().getContext().getDevice();
 		
 		std::vector<vk::ImageView> views;
 		std::vector<vk::ImageView> arrayViews;
@@ -242,25 +335,22 @@ namespace vkcv {
 			arrayViews.push_back(device.createImageView(imageViewCreateInfo));
 		}
 		
-		const uint64_t id = m_images.size();
-		m_images.push_back({
+		return add({
 			image,
 			allocation,
 			
 			views,
 			arrayViews,
-			
+	
 			width,
 			height,
 			depth,
-			
+	
 			format,
 			arrayLayers,
 			vk::ImageLayout::eUndefined,
 			supportStorage
 		});
-		
-		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
 	}
 	
 	ImageHandle ImageManager::createSwapchainImage() const {
@@ -268,38 +358,18 @@ namespace vkcv {
 	}
 	
 	vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const {
-
-		if (handle.isSwapchainImage()) {
-			m_swapchainImages[m_currentSwapchainInputImage].m_handle;
-		}
-
-		const uint64_t id = handle.getId();
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return nullptr;
-		}
-		
-		auto& image = m_images[id];
-		
+		auto& image = (*this)[handle];
 		return image.m_handle;
 	}
 	
 	vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const {
-
 		if (handle.isSwapchainImage()) {
 			vkcv_log(LogLevel::ERROR, "Swapchain image has no memory");
 			return nullptr;
 		}
-
-		const uint64_t id = handle.getId();
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return nullptr;
-		}
 		
-		auto& image = m_images[id];
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		auto& image = (*this)[handle];
+		const vma::Allocator& allocator = getCore().getContext().getAllocator();
 		
 		auto info = allocator.getAllocationInfo(
 				image.m_allocation
@@ -311,18 +381,11 @@ namespace vkcv {
 	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle,
 												   size_t mipLevel,
 												   bool arrayView) const {
-		
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0];
 		}
-
-		const uint64_t id = handle.getId();
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return nullptr;
-		}
 		
-		const auto& image = m_images[id];
+		const auto& image = (*this)[handle];
 		const auto& views = arrayView? image.m_arrayViewPerMip : image.m_viewPerMip;
 		
 		if (mipLevel >= views.size()) {
@@ -333,7 +396,7 @@ namespace vkcv {
 		return views[mipLevel];
 	}
 	
-	static vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image &image,
+	static vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageEntry &image,
 																	 uint32_t mipLevelCount,
 																	 uint32_t mipLevelOffset,
 																	 vk::ImageLayout newLayout) {
@@ -374,23 +437,14 @@ namespace vkcv {
 	}
 	
 	void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) {
-		uint64_t id = handle.getId();
-		
-		const bool isSwapchainImage = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainImage) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return;
-		}
-		
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
+		auto& image = (*this)[handle];
 		const auto transitionBarrier = createImageLayoutTransitionBarrier(image, 0, 0, newLayout);
 		
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Graphics;
+		auto& core = getCore();
+		auto stream = core.createCommandStream(QueueType::Graphics);
 		
-		m_core->recordAndSubmitCommandsImmediate(
-			submitInfo,
+		core.recordCommandsToStream(
+			stream,
 			[transitionBarrier](const vk::CommandBuffer& commandBuffer) {
 			// TODO: precise PipelineStageFlagBits, will require a lot of context
 			commandBuffer.pipelineBarrier(
@@ -405,6 +459,7 @@ namespace vkcv {
 			nullptr
 		);
 		
+		core.submitCommandStream(stream, false);
 		image.m_layout = newLayout;
 	}
 
@@ -413,15 +468,7 @@ namespace vkcv {
 												   uint32_t mipLevelOffset,
 												   vk::ImageLayout newLayout,
 												   vk::CommandBuffer cmdBuffer) {
-		const uint64_t id = handle.getId();
-		const bool isSwapchainImage = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainImage) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return;
-		}
-
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
+		auto& image = (*this)[handle];
 		const auto transitionBarrier = createImageLayoutTransitionBarrier(
 				image,
 				mipLevelCount,
@@ -441,19 +488,9 @@ namespace vkcv {
 		image.m_layout = newLayout;
 	}
 
-	void ImageManager::recordImageMemoryBarrier(
-		const ImageHandle& handle,
-		vk::CommandBuffer cmdBuffer) {
-
-		const uint64_t id = handle.getId();
-		const bool isSwapchainImage = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainImage) {
-			std::cerr << "Error: ImageManager::recordImageMemoryBarrier invalid handle" << std::endl;
-			return;
-		}
-
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
+	void ImageManager::recordImageMemoryBarrier(const ImageHandle& handle,
+												vk::CommandBuffer cmdBuffer) {
+		auto& image = (*this)[handle];
 		const auto transitionBarrier = createImageLayoutTransitionBarrier(image, 0, 0, image.m_layout);
 		
 		cmdBuffer.pipelineBarrier(
@@ -486,25 +523,14 @@ namespace vkcv {
 		}
 	}
 	
-	void ImageManager::fillImage(const ImageHandle& handle, const void* data, size_t size)
-	{
-		const uint64_t id = handle.getId();
-		
+	void ImageManager::fillImage(const ImageHandle& handle, const void* data, size_t size) {
 		if (handle.isSwapchainImage()) {
 			vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled");
 			return;
 		}
-
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return;
-		}
 		
-		auto& image = m_images[id];
-		
-		switchImageLayoutImmediate(
-				handle,
-				vk::ImageLayout::eTransferDstOptimal);
+		auto& image = (*this)[handle];
+		switchImageLayoutImmediate(handle, vk::ImageLayout::eTransferDstOptimal);
 		
 		const size_t image_size = (
 				image.m_width * image.m_height * image.m_depth *
@@ -513,23 +539,23 @@ namespace vkcv {
 		
 		const size_t max_size = std::min(size, image_size);
 		
-		BufferHandle bufferHandle = m_bufferManager.createBuffer(
+		BufferHandle bufferHandle = getBufferManager().createBuffer(
+				TypeGuard(1),
 				BufferType::STAGING,
-				max_size,
 				BufferMemoryType::DEVICE_LOCAL,
-				false,
+				max_size,
 				false
 		);
 		
-		m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0);
+		getBufferManager().fillBuffer(bufferHandle, data, max_size, 0);
 		
-		vk::Buffer stagingBuffer = m_bufferManager.getBuffer(bufferHandle);
+		vk::Buffer stagingBuffer = getBufferManager().getBuffer(bufferHandle);
 		
-		SubmitInfo submitInfo;
-		submitInfo.queueType = QueueType::Transfer;
+		auto& core = getCore();
+		auto stream = core.createCommandStream(QueueType::Transfer);
 		
-		m_core->recordAndSubmitCommandsImmediate(
-				submitInfo,
+		core.recordCommandsToStream(
+				stream,
 				[&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) {
 					vk::ImageAspectFlags aspectFlags;
 					
@@ -562,97 +588,28 @@ namespace vkcv {
 					);
 				},
 				[&]() {
-					switchImageLayoutImmediate(
-							handle,
-							vk::ImageLayout::eShaderReadOnlyOptimal
-					);
+					switchImageLayoutImmediate(handle,vk::ImageLayout::eShaderReadOnlyOptimal);
 				}
 		);
+		
+		core.submitCommandStream(stream, false);
 	}
 
-	void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle) {
-
-		const auto id = handle.getId();
-		if (id >= m_images.size()) {
-			vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle");
-			return;
-		}
-
-		auto& image = m_images[id];
-		recordImageLayoutTransition(handle, 0, 0, vk::ImageLayout::eGeneral, cmdBuffer);
-
-		vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ?
-			vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor;
-
-		uint32_t srcWidth = image.m_width;
-		uint32_t srcHeight = image.m_height;
-		uint32_t srcDepth = image.m_depth;
-
-		auto half = [](uint32_t in) {
-			return std::max<uint32_t>(in / 2, 1);
-		};
-
-		uint32_t dstWidth = half(srcWidth);
-		uint32_t dstHeight = half(srcHeight);
-		uint32_t dstDepth = half(srcDepth);
-
-		for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) {
-			uint32_t dstMip = srcMip + 1;
-			vk::ImageBlit region(
-				vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1),
-				{ vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) },
-				vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1),
-				{ vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) });
-
-			cmdBuffer.blitImage(
-				image.m_handle,
-				vk::ImageLayout::eGeneral,
-				image.m_handle,
-				vk::ImageLayout::eGeneral,
-				region,
-				vk::Filter::eLinear);
-
-			srcWidth = dstWidth;
-			srcHeight = dstHeight;
-			srcDepth = dstDepth;
-
-			dstWidth = half(dstWidth);
-			dstHeight = half(dstHeight);
-			dstDepth = half(dstDepth);
-
-			recordImageMemoryBarrier(handle, cmdBuffer);
-		}
-	}
-
-	void ImageManager::recordImageMipChainGenerationToCmdStream(
-		const vkcv::CommandStreamHandle& cmdStream,
-		const ImageHandle& handle) {
-
+	void ImageManager::recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream,
+																const ImageHandle& handle) {
 		const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) {
 			recordImageMipGenerationToCmdBuffer(cmdBuffer, handle);
 		};
-		m_core->recordCommandsToStream(cmdStream, record, nullptr);
+		
+		getCore().recordCommandsToStream(cmdStream, record, nullptr);
 	}
 
-	void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, ImageHandle dst) {
-
-		const uint64_t srcId = src.getId();
-		const uint64_t dstId = dst.getId();
-
-		const bool isSrcSwapchainImage = src.isSwapchainImage();
-		const bool isDstSwapchainImage = dst.isSwapchainImage();
-
-		const bool isSrcHandleInvalid = srcId >= m_images.size() && !isSrcSwapchainImage;
-		const bool isDstHandleInvalid = dstId >= m_images.size() && !isDstSwapchainImage;
-
-		if (isSrcHandleInvalid || isDstHandleInvalid) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return;
-		}
-
-		auto& srcImage = isSrcSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[srcId];
-		auto& dstImage = isDstSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[dstId];
-
+	void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer,
+										 const ImageHandle& src,
+										 const ImageHandle& dst) {
+		auto& srcImage = (*this)[src];
+		auto& dstImage = (*this)[dst];
+		
 		vk::ImageResolve region(
 			vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
 			vk::Offset3D(0, 0, 0),
@@ -672,152 +629,62 @@ namespace vkcv {
 	}
 
 	uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const {
-		const uint64_t id = handle.getId();
-		const bool isSwapchainImage = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainImage) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return 0;
-		}
-		
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
-		
+		auto& image = (*this)[handle];
 		return image.m_width;
 	}
 	
 	uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const {
-		const uint64_t id = handle.getId();
-		const bool isSwapchainImage = handle.isSwapchainImage();
-		
-		if (id >= m_images.size() && !isSwapchainImage) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return 0;
-		}
-		
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
-		
+		auto& image = (*this)[handle];
 		return image.m_height;
 	}
 	
 	uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const {
-		const uint64_t id = handle.getId();
-		const bool isSwapchainImage = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainImage) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return 0;
-		}
-		
-		auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id];
-		
+		auto& image = (*this)[handle];
 		return image.m_depth;
 	}
-	
-	void ImageManager::destroyImageById(uint64_t id)
-	{
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return;
-		}
-		
-		auto& image = m_images[id];
-
-		const vk::Device& device = m_core->getContext().getDevice();
-		
-		for (auto& view : image.m_viewPerMip) {
-			if (view) {
-				device.destroyImageView(view);
-				view = nullptr;
-			}
-		}
-		
-		for (auto& view : image.m_arrayViewPerMip) {
-			if (view) {
-				device.destroyImageView(view);
-				view = nullptr;
-			}
-		}
-		
-		const vma::Allocator& allocator = m_core->getContext().getAllocator();
-
-		if (image.m_handle) {
-			allocator.destroyImage(image.m_handle, image.m_allocation);
-			
-			image.m_handle = nullptr;
-			image.m_allocation = nullptr;
-		}
-	}
 
 	vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const {
-
-		const uint64_t id = handle.getId();
-		const bool isSwapchainFormat = handle.isSwapchainImage();
-
-		if (id >= m_images.size() && !isSwapchainFormat) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return vk::Format::eUndefined;
-		}
-
-		return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format;
+		auto& image = (*this)[handle];
+		return image.m_format;
 	}
 	
 	bool ImageManager::isImageSupportingStorage(const ImageHandle& handle) const {
-		const uint64_t id = handle.getId();
-		const bool isSwapchainFormat = handle.isSwapchainImage();
-		
-		if (isSwapchainFormat) {
-			return false;
-		}
-		
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
+		if (handle.isSwapchainImage()) {
 			return false;
 		}
 		
-		return m_images[id].m_storage;
+		auto& image = (*this)[handle];
+		return image.m_storage;
 	}
 
 	uint32_t ImageManager::getImageMipCount(const ImageHandle& handle) const {
-		const uint64_t id = handle.getId();
-
 		if (handle.isSwapchainImage()) {
 			return 1;
 		}
-
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return 0;
-		}
-
-		return m_images[id].m_viewPerMip.size();
+		
+		auto& image = (*this)[handle];
+		return image.m_viewPerMip.size();
 	}
 	
 	uint32_t ImageManager::getImageArrayLayers(const ImageHandle& handle) const {
-		const uint64_t id = handle.getId();
-		
-		if (handle.isSwapchainImage()) {
-			return m_swapchainImages[0].m_layers;
-		}
-		
-		if (id >= m_images.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid handle");
-			return 0;
-		}
-		
-		return m_images[id].m_layers;
+		auto& image = (*this)[handle];
+		return image.m_layers;
 	}
 
 	void ImageManager::setCurrentSwapchainImageIndex(int index) {
 		m_currentSwapchainInputImage = index;
 	}
 
-	void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, const std::vector<vk::ImageView>& views,
-		uint32_t width, uint32_t height, vk::Format format) {
+	void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images,
+										  const std::vector<vk::ImageView>& views,
+										  uint32_t width,
+										  uint32_t height,
+										  vk::Format format) {
 
 		// destroy old views
 		for (const auto& image : m_swapchainImages) {
 			for (const auto& view : image.m_viewPerMip) {
-				m_core->getContext().getDevice().destroyImageView(view);
+				getCore().getContext().getDevice().destroyImageView(view);
 			}
 		}
 
@@ -840,20 +707,10 @@ namespace vkcv {
 		}
 	}
 
-	void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle& handle, const vk::ImageLayout layout) {
-		const uint64_t id = handle.getId();
-
-		if (handle.isSwapchainImage()) {
-			m_swapchainImages[m_currentSwapchainInputImage].m_layout = layout;
-		}
-		else {
-			if (id >= m_images.size()) {
-				vkcv_log(LogLevel::ERROR, "Invalid handle");
-				return;
-			}
-			m_swapchainImages[id].m_layout = layout;
-		}
-		
+	void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle& handle,
+											   const vk::ImageLayout layout) {
+		auto& image = (*this)[handle];
+		image.m_layout = layout;
 	}
 
 }
\ No newline at end of file
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 3e577fd9a61fea6334faaf136386bf845ca387e8..de91245c76a76ea0658a1fb69472d5f9059215e6 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -8,9 +8,9 @@
 #include <vulkan/vulkan.hpp>
 #include <vk_mem_alloc.hpp>
 
-#include "vkcv/BufferManager.hpp"
-#include "vkcv/Handles.hpp"
-#include "vkcv/ImageConfig.hpp"
+#include "BufferManager.hpp"
+#include "HandleManager.hpp"
+#include "vkcv/Multisampling.hpp"
 
 namespace vkcv {
 	
@@ -22,44 +22,44 @@ namespace vkcv {
 	 * @return True, if the format is usable for depth buffers, otherwise false.
 	 */
 	bool isDepthImageFormat(vk::Format format);
+	
+	struct ImageEntry {
+		vk::Image                   m_handle;
+		vma::Allocation             m_allocation;
+		
+		std::vector<vk::ImageView>  m_viewPerMip;
+		std::vector<vk::ImageView>  m_arrayViewPerMip;
+		
+		uint32_t                    m_width;
+		uint32_t                    m_height;
+		uint32_t                    m_depth;
+		
+		vk::Format                  m_format;
+		uint32_t                    m_layers;
+		vk::ImageLayout             m_layout;
+		bool 						m_storage;
+	};
 
 	/**
 	 * @brief Class to manage the creation, destruction, allocation
 	 * and filling of images.
 	 */
-	class ImageManager
+	class ImageManager : HandleManager<ImageEntry, ImageHandle>
 	{
 		friend class Core;
-	public:
-		struct Image
-		{
-			vk::Image                   m_handle;
-			vma::Allocation             m_allocation;
-			
-			std::vector<vk::ImageView>  m_viewPerMip;
-			std::vector<vk::ImageView>  m_arrayViewPerMip;
-			
-			uint32_t                    m_width;
-			uint32_t                    m_height;
-			uint32_t                    m_depth;
-			
-			vk::Format                  m_format;
-			uint32_t                    m_layers;
-			vk::ImageLayout             m_layout;
-			bool 						m_storage;
-		private:
-			friend ImageManager;
-		};
 	private:
+		BufferManager* m_bufferManager;
 		
-		Core* m_core;
-		BufferManager& m_bufferManager;
-		
-		std::vector<Image> m_images;
-		std::vector<Image> m_swapchainImages;
+		std::vector<ImageEntry> m_swapchainImages;
 		int m_currentSwapchainInputImage;
 		
-		explicit ImageManager(BufferManager& bufferManager) noexcept;
+		bool init(Core& core, BufferManager& bufferManager);
+		
+		[[nodiscard]]
+		uint64_t getIdFrom(const ImageHandle& handle) const override;
+		
+		[[nodiscard]]
+		ImageHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
 		
 		/**
 		 * Destroys and deallocates image represented by a given
@@ -67,18 +67,29 @@ namespace vkcv {
 		 *
 		 * @param id Image handle id
 		 */
-		void destroyImageById(uint64_t id);
+		void destroyById(uint64_t id) override;
+		
+		[[nodiscard]]
+		const BufferManager& getBufferManager() const;
+		
+		[[nodiscard]]
+		BufferManager& getBufferManager();
 
 		void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle);
-
+		
+	protected:
+		[[nodiscard]]
+		virtual const ImageEntry& operator[](const ImageHandle& handle) const override;
+		
+		[[nodiscard]]
+		virtual ImageEntry& operator[](const ImageHandle& handle) override;
+		
 	public:
-		~ImageManager() noexcept;
-		ImageManager(ImageManager&& other) = delete;
-		ImageManager(const ImageManager& other) = delete;
-
-		ImageManager& operator=(ImageManager&& other) = delete;
-		ImageManager& operator=(const ImageManager& other) = delete;
+		ImageManager() noexcept;
+		
+		~ImageManager() noexcept override;
 		
+		[[nodiscard]]
 		ImageHandle createImage(
 			uint32_t        width, 
 			uint32_t        height, 
@@ -103,22 +114,28 @@ namespace vkcv {
 										 size_t mipLevel = 0,
 										 bool arrayView = false) const;
 
-		void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout);
-		
-		void recordImageLayoutTransition(
-			const ImageHandle& handle,
-			uint32_t mipLevelCount,
-			uint32_t mipLevelOffset,
-			vk::ImageLayout newLayout, 
-			vk::CommandBuffer cmdBuffer);
+		void switchImageLayoutImmediate(const ImageHandle& handle,
+										vk::ImageLayout newLayout);
+		
+		void recordImageLayoutTransition(const ImageHandle& handle,
+										 uint32_t mipLevelCount,
+										 uint32_t mipLevelOffset,
+										 vk::ImageLayout newLayout,
+										 vk::CommandBuffer cmdBuffer);
 
-		void recordImageMemoryBarrier(
-			const ImageHandle& handle,
-			vk::CommandBuffer cmdBuffer);
+		void recordImageMemoryBarrier(const ImageHandle& handle,
+									  vk::CommandBuffer cmdBuffer);
 
-		void fillImage(const ImageHandle& handle, const void* data, size_t size);
-		void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle);
-		void recordMSAAResolve(vk::CommandBuffer cmdBuffer, ImageHandle src, ImageHandle dst);
+		void fillImage(const ImageHandle& handle,
+					   const void* data,
+					   size_t size);
+		
+		void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream,
+													  const ImageHandle& handle);
+		
+		void recordMSAAResolve(vk::CommandBuffer cmdBuffer,
+							   const ImageHandle& src,
+							   const ImageHandle& dst);
 
 		[[nodiscard]]
 		uint32_t getImageWidth(const ImageHandle& handle) const;
@@ -143,12 +160,16 @@ namespace vkcv {
 
 		void setCurrentSwapchainImageIndex(int index);
 		
-		void setSwapchainImages(const std::vector<vk::Image>& images, const std::vector<vk::ImageView>& views,
-								uint32_t width, uint32_t height, vk::Format format);
+		void setSwapchainImages(const std::vector<vk::Image>& images,
+								const std::vector<vk::ImageView>& views,
+								uint32_t width,
+								uint32_t height,
+								vk::Format format);
 
 		// if manual vulkan work, e.g. ImGui integration, changes an image layout this function must be used
 		// to update the internal image state
-		void updateImageLayoutManual(const vkcv::ImageHandle& handle, const vk::ImageLayout layout);
+		void updateImageLayoutManual(const vkcv::ImageHandle& handle,
+									 const vk::ImageLayout layout);
 
 	};
 }
\ No newline at end of file
diff --git a/src/vkcv/Multisampling.cpp b/src/vkcv/Multisampling.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b64dd06cbfbabf74ca777a840512d6eeffeb1ed
--- /dev/null
+++ b/src/vkcv/Multisampling.cpp
@@ -0,0 +1,38 @@
+#include <vkcv/Multisampling.hpp>
+#include <vkcv/Logger.hpp>
+
+namespace vkcv {
+	
+	vk::SampleCountFlagBits msaaToSampleCountFlagBits(Multisampling msaa) {
+		switch (msaa) {
+			case Multisampling::None:
+				return vk::SampleCountFlagBits::e1;
+			case Multisampling::MSAA2X:
+				return vk::SampleCountFlagBits::e2;
+			case Multisampling::MSAA4X:
+				return vk::SampleCountFlagBits::e4;
+			case Multisampling::MSAA8X:
+				return vk::SampleCountFlagBits::e8;
+			default:
+				vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting");
+				return vk::SampleCountFlagBits::e1;
+		}
+	}
+
+	uint32_t msaaToSampleCount(Multisampling msaa) {
+		switch (msaa) {
+			case Multisampling::None:
+				return 1;
+			case Multisampling::MSAA2X:
+				return 2;
+			case Multisampling::MSAA4X:
+				return 4;
+			case Multisampling::MSAA8X:
+				return 8;
+			default:
+				vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting");
+				return 1;
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/src/vkcv/Pass.cpp b/src/vkcv/Pass.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..54482cd9d3189fd1e4ed5a15a07163b079105db4
--- /dev/null
+++ b/src/vkcv/Pass.cpp
@@ -0,0 +1,47 @@
+
+#include "vkcv/Pass.hpp"
+
+namespace vkcv {
+	
+	PassHandle passFormats(Core &core,
+						   const std::vector<vk::Format> formats,
+						   bool clear,
+						   Multisampling multisampling) {
+		AttachmentDescriptions attachments;
+		
+		for (const auto format : formats) {
+			attachments.emplace_back(
+					format,
+					clear? AttachmentOperation::CLEAR : AttachmentOperation::LOAD,
+					AttachmentOperation::STORE
+			);
+		}
+		
+		const PassConfig config (attachments, multisampling);
+		return core.createPass(config);
+	}
+	
+	PassHandle passFormat(Core &core,
+						  vk::Format format,
+						  bool clear,
+						  Multisampling multisampling) {
+		return passFormats(core, { format }, clear, multisampling);
+	}
+	
+	PassHandle passSwapchain(Core &core,
+							 const SwapchainHandle &swapchain,
+							 const std::vector<vk::Format> formats,
+							 bool clear,
+							 Multisampling multisampling) {
+		std::vector<vk::Format> swapchainFormats (formats);
+		
+		for (auto& format : swapchainFormats) {
+			if (vk::Format::eUndefined == format) {
+				format = core.getSwapchainFormat(swapchain);
+			}
+		}
+		
+		return passFormats(core, swapchainFormats, clear, multisampling);
+	}
+	
+}
diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..54f3f17f080ec01207a162532dc140e9abd92dd3
--- /dev/null
+++ b/src/vkcv/PassConfig.cpp
@@ -0,0 +1,82 @@
+
+#include "vkcv/PassConfig.hpp"
+#include "vkcv/Image.hpp"
+
+namespace vkcv
+{
+	
+	AttachmentDescription::AttachmentDescription(vk::Format format,
+												 AttachmentOperation load,
+												 AttachmentOperation store)
+			: m_format(format),
+			  m_load_op(load),
+			  m_store_op(store),
+			  m_clear_value()
+	{
+		if (isDepthFormat(format)) {
+			setClearValue(vk::ClearValue(
+					vk::ClearDepthStencilValue(1.0f, 0)
+			));
+		} else {
+			setClearValue(vk::ClearValue(
+					vk::ClearColorValue(std::array<float, 4>{
+							0.0f, 0.0f, 0.0f, 0.0f
+					})
+			));
+		}
+	}
+	
+	AttachmentDescription::AttachmentDescription(vk::Format format,
+												 AttachmentOperation load,
+												 AttachmentOperation store,
+												 const vk::ClearValue &clear)
+	: m_format(format),
+	  m_load_op(load),
+	  m_store_op(store),
+	  m_clear_value(clear)
+	{}
+	
+	vk::Format AttachmentDescription::getFormat() const {
+		return m_format;
+	}
+	
+	AttachmentOperation AttachmentDescription::getLoadOperation() const {
+		return m_load_op;
+	}
+	
+	AttachmentOperation AttachmentDescription::getStoreOperation() const {
+		return m_store_op;
+	}
+	
+	void AttachmentDescription::setClearValue(const vk::ClearValue &clear) {
+		m_clear_value = clear;
+	}
+	
+	const vk::ClearValue &AttachmentDescription::getClearValue() const {
+		return m_clear_value;
+	}
+	
+	PassConfig::PassConfig()
+	: m_attachments(),
+	  m_multisampling(Multisampling::None)
+	{}
+	
+	PassConfig::PassConfig(const AttachmentDescriptions &attachments,
+						   Multisampling multisampling)
+	: m_attachments(attachments),
+	  m_multisampling(multisampling)
+	{}
+	
+	const AttachmentDescriptions &PassConfig::getAttachments() const {
+		return m_attachments;
+	}
+	
+	void PassConfig::setMultisampling(Multisampling multisampling) {
+		m_multisampling = multisampling;
+	}
+	
+	Multisampling PassConfig::getMultisampling() const {
+		return m_multisampling;
+	}
+	
+}
diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp
index 5eb727c75f9ec7e6f4cc3fa8fd8118a1a05f036f..75343f4cecea4ac3d46180ccab4cf09d7a43a2a3 100644
--- a/src/vkcv/PassManager.cpp
+++ b/src/vkcv/PassManager.cpp
@@ -1,5 +1,6 @@
 #include "PassManager.hpp"
 #include "vkcv/Image.hpp"
+#include "vkcv/Core.hpp"
 
 namespace vkcv
 {
@@ -27,129 +28,147 @@ namespace vkcv
                 return vk::AttachmentLoadOp::eDontCare;
         }
     }
-
-    PassManager::PassManager(vk::Device device) noexcept :
-    m_Device{device},
-    m_Passes{}
-    {}
-
-    PassManager::~PassManager() noexcept
-    {
-    	for (uint64_t id = 0; id < m_Passes.size(); id++) {
-			destroyPassById(id);
-    	}
+	
+	uint64_t PassManager::getIdFrom(const PassHandle &handle) const {
+		return handle.getId();
+	}
+	
+	PassHandle PassManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return PassHandle(id, destroy);
+	}
+	
+	void PassManager::destroyById(uint64_t id) {
+		auto& pass = getById(id);
+		
+		if (pass.m_Handle) {
+			getCore().getContext().getDevice().destroy(pass.m_Handle);
+			pass.m_Handle = nullptr;
+		}
+	}
+	
+	PassManager::PassManager() noexcept : HandleManager<PassEntry, PassHandle>() {}
+	
+	PassManager::~PassManager() noexcept {
+    	clear();
     }
 
-    PassHandle PassManager::createPass(const PassConfig &config)
-    {
+    PassHandle PassManager::createPass(const PassConfig &config) {
         // description of all {color, input, depth/stencil} attachments of the render pass
         std::vector<vk::AttachmentDescription> attachmentDescriptions{};
 
         // individual references to color attachments (of a subpass)
         std::vector<vk::AttachmentReference> colorAttachmentReferences{};
         // individual reference to depth attachment (of a subpass)
-        vk::AttachmentReference depthAttachmentReference{};
-        vk::AttachmentReference *pDepthAttachment = nullptr;	//stays nullptr if no depth attachment used
+        vk::AttachmentReference depthStencilAttachmentRef;
+		
+		// stays nullptr if no depth attachment used
+        vk::AttachmentReference *pDepthStencil = nullptr;
+		
+		const auto &featureManager = getCore().getContext().getFeatureManager();
+	
+		const bool separateDepthStencil = (
+			featureManager.checkFeatures<vk::PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR>(
+				vk::StructureType::ePhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR,
+				[](const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR& features) {
+					return features.separateDepthStencilLayouts;
+				}
+			)
+		);
+		
+		const auto& attachments = config.getAttachments();
+		
+		std::vector<vk::ImageLayout> layouts;
+		layouts.reserve(attachments.size());
 
-        for (uint32_t i = 0; i < config.attachments.size(); i++)
-        {
-            // TODO: Renderpass struct should hold proper format information
-            vk::Format      format = config.attachments[i].format;
+        for (uint32_t i = 0; i < attachments.size(); i++) {
+            vk::Format format = attachments[i].getFormat();
             vk::ImageLayout layout;
-
-            if (isDepthFormat(config.attachments[i].format))
-            {
-                layout                              = vk::ImageLayout::eDepthStencilAttachmentOptimal;
-                depthAttachmentReference.attachment = i;
-                depthAttachmentReference.layout     = layout;
-                pDepthAttachment                    = &depthAttachmentReference;
-            }
-            else
-            {
-                layout = vk::ImageLayout::eColorAttachmentOptimal;
-                vk::AttachmentReference attachmentRef(i, layout);
+			
+			bool depthFormat = isDepthFormat(attachments[i].getFormat());
+			bool stencilFormat = isStencilFormat(attachments[i].getFormat());
+			
+            if ((separateDepthStencil) && (depthFormat) && (!stencilFormat)) {
+				layout = vk::ImageLayout::eDepthAttachmentOptimal;
+			} else
+			if ((separateDepthStencil) && (!depthFormat) && (stencilFormat)) {
+				layout = vk::ImageLayout::eStencilAttachmentOptimal;
+			} else
+			if ((depthFormat) || (stencilFormat)) {
+				layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
+			} else {
+				layout = vk::ImageLayout::eColorAttachmentOptimal;
+			}
+			
+			if ((depthFormat) || (stencilFormat)) {
+				depthStencilAttachmentRef = vk::AttachmentReference(i, layout);
+				pDepthStencil = &depthStencilAttachmentRef;
+            } else {
+                vk::AttachmentReference attachmentRef (i, layout);
                 colorAttachmentReferences.push_back(attachmentRef);
             }
 
-            vk::AttachmentDescription attachmentDesc(
-                {},
-                format,
-                msaaToVkSampleCountFlag(config.msaa),
-                getVKLoadOpFromAttachOp(config.attachments[i].load_operation),
-                getVkStoreOpFromAttachOp(config.attachments[i].store_operation),
-                vk::AttachmentLoadOp::eDontCare,
-                vk::AttachmentStoreOp::eDontCare,
-                layout,
-                layout);
+            vk::AttachmentDescription attachmentDesc (
+					{},
+					format,
+					msaaToSampleCountFlagBits(config.getMultisampling()),
+					getVKLoadOpFromAttachOp(attachments[i].getLoadOperation()),
+					getVkStoreOpFromAttachOp(attachments[i].getStoreOperation()),
+					vk::AttachmentLoadOp::eDontCare,
+					vk::AttachmentStoreOp::eDontCare,
+					layout,
+					layout
+			);
+	
+			if (stencilFormat) {
+				attachmentDesc.setStencilLoadOp(attachmentDesc.loadOp);
+				attachmentDesc.setStencilStoreOp(attachmentDesc.storeOp);
+			}
 
             attachmentDescriptions.push_back(attachmentDesc);
+			layouts.push_back(layout);
         }
         
-        const vk::SubpassDescription subpassDescription(
-            {},
-            vk::PipelineBindPoint::eGraphics,
-            0,
-            {},
-            static_cast<uint32_t>(colorAttachmentReferences.size()),
-            colorAttachmentReferences.data(),
-            {},
-            pDepthAttachment,
-            0,
-            {});
+        const vk::SubpassDescription subpassDescription (
+				{},
+				vk::PipelineBindPoint::eGraphics,
+				0,
+				{},
+				static_cast<uint32_t>(colorAttachmentReferences.size()),
+				colorAttachmentReferences.data(),
+				{},
+				pDepthStencil,
+				0,
+				{}
+		);
 
-        const vk::RenderPassCreateInfo passInfo(
-            {},
-            static_cast<uint32_t>(attachmentDescriptions.size()),
-            attachmentDescriptions.data(),
-            1,
-            &subpassDescription,
-            0,
-            {});
+        const vk::RenderPassCreateInfo passInfo (
+				{},
+				static_cast<uint32_t>(attachmentDescriptions.size()),
+				attachmentDescriptions.data(),
+				1,
+				&subpassDescription,
+				0,
+				{}
+		);
 
-        vk::RenderPass renderPass = m_Device.createRenderPass(passInfo);
+        vk::RenderPass renderPass = getCore().getContext().getDevice().createRenderPass(passInfo);
 
-        const uint64_t id = m_Passes.size();
-        m_Passes.push_back({ renderPass, config });
-        return PassHandle(id, [&](uint64_t id) { destroyPassById(id); });
+        return add({ renderPass, config, layouts });
     }
 
-    vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const
-    {
-    	const uint64_t id = handle.getId();
-    	
-    	if (id >= m_Passes.size()) {
-    		return nullptr;
-    	}
-    	
-    	auto& pass = m_Passes[id];
-    	
+    vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const {
+    	auto& pass = (*this)[handle];
         return pass.m_Handle;
     }
     
     const PassConfig& PassManager::getPassConfig(const PassHandle &handle) const {
-		const uint64_t id = handle.getId();
-	
-		if (id >= m_Passes.size()) {
-			static PassConfig emptyConfig = PassConfig({});
-			return emptyConfig;
-		}
-	
-		auto& pass = m_Passes[id];
-	
+		auto& pass = (*this)[handle];
 		return pass.m_Config;
     }
-    
-    void PassManager::destroyPassById(uint64_t id) {
-    	if (id >= m_Passes.size()) {
-    		return;
-    	}
-    	
-    	auto& pass = m_Passes[id];
 	
-		if (pass.m_Handle) {
-			m_Device.destroy(pass.m_Handle);
-			pass.m_Handle = nullptr;
-		}
-    }
+	const std::vector<vk::ImageLayout>& PassManager::getLayouts(const PassHandle &handle) const {
+		auto& pass = (*this)[handle];
+		return pass.m_Layouts;
+	}
     
 }
diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp
index 1ef976e0482ea39afad33346068f2a168eac70d8..f8dc653c1017ff776d70f09415a3a10fff209695 100644
--- a/src/vkcv/PassManager.hpp
+++ b/src/vkcv/PassManager.hpp
@@ -2,39 +2,45 @@
 
 #include <vulkan/vulkan.hpp>
 #include <vector>
-#include "vkcv/Handles.hpp"
+
+#include "HandleManager.hpp"
 #include "vkcv/PassConfig.hpp"
 
 namespace vkcv
 {
 	
+	struct PassEntry {
+		vk::RenderPass m_Handle;
+		PassConfig m_Config;
+		std::vector<vk::ImageLayout> m_Layouts;
+	};
+	
 	/**
 	 * @brief Class to manage the creation and destruction of passes.
 	 */
-    class PassManager
-    {
+    class PassManager : public HandleManager<PassEntry, PassHandle> {
+		friend class Core;
     private:
-    	struct Pass {
-			vk::RenderPass m_Handle;
-			PassConfig m_Config;
-    	};
-    	
-        vk::Device m_Device;
-        std::vector<Pass> m_Passes;
-        
-        void destroyPassById(uint64_t id);
+		[[nodiscard]]
+		uint64_t getIdFrom(const PassHandle& handle) const override;
+	
+		[[nodiscard]]
+		PassHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+	
+		/**
+		 * Destroys and deallocates pass represented by a given
+		 * pass handle id.
+		 *
+		 * @param id Pass handle id
+		 */
+		void destroyById(uint64_t id) override;
         
     public:
-        PassManager() = delete; // no default ctor
-        explicit PassManager(vk::Device device) noexcept; // ctor
-        ~PassManager() noexcept; // dtor
-
-        PassManager(const PassManager &other) = delete; // copy-ctor
-        PassManager(PassManager &&other) = delete; // move-ctor;
-
-        PassManager & operator=(const PassManager &other) = delete; // copy-assign op
-        PassManager & operator=(PassManager &&other) = delete; // move-assign op
-
+		PassManager() noexcept;
+		
+        ~PassManager() noexcept override; // dtor
+	
+		[[nodiscard]]
         PassHandle createPass(const PassConfig &config);
 
         [[nodiscard]]
@@ -42,6 +48,9 @@ namespace vkcv
         
         [[nodiscard]]
         const PassConfig& getPassConfig(const PassHandle &handle) const;
+	
+		[[nodiscard]]
+		const std::vector<vk::ImageLayout>& getLayouts(const PassHandle &handle) const;
         
     };
 	
diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7585f9fe6d95eaf0409eb187d9da43162770719f
--- /dev/null
+++ b/src/vkcv/PipelineConfig.cpp
@@ -0,0 +1,36 @@
+
+#include "vkcv/PipelineConfig.hpp"
+
+namespace vkcv {
+	
+	PipelineConfig::PipelineConfig() : m_ShaderProgram(), m_DescriptorSetLayouts() {}
+	
+	PipelineConfig::PipelineConfig(const ShaderProgram &program,
+								   const std::vector<DescriptorSetLayoutHandle> &layouts)
+								   : m_ShaderProgram(program), m_DescriptorSetLayouts(layouts) {}
+	
+	void PipelineConfig::setShaderProgram(const ShaderProgram& program) {
+		m_ShaderProgram = program;
+	}
+	
+	const ShaderProgram &PipelineConfig::getShaderProgram() const {
+		return m_ShaderProgram;
+	}
+	
+	void PipelineConfig::addDescriptorSetLayout(const DescriptorSetLayoutHandle &layout) {
+		m_DescriptorSetLayouts.push_back(layout);
+	}
+	
+	void PipelineConfig::addDescriptorSetLayouts(const std::vector<DescriptorSetLayoutHandle> &layouts) {
+		m_DescriptorSetLayouts.reserve(m_DescriptorSetLayouts.size() + layouts.size());
+		
+		for (const auto& layout : layouts) {
+			m_DescriptorSetLayouts.push_back(layout);
+		}
+	}
+	
+	const std::vector<DescriptorSetLayoutHandle> &PipelineConfig::getDescriptorSetLayouts() const {
+		return m_DescriptorSetLayouts;
+	}
+	
+}
diff --git a/src/vkcv/PushConstants.cpp b/src/vkcv/PushConstants.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5492ba2e93df1eeceaf70a8dcca4841f6243cc43
--- /dev/null
+++ b/src/vkcv/PushConstants.cpp
@@ -0,0 +1,44 @@
+#include <vkcv/PushConstants.hpp>
+
+namespace vkcv {
+	
+	PushConstants::PushConstants(size_t sizePerDrawcall) :
+	m_typeGuard(sizePerDrawcall),
+	m_data()
+	{}
+	
+	PushConstants::PushConstants(const TypeGuard &guard) :
+	m_typeGuard(guard),
+	m_data()
+	{}
+	
+	size_t PushConstants::getSizePerDrawcall() const {
+		return m_typeGuard.typeSize();
+	}
+	
+	size_t PushConstants::getFullSize() const {
+		return m_data.size();
+	}
+	
+	size_t PushConstants::getDrawcallCount() const {
+		return getFullSize() / getSizePerDrawcall();
+	}
+	
+	void PushConstants::clear() {
+		m_data.clear();
+	}
+
+	const void* PushConstants::getDrawcallData(size_t index) const {
+		const size_t offset = (index * getSizePerDrawcall());
+		return reinterpret_cast<const void*>(m_data.data() + offset);
+	}
+
+	const void* PushConstants::getData() const {
+		if (m_data.empty()) {
+			return nullptr;
+		} else {
+			return m_data.data();
+		}
+	}
+
+}
diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp
index d2a4d593b6a3648e9d0f01404643999ed406c26d..c818769e3ddca9b23134db1cf638f8852b554253 100644
--- a/src/vkcv/QueueManager.cpp
+++ b/src/vkcv/QueueManager.cpp
@@ -5,7 +5,6 @@
 
 #include "vkcv/QueueManager.hpp"
 #include "vkcv/Logger.hpp"
-#include "vkcv/Swapchain.hpp"
 
 namespace vkcv {
 
diff --git a/src/vkcv/Sampler.cpp b/src/vkcv/Sampler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f122608afa6445bd59735be5c3d2cedc2a5e3d08
--- /dev/null
+++ b/src/vkcv/Sampler.cpp
@@ -0,0 +1,28 @@
+
+#include "vkcv/Sampler.hpp"
+
+namespace vkcv {
+	
+	SamplerHandle samplerLinear(Core &core, bool clampToEdge) {
+		return core.createSampler(
+				vkcv::SamplerFilterType::LINEAR,
+				vkcv::SamplerFilterType::LINEAR,
+				vkcv::SamplerMipmapMode::LINEAR,
+				clampToEdge?
+					vkcv::SamplerAddressMode::CLAMP_TO_EDGE :
+					vkcv::SamplerAddressMode::REPEAT
+		);
+	}
+	
+	SamplerHandle samplerNearest(Core &core, bool clampToEdge) {
+		return core.createSampler(
+				vkcv::SamplerFilterType::NEAREST,
+				vkcv::SamplerFilterType::NEAREST,
+				vkcv::SamplerMipmapMode::NEAREST,
+				clampToEdge?
+				vkcv::SamplerAddressMode::CLAMP_TO_EDGE :
+				vkcv::SamplerAddressMode::REPEAT
+		);
+	}
+	
+}
diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp
index 9a80635744e5a3dd0b6bd8db476cec841b1c317d..49accf6892560c1c8d3cd856cd1dd207bb214399 100644
--- a/src/vkcv/SamplerManager.cpp
+++ b/src/vkcv/SamplerManager.cpp
@@ -4,16 +4,29 @@
 
 namespace vkcv {
 	
-	SamplerManager::SamplerManager(const vk::Device& device) noexcept :
-		m_device(device), m_samplers()
-	{}
+	uint64_t SamplerManager::getIdFrom(const SamplerHandle &handle) const {
+		return handle.getId();
+	}
+	
+	SamplerHandle SamplerManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return SamplerHandle(id, destroy);
+	}
 	
-	SamplerManager::~SamplerManager() {
-		for (uint64_t id = 0; id < m_samplers.size(); id++) {
-			destroySamplerById(id);
+	void SamplerManager::destroyById(uint64_t id) {
+		auto& sampler = getById(id);
+		
+		if (sampler) {
+			getCore().getContext().getDevice().destroySampler(sampler);
+			sampler = nullptr;
 		}
 	}
 	
+	SamplerManager::SamplerManager() noexcept : HandleManager<vk::Sampler, SamplerHandle>() {}
+	
+	SamplerManager::~SamplerManager() noexcept {
+		clear();
+	}
+	
 	SamplerHandle SamplerManager::createSampler(SamplerFilterType magFilter,
 												SamplerFilterType minFilter,
 												SamplerMipmapMode mipmapMode,
@@ -121,34 +134,15 @@ namespace vkcv {
 				false
 		);
 		
-		const vk::Sampler sampler = m_device.createSampler(samplerCreateInfo);
+		const vk::Sampler sampler = getCore().getContext().getDevice().createSampler(
+				samplerCreateInfo
+		);
 		
-		const uint64_t id = m_samplers.size();
-		m_samplers.push_back(sampler);
-		return SamplerHandle(id, [&](uint64_t id) { destroySamplerById(id); });
+		return add(sampler);
 	}
 	
 	vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const {
-		const uint64_t id = handle.getId();
-		
-		if (id >= m_samplers.size()) {
-			return nullptr;
-		}
-		
-		return m_samplers[id];
-	}
-	
-	void SamplerManager::destroySamplerById(uint64_t id) {
-		if (id >= m_samplers.size()) {
-			return;
-		}
-		
-		auto& sampler = m_samplers[id];
-		
-		if (sampler) {
-			m_device.destroySampler(sampler);
-			sampler = nullptr;
-		}
+		return (*this)[handle];
 	}
 
 }
diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp
index 24d89f0d6af151ff009a2f2bb64953d48a9e7213..3852d3be559b679710a64176ebe96c271ca92298 100644
--- a/src/vkcv/SamplerManager.hpp
+++ b/src/vkcv/SamplerManager.hpp
@@ -3,34 +3,30 @@
 #include <vector>
 #include <vulkan/vulkan.hpp>
 
-#include "vkcv/Handles.hpp"
+#include "HandleManager.hpp"
+
 #include "vkcv/Sampler.hpp"
 
 namespace vkcv {
 	
-	class Core;
-	
 	/**
 	 * @brief Class to manage the creation and destruction of samplers.
 	 */
-	class SamplerManager {
+	class SamplerManager : public HandleManager<vk::Sampler, SamplerHandle> {
 		friend class Core;
 	private:
-		vk::Device m_device;
-		std::vector<vk::Sampler> m_samplers;
+		[[nodiscard]]
+		uint64_t getIdFrom(const SamplerHandle& handle) const override;
 		
-		explicit SamplerManager(const vk::Device& device) noexcept;
+		[[nodiscard]]
+		SamplerHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
 		
-		void destroySamplerById(uint64_t id);
+		void destroyById(uint64_t id) override;
 		
 	public:
-		~SamplerManager();
-		
-		SamplerManager(const SamplerManager& other) = delete;
-		SamplerManager(SamplerManager&& other) = delete;
+		SamplerManager() noexcept;
 		
-		SamplerManager& operator=(const SamplerManager& other) = delete;
-		SamplerManager& operator=(SamplerManager&& other) = delete;
+		~SamplerManager() noexcept override;
 		
 		SamplerHandle createSampler(SamplerFilterType magFilter,
 									SamplerFilterType minFilter,
diff --git a/src/vkcv/Surface.cpp b/src/vkcv/Surface.cpp
deleted file mode 100644
index 3fa601b7bed751b1eb70538a0e2cf10c0b7b50f7..0000000000000000000000000000000000000000
--- a/src/vkcv/Surface.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-
-#include <vkcv/Surface.hpp>
-#include <vkcv/Logger.hpp>
-
-#include <GLFW/glfw3.h>
-
-namespace vkcv {
-	
-	/**
-    * @brief Creates vulkan surface and checks availability.
-    *
-    * @param[in,out] window Current window for the surface
-    * @param[in,out] instance Vulkan-Instance
-    * @param[in,out] physicalDevice Vulkan-PhysicalDevice
-    * @param[out] surface Vulkan-Surface
-    * @return Created vulkan surface
-    */
-	bool createVulkanSurface(GLFWwindow* window,
-							 const vk::Instance& instance,
-							 const vk::PhysicalDevice& physicalDevice,
-							 vk::SurfaceKHR& surface) {
-		VkSurfaceKHR api_surface;
-		
-		if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &api_surface) != VK_SUCCESS) {
-			vkcv_log(LogLevel::ERROR, "Failed to create a window surface");
-			return false;
-		}
-		
-		vk::Bool32 surfaceSupport = false;
-		surface = vk::SurfaceKHR(api_surface);
-		
-		if ((physicalDevice.getSurfaceSupportKHR(0, surface, &surfaceSupport) != vk::Result::eSuccess) ||
-			(!surfaceSupport)) {
-			vkcv_log(LogLevel::ERROR, "Surface is not supported by the device");
-			instance.destroy(surface);
-			surface = nullptr;
-			return false;
-		}
-		
-		return true;
-	}
-	
-	/**
-     * @brief Chooses an Extent and clamps values to the available capabilities.
-     *
-     * @param physicalDevice Vulkan-PhysicalDevice
-     * @param surface Vulkan-Surface of the swapchain
-     * @param window Window of the current application
-     * @return Chosen Extent for the surface
-     */
-	vk::Extent2D chooseExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window) {
-		int fb_width, fb_height;
-		window.getFramebufferSize(fb_width, fb_height);
-		
-		VkExtent2D extent2D = {
-				static_cast<uint32_t>(fb_width),
-				static_cast<uint32_t>(fb_height)
-		};
-		
-		vk::SurfaceCapabilitiesKHR surfaceCapabilities;
-		if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess) {
-			vkcv_log(LogLevel::WARNING, "The capabilities of the surface can not be retrieved");
-			
-			extent2D.width = std::max(MIN_SURFACE_SIZE, extent2D.width);
-			extent2D.height = std::max(MIN_SURFACE_SIZE, extent2D.height);
-		} else {
-			extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width));
-			extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height));
-		}
-		
-		return extent2D;
-	}
-	
-	/**
-     * @brief Chooses Surface Format for the current surface
-     *
-     * @param physicalDevice Vulkan-PhysicalDevice
-     * @param surface Vulkan-Surface of the swapchain
-     * @return Available Format
-     */
-	vk::SurfaceFormatKHR chooseSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
-		std::vector<vk::SurfaceFormatKHR> availableFormats = physicalDevice.getSurfaceFormatsKHR(surface);
-		
-		for (const auto& availableFormat : availableFormats) {
-			if (availableFormat.format == vk::Format::eB8G8R8A8Unorm  && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
-				return availableFormat;
-			}
-		}
-		
-		return availableFormats[0];
-	}
-	
-	/**
-     * @brief Returns vk::PresentModeKHR::eMailbox if available or
-     * vk::PresentModeKHR::eFifo otherwise
-     *
-     * @param physicalDevice Vulkan-PhysicalDevice
-     * @param surface Vulkan-Surface of the swapchain
-     * @return Available PresentationMode
-     */
-	vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
-		std::vector<vk::PresentModeKHR> availablePresentModes = physicalDevice.getSurfacePresentModesKHR(surface);
-		
-		for (const auto& availablePresentMode : availablePresentModes) {
-			if (availablePresentMode == vk::PresentModeKHR::eMailbox) {
-				return availablePresentMode;
-			}
-		}
-		// The FIFO present mode is guaranteed by the spec to be supported
-		return vk::PresentModeKHR::eFifo;
-	}
-	
-	/**
-	* @brief Returns the minImageCount +1 for at least double buffering,
-	* if it's greater than maxImageCount return maxImageCount
-	*
-	* @param physicalDevice Vulkan-PhysicalDevice
-	* @param surface Vulkan-Surface of the swapchain
-	* @return Available image count
-	*/
-	uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
-		vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface);
-		
-		// minImageCount should always be at least 2; set to 3 for triple buffering
-		uint32_t imageCount = surfaceCapabilities.minImageCount + 1;
-		
-		// check if requested image count is supported
-		if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) {
-			imageCount = surfaceCapabilities.maxImageCount;
-		}
-		
-		return imageCount;
-	}
-	
-	Surface::Surface(const Context &context,
-					 const vk::SurfaceKHR &handle,
-					 uint32_t presentQueueIndex,
-					 const vk::Extent2D &extent,
-					 vk::Format format,
-					 vk::ColorSpaceKHR colorSpace)
-					 : m_Context(&context),
-					   m_Handle(handle),
-					   m_PresentQueueIndex(presentQueueIndex),
-					   m_Extent(extent),
-					   m_Format(format),
-					   m_ColorSpace(colorSpace) {
-	}
-	
-	vk::SwapchainKHR Surface::createVulkanSwapchain(const Window &window,
-													const vk::SwapchainKHR &oldSwapchain) {
-		if ((!m_Context) || (!m_Handle))
-			return nullptr;
-		
-		const vk::PhysicalDevice& physicalDevice = m_Context->getPhysicalDevice();
-		const vk::Device& device = m_Context->getDevice();
-		
-		m_Extent = chooseExtent(physicalDevice, m_Handle, window);
-		
-		if ((m_Extent.width < MIN_SURFACE_SIZE) || (m_Extent.height < MIN_SURFACE_SIZE)) {
-			return nullptr;
-		}
-		
-		vk::SurfaceFormatKHR chosenSurfaceFormat = chooseSurfaceFormat(physicalDevice, m_Handle);
-		vk::PresentModeKHR chosenPresentMode = choosePresentMode(physicalDevice, m_Handle);
-		uint32_t chosenImageCount = chooseImageCount(physicalDevice, m_Handle);
-		
-		m_Format = chosenSurfaceFormat.format;
-		m_ColorSpace = chosenSurfaceFormat.colorSpace;
-		
-		vk::SwapchainCreateInfoKHR swapchainCreateInfo (
-				vk::SwapchainCreateFlagsKHR(),
-				m_Handle,
-				chosenImageCount,
-				m_Format,
-				m_ColorSpace,
-				m_Extent,
-				1,
-				vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage,
-				vk::SharingMode::eExclusive,
-				0,
-				nullptr,
-				vk::SurfaceTransformFlagBitsKHR::eIdentity,
-				vk::CompositeAlphaFlagBitsKHR::eOpaque,
-				chosenPresentMode,
-				true,
-				oldSwapchain
-		);
-		
-		return device.createSwapchainKHR(swapchainCreateInfo);
-	}
-	
-	Surface::Surface(Surface &&other) noexcept
-	: m_Context(other.m_Context),
-	  m_Handle(other.m_Handle),
-	  m_PresentQueueIndex(other.m_PresentQueueIndex),
-	  m_Extent(other.m_Extent),
-	  m_Format(other.m_Format),
-	  m_ColorSpace(other.m_ColorSpace) {
-		other.m_Context = nullptr;
-		other.m_Handle = nullptr;
-	}
-	
-	Surface &Surface::operator=(Surface &&other) noexcept {
-		m_Context = other.m_Context;
-		m_Handle = other.m_Handle;
-		m_PresentQueueIndex = other.m_PresentQueueIndex;
-		m_Extent = other.m_Extent;
-		m_Format = other.m_Format;
-		m_ColorSpace = other.m_ColorSpace;
-		
-		other.m_Context = nullptr;
-		other.m_Handle = nullptr;
-		return *this;
-	}
-	
-	Surface::~Surface() {
-		// needs to be destroyed by creator
-	}
-	
-	Surface Surface::create(const Window &window, const Context &context) {
-		const vk::Instance& instance = context.getInstance();
-		const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
-		
-		uint32_t presentQueueIndex = 0;
-		
-		vk::SurfaceKHR surfaceHandle;
-		if (!createVulkanSurface(window.getWindow(), instance, physicalDevice, surfaceHandle))
-			surfaceHandle = nullptr;
-		else
-			presentQueueIndex = QueueManager::checkSurfaceSupport(physicalDevice, surfaceHandle);
-		
-		const vk::Extent2D extent = chooseExtent(physicalDevice, surfaceHandle, window);
-		const vk::SurfaceFormatKHR format = chooseSurfaceFormat(physicalDevice, surfaceHandle);
-		
-		return { context, surfaceHandle, presentQueueIndex, extent, format.format, format.colorSpace };
-	}
-	
-	vk::SurfaceKHR Surface::getSurface() const {
-		return m_Handle;
-	}
-	
-	uint32_t Surface::getPresentQueueIndex() const {
-		return m_PresentQueueIndex;
-	}
-	
-	const vk::Extent2D& Surface::getExtent() const {
-		return m_Extent;
-	}
-	
-	vk::Format Surface::getFormat() const {
-		return m_Format;
-	}
-	
-	vk::ColorSpaceKHR Surface::getColorSpace() const {
-		return m_ColorSpace;
-	}
-
-}
diff --git a/src/vkcv/Swapchain.cpp b/src/vkcv/Swapchain.cpp
deleted file mode 100644
index 2ca69e7d58991ee67d8e72261e1cddf65de60ed0..0000000000000000000000000000000000000000
--- a/src/vkcv/Swapchain.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#include <vkcv/Swapchain.hpp>
-#include <utility>
-
-#include <GLFW/glfw3.h>
-
-namespace vkcv
-{
-
-    Swapchain::Swapchain(const Context &context,
-						 const Surface &surface,
-                         vk::SwapchainKHR swapchain) noexcept
-						 : m_Context(&context),
-						   m_Surface(surface),
-						   m_Swapchain(swapchain),
-						   m_RecreationRequired(false) {
-	}
-    
-    Swapchain::Swapchain(const Swapchain &other) :
-	m_Context(other.m_Context),
-	m_Surface(other.m_Surface),
-	m_Swapchain(other.m_Swapchain),
-	m_RecreationRequired(other.m_RecreationRequired.load()) {
-	}
-
-    const vk::SwapchainKHR& Swapchain::getSwapchain() const {
-        return m_Swapchain;
-    }
-
-    const Surface& Swapchain::getSurface() const {
-        return m_Surface;
-    }
-
-    vk::Format Swapchain::getFormat() const{
-        return m_Surface.getFormat();
-    }
-
-    Swapchain Swapchain::create(const Window &window, const Context &context) {
-        Surface surface = Surface::create(window, context);
-
-        vk::SwapchainKHR swapchain = surface.createVulkanSwapchain(
-				window, nullptr
-		);
-
-        return { context, surface, swapchain };
-    }
-    
-    bool Swapchain::shouldUpdateSwapchain() const {
-    	return m_RecreationRequired;
-    }
-    
-    void Swapchain::updateSwapchain(const Context &context, const Window &window) {
-    	if (!m_RecreationRequired.exchange(false)) {
-			return;
-		}
-    	
-		vk::SwapchainKHR oldSwapchain = m_Swapchain;
-	
-		m_Swapchain = m_Surface.createVulkanSwapchain(
-				window, oldSwapchain
-		);
-		
-		if (!m_Swapchain) {
-			signalSwapchainRecreation();
-		}
-		
-		if (oldSwapchain) {
-			context.getDevice().destroySwapchainKHR(oldSwapchain);
-		}
-    }
-
-    void Swapchain::signalSwapchainRecreation() {
-		m_RecreationRequired = true;
-    }
-    
-    const vk::Extent2D& Swapchain::getExtent() const {
-    	return m_Surface.getExtent();
-    }
-
-    Swapchain::~Swapchain() {
-        // needs to be destroyed by creator
-    }
-
-	uint32_t Swapchain::getImageCount() const {
-		uint32_t imageCount = 0;
-		
-		if (vk::Result::eSuccess != m_Context->getDevice().getSwapchainImagesKHR(m_Swapchain, &imageCount, nullptr))
-			return 0;
-		else
-			return imageCount;
-	}
-
-	uint32_t Swapchain::getPresentQueueIndex() const {
-		return m_Surface.getPresentQueueIndex();
-	}
-	
-}
diff --git a/src/vkcv/SwapchainManager.cpp b/src/vkcv/SwapchainManager.cpp
index 369e077aabc7a5a80bc4dd668d9c6eea1277e66f..f16a4c30e9479bfcbc177e77783985d6d6a54f60 100644
--- a/src/vkcv/SwapchainManager.cpp
+++ b/src/vkcv/SwapchainManager.cpp
@@ -1,62 +1,309 @@
 #include "SwapchainManager.hpp"
 
-namespace vkcv {
+#include <GLFW/glfw3.h>
+
+#include "vkcv/Core.hpp"
 
-	SwapchainManager::SwapchainManager() noexcept {
+namespace vkcv {
+	
+	uint64_t SwapchainManager::getIdFrom(const SwapchainHandle &handle) const {
+		return handle.getId();
+	}
+	
+	SwapchainHandle SwapchainManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return SwapchainHandle(id, destroy);
+	}
+	
+	void SwapchainManager::destroyById(uint64_t id) {
+		auto& swapchain = getById(id);
+		
+		if (swapchain.m_Swapchain) {
+			getCore().getContext().getDevice().destroySwapchainKHR(swapchain.m_Swapchain);
+			swapchain.m_Swapchain = nullptr;
+		}
+		
+		if (swapchain.m_Surface) {
+			getCore().getContext().getInstance().destroySurfaceKHR(swapchain.m_Surface);
+			swapchain.m_Surface = nullptr;
+		}
 	}
+	
+	SwapchainManager::SwapchainManager() noexcept : HandleManager<SwapchainEntry, SwapchainHandle>() {}
 
 	SwapchainManager::~SwapchainManager() noexcept {
-		for (uint64_t id = 0; id < m_swapchains.size(); id++) {
-			destroySwapchainById(id);
+		clear();
+	}
+	
+	/**
+    * @brief Creates vulkan surface and checks availability.
+    *
+    * @param[in,out] window Current window for the surface
+    * @param[in,out] instance Vulkan-Instance
+    * @param[in,out] physicalDevice Vulkan-PhysicalDevice
+    * @param[out] surface Vulkan-Surface
+    * @return Created vulkan surface
+    */
+	static bool createVulkanSurface(GLFWwindow* window,
+									const vk::Instance& instance,
+									const vk::PhysicalDevice& physicalDevice,
+									vk::SurfaceKHR& surface) {
+		VkSurfaceKHR api_surface;
+		
+		if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &api_surface) != VK_SUCCESS) {
+			vkcv_log(LogLevel::ERROR, "Failed to create a window surface");
+			return false;
+		}
+		
+		vk::Bool32 surfaceSupport = false;
+		surface = vk::SurfaceKHR(api_surface);
+		
+		if ((physicalDevice.getSurfaceSupportKHR(0, surface, &surfaceSupport) != vk::Result::eSuccess) ||
+			(!surfaceSupport)) {
+			vkcv_log(LogLevel::ERROR, "Surface is not supported by the device");
+			instance.destroy(surface);
+			surface = nullptr;
+			return false;
+		}
+		
+		return true;
+	}
+	
+	/**
+     * @brief Chooses an Extent and clamps values to the available capabilities.
+     *
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface Vulkan-Surface of the swapchain
+     * @param window Window of the current application
+     * @return Chosen Extent for the surface
+     */
+	static vk::Extent2D chooseExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window) {
+		int fb_width, fb_height;
+		window.getFramebufferSize(fb_width, fb_height);
+		
+		VkExtent2D extent2D = {
+				static_cast<uint32_t>(fb_width),
+				static_cast<uint32_t>(fb_height)
+		};
+		
+		vk::SurfaceCapabilitiesKHR surfaceCapabilities;
+		if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess) {
+			vkcv_log(LogLevel::WARNING, "The capabilities of the surface can not be retrieved");
+			
+			extent2D.width = std::max(MIN_SURFACE_SIZE, extent2D.width);
+			extent2D.height = std::max(MIN_SURFACE_SIZE, extent2D.height);
+		} else {
+			extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width));
+			extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height));
+		}
+		
+		return extent2D;
+	}
+	
+	/**
+     * @brief Chooses Surface Format for the current surface
+     *
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface Vulkan-Surface of the swapchain
+     * @return Available Format
+     */
+	static vk::SurfaceFormatKHR chooseSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+		std::vector<vk::SurfaceFormatKHR> availableFormats = physicalDevice.getSurfaceFormatsKHR(surface);
+		
+		for (const auto& availableFormat : availableFormats) {
+			if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
+				return availableFormat;
+			}
+		}
+		
+		return availableFormats[0];
+	}
+	
+	/**
+     * @brief Returns vk::PresentModeKHR::eMailbox if available or
+     * vk::PresentModeKHR::eFifo otherwise
+     *
+     * @param physicalDevice Vulkan-PhysicalDevice
+     * @param surface Vulkan-Surface of the swapchain
+     * @return Available PresentationMode
+     */
+	static vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+		std::vector<vk::PresentModeKHR> availablePresentModes = physicalDevice.getSurfacePresentModesKHR(surface);
+		
+		for (const auto& availablePresentMode : availablePresentModes) {
+			if (availablePresentMode == vk::PresentModeKHR::eMailbox) {
+				return availablePresentMode;
+			}
 		}
-		m_swapchains.clear();
+		// The FIFO present mode is guaranteed by the spec to be supported
+		return vk::PresentModeKHR::eFifo;
+	}
+	
+	/**
+	* @brief Returns the minImageCount +1 for at least double buffering,
+	* if it's greater than maxImageCount return maxImageCount
+	*
+	* @param physicalDevice Vulkan-PhysicalDevice
+	* @param surface Vulkan-Surface of the swapchain
+	* @return Available image count
+	*/
+	static uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
+		vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface);
+		
+		// minImageCount should always be at least 2; set to 3 for triple buffering
+		uint32_t imageCount = surfaceCapabilities.minImageCount + 1;
+		
+		// check if requested image count is supported
+		if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) {
+			imageCount = surfaceCapabilities.maxImageCount;
+		}
+		
+		return imageCount;
+	}
+	
+	static bool createVulkanSwapchain(const Context& context,
+									  const Window &window,
+									  SwapchainEntry& entry) {
+		const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
+		const vk::Device& device = context.getDevice();
+		
+		entry.m_Extent = chooseExtent(physicalDevice, entry.m_Surface, window);
+		
+		if ((entry.m_Extent.width < MIN_SURFACE_SIZE) || (entry.m_Extent.height < MIN_SURFACE_SIZE)) {
+			return false;
+		}
+		
+		vk::SurfaceFormatKHR chosenSurfaceFormat = chooseSurfaceFormat(physicalDevice, entry.m_Surface);
+		vk::PresentModeKHR chosenPresentMode = choosePresentMode(physicalDevice, entry.m_Surface);
+		uint32_t chosenImageCount = chooseImageCount(physicalDevice, entry.m_Surface);
+		
+		entry.m_Format = chosenSurfaceFormat.format;
+		entry.m_ColorSpace = chosenSurfaceFormat.colorSpace;
+		
+		vk::SwapchainCreateInfoKHR swapchainCreateInfo (
+				vk::SwapchainCreateFlagsKHR(),
+				entry.m_Surface,
+				chosenImageCount,
+				entry.m_Format,
+				entry.m_ColorSpace,
+				entry.m_Extent,
+				1,
+				vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage,
+				vk::SharingMode::eExclusive,
+				0,
+				nullptr,
+				vk::SurfaceTransformFlagBitsKHR::eIdentity,
+				vk::CompositeAlphaFlagBitsKHR::eOpaque,
+				chosenPresentMode,
+				true,
+				entry.m_Swapchain
+		);
+		
+		entry.m_Swapchain = device.createSwapchainKHR(swapchainCreateInfo);
+		return true;
 	}
 
 	SwapchainHandle SwapchainManager::createSwapchain(Window &window) {
-		const uint64_t id = m_swapchains.size();
-
-		Swapchain swapchain = Swapchain::create(window, *m_context);
-
-		m_swapchains.push_back(swapchain);
-		SwapchainHandle swapchainHandle = SwapchainHandle(id, [&](uint64_t id) { destroySwapchainById(id); });
-		window.m_swapchainHandle = swapchainHandle;
-		return swapchainHandle;
+		const vk::Instance& instance = getCore().getContext().getInstance();
+		const vk::PhysicalDevice& physicalDevice = getCore().getContext().getPhysicalDevice();
+		
+		vk::SurfaceKHR surfaceHandle;
+		if (!createVulkanSurface(window.getWindow(), instance, physicalDevice, surfaceHandle)) {
+			return {};
+		}
+		
+		uint32_t presentQueueIndex = QueueManager::checkSurfaceSupport(physicalDevice, surfaceHandle);
+		
+		const vk::Extent2D extent = chooseExtent(physicalDevice, surfaceHandle, window);
+		const vk::SurfaceFormatKHR format = chooseSurfaceFormat(physicalDevice, surfaceHandle);
+		
+		SwapchainEntry entry {
+			nullptr,
+			false,
+			
+			surfaceHandle,
+			presentQueueIndex,
+			extent,
+			format.format,
+			format.colorSpace
+		};
+		
+		if (!createVulkanSwapchain(getCore().getContext(), window, entry)) {
+			instance.destroySurfaceKHR(surfaceHandle);
+			return {};
+		}
+		
+		window.m_swapchainHandle = add(entry);
+		return window.m_swapchainHandle;
 	}
 
-	Swapchain& SwapchainManager::getSwapchain(const SwapchainHandle& handle) {
-		return m_swapchains[handle.getId()];
+	SwapchainEntry& SwapchainManager::getSwapchain(const SwapchainHandle& handle) {
+		return (*this)[handle];
 	}
-
-	void SwapchainManager::destroySwapchainById(uint64_t id) {
-
-		if (id >= m_swapchains.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid id");
+	
+	bool SwapchainManager::shouldUpdateSwapchain(const SwapchainHandle &handle) const {
+		return (*this)[handle].m_RecreationRequired;
+	}
+	
+	void SwapchainManager::updateSwapchain(const SwapchainHandle &handle, const Window &window) {
+		auto& swapchain = (*this)[handle];
+		
+		if (!swapchain.m_RecreationRequired) {
 			return;
+		} else {
+			swapchain.m_RecreationRequired = false;
 		}
-		Swapchain &swapchain = m_swapchains[id];
-
-		if (swapchain.m_Swapchain) {
-			m_context->getDevice().destroySwapchainKHR(swapchain.m_Swapchain);
-			swapchain.m_Swapchain = nullptr;
-		}
 		
-		if (swapchain.m_Surface.m_Handle) {
-			m_context->getInstance().destroySurfaceKHR(swapchain.m_Surface.m_Handle);
-			swapchain.m_Surface.m_Handle = nullptr;
+		vk::SwapchainKHR oldSwapchain = swapchain.m_Swapchain;
+		
+		if (createVulkanSwapchain(getCore().getContext(), window, swapchain)) {
+			if (oldSwapchain) {
+				getCore().getContext().getDevice().destroySwapchainKHR(oldSwapchain);
+			}
+		} else {
+			signalRecreation(handle);
 		}
 	}
-
+	
 	void SwapchainManager::signalRecreation(const SwapchainHandle& handle) {
-		m_swapchains[handle.getId()].signalSwapchainRecreation();
+		(*this)[handle].m_RecreationRequired = true;
 	}
-
-	std::vector<vk::Image> SwapchainManager::getSwapchainImages(const SwapchainHandle& handle) {
-		return m_context->getDevice().getSwapchainImagesKHR(m_swapchains[handle.getId()].getSwapchain());
+	
+	vk::Format SwapchainManager::getFormat(const SwapchainHandle &handle) const {
+		return (*this)[handle].m_Format;
+	}
+	
+	uint32_t SwapchainManager::getImageCount(const SwapchainHandle &handle) const {
+		auto& swapchain = (*this)[handle];
+		
+		uint32_t imageCount;
+		if (vk::Result::eSuccess != getCore().getContext().getDevice().getSwapchainImagesKHR(swapchain.m_Swapchain,
+																							 &imageCount,
+																							 nullptr)) {
+			return 0;
+		} else {
+			return imageCount;
+		}
+	}
+	
+	const vk::Extent2D &SwapchainManager::getExtent(const SwapchainHandle &handle) const {
+		return (*this)[handle].m_Extent;
+	}
+	
+	uint32_t SwapchainManager::getPresentQueueIndex(const SwapchainHandle &handle) const {
+		return (*this)[handle].m_PresentQueueIndex;
+	}
+	
+	vk::ColorSpaceKHR SwapchainManager::getSurfaceColorSpace(const SwapchainHandle &handle) const {
+		return (*this)[handle].m_ColorSpace;
+	}
+	
+	std::vector<vk::Image> SwapchainManager::getSwapchainImages(const SwapchainHandle& handle) const {
+		return getCore().getContext().getDevice().getSwapchainImagesKHR((*this)[handle].m_Swapchain);
 	}
 
 	std::vector<vk::ImageView> SwapchainManager::createSwapchainImageViews(SwapchainHandle& handle) {
 		std::vector<vk::Image> images = getSwapchainImages(handle);
-		Swapchain &swapchain = m_swapchains[handle.getId()];
+		auto& swapchain = (*this)[handle];
 
 		std::vector<vk::ImageView> imageViews;
 		imageViews.reserve(images.size());
@@ -74,12 +321,13 @@ namespace vkcv {
 					vk::ImageViewCreateFlags(),
 					image,
 					vk::ImageViewType::e2D,
-					swapchain.getFormat(),
+					swapchain.m_Format,
 					componentMapping,
 					subResourceRange);
 
-			imageViews.push_back(m_context->getDevice().createImageView(imageViewCreateInfo));
+			imageViews.push_back(getCore().getContext().getDevice().createImageView(imageViewCreateInfo));
 		}
+		
 		return imageViews;
 	}
 }
\ No newline at end of file
diff --git a/src/vkcv/SwapchainManager.hpp b/src/vkcv/SwapchainManager.hpp
index 285641db25f98d1e3dd8d8ed1375fa27e7db5708..d9ba37aacaea1adc2587534e444a178cb701c95e 100644
--- a/src/vkcv/SwapchainManager.hpp
+++ b/src/vkcv/SwapchainManager.hpp
@@ -1,35 +1,55 @@
 #pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/SwapchainManager.hpp
+ * @brief Class to manage the swapchains and their surfaces.
+ */
 
+#include <atomic>
 #include <vector>
-#include <GLFW/glfw3.h>
+#include <vulkan/vulkan.hpp>
 
-#include "WindowManager.hpp"
-#include "vkcv/Swapchain.hpp"
-#include "vkcv/Handles.hpp"
+#include "vkcv/Window.hpp"
+
+#include "HandleManager.hpp"
 
 namespace vkcv {
 	
-	class Core;
+	const uint32_t MIN_SURFACE_SIZE = 2;
+	
+	/**
+     * @brief Structure to handle swapchains.
+     */
+	struct SwapchainEntry {
+		vk::SwapchainKHR m_Swapchain;
+		bool m_RecreationRequired;
+		
+		vk::SurfaceKHR m_Surface;
+		uint32_t m_PresentQueueIndex;
+		vk::Extent2D m_Extent;
+		vk::Format m_Format;
+		vk::ColorSpaceKHR m_ColorSpace;
+	};
 	
 	/**
 	 * @brief Class to manage the creation, destruction and
 	 * allocation of swapchains.
 	 */
-	class SwapchainManager {
+	class SwapchainManager : public HandleManager<SwapchainEntry, SwapchainHandle> {
 		friend class Core;
-
-		friend class WindowManager;
-
 	private:
-		std::vector<Swapchain> m_swapchains;
-
-		Context *m_context;
-
+		[[nodiscard]]
+		uint64_t getIdFrom(const SwapchainHandle& handle) const override;
+		
+		[[nodiscard]]
+		SwapchainHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+		
 		/**
-		 * destroys a specific swapchain by a given id
-		 * @param id of the swapchain to be destroyed
+		 * @brief Destroys a specific swapchain by a given id
+		 *
+		 * @param[in] id ID of the swapchain to be destroyed
 		 */
-		void destroySwapchainById(uint64_t id);
+		void destroyById(uint64_t id) override;
 
 	public:
 		SwapchainManager() noexcept;
@@ -37,15 +57,7 @@ namespace vkcv {
 		/**
 		 * destroys every swapchain
 		 */
-		~SwapchainManager() noexcept;
-
-		SwapchainManager(SwapchainManager &&other) = delete;
-
-		SwapchainManager(const SwapchainManager &other) = delete;
-
-		SwapchainManager &operator=(SwapchainManager &&other) = delete;
-
-		SwapchainManager &operator=(const SwapchainManager &other) = delete;
+		~SwapchainManager() noexcept override;
 
 		/**
 		 * creates a swapchain and returns the handle
@@ -59,27 +71,96 @@ namespace vkcv {
 		 * @return the reference of the swapchain
 		 */
 		[[nodiscard]]
-		Swapchain &getSwapchain(const SwapchainHandle& handle);
-
+		SwapchainEntry& getSwapchain(const SwapchainHandle& handle);
+		
+		/**
+		 * @brief Checks whether the swapchain needs to be recreated.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @return True, if the swapchain should be updated,
+		 * otherwise false.
+		 */
+		bool shouldUpdateSwapchain(const SwapchainHandle& handle) const;
+		
+		/**
+		 * @brief Updates and recreates the swapchain.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @param[in] window that the new swapchain gets bound to
+		 */
+		void updateSwapchain(const SwapchainHandle& handle, const Window &window);
+		
 		/**
-		 * sets  the recreation  flag fot the swapchain
-		 * @param handle of the swapchain that should be recreated
+		 * @brief Signals the swapchain to be recreated.
+		 *
+		 * @param[in] handle Swapchain handle
 		 */
 		void signalRecreation(const SwapchainHandle& handle);
+		
+		/**
+         * @brief Returns the image format for the current surface
+         * of the swapchain.
+         *
+         * @param[in] handle Swapchain handle
+         * @return Swapchain image format
+         */
+		[[nodiscard]]
+		vk::Format getFormat(const SwapchainHandle& handle) const;
+		
+		/**
+		 * @brief Returns the amount of images for the swapchain.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @return Number of images
+		*/
+		uint32_t getImageCount(const SwapchainHandle& handle) const;
+		
+		/**
+         * @brief Returns the extent from the current surface of
+         * the swapchain.
+         *
+         * @param[in] handle Swapchain handle
+         * @return Extent of the swapchains surface
+         */
+		[[nodiscard]]
+		const vk::Extent2D& getExtent(const SwapchainHandle& handle) const;
+		
+		/**
+		 * @brief Returns the present queue index to be used with
+		 * the swapchain and its current surface.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @return Present queue family index
+		 */
+		[[nodiscard]]
+		uint32_t getPresentQueueIndex(const SwapchainHandle& handle) const;
+		
+		/**
+		 * @brief Returns the color space of the surface from
+		 * a swapchain.
+		 *
+		 * @param[in] handle Swapchain handle
+		 * @return Color space
+		 */
+		[[nodiscard]]
+		vk::ColorSpaceKHR getSurfaceColorSpace(const SwapchainHandle& handle) const;
 
 		/**
 		 * gets the swapchain images
 		 * @param handle of the swapchain
 		 * @return a vector of the swapchain images
 		 */
-		std::vector<vk::Image> getSwapchainImages(const SwapchainHandle& handle);
+		[[nodiscard]]
+		std::vector<vk::Image> getSwapchainImages(const SwapchainHandle& handle) const;
 
 		/**
 		 * creates the swapchain imageViews for the swapchain
 		 * @param handle of the swapchain which ImageViews should be created
 		 * @return a ov ImageViews of the swapchain
 		 */
+		[[nodiscard]]
 		std::vector<vk::ImageView> createSwapchainImageViews(SwapchainHandle& handle);
+		
 	};
 	
 }
\ No newline at end of file
diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp
deleted file mode 100644
index 8bd53c85e8cdede55d5b1db71d44bf483e24acb1..0000000000000000000000000000000000000000
--- a/src/vkcv/SyncResources.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "vkcv/SyncResources.hpp"
-
-namespace vkcv {
-	SyncResources createSyncResources(const vk::Device& device) {
-		SyncResources resources;
-
-		const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits();
-		const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags);
-		resources.renderFinished			= device.createSemaphore(semaphoreInfo, nullptr, {});
-		resources.swapchainImageAcquired	= device.createSemaphore(semaphoreInfo);
-
-		resources.presentFinished			= createFence(device);
-		
-		return resources;
-	}
-
-	void destroySyncResources(const vk::Device& device, const SyncResources& resources) {
-		device.destroySemaphore(resources.renderFinished);
-		device.destroySemaphore(resources.swapchainImageAcquired);
-		device.destroyFence(resources.presentFinished);
-	}
-
-	vk::Fence createFence(const vk::Device& device) {
-		const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
-		vk::FenceCreateInfo fenceInfo(fenceFlags);
-		return device.createFence(fenceInfo, nullptr, {});
-	}
-
-	void waitForFence(const vk::Device& device, const vk::Fence& fence) {
-		const auto result = device.waitForFences(fence, true, UINT64_MAX);
-		assert(result == vk::Result::eSuccess);
-	}
-}
\ No newline at end of file
diff --git a/src/vkcv/TypeGuard.cpp b/src/vkcv/TypeGuard.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..31a758913e4fea5bdef5d91e9519b67eefdd1e93
--- /dev/null
+++ b/src/vkcv/TypeGuard.cpp
@@ -0,0 +1,93 @@
+#include <vkcv/TypeGuard.hpp>
+
+#include <vkcv/Logger.hpp>
+#include <string.h>
+
+namespace vkcv {
+	
+#ifndef NDEBUG
+	bool TypeGuard::checkType(const char* name, size_t hash, size_t size) const {
+		if (!checkTypeSize(size)) {
+			return false;
+		}
+		
+		if ((!m_typeName) || (!name)) {
+			return true;
+		}
+		
+		if (m_typeHash != hash) {
+			vkcv_log(
+					LogLevel::WARNING,
+					"Hash (%lu) does not match the specified hash of the type guard (%lu)",
+					hash,
+					m_typeHash
+			);
+			
+			return false;
+		}
+		
+		if (strcmp(m_typeName, name) != 0) {
+			vkcv_log(
+					LogLevel::WARNING,
+					"Name (%s) does not match the specified name of the type guard (%s)",
+					name,
+					m_typeName
+			);
+			
+			return false;
+		} else {
+			return true;
+		}
+	}
+#endif
+	
+	bool TypeGuard::checkTypeSize(size_t size) const {
+		if (m_typeSize != size) {
+			vkcv_log(
+					LogLevel::WARNING,
+					"Size (%lu) does not match the specified size of the type guard (%lu)",
+					size,
+					m_typeSize
+			);
+			
+			return false;
+		} else {
+			return true;
+		}
+	}
+	
+	TypeGuard::TypeGuard(size_t size) :
+#ifndef NDEBUG
+	m_typeName(nullptr), m_typeHash(0),
+#endif
+	m_typeSize(size)
+	{}
+	
+	TypeGuard::TypeGuard(const std::type_info &info, size_t size) :
+#ifndef NDEBUG
+	m_typeName(info.name()), m_typeHash(info.hash_code()),
+#endif
+	m_typeSize(size)
+	{}
+
+	bool TypeGuard::operator==(const TypeGuard &other) const {
+#ifndef NDEBUG
+		return checkType(other.m_typeName, other.m_typeHash, other.m_typeSize);
+#else
+		return checkTypeSize(other.m_typeSize);
+#endif
+	}
+
+	bool TypeGuard::operator!=(const TypeGuard &other) const {
+#ifndef NDEBUG
+		return !checkType(other.m_typeName, other.m_typeHash, other.m_typeSize);
+#else
+		return !checkTypeSize(other.m_typeSize);
+#endif
+	}
+
+	size_t TypeGuard::typeSize() const {
+		return m_typeSize;
+	}
+
+}
diff --git a/src/vkcv/VertexData.cpp b/src/vkcv/VertexData.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bea3b700549f2951987f148f537461c9ca7f9041
--- /dev/null
+++ b/src/vkcv/VertexData.cpp
@@ -0,0 +1,42 @@
+
+#include "vkcv/VertexData.hpp"
+
+namespace vkcv {
+	
+	VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset) {
+		VertexBufferBinding binding (buffer, offset);
+		return binding;
+	}
+	
+	VertexData::VertexData(const std::vector<VertexBufferBinding> &bindings)
+	: m_bindings(bindings),
+	m_indices(),
+	m_indexBitCount(IndexBitCount::Bit16),
+	m_count(0) {}
+	
+	const std::vector<VertexBufferBinding> &VertexData::getVertexBufferBindings() const {
+		return m_bindings;
+	}
+	
+	void VertexData::setIndexBuffer(const BufferHandle &indices, IndexBitCount indexBitCount) {
+		m_indices = indices;
+		m_indexBitCount = indexBitCount;
+	}
+	
+	const BufferHandle &VertexData::getIndexBuffer() const {
+		return m_indices;
+	}
+	
+	IndexBitCount VertexData::getIndexBitCount() const {
+		return m_indexBitCount;
+	}
+	
+	void VertexData::setCount(size_t count) {
+		m_count = count;
+	}
+	
+	size_t VertexData::getCount() const {
+		return m_count;
+	}
+	
+}
diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp
index 73e3885bf8f97854712d5b202123c7f7202042d1..1409fee0e485eb389fbd4697a4da98c0cdae2813 100644
--- a/src/vkcv/VertexLayout.cpp
+++ b/src/vkcv/VertexLayout.cpp
@@ -43,5 +43,16 @@ namespace vkcv {
 		binding.stride = offset;
 		return binding;
 	}
-
+	
+	VertexBindings createVertexBindings(const VertexAttachments &attachments) {
+		VertexBindings bindings;
+		bindings.reserve(attachments.size());
+		
+		for (uint32_t i = 0; i < attachments.size(); i++) {
+			bindings.push_back(createVertexBinding(i, { attachments[i] }));
+		}
+		
+		return bindings;
+	}
+	
 }
\ No newline at end of file
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index f6f9aa6950b767c31bdd1608782c5b7b7f7071ed..8787053dd046d625961cc26c995fec28dc43d74b 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -284,7 +284,7 @@ namespace vkcv {
 		return window;
 	}
 
-	SwapchainHandle Window::getSwapchainHandle() const {
+	SwapchainHandle Window::getSwapchain() const {
 		return m_swapchainHandle;
 	}
 }
diff --git a/src/vkcv/WindowManager.cpp b/src/vkcv/WindowManager.cpp
index 3179b219e9e93905d7202cf8e11dbbf0e019020f..930c5f3a1d5b6c405f83fc94fdc9998efc9c1fea 100644
--- a/src/vkcv/WindowManager.cpp
+++ b/src/vkcv/WindowManager.cpp
@@ -2,26 +2,40 @@
 
 namespace vkcv {
 	
-	WindowManager::WindowManager() noexcept {
+	uint64_t WindowManager::getIdFrom(const WindowHandle &handle) const {
+		return handle.getId();
 	}
+	
+	WindowHandle WindowManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return WindowHandle(id, destroy);
+	}
+	
+	void WindowManager::destroyById(uint64_t id) {
+		auto& window = getById(id);
 
-	WindowManager::~WindowManager() noexcept {
-		for (uint64_t id = 0; id < m_windows.size(); id++) {
-			destroyWindowById(id);
+		if (window) {
+			delete window;
+			window = nullptr;
 		}
-		
-		m_windows.clear();
 	}
+	
+	WindowManager::WindowManager() noexcept : HandleManager<Window *, WindowHandle>() {}
 
-	WindowHandle WindowManager::createWindow(
-			SwapchainManager &swapchainManager,
-			const char *applicationName,
-			uint32_t windowWidth,
-			uint32_t windowHeight,
-			bool resizeable) {
-		const uint64_t id = m_windows.size();
+	WindowManager::~WindowManager() noexcept {
+		clear();
+	}
 
-		auto window = new Window(applicationName, windowWidth, windowHeight, resizeable);
+	WindowHandle WindowManager::createWindow(SwapchainManager &swapchainManager,
+											 const char *applicationName,
+											 uint32_t windowWidth,
+											 uint32_t windowHeight,
+											 bool resizeable) {
+		auto window = new Window(
+				applicationName,
+				static_cast<int>(windowWidth),
+				static_cast<int>(windowHeight),
+				resizeable
+		);
 
 		SwapchainHandle swapchainHandle = swapchainManager.createSwapchain(*window);
 
@@ -30,27 +44,27 @@ namespace vkcv {
 				// copy handle because it would run out of scope and be invalid
 				swapchainManager.signalRecreation(handle);
 			});
+			
 			window->m_resizeHandle = resizeHandle;
 		}
 
-		m_windows.push_back(window);
-		return WindowHandle(id, [&](uint64_t id) { destroyWindowById(id); });
+		return add(window);
 	}
 
-	Window &WindowManager::getWindow(const WindowHandle handle) const {
-		return *m_windows[handle.getId()];
+	Window &WindowManager::getWindow(const WindowHandle& handle) const {
+		return *(*this)[handle];
 	}
-
-	void WindowManager::destroyWindowById(uint64_t id) {
-
-		if (id >= m_windows.size()) {
-			vkcv_log(LogLevel::ERROR, "Invalid id");
-			return;
-		}
-
-		if (m_windows[id] != nullptr) {
-			delete m_windows[id];
-			m_windows[id] = nullptr;
+	
+	std::vector<WindowHandle> WindowManager::getWindowHandles() const {
+		std::vector<WindowHandle> handles;
+		
+		for (size_t id = 0; id < getCount(); id++) {
+			if (getById(id)->isOpen()) {
+				handles.push_back(WindowHandle(id));
+			}
 		}
+		
+		return handles;
 	}
+
 }
\ No newline at end of file
diff --git a/src/vkcv/WindowManager.hpp b/src/vkcv/WindowManager.hpp
index 81658c241174c62359457c04e143accf07253edb..172cec2a1c9ee5a5c437c8e25f4daec4965fa9e9 100644
--- a/src/vkcv/WindowManager.hpp
+++ b/src/vkcv/WindowManager.hpp
@@ -5,32 +5,31 @@
 #include <GLFW/glfw3.h>
 
 #include "vkcv/Window.hpp"
-#include "vkcv/Handles.hpp"
+
+#include "HandleManager.hpp"
 #include "SwapchainManager.hpp"
 
 namespace vkcv {
-	
-	class Context;
-
-	class SwapchainManager;
 
 	/**
 	 * @brief Class to manage the windows of applications.
 	 */
-	class WindowManager {
+	class WindowManager : public HandleManager<Window*, WindowHandle> {
 		friend class Core;
 
 	private:
+		[[nodiscard]]
+		uint64_t getIdFrom(const WindowHandle& handle) const override;
+		
+		[[nodiscard]]
+		WindowHandle createById(uint64_t id, const HandleDestroyFunction& destroy) override;
+		
 		/**
-		 * vector of all managed windows
-		 */
-		std::vector<Window*> m_windows;
-
-		/**
-		 * destroys a specific window by a given id
-		 * @param id of the window to be destroyed
+		 * Destroys a specific window by a given id.
+		 *
+		 * @param[in] id ID of the window to be destroyed
 		 */
-		void destroyWindowById(uint64_t id);
+		void destroyById(uint64_t id) override;
 
 	public:
 		WindowManager() noexcept;
@@ -38,15 +37,7 @@ namespace vkcv {
 		/**
 		 * destroys every window
 		 */
-		~WindowManager() noexcept;
-
-		WindowManager(WindowManager &&other) = delete;
-
-		WindowManager(const WindowManager &other) = delete;
-
-		WindowManager &operator=(WindowManager &&other) = delete;
-
-		WindowManager &operator=(const WindowManager &other) = delete;
+		~WindowManager() noexcept override;
 
 		/**
 		 * creates a window and returns it's  handle
@@ -57,7 +48,9 @@ namespace vkcv {
 		 * @param resizeable if the window is resizable
 		 * @return window handle
 		 */
-		WindowHandle createWindow(SwapchainManager &swapchainManager, const char *applicationName, uint32_t windowWidth,
+		WindowHandle createWindow(SwapchainManager &swapchainManager,
+								  const char *applicationName,
+								  uint32_t windowWidth,
 								  uint32_t windowHeight,
 								  bool resizeable);
 
@@ -66,7 +59,16 @@ namespace vkcv {
 		 * @return the reference of the window
 		 */
 		[[nodiscard]]
-		Window &getWindow(const WindowHandle handle) const;
+		Window &getWindow(const WindowHandle& handle) const;
+		
+		/**
+		 * Returns a list of window handles for current active
+		 * and open windows.
+		 *
+		 * @return List of window handles
+		 */
+		[[nodiscard]]
+		std::vector<WindowHandle> getWindowHandles() const;
 
 	};