From 682d3ceda19df700ce6590028717e4f0042783ec Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 1 Dec 2021 09:45:38 +0200 Subject: [PATCH] Track the order of transfers involving the same objects Mipmap generation requires barriers between the steps, since each level is generated using the previous one. --- source/backends/vulkan/buffer_backend.cpp | 2 +- source/backends/vulkan/texture1d_backend.cpp | 2 +- source/backends/vulkan/texture2d_backend.cpp | 2 +- source/backends/vulkan/texture3d_backend.cpp | 2 +- .../backends/vulkan/texturecube_backend.cpp | 2 +- source/backends/vulkan/transferqueue.cpp | 36 +++++++++++++------ source/backends/vulkan/transferqueue.h | 10 +++--- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/source/backends/vulkan/buffer_backend.cpp b/source/backends/vulkan/buffer_backend.cpp index 14164daf..21e1dabe 100644 --- a/source/backends/vulkan/buffer_backend.cpp +++ b/source/backends/vulkan/buffer_backend.cpp @@ -52,7 +52,7 @@ void VulkanBuffer::allocate() void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d) { - void *staging = device.get_transfer_queue().prepare_transfer(sz, + void *staging = device.get_transfer_queue().prepare_transfer(this, false, sz, [this, off, sz](){ device.get_synchronizer().write_buffer(handle, off, sz); }, diff --git a/source/backends/vulkan/texture1d_backend.cpp b/source/backends/vulkan/texture1d_backend.cpp index d97576ce..e28ec423 100644 --- a/source/backends/vulkan/texture1d_backend.cpp +++ b/source/backends/vulkan/texture1d_backend.cpp @@ -30,7 +30,7 @@ void VulkanTexture1D::sub_image(unsigned level, int x, unsigned wd, const void * bool discard = (x==0 && wd==level_size); size_t data_size = wd*get_pixel_size(storage_fmt); - void *staging = device.get_transfer_queue().prepare_transfer(data_size, + void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size, [this, level, discard](){ unsigned n_levels = static_cast(this)->levels; change_layout(n_levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard); diff --git a/source/backends/vulkan/texture2d_backend.cpp b/source/backends/vulkan/texture2d_backend.cpp index e568d14f..71fb90cd 100644 --- a/source/backends/vulkan/texture2d_backend.cpp +++ b/source/backends/vulkan/texture2d_backend.cpp @@ -31,7 +31,7 @@ void VulkanTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsig bool discard = (x==0 && y==0 && wd==level_size.x && ht==level_size.y); size_t data_size = wd*ht*get_pixel_size(storage_fmt); - void *staging = device.get_transfer_queue().prepare_transfer(data_size, + void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size, [this, level, discard](){ unsigned n_levels = static_cast(this)->levels; change_layout(n_levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard); diff --git a/source/backends/vulkan/texture3d_backend.cpp b/source/backends/vulkan/texture3d_backend.cpp index 4c9873d9..581206c6 100644 --- a/source/backends/vulkan/texture3d_backend.cpp +++ b/source/backends/vulkan/texture3d_backend.cpp @@ -36,7 +36,7 @@ void VulkanTexture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd bool discard = (x==0 && y==0 && z==0 && wd==level_size.x && ht==level_size.y && dp==level_size.z); size_t data_size = wd*ht*dp*get_pixel_size(storage_fmt); - void *staging = device.get_transfer_queue().prepare_transfer(data_size, + void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size, [this, level, discard](){ unsigned n_levels = static_cast(this)->levels; change_layout(n_levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard); diff --git a/source/backends/vulkan/texturecube_backend.cpp b/source/backends/vulkan/texturecube_backend.cpp index f421e20d..5b7bd368 100644 --- a/source/backends/vulkan/texturecube_backend.cpp +++ b/source/backends/vulkan/texturecube_backend.cpp @@ -33,7 +33,7 @@ void VulkanTextureCube::sub_image(unsigned face, unsigned level, int x, int y, u bool discard = (x==0 && y==0 && wd==level_size && ht==level_size); size_t data_size = wd*ht*get_pixel_size(storage_fmt); - void *staging = device.get_transfer_queue().prepare_transfer(data_size, + void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size, [this, level, discard](){ unsigned n_levels = static_cast(this)->levels; change_layout(n_levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard); diff --git a/source/backends/vulkan/transferqueue.cpp b/source/backends/vulkan/transferqueue.cpp index 7f1cfa91..87afc3c0 100644 --- a/source/backends/vulkan/transferqueue.cpp +++ b/source/backends/vulkan/transferqueue.cpp @@ -12,8 +12,17 @@ TransferQueue::TransferQueue(Device &d): device(d) { } -TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(size_t size) +TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(void *object, bool ordered, size_t size) { + unsigned &order = next_orders[object]; + order += !order; + order += (order&1)|ordered; + + auto j = upper_bound_member(transfers, order, &PendingTransfer::order); + + PendingTransfer &transfer = *transfers.emplace(j); + transfer.order = order; + auto i = find_if(buffers, [size](const StagingBuffer &b){ return b.used+size<=b.size; }); if(i==buffers.end()) { @@ -21,15 +30,15 @@ TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(size_t size) i = prev(buffers.end()); } - PendingTransfer transfer; transfer.buffer_index = distance(buffers.begin(), i); transfer.offset = i->used; transfer.size = size; - transfers.push_back(transfer); i->used += size; - return transfers.back(); + order += ordered; + + return transfer; } void TransferQueue::dispatch_transfers(VkCommandBuffer command_buffer) @@ -37,18 +46,23 @@ void TransferQueue::dispatch_transfers(VkCommandBuffer command_buffer) if(transfers.empty()) return; - for(const PendingTransfer &t: transfers) - t.synchronize(); + for(auto i=transfers.begin(); i!=transfers.end(); ) + { + auto j = i; + for(; (j!=transfers.end() && j->order==i->order); ++j) + j->synchronize(); - device.get_synchronizer().barrier(command_buffer); + device.get_synchronizer().barrier(command_buffer); - for(const PendingTransfer &t: transfers) - { - VkBuffer buffer = buffers[t.buffer_index].buffer; - t.transfer(command_buffer, buffer, t.offset); + for(; i!=j; ++i) + { + VkBuffer buffer = buffers[i->buffer_index].buffer; + i->transfer(command_buffer, buffer, i->offset); + } } transfers.clear(); + next_orders.clear(); } diff --git a/source/backends/vulkan/transferqueue.h b/source/backends/vulkan/transferqueue.h index a25519c8..b8719d62 100644 --- a/source/backends/vulkan/transferqueue.h +++ b/source/backends/vulkan/transferqueue.h @@ -30,6 +30,7 @@ private: struct PendingTransfer { + unsigned order = 0; unsigned buffer_index = 0; std::size_t offset = 0; std::size_t size = 0; @@ -41,24 +42,25 @@ private: std::size_t default_buffer_size = 16*1048576; std::vector buffers; std::vector transfers; + std::map next_orders; public: TransferQueue(Device &); template - void *prepare_transfer(std::size_t, S &&, T &&); + void *prepare_transfer(void *, bool, std::size_t, S &&, T &&); private: - PendingTransfer &prepare_transfer(std::size_t); + PendingTransfer &prepare_transfer(void *, bool, std::size_t); public: void dispatch_transfers(VkCommandBuffer); }; template -void *TransferQueue::prepare_transfer(std::size_t size, S &&synchronize, T &&transfer) +void *TransferQueue::prepare_transfer(void *object, bool ordered, std::size_t size, S &&synchronize, T &&transfer) { - PendingTransfer &pt = prepare_transfer(size); + PendingTransfer &pt = prepare_transfer(object, ordered, size); pt.synchronize = std::forward(synchronize); pt.transfer = std::forward(transfer); return static_cast(buffers[pt.buffer_index].mapped_address)+pt.offset; -- 2.43.0