From 63a87edc29bb7b78e38dabb6b180451276832852 Mon Sep 17 00:00:00 2001 From: Draklaw Date: Tue, 1 Mar 2022 23:53:32 +0100 Subject: [PATCH] Wrapper around fences and render pass. --- CMakeLists.txt | 3 + src/VulkanTutorial.cpp | 10 +--- src/VulkanTutorial.h | 3 +- src/core/ArrayView.h | 11 ++-- src/core/math.h | 5 ++ src/vk/Buffer.cpp | 1 + src/vk/Context.cpp | 12 ++-- src/vk/Context.h | 3 +- src/vk/Fence.cpp | 123 +++++++++++++++++++++++++++++++++++++++++ src/vk/Fence.h | 69 +++++++++++++++++++++++ src/vk/RenderPass.cpp | 63 +++++++++++++++++++++ src/vk/RenderPass.h | 53 ++++++++++++++++++ src/vk/Swapchain.cpp | 44 ++++----------- src/vk/Swapchain.h | 6 +- src/vk/forward.h | 4 ++ 15 files changed, 352 insertions(+), 58 deletions(-) create mode 100644 src/vk/Fence.cpp create mode 100644 src/vk/Fence.h create mode 100644 src/vk/RenderPass.cpp create mode 100644 src/vk/RenderPass.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 472e85a..03c71be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,9 +37,12 @@ add_executable(vk_expe src/core/Logger.cpp src/vk/Context.cpp + src/vk/Fence.cpp src/vk/Swapchain.cpp src/vk/Memory.cpp src/vk/Buffer.cpp + src/vk/RenderPass.cpp + src/main.cpp src/Simplex.cpp src/Planet.cpp diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 4ffa6b9..71ea733 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -333,7 +333,7 @@ void VulkanTutorial::destroy_swapchain_objects() { m_context.destroy_pipeline(m_pipeline); m_context.destroy_pipeline_layout(m_pipeline_layout); - m_context.destroy_render_pass(m_render_pass); + m_render_pass.destroy(); } void VulkanTutorial::create_render_pass() { @@ -378,13 +378,7 @@ void VulkanTutorial::create_render_pass() { .pDependencies = &subpass_dep, }; - if(vkCreateRenderPass( - m_context.device(), - &render_pass_info, - nullptr, - &m_render_pass - ) != VK_SUCCESS) - throw std::runtime_error("failed to create render pass"); + m_render_pass = vk::RenderPass(m_context, render_pass_info); } void VulkanTutorial::create_descriptor_set_layout() { diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index 908c4d5..9f0f67b 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -69,7 +70,7 @@ private: VkQueue m_graphic_queue = nullptr; VkQueue m_presentation_queue = nullptr; - VkRenderPass m_render_pass = VK_NULL_HANDLE; + vk::RenderPass m_render_pass; VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; VkPipeline m_pipeline = VK_NULL_HANDLE; diff --git a/src/core/ArrayView.h b/src/core/ArrayView.h index e43a58e..f91fe2f 100644 --- a/src/core/ArrayView.h +++ b/src/core/ArrayView.h @@ -42,12 +42,16 @@ public: return m_stride; } + bool is_dense() const { + return m_stride == sizeof(T); + } + const T* data() const { - return m_data; + return reinterpret_cast(m_data); } T* data() { - return m_data; + return reinterpret_cast(m_data); } const T& operator[](Index index) const { @@ -74,6 +78,3 @@ private: Index m_size = 0; Index m_stride = sizeof(T); }; - -using Vector3AV = ArrayView; -using TriangleAV = ArrayView; diff --git a/src/core/math.h b/src/core/math.h index cd6dd26..b65ac71 100644 --- a/src/core/math.h +++ b/src/core/math.h @@ -3,6 +3,7 @@ #include +#include #include @@ -26,6 +27,10 @@ using Quaternion = Eigen::Quaternion; using Triangle = Eigen::Array; +using Vector3AV = ArrayView; +using TriangleAV = ArrayView; + + template auto lerp( Scalar factor, diff --git a/src/vk/Buffer.cpp b/src/vk/Buffer.cpp index a4a3d3b..43bfba6 100644 --- a/src/vk/Buffer.cpp +++ b/src/vk/Buffer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace vk { diff --git a/src/vk/Context.cpp b/src/vk/Context.cpp index 135f919..ee2142a 100644 --- a/src/vk/Context.cpp +++ b/src/vk/Context.cpp @@ -140,7 +140,7 @@ void Context::shutdown() { m_allocator->free_all_pages(); m_allocator.reset(); - destroy_fence(m_transfer_fence); + m_transfer_fence.destroy(); destroy_command_pool(m_transfer_command_pool); destroy_device(m_device); @@ -330,7 +330,7 @@ void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, vkEndCommandBuffer(command_buffer); - vkResetFences(m_device, 1, &m_transfer_fence); + m_transfer_fence.reset(); VkSubmitInfo submit_info { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -342,7 +342,7 @@ void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, 1, &submit_info, m_transfer_fence ); - vkWaitForFences(m_device, 1, &m_transfer_fence, VK_TRUE, UINT64_MAX); + m_transfer_fence.wait(); } @@ -790,11 +790,7 @@ void Context::create_internal_objects() { throw std::runtime_error("failed to create command pool"); } - VkFenceCreateInfo fence_info { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - }; - if(vkCreateFence(m_device, &fence_info, nullptr, &m_transfer_fence) != VK_SUCCESS) - throw std::runtime_error("failed to create fence"); + m_transfer_fence = Fence(*this); } void Context::initialize_extension_functions() { diff --git a/src/vk/Context.h b/src/vk/Context.h index 76583b2..6dd2822 100644 --- a/src/vk/Context.h +++ b/src/vk/Context.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include @@ -194,7 +195,7 @@ private: uint32_t m_transfer_queue_index = UINT32_MAX; VkCommandPool m_transfer_command_pool = VK_NULL_HANDLE; - VkFence m_transfer_fence = VK_NULL_HANDLE; + Fence m_transfer_fence; std::vector m_context_destruction_callbacks; diff --git a/src/vk/Fence.cpp b/src/vk/Fence.cpp new file mode 100644 index 0000000..03a81f1 --- /dev/null +++ b/src/vk/Fence.cpp @@ -0,0 +1,123 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +Fence::Fence() noexcept { +} + +Fence::Fence(Context& context, VkFenceCreateFlags flags) + : m_context(&context) +{ + assert(m_context); + + VkFenceCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = flags, + }; + if(vkCreateFence( + context.device(), + &create_info, + nullptr, + &m_fence + ) != VK_SUCCESS) + throw std::runtime_error("failed to create fence"); +} + +Fence::Fence(Fence&& other) noexcept +{ + swap(*this, other); +} + +Fence::~Fence() noexcept { + if(is_valid()) + destroy(); +} + + +Fence& Fence::operator=(Fence&& other) noexcept { + swap(*this, other); + if(other) + other.destroy(); + return *this; +} + + +VkResult Fence::get_status() const { + assert(m_context); + assert(is_valid()); + + VkResult result = vkGetFenceStatus( + m_context->device(), + m_fence + ); + + if(result < 0) + throw std::runtime_error("failed to get fence status"); + + return result; +} + +void Fence::reset() { + assert(m_context); + assert(is_valid()); + + if(vkResetFences( + m_context->device(), + 1, + &m_fence + ) != VK_SUCCESS) + throw std::runtime_error("failed to reset fence"); +} + +void Fence::wait(uint64_t timeout) const { + assert(m_context); + assert(is_valid()); + + wait_for_fences( + m_context->device(), + ArrayView(1, const_cast(&m_fence)), + true, + timeout + ); +} + + +void Fence::destroy() noexcept { + assert(is_valid()); + assert(m_context); + + vkDestroyFence( + m_context->device(), + m_fence, + nullptr + ); + + m_fence = nullptr; +} + + +void wait_for_fences(VkDevice device, ArrayView fences, bool wait_all, uint64_t timeout) { + assert(fences.is_dense()); + + if(fences.size() == 0) + return; + + if(vkWaitForFences( + device, + fences.size(), + fences.data(), + wait_all, + timeout + ) != VK_SUCCESS) + throw std::runtime_error("failed to wait for fences"); +} + + +} diff --git a/src/vk/Fence.h b/src/vk/Fence.h new file mode 100644 index 0000000..dd80aaf --- /dev/null +++ b/src/vk/Fence.h @@ -0,0 +1,69 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + +#include + + +namespace vk { + + +class Fence { +public: + Fence() noexcept; + Fence(Context& context, VkFenceCreateFlags flags=0); + Fence(const Fence&) = delete; + Fence(Fence&& other) noexcept; + ~Fence() noexcept; + + Fence& operator=(const Fence&) = delete; + Fence& operator=(Fence&& other) noexcept; + + explicit inline operator bool() const noexcept { + return is_valid(); + } + + inline bool is_valid() const noexcept { + return m_fence != VK_NULL_HANDLE; + } + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline operator VkFence() noexcept { + return m_fence; + } + + inline VkFence fence() noexcept { + return m_fence; + } + + VkResult get_status() const; + void reset(); + void wait(uint64_t timeout=UINT64_MAX) const; + + friend inline void swap(Fence& fence_0, Fence& fence_1) noexcept { + using std::swap; + swap(fence_0.m_context, fence_1.m_context); + swap(fence_0.m_fence, fence_1.m_fence); + } + + void destroy() noexcept; + +private: + Context* m_context = nullptr; + VkFence m_fence = VK_NULL_HANDLE; +}; + +void wait_for_fences(VkDevice device, ArrayView fences, bool wait_all=true, uint64_t timeout=UINT64_MAX); + + +} diff --git a/src/vk/RenderPass.cpp b/src/vk/RenderPass.cpp new file mode 100644 index 0000000..e1cbe99 --- /dev/null +++ b/src/vk/RenderPass.cpp @@ -0,0 +1,63 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +RenderPass::RenderPass() noexcept { +} + +RenderPass::RenderPass(Context& context, const VkRenderPassCreateInfo& create_info) + : m_context(&context) +{ + assert(m_context); + + if(vkCreateRenderPass( + context.device(), + &create_info, + nullptr, + &m_render_pass + ) != VK_SUCCESS) + throw std::runtime_error("failed to create render pass"); +} + +RenderPass::RenderPass(RenderPass&& other) noexcept +{ + swap(*this, other); +} + +RenderPass::~RenderPass() noexcept { + if(is_valid()) + destroy(); +} + + +RenderPass& RenderPass::operator=(RenderPass&& other) noexcept { + swap(*this, other); + if(other) + other.destroy(); + return *this; +} + + +void RenderPass::destroy() noexcept { + assert(is_valid()); + assert(m_context); + + vkDestroyRenderPass( + m_context->device(), + m_render_pass, + nullptr + ); + + m_render_pass = nullptr; +} + + + +} diff --git a/src/vk/RenderPass.h b/src/vk/RenderPass.h new file mode 100644 index 0000000..db1b9e6 --- /dev/null +++ b/src/vk/RenderPass.h @@ -0,0 +1,53 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + + +namespace vk { + + +class RenderPass { +public: + RenderPass() noexcept; + RenderPass(Context& context, const VkRenderPassCreateInfo& create_info); + RenderPass(const RenderPass&) = delete; + RenderPass(RenderPass&& other) noexcept; + ~RenderPass() noexcept; + + RenderPass& operator=(const RenderPass&) = delete; + RenderPass& operator=(RenderPass&& other) noexcept; + + explicit inline operator bool() const noexcept { + return is_valid(); + } + + inline bool is_valid() const noexcept { + return m_render_pass != VK_NULL_HANDLE; + } + + inline operator VkRenderPass() noexcept { + return m_render_pass; + } + + inline VkRenderPass render_pass() noexcept { + return m_render_pass; + } + + friend inline void swap(RenderPass& render_pass_0, RenderPass& render_pass_1) noexcept { + using std::swap; + swap(render_pass_0.m_context, render_pass_1.m_context); + swap(render_pass_0.m_render_pass, render_pass_1.m_render_pass); + } + + void destroy() noexcept; + +private: + Context* m_context = nullptr; + VkRenderPass m_render_pass = VK_NULL_HANDLE; +}; + + +} diff --git a/src/vk/Swapchain.cpp b/src/vk/Swapchain.cpp index aad026b..f1d595c 100644 --- a/src/vk/Swapchain.cpp +++ b/src/vk/Swapchain.cpp @@ -84,8 +84,8 @@ VkSemaphore Swapchain::ready_to_render() { return m_frame_resources[m_frame_resources_index].ready_to_render; } -VkFence Swapchain::render_done() { - return m_frame_resources[m_frame_resources_index].render_done; +Fence& Swapchain::render_done() { + return *m_frame_resources[m_frame_resources_index].render_done; } void Swapchain::initialize(const SwapchainSettings& settings) { @@ -112,13 +112,8 @@ void Swapchain::begin_frame() { // frame_resources are used cyclically. We wait for the current one to be // done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at // the same time. - if(frame_resources.render_done != VK_NULL_HANDLE) { - vkWaitForFences( - m_context->device(), - 1, &frame_resources.render_done, - VK_TRUE, - UINT64_MAX - ); + if(frame_resources.render_done) { + frame_resources.render_done->wait(); } bool acquired_image = false; @@ -151,18 +146,13 @@ void Swapchain::begin_frame() { // In case vkAcquireNextImageKHR doesn't return images in-order, we wait // again to be sure. if(image_resources.render_done != VK_NULL_HANDLE) { - vkWaitForFences( - m_context->device(), - 1, &image_resources.render_done, - VK_TRUE, - UINT64_MAX - ); + image_resources.render_done.wait(); } // Remember the right frame to wait for. - frame_resources.render_done = image_resources.render_done; + frame_resources.render_done = &image_resources.render_done; - vkResetFences(m_context->device(), 1, &frame_resources.render_done); + frame_resources.render_done->reset(); } void Swapchain::swap_buffers(Array wait_semaphores) { @@ -297,9 +287,9 @@ void Swapchain::create() { swapchain_images ); - m_image_resources.resize(image_count, {}); for(size_t index = 0; index < image_count; index += 1) { - auto& image_resources = m_image_resources[index]; + m_image_resources.emplace_back(); + auto& image_resources = m_image_resources.back(); image_resources.image = swapchain_images[index]; @@ -332,17 +322,7 @@ void Swapchain::create() { throw std::runtime_error("failed to create swapchain image view"); } - VkFenceCreateInfo fence_info { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, - }; - if(vkCreateFence( - m_context->device(), - &fence_info, - nullptr, - &image_resources.render_done - ) != VK_SUCCESS) - throw std::runtime_error("failed to create fence"); + image_resources.render_done = Fence(*m_context, VK_FENCE_CREATE_SIGNALED_BIT); } m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {}); @@ -374,11 +354,11 @@ void Swapchain::destroy() { for(auto& frame_resources: m_frame_resources) { m_context->destroy_semaphore(frame_resources.ready_to_render); // This is a reference to an ImageResources.render_done - frame_resources.render_done = VK_NULL_HANDLE; + frame_resources.render_done = nullptr; } for(auto& image_resources: m_image_resources) { - m_context->destroy_fence(image_resources.render_done); + image_resources.render_done.destroy(); m_context->destroy_image_view(image_resources.view); } diff --git a/src/vk/Swapchain.h b/src/vk/Swapchain.h index c6adff8..8f09689 100644 --- a/src/vk/Swapchain.h +++ b/src/vk/Swapchain.h @@ -59,7 +59,7 @@ public: VkImageView image_view(); VkImageView image_view(size_t image_index); VkSemaphore ready_to_render(); - VkFence render_done(); + Fence& render_done(); void initialize(const SwapchainSettings& settings); void shutdown(); @@ -76,12 +76,12 @@ private: struct ImageResources { VkImage image = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; - VkFence render_done = VK_NULL_HANDLE; + Fence render_done; }; struct FrameResources { VkSemaphore ready_to_render = VK_NULL_HANDLE; - VkFence render_done = VK_NULL_HANDLE; + Fence* render_done = nullptr; }; private: diff --git a/src/vk/forward.h b/src/vk/forward.h index 078d3a4..8caee4b 100644 --- a/src/vk/forward.h +++ b/src/vk/forward.h @@ -15,5 +15,9 @@ using MemoryPageSP = std::shared_ptr; class Context; class ContextSettings; +class Buffer; +class RenderPass; +class Fence; + } \ No newline at end of file