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