state_bufferobject.c revision 0e30fe79e4624e42422e7eb3232c41f1832d69d9
/* Copyright (c) 2001, Stanford University
* All rights reserved
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include "state.h"
#include "state/cr_statetypes.h"
#include "state/cr_statefuncs.h"
#include "state_internals.h"
#include "cr_mem.h"
#include "cr_string.h"
static CRBufferObject *AllocBufferObject(GLuint name)
{
CRBufferObject *b = crCalloc(sizeof(CRBufferObject));
if (b) {
b->refCount = 1;
b->id = name;
b->hwid = name;
b->usage = GL_STATIC_DRAW_ARB;
b->access = GL_READ_WRITE_ARB;
b->bResyncOnRead = GL_FALSE;
#ifndef IN_GUEST
CR_STATE_SHAREDOBJ_USAGE_INIT(b);
#endif
}
return b;
}
void STATE_APIENTRY crStateGenBuffersARB(GLsizei n, GLuint *buffers)
{
CRContext *g = GetCurrentContext();
crStateGenNames(g, g->shared->buffersTable, n, buffers);
}
GLboolean crStateIsBufferBound(GLenum target)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &(g->bufferobject);
switch (target)
{
case GL_ARRAY_BUFFER_ARB:
return b->arrayBuffer->id!=0;
case GL_ELEMENT_ARRAY_BUFFER_ARB:
return b->elementsBuffer->id!=0;
#ifdef CR_ARB_pixel_buffer_object
case GL_PIXEL_PACK_BUFFER_ARB:
return b->packBuffer->id!=0;
case GL_PIXEL_UNPACK_BUFFER_ARB:
return b->unpackBuffer->id!=0;
#endif
default:
return GL_FALSE;
}
}
CRBufferObject *crStateGetBoundBufferObject(GLenum target, CRBufferObjectState *b)
{
switch (target)
{
case GL_ARRAY_BUFFER_ARB:
return b->arrayBuffer;
case GL_ELEMENT_ARRAY_BUFFER_ARB:
return b->elementsBuffer;
#ifdef CR_ARB_pixel_buffer_object
case GL_PIXEL_PACK_BUFFER_ARB:
return b->packBuffer;
case GL_PIXEL_UNPACK_BUFFER_ARB:
return b->unpackBuffer;
#endif
default:
return NULL;
}
}
DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsBufferARB( GLuint buffer )
{
CRContext *g = GetCurrentContext();
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glIsBufferARB called in begin/end");
return GL_FALSE;
}
return buffer ? crHashtableIsKeyUsed(g->shared->buffersTable, buffer) : GL_FALSE;
}
void crStateBufferObjectInit (CRContext *ctx)
{
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &sb->bufferobject;
CRBufferObjectState *b = &ctx->bufferobject;
RESET(bb->dirty, ctx->bitid);
RESET(bb->arrayBinding, ctx->bitid);
RESET(bb->elementsBinding, ctx->bitid);
#ifdef CR_ARB_pixel_buffer_object
RESET(bb->unpackBinding, ctx->bitid);
RESET(bb->packBinding, ctx->bitid);
#endif
#ifdef IN_GUEST
b->retainBufferData = GL_TRUE;
#else
b->retainBufferData = GL_FALSE;
#endif
b->nullBuffer = AllocBufferObject(0);
b->arrayBuffer = b->nullBuffer;
b->elementsBuffer = b->nullBuffer;
b->nullBuffer->refCount += 2;
#ifdef CR_ARB_pixel_buffer_object
b->packBuffer = b->nullBuffer;
b->unpackBuffer = b->nullBuffer;
b->nullBuffer->refCount += 2;
#endif
ctx->shared->bVBOResyncNeeded = GL_FALSE;
}
void crStateFreeBufferObject(void *data)
{
CRBufferObject *pObj = (CRBufferObject *)data;
if (pObj->data) crFree(pObj->data);
#ifndef IN_GUEST
if (diff_api.DeleteBuffersARB)
{
diff_api.DeleteBuffersARB(1, &pObj->hwid);
}
#endif
crFree(pObj);
}
void crStateBufferObjectDestroy (CRContext *ctx)
{
CRBufferObjectState *b = &ctx->bufferobject;
crFree(b->nullBuffer);
}
static void crStateCheckBufferHWIDCB(unsigned long key, void *data1, void *data2)
{
CRBufferObject *pObj = (CRBufferObject *) data1;
crCheckIDHWID_t *pParms = (crCheckIDHWID_t*) data2;
(void) key;
if (pObj->hwid==pParms->hwid)
pParms->id = pObj->id;
}
DECLEXPORT(GLuint) STATE_APIENTRY crStateBufferHWIDtoID(GLuint hwid)
{
CRContext *g = GetCurrentContext();
crCheckIDHWID_t parms;
parms.id = hwid;
parms.hwid = hwid;
crHashtableWalk(g->shared->buffersTable, crStateCheckBufferHWIDCB, &parms);
return parms.id;
}
DECLEXPORT(GLuint) STATE_APIENTRY crStateGetBufferHWID(GLuint id)
{
CRContext *g = GetCurrentContext();
CRBufferObject *pObj = (CRBufferObject *) crHashtableSearch(g->shared->buffersTable, id);
return pObj ? pObj->hwid : 0;
}
void STATE_APIENTRY
crStateBindBufferARB (GLenum target, GLuint buffer)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &(g->bufferobject);
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &(sb->bufferobject);
CRBufferObject *oldObj, *newObj;
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBindBufferARB called in begin/end");
return;
}
FLUSH();
oldObj = crStateGetBoundBufferObject(target, b);
if (!oldObj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glBindBufferARB(target)");
return;
}
if (buffer == 0) {
newObj = b->nullBuffer;
}
else {
newObj = (CRBufferObject *) crHashtableSearch(g->shared->buffersTable, buffer);
if (!newObj) {
CRSTATE_CHECKERR(!crHashtableIsKeyUsed(g->shared->buffersTable, buffer), GL_INVALID_OPERATION, "name is not a buffer object");
newObj = AllocBufferObject(buffer);
CRSTATE_CHECKERR(!newObj, GL_OUT_OF_MEMORY, "glBindBuffer");
#ifndef IN_GUEST
diff_api.GenBuffersARB(1, &newObj->hwid);
if (!newObj->hwid)
{
crWarning("GenBuffersARB failed!");
crFree(newObj);
return;
}
#endif
crHashtableAdd( g->shared->buffersTable, buffer, newObj );
}
#ifndef IN_GUEST
CR_STATE_SHAREDOBJ_USAGE_SET(newObj, g);
#endif
}
newObj->refCount++;
oldObj->refCount--;
switch (target)
{
case GL_ARRAY_BUFFER_ARB:
b->arrayBuffer = newObj;
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(bb->arrayBinding, g->neg_bitid);
break;
case GL_ELEMENT_ARRAY_BUFFER_ARB:
b->elementsBuffer = newObj;
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(bb->elementsBinding, g->neg_bitid);
break;
#ifdef CR_ARB_pixel_buffer_object
case GL_PIXEL_PACK_BUFFER_ARB:
b->packBuffer = newObj;
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(bb->packBinding, g->neg_bitid);
break;
case GL_PIXEL_UNPACK_BUFFER_ARB:
b->unpackBuffer = newObj;
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(bb->unpackBinding, g->neg_bitid);
break;
#endif
default: /*can't get here*/
CRASSERT(false);
return;
}
if (oldObj->refCount <= 0) {
/*we shouldn't reach this point*/
CRASSERT(false);
crHashtableDelete(g->shared->buffersTable, (unsigned long) oldObj->id, crStateFreeBufferObject);
}
#ifdef IN_GUEST
if (target == GL_PIXEL_PACK_BUFFER_ARB)
{
newObj->bResyncOnRead = GL_TRUE;
}
#endif
}
static void ctStateBuffersRefsCleanup(CRContext *ctx, CRBufferObject *obj, CRbitvalue *neg_bitid)
{
CRBufferObjectState *b = &(ctx->bufferobject);
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &(sb->bufferobject);
int j, k;
if (obj == b->arrayBuffer)
{
b->arrayBuffer = b->nullBuffer;
b->arrayBuffer->refCount++;
DIRTY(bb->dirty, neg_bitid);
DIRTY(bb->arrayBinding, neg_bitid);
}
if (obj == b->elementsBuffer)
{
b->elementsBuffer = b->nullBuffer;
b->elementsBuffer->refCount++;
DIRTY(bb->dirty, neg_bitid);
DIRTY(bb->elementsBinding, neg_bitid);
}
#ifdef CR_ARB_pixel_buffer_object
if (obj == b->packBuffer)
{
b->packBuffer = b->nullBuffer;
b->packBuffer->refCount++;
DIRTY(bb->dirty, neg_bitid);
DIRTY(bb->packBinding, neg_bitid);
}
if (obj == b->unpackBuffer)
{
b->unpackBuffer = b->nullBuffer;
b->unpackBuffer->refCount++;
DIRTY(bb->dirty, neg_bitid);
DIRTY(bb->unpackBinding, neg_bitid);
}
#endif
#ifdef CR_ARB_vertex_buffer_object
for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j)
{
CRClientPointer *cp = crStateGetClientPointerByIndex(j, &ctx->client.array);
if (obj == cp->buffer)
{
cp->buffer = b->nullBuffer;
++b->nullBuffer->refCount;
}
}
for (k=0; k<ctx->client.vertexArrayStackDepth; ++k)
{
CRVertexArrays *pArray = &ctx->client.vertexArrayStack[k];
for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j)
{
CRClientPointer *cp = crStateGetClientPointerByIndex(j, pArray);
if (obj == cp->buffer)
{
cp->buffer = b->nullBuffer;
++b->nullBuffer->refCount;
}
}
}
#endif
#ifndef IN_GUEST
CR_STATE_SHAREDOBJ_USAGE_CLEAR(obj, ctx);
#endif
}
void STATE_APIENTRY
crStateDeleteBuffersARB(GLsizei n, const GLuint *buffers)
{
CRContext *g = GetCurrentContext();
int i;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glDeleteBuffersARB called in Begin/End");
return;
}
if (n < 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
"glDeleteBuffersARB(n < 0)");
return;
}
for (i = 0; i < n; i++) {
if (buffers[i]) {
CRBufferObject *obj = (CRBufferObject *)
crHashtableSearch(g->shared->buffersTable, buffers[i]);
if (obj) {
int j;
ctStateBuffersRefsCleanup(g, obj, g->neg_bitid);
CR_STATE_SHAREDOBJ_USAGE_FOREACH_USED_IDX(obj, j)
{
CRContext *ctx = g_pAvailableContexts[j];
CRASSERT(ctx);
ctStateBuffersRefsCleanup(ctx, obj, g->neg_bitid); /* <- yes, use g->neg_bitid, i.e. neg_bitid of the current context to ensure others bits get dirtified,
* but not the current context ones*/
}
crHashtableDelete(g->shared->buffersTable, buffers[i], crStateFreeBufferObject);
}
}
}
}
void STATE_APIENTRY
crStateBufferDataARB(GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &sb->bufferobject;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferDataARB called in begin/end");
return;
}
if (size < 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
"glBufferDataARB(size < 0)");
return;
}
switch (usage) {
case GL_STREAM_DRAW_ARB:
case GL_STREAM_READ_ARB:
case GL_STREAM_COPY_ARB:
case GL_STATIC_DRAW_ARB:
case GL_STATIC_READ_ARB:
case GL_STATIC_COPY_ARB:
case GL_DYNAMIC_DRAW_ARB:
case GL_DYNAMIC_READ_ARB:
case GL_DYNAMIC_COPY_ARB:
/* OK */
break;
default:
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
"glBufferDataARB(usage)");
return;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glBufferDataARB(target)");
return;
}
if (obj->id == 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glBufferDataARB");
return;
}
if (obj->pointer) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferDataARB(buffer is mapped)");
return;
}
obj->usage = usage;
obj->size = size;
/* The user of the state tracker should set the retainBufferData field
* during context initialization, if needed.
*/
if (b->retainBufferData) {
if (obj->data) {
crFree(obj->data);
}
obj->data = crAlloc(size);
if (!obj->data) {
crStateError(__LINE__, __FILE__, GL_OUT_OF_MEMORY, "glBufferDataARB");
return;
}
if (data)
crMemcpy(obj->data, data, size);
}
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(obj->dirty, g->neg_bitid);
obj->dirtyStart = 0;
obj->dirtyLength = size;
}
void STATE_APIENTRY
crStateBufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &sb->bufferobject;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferSubDataARB called in begin/end");
return;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glBufferSubDataARB(target)");
return;
}
if (obj->id == 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferSubDataARB");
return;
}
if (obj->pointer) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferSubDataARB(buffer is mapped)");
return;
}
if (size < 0 || offset < 0 || (unsigned int)offset + size > obj->size) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glBufferSubDataARB(bad offset and/or size)");
return;
}
if (b->retainBufferData && obj->data) {
crMemcpy((char *) obj->data + offset, data, size);
}
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(obj->dirty, g->neg_bitid);
/* grow dirty region */
if (offset + size > obj->dirtyStart + obj->dirtyLength)
obj->dirtyLength = offset + size;
if (offset < obj->dirtyStart)
obj->dirtyStart = offset;
}
void STATE_APIENTRY
crStateGetBufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferSubDataARB called in begin/end");
return;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetBufferSubDataARB(target)");
return;
}
if (obj->id == 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferSubDataARB");
return;
}
if (obj->pointer) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferSubDataARB(buffer is mapped)");
return;
}
if (size < 0 || offset < 0 || (unsigned int)offset + size > obj->size) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferSubDataARB(bad offset and/or size)");
return;
}
if (b->retainBufferData && obj->data) {
crMemcpy(data, (char *) obj->data + offset, size);
}
}
void * STATE_APIENTRY
crStateMapBufferARB(GLenum target, GLenum access)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glMapBufferARB called in begin/end");
return NULL;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glMapBufferARB(target)");
return NULL;
}
if (obj->id == 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glMapBufferARB");
return GL_FALSE;
}
switch (access) {
case GL_READ_ONLY_ARB:
case GL_WRITE_ONLY_ARB:
case GL_READ_WRITE_ARB:
obj->access = access;
break;
default:
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
"glMapBufferARB(access)");
return NULL;
}
if (b->retainBufferData && obj->data)
obj->pointer = obj->data;
return obj->pointer;
}
GLboolean STATE_APIENTRY
crStateUnmapBufferARB(GLenum target)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
CRStateBits *sb = GetCurrentBits();
CRBufferObjectBits *bb = &sb->bufferobject;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glUnmapBufferARB called in begin/end");
return GL_FALSE;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glUnmapBufferARB(target)");
return GL_FALSE;
}
if (obj->id == 0) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glUnmapBufferARB");
return GL_FALSE;
}
if (!obj->pointer) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glUnmapBufferARB");
return GL_FALSE;
}
obj->pointer = NULL;
if (obj->access != GL_READ_ONLY_ARB) {
/* the data was most likely modified */
DIRTY(bb->dirty, g->neg_bitid);
DIRTY(obj->dirty, g->neg_bitid);
obj->dirtyStart = 0;
obj->dirtyLength = obj->size;
}
return GL_TRUE;
}
void STATE_APIENTRY
crStateGetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferParameterivARB called in begin/end");
return;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetBufferParameterivARB(target)");
return;
}
switch (pname) {
case GL_BUFFER_SIZE_ARB:
*params = obj->size;
break;
case GL_BUFFER_USAGE_ARB:
*params = obj->usage;
break;
case GL_BUFFER_ACCESS_ARB:
*params = obj->access;
break;
case GL_BUFFER_MAPPED_ARB:
*params = (obj->pointer != NULL);
break;
default:
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
"glGetBufferParameterivARB(pname)");
return;
}
}
void STATE_APIENTRY
crStateGetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
{
CRContext *g = GetCurrentContext();
CRBufferObjectState *b = &g->bufferobject;
CRBufferObject *obj;
FLUSH();
if (g->current.inBeginEnd) {
crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
"glGetBufferPointervARB called in begin/end");
return;
}
obj = crStateGetBoundBufferObject(target, b);
if (!obj)
{
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetBufferPointervARB(target)");
return;
}
if (pname != GL_BUFFER_MAP_POINTER_ARB) {
crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetBufferPointervARB(pname)");
return;
}
*params = obj->pointer;
}
/**
* We need to check if the GL_EXT_vertex/pixel_buffer_object extensions
* are supported before calling any of the diff_api functions.
* This flag indicates if the extensions is available (1), not available (0)
* or needs to be tested for (-1).
* If we don't do this, we can segfault inside OpenGL.
* Ideally, the render SPU should no-op unsupported GL functions, but
* that's a bit complicated.
*/
static GLboolean
HaveBufferObjectExtension(void)
{
static GLint haveBufferObjectExt = -1;
if (haveBufferObjectExt == -1) {
const char *ext;
/* XXX this check is temporary. We need to make the tilesort SPU plug
* GetString into the diff'ing table in order for this to really work.
*/
if (!diff_api.GetString) {
haveBufferObjectExt = 0;
return 0;
}
CRASSERT(diff_api.GetString);
ext = (const char *) diff_api.GetString(GL_EXTENSIONS);
if (crStrstr(ext, "GL_ARB_vertex_buffer_object") ||
crStrstr(ext, "GL_ARB_pixel_buffer_object")) {
haveBufferObjectExt = 1;
}
else {
haveBufferObjectExt = 0;
}
}
return haveBufferObjectExt;
}
static void crStateBufferObjectIntCmp(CRBufferObjectBits *bb, CRbitvalue *bitID,
CRContext *fromCtx, CRContext *toCtx,
GLboolean bSwitch)
{
CRBufferObjectState *from = &(fromCtx->bufferobject);
const CRBufferObjectState *to = &(toCtx->bufferobject);
/* ARRAY_BUFFER */
if (CHECKDIRTY(bb->arrayBinding, bitID))
{
if (from->arrayBuffer != to->arrayBuffer)
{
GLuint bufferID = to->arrayBuffer ? to->arrayBuffer->hwid : 0;
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, bufferID);
if (bSwitch)
{
FILLDIRTY(bb->arrayBinding);
FILLDIRTY(bb->dirty);
}
else
{
CLEARDIRTY2(bb->arrayBinding, bitID);
from->arrayBuffer = to->arrayBuffer;
}
}
if (bSwitch) CLEARDIRTY2(bb->arrayBinding, bitID);
}
if (to->arrayBuffer && CHECKDIRTY(to->arrayBuffer->dirty, bitID))
{
/* update array buffer data */
CRBufferObject *bufObj = to->arrayBuffer;
CRASSERT(bufObj);
if (bufObj->dirtyStart == 0 && bufObj->dirtyLength == (int) bufObj->size)
{
/* update whole buffer */
diff_api.BufferDataARB(GL_ARRAY_BUFFER_ARB, bufObj->size,
bufObj->data, bufObj->usage);
}
else
{
/* update sub buffer */
diff_api.BufferSubDataARB(GL_ARRAY_BUFFER_ARB,
bufObj->dirtyStart, bufObj->dirtyLength,
(char *) bufObj->data + bufObj->dirtyStart);
}
if (bSwitch) FILLDIRTY(bufObj->dirty);
CLEARDIRTY2(bufObj->dirty, bitID);
}
/* ELEMENTS_BUFFER */
if (CHECKDIRTY(bb->elementsBinding, bitID))
{
if (from->elementsBuffer != to->elementsBuffer)
{
GLuint bufferID = to->elementsBuffer ? to->elementsBuffer->hwid : 0;
diff_api.BindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, bufferID);
if (bSwitch)
{
FILLDIRTY(bb->elementsBinding);
FILLDIRTY(bb->dirty);
}
else
{
CLEARDIRTY2(bb->elementsBinding, bitID);
from->elementsBuffer = to->elementsBuffer;
}
}
if (bSwitch) CLEARDIRTY2(bb->elementsBinding, bitID);
}
if (to->elementsBuffer && CHECKDIRTY(to->elementsBuffer->dirty, bitID))
{
/* update array buffer data */
CRBufferObject *bufObj = to->elementsBuffer;
CRASSERT(bufObj);
if (bufObj->dirtyStart == 0 && bufObj->dirtyLength == (int) bufObj->size)
{
/* update whole buffer */
diff_api.BufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, bufObj->size,
bufObj->data, bufObj->usage);
}
else
{
/* update sub buffer */
diff_api.BufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
bufObj->dirtyStart, bufObj->dirtyLength,
(char *) bufObj->data + bufObj->dirtyStart);
}
if (bSwitch) FILLDIRTY(bufObj->dirty);
CLEARDIRTY2(bufObj->dirty, bitID);
}
#ifdef CR_ARB_pixel_buffer_object
/* PIXEL_PACK_BUFFER */
if (CHECKDIRTY(bb->packBinding, bitID))
{
if (from->packBuffer != to->packBuffer)
{
GLuint bufferID = to->packBuffer ? to->packBuffer->hwid : 0;
diff_api.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, bufferID);
if (bSwitch)
{
FILLDIRTY(bb->packBinding);
FILLDIRTY(bb->dirty);
}
else
{
CLEARDIRTY2(bb->packBinding, bitID);
from->packBuffer = to->packBuffer;
}
}
if (bSwitch) CLEARDIRTY2(bb->packBinding, bitID);
}
if (to->packBuffer && CHECKDIRTY(to->packBuffer->dirty, bitID))
{
/* update array buffer data */
CRBufferObject *bufObj = to->packBuffer;
CRASSERT(bufObj);
if (bufObj->dirtyStart == 0 && bufObj->dirtyLength == (int) bufObj->size)
{
/* update whole buffer */
diff_api.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, bufObj->size,
bufObj->data, bufObj->usage);
}
else
{
/* update sub buffer */
diff_api.BufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB,
bufObj->dirtyStart, bufObj->dirtyLength,
(char *) bufObj->data + bufObj->dirtyStart);
}
if (bSwitch) FILLDIRTY(bufObj->dirty);
CLEARDIRTY2(bufObj->dirty, bitID);
}
/* PIXEL_UNPACK_BUFFER */
if (CHECKDIRTY(bb->unpackBinding, bitID))
{
if (from->unpackBuffer != to->unpackBuffer)
{
GLuint bufferID = to->unpackBuffer ? to->unpackBuffer->hwid : 0;
diff_api.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, bufferID);
if (bSwitch)
{
FILLDIRTY(bb->unpackBinding);
FILLDIRTY(bb->dirty);
}
else
{
CLEARDIRTY2(bb->unpackBinding, bitID);
from->unpackBuffer = to->unpackBuffer;
}
}
if (bSwitch) CLEARDIRTY2(bb->unpackBinding, bitID);
}
if (to->unpackBuffer && CHECKDIRTY(to->unpackBuffer->dirty, bitID))
{
/* update array buffer data */
CRBufferObject *bufObj = to->unpackBuffer;
CRASSERT(bufObj);
if (bufObj->dirtyStart == 0 && bufObj->dirtyLength == (int) bufObj->size)
{
/* update whole buffer */
diff_api.BufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, bufObj->size,
bufObj->data, bufObj->usage);
}
else
{
/* update sub buffer */
diff_api.BufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB,
bufObj->dirtyStart, bufObj->dirtyLength,
(char *) bufObj->data + bufObj->dirtyStart);
}
if (bSwitch) FILLDIRTY(bufObj->dirty);
CLEARDIRTY2(bufObj->dirty, bitID);
}
#endif /*ifdef CR_ARB_pixel_buffer_object*/
}
void crStateBufferObjectDiff(CRBufferObjectBits *bb, CRbitvalue *bitID,
CRContext *fromCtx, CRContext *toCtx)
{
CRBufferObjectState *from = &(fromCtx->bufferobject);
const CRBufferObjectState *to = &(toCtx->bufferobject);
if (!HaveBufferObjectExtension())
return;
crStateBufferObjectIntCmp(bb, bitID, fromCtx, toCtx, GL_FALSE);
}
static void crStateBufferObjectSyncCB(unsigned long key, void *data1, void *data2)
{
CRBufferObject *pBufferObj = (CRBufferObject *) data1;
CRBufferObjectState *pState = (CRBufferObjectState *) data2;
if (pBufferObj->id && !pBufferObj->hwid)
{
diff_api.GenBuffersARB(1, &pBufferObj->hwid);
CRASSERT(pBufferObj->hwid);
}
if (pBufferObj->data)
{
/*@todo http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt
"While it is entirely legal to create a buffer object by binding
it to GL_ARRAY_BUFFER and loading it with data, then using it
with the GL_PIXEL_UNPACK_BUFFER_ARB or GL_PIXEL_PACK_BUFFER_ARB
binding, such behavior is liable to confuse the driver and may
hurt performance."
*/
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, pBufferObj->hwid);
diff_api.BufferDataARB(GL_ARRAY_BUFFER_ARB, pBufferObj->size, pBufferObj->data, pBufferObj->usage);
if (!pState->retainBufferData)
{
crFree(pBufferObj->data);
pBufferObj->data = NULL;
}
}
}
/*
* XXX this function might need some testing/fixing.
*/
void crStateBufferObjectSwitch(CRBufferObjectBits *bb, CRbitvalue *bitID,
CRContext *fromCtx, CRContext *toCtx)
{
const CRBufferObjectState *from = &(fromCtx->bufferobject);
CRBufferObjectState *to = &(toCtx->bufferobject);
int i;
if (!HaveBufferObjectExtension())
return;
if (toCtx->shared->bVBOResyncNeeded)
{
CRClientPointer *cp;
GLboolean locked = toCtx->client.array.locked;
crHashtableWalk(toCtx->shared->buffersTable, crStateBufferObjectSyncCB, to);
toCtx->shared->bVBOResyncNeeded = GL_FALSE;
/*@todo, move to state_client.c*/
cp = &toCtx->client.array.v;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.VertexPointer(cp->size, cp->type, cp->stride, cp->p);
}
cp = &toCtx->client.array.c;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.ColorPointer(cp->size, cp->type, cp->stride, cp->p);
}
cp = &toCtx->client.array.f;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.FogCoordPointerEXT(cp->type, cp->stride, cp->p);
}
cp = &toCtx->client.array.s;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.SecondaryColorPointerEXT(cp->size, cp->type, cp->stride, cp->p);
}
cp = &toCtx->client.array.e;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.EdgeFlagPointer(cp->stride, cp->p);
}
cp = &toCtx->client.array.i;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.IndexPointer(cp->type, cp->stride, cp->p);
}
cp = &toCtx->client.array.n;
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.NormalPointer(cp->type, cp->stride, cp->p);
}
for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++)
{
cp = &toCtx->client.array.t[i];
if (cp->buffer && (cp->buffer->id || locked))
{
if (diff_api.ActiveTextureARB)
diff_api.ActiveTextureARB(i+GL_TEXTURE0_ARB);
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.TexCoordPointer(cp->size, cp->type, cp->stride, cp->p);
}
}
if (diff_api.ActiveTextureARB)
diff_api.ActiveTextureARB(toCtx->client.curClientTextureUnit+GL_TEXTURE0_ARB);
#ifdef CR_NV_vertex_program
for (i = 0; i < CR_MAX_VERTEX_ATTRIBS; i++)
{
cp = &toCtx->client.array.a[i];
if (cp->buffer && (cp->buffer->id || locked))
{
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, cp->buffer->hwid);
diff_api.VertexAttribPointerARB(i, cp->size, cp->type, cp->normalized, cp->stride, cp->p);
}
}
#endif
diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, to->arrayBuffer->hwid);
diff_api.BindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, to->elementsBuffer->hwid);
#ifdef CR_ARB_pixel_buffer_object
diff_api.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, to->packBuffer->hwid);
diff_api.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, to->unpackBuffer->hwid);
#endif
}
else
{
crStateBufferObjectIntCmp(bb, bitID, fromCtx, toCtx, GL_TRUE);
}
}