+#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