From: Mikko Rasa Date: Sun, 21 Nov 2021 10:09:40 +0000 (+0200) Subject: Add a synchronization helper class to the Vulkan backend X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=644b81d62660bafa702bfd7acedd3a0039aad993 Add a synchronization helper class to the Vulkan backend --- diff --git a/source/backends/vulkan/buffer_backend.cpp b/source/backends/vulkan/buffer_backend.cpp index 4207e87d..29dd15ef 100644 --- a/source/backends/vulkan/buffer_backend.cpp +++ b/source/backends/vulkan/buffer_backend.cpp @@ -52,6 +52,8 @@ void VulkanBuffer::allocate() void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d) { + device.get_synchronizer().access(handle, off, sz); + void *staging = device.get_transfer_queue().prepare_transfer(sz, [this, off, sz](VkCommandBuffer cmd_buf, VkBuffer staging_buf, size_t src_off){ const VulkanFunctions &vk = device.get_functions(); @@ -60,19 +62,6 @@ void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d) region.dstOffset = off; region.size = sz; vk.CmdCopyBuffer(cmd_buf, staging_buf, handle, 1, ®ion); - - VkBufferMemoryBarrier barrier = { }; - barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; - barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.buffer = handle_cast<::VkBuffer>(handle); - barrier.offset = off; - barrier.size = sz; - - vk.CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, 0, 0, 1, &barrier, 0, 0); }); const char *src = static_cast(d); diff --git a/source/backends/vulkan/buffer_backend.h b/source/backends/vulkan/buffer_backend.h index 77e83c9b..3fcb2176 100644 --- a/source/backends/vulkan/buffer_backend.h +++ b/source/backends/vulkan/buffer_backend.h @@ -11,6 +11,7 @@ class Device; class VulkanBuffer: public NonCopyable { + friend class Synchronizer; friend class VulkanPipelineState; friend class VulkanVertexSetup; diff --git a/source/backends/vulkan/commands_backend.cpp b/source/backends/vulkan/commands_backend.cpp index 1752e9da..c2c86307 100644 --- a/source/backends/vulkan/commands_backend.cpp +++ b/source/backends/vulkan/commands_backend.cpp @@ -69,6 +69,10 @@ void VulkanCommands::begin_render_pass(bool clear, const ClearValue *clear_value device.get_transfer_queue().dispatch_transfers(current_buffer); + Synchronizer &sync = device.get_synchronizer(); + sync.reset(); + sync.barrier(current_buffer); + bool to_present = false; unsigned n_attachments = framebuffer->get_format().size(); for(unsigned i=0; i(this)), destroy_queue(*static_cast(this)), + synchronizer(*static_cast(this)), transfer_queue(*static_cast(this)), pipeline_cache(*static_cast(this)) { } diff --git a/source/backends/vulkan/device_backend.h b/source/backends/vulkan/device_backend.h index 9c115661..b4b53717 100644 --- a/source/backends/vulkan/device_backend.h +++ b/source/backends/vulkan/device_backend.h @@ -7,6 +7,7 @@ #include "handles.h" #include "memoryallocator.h" #include "pipelinecache.h" +#include "synchronizer.h" #include "transferqueue.h" namespace Msp { @@ -25,6 +26,7 @@ protected: RefPtr functions; MemoryAllocator allocator; DestroyQueue destroy_queue; + Synchronizer synchronizer; TransferQueue transfer_queue; PipelineCache pipeline_cache; unsigned n_frames_in_flight = 3; @@ -42,6 +44,7 @@ public: const VulkanFunctions &get_functions() const { return *functions; } MemoryAllocator &get_allocator() { return allocator; } DestroyQueue &get_destroy_queue() { return destroy_queue; } + Synchronizer &get_synchronizer() { return synchronizer; } TransferQueue &get_transfer_queue() { return transfer_queue; } PipelineCache &get_pipeline_cache() { return pipeline_cache; } unsigned get_n_frames_in_flight() const { return n_frames_in_flight; } diff --git a/source/backends/vulkan/synchronizer.cpp b/source/backends/vulkan/synchronizer.cpp new file mode 100644 index 00000000..85c813e2 --- /dev/null +++ b/source/backends/vulkan/synchronizer.cpp @@ -0,0 +1,99 @@ +#include +#include "buffer.h" +#include "device.h" +#include "texture.h" +#include "synchronizer.h" +#include "vulkan.h" + +using namespace std; + +namespace Msp { +namespace GL { + +Synchronizer::Synchronizer(Device &d): + device(d) +{ } + +void Synchronizer::access(VkBuffer buffer, size_t offset, size_t size) +{ + auto i = find_member(buffer_accesses, buffer, &BufferAccess::buffer); + if(i==buffer_accesses.end()) + { + i = buffer_accesses.emplace(buffer_accesses.end()); + i->buffer = buffer; + i->offset = offset; + i->size = size; + } + else + { + size_t begin = min(offset, i->offset); + size_t end = max(offset+size, i->offset+i->size); + i->offset = begin; + i->size = end-begin; + } + + i->pending_write = true; +} + +void Synchronizer::reset() +{ + for(BufferAccess &b: buffer_accesses) + b.pending_write = false; +} + +void Synchronizer::barrier(VkCommandBuffer command_buffer) +{ + const VulkanFunctions &vk = device.get_functions(); + + if(buffer_accesses.empty()) + return; + + VkPipelineStageFlags src_stage = 0; + VkPipelineStageFlags dst_stage = 0; + + static constexpr VkPipelineStageFlags buffer_read_stages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT| + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT|VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + static constexpr VkPipelineStageFlags buffer_write_stages = VK_PIPELINE_STAGE_TRANSFER_BIT; + + vector buffer_barriers; + buffer_barriers.reserve(buffer_accesses.size()); + for(BufferAccess &b: buffer_accesses) + { + buffer_barriers.emplace_back(VkBufferMemoryBarrier{ }); + VkBufferMemoryBarrier &barrier = buffer_barriers.back(); + + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.srcAccessMask = (b.was_written ? VK_ACCESS_MEMORY_WRITE_BIT : 0); + barrier.dstAccessMask = (b.pending_write ? VK_ACCESS_MEMORY_WRITE_BIT : VK_ACCESS_MEMORY_READ_BIT); + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.buffer = handle_cast<::VkBuffer>(b.buffer); + barrier.offset = b.offset; + barrier.size = b.size; + + src_stage |= (b.was_written ? buffer_write_stages : buffer_read_stages); + dst_stage |= (b.pending_write ? buffer_write_stages : buffer_read_stages); + } + + if(!src_stage) + src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if(!dst_stage) + dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + vk.CmdPipelineBarrier(command_buffer, src_stage, dst_stage, 0, 0, 0, + buffer_barriers.size(), buffer_barriers.data(), 0, 0); + + for(auto i=buffer_accesses.begin(); i!=buffer_accesses.end(); ) + { + if(!i->pending_write) + i = buffer_accesses.erase(i); + else + { + i->was_written = i->pending_write; + ++i; + } + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/backends/vulkan/synchronizer.h b/source/backends/vulkan/synchronizer.h new file mode 100644 index 00000000..ea3d0759 --- /dev/null +++ b/source/backends/vulkan/synchronizer.h @@ -0,0 +1,37 @@ +#ifndef MSP_GL_VULKAN_SYNCHRONIZER_H_ +#define MSP_GL_VULKAN_SYNCHRONIZER_H_ + +#include +#include +#include "handles.h" + +namespace Msp { +namespace GL { + +class Synchronizer +{ +private: + struct BufferAccess + { + VkBuffer buffer = 0; + std::size_t offset = 0; + std::size_t size = 0; + bool was_written = false; + bool pending_write = false; + }; + + Device &device; + std::vector buffer_accesses; + +public: + Synchronizer(Device &); + + void access(VkBuffer, std::size_t, std::size_t); + void reset(); + void barrier(VkCommandBuffer); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/backends/vulkan/transferqueue.cpp b/source/backends/vulkan/transferqueue.cpp index 8080f673..d0ccb0d5 100644 --- a/source/backends/vulkan/transferqueue.cpp +++ b/source/backends/vulkan/transferqueue.cpp @@ -34,6 +34,9 @@ TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(size_t size) void TransferQueue::dispatch_transfers(VkCommandBuffer command_buffer) { + if(!transfers.empty()) + device.get_synchronizer().barrier(command_buffer); + for(const PendingTransfer &t: transfers) { VkBuffer buffer = buffers[t.buffer_index].buffer;