From 369d3dcfac02b30a9b615d2a6b5396ff1d65f30a Mon Sep 17 00:00:00 2001 From: Alexander Gauggel <agauggel@uni-koblenz.de> Date: Sat, 5 Jun 2021 15:25:03 +0200 Subject: [PATCH] [#66] Added concept of command stream and started layout transition rework --- config/Sources.cmake | 5 + .../vkcv/CommandRecordingFunctionTypes.hpp | 8 + include/vkcv/CommandStreamManager.hpp | 53 +++++++ include/vkcv/Core.hpp | 55 ++++--- include/vkcv/Handles.hpp | 6 + include/vkcv/Image.hpp | 14 +- include/vkcv/PassConfig.hpp | 16 +- include/vkcv/SwapChain.hpp | 2 +- projects/cmd_sync_test/src/main.cpp | 13 +- projects/first_mesh/src/main.cpp | 2 +- projects/first_triangle/src/main.cpp | 2 +- src/vkcv/BufferManager.cpp | 2 +- src/vkcv/CommandStreamManager.cpp | 119 +++++++++++++++ src/vkcv/Core.cpp | 106 ++++++++++--- src/vkcv/Image.cpp | 31 ++-- src/vkcv/ImageLayoutTransitions.cpp | 72 +++++++-- src/vkcv/ImageLayoutTransitions.hpp | 10 +- src/vkcv/ImageManager.cpp | 142 +++++++++--------- src/vkcv/ImageManager.hpp | 48 ++++-- src/vkcv/PassConfig.cpp | 6 - src/vkcv/PassManager.cpp | 77 +++++----- src/vkcv/PipelineManager.cpp | 3 +- src/vkcv/SwapChain.cpp | 2 +- 23 files changed, 561 insertions(+), 233 deletions(-) create mode 100644 include/vkcv/CommandRecordingFunctionTypes.hpp create mode 100644 include/vkcv/CommandStreamManager.hpp create mode 100644 src/vkcv/CommandStreamManager.cpp diff --git a/config/Sources.cmake b/config/Sources.cmake index e3f5c950..a5b2ddae 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -72,4 +72,9 @@ set(vkcv_sources ${vkcv_include}/vkcv/DrawcallRecording.hpp ${vkcv_source}/vkcv/DrawcallRecording.cpp + + ${vkcv_include}/vkcv/CommandStreamManager.hpp + ${vkcv_source}/vkcv/CommandStreamManager.cpp + + ${vkcv_include}/vkcv/CommandRecordingFunctionTypes.hpp ) diff --git a/include/vkcv/CommandRecordingFunctionTypes.hpp b/include/vkcv/CommandRecordingFunctionTypes.hpp new file mode 100644 index 00000000..c236fb2c --- /dev/null +++ b/include/vkcv/CommandRecordingFunctionTypes.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "vkcv/Event.hpp" +#include <vulkan/vulkan.hpp> + +namespace vkcv { + typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction; + typedef typename event_function<>::type FinishCommandFunction; +} \ No newline at end of file diff --git a/include/vkcv/CommandStreamManager.hpp b/include/vkcv/CommandStreamManager.hpp new file mode 100644 index 00000000..60f7bbaa --- /dev/null +++ b/include/vkcv/CommandStreamManager.hpp @@ -0,0 +1,53 @@ +#pragma once +#include <vulkan/vulkan.hpp> +#include <vector> +#include "vkcv/Event.hpp" +#include "vkcv/Handles.hpp" +#include "vkcv/CommandRecordingFunctionTypes.hpp" + +namespace vkcv { + + class CommandStreamManager + { + friend class Core; + private: + struct CommandStream { + inline CommandStream(vk::CommandBuffer cmdBuffer, vk::Queue queue, vk::CommandPool cmdPool) + : cmdBuffer(cmdBuffer), cmdPool(cmdPool), queue(queue) {}; + vk::CommandBuffer cmdBuffer; + vk::CommandPool cmdPool; + vk::Queue queue; + std::vector<FinishCommandFunction> callbacks; + }; + + Core* m_core; + std::vector<CommandStream> m_commandStreams; + + CommandStreamManager() noexcept; + + void init(Core* core); + + public: + ~CommandStreamManager() noexcept; + + CommandStreamManager(CommandStreamManager&& other) = delete; + CommandStreamManager(const CommandStreamManager& other) = delete; + + CommandStreamManager& operator=(CommandStreamManager&& other) = delete; + CommandStreamManager& operator=(const CommandStreamManager& other) = delete; + + CommandStreamHandle createCommandStream( + const vk::Queue queue, + vk::CommandPool cmdPool); + + void recordCommandsToStream(const CommandStreamHandle handle, const RecordCommandFunction record); + void addFinishCallbackToStream(const CommandStreamHandle handle, const FinishCommandFunction finish); + void submitCommandStreamSynchronous( + const CommandStreamHandle handle, + std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores); + + vk::CommandBuffer getStreamCommandBuffer(const CommandStreamHandle handle); + }; + +} \ No newline at end of file diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 02a58ae8..0a45856e 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -23,6 +23,7 @@ #include "DescriptorWrites.hpp" #include "Event.hpp" #include "DrawcallRecording.hpp" +#include "CommandRecordingFunctionTypes.hpp" namespace vkcv { @@ -34,15 +35,13 @@ namespace vkcv class BufferManager; class SamplerManager; class ImageManager; + class CommandStreamManager; struct SubmitInfo { QueueType queueType; std::vector<vk::Semaphore> waitSemaphores; std::vector<vk::Semaphore> signalSemaphores; }; - - typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction; - typedef typename event_function<>::type FinishCommandFunction; class Core final { @@ -62,25 +61,30 @@ namespace vkcv Context m_Context; - SwapChain m_swapchain; - std::vector<vk::ImageView> m_swapchainImageViews; - const Window& m_window; + SwapChain m_swapchain; + std::vector<vk::ImageView> m_swapchainImageViews; + std::vector<vk::Image> m_swapchainImages; + std::vector<vk::ImageLayout> m_swapchainImageLayouts; + const Window& m_window; - std::unique_ptr<PassManager> m_PassManager; - std::unique_ptr<PipelineManager> m_PipelineManager; - std::unique_ptr<DescriptorManager> m_DescriptorManager; - std::unique_ptr<BufferManager> m_BufferManager; - std::unique_ptr<SamplerManager> m_SamplerManager; - std::unique_ptr<ImageManager> m_ImageManager; + std::unique_ptr<PassManager> m_PassManager; + std::unique_ptr<PipelineManager> m_PipelineManager; + std::unique_ptr<DescriptorManager> m_DescriptorManager; + std::unique_ptr<BufferManager> m_BufferManager; + std::unique_ptr<SamplerManager> m_SamplerManager; + std::unique_ptr<ImageManager> m_ImageManager; + std::unique_ptr<CommandStreamManager> m_CommandStreamManager; - CommandResources m_CommandResources; - SyncResources m_SyncResources; - uint32_t m_currentSwapchainImageIndex; + CommandResources m_CommandResources; + SyncResources m_SyncResources; + uint32_t m_currentSwapchainImageIndex; std::function<void(int, int)> e_resizeHandle; static std::vector<vk::ImageView> createImageViews( Context &context, SwapChain& swapChain); + void recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout); + public: /** * Destructor of #Core destroys the Vulkan objects contained in the core's context. @@ -216,10 +220,8 @@ namespace vkcv */ void beginFrame(); - /** - * @brief render a beautiful triangle - */ - void recordDrawcalls( + void recordDrawcallsToCmdStream( + const CommandStreamHandle cmdStreamHandle, const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const PushConstantData &pushConstantData, @@ -242,6 +244,19 @@ namespace vkcv * @param record Record-command-function * @param finish Finish-command-function or nullptr */ - void submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish); + void recordAndSubmitCommands( + const SubmitInfo &submitInfo, + const RecordCommandFunction &record, + const FinishCommandFunction &finish); + + CommandStreamHandle createCommandStream(QueueType queueType); + + void Core::recordCommandsToStream( + const CommandStreamHandle cmdStreamHandle, + const RecordCommandFunction &record, + const FinishCommandFunction &finish); + + void submitCommandStream(const CommandStreamHandle handle); + void prepareSwapchainImageForPresent(const CommandStreamHandle handle); }; } diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index f2a0da0a..ea05bd21 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -101,5 +101,11 @@ namespace vkcv static ImageHandle createSwapchainImageHandle(const HandleDestroyFunction& destroy = nullptr); }; + + class CommandStreamHandle : public Handle { + friend class CommandStreamManager; + private: + using Handle::Handle; + }; } diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index d76bd121..a1219ce4 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -9,8 +9,12 @@ #include "Handles.hpp" namespace vkcv { - - class ImageManager; + + // forward declares + class ImageManager; + + bool isDepthFormat(const vk::Format format); + class Image { friend class Core; public: @@ -37,11 +41,9 @@ namespace vkcv { void fill(void* data, size_t size = SIZE_MAX); private: ImageManager* const m_manager; - const ImageHandle m_handle; - const vk::Format m_format; - vk::ImageLayout m_layout; + const ImageHandle m_handle; - Image(ImageManager* manager, const ImageHandle& handle, vk::Format format); + Image(ImageManager* manager, const ImageHandle& handle); static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index d9a5bcd8..8f3b516d 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -32,23 +32,15 @@ namespace vkcv struct AttachmentDescription { - AttachmentDescription() = delete; AttachmentDescription( - AttachmentLayout initial, - AttachmentLayout in_pass, - AttachmentLayout final, - AttachmentOperation store_op, - AttachmentOperation load_op, - vk::Format format) noexcept; - - AttachmentLayout layout_initial; - AttachmentLayout layout_in_pass; - AttachmentLayout layout_final; + AttachmentOperation store_op, + AttachmentOperation load_op, + vk::Format format) noexcept; AttachmentOperation store_operation; AttachmentOperation load_operation; - vk::Format format; + vk::Format format; }; struct PassConfig diff --git a/include/vkcv/SwapChain.hpp b/include/vkcv/SwapChain.hpp index 53f7b783..089205d1 100644 --- a/include/vkcv/SwapChain.hpp +++ b/include/vkcv/SwapChain.hpp @@ -107,7 +107,7 @@ namespace vkcv /** * */ - void recreateSwapchain(); + void signalSwapchainRecreation(); /** * TODO diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp index 1f28626f..4933c44f 100644 --- a/projects/cmd_sync_test/src/main.cpp +++ b/projects/cmd_sync_test/src/main.cpp @@ -70,18 +70,12 @@ int main(int argc, const char** argv) { // an example attachment for passes that output to the window const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentLayout::UNDEFINED, - vkcv::AttachmentLayout::COLOR_ATTACHMENT, - vkcv::AttachmentLayout::PRESENTATION, vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, core.getSwapchainImageFormat() ); const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentLayout::UNDEFINED, - vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, - vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, vk::Format::eD32Sfloat @@ -185,12 +179,17 @@ int main(int argc, const char** argv) { vkcv::PushConstantData pushConstantData((void*)mvpMatrices.data(), sizeof(glm::mat4)); const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; - core.recordDrawcalls( + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordDrawcallsToCmdStream( + cmdStream, trianglePass, trianglePipeline, pushConstantData, drawcalls, renderTargets); + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); core.endFrame(); } diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index bf728391..a93346cf 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -170,7 +170,7 @@ int main(int argc, const char** argv) { cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); - core.recordDrawcalls( + core.recordDrawcallsToCmdStream( trianglePass, trianglePipeline, sizeof(mvp), diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 649a3f87..40e8a3e2 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -147,7 +147,7 @@ int main(int argc, const char** argv) { cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); - core.recordDrawcalls( + core.recordDrawcallsToCmdStream( trianglePass, trianglePipeline, sizeof(mvp), diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index ef874606..6d494c4e 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -158,7 +158,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - core->submitCommands( + core->recordAndSubmitCommands( submitInfo, [&info, &mapped_size](const vk::CommandBuffer& commandBuffer) { const vk::BufferCopy region ( diff --git a/src/vkcv/CommandStreamManager.cpp b/src/vkcv/CommandStreamManager.cpp new file mode 100644 index 00000000..9d0a236a --- /dev/null +++ b/src/vkcv/CommandStreamManager.cpp @@ -0,0 +1,119 @@ +#include "vkcv/CommandStreamManager.hpp" +#include "vkcv/Core.hpp" + +namespace vkcv { + CommandStreamManager::CommandStreamManager() noexcept : m_core(nullptr){} + + CommandStreamManager::~CommandStreamManager() noexcept { + for (const auto& stream : m_commandStreams) { + if (stream.cmdBuffer && stream.cmdBuffer) { + m_core->getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); + } + } + } + + void CommandStreamManager::init(Core* core) { + if (!core) { + std::cerr << "Error: CommandStreamManager::init requires valid core pointer" << std::endl; + } + m_core = core; + } + + CommandStreamHandle CommandStreamManager::createCommandStream( + const vk::Queue queue, + vk::CommandPool cmdPool) { + + const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(m_core->getContext().getDevice(), cmdPool); + + CommandStream stream(cmdBuffer, queue, cmdPool); + beginCommandBuffer(stream.cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + + // find unused stream + int unusedStreamIndex = -1; + for (int i = 0; i < m_commandStreams.size(); i++) { + if (m_commandStreams[i].cmdBuffer) { + // still in use + } + else { + unusedStreamIndex = i; + break; + } + } + + const bool foundUnusedStream = unusedStreamIndex >= 0; + if (foundUnusedStream) { + m_commandStreams[unusedStreamIndex] = stream; + return CommandStreamHandle(unusedStreamIndex); + } + + CommandStreamHandle handle(m_commandStreams.size()); + m_commandStreams.push_back(stream); + return handle; + } + + void CommandStreamManager::recordCommandsToStream( + const CommandStreamHandle handle, + const RecordCommandFunction record) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + std::cerr << "Error: CommandStreamManager::recordCommandsToStream requires valid handle" << std::endl; + return; + } + + CommandStream& stream = m_commandStreams[id]; + record(stream.cmdBuffer); + } + + void CommandStreamManager::addFinishCallbackToStream( + const CommandStreamHandle handle, + const FinishCommandFunction finish) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + std::cerr << "Error: CommandStreamManager::addFinishCallbackToStream requires valid handle" << std::endl; + return; + } + + CommandStream& stream = m_commandStreams[id]; + stream.callbacks.push_back(finish); + } + + void CommandStreamManager::submitCommandStreamSynchronous( + const CommandStreamHandle handle, + std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + std::cerr << "Error: CommandStreamManager::submitCommandStreamSynchronous requires valid handle" << std::endl; + return; + } + CommandStream& stream = m_commandStreams[id]; + stream.cmdBuffer.end(); + + const auto device = m_core->getContext().getDevice(); + const vk::Fence waitFence = createFence(device); + submitCommandBufferToQueue(stream.queue, stream.cmdBuffer, waitFence, waitSemaphores, signalSemaphores); + waitForFence(device, waitFence); + device.destroyFence(waitFence); + + device.freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); + stream.cmdBuffer = nullptr; + stream.cmdPool = nullptr; + stream.queue = nullptr; + + for (const auto& finishCallback : stream.callbacks) { + finishCallback(); + } + } + + vk::CommandBuffer CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle handle) { + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + std::cerr << "Error: CommandStreamManager::submitCommandStreamSynchronous requires valid handle" << std::endl; + return nullptr; + } + return m_commandStreams[id].cmdBuffer; + } +} \ No newline at end of file diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 08189af1..c9ac4dec 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -14,6 +14,7 @@ #include "ImageManager.hpp" #include "DescriptorManager.hpp" #include "ImageLayoutTransitions.hpp" +#include "vkcv/CommandStreamManager.hpp" namespace vkcv { @@ -52,8 +53,8 @@ namespace vkcv return m_Context; } - Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, - const CommandResources& commandResources, const SyncResources& syncResources) noexcept : + Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, + const CommandResources& commandResources, const SyncResources& syncResources) noexcept : m_Context(std::move(context)), m_window(window), m_swapchain(swapChain), @@ -61,20 +62,25 @@ namespace vkcv 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_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))), - m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, + m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, + m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))), + m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, + m_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)}, m_CommandResources(commandResources), m_SyncResources(syncResources) { - m_BufferManager->m_core = this; - m_BufferManager->init(); - - m_ImageManager->m_core = this; + m_BufferManager->m_core = this; + m_BufferManager->init(); + m_CommandStreamManager->init(this); + + m_ImageManager->m_core = this; + + e_resizeHandle = window.e_resize.add( [&](int width, int height) { + m_swapchain.signalSwapchainRecreation(); + }); - e_resizeHandle = window.e_resize.add( [&](int width, int height) { - m_swapchain.recreateSwapchain(); - }); + m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); } Core::~Core() noexcept { @@ -130,6 +136,10 @@ namespace vkcv m_swapchain.updateSwapchain(m_Context, m_window); m_swapchainImageViews = createImageViews(m_Context, m_swapchain); + m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + + m_swapchainImageLayouts.clear(); + m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); } if (acquireSwapchainImage() != Result::SUCCESS) { @@ -141,7 +151,8 @@ namespace vkcv m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but its getting late - Alex } - void Core::recordDrawcalls( + void Core::recordDrawcallsToCmdStream( + const CommandStreamHandle cmdStreamHandle, const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const PushConstantData &pushConstantData, @@ -184,11 +195,17 @@ namespace vkcv std::vector<vk::ImageView> attachmentsViews; for (const ImageHandle handle : renderTargets) { vk::ImageView targetHandle; + const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); if (handle.isSwapchainImage()) { + recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::eColorAttachmentOptimal); targetHandle = m_swapchainImageViews[m_currentSwapchainImageIndex]; } else { targetHandle = m_ImageManager->getVulkanImageView(handle); + const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); + const vk::ImageLayout targetLayout = + isDepthFormat ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; + m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); } attachmentsViews.push_back(targetHandle); } @@ -227,7 +244,7 @@ namespace vkcv if (attachment.load_operation == AttachmentOperation::CLEAR) { float clear = 0.0f; - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + if (isDepthFormat(attachment.format)) { clear = 1.0f; } @@ -260,12 +277,12 @@ namespace vkcv cmdBuffer.endRenderPass(); }; - auto finishFunction = [&]() + auto finishFunction = [framebuffer, this]() { m_Context.m_Device.destroy(framebuffer); }; - submitCommands(submitInfo, submitFunction, finishFunction); + recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); } void Core::endFrame() { @@ -297,7 +314,10 @@ namespace vkcv return m_swapchain.getSwapchainFormat(); } - void Core::submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish) + void Core::recordAndSubmitCommands( + const SubmitInfo &submitInfo, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { const vk::Device& device = m_Context.getDevice(); @@ -320,12 +340,37 @@ namespace vkcv finish(); } } - + + CommandStreamHandle Core::createCommandStream(QueueType queueType) { + + const vk::Device& device = m_Context.getDevice(); + const vkcv::Queue queue = getQueueForSubmit(queueType, m_Context.getQueueManager()); + const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources); + + return m_CommandStreamManager->createCommandStream(queue.handle, cmdPool); + } + + void Core::recordCommandsToStream( + const CommandStreamHandle cmdStreamHandle, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { + + m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record); + m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish); + } + + void Core::submitCommandStream(const CommandStreamHandle handle) { + std::vector<vk::Semaphore> waitSemaphores; + // FIXME: add proper user controllable sync + std::vector<vk::Semaphore> signalSemaphores = { m_SyncResources.renderFinished }; + m_CommandStreamManager->submitCommandStreamSynchronous(handle, waitSemaphores, signalSemaphores); + } + SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode) { - return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); - } - + return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); + } + Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth) { return Image::create(m_ImageManager.get(), format, width, height, depth); @@ -371,11 +416,26 @@ namespace vkcv vk::ImageViewType::e2D, swapChain.getSwapchainFormat(), componentMapping, - subResourceRange - ); + subResourceRange); imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); } return imageViews; } + + void Core::recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout) { + auto& imageLayout = m_swapchainImageLayouts[m_currentSwapchainImageIndex]; + const auto transitionBarrier = createSwapchainImageLayoutTransitionBarrier( + m_swapchainImages[m_currentSwapchainImageIndex], + imageLayout, + newLayout); + recordImageBarrier(cmdBuffer, transitionBarrier); + imageLayout = newLayout; + } + + void Core::prepareSwapchainImageForPresent(const CommandStreamHandle handle) { + m_CommandStreamManager->recordCommandsToStream(handle, [&](vk::CommandBuffer cmdBuffer) { + recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::ePresentSrcKHR); + }); + } } diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index 9ce5c25a..f861daeb 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -8,13 +8,24 @@ namespace vkcv{ + bool isDepthFormat(const vk::Format format) { + switch (format) { + case(vk::Format::eD16Unorm): return true; + case(vk::Format::eD16UnormS8Uint): return true; + case(vk::Format::eD24UnormS8Uint): return true; + case(vk::Format::eD32Sfloat): return true; + case(vk::Format::eD32SfloatS8Uint): return true; + default: return false; + } + } + Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth) { - return Image(manager, manager->createImage(width, height, depth, format), format); + return Image(manager, manager->createImage(width, height, depth, format)); } vk::Format Image::getFormat() const { - return m_format; + return m_manager->getImageFormat(m_handle); } uint32_t Image::getWidth() const { @@ -28,15 +39,10 @@ namespace vkcv{ uint32_t Image::getDepth() const { return m_manager->getImageDepth(m_handle); } - - vk::ImageLayout Image::getLayout() const { - return m_layout; - } void Image::switchLayout(vk::ImageLayout newLayout) { - m_manager->switchImageLayout(m_handle, m_layout, newLayout); - m_layout = newLayout; + m_manager->switchImageLayoutImmediate(m_handle, newLayout); } vkcv::ImageHandle Image::getHandle() const { @@ -47,12 +53,9 @@ namespace vkcv{ m_manager->fillImage(m_handle, data, size); } - Image::Image(ImageManager* manager, const ImageHandle& handle, vk::Format format) : + Image::Image(ImageManager* manager, const ImageHandle& handle) : m_manager(manager), - m_handle(handle), - m_format(format), - m_layout(vk::ImageLayout::eUndefined) - { - } + m_handle(handle) + {} } diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp index 0b088194..cb0f90a7 100644 --- a/src/vkcv/ImageLayoutTransitions.cpp +++ b/src/vkcv/ImageLayoutTransitions.cpp @@ -1,24 +1,68 @@ #include "ImageLayoutTransitions.hpp" +#include "vkcv/Image.hpp" namespace vkcv { - void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image, - const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout) { + vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image &image, vk::ImageLayout newLayout) { - // TODO: proper src and dst masks - const vk::PipelineStageFlags srcStageMask = vk::PipelineStageFlagBits::eAllCommands; - const vk::PipelineStageFlags dstStageMask = vk::PipelineStageFlagBits::eAllCommands; - const vk::DependencyFlags dependecyFlags = {}; + vk::ImageAspectFlags aspectFlags; + if (isDepthFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } + else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } - // TODO: proper src and dst masks - const vk::AccessFlags srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; - const vk::AccessFlags dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + vk::ImageSubresourceRange imageSubresourceRange( + aspectFlags, + 0, + image.m_levels, + 0, + image.m_layers + ); - // TODO: proper aspect flags - const vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor; + // TODO: precise AccessFlagBits, will require a lot of context + return vk::ImageMemoryBarrier( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + image.m_layout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + image.m_handle, + imageSubresourceRange); + } + + vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( + vk::Image vulkanHandle, + vk::ImageLayout oldLayout, + vk::ImageLayout newLayout) { - const vk::ImageSubresourceRange subresourceRange(aspectFlags, 0, 1, 0, 1); - vk::ImageMemoryBarrier imageBarrier(srcAccessMask, dstAccessMask, oldLayout, newLayout, 0, 0, image, subresourceRange); + vk::ImageSubresourceRange imageSubresourceRange( + vk::ImageAspectFlagBits::eColor, + 0, + 1, + 0, + 1); + + // TODO: precise AccessFlagBits, will require a lot of context + return vk::ImageMemoryBarrier( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + oldLayout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + vulkanHandle, + imageSubresourceRange); + } - cmdBuffer.pipelineBarrier(srcStageMask, dstStageMask, dependecyFlags, 0, nullptr, 0, nullptr, 1, &imageBarrier, {}); + void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier) { + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + nullptr, + barrier); } } \ No newline at end of file diff --git a/src/vkcv/ImageLayoutTransitions.hpp b/src/vkcv/ImageLayoutTransitions.hpp index 3dbfbdf6..5c147f13 100644 --- a/src/vkcv/ImageLayoutTransitions.hpp +++ b/src/vkcv/ImageLayoutTransitions.hpp @@ -1,7 +1,13 @@ #pragma once #include <vulkan/vulkan.hpp> +#include "ImageManager.hpp" namespace vkcv { - void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image, - const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout); + vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image& image, vk::ImageLayout newLayout); + vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( + vk::Image vulkanHandle, + vk::ImageLayout oldLayout, + vk::ImageLayout newLayout); + + void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier); } \ No newline at end of file diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index a3ca22b1..b732630a 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -5,11 +5,34 @@ */ #include "ImageManager.hpp" #include "vkcv/Core.hpp" +#include "ImageLayoutTransitions.hpp" #include <algorithm> namespace vkcv { + ImageManager::Image::Image( + vk::Image handle, + vk::DeviceMemory memory, + vk::ImageView view, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers, + uint32_t levels) + : + m_handle(handle), + m_memory(memory), + m_view(view), + m_width(width), + m_height(height), + m_depth(depth), + m_format(format), + m_layers(layers), + m_levels(levels) + {} + /** * @brief searches memory type index for image allocation, combines requirements of image and application * @param physicalMemoryProperties Memory Properties of physical device @@ -166,7 +189,7 @@ namespace vkcv { vk::ImageView view = device.createImageView(imageViewCreateInfo); const uint64_t id = m_images.size(); - m_images.push_back({ image, memory, view, width, height, depth, format, arrayLayers, mipLevels }); + m_images.push_back(Image(image, memory, view, width, height, depth, format, arrayLayers, mipLevels)); return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); }); } @@ -213,7 +236,7 @@ namespace vkcv { return image.m_view; } - void ImageManager::switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { + void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) { const uint64_t id = handle.getId(); if (id >= m_images.size()) { @@ -222,76 +245,43 @@ namespace vkcv { } auto& image = m_images[id]; - - //alternativly we could use switch case for every variable to set - vk::AccessFlags sourceAccessMask; - vk::PipelineStageFlags sourceStage; - - vk::AccessFlags destinationAccessMask; - vk::PipelineStageFlags destinationStage; - - if ((oldLayout == vk::ImageLayout::eUndefined) && - (newLayout == vk::ImageLayout::eTransferDstOptimal)) - { - destinationAccessMask = vk::AccessFlagBits::eTransferWrite; - - sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; - destinationStage = vk::PipelineStageFlagBits::eTransfer; - } - else if ((oldLayout == vk::ImageLayout::eTransferDstOptimal) && - (newLayout == vk::ImageLayout::eShaderReadOnlyOptimal)) - { - sourceAccessMask = vk::AccessFlagBits::eTransferWrite; - destinationAccessMask = vk::AccessFlagBits::eShaderRead; - - sourceStage = vk::PipelineStageFlagBits::eTransfer; - destinationStage = vk::PipelineStageFlagBits::eFragmentShader; - } - - vk::ImageAspectFlags aspectFlags; - - if (isDepthImageFormat(image.m_format)) { - aspectFlags = vk::ImageAspectFlagBits::eDepth; - } else { - aspectFlags = vk::ImageAspectFlagBits::eColor; - } - - vk::ImageSubresourceRange imageSubresourceRange( - aspectFlags, - 0, - image.m_levels, - 0, - image.m_layers - ); - - vk::ImageMemoryBarrier imageMemoryBarrier( - sourceAccessMask, - destinationAccessMask, - oldLayout, - newLayout, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - image.m_handle, - imageSubresourceRange - ); + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; - m_core->submitCommands( + m_core->recordAndSubmitCommands( submitInfo, - [sourceStage, destinationStage, imageMemoryBarrier](const vk::CommandBuffer& commandBuffer) { - commandBuffer.pipelineBarrier( - sourceStage, - destinationStage, - {}, - nullptr, - nullptr, - imageMemoryBarrier + [transitionBarrier](const vk::CommandBuffer& commandBuffer) { + // TODO: precise PipelineStageFlagBits, will require a lot of context + commandBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + nullptr, + transitionBarrier ); }, - nullptr - ); + nullptr); + image.m_layout = newLayout; + } + + void ImageManager::recordImageLayoutTransition( + const ImageHandle& handle, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer) { + + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + std::cerr << "Error: ImageManager::switchImageLayout invalid handle" << std::endl; + return; + } + + auto& image = m_images[id]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); + recordImageBarrier(cmdBuffer, transitionBarrier); } void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) @@ -305,11 +295,9 @@ namespace vkcv { auto& image = m_images[id]; - switchImageLayout( + switchImageLayoutImmediate( handle, - vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferDstOptimal - ); + vk::ImageLayout::eTransferDstOptimal); uint32_t channels = 4; // TODO: check image.m_format const size_t image_size = ( @@ -329,7 +317,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - m_core->submitCommands( + m_core->recordAndSubmitCommands( submitInfo, [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { vk::ImageAspectFlags aspectFlags; @@ -363,9 +351,8 @@ namespace vkcv { ); }, [&]() { - switchImageLayout( + switchImageLayoutImmediate( handle, - vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal ); } @@ -438,5 +425,16 @@ namespace vkcv { } } + vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const { + + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + std::cerr << "Error: ImageManager::destroyImageById invalid handle" << std::endl; + return vk::Format::eUndefined; + } + + return m_images[id].m_format; + } } \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index f1bd9d0b..b9fccb25 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -11,23 +11,38 @@ #include "vkcv/Handles.hpp" namespace vkcv { - + class ImageManager { friend class Core; - private: + public: struct Image { - vk::Image m_handle; - vk::DeviceMemory m_memory; - vk::ImageView m_view; - uint32_t m_width = 0; - uint32_t m_height = 0; - uint32_t m_depth = 0; - vk::Format m_format; - uint32_t m_layers = 1; - uint32_t m_levels = 1; + vk::Image m_handle; + vk::DeviceMemory m_memory; + vk::ImageView m_view; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_depth = 0; + vk::Format m_format; + uint32_t m_layers = 1; + uint32_t m_levels = 1; + vk::ImageLayout m_layout = vk::ImageLayout::eUndefined; + private: + // struct is public so utility functions can access members, but only ImageManager can create Image + friend ImageManager; + Image( + vk::Image handle, + vk::DeviceMemory memory, + vk::ImageView view, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers, + uint32_t levels); }; + private: Core* m_core; BufferManager& m_bufferManager; @@ -64,8 +79,13 @@ namespace vkcv { [[nodiscard]] vk::ImageView getVulkanImageView(const ImageHandle& handle) const; - - void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout); + + void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout); + void recordImageLayoutTransition( + const ImageHandle& handle, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer); + void fillImage(const ImageHandle& handle, void* data, size_t size); [[nodiscard]] @@ -77,5 +97,7 @@ namespace vkcv { [[nodiscard]] uint32_t getImageDepth(const ImageHandle& handle) const; + [[nodiscard]] + vk::Format getImageFormat(const ImageHandle& handle) const; }; } \ No newline at end of file diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp index ef07d3ee..602f1d3e 100644 --- a/src/vkcv/PassConfig.cpp +++ b/src/vkcv/PassConfig.cpp @@ -5,15 +5,9 @@ namespace vkcv { AttachmentDescription::AttachmentDescription( - AttachmentLayout initial, - AttachmentLayout in_pass, - AttachmentLayout final, AttachmentOperation store_op, AttachmentOperation load_op, vk::Format format) noexcept : - layout_initial{initial}, - layout_in_pass{in_pass}, - layout_final{final}, store_operation{store_op}, load_operation{load_op}, format(format) diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index 8b59495a..c34b0d36 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -1,4 +1,5 @@ #include "PassManager.hpp" +#include "vkcv/Image.hpp" namespace vkcv { @@ -73,63 +74,63 @@ namespace vkcv for (uint32_t i = 0; i < config.attachments.size(); i++) { // TODO: Renderpass struct should hold proper format information - vk::Format format = config.attachments[i].format; + vk::Format format = config.attachments[i].format; + vk::ImageLayout layout; - if (config.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) + if (isDepthFormat(config.attachments[i].format)) { + layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; depthAttachmentReference.attachment = i; - depthAttachmentReference.layout = getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass); - pDepthAttachment = &depthAttachmentReference; + depthAttachmentReference.layout = layout; + pDepthAttachment = &depthAttachmentReference; } else { - vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass)); + layout = vk::ImageLayout::eColorAttachmentOptimal; + vk::AttachmentReference attachmentRef(i, layout); colorAttachmentReferences.push_back(attachmentRef); } vk::AttachmentDescription attachmentDesc( - {}, - format, - vk::SampleCountFlagBits::e1, - getVKLoadOpFromAttachOp(config.attachments[i].load_operation), - getVkStoreOpFromAttachOp(config.attachments[i].store_operation), - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - getVkLayoutFromAttachLayout(config.attachments[i].layout_initial), - getVkLayoutFromAttachLayout(config.attachments[i].layout_final) - ); - + {}, + format, + vk::SampleCountFlagBits::e1, + getVKLoadOpFromAttachOp(config.attachments[i].load_operation), + getVkStoreOpFromAttachOp(config.attachments[i].store_operation), + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + layout, + layout); + attachmentDescriptions.push_back(attachmentDesc); } const vk::SubpassDescription subpassDescription( - {}, - vk::PipelineBindPoint::eGraphics, - 0, - {}, - static_cast<uint32_t>(colorAttachmentReferences.size()), - colorAttachmentReferences.data(), - {}, - pDepthAttachment, - 0, - {} - ); + {}, + vk::PipelineBindPoint::eGraphics, + 0, + {}, + static_cast<uint32_t>(colorAttachmentReferences.size()), + colorAttachmentReferences.data(), + {}, + pDepthAttachment, + 0, + {}); const vk::RenderPassCreateInfo passInfo( - {}, - static_cast<uint32_t>(attachmentDescriptions.size()), - attachmentDescriptions.data(), - 1, - &subpassDescription, - 0, - {} - ); + {}, + static_cast<uint32_t>(attachmentDescriptions.size()), + attachmentDescriptions.data(), + 1, + &subpassDescription, + 0, + {}); vk::RenderPass renderPass = m_Device.createRenderPass(passInfo); - + const uint64_t id = m_Passes.size(); - m_Passes.push_back({ renderPass, config }); - return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); + m_Passes.push_back({ renderPass, config }); + return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); } vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index e065033f..9afa2bc9 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -1,4 +1,5 @@ #include "PipelineManager.hpp" +#include "vkcv/Image.hpp" namespace vkcv { @@ -207,7 +208,7 @@ namespace vkcv const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle); for (const auto& attachment : passConfig.attachments) { - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + if (isDepthFormat(attachment.format)) { p_depthStencilCreateInfo = &depthStencilCreateInfo; break; } diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/SwapChain.cpp index 39c310de..b787536b 100644 --- a/src/vkcv/SwapChain.cpp +++ b/src/vkcv/SwapChain.cpp @@ -244,7 +244,7 @@ namespace vkcv m_Extent = extent2D; } - void SwapChain::recreateSwapchain() { + void SwapChain::signalSwapchainRecreation() { m_RecreationRequired = true; } -- GitLab