diff --git a/README.md b/README.md
index 239a47f5f57ae9755e9a6e9c9e70d27d6482cc20..520b6242f86ccc3db6e1de78cf16ec26d550efb2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
 # VkCV Framework
  A Vulkan framework for computer visualistics simplifying building applications
 
+![Vulkan-Chan](https://gitlab.uni-koblenz.de/uploads/-/system/project/avatar/3712/VulkanChan.jpg)
+
 ## Build
 
 Git submodules are used for libraries. 
diff --git a/config/Sources.cmake b/config/Sources.cmake
index 9d2c7b1f2595beb17212a6daf1b5331d8926d76d..cb73f57c2ca7278765ef0c8d01989c09a445c7b5 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -20,7 +20,9 @@ set(vkcv_sources
 		${vkcv_source}/vkcv/Window.cpp
 
 		${vkcv_include}/vkcv/Buffer.hpp
-		${vkcv_source}/vkcv/Buffer.cpp
+		
+		${vkcv_include}/vkcv/BufferManager.hpp
+		${vkcv_source}/vkcv/BufferManager.cpp
 
 		${vkcv_include}/vkcv/SwapChain.hpp
 		${vkcv_source}/vkcv/SwapChain.cpp
@@ -53,4 +55,10 @@ set(vkcv_sources
         ${vkcv_source}/vkcv/Framebuffer.cpp
 
 		${vkcv_include}/vkcv/Event.hpp
+
+		${vkcv_source}/vkcv/DescriptorManager.hpp
+		${vkcv_source}/vkcv/DescriptorManager.cpp
+
+		${vkcv_include}/vkcv/DescriptorConfig.hpp
+		${vkcv_source}/vkcv/DescriptorConfig.cpp
 )
diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp
index 6647cbf2e2686edf615ace45e1c63c002f0fd457..5c21b1ce1d2d84afad6816fd8bf5f66a82b80941 100644
--- a/include/vkcv/Buffer.hpp
+++ b/include/vkcv/Buffer.hpp
@@ -1,163 +1,61 @@
 #pragma once
 /**
- * @authors Lars Hoerttrich
- * @file include/vkcv/Buffer.hpp
+ * @authors Lars Hoerttrich, Tobias Frisch
+ * @file vkcv/Buffer.hpp
  * @brief template buffer class, template for type security, implemented here because template classes can't be written in .cpp
  */
-#include "vkcv/Handles.hpp"
-#include <vulkan/vulkan.hpp>
-#include "vkcv/Context.hpp"
-
-
+#include "BufferManager.hpp"
 
 namespace vkcv {
-	//Enum of buffertypes
-	enum BufferType { VERTEX, UNIFORM, STORAGE };
-
-	//Functions outsourced to Buffer.cpp file:
-	void outsourcedDestructor(vk::Device device, vk::DeviceMemory bufferMemory, vk::Buffer buffer);
-	vk::Buffer outsourcedCreateBuffer(vk::Device device, BufferType type, size_t size);
-	vk::DeviceMemory outsourcedAllocateMemory(vk::Device device, vk::PhysicalDevice physicalDevice, vk::MemoryRequirements memoryRequirements);
 
 	template<typename T>
 	class Buffer {
 	public:
-		//future bufferHandle struct
-		struct Handle {
-			uint64_t id;
-
-		};
-
 		// explicit destruction of default constructor
 		Buffer<T>() = delete;
-		// is never called directly
-		~Buffer<T>() noexcept {
-			outsourcedDestructor(m_Device, m_BufferMemory, m_Buffer);
-		}
 
-		Buffer<T>(const Buffer<T>& other) = delete; // copy-ctor
-		Buffer<T>(Buffer<T>&& other) noexcept :
-		        m_Buffer(other.m_Buffer),
-				m_BufferMemory(other.m_BufferMemory),
-				m_Device(other.m_Device),
-		        m_Type(other.m_Type),
-		        m_Size(other.m_Size),
-		        m_DataP(other.m_DataP)
-		{
-			other.m_Buffer = nullptr;
-			other.m_BufferMemory = nullptr;
-			other.m_Device = nullptr;
-			other.m_Type = vkcv::VERTEX; //set to 0
-			other.m_Size = 0;
-			other.m_DataP = nullptr;
-		} // move-ctor
-
-		Buffer<T>& operator=(const Buffer<T>& other) = delete; // copy assignment
-		Buffer<T>& operator=(Buffer<T>&& other) noexcept {
-			m_Buffer = other.m_Buffer;
-			m_BufferMemory = other.m_BufferMemory;
-			m_Device = other.m_Device;
-			m_Type = other.m_Type;
-			m_Size = other.m_Size;
-			m_DataP = other.m_DataP;
-
-			other.m_Buffer = nullptr;
-			other.m_BufferMemory = nullptr;
-			other.m_Device = nullptr;
-			other.m_Type = vkcv::VERTEX; //set to 0
-			other.m_Size = 0;
-			other.m_DataP = nullptr;
-
-		}// move assignment
-
-		BufferType getType() { return m_Type; };
-		size_t getSize() { return m_Size; };
-		
-		/**
-		 * Maps this buffers Memory, fills this buffer with @p data of type T and count @p count
-		 * unmaps afterwards.
-		 * @p data Pointer to data
-		 * @p count Amount of data of type T
-		 */
-		 // TODO: we will probably need staging-buffer here later (possible add in BufferManager later?)
-		void fill(T* data, size_t count) {
-			const vk::MemoryRequirements requirements = m_Device.getBufferMemoryRequirements(m_Buffer);
-			
-			// TODO: check if mapped already
-			m_DataP = static_cast<uint8_t*>(m_Device.mapMemory(m_BufferMemory, 0, requirements.size));
-			memcpy(m_DataP, data, sizeof(T) * count);
-			m_Device.unmapMemory(m_BufferMemory);
+		BufferType getType() {
+			return m_type;
 		};
 		
-		T* map() {
-			const vk::MemoryRequirements requirements = m_Device.getBufferMemoryRequirements(m_Buffer);
-			
-			m_DataP = static_cast<uint8_t*>(m_Device.mapMemory(m_BufferMemory, 0, requirements.size));
-			// TODO: make sure to unmap before deallocation
-			
-			return reinterpret_cast<T*>(m_DataP);
-		};
+		size_t getCount() {
+			return m_count;
+		}
 		
-		void unmap() {
-			m_Device.unmapMemory(m_BufferMemory);
-			// TODO: mark m_DataP as invalid?
-		};
+		size_t getSize() {
+			return m_count * sizeof(T);
+		}
 		
-		/**
-		 * * Create function of #Buffer requires a @p device, a @p physicalDevice, a @p buffer type and a @p size.		 *
-		 * @param device Vulkan-Device
-		 * @param physicalDevice Vulkan-PhysicalDevice
-		 * @param type Enum type of possible vkcv::BufferType
-		 * @param Size size of data
-		 */
-		static Buffer<T> create(vk::Device device, vk::PhysicalDevice physicalDevice, BufferType type, size_t size) {
-			vk::Buffer buffer = nullptr;
-			
+		void fill(T* data, size_t count = 0, size_t offset = 0) {
+			 m_manager->fillBuffer(m_handle_id, data, count * sizeof(T), offset * sizeof(T));
+		}
+		
+		T* map(size_t offset = 0, size_t count = 0) {
+			return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle_id, offset * sizeof(T), count * sizeof(T)));
+		}
 
-			buffer = outsourcedCreateBuffer(device, type, sizeof(T) * size);
-			
-			if (!buffer) {
-				//TODO: potential issue
-			}
-			
-			//get requirements for allocation
-			const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer);
-			
-			vk::DeviceMemory memory= outsourcedAllocateMemory(device, physicalDevice, requirements);
-			
-			if (!memory) {
-				//TODO: other potential issue
-			}
-			
-			device.bindBufferMemory(buffer, memory, 0);
-			
-			return Buffer<T>(buffer, memory, device, type, size);
+		void unmap() {
+			m_manager->unmapBuffer(m_handle_id);
+		}
+		
+		static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType) {
+			return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType);
 		}
 
 	private:
-		vk::Buffer m_Buffer;
-		vk::DeviceMemory m_BufferMemory;
-		vk::Device m_Device;
-		BufferType m_Type;
-		size_t m_Size=0;
-		uint8_t* m_DataP;
-
-		/**
-		 * * Constructor of #Buffer requires a @p buffer, a @p memory, @p device, @ requirement, a @p buffer type and a @p size.
-		 * @param buffer Vulkan-Buffer
-		 * @param memory Vulkan-DeviceMemory
-		 * @param device Vulkan-Device
-		 * @param requirement Vulkan-MemoryRequirements
-		 * @param type Enum type of possible vkcv::BufferType
-		 * @param Size size of data
-		 */
-		Buffer<T>(vk::Buffer buffer, vk::DeviceMemory memory, vk::Device device, BufferType type, size_t size) :
-				m_Buffer(buffer),
-				m_BufferMemory(memory),
-				m_Device(device),
-				m_Type(type),
-				m_Size(size),
-				m_DataP(nullptr)
-		{}	
+		BufferManager* const m_manager;
+		const uint64_t m_handle_id;
+		const BufferType m_type;
+		const size_t m_count;
+		const BufferMemoryType m_memoryType;
+		
+		Buffer<T>(BufferManager* manager, uint64_t id, BufferType type, size_t count, BufferMemoryType memoryType) :
+				m_manager(manager),
+				m_handle_id(id),
+				m_type(type),
+				m_count(count),
+				m_memoryType(memoryType)
+		{}
+		
 	};
 }
diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eb8cb178d1b844fff57892c7312ed71d325900cc
--- /dev/null
+++ b/include/vkcv/BufferManager.hpp
@@ -0,0 +1,104 @@
+#pragma once
+
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv
+{
+	enum class BufferType {
+		VERTEX,
+		UNIFORM,
+		STORAGE,
+		STAGING
+	};
+	
+	enum class BufferMemoryType {
+		DEVICE_LOCAL,
+		HOST_VISIBLE
+	};
+	
+	class Core;
+	
+	class BufferManager
+	{
+		friend class Core;
+	private:
+		
+		struct Buffer
+		{
+			vk::Buffer m_handle;
+			vk::DeviceMemory m_memory;
+			size_t m_size;
+			void* m_mapped = nullptr;
+			bool m_mappable;
+		};
+		
+		Core* m_core;
+		std::vector<Buffer> m_buffers;
+		uint64_t m_stagingBuffer;
+		
+		BufferManager() noexcept;
+		
+		void init();
+		
+	public:
+		~BufferManager() noexcept;
+		
+		BufferManager(BufferManager&& other) = delete;
+		BufferManager(const BufferManager& other) = delete;
+		
+		BufferManager& operator=(BufferManager&& other) = delete;
+		BufferManager& operator=(const BufferManager& other) = delete;
+		
+		/**
+		 * Creates and allocates a new buffer and returns its
+		 * unique buffer handle id.
+		 *
+		 * @param type Type of buffer
+		 * @param size Size of buffer in bytes
+		 * @param memoryType Type of buffers memory
+		 * @return New buffer handle id
+		 */
+		uint64_t createBuffer(BufferType type, size_t size, BufferMemoryType memoryType);
+		
+		/**
+		 * Fills a buffer represented by a given buffer
+		 * handle id with custom data.
+		 *
+		 * @param id Buffer handle id
+		 * @param data Pointer to data
+		 * @param size Size of data in bytes
+		 * @param offset Offset to fill in data in bytes
+		 */
+		void fillBuffer(uint64_t id, void* data, size_t size, size_t offset);
+		
+		/**
+		 * Maps memory to a buffer represented by a given
+		 * buffer handle id and returns it.
+		 *
+		 * @param id Buffer handle id
+		 * @param offset Offset of mapping in bytes
+		 * @param size Size of mapping in bytes
+		 * @return Pointer to mapped memory
+		 */
+		void* mapBuffer(uint64_t id, size_t offset, size_t size);
+		
+		/**
+		 * Unmaps memory from a buffer represented by a given
+		 * buffer handle id.
+		 *
+		 * @param id Buffer handle id
+		 */
+		void unmapBuffer(uint64_t id);
+	
+		/**
+		 * Destroys and deallocates buffer represented by a given
+		 * buffer handle id.
+		 *
+		 * @param id Buffer handle id
+		 */
+		void destroyBuffer(uint64_t id);
+		
+	};
+	
+}
diff --git a/include/vkcv/CommandResources.hpp b/include/vkcv/CommandResources.hpp
index 05e848294935bcf3642e1712072acf607d153611..ffdd6d0315549c7522623f535856bbaffc8e5c6e 100644
--- a/include/vkcv/CommandResources.hpp
+++ b/include/vkcv/CommandResources.hpp
@@ -1,12 +1,25 @@
 #pragma once
 #include <vulkan/vulkan.hpp>
