diff --git a/Outputs/CRT/Internals/ArrayBuilder.cpp b/Outputs/CRT/Internals/ArrayBuilder.cpp index dd7057e63..ddc89a6ed 100644 --- a/Outputs/CRT/Internals/ArrayBuilder.cpp +++ b/Outputs/CRT/Internals/ArrayBuilder.cpp @@ -8,3 +8,115 @@ #include "ArrayBuilder.hpp" +using namespace Outputs::CRT; + +ArrayBuilder::ArrayBuilder(size_t input_size, size_t output_size) : + output_(output_size), + input_(input_size) +{} + +ArrayBuilder::Buffer::Buffer(size_t size) : + allocated_data(0), completed_data(0) +{ + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)size, NULL, GL_STREAM_DRAW); + data.resize(size); +} + +ArrayBuilder::Buffer::~Buffer() +{ + glDeleteBuffers(1, &buffer); +} + +uint8_t *ArrayBuilder::get_input_storage(size_t size) +{ + return input_.get_storage(size); +} + +uint8_t *ArrayBuilder::get_output_storage(size_t size) +{ + return output_.get_storage(size); +} + +void ArrayBuilder::flush() +{ + // TODO: don't flush anything if either buffer was exhausted + buffer_mutex_.lock(); + input_.flush(); + output_.flush(); + buffer_mutex_.unlock(); +} + +void ArrayBuilder::bind_input() +{ + input_.bind(); +} + +void ArrayBuilder::bind_output() +{ + output_.bind(); +} + +ArrayBuilder::Submission ArrayBuilder::submit() +{ + ArrayBuilder::Submission submission; + + buffer_mutex_.lock(); + submission.input_size = input_.submit(); + submission.output_size = output_.submit(); + buffer_mutex_.unlock(); + // TODO: if either buffer was exhausted, reset both + + return submission; +} + +uint8_t *ArrayBuilder::Buffer::get_storage(size_t size) +{ + if(allocated_data + size > data.size()) return nullptr; + uint8_t *pointer = &data[allocated_data]; + vended_pointer = allocated_data; + allocated_data += size; + return pointer; +} + +void ArrayBuilder::Buffer::flush() +{ + // Ordinarily this just requires the completed data count to be bumped up + // to the current allocated data value. However if the amount of allocated + // data is now less than the completed data then that implies a submission + // occurred while the pointer previously vended by get_storage was in use. + // So copy whatever is danging back to the start and make amends. + if(completed_data < allocated_data) + completed_data = allocated_data; + else + { + completed_data = allocated_data - vended_pointer; + allocated_data = completed_data; + memcpy(data.data(), &data[vended_pointer], completed_data); + } +} + +size_t ArrayBuilder::Buffer::submit() +{ + size_t length = completed_data; + + glBindBuffer(GL_ARRAY_BUFFER, buffer); + uint8_t *destination = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); + memcpy(destination, data.data(), length); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length); + glUnmapBuffer(GL_ARRAY_BUFFER); + + completed_data = 0; + return length; +} + +void ArrayBuilder::Buffer::bind() +{ + glBindBuffer(GL_ARRAY_BUFFER, buffer); +} + +void ArrayBuilder::Buffer::reset() +{ + completed_data = allocated_data = 0; +} diff --git a/Outputs/CRT/Internals/ArrayBuilder.hpp b/Outputs/CRT/Internals/ArrayBuilder.hpp index 97a7b2b63..e318e9dfe 100644 --- a/Outputs/CRT/Internals/ArrayBuilder.hpp +++ b/Outputs/CRT/Internals/ArrayBuilder.hpp @@ -11,32 +11,57 @@ #include #include +#include #include "OpenGL.hpp" namespace Outputs { namespace CRT { +/*! + Owns two array buffers, an 'input' and an 'output' and vends pointers to allow an owner to write provisional data into those + plus a flush function to lock provisional data into place. Also supplies a submit method to transfer all currently locked + data to the GPU and bind_input/output methods to bind the internal buffers. + + It is safe for one thread to communicate via the get_*_storage and flush inputs asynchronously from another that is making + use of the bind and submit outputs. +*/ class ArrayBuilder { public: - ArrayBuilder(); - virtual ~ArrayBuilder(); + /// Creates an instance of ArrayBuilder with @c output_size bytes of storage for the output buffer and + /// @c input_size bytes of storage for the input buffer. + ArrayBuilder(size_t input_size, size_t output_size); + /// Attempts to add @c size bytes uint8_t *get_input_storage(size_t size); uint8_t *get_output_storage(size_t size); - void flush_storage(); + void flush(); void bind_input(); void bind_output(); - void submit(); + + struct Submission { + size_t input_size, output_size; + }; + Submission submit(); private: - struct { + struct Buffer { + Buffer(size_t size); + ~Buffer(); + std::vector data; - size_t allocated_data, completed_data; + size_t allocated_data, completed_data, vended_pointer; GLuint buffer; + + uint8_t *get_storage(size_t size); + void flush(); + size_t submit(); + void bind(); + void reset(); } output_, input_; std::mutex buffer_mutex_; + bool is_exhausted_; }; }