]> git.tdb.fi Git - libs/gl.git/blobdiff - source/backends/vulkan/synchronizer.cpp
Add a synchronization helper class to the Vulkan backend
[libs/gl.git] / source / backends / vulkan / synchronizer.cpp
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