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 065c21d225810f465f0909cb8c1479c1031aba8f..0712be4887d8d27185bc7ccdd0a4f6c318296ee4 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -24,6 +24,15 @@ namespace vkcv
     class PassManager;
     class PipelineManager;
 
+	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
     {
     private:
@@ -172,5 +181,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/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/src/main.cpp b/projects/first_triangle/src/main.cpp
index d718da0989251b7a992d020324c2bf2131dbf19e..08097ef062075e29a5e60a7f540db2f462bb06af 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -16,30 +16,6 @@ int main(int argc, const char** argv) {
 		false
     );
 
-    // showing basic usage lambda events of window
-    window.e_mouseMove.add([&](double x, double y){
-        std::cout << "movement: " << x << " , " << y << std::endl;
-    });
-
-    window.e_key.add([&](int key, int scancode, int action, int mods){
-        switch (key) {
-            case GLFW_KEY_W:
-                std::cout << "Move forward" << std::endl;
-                break;
-            case GLFW_KEY_A:
-                std::cout << "Move left" << std::endl;
-                break;
-            case GLFW_KEY_S:
-                std::cout << "Move backward" << std::endl;
-                break;
-            case GLFW_KEY_D:
-                std::cout << "Move right" << std::endl;
-                break;
-            default:
-                std::cout << "this key is not supported yet: " << std::endl;
-        }
-    });
-
     window.initEvents();
 
 	vkcv::Core core = vkcv::Core::create(
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 488715116cbb3b978dfab79e1e0e3d8e05c0f4dc..e5e847ab8fd112ef8516305fd73e2f2209da0024 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -66,15 +66,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
@@ -124,25 +125,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 +154,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, 
@@ -171,22 +161,31 @@ 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::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);
-		m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
-		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](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.draw(3, 1, 0, 0, {});
+			cmdBuffer.endRenderPass();
+		}, nullptr);
 	}
 
 	void Core::endFrame() {
@@ -196,18 +195,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;
@@ -222,4 +222,28 @@ 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();
+		}
+	}
 }
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