#include <stdexcept>
-#include <msp/gl/extensions/arb_buffer_storage.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_map_buffer_range.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/khr_debug.h>
-#include <msp/gl/extensions/oes_mapbuffer.h>
#include <msp/strings/format.h>
#include "buffer.h"
#include "error.h"
namespace Msp {
namespace GL {
-Buffer *Buffer::scratch_binding = 0;
-
-Buffer::Buffer():
- size(0)
-{
- static Require _req(ARB_vertex_buffer_object);
-
- if(ARB_direct_state_access)
- glCreateBuffers(1, &id);
- else
- glGenBuffers(1, &id);
-}
-
-Buffer::~Buffer()
-{
- if(this==scratch_binding)
- unbind_scratch();
- glDeleteBuffers(1, &id);
-}
-
-void Buffer::storage(unsigned sz)
+void Buffer::storage(size_t sz, BufferUsage u)
{
if(size>0)
{
throw invalid_argument("Buffer::storage");
size = sz;
+ usage = u;
- if(ARB_buffer_storage)
- {
- static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
- if(ARB_direct_state_access)
- glNamedBufferStorage(id, size, 0, flags);
- else
- {
- bind_scratch();
- glBufferStorage(GL_ARRAY_BUFFER, size, 0, flags);
- }
- }
- else if(ARB_direct_state_access)
- glNamedBufferData(id, size, 0, GL_STATIC_DRAW);
- else
- {
- bind_scratch();
- glBufferData(GL_ARRAY_BUFFER, size, 0, GL_STATIC_DRAW);
- }
+ allocate();
}
void Buffer::data(const void *d)
return sub_data(0, size, d);
}
-void Buffer::sub_data(unsigned off, unsigned sz, const void *d)
+void Buffer::sub_data(size_t off, size_t sz, const void *d)
{
- if(size==0)
- throw invalid_operation("Buffer::sub_data");
+ check_sub_data(off, sz, "Buffer::sub_data");
+ BufferBackend::sub_data(off, sz, d);
+}
- if(ARB_direct_state_access)
- glNamedBufferSubData(id, off, sz, d);
- else
- {
- bind_scratch();
- glBufferSubData(GL_ARRAY_BUFFER, 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::require_size(unsigned req_sz) const
+void Buffer::check_sub_data(size_t off, size_t sz, const char *func)
+{
+ if(size==0)
+ throw invalid_operation(func);
+ if(off>get_total_size() || off%size+sz>size)
+ throw out_of_range(func);
+}
+
+void Buffer::require_size(size_t req_sz) const
{
if(size<req_sz)
throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
void *Buffer::map()
{
- static Require _req(ARB_map_buffer_range);
-
- if(ARB_direct_state_access)
- return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
- else
- {
- bind_scratch();
- void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
- return result;
- }
+ if(size==0 || !can_map() || mapped)
+ throw invalid_operation("Buffer::map");
+ void *result = BufferBackend::map();
+ mapped = true;
+ return result;
}
bool Buffer::unmap()
{
- // TODO check if it's mapped
- if(ARB_direct_state_access)
- return glUnmapNamedBuffer(id);
- else if(OES_mapbuffer)
- {
- bind_scratch();
- bool result = glUnmapBuffer(GL_ARRAY_BUFFER);
- return result;
- }
- else
- return true;
+ if(size==0 || !can_map() || !mapped)
+ throw invalid_operation("Buffer::unmap");
+ bool result = BufferBackend::unmap();
+ mapped = false;
+ return result;
}
-void Buffer::set_debug_name(const string &name)
+
+Buffer::AsyncTransfer::AsyncTransfer(Buffer &b, size_t o, size_t s):
+ buffer(&b),
+ offset(o),
+ size(s),
+ dest_addr(0)
{
-#ifdef DEBUG
- if(KHR_debug)
- glObjectLabel(GL_BUFFER, id, name.size(), name.c_str());
-#else
- (void)name;
-#endif
+ allocate();
}
-void Buffer::bind_scratch()
+Buffer::AsyncTransfer::AsyncTransfer(AsyncTransfer &&other):
+ buffer(other.buffer),
+ offset(other.offset),
+ size(other.size),
+ dest_addr(other.dest_addr)
{
- if(scratch_binding!=this)
- {
- glBindBuffer(GL_ARRAY_BUFFER, id);
- scratch_binding = this;
- }
+ other.dest_addr = 0;
}
-void Buffer::unbind_scratch()
+Buffer::AsyncTransfer &Buffer::AsyncTransfer::operator=(AsyncTransfer &&other)
{
- if(scratch_binding)
- {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- scratch_binding = 0;
- }
+ if(dest_addr)
+ finalize();
+
+ buffer = other.buffer;
+ offset = other.offset;
+ size = other.size;
+ dest_addr = other.dest_addr;
+
+ other.dest_addr = 0;
+
+ return *this;
+}
+
+Buffer::AsyncTransfer::~AsyncTransfer()
+{
+ if(dest_addr)
+ finalize();
}
} // namespace GL