}
}
+
+void Buffer::AsyncTransfer::allocate()
+{
+ dest_addr = buffer.map();
+}
+
+void Buffer::AsyncTransfer::finalize()
+{
+ buffer.unmap();
+}
+
} // namespace GL
} // namespace Msp
void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d)
{
- 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);
- },
- [this, off, sz](VkCommandBuffer cmd_buf, VkBuffer staging_buf, size_t src_off){
- const VulkanFunctions &vk = device.get_functions();
-
- VkBufferCopy region = { };
- region.srcOffset = src_off;
- region.dstOffset = off;
- region.size = sz;
- vk.CmdCopyBuffer(cmd_buf, staging_buf, handle, 1, ®ion);
- });
-
+ Buffer::AsyncTransfer transfer(*static_cast<Buffer *>(this), off, sz);
const char *src = static_cast<const char *>(d);
- copy(src, src+sz, static_cast<char *>(staging));
- tq.finalize_transfer(staging);
+ copy(src, src+sz, static_cast<char *>(transfer.get_address()));
}
unsigned VulkanBuffer::get_multiplicity() const
#endif
}
+
+void Buffer::AsyncTransfer::allocate()
+{
+ if(buffer.can_map())
+ dest_addr = static_cast<char *>(buffer.map())+offset;
+ else
+ {
+ Buffer &buf = buffer;
+ size_t off = offset;
+ size_t sz = size;
+
+ dest_addr = buffer.device.get_transfer_queue().prepare_transfer(&buffer, false, size,
+ [&buf, off, sz](){
+ buf.device.get_synchronizer().write_buffer(buf.handle, off, sz);
+ },
+ [&buf, off, sz](VkCommandBuffer cmd_buf, VkBuffer staging_buf, size_t src_off){
+ const VulkanFunctions &vk = buf.device.get_functions();
+
+ VkBufferCopy region = { };
+ region.srcOffset = src_off;
+ region.dstOffset = off;
+ region.size = sz;
+ vk.CmdCopyBuffer(cmd_buf, staging_buf, buf.handle, 1, ®ion);
+ });
+ }
+}
+
+void Buffer::AsyncTransfer::finalize()
+{
+ if(buffer.can_map())
+ {
+ buffer.unmap();
+ buffer.device.get_synchronizer().write_buffer(buffer.handle, offset, size, true);
+ }
+ else
+ buffer.device.get_transfer_queue().finalize_transfer(dest_addr);
+}
+
} // namespace GL
} // namespace Msp
device(d)
{ }
-void Synchronizer::write_buffer(VkBuffer buffer, size_t offset, size_t size)
+void Synchronizer::write_buffer(VkBuffer buffer, size_t offset, size_t size, bool mapped)
{
auto i = lower_bound_member(buffer_accesses, buffer, &BufferAccess::buffer);
if(i==buffer_accesses.end() || i->buffer!=buffer)
i->size = end-begin;
}
+ if(mapped)
+ i->was_written = true;
i->pending_write = true;
}
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;
+ static constexpr VkPipelineStageFlags buffer_write_stages = VK_PIPELINE_STAGE_TRANSFER_BIT|
+ VK_PIPELINE_STAGE_HOST_BIT;
vector<VkBufferMemoryBarrier> buffer_barriers;
buffer_barriers.reserve(buffer_accesses.size());
public:
Synchronizer(Device &);
- void write_buffer(VkBuffer, std::size_t, std::size_t);
+ void write_buffer(VkBuffer, std::size_t, std::size_t, bool = false);
void split_image_mipmap(VkImage, unsigned, unsigned);
void change_image_layout(VkImage, unsigned, int, unsigned, bool);
void reset();
}
void Buffer::sub_data(size_t off, size_t sz, const void *d)
+{
+ check_sub_data(off, sz, "Buffer::sub_data");
+ BufferBackend::sub_data(off, sz, d);
+}
+
+Buffer::AsyncTransfer Buffer::sub_data_async(size_t off, size_t sz)
+{
+ check_sub_data(off, sz, "Buffer::sub_data_async");
+ return AsyncTransfer(*this, off, sz);
+}
+
+void Buffer::check_sub_data(size_t off, size_t sz, const char *func)
{
if(size==0)
- throw invalid_operation("Buffer::sub_data");
+ throw invalid_operation(func);
if(off>get_total_size() || off%size+sz>size)
- throw out_of_range("Buffer::sub_data");
-
- BufferBackend::sub_data(off, sz, d);
+ throw out_of_range(func);
}
void Buffer::require_size(size_t req_sz) const
return result;
}
+
+Buffer::AsyncTransfer::AsyncTransfer(Buffer &b, size_t o, size_t s):
+ buffer(b),
+ offset(o),
+ size(s),
+ dest_addr(0)
+{
+ allocate();
+}
+
+Buffer::AsyncTransfer::AsyncTransfer(AsyncTransfer &&other):
+ buffer(other.buffer),
+ offset(other.offset),
+ size(other.size),
+ dest_addr(other.dest_addr)
+{
+ other.dest_addr = 0;
+}
+
+Buffer::AsyncTransfer::~AsyncTransfer()
+{
+ if(dest_addr)
+ finalize();
+}
+
} // namespace GL
} // namespace Msp
{
friend BufferBackend;
+public:
+ /**
+ An RAII handle for asynchronously writing data into a buffer.
+ */
+ class AsyncTransfer: public NonCopyable
+ {
+ friend BufferBackend;
+ friend class Buffer;
+
+ private:
+ Buffer &buffer;
+ std::size_t offset = 0;
+ std::size_t size = 0;
+ void *dest_addr = 0;
+
+ AsyncTransfer(Buffer &, std::size_t, std::size_t);
+ public:
+ AsyncTransfer(AsyncTransfer &&);
+ ~AsyncTransfer();
+
+ private:
+ void allocate();
+ void finalize();
+
+ public:
+ /** Returns an address for writing the data. It should not be used
+ beyond the lifetime of the object. */
+ void *get_address() { return dest_addr; }
+ };
+
private:
std::size_t size = 0;
BufferUsage usage = STATIC;
The range must be fully inside the buffer. */
void sub_data(std::size_t, std::size_t, const void *);
+ /** Creates an asynchronous transfer for writing data to a range of bytes in
+ the buffer. While the transfer is pending, the state of the buffer region
+ is indeterminate. */
+ AsyncTransfer sub_data_async(std::size_t, std::size_t);
+
+private:
+ void check_sub_data(std::size_t, std::size_t, const char *);
+
+public:
std::size_t get_size() const { return size; }
using BufferBackend::get_multiplicity;
std::size_t get_total_size() const { return size*get_multiplicity(); }
#include <stdexcept>
-#include "buffer.h"
#include "bufferable.h"
#include "error.h"
Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
- bufferable(b)
-{
- bufferable.buffer->require_size(bufferable.get_required_buffer_size());
- mapped_address = reinterpret_cast<char *>(bufferable.buffer->map());
-}
-
-Bufferable::AsyncUpdater::~AsyncUpdater()
-{
- bufferable.buffer->unmap();
-}
+ bufferable(b),
+ transfer(b.buffer->sub_data_async(0, bufferable.get_required_buffer_size()))
+{ }
void Bufferable::AsyncUpdater::upload_data()
{
+ char *mapped_address = static_cast<char *>(transfer.get_address());
bufferable.upload_data(0, mapped_address+bufferable.offset);
// Update all bufferables in the same buffer at once
for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
#define MSP_GL_BUFFERABLE_H_
#include <msp/core/noncopyable.h>
+#include "buffer.h"
namespace Msp {
namespace GL {
-class Buffer;
-
/**
Base class for things that can store data in buffers. Multiple Bufferables
may be put in the same buffer.
{
private:
const Bufferable &bufferable;
- char *mapped_address;
+ Buffer::AsyncTransfer transfer;
public:
AsyncUpdater(const Bufferable &);
- ~AsyncUpdater();
void upload_data();
};