182 lines
3.6 KiB
C++
182 lines
3.6 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& 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(VBO&& move) : _bufID(move._bufID), _allocs(move._allocs) {
|
|
move._bufID = 0;
|
|
}
|
|
|
|
~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
|