]> git.tdb.fi Git - libs/gl.git/commitdiff
Add a synchronization helper class to the Vulkan backend
authorMikko Rasa <tdb@tdb.fi>
Sun, 21 Nov 2021 10:09:40 +0000 (12:09 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 21 Nov 2021 23:03:08 +0000 (01:03 +0200)
source/backends/vulkan/buffer_backend.cpp
source/backends/vulkan/buffer_backend.h
source/backends/vulkan/commands_backend.cpp
source/backends/vulkan/device_backend.cpp
source/backends/vulkan/device_backend.h
source/backends/vulkan/synchronizer.cpp [new file with mode: 0644]
source/backends/vulkan/synchronizer.h [new file with mode: 0644]
source/backends/vulkan/transferqueue.cpp

index 4207e87d35d96a4bde10ff3dc642eb02f9c90267..29dd15ef363fa26fe36794e3d1a877bc821cb996 100644 (file)
@@ -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, &region);
-
-               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<const char *>(d);
index 77e83c9b3d4d8221a7b22acbd15cfb6fc3cc43f3..3fcb2176defd9c0a1760dc106d9c07e0f77f8f0b 100644 (file)
@@ -11,6 +11,7 @@ class Device;
 
 class VulkanBuffer: public NonCopyable
 {
+       friend class Synchronizer;
        friend class VulkanPipelineState;
        friend class VulkanVertexSetup;
 
index 1752e9dad68cf37422ab4dfdc07c0d23f708cfc1..c2c86307f8bb668d3a3d8d13f06d050ae8e6bab6 100644 (file)
@@ -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<n_attachments; ++i)
index b5d3db70548701d314a0b1239b829af9c0fd8faf..cc94e967f03c5372b18211d96ac4a2557fe44aa5 100644 (file)
@@ -13,6 +13,7 @@ VulkanDevice::VulkanDevice(Graphics::Window &wnd, const Graphics::VulkanOptions
        functions(new VulkanFunctions(context)),
        allocator(*static_cast<Device *>(this)),
        destroy_queue(*static_cast<Device *>(this)),
+       synchronizer(*static_cast<Device *>(this)),
        transfer_queue(*static_cast<Device *>(this)),
        pipeline_cache(*static_cast<Device *>(this))
 { }
index 9c115661b69173e9227cd47225af6026ed392354..b4b53717ff3cdc88b8773db1f1faba5badba2bd5 100644 (file)
@@ -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<VulkanFunctions> 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 (file)
index 0000000..85c813e
--- /dev/null
@@ -0,0 +1,99 @@
+#include <msp/core/algorithm.h>
+#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<VkBufferMemoryBarrier> 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 (file)
index 0000000..ea3d075
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef MSP_GL_VULKAN_SYNCHRONIZER_H_
+#define MSP_GL_VULKAN_SYNCHRONIZER_H_
+
+#include <cstdint>
+#include <vector>
+#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<BufferAccess> buffer_accesses;
+
+public:
+       Synchronizer(Device &);
+
+       void access(VkBuffer, std::size_t, std::size_t);
+       void reset();
+       void barrier(VkCommandBuffer);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
index 8080f673226db30bf0aca97c35a0aec61b7d9570..d0ccb0d571c88419838faec234a7cd99e0f7fcd9 100644 (file)
@@ -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;