+#include <unordered_set>
+#include "QueueManager.hpp"
 
 namespace vkcv {
 	struct CommandResources {
-		vk::CommandPool commandPool;
-		vk::CommandBuffer commandBuffer;
+		std::vector<vk::CommandPool> cmdPoolPerQueueFamily;
 	};
 
-	CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex);
-	void destroyCommandResources(const vk::Device& device, const CommandResources& resources);
+	std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager& queueManager);
+	CommandResources		createCommandResources(const vk::Device& device, const std::unordered_set<int> &familyIndexSet);
+	void					destroyCommandResources(const vk::Device& device, const CommandResources& resources);
+	vk::CommandBuffer		allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool);
+	vk::CommandPool			chooseCmdPool(const Queue &queue, const CommandResources &cmdResources);
+	Queue					getQueueForSubmit(const QueueType type, const QueueManager &queueManager);
+	void					beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags);
+
+	void submitCommandBufferToQueue(
+		const 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/Core.hpp b/include/vkcv/Core.hpp
index a3016f911fbade226f3a7d2b39764e9e409c837e..66db4776d053352d8ccb2eea5e09c3fb77b68561 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -17,12 +17,24 @@
 #include "CommandResources.hpp"
 #include "SyncResources.hpp"
 #include "Result.hpp"
+#include "vkcv/DescriptorConfig.hpp"
 
 namespace vkcv
 {
     // forward declarations
     class PassManager;
     class PipelineManager;
+    class DescriptorManager;
+    class BufferManager;
+
+	struct SubmitInfo {
+		QueueType queueType;
+		std::vector<vk::Semaphore> waitSemaphores;
+		std::vector<vk::Semaphore> signalSemaphores;
+	};
+	
+	typedef std::function<void(const vk::CommandBuffer& cmdBuffer)> RecordCommandFunction;
+	typedef std::function<void(void)> FinishCommandFunction;
 
     class Core final
     {
@@ -49,6 +61,9 @@ namespace vkcv
 
         std::unique_ptr<PassManager> m_PassManager;
         std::unique_ptr<PipelineManager> m_PipelineManager;
+        std::unique_ptr<DescriptorManager> m_DescriptorManager;
+        std::unique_ptr<BufferManager> m_BufferManager;
+
 		CommandResources m_CommandResources;
 		SyncResources m_SyncResources;
 		uint32_t m_currentSwapchainImageIndex;
@@ -146,15 +161,23 @@ namespace vkcv
 
         /**
             * Creates a #Buffer with data-type T and @p bufferType 
-            * @param bufferType Type of Buffer created
-            * @param size Amount of Data of type T
+            * @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 bufferType,size_t size) {
-        	return Buffer<T>::create(m_Context.getDevice(), m_Context.getPhysicalDevice(), bufferType, size);
+        Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) {
+        	return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType);
         }
 
+        /** TODO:
+         *   @param setDescriptions
+         *   @return
+         */
+        [[nodiscard]]
+        ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> &descriptorSets);
+
 		/**
 		 * @brief start recording command buffers and increment frame index
 		*/
@@ -172,5 +195,16 @@ namespace vkcv
 		void endFrame();
 
 		vk::Format getSwapchainImageFormat();
+
+		/**
+		 * 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 submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish);
     };
 }
diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8116259757fe623f97a2814991b226ee27efca50
--- /dev/null
+++ b/include/vkcv/DescriptorConfig.hpp
@@ -0,0 +1,50 @@
+#pragma once
+#include <vkcv/ShaderProgram.hpp>
+
+namespace vkcv
+{
+    /*
+    * All the types of descriptors (resources) that can be retrieved by the shaders
+    */
+    enum class DescriptorType
+    {
+        UNIFORM_BUFFER,
+        STORAGE_BUFFER,
+        SAMPLER,
+        IMAGE
+    };    
+    
+    /*
+    * One binding for a descriptor set
+    * @param[in] a unique binding ID
+    * @param[in] a descriptor type
+    * @param[in] the number of descriptors of this type (arrays of the same type possible)
+    * @param[in] the shader stage where the descriptor is supposed to be retrieved
+    */
+    struct DescriptorBinding
+    {
+        DescriptorBinding() = delete;
+        DescriptorBinding(
+            DescriptorType descriptorType,
+            uint32_t descriptorCount,
+            ShaderStage shaderStage
+        ) noexcept;
+
+        DescriptorType descriptorType;
+        uint32_t descriptorCount;
+        ShaderStage shaderStage;
+    };
+
+    /*
+    * One descriptor set struct that contains all the necessary information for the actual creation.
+    * @param[in] a number of bindings that were created beforehand
+    * @param[in] the number of (identical) sets that should be created from the attached bindings
+    */
+    struct DescriptorSet
+    {
+        DescriptorSet() = delete;
+        explicit DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept;
+
+        std::vector<DescriptorBinding> bindings;
+    };
+}
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index 4ec2bc058409e9119695700b2b727be9426c2bcd..ad48a1ecbd41f75286cc33e88e62699468a5f11f 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -13,4 +13,5 @@ namespace vkcv
     struct BufferHandle     {uint64_t id;};
     struct PassHandle       {uint64_t id;};
     struct PipelineHandle   {uint64_t id;};
+    struct ResourcesHandle  {uint64_t id;};
 }
diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp
index 9dc5fa1663c2428fe7d33b92aa353e313463bdca..ac043b2d351014ea79fcae0d0fc439bb64a87b72 100644
--- a/include/vkcv/QueueManager.hpp
+++ b/include/vkcv/QueueManager.hpp
@@ -2,7 +2,9 @@
 #include <vulkan/vulkan.hpp>
 
 namespace vkcv {
-	
+
+	enum class QueueType { Compute, Transfer, Graphics, Present };
+
 	struct Queue {
 		int familyIndex;
 		int queueIndex;
diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp
index 4456594722a30f73128a864714bf4690b2902525..c41019cc46ee1375a83323a6ecc877ecc1c1727a 100644
--- a/include/vkcv/SyncResources.hpp
+++ b/include/vkcv/SyncResources.hpp
@@ -3,11 +3,13 @@
 
 namespace vkcv {
 	struct SyncResources {
-		vk::Semaphore renderFinished;
-		vk::Fence swapchainImageAcquired;
-		vk::Fence presentFinished;
+		vk::Semaphore	renderFinished;
+		vk::Semaphore	swapchainImageAcquired;
+		vk::Fence		presentFinished;
 	};
 
-	SyncResources createDefaultSyncResources(const vk::Device& device);
-	void destroySyncResources(const vk::Device& device, const SyncResources& resources);
+	SyncResources	createSyncResources(const vk::Device &device);
+	void			destroySyncResources(const vk::Device &device, const SyncResources &resources);
+	vk::Fence		createFence(const vk::Device &device);
+	void			waitForFence(const vk::Device& device, const vk::Fence fence);
 }
\ No newline at end of file
diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt
index 0a5d8ee574475fa24e217ff2be05202028ff641c..e7c8373e085df6497060b8d1d8164cf740dfb01f 100644
--- a/projects/first_triangle/CMakeLists.txt
+++ b/projects/first_triangle/CMakeLists.txt
@@ -15,6 +15,10 @@ add_executable(first_triangle src/main.cpp)
 if(MSVC)
 	set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
 	set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+
+	# in addition to setting the output directory, the working directory has to be set
+	# by default visual studio sets the working directory to the build directory, when using the debugger
+	set_target_properties(first_triangle PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
 endif()
 
 # including headers of dependencies and the VkCV framework
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 7908831a464127306d90759a431cd221938b9d28..ff126193d94676ab966dfd3e4410c2913b54988a 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -4,7 +4,7 @@
 #include <vkcv/ShaderProgram.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
-
+#include <vkcv/DescriptorConfig.hpp>
 
 int main(int argc, const char** argv) {
     const char* applicationName = "First Triangle";
@@ -40,12 +40,22 @@ int main(int argc, const char** argv) {
 		float x, y, z;
 	};
 	
-	auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, 3);
+	const size_t n = 5027;
 	
-	vec3* m = buffer.map();
-	m[0] = { 0, 0, 0 };
-	m[0] = { 0, 0, 0 };
+	auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL);
+	vec3 vec_data [n];
+	
+	for (size_t i = 0; i < n; i++) {
+		vec_data[i] = { 42, static_cast<float>(i), 7 };
+	}
+	
+	buffer.fill(vec_data);
+	
+	/*vec3* m = buffer.map();
 	m[0] = { 0, 0, 0 };
+	m[1] = { 0, 0, 0 };
+	m[2] = { 0, 0, 0 };
+	buffer.unmap();*/
 
 	std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl;
 
@@ -88,6 +98,24 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
+	//just an example
+	//creates 20 descriptor sets, each containing bindings for 50 uniform buffers, images, and samplers
+	std::vector<vkcv::DescriptorSet> sets;
+
+	for (uint32_t i = 0; i < 20; i++)
+	{
+		vkcv::DescriptorBinding uniformBufBinding(vkcv::DescriptorType::UNIFORM_BUFFER, 50, vkcv::ShaderStage::VERTEX);
+        vkcv::DescriptorBinding storageBufBinding(vkcv::DescriptorType::STORAGE_BUFFER, 50, vkcv::ShaderStage::VERTEX);
+        vkcv::DescriptorBinding imageBinding(vkcv::DescriptorType::IMAGE, 50, vkcv::ShaderStage::VERTEX);
+        vkcv::DescriptorBinding samplerBinding(vkcv::DescriptorType::SAMPLER, 50, vkcv::ShaderStage::VERTEX);
+
+        vkcv::DescriptorSet set({uniformBufBinding, storageBufBinding, imageBinding, samplerBinding});
+
+		sets.push_back(set);
+        auto resourceHandle = core.createResourceDescription(sets);
+        std::cout << "Resource " << resourceHandle.id << " created." << std::endl;
+	}
+
 	/*
 	 * BufferHandle triangleVertices = core.createBuffer(vertices);
 	 * BufferHandle triangleIndices = core.createBuffer(indices);
diff --git a/src/vkcv/Buffer.cpp b/src/vkcv/Buffer.cpp
deleted file mode 100644
index 956f680064b30ac5e22ce41c622e2cb96ccecd46..0000000000000000000000000000000000000000
--- a/src/vkcv/Buffer.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @authors Lars Hoerttrich
- * @file src/vkcv/Buffer.cpp
- * @brief Implementation of template buffer class, template for type security
- */
-#include"vkcv/Buffer.hpp"
-
-namespace vkcv {
-
-	/**
-	 * @brief searches memory type index for buffer allocation, inspired by vulkan tutorial and "https://github.com/KhronosGroup/Vulkan-Hpp/blob/master/samples/utils/utils.hpp"
-	 * @param physicalMemoryProperties Memory Properties of physical device
-	 * @param typeBits
-	 * @param requirements Property flags that are required
-	 * @return memory type index for Buffer
-	 */
-	uint32_t searchMemoryType(vk::PhysicalDeviceMemoryProperties const& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) {
-		uint32_t memoryTypeIndex = uint32_t(0);
-		for (uint32_t i = 0; i < physicalMemoryProperties.memoryTypeCount; i++)
-		{
-			if ((typeBits & 1) &&
-				((physicalMemoryProperties.memoryTypes[i].propertyFlags & requirements) == requirements))
-			{
-				memoryTypeIndex = i;
-				break;
-			}
-			typeBits >>= 1;
-		}
-		return memoryTypeIndex;
-	}
-
-	void outsourcedDestructor(vk::Device device, vk::DeviceMemory bufferMemory, vk::Buffer buffer)
-	{
-		if (device) {
-			device.freeMemory(bufferMemory);
-			device.destroyBuffer(buffer);
-		}
-	}
-
-	vk::Buffer outsourcedCreateBuffer(vk::Device device, BufferType type, size_t size)
-	{
-		switch (type) {
-		case VERTEX: {
-			//create vertex buffer
-			return device.createBuffer(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, vk::BufferUsageFlagBits::eVertexBuffer));
-		}
-		default: {
-			// TODO: maybe an issue
-		}
-		}
-
-		return vk::Buffer(); //should never be reached
-	}
-
-	vk::DeviceMemory outsourcedAllocateMemory(vk::Device device, vk::PhysicalDevice physicalDevice, vk::MemoryRequirements memoryRequirements)
-	{
-		//find Memory Type
-		uint32_t memoryType = searchMemoryType(physicalDevice.getMemoryProperties(), memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
-		return device.allocateMemory(vk::MemoryAllocateInfo(memoryRequirements.size, memoryType));
-	}
-}
\ No newline at end of file
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb2425a51ee177d062120b0eb61ae089f6f1692f
--- /dev/null
+++ b/src/vkcv/BufferManager.cpp
@@ -0,0 +1,290 @@
+/**
+ * @author Tobias Frisch
+ * @file vkcv/BufferManager.cpp
+ */
+
+#include "vkcv/BufferManager.hpp"
+#include "vkcv/Core.hpp"
+
+namespace vkcv {
+	
+	BufferManager::BufferManager() noexcept :
+		m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX)
+	{
+	}
+	
+	void BufferManager::init() {
+		if (!m_core) {
+			return;
+		}
+		
+		m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE);
+	}
+	
+	BufferManager::~BufferManager() noexcept {
+		for (size_t id = 0; id < m_buffers.size(); id++) {
+			destroyBuffer(id);
+		}
+	}
+	
+	/**
+	 * @brief searches memory type index for buffer allocation, inspired by vulkan tutorial and "https://github.com/KhronosGroup/Vulkan-Hpp/blob/master/samples/utils/utils.hpp"
+	 * @param physicalMemoryProperties Memory Properties of physical device
+	 * @param typeBits
+	 * @param requirements Property flags that are required
+	 * @return memory type index for Buffer
+	 */
+	uint32_t searchMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) {
+		uint32_t memoryTypeIndex = 0;
+		
+		for (uint32_t i = 0; i < physicalMemoryProperties.memoryTypeCount; i++)
+		{
+			if ((typeBits & 1) &&
+				((physicalMemoryProperties.memoryTypes[i].propertyFlags & requirements) == requirements))
+			{
+				memoryTypeIndex = i;
+				break;
+			}
+			
+			typeBits >>= 1;
+		}
+		
+		return memoryTypeIndex;
+	}
+	
+	uint64_t BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) {
+		vk::BufferCreateFlags createFlags;
+		vk::BufferUsageFlags usageFlags;
+		
+		switch (type) {
+			case BufferType::VERTEX:
+				usageFlags = vk::BufferUsageFlagBits::eVertexBuffer;
+				break;
+			case BufferType::UNIFORM:
+				usageFlags = vk::BufferUsageFlagBits::eUniformBuffer;
+				break;
+			case BufferType::STORAGE:
+				usageFlags = vk::BufferUsageFlagBits::eStorageBuffer;
+				break;
+			case BufferType::STAGING:
+				usageFlags = vk::BufferUsageFlagBits::eTransferSrc;
+				break;
+			default:
+				// TODO: maybe an issue
+				break;
+		}
+		
+		if (memoryType == BufferMemoryType::DEVICE_LOCAL) {
+			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
+		}
+		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
+		vk::Buffer buffer = device.createBuffer(
+				vk::BufferCreateInfo(createFlags, size, usageFlags)
+		);
+		
+		const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer);
+		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
+		
+		vk::MemoryPropertyFlags memoryTypeFlags;
+		bool mappable = false;
+		
+		switch (memoryType) {
+			case BufferMemoryType::DEVICE_LOCAL:
+				memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
+				break;
+			case BufferMemoryType::HOST_VISIBLE:
+				memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
+				mappable = true;
+				break;
+			default:
+				// TODO: maybe an issue
+				break;
+		}
+		
+		const uint32_t memoryTypeIndex = searchMemoryType(
+				physicalDevice.getMemoryProperties(),
+				requirements.memoryTypeBits,
+				memoryTypeFlags
+		);
+		
+		vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex));
+		
+		device.bindBufferMemory(buffer, memory, 0);
+		
+		const uint64_t id = m_buffers.size();
+		m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
+		return id;
+	}
+	
+	struct StagingStepInfo {
+		void* data;
+		size_t size;
+		size_t offset;
+		
+		vk::Buffer buffer;
+		vk::Buffer stagingBuffer;
+		vk::DeviceMemory stagingMemory;
+		
+		size_t stagingLimit;
+		size_t stagingPosition;
+	};
+	
+	/**
+	 * Copies data from CPU to a staging buffer and submits the commands to copy
+	 * each part one after another into the actual target buffer.
+	 *
+	 * The function can be used fully asynchronously!
+	 * Just be careful to not use the staging buffer in parallel!
+	 *
+	 * @param core Core instance
+	 * @param info Staging-info structure
+	 */
+	void copyFromStagingBuffer(Core* core, StagingStepInfo& info) {
+		const size_t remaining = info.size - info.stagingPosition;
+		const size_t mapped_size = std::min(remaining, info.stagingLimit);
+		
+		const vk::Device& device = core->getContext().getDevice();
+		
+		void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size);
+		memcpy(mapped, reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped_size);
+		device.unmapMemory(info.stagingMemory);
+		
+		SubmitInfo submitInfo;
+		submitInfo.queueType = QueueType::Transfer;
+		
+		core->submitCommands(
+				submitInfo,
+				[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
+					const vk::BufferCopy region (
+							0,
+							info.offset + info.stagingPosition,
+							mapped_size
+					);
+					
+					commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, &region);
+				},
+				[&core, &info, &mapped_size, &remaining]() {
+					if (mapped_size < remaining) {
+						info.stagingPosition += mapped_size;
+						
+						copyFromStagingBuffer(
+								core,
+								info
+						);
+					}
+				}
+		);
+	}
+	
+	void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) {
+		if (size == 0) {
+			size = SIZE_MAX;
+		}
+		
+		if (id >= m_buffers.size()) {
+			return;
+		}
+		
+		auto& buffer = m_buffers[id];
+		
+		if (buffer.m_mapped) {
+			return;
+		}
+		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
+		if (offset > buffer.m_size) {
+			return;
+		}
+		
+		const size_t max_size = std::min(size, buffer.m_size - offset);
+		
+		if (buffer.m_mappable) {
+			void* mapped = device.mapMemory(buffer.m_memory, offset, max_size);
+			memcpy(mapped, data, max_size);
+			device.unmapMemory(buffer.m_memory);
+		} else {
+			auto& stagingBuffer = m_buffers[m_stagingBuffer];
+			
+			StagingStepInfo info;
+			info.data = data;
+			info.size = std::min(size, max_size - offset);
+			info.offset = offset;
+			
+			info.buffer = buffer.m_handle;
+			info.stagingBuffer = stagingBuffer.m_handle;
+			info.stagingMemory = stagingBuffer.m_memory;
+			
+			const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle);
+			
+			info.stagingLimit = stagingRequirements.size;
+			info.stagingPosition = 0;
+			
+			copyFromStagingBuffer(m_core, info);
+		}
+	}
+	
+	void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) {
+		if (size == 0) {
+			size = SIZE_MAX;
+		}
+		
+		if (id >= m_buffers.size()) {
+			return nullptr;
+		}
+		
+		auto& buffer = m_buffers[id];
+		
+		if (buffer.m_mapped) {
+			return nullptr;
+		}
+		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
+		if (offset > buffer.m_size) {
+			return nullptr;
+		}
+		
+		const size_t max_size = std::min(size, buffer.m_size - offset);
+		buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_size);
+		return buffer.m_mapped;
+	}
+	
+	void BufferManager::unmapBuffer(uint64_t id) {
+		if (id >= m_buffers.size()) {
+			return;
+		}
+		
+		auto& buffer = m_buffers[id];
+		
+		if (buffer.m_mapped == nullptr) {
+			return;
+		}
+		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
+		device.unmapMemory(buffer.m_memory);
+		buffer.m_mapped = nullptr;
+	}
+	
+	void BufferManager::destroyBuffer(uint64_t id) {
+		if (id >= m_buffers.size()) {
+			return;
+		}
+		
+		auto& buffer = m_buffers[id];
+		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
+		if (buffer.m_memory) {
+			device.freeMemory(buffer.m_memory);
+		}
+		
+		if (buffer.m_handle) {
+			device.destroyBuffer(buffer.m_handle);
+		}
+	}
+
+}
diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp
index 451ec4f27b3bc68e6a787bb79d1dc12f59a086aa..71c990c3c222f2318c2f5744ff6295f667d9e6f8 100644
--- a/src/vkcv/CommandResources.cpp
+++ b/src/vkcv/CommandResources.cpp
@@ -1,23 +1,86 @@
 #include "vkcv/CommandResources.hpp"
