Files
openglplayground/VBOManager.hh
2015-01-23 15:43:06 +01:00

197 lines
4.0 KiB
C++

#ifndef __OPENGLPLAYGROUND_VBOMANAGER_HH__
#define __OPENGLPLAYGROUND_VBOMANAGER_HH__
#include <map>
#include <tuple>
#include <vector>
#include <list>
#include <glbinding/gl/gl.h>
using namespace gl;
class VBOManager {
public:
VBOManager() {
}
~VBOManager() {
}
private:
class VBO;
static const size_t default_size = 1048576;
static const size_t alignment = 8;
class AllocFailed {};
public:
class VBOAlloc {
public:
~VBOAlloc() {
if(_vbo)
_vbo->free(*this);
_vbo = nullptr;
}
VBOAlloc(VBOAlloc&& move) : _vbo(move._vbo), _ofs(move._ofs), _size(move._size) {
move._vbo = nullptr;
}
VBOAlloc(VBOAlloc const& copy) = delete;
VBOAlloc& operator=(VBOAlloc const& copy) = delete;
VBOAlloc& operator=(VBOAlloc&& move) {
_vbo = move._vbo;
_ofs = move._ofs;
_size = move._size;
move._vbo = nullptr;
return *this;
}
VBOAlloc() : _vbo(nullptr), _ofs(0), _size(0) {
}
size_t getOfs() const {
assert(_vbo);
return _ofs;
}
size_t getSize() const {
assert(_vbo);
return _size;
}
GLuint getVBOId() const {
assert(_vbo);
return _vbo->getID();
}
private:
VBOAlloc(VBO &vbo, size_t ofs, size_t size) : _vbo(&vbo), _ofs(ofs), _size(size) {
}
friend class VBOManager;
friend class VBO;
VBO *_vbo;
size_t _ofs, _size;
};
VBOAlloc alloc(size_t size, GLenum type = GL_STATIC_DRAW) {
for(auto& vbo : _vbos[type]) {
try {
return vbo.alloc(size);
} catch (AllocFailed &ex) {
continue;
}
}
_vbos[type].emplace_back((size>default_size)?size:default_size, type);
return _vbos[type].back().alloc(size);
}
private:
class VBO {
public:
VBO(size_t size = default_size, GLenum type = GL_STATIC_DRAW) : _bufID(0) {
glGenBuffers(1, &_bufID);
glBindBuffer(GL_ARRAY_BUFFER, _bufID);
glBufferData(GL_ARRAY_BUFFER, size, NULL, type);
_allocs.emplace_back(_Entry{size, false});
}
VBO(VBO const& copy) = delete;
VBO& operator=(VBO const& copy) = delete;
VBO(VBO&& move) : _bufID(move._bufID), _allocs(std::move(move._allocs)) {
move._bufID = 0;
}
VBO& operator=(VBO&& move) {
for (auto ent : _allocs) {
assert(!ent.used);
}
if (_bufID)
glDeleteBuffers(1, &_bufID);
_bufID = move._bufID;
move._bufID = 0;
_allocs = std::move(move._allocs);
}
~VBO() {
for (auto ent : _allocs) {
assert(!ent.used);
}
if (_bufID)
glDeleteBuffers(1, &_bufID);
_bufID = 0;
}
VBOAlloc alloc(size_t size) {
if (size%alignment != 0)
size += (alignment - (size%alignment));
size_t pos = 0;
for (auto it = _allocs.begin();it != _allocs.end();++it) {
if (!it->used && (it->size >= size)) {
size_t leftover = it->size - size;
it->used = true;
it->size = size;
if (leftover > 0)
_allocs.insert(++it, _Entry{leftover, false});
printf("DEBUG: VBO: Allocated %lu @ %lu in %u\n", size, pos, _bufID);
return VBOAlloc(*this, pos, size);
}
pos += it->size;
}
throw AllocFailed();
}
void free(VBOAlloc& alloc) {
size_t pos = 0;
for (auto it = _allocs.begin();it != _allocs.end();++it) {
if (pos == alloc._ofs) {
assert(it->size == alloc._size);
printf("DEBUG: VBO: Freed %lu @ %lu in %u\n", alloc._size, pos, _bufID);
it->used = false;
if ((std::next(it) != _allocs.end()) &&
!std::next(it)->used) {
it->size += std::next(it)->size;
_allocs.erase(std::next(it));
}
if ((it != _allocs.begin()) &&
!std::prev(it)->used) {
it->size += std::prev(it)->size;
_allocs.erase(std::prev(it));
}
}
pos += it->size;
}
}
GLuint getID() const {
return _bufID;
}
private:
GLuint _bufID;
struct _Entry {
size_t size;
bool used;
};
std::list<_Entry> _allocs;
};
std::map<GLenum, std::vector<VBO> > _vbos;
};
#endif