1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-29 12:03:51 +00:00
Aquaria/BBGE/VertexBuffer.cpp
2023-08-25 14:07:25 +02:00

362 lines
9.1 KiB
C++

#include "VertexBuffer.h"
#include "RenderBase.h"
#include "Base.h"
#include <assert.h>
#include "Texture.h" // TexCoordBox
bool DynamicGPUBuffer::_HasARB = false;
static unsigned s_lastVertexBuffer = 0;
static unsigned s_lastIndexBuffer = 0;
static void *s_lastHostPtr = NULL;
static BufDataType s_lastDataType = BufDataType(-1);
static unsigned s_lastState = 0; // StateBits
enum StateBits
{
SB_COLOR_FROM_BUFFER = 0x01
};
static unsigned toGlUsage(unsigned usage)
{
if(usage & GPUBUF_STATIC)
return GL_STATIC_DRAW_ARB;
return GL_DYNAMIC_DRAW;
}
void DynamicGPUBuffer::StaticInit()
{
_HasARB = glGenBuffersARB && glDeleteBuffersARB
&& glBufferDataARB && glBufferSubDataARB
&& glBindBufferARB && glMapBufferARB && glUnmapBufferARB;
}
DynamicGPUBuffer::DynamicGPUBuffer(unsigned usage)
: _bufid(0)
, _binding((usage & GPUBUF_INDEXBUF) ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB)
, _size(0)
, _h_cap(0)
, _h_data(NULL)
, _d_cap(0)
, _d_map(NULL)
, _usage(toGlUsage(usage))
, _datatype(BufDataType(-1))
{
}
DynamicGPUBuffer::~DynamicGPUBuffer()
{
dropBuffer();
}
void* DynamicGPUBuffer::_allocBytes(size_t bytes)
{
if(s_lastHostPtr == _h_data)
s_lastHostPtr = NULL;
void *p = realloc(_h_data, bytes);
if(p)
{
_h_cap = bytes;
_h_data = p;
}
return p;
}
void* DynamicGPUBuffer::_ensureBytes(size_t bytes)
{
if(bytes < _h_cap)
return _h_data;
size_t newsize = 2 * _h_cap;
if(newsize < bytes)
newsize += bytes;
return _allocBytes(newsize);
}
void* DynamicGPUBuffer::beginWrite(BufDataType type, size_t newsize, unsigned access)
{
assert(!_d_map);
_size = newsize;
_datatype = type;
if(_HasARB)
{
glBindBufferARB(_binding, _ensureDBuf());
if(!(access & GPUACCESS_HOSTCOPY))
{
_d_cap = newsize;
glBufferDataARB(_binding, newsize, NULL, _usage); // orphan buffer
void *p = glMapBufferARB(_binding, GL_WRITE_ONLY_ARB);
_d_map = p;
if(p)
return p;
}
}
return _ensureBytes(newsize);
}
bool DynamicGPUBuffer::commitWrite()
{
return _commitWrite(_size);
}
bool DynamicGPUBuffer::commitWrite(size_t used)
{
_size = used;
return _commitWrite(used);
}
bool DynamicGPUBuffer::_commitWrite(size_t used)
{
if(_HasARB)
{
if(_d_map)
{
assert(used <= _d_cap);
bool ok = glUnmapBufferARB(_binding); // can fail
if(ok)
_d_map = NULL;
return ok;
}
// otherwise, the prev. call to glMapBufferARB failed (or GPUACCESS_HOSTCOPY was set).
// -> didn't map, but wrote to host memory. upload it.
assert(_h_data);
assert(used <= _h_cap);
if(used <= _d_cap)
glBufferSubDataARB(_binding, 0, used, _h_data); // update existing buffer
else
{
_d_cap = used;
glBufferDataARB(_binding, used, _h_data, _usage); // alloc new buffer
}
}
// else nothing to do
assert(used <= _h_cap);
return true;
}
void DynamicGPUBuffer::upload(BufDataType type, const void* data, size_t size)
{
_datatype = type;
if(_HasARB)
{
glBindBufferARB(_binding, _ensureDBuf());
glBufferDataARB(_binding, size, data, _usage);
}
else
memcpy(_ensureBytes(size), data, size);
}
static const unsigned s_gltype[] =
{
GL_SHORT,
GL_FLOAT,
};
struct BufPtrConfig
{
uintptr_t bufidOrPtr;
BufDataType datatype;
};
void DynamicGPUBuffer::apply(BufDataType usetype) const
{
if(!usetype)
usetype = _datatype;
const unsigned bufid = this->_bufid;
void *p;
if(bufid)
{
p = NULL;
//if(bufid != s_lastVertexBuffer)
// glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
}
else
{
p = (void*)this->_h_data;
//assert(p != s_lastHostPtr); // check that it's no redundant state change
//if(p == s_lastHostPtr && usetype == s_lastDataType) // don't need to check for datatype since that's const for the buffer with that ptr
// return;
}
assert(bufid || p);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, bufid);
//if(bufid == s_lastVertexBuffer && usetype == s_lastDataType && p == s_lastHostPtr)
// return;
s_lastDataType = usetype;
s_lastVertexBuffer = bufid;
unsigned u = usetype; // always want unsigned shifts
assert((u & 0xf) < Countof(s_gltype));
const unsigned gltype = s_gltype[u & 0xf];
const unsigned scalars = (u >> 4u) & 0xf;
const unsigned stride = (u >> 8u) & 0xff;
const unsigned tcoffset = (u >> 16u) & 0xff;
const unsigned coloroffset = u >> 24u;
// vertex and texcoords are always enabled
glVertexPointer(scalars, gltype, stride, p);
if(tcoffset)
glTexCoordPointer(2, gltype, stride, (void*)((uintptr_t)p + tcoffset));
unsigned wantedstate = 0;
if(coloroffset)
{
wantedstate |= SB_COLOR_FROM_BUFFER;
glColorPointer(4, gltype, stride, (void*)((uintptr_t)p + coloroffset));
}
unsigned wrongbits = wantedstate ^ s_lastState;
if(wrongbits)
{
if(wrongbits & SB_COLOR_FROM_BUFFER)
{
if(wantedstate & SB_COLOR_FROM_BUFFER)
glEnableClientState(GL_COLOR_ARRAY);
else
glDisableClientState(GL_COLOR_ARRAY);
}
s_lastState = wantedstate;
}
}
unsigned DynamicGPUBuffer::_ensureDBuf()
{
assert(_HasARB);
if(!_bufid)
glGenBuffersARB(1, &_bufid);
return _bufid;
}
void DynamicGPUBuffer::dropBuffer()
{
if(s_lastHostPtr == _h_data)
s_lastHostPtr = NULL;
free(_h_data);
_h_data = NULL;
if(_bufid)
{
if(s_lastVertexBuffer == _bufid)
s_lastVertexBuffer = 0;
if(s_lastIndexBuffer == _bufid)
s_lastIndexBuffer = 0;
glDeleteBuffersARB(1, &_bufid);
_bufid = 0;
}
_size = 0;
}
/*static unsigned getBoundBuffer(unsigned target)
{
int id = 0;
glGetIntegerv(target, &id);
return id;
}*/
void DynamicGPUBuffer::drawElements(unsigned glmode, size_t n, size_t first) const
{
assert(_binding == GL_ELEMENT_ARRAY_BUFFER_ARB);
assert(s_gltype[_datatype & 0xf] == GL_SHORT);
//assert(getBoundBuffer(GL_ARRAY_BUFFER_BINDING)); // FIXME: this assert is wrong if indices are on the host
unsigned id = _bufid;
//if(s_lastIndexBuffer != id)
//{
// s_lastIndexBuffer = id;
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, id);
//}
void *p = id ? NULL : _h_data;
assert(p || id);
glDrawElements(glmode, n, GL_UNSIGNED_SHORT, p);
}
void DynamicGPUBuffer::initQuadVertices(const TexCoordBox& tc, unsigned access)
{
do
{
float *p = (float*)beginWrite(GPUBUFTYPE_VEC2_TC, (4*4) * sizeof(float), access);
*p++ = -0.5f; *p++ = +0.5f; // xy
*p++ = tc.u1; *p++ = tc.v1; // uv
*p++ = +0.5f; *p++ = +0.5f; // xy
*p++ = tc.u2; *p++ = tc.v1; // uv
*p++ = +0.5f; *p++ = -0.5f; // xy
*p++ = tc.u2; *p++ = tc.v2; // uv
*p++ = -0.5f; *p++ = -0.5f; // xy
*p++ = tc.u1; *p++ = tc.v2; // uv
}
while(!commitWrite());
}
// 0---1---2---3
// | | | |
// 4---5---6---7
// | | | |
// 8---9---10--11
// This is a 4x3 grid
// Which is 3*2 = 6 quads
// That's 12 triangles
// Each triangle is 3 indices, so we get 36 indices in total
size_t DynamicGPUBuffer::initGridIndices_Triangles(size_t w, size_t h, bool invert, unsigned access)
{
assert(w * h < 0xffff);
const size_t quadsx = w - 1;
const size_t quadsy = h - 1;
const size_t quads = quadsx * quadsy;
do
{
unsigned short *p = (unsigned short*)beginWrite(GPUBUFTYPE_U16, 6*quads * sizeof(short), access);
if(!invert)
{
// top to bottom
for(size_t y = 0; y < quadsy; ++y)
{
for(size_t x = 0, i = y * w; x < quadsx; ++x, ++i)
{
*p++ = (unsigned short)(i); // 0
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w); // 4
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w + 1); // 5
*p++ = (unsigned short)(i + w); // 4
}
}
}
else
{
// bottom to top
for(size_t y = quadsy; y --> 0; )
{
for(size_t x = 0, i = y * w; x < quadsx; ++x, ++i)
{
*p++ = (unsigned short)(i); // 0
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w); // 4
*p++ = (unsigned short)(i + 1); // 1
*p++ = (unsigned short)(i + w + 1); // 5
*p++ = (unsigned short)(i + w); // 4
}
}
}
}
while(!commitWrite());
return quads * 6; // each quad is 2 triangles x 3 verts
}