+#include <iostream>
+
 
 namespace vkcv {
-	CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex) {
+
+	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;
-		vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
-		vk::CommandPoolCreateInfo poolCreateInfo(flags, graphicFamilyIndex);
-		resources.commandPool = device.createCommandPool(poolCreateInfo, nullptr, {});
+		const size_t queueFamiliesCount = familyIndexSet.size();
+		resources.cmdPoolPerQueueFamily.resize(queueFamiliesCount);
 
-		const int commandPoolCount = 1;
-		vk::CommandBufferAllocateInfo allocateInfo(resources.commandPool, vk::CommandBufferLevel::ePrimary, commandPoolCount);
-		const std::vector<vk::CommandBuffer> createdBuffers = device.allocateCommandBuffers(allocateInfo, {});
-		assert(createdBuffers.size() == 1);
-		resources.commandBuffer = createdBuffers[0];
+		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) {
-		device.freeCommandBuffers(resources.commandPool, resources.commandBuffer, {});
-		device.destroyCommandPool(resources.commandPool, {});
+		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(const 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 {
+			std::cerr << "getQueueForSubmit error: unknown queue type" << std::endl;
+			return queueManager.getGraphicsQueues().front();	// graphics is the most general queue
+		}
+	}
+
+	void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags) {
+		const vk::CommandBufferBeginInfo beginInfo(flags);
+		cmdBuffer.begin(beginInfo);
+	}
+
+	void submitCommandBufferToQueue(
+		const 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/Core.cpp b/src/vkcv/Core.cpp
index 2a8dddfc57f767089079aa17cc28bae5dcb3e555..0d61d2adf04b23b45fc4e1d7637a22c0ec3d9ccc 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -9,6 +9,8 @@
 #include "vkcv/Core.hpp"
 #include "PassManager.hpp"
 #include "PipelineManager.hpp"
+#include "vkcv/BufferManager.hpp"
+#include "DescriptorManager.hpp"
 #include "Surface.hpp"
 #include "ImageLayoutTransitions.hpp"
 #include "Framebuffer.hpp"
@@ -66,15 +68,16 @@ namespace vkcv
 
         const auto& queueManager = context.getQueueManager();
         
-		const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex;
-		const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex);
-		const auto defaultSyncResources = createDefaultSyncResources(context.getDevice());
+		const int						graphicQueueFamilyIndex	= queueManager.getGraphicsQueues()[0].familyIndex;
+		const std::unordered_set<int>	queueFamilySet			= generateQueueFamilyIndexSet(queueManager);
+		const auto						commandResources		= createCommandResources(context.getDevice(), queueFamilySet);
+		const auto						defaultSyncResources	= createSyncResources(context.getDevice());
 
         window.e_resize.add([&](int width, int height){
             recreateSwapchain(width,height);
         });
 
