mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Fleshed out first implementation of ArrayBuilder
, albeit that I need to implement exhaustion properly, as soon as I think of a sensible way to handle synchronisation.
This commit is contained in:
parent
324a1de43d
commit
57f0648742
@ -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;
|
||||
}
|
||||
|
@ -11,32 +11,57 @@
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#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<uint8_t> 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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user