From 6b71e042b41c08fd84d9776e0ee6afb553537488 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Tue, 25 May 2021 14:03:48 +0200
Subject: [PATCH] [#39]implement submit function

---
 include/vkcv/CommandResources.hpp    | 13 ++--
 include/vkcv/Core.hpp                | 12 ++++
 include/vkcv/SyncResources.hpp       |  5 +-
 projects/first_triangle/src/main.cpp | 24 -------
 src/vkcv/CommandResources.cpp        | 47 ++++++++++----
 src/vkcv/Core.cpp                    | 94 +++++++++++++++++++++-------
 src/vkcv/SyncResources.cpp           | 12 ++--
 7 files changed, 138 insertions(+), 69 deletions(-)

diff --git a/include/vkcv/CommandResources.hpp b/include/vkcv/CommandResources.hpp
index 05e84829..197227a1 100644
--- a/include/vkcv/CommandResources.hpp
+++ b/include/vkcv/CommandResources.hpp
@@ -1,12 +1,17 @@
 #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);
 }
\ No newline at end of file
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 065c21d2..0a843c4c 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -24,6 +24,13 @@ namespace vkcv
     class PassManager;
     class PipelineManager;
 
+	enum class QueueType { Compute, Transfer, Graphics, Present };
+	struct SubmitInfo {
+		QueueType queueType;
+		std::vector<vk::Semaphore> waitSemaphores;
+		std::vector<vk::Semaphore> signalSemaphores;
+	};
+
     class Core final
     {
     private:
@@ -172,5 +179,10 @@ namespace vkcv
 		void endFrame();
 
 		vk::Format getSwapchainImageFormat();
+
+		void submitCommands(
+			const SubmitInfo &submitInfo,
+			const std::function<void(vk::CommandBuffer cmdBuffer)> recording, 
+			const std::function<void()> finishCallback);
     };
 }
diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp
index 44565947..c399631f 100644
--- a/include/vkcv/SyncResources.hpp
+++ b/include/vkcv/SyncResources.hpp
@@ -8,6 +8,7 @@ namespace vkcv {
 		vk::Fence presentFinished;
 	};
 
-	SyncResources createDefaultSyncResources(const vk::Device& device);
-	void destroySyncResources(const vk::Device& device, const SyncResources& resources);
+	SyncResources createDefaultSyncResources(const vk::Device &device);
+	void destroySyncResources(const vk::Device &device, const SyncResources &resources);
+	vk::Fence createFence(const vk::Device &device);
 }
\ No newline at end of file
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index d718da09..08097ef0 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 451ec4f2..69028620 100644
--- a/src/vkcv/CommandResources.cpp
+++ b/src/vkcv/CommandResources.cpp
@@ -1,23 +1,48 @@
 #include "vkcv/CommandResources.hpp"
 
 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];
 	}
 }
\ No newline at end of file
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 48871511..7df30529 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	= createDefaultSyncResources(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
@@ -161,9 +162,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 +169,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,13 +203,8 @@ 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);
 
 		vk::Result presentResult;
 		const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
@@ -222,4 +224,48 @@ namespace vkcv
         /* boilerplate for #34 */
         std::cout << "Resized to : " << width << " , " << height << std::endl;
     }
+
+	void Core::submitCommands(
+		const SubmitInfo& submitInfo,
+		const std::function<void(vk::CommandBuffer cmdBuffer)> recording,
+		const std::function<void()> finishCallback) {
+
+		vkcv::Queue queue;
+		if (submitInfo.queueType == QueueType::Graphics) {
+			queue = m_Context.getQueueManager().getGraphicsQueues().front();
+		}
+		else if (submitInfo.queueType == QueueType::Compute) {
+			queue = m_Context.getQueueManager().getComputeQueues().front();
+		}
+		else if (submitInfo.queueType == QueueType::Transfer) {
+			queue = m_Context.getQueueManager().getTransferQueues().front();
+		}
+		else if (submitInfo.queueType == QueueType::Present) {
+			queue = m_Context.getQueueManager().getPresentQueue();
+		}
+		else {
+			std::cerr << "Unknown queue type" << std::endl;
+			return;
+		}
+
+		const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources);
+		const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(m_Context.getDevice(), cmdPool);
+		const vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+		cmdBuffer.begin(beginInfo);
+		recording(cmdBuffer);
+		cmdBuffer.end();
+		const std::vector<vk::PipelineStageFlags> waitDstStageMasks(submitInfo.waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands);
+		vk::SubmitInfo queueSubmitInfo(submitInfo.waitSemaphores, waitDstStageMasks, cmdBuffer, submitInfo.signalSemaphores);
+		if (finishCallback) {
+			vk::Fence waitFence = createFence(m_Context.getDevice());
+			queue.handle.submit(queueSubmitInfo, waitFence);
+			const auto result = m_Context.getDevice().waitForFences(waitFence, true, UINT64_MAX);
+			assert(result == vk::Result::eSuccess);
+			m_Context.getDevice().destroyFence(waitFence);
+			finishCallback();
+		}
+		else {
+			queue.handle.submit(queueSubmitInfo);
+		}
+	}
 }
diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp
index 10d582a2..01833da6 100644
--- a/src/vkcv/SyncResources.cpp
+++ b/src/vkcv/SyncResources.cpp
@@ -8,10 +8,8 @@ namespace vkcv {
 		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.presentFinished			= createFence(device);
+		resources.swapchainImageAcquired	= createFence(device);
 
 		return resources;
 	}
@@ -21,4 +19,10 @@ namespace vkcv {
 		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, {});
+	}
 }
\ No newline at end of file
-- 
GitLab