-        return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources);
+        return Core(std::move(context) , window, swapChain, imageViews, commandResources, defaultSyncResources);
     }
 
     const Context &Core::getContext() const
@@ -90,9 +93,14 @@ namespace vkcv
             m_swapchainImageViews(imageViews),
             m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
             m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)},
+            m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)),
+			m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())},
             m_CommandResources(commandResources),
             m_SyncResources(syncResources)
-	{}
+	{
+    	m_BufferManager->m_core = this;
+    	m_BufferManager->init();
+	}
 
 	Core::~Core() noexcept {
 		m_Context.getDevice().waitIdle();
@@ -124,25 +132,17 @@ namespace vkcv
     	uint32_t imageIndex;
     	
 		const auto& acquireResult = m_Context.getDevice().acquireNextImageKHR(
-				m_swapchain.getSwapchain(), std::numeric_limits<uint64_t>::max(), nullptr,
-				m_SyncResources.swapchainImageAcquired, &imageIndex, {}
+			m_swapchain.getSwapchain(), 
+			std::numeric_limits<uint64_t>::max(), 
+			m_SyncResources.swapchainImageAcquired,
+			nullptr, 
+			&imageIndex, {}
 		);
 		
 		if (acquireResult != vk::Result::eSuccess) {
 			return Result::ERROR;
 		}
 		
-		const auto& result = m_Context.getDevice().waitForFences(
-				m_SyncResources.swapchainImageAcquired, true,
-				std::numeric_limits<uint64_t>::max()
-		);
-		
-		m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired);
-		
-		if (result != vk::Result::eSuccess) {
-			return Result::ERROR;
-		}
-		
 		m_currentSwapchainImageIndex = imageIndex;
 		return Result::SUCCESS;
 	}
