This allows the data for the transfer to be written asynchronously.
void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d)
{
- void *staging = device.get_transfer_queue().prepare_transfer(this, false, sz,
+ TransferQueue &tq = device.get_transfer_queue();
+ void *staging = tq.prepare_transfer(this, false, sz,
[this, off, sz](){
device.get_synchronizer().write_buffer(handle, off, sz);
},
const char *src = static_cast<const char *>(d);
copy(src, src+sz, static_cast<char *>(staging));
+ tq.finalize_transfer(staging);
}
bool VulkanBuffer::can_map() const
unsigned level_size = self.get_level_size(level);
bool discard = (x==0 && wd==level_size);
+ TransferQueue &tq = device.get_transfer_queue();
size_t data_size = wd*get_pixel_size(storage_fmt);
- void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size,
+ void *staging = tq.prepare_transfer(this, false, data_size,
[this, level, discard](){
change_layout(level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard);
},
});
stage_pixels(staging, data, wd);
+ tq.finalize_transfer(staging);
}
void VulkanTexture1D::fill_mipmap_blit(unsigned level, void *b)
auto level_size = self.get_level_size(level);
bool discard = (x==0 && y==0 && wd==level_size.x && ht==level_size.y);
+ TransferQueue &tq = device.get_transfer_queue();
size_t data_size = wd*ht*get_pixel_size(storage_fmt);
- void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size,
+ void *staging = tq.prepare_transfer(this, false, data_size,
[this, level, discard](){
change_layout(level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard);
},
});
stage_pixels(staging, data, wd*ht);
+ tq.finalize_transfer(staging);
}
void VulkanTexture2D::fill_mipmap_blit(unsigned level, void *b)
auto level_size = self.get_level_size(level);
bool discard = (x==0 && y==0 && z==0 && wd==level_size.x && ht==level_size.y && dp==level_size.z);
+ TransferQueue &tq = device.get_transfer_queue();
size_t data_size = wd*ht*dp*get_pixel_size(storage_fmt);
- void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size,
+ void *staging = tq.prepare_transfer(this, false, data_size,
[this, level, discard](){
change_layout(level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard);
},
});
stage_pixels(staging, data, wd*ht*dp);
+ tq.finalize_transfer(staging);
}
void VulkanTexture3D::fill_mipmap_blit(unsigned level, void *b)
TransferQueue &tq = device.get_transfer_queue();
for(unsigned i=0; i+1<n_levels; ++i)
{
- tq.prepare_transfer(this, true, 0,
+ tq.prepare_transfer(this, true,
[this, i](){
change_layout(i, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, false);
change_layout(i+1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true);
unsigned level_size = self.get_level_size(level);
bool discard = (x==0 && y==0 && wd==level_size && ht==level_size);
+ TransferQueue &tq = device.get_transfer_queue();
size_t data_size = wd*ht*get_pixel_size(storage_fmt);
- void *staging = device.get_transfer_queue().prepare_transfer(this, false, data_size,
+ void *staging = tq.prepare_transfer(this, false, data_size,
[this, level, discard](){
change_layout(level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard);
},
});
stage_pixels(staging, data, wd*ht);
+ tq.finalize_transfer(staging);
}
void VulkanTextureCube::fill_mipmap_blit(unsigned level, void *b)
#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
#include "device.h"
#include "transferqueue.h"
#include "vulkan.h"
device(d)
{ }
-TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(const void *object, bool ordered, size_t size)
+void TransferQueue::allocate_staging(PendingTransfer &transfer, size_t size)
{
- unsigned &order = next_orders[object];
- order += !order;
- order += (order&1)|ordered;
+ auto i = find_if(buffers, [size](const StagingBuffer &b){ return b.used+size<=b.size; });
+ if(i==buffers.end())
+ {
+ buffers.emplace_back(device, max(default_buffer_size, size));
+ i = prev(buffers.end());
+ }
- auto j = upper_bound_member(transfers, order, &PendingTransfer::order);
+ transfer.buffer_index = distance(buffers.begin(), i);
+ transfer.offset = i->used;
+ transfer.size = size;
+ transfer.staging_address = static_cast<char *>(i->mapped_address)+transfer.offset;
- PendingTransfer &transfer = *transfers.emplace(j);
- transfer.order = order;
+ i->used += size;
+}
+
+TransferQueue::PendingTransfer &TransferQueue::prepare_transfer(const void *object, bool ordered, size_t size)
+{
+ PendingTransfer transfer;
+ transfer.object = object;
+ transfer.order = ordered;
if(size)
{
- auto i = find_if(buffers, [size](const StagingBuffer &b){ return b.used+size<=b.size; });
- if(i==buffers.end())
- {
- buffers.emplace_back(device, max(default_buffer_size, size));
- i = prev(buffers.end());
- }
+ allocate_staging(transfer, size);
+ auto i = lower_bound_member(async_transfers, transfer.staging_address, &PendingTransfer::staging_address);
+ i = async_transfers.emplace(i, move(transfer));
+ return *i;
+ }
+ else
+ return insert_transfer(move(transfer));
+}
+
+void TransferQueue::finalize_transfer(void *staging)
+{
+ auto i = lower_bound_member(async_transfers, staging, &PendingTransfer::staging_address);
+ if(i==async_transfers.end() || i->staging_address!=staging)
+ throw key_error(staging);
- transfer.buffer_index = distance(buffers.begin(), i);
- transfer.offset = i->used;
- transfer.size = size;
+ insert_transfer(move(*i));
+ async_transfers.erase(i);
+}
- i->used += size;
- }
+TransferQueue::PendingTransfer &TransferQueue::insert_transfer(PendingTransfer &&pt)
+{
+ bool ordered = pt.order;
+
+ unsigned &order = next_orders[pt.object];
+ order += !order;
+ order += (order&1)|ordered;
+
+ auto j = upper_bound_member(transfers, order, &PendingTransfer::order);
+ j = transfers.emplace(j, move(pt));
+ j->order = order;
order += ordered;
- return transfer;
+ return *j;
}
void TransferQueue::dispatch_transfers(VkCommandBuffer command_buffer)
struct PendingTransfer
{
+ const void *object = 0;
unsigned order = 0;
int buffer_index = -1;
std::size_t offset = 0;
std::size_t size = 0;
+ void *staging_address = 0;
std::function<void()> synchronize;
std::function<void(VkCommandBuffer, VkBuffer, std::size_t)> transfer;
};
std::size_t default_buffer_size = 16*1048576;
std::vector<StagingBuffer> buffers;
std::vector<PendingTransfer> transfers;
+ std::vector<PendingTransfer> async_transfers;
std::map<const void *, unsigned> next_orders;
public:
template<typename S, typename T>
void *prepare_transfer(const void *, bool, std::size_t, S &&, T &&);
+ template<typename S, typename T>
+ void prepare_transfer(const void *o, bool r, S &&s, T &&t)
+ { prepare_transfer(o, r, 0, std::forward<S>(s), std::forward<T>(t)); }
+
+ void finalize_transfer(void *);
+
private:
+ void allocate_staging(PendingTransfer &, std::size_t);
PendingTransfer &prepare_transfer(const void *, bool, std::size_t);
+ PendingTransfer &insert_transfer(PendingTransfer &&);
public:
void dispatch_transfers(VkCommandBuffer);
PendingTransfer &pt = prepare_transfer(object, ordered, size);
pt.synchronize = std::forward<S>(synchronize);
pt.transfer = std::forward<T>(transfer);
- return (pt.buffer_index<0 ? 0 : static_cast<char *>(buffers[pt.buffer_index].mapped_address)+pt.offset);
+ return pt.staging_address;
}
} // namespace GL