From 7ec394db8961009e6ac23fea909f8353d865f7a3 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Fri, 9 May 2025 00:32:33 +0200 Subject: Add and use Vulkan synchronization library --- hk21/game/BUILD.bazel | 1 + hk21/game/gpu.cpp | 170 +++++++++++++++++++++++++++----------------------- 2 files changed, 94 insertions(+), 77 deletions(-) (limited to 'hk21/game') diff --git a/hk21/game/BUILD.bazel b/hk21/game/BUILD.bazel index 0a571f3..218d1f0 100644 --- a/hk21/game/BUILD.bazel +++ b/hk21/game/BUILD.bazel @@ -18,6 +18,7 @@ cc_binary( "@asl//asl/containers:buffer", "@sdl3_windows//:sdl3", "//hk21/vulkan/loader", + "//hk21/vulkan/sync", ], applicable_licenses = ["//:license"], ) diff --git a/hk21/game/gpu.cpp b/hk21/game/gpu.cpp index 0d74559..a929e1a 100644 --- a/hk21/game/gpu.cpp +++ b/hk21/game/gpu.cpp @@ -14,11 +14,11 @@ #include #include "hk21/vulkan/loader/loader.hpp" +#include "hk21/vulkan/sync/sync.hpp" // @Todo Make fences recyclable // @Todo Make command pool recyclable // @Todo Make frame structure recyclable -// @Todo Auto barriers for images #define VK_ALLOCATOR nullptr @@ -288,8 +288,53 @@ static asl::status_or create_device(VkPhysicalDevice physical_device, struct FrameResources : asl::intrusive_list_node { - VkFence complete_fence = VK_NULL_HANDLE; - VkCommandPool command_pool = VK_NULL_HANDLE; + VkFence complete_fence = VK_NULL_HANDLE; + VkCommandPool command_pool = VK_NULL_HANDLE; +}; + +class DependencyInfoBuilder : public vulkan_sync::DependencyInfoBuilder +{ + // @Todo Configure allocator + + asl::buffer m_image_barriers; + asl::buffer m_buffer_barriers; + +public: + void add_image_barrier(const VkImageMemoryBarrier2& barrier) override + { + m_image_barriers.push(barrier); + } + + void add_buffer_barrier(const VkBufferMemoryBarrier2& barrier) override + { + m_buffer_barriers.push(barrier); + } + + void apply(VkCommandBuffer command_buffer) + { + if (m_image_barriers.is_empty() && + m_buffer_barriers.is_empty()) + { + return; + } + + VkDependencyInfo dependency_info{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = static_cast(m_buffer_barriers.size()), + .pBufferMemoryBarriers = m_buffer_barriers.data(), + .imageMemoryBarrierCount = static_cast(m_image_barriers.size()), + .pImageMemoryBarriers = m_image_barriers.data(), + }; + + vkCmdPipelineBarrier2(command_buffer, &dependency_info); + + m_image_barriers.clear(); + m_buffer_barriers.clear(); + } }; class GpuImpl : public Gpu @@ -303,8 +348,14 @@ class GpuImpl : public Gpu VkDevice m_device; VkQueue m_queue; + struct Image + { + vulkan_sync::ImageState state; + VkImage image{}; + }; + asl::option m_swapchain; - asl::buffer m_swapchain_images; + asl::buffer m_swapchain_images; VkSemaphore m_swapchain_image_acquire_semaphore = VK_NULL_HANDLE; VkSemaphore m_queue_complete_semaphore = VK_NULL_HANDLE; @@ -312,6 +363,8 @@ class GpuImpl : public Gpu asl::GlobalHeap m_allocator; // @Todo Make this configurable asl::IntrusiveList m_in_flight_frames; + DependencyInfoBuilder m_dependency_builder; + public: GpuImpl( VkInstance instance, @@ -451,11 +504,24 @@ public: m_swapchain = swapchain; vkGetSwapchainImagesKHR(m_device, m_swapchain.value(), &count, nullptr); - m_swapchain_images.resize_zero(count); - res = vkGetSwapchainImagesKHR(m_device, m_swapchain.value(), &count, m_swapchain_images.data()); - if (res != VK_SUCCESS) + { - return asl::runtime_error("Couldn't retrieve Vulkan swapchain images: {}", res); + m_swapchain_images.resize(count); + + // @Todo Good candidate for temporary allocation + asl::buffer images; + images.resize_zero(count); + + res = vkGetSwapchainImagesKHR(m_device, m_swapchain.value(), &count, images.data()); + if (res != VK_SUCCESS) + { + return asl::runtime_error("Couldn't retrieve Vulkan swapchain images: {}", res); + } + + for (int64_t i = 0; i < count; ++i) + { + m_swapchain_images[i].image = images[i]; + } } ASL_LOG_INFO("Vulkan swapchain created ({}x{} with {} images)", @@ -518,6 +584,8 @@ public: { return asl::runtime_error("Couldn't acquire swapchain image: {}", res); } + + auto& swapchain_image = m_swapchain_images[image_index]; VkCommandPoolCreateInfo command_pool_create_info{ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -561,40 +629,12 @@ public: return asl::runtime_error("Couldn't begin command buffer: {}", res); } - VkImageMemoryBarrier2 barrier1{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = m_swapchain_images[image_index], - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - VkDependencyInfo dependency_info1{ - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = 0, - .memoryBarrierCount = 0, - .pMemoryBarriers = nullptr, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = nullptr, - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &barrier1, - }; - - vkCmdPipelineBarrier2(command_buffer, &dependency_info1); + vulkan_sync::synchronize_resource( + swapchain_image.image, + VK_IMAGE_ASPECT_COLOR_BIT, + &swapchain_image.state, + vulkan_sync::Usage::kImageClear, + &m_dependency_builder); VkClearColorValue clear_color{ .float32 = { 0.0F, 0.137F, 0.4F, 1.0F }, @@ -603,47 +643,23 @@ public: VkImageSubresourceRange range{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, - .levelCount = 1, + .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, - .layerCount = 1, + .layerCount = VK_REMAINING_ARRAY_LAYERS, }; - vkCmdClearColorImage(command_buffer, m_swapchain_images[image_index], VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); + m_dependency_builder.apply(command_buffer); + vkCmdClearColorImage(command_buffer, swapchain_image.image, swapchain_image.state.current_layout, &clear_color, 1, &range); - VkImageMemoryBarrier2 barrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, - .dstAccessMask = VK_ACCESS_2_NONE, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = m_swapchain_images[image_index], - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; + vulkan_sync::synchronize_resource( + swapchain_image.image, + VK_IMAGE_ASPECT_COLOR_BIT, + &swapchain_image.state, + vulkan_sync::Usage::kImagePresent, + &m_dependency_builder); - VkDependencyInfo dependency_info2{ - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = 0, - .memoryBarrierCount = 0, - .pMemoryBarriers = nullptr, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = nullptr, - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &barrier2, - }; + m_dependency_builder.apply(command_buffer); - vkCmdPipelineBarrier2(command_buffer, &dependency_info2); res = vkEndCommandBuffer(command_buffer); if (res != VK_SUCCESS) -- cgit