@@ -161,9 +161,6 @@ namespace vkcv
 		m_window.pollEvents();
 		m_Context.getDevice().waitIdle();	// FIMXE: this is a sin against graphics programming, but its getting late - Alex
 		destroyTemporaryFramebuffers();
-		const vk::CommandBufferUsageFlags beginFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
-		const vk::CommandBufferBeginInfo beginInfos(beginFlags);
-		m_CommandResources.commandBuffer.begin(beginInfos);
 	}
 
 	void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, 
@@ -172,24 +169,33 @@ namespace vkcv
 		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
 			return;
 		}
-  
+
 		const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
-		const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
-		const vk::ClearValue clearValues(clearColor);
+		const vk::ImageView imageView	= m_swapchainImageViews[m_currentSwapchainImageIndex];
+		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));
-		const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex];
+
 		const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView);
 		m_TemporaryFramebuffers.push_back(framebuffer);
-		const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
-		const vk::SubpassContents subpassContents = {};
-		m_CommandResources.commandBuffer.beginRenderPass(beginInfo, subpassContents, {});
-
-		const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
-		const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
-		m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
-		m_CommandResources.commandBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData);
-		m_CommandResources.commandBuffer.draw(3, 1, 0, 0, {});
-		m_CommandResources.commandBuffer.endRenderPass();
+
+		SubmitInfo submitInfo;
+		submitInfo.queueType = QueueType::Graphics;
+		submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
+		submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline, pipelineLayout, pushConstantSize, pushConstantData](const vk::CommandBuffer& cmdBuffer) {
+
+			const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
+			const vk::ClearValue clearValues(clearColor);
+
+			const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
+			const vk::SubpassContents subpassContents = {};
+			cmdBuffer.beginRenderPass(beginInfo, subpassContents, {});
+
+			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
+            cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData);
+			cmdBuffer.draw(3, 1, 0, 0, {});
+			cmdBuffer.endRenderPass();
+		}, nullptr);
 	}
 
 	void Core::endFrame() {
@@ -199,18 +205,19 @@ namespace vkcv
   
 		const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
 		const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex];
-
-		m_CommandResources.commandBuffer.end();
 		
 		const auto& queueManager = m_Context.getQueueManager();
-		
-		const vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished);
-		queueManager.getGraphicsQueues()[0].handle.submit(submitInfo);
+		std::array<vk::Semaphore, 2> waitSemaphores{ 
+			m_SyncResources.renderFinished, 
+			m_SyncResources.swapchainImageAcquired };
 
 		vk::Result presentResult;
 		const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
-		const vk::PresentInfoKHR presentInfo(1, &m_SyncResources.renderFinished, 1, &swapchain, 
-			&m_currentSwapchainImageIndex, &presentResult);
+		const vk::PresentInfoKHR presentInfo(
+			waitSemaphores,
+			swapchain,
+			m_currentSwapchainImageIndex, 
+			presentResult);
         queueManager.getPresentQueue().handle.presentKHR(presentInfo);
 		if (presentResult != vk::Result::eSuccess) {
 			std::cout << "Error: swapchain present failed" << std::endl;
@@ -225,4 +232,33 @@ namespace vkcv
         /* boilerplate for #34 */
         std::cout << "Resized to : " << width << " , " << height << std::endl;
     }
