It's intentionally not very robust. If a program crashes using these functions it will probably also crash if using real VBOs, and I think you'll want to know about that. Note that it's quite possible to write code using these functions that performs well enough, only to have it be sucked down into the morass of performance loss if using real VBOs. The reason is that real VBO code can do things like stall the pipeline if you use it badly, whereas this code won't. It's not intended as something that you can use for a quick, easy and fun way of writing VBO code if your hardware doesn't support VBOs and you must therefore test your original code on hardware that does support them. Once you've got that working you can drop this in and give the player a fallback if their hardware doesn't. Because that's what it's for.
On to the code.
Code: Select all
// software emulation layer for VBOs so that i can maintain a single code path in the program
// this is intentionally not very robust - if it crashes, your code will probably crash with real VBOs too
qboolean gl_swbuffers = false;
typedef struct gl_swvbo_s
{
GLint access;
GLint mapped;
GLint size;
GLint usage;
GLvoid *data;
} gl_swvbo_t;
#define MAX_SWVBO 65536
gl_swvbo_t *gl_swvbos[MAX_SWVBO];
GLuint gl_currentvbo[2] = {0, 0};
void GL_ResetAllBuffers (void)
{
// to be called when the video mode changes
int i;
for (i = 0; i < MAX_SWVBO; i++)
{
if (gl_swvbos[i])
{
if (gl_swvbos[i]->data)
free (gl_swvbos[i]->data);
free (gl_swvbos[i]);
gl_swvbos[i] = NULL;
}
}
gl_currentvbo[0] = 0;
gl_currentvbo[1] = 0;
}
gl_swvbo_t *GL_GetBufferForTarget (GLenum target)
{
int buffer = -1;
switch (target)
{
case GL_ARRAY_BUFFER:
buffer = gl_currentvbo[0];
break;
case GL_ELEMENT_ARRAY_BUFFER:
buffer = gl_currentvbo[1];
break;
default:
return NULL;
}
return gl_swvbos[buffer];
}
void *GL_FixupPointer (GLenum target, void *pointer)
{
if (gl_swbuffers)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
if (buffer)
return ((byte *) buffer->data) + ((intptr_t) pointer);
else return pointer;
}
else return pointer;
}
void APIENTRY glBindBufferRMQ (GLenum target, GLuint buffer)
{
switch (target)
{
case GL_ARRAY_BUFFER:
gl_currentvbo[0] = buffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
gl_currentvbo[1] = buffer;
break;
default:
break;
}
}
void APIENTRY glDeleteBuffersRMQ (GLsizei n, const GLuint *buffers)
{
int i;
for (i = 0; i < n; i++)
{
if (gl_swvbos[buffers[i]])
{
if (gl_swvbos[buffers[i]]->data)
free (gl_swvbos[buffers[i]]->data);
free (gl_swvbos[buffers[i]]);
gl_swvbos[buffers[i]] = NULL;
}
}
}
void APIENTRY glGenBuffersRMQ (GLsizei n, GLuint *buffers)
{
int i, b;
for (i = 0; i < n; i++)
{
// buffer object 0 is reserved
for (b = 1; b < MAX_SWVBO; b++)
{
if (!gl_swvbos[b])
{
gl_swvbos[b] = (gl_swvbo_t *) malloc (sizeof (gl_swvbo_t));
// defaults per http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameteriv.xml
gl_swvbos[b]->access = GL_READ_WRITE;
gl_swvbos[b]->data = NULL;
gl_swvbos[b]->mapped = GL_FALSE;
gl_swvbos[b]->size = 0;
gl_swvbos[b]->usage = GL_STATIC_DRAW;
buffers[i] = b;
break;
}
}
}
}
GLboolean APIENTRY glIsBufferRMQ (GLuint buffer)
{
if (gl_swvbos[buffer])
return GL_TRUE;
else return GL_FALSE;
}
void APIENTRY glBufferDataRMQ (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
if (size > buffer->size)
{
if (buffer->data)
{
free (buffer->data);
buffer->data = NULL;
}
}
buffer->size = size;
buffer->data = malloc (size);
if (data)
memcpy (buffer->data, data, size);
}
void APIENTRY glBufferSubDataRMQ (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
memcpy (((byte *) buffer->data) + offset, data, size);
}
void APIENTRY glGetBufferSubDataRMQ (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
memcpy (data, ((byte *) buffer->data) + offset, size);
}
GLvoid *APIENTRY glMapBufferRMQ (GLenum target, GLenum access)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
buffer->mapped = GL_TRUE;
buffer->access = access;
return buffer->data;
}
GLboolean APIENTRY glUnmapBufferRMQ (GLenum target)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
buffer->mapped = GL_FALSE;
return GL_TRUE;
}
void APIENTRY glGetBufferParameterivRMQ (GLenum target, GLenum value, GLint *data)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
switch (value)
{
case GL_BUFFER_ACCESS: data[0] = buffer->access; break;
case GL_BUFFER_MAPPED: data[0] = buffer->mapped; break;
case GL_BUFFER_SIZE: data[0] = buffer->size; break;
case GL_BUFFER_USAGE: data[0] = buffer->usage; break;
default: break;
}
}
void APIENTRY glGetBufferPointervRMQ (GLenum target, GLenum name, GLvoid **params)
{
gl_swvbo_t *buffer = GL_GetBufferForTarget (target);
if (name == GL_BUFFER_MAP_POINTER_ARB)
{
if (buffer->mapped)
params[0] = buffer->data;
else params[0] = NULL;
}
else params[0] = NULL;
}
Note the GL_FixupPointer function above. This part is where that comes in.
A gl*Pointer function is now changed to look like this:
Code: Select all
glWhateverPointer (size, type, stride, GL_FixupPointer (GL_ARRAY_BUFFER, pointer));
Code: Select all
glDrawElements (mode, count, type, GL_FixupPointer (GL_ELEMENT_ARRAY_BUFFER, indices));