From 2c93e5e1b3bcc9b9a023b8cc613b7c0af11dd0b1 Mon Sep 17 00:00:00 2001 From: Draklaw Date: Tue, 11 Jan 2022 00:22:39 +0100 Subject: [PATCH] Implement staging buffer. --- src/Vulkan/Context.cpp | 95 +++++++++++++++++++++++++++++++++++++++++- src/Vulkan/Context.h | 6 +++ src/VulkanTutorial.cpp | 28 +++++++++++-- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/Vulkan/Context.cpp b/src/Vulkan/Context.cpp index c67d3b3..8e45eac 100644 --- a/src/Vulkan/Context.cpp +++ b/src/Vulkan/Context.cpp @@ -121,6 +121,7 @@ void Context::initialize(const ContextSettings& settings) { create_surface(settings); choose_physical_device(settings); create_device(settings); + create_internal_objects(); } void Context::shutdown() { @@ -132,6 +133,9 @@ void Context::shutdown() { for(auto& callback: m_context_destruction_callbacks) callback(); + destroy_fence(m_transfer_fence); + destroy_command_pool(m_transfer_command_pool); + destroy_device(m_device); destroy_surface(m_surface); destroy_debug_messenger(m_debug_messenger); @@ -334,6 +338,68 @@ std::tuple Context::create_buffer(VkDeviceSize size, V return { buffer, memory }; } +void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size) { + VkCommandBufferAllocateInfo alloc_info { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = m_transfer_command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + VkCommandBuffer command_buffer; + vkAllocateCommandBuffers(m_device, &alloc_info, &command_buffer); + auto const command_buffer_guard = make_guard([this, command_buffer]() { + vkFreeCommandBuffers(m_device, m_transfer_command_pool, 1, &command_buffer); + }); + + VkCommandBufferBeginInfo begin_info { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + vkBeginCommandBuffer(command_buffer, &begin_info); + + VkBufferCopy region { + .srcOffset = 0, + .dstOffset = 0, + .size = size, + }; + vkCmdCopyBuffer(command_buffer, src, dst, 1, ®ion); + + VkBufferMemoryBarrier buffer_barrier { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = queue_family(VK_QUEUE_TRANSFER_BIT), + .dstQueueFamilyIndex = dst_queue_family, + .buffer = dst, + .offset = 0, + .size = size, + }; + vkCmdPipelineBarrier( + command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, + 0, nullptr, + 1, &buffer_barrier, + 0, nullptr + ); + + vkEndCommandBuffer(command_buffer); + + VkSubmitInfo submit_info { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &command_buffer, + }; + vkQueueSubmit( + queue(m_transfer_queue_index), + 1, &submit_info, + m_transfer_fence + ); + vkWaitForFences(m_device, 1, &m_transfer_fence, VK_TRUE, UINT64_MAX); +} + void Context::destroy_instance(VkInstance& instance) { if(instance == nullptr) @@ -599,7 +665,8 @@ std::optional Context::select_physical_device( VkPhysicalDeviceProperties device_properties; vkGetPhysicalDeviceProperties(physical_device, &device_properties); - m_queue_families.assign(settings.queues().size(), INVALID_QUEUE_FAMILY); + m_transfer_queue_index = settings.queues().size(); + m_queue_families.assign(settings.queues().size() + 1, INVALID_QUEUE_FAMILY); m_presentation_queue_family = INVALID_QUEUE_FAMILY; auto const queue_families = this->queue_families(physical_device); @@ -628,6 +695,10 @@ std::optional Context::select_physical_device( m_queue_families[index] = queue_family_index; } + if(m_queue_families[m_transfer_queue_index] == INVALID_QUEUE_FAMILY + && queue_family.queueFlags == VK_QUEUE_TRANSFER_BIT) + m_queue_families[m_transfer_queue_index] = queue_family_index; + queue_family_index += 1; } @@ -734,6 +805,28 @@ void Context::create_device(const ContextSettings& settings) { vkGetDeviceQueue(m_device, m_presentation_queue_family, 0, &m_presentation_queue); } +void Context::create_internal_objects() { + VkCommandPoolCreateInfo pool_info { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = 0, + .queueFamilyIndex = queue_family(m_transfer_queue_index), + }; + if(vkCreateCommandPool( + m_device, + &pool_info, + nullptr, + &m_transfer_command_pool + ) != VK_SUCCESS) { + 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"); +} + void Context::initialize_extension_functions() { uint32_t errors_count = 0; diff --git a/src/Vulkan/Context.h b/src/Vulkan/Context.h index 5db2695..564d6c2 100644 --- a/src/Vulkan/Context.h +++ b/src/Vulkan/Context.h @@ -104,6 +104,7 @@ public: VkDeviceMemory allocate_memory(VkDeviceSize size, uint32_t type_filter, VkMemoryPropertyFlags properties); std::tuple create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_properties); + void copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size); void destroy_instance(VkInstance& instance); @@ -142,6 +143,7 @@ private: const ContextSettings& settings ); void create_device(const ContextSettings& settings); + void create_internal_objects(); void initialize_extension_functions(); @@ -172,6 +174,10 @@ private: VkSurfaceFormatKHR m_surface_format; VkPresentModeKHR m_present_mode; + uint32_t m_transfer_queue_index = UINT32_MAX; + VkCommandPool m_transfer_command_pool = VK_NULL_HANDLE; + VkFence m_transfer_fence = VK_NULL_HANDLE; + std::vector m_context_destruction_callbacks; }; diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 6230c2e..2f08392 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -438,17 +438,30 @@ void VulkanTutorial::create_vertex_buffer() { VkDeviceSize buffer_size = sizeof(vertices[0]) * vertices.size(); - std::tie(m_vertex_buffer, m_vertex_buffer_memory) = m_context.create_buffer( + VkBuffer tmp_buffer = VK_NULL_HANDLE; + VkDeviceMemory tmp_buffer_memory = VK_NULL_HANDLE; + std::tie(tmp_buffer, tmp_buffer_memory) = m_context.create_buffer( buffer_size, - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ); + auto const tmp_buffer_guard = make_guard([&] { + m_context.free_memory(tmp_buffer_memory); + m_context.destroy_buffer(tmp_buffer); + }); + + std::tie(m_vertex_buffer, m_vertex_buffer_memory) = m_context.create_buffer( + buffer_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + ); void* buffer = nullptr; vkMapMemory( m_context.device(), - m_vertex_buffer_memory, + tmp_buffer_memory, 0, buffer_size, 0, @@ -459,7 +472,14 @@ void VulkanTutorial::create_vertex_buffer() { vkUnmapMemory( m_context.device(), - m_vertex_buffer_memory + tmp_buffer_memory + ); + + m_context.copy_buffer( + m_vertex_buffer, + tmp_buffer, + m_context.queue_family(GRAPHIC_QUEUE), + buffer_size ); }