+	
+	void Core::submitCommands(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();
+
+		const 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();
+		}
+	}
+
+    ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets)
+    {
+        return m_DescriptorManager->createResourceDescription(descriptorSets);
+    }
 }
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f437ab6833edca09f58499b6d74307606bba8999
--- /dev/null
+++ b/src/vkcv/DescriptorConfig.cpp
@@ -0,0 +1,20 @@
+#include "vkcv/DescriptorConfig.hpp"
+
+#include <utility>
+
+namespace vkcv {
+
+    DescriptorBinding::DescriptorBinding(
+        DescriptorType descriptorType,
+        uint32_t descriptorCount,
+        ShaderStage shaderStage
+    ) noexcept :
+        descriptorType{descriptorType},
+        descriptorCount{descriptorCount},
+        shaderStage{shaderStage}
+    {};
+
+    DescriptorSet::DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept :
+        bindings{std::move(bindings)}
+    {};
+}
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bcebb142af2f29c23e77185112cbb83a827e41ba
--- /dev/null
+++ b/src/vkcv/DescriptorManager.cpp
@@ -0,0 +1,125 @@
+#include "DescriptorManager.hpp"
+
+namespace vkcv
+{
+    DescriptorManager::ResourceDescription::ResourceDescription(std::vector<vk::DescriptorSet> sets,
+                                                                std::vector<vk::DescriptorSetLayout> layouts) noexcept :
+    descriptorSets{std::move(sets)},
+    descriptorSetLayouts{std::move(layouts)}
+    {}
+    DescriptorManager::DescriptorManager(vk::Device device) noexcept:
+        m_Device{ device }, m_NextResourceDescriptionID{ 1 }
+    {
+        /**
+         * Allocate a set size for the initial pool, namely 1000 units of each descriptor type below.
+         */
+        const std::vector<vk::DescriptorPoolSize> 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::DescriptorPoolCreateInfo poolInfo({},
+                                              1000,
+                                              static_cast<uint32_t>(poolSizes.size()),
+                                              poolSizes.data());
+
+        if(m_Device.createDescriptorPool(&poolInfo, nullptr, &m_Pool) != vk::Result::eSuccess)
+        {
+            std::cout << "FAILED TO ALLOCATED DESCRIPTOR POOL." << std::endl;
+            m_Pool = nullptr;
+        };
+    }
+
+    DescriptorManager::~DescriptorManager() noexcept
+    {
+        for(const auto &resource : m_ResourceDescriptions)
+        {
+            for(const auto &layout : resource.descriptorSetLayouts)
+                m_Device.destroyDescriptorSetLayout(layout);
+        }
+        m_Device.destroy(m_Pool);
+    }
+
+    ResourcesHandle DescriptorManager::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets)
+    {
+        std::vector<vk::DescriptorSet> vk_sets;
+        std::vector<vk::DescriptorSetLayout> vk_setLayouts;
+
+        for (const auto &set : descriptorSets) {
+            std::vector<vk::DescriptorSetLayoutBinding> setBindings = {};
+
+            //create each set's binding
+            for (uint32_t j = 0; j < set.bindings.size(); j++) {
+                vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
+                        j,
+                        convertDescriptorTypeFlag(set.bindings[j].descriptorType),
+                        set.bindings[j].descriptorCount,
+                        convertShaderStageFlag(set.bindings[j].shaderStage));
+                setBindings.push_back(descriptorSetLayoutBinding);
+            }
+
+            //create the descriptor set's layout from the bindings gathered above
+            vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings);
+            vk::DescriptorSetLayout layout = nullptr;
+            if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &layout) != vk::Result::eSuccess)
+            {
+                std::cout << "FAILED TO CREATE DESCRIPTOR SET LAYOUT" << std::endl;
+                return ResourcesHandle{0};
+            };
+            vk_setLayouts.push_back(layout);
+        }
+        //create and allocate the set(s) based on the layouts that have been gathered above
+        vk_sets.resize(vk_setLayouts.size());
+        vk::DescriptorSetAllocateInfo allocInfo(m_Pool, vk_sets.size(), vk_setLayouts.data());
+        auto result = m_Device.allocateDescriptorSets(&allocInfo, vk_sets.data());
+        if(result != vk::Result::eSuccess)
+        {
+            std::cout << "FAILED TO ALLOCATE DESCRIPTOR SET" << std::endl;
+            std::cout << vk::to_string(result) << std::endl;
+            for(const auto &layout : vk_setLayouts)
+                m_Device.destroy(layout);
+
+            return ResourcesHandle{0};
+        };
+
+        m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts);
+        return ResourcesHandle{m_NextResourceDescriptionID++};
+    }
+
+    vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) {
+        switch (type)
+        {
+            case DescriptorType::UNIFORM_BUFFER:
+                return vk::DescriptorType::eUniformBuffer;
+            case DescriptorType::STORAGE_BUFFER:
+                return vk::DescriptorType::eStorageBuffer;
+            case DescriptorType::SAMPLER:
+                return vk::DescriptorType::eSampler;
+            case DescriptorType::IMAGE:
+                return vk::DescriptorType::eSampledImage;
+            default:
+                return vk::DescriptorType::eUniformBuffer;
+        }
+    }
+
+    vk::ShaderStageFlagBits DescriptorManager::convertShaderStageFlag(ShaderStage stage) {
+        switch (stage) 
+        {
+            case ShaderStage::VERTEX:
+                return vk::ShaderStageFlagBits::eVertex;
+            case ShaderStage::FRAGMENT:
+                return vk::ShaderStageFlagBits::eFragment;
+            case ShaderStage::TESS_CONTROL:
+                return vk::ShaderStageFlagBits::eTessellationControl;
+            case ShaderStage::TESS_EVAL:
+                return vk::ShaderStageFlagBits::eTessellationEvaluation;
+            case ShaderStage::GEOMETRY:
+                return vk::ShaderStageFlagBits::eGeometry;
+            case ShaderStage::COMPUTE:
+                return vk::ShaderStageFlagBits::eCompute;
+            default:
+                return vk::ShaderStageFlagBits::eAll;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..744f557051bee3a6524abcf16822c3faa9c78576
--- /dev/null
+++ b/src/vkcv/DescriptorManager.hpp
@@ -0,0 +1,61 @@
+#include <vulkan/vulkan.hpp>
+
+#include "vkcv/Handles.hpp"
+#include "vkcv/DescriptorConfig.hpp"
+
+namespace vkcv
+{
+	class DescriptorManager
+	{
+	public:
+	    explicit DescriptorManager(vk::Device device) noexcept;
+	    ~DescriptorManager() noexcept;
+
+		/**
+		* Creates all vk::DescriptorSets and allocates them from the pool. 
+		* DescriptorSets are put inside a ResourceDescription struct. 
+		* Structs are then put into m_ResourceDescriptions.
+		* @param[in] vector of filled vkcv::DescriptorSet structs
+		* @return index into that objects a resource handle
+		*/
+        ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> & descriptorSets);
+
+	private:
+		vk::Device m_Device;
+        vk::DescriptorPool m_Pool;
+
+
+		/**
+		* Container for all resources requested by the user in one call of createResourceDescription.
+		* Includes descriptor sets and the respective descriptor set layouts.
+		*/
+        struct ResourceDescription
+        {
+            ResourceDescription() = delete;
+            ResourceDescription(std::vector<vk::DescriptorSet> sets, std::vector<vk::DescriptorSetLayout> layouts) noexcept;
+
+            std::vector<vk::DescriptorSet> descriptorSets;
+            std::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
+        };
+
+		/**
+		* Contains all the resource descriptions that were requested by the user in calls of createResourceDescription.
+		*/
+        std::vector<ResourceDescription> m_ResourceDescriptions;
+        // Counter for the vector above
+        uint64_t m_NextResourceDescriptionID;
+		
+		/**
+		* Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk).
+		* @param[in] vkcv flag of the DescriptorType (see DescriptorConfig.hpp)
+		* @return vk flag of the DescriptorType
+		*/
+		static vk::DescriptorType convertDescriptorTypeFlag(DescriptorType type);
+		/**
+		* Converts the flags of the shader stages from VulkanCV (vkcv) to Vulkan (vk).
+		* @param[in] vkcv flag of the ShaderStage (see ShaderProgram.hpp)
+		* @return vk flag of the ShaderStage
+		*/
+		static vk::ShaderStageFlagBits convertShaderStageFlag(ShaderStage stage);
+	};
+}
\ No newline at end of file
diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp
index 10d582a20501e1cc8d0bb9a98aa95b09d699c22c..9c27fe32452e0ae648565020d92891764ececb3f 100644
--- a/src/vkcv/SyncResources.cpp
+++ b/src/vkcv/SyncResources.cpp
@@ -1,24 +1,33 @@
 #include "vkcv/SyncResources.hpp"
 
 namespace vkcv {
-	SyncResources createDefaultSyncResources(const vk::Device& device) {
+	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, {});
-
-		const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
-		vk::FenceCreateInfo fenceInfo(fenceFlags);
-		resources.presentFinished = device.createFence(fenceInfo, nullptr, {});
-		resources.swapchainImageAcquired = device.createFence(fenceInfo, nullptr, {});
+		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);
-		device.destroyFence(resources.swapchainImageAcquired);
+	}
+
+	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