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();
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<const char *>(d);
class VulkanBuffer: public NonCopyable
{
+ friend class Synchronizer;
friend class VulkanPipelineState;
friend class VulkanVertexSetup;
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)
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))
{ }
#include "handles.h"
#include "memoryallocator.h"
#include "pipelinecache.h"
+#include "synchronizer.h"
#include "transferqueue.h"
namespace Msp {
RefPtr<VulkanFunctions> functions;
MemoryAllocator allocator;
DestroyQueue destroy_queue;
+ Synchronizer synchronizer;
TransferQueue transfer_queue;
PipelineCache pipeline_cache;
unsigned n_frames_in_flight = 3;
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; }
--- /dev/null
+#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
--- /dev/null
+#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
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;