+_mesa_init_buffer_objects( GLcontext *ctx )
+{
+ GLuint i;
+
+ /* Allocate the default buffer object and set refcount so high that
+ * it never gets deleted.
+ */
+ ctx->Array.NullBufferObj = _mesa_new_buffer_object(ctx, 0, 0);
+ if (ctx->Array.NullBufferObj)
+ ctx->Array.NullBufferObj->RefCount = 1000;
+
+ ctx->Array.ArrayBufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.ElementArrayBufferObj = ctx->Array.NullBufferObj;
+
+ /* Vertex array buffers */
+ ctx->Array.Vertex.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.Normal.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.Color.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.SecondaryColor.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.FogCoord.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.Index.BufferObj = ctx->Array.NullBufferObj;
+ for (i = 0; i < MAX_TEXTURE_UNITS; i++) {
+ ctx->Array.TexCoord[i].BufferObj = ctx->Array.NullBufferObj;
+ }
+ ctx->Array.EdgeFlag.BufferObj = ctx->Array.NullBufferObj;
+ for (i = 0; i < VERT_ATTRIB_MAX; i++) {
+ ctx->Array.VertexAttrib[i].BufferObj = ctx->Array.NullBufferObj;
+ }
+}
+
+
+/**
+ * When we're about to read pixel data out of a PBO (via glDrawPixels,
+ * glTexImage, etc) or write data into a PBO (via glReadPixels,
+ * glGetTexImage, etc) we call this function to check that we're not
+ * going to read out of bounds.
+ *
+ * XXX This would also be a convenient time to check that the PBO isn't
+ * currently mapped. Whoever calls this function should check for that.
+ * Remember, we can't use a PBO when it's mapped!
+ *
+ * \param width width of image to read/write
+ * \param height height of image to read/write
+ * \param depth depth of image to read/write
+ * \param format format of image to read/write
+ * \param type datatype of image to read/write
+ * \param ptr the user-provided pointer/offset
+ * \return GL_TRUE if the PBO access is OK, GL_FALSE if the access would
+ * go out of bounds.
+ */
+GLboolean
+_mesa_validate_pbo_access(GLuint dimensions,
+ const struct gl_pixelstore_attrib *pack,
+ GLsizei width, GLsizei height, GLsizei depth,
+ GLenum format, GLenum type, const GLvoid *ptr)
+{
+ GLvoid *start, *end;
+
+ ASSERT(pack->BufferObj->Name != 0);
+
+ if (pack->BufferObj->Size == 0)
+ /* no buffer! */
+ return GL_FALSE;
+
+ /* get address of first pixel we'll read */
+ start = _mesa_image_address(dimensions, pack, ptr, width, height,
+ format, type, 0, 0, 0);
+
+ /* get address just past the last pixel we'll read */
+ end = _mesa_image_address(dimensions, pack, ptr, width, height,
+ format, type, depth-1, height-1, width);
+
+
+ if ((const GLubyte *) start > (const GLubyte *) pack->BufferObj->Size) {
+ /* This will catch negative values / wrap-around */
+ return GL_FALSE;
+ }
+ if ((const GLubyte *) end > (const GLubyte *) pack->BufferObj->Size) {
+ /* Image read goes beyond end of buffer */
+ return GL_FALSE;
+ }
+
+ /* OK! */
+ return GL_TRUE;
+}
+
+
+
+
+/**********************************************************************/
+/* API Functions */
+/**********************************************************************/
+
+void GLAPIENTRY
+_mesa_BindBufferARB(GLenum target, GLuint buffer)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *oldBufObj;
+ struct gl_buffer_object *newBufObj = NULL;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ oldBufObj = buffer_object_get_target( ctx, target, "BindBufferARB" );
+ if (oldBufObj && oldBufObj->Name == buffer)
+ return; /* rebinding the same buffer object- no change */
+
+ /*
+ * Get pointer to new buffer object (newBufObj)
+ */
+ if (buffer == 0) {
+ /* The spec says there's not a buffer object named 0, but we use
+ * one internally because it simplifies things.
+ */
+ newBufObj = ctx->Array.NullBufferObj;
+ }
+ else {
+ /* non-default buffer object */
+ const struct _mesa_HashTable *hash = ctx->Shared->BufferObjects;
+ newBufObj = (struct gl_buffer_object *) _mesa_HashLookup(hash, buffer);
+ if (!newBufObj) {
+ /* if this is a new buffer object id, allocate a buffer object now */
+ newBufObj = (*ctx->Driver.NewBufferObject)(ctx, buffer, target);
+ if (!newBufObj) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB");
+ return;
+ }
+ _mesa_save_buffer_object(ctx, newBufObj);
+ }
+ newBufObj->RefCount++;
+ }
+
+ switch (target) {
+ case GL_ARRAY_BUFFER_ARB:
+ ctx->Array.ArrayBufferObj = newBufObj;
+ break;
+ case GL_ELEMENT_ARRAY_BUFFER_ARB:
+ ctx->Array.ElementArrayBufferObj = newBufObj;
+ break;
+ case GL_PIXEL_PACK_BUFFER_EXT:
+ ctx->Pack.BufferObj = newBufObj;
+ break;
+ case GL_PIXEL_UNPACK_BUFFER_EXT:
+ ctx->Unpack.BufferObj = newBufObj;
+ break;
+ default:
+ _mesa_problem(ctx, "Bad target in _mesa_BindBufferARB");
+ return;
+ }
+
+ /* Pass BindBuffer call to device driver */
+ if (ctx->Driver.BindBuffer && newBufObj)
+ (*ctx->Driver.BindBuffer)( ctx, target, newBufObj );
+
+ if (oldBufObj) {
+ oldBufObj->RefCount--;
+ assert(oldBufObj->RefCount >= 0);
+ if (oldBufObj->RefCount == 0) {
+ assert(oldBufObj->Name != 0);
+ ASSERT(ctx->Driver.DeleteBuffer);
+ ctx->Driver.DeleteBuffer( ctx, oldBufObj );
+ }
+ }
+}
+
+
+/**
+ * Delete a set of buffer objects.
+ *
+ * \param n Number of buffer objects to delete.
+ * \param ids Array of \c n buffer object IDs.
+ */
+void GLAPIENTRY
+_mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ GLsizei i;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteBuffersARB(n)");
+ return;
+ }
+
+ _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+
+ for (i = 0; i < n; i++) {
+ if (ids[i] != 0) {
+ struct gl_buffer_object *bufObj = (struct gl_buffer_object *)
+ _mesa_HashLookup(ctx->Shared->BufferObjects, ids[i]);
+ if (bufObj) {
+ /* unbind any vertex pointers bound to this buffer */
+ GLuint j;
+
+ ASSERT(bufObj->Name == ids[i]);
+
+ if (ctx->Array.Vertex.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.Vertex.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.Normal.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.Normal.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.Color.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.Color.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.SecondaryColor.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.SecondaryColor.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.FogCoord.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.FogCoord.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.Index.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.Index.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ if (ctx->Array.EdgeFlag.BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.EdgeFlag.BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ for (j = 0; j < MAX_TEXTURE_UNITS; j++) {
+ if (ctx->Array.TexCoord[j].BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.TexCoord[j].BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ }
+ for (j = 0; j < VERT_ATTRIB_MAX; j++) {
+ if (ctx->Array.VertexAttrib[j].BufferObj == bufObj) {
+ bufObj->RefCount--;
+ ctx->Array.VertexAttrib[j].BufferObj = ctx->Array.NullBufferObj;
+ ctx->Array.NullBufferObj->RefCount++;
+ }
+ }
+
+ if (ctx->Array.ArrayBufferObj == bufObj) {
+ _mesa_BindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
+ }
+ if (ctx->Array.ElementArrayBufferObj == bufObj) {
+ _mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
+ }
+
+ if (ctx->Pack.BufferObj == bufObj) {
+ _mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 );
+ }
+ if (ctx->Unpack.BufferObj == bufObj) {
+ _mesa_BindBufferARB( GL_PIXEL_UNPACK_BUFFER_EXT, 0 );
+ }
+
+ /* The ID is immediately freed for re-use */
+ _mesa_remove_buffer_object(ctx, bufObj);
+ bufObj->RefCount--;
+ if (bufObj->RefCount <= 0) {
+ ASSERT(ctx->Array.ArrayBufferObj != bufObj);
+ ASSERT(ctx->Array.ElementArrayBufferObj != bufObj);
+ ASSERT(ctx->Array.Vertex.BufferObj != bufObj);
+ ASSERT(ctx->Driver.DeleteBuffer);
+ ctx->Driver.DeleteBuffer(ctx, bufObj);
+ }
+ }
+ }
+ }
+
+ _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+}
+
+
+/**
+ * Generate a set of unique buffer object IDs and store them in \c buffer.
+ *
+ * \param n Number of IDs to generate.
+ * \param buffer Array of \c n locations to store the IDs.
+ */
+void GLAPIENTRY
+_mesa_GenBuffersARB(GLsizei n, GLuint *buffer)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ GLuint first;
+ GLint i;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glGenBuffersARB");
+ return;
+ }
+
+ if (!buffer) {
+ return;
+ }
+
+ /*
+ * This must be atomic (generation and allocation of buffer object IDs)
+ */
+ _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+
+ first = _mesa_HashFindFreeKeyBlock(ctx->Shared->BufferObjects, n);
+
+ /* Allocate new, empty buffer objects and return identifiers */
+ for (i = 0; i < n; i++) {
+ struct gl_buffer_object *bufObj;
+ GLuint name = first + i;
+ GLenum target = 0;
+ bufObj = (*ctx->Driver.NewBufferObject)( ctx, name, target );
+ if (!bufObj) {
+ _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenBuffersARB");
+ return;
+ }
+ _mesa_save_buffer_object(ctx, bufObj);
+ buffer[i] = first + i;
+ }
+
+ _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+}
+
+
+/**
+ * Determine if ID is the name of a buffer object.
+ *
+ * \param id ID of the potential buffer object.
+ * \return \c GL_TRUE if \c id is the name of a buffer object,
+ * \c GL_FALSE otherwise.
+ */
+GLboolean GLAPIENTRY
+_mesa_IsBufferARB(GLuint id)
+{
+ struct gl_buffer_object * bufObj;
+ GET_CURRENT_CONTEXT(ctx);
+ ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+
+ if (id == 0)
+ return GL_FALSE;
+
+ _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+ bufObj = (struct gl_buffer_object *) _mesa_HashLookup(ctx->Shared->BufferObjects, id);
+ _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+
+ return bufObj ? GL_TRUE : GL_FALSE;
+}
+
+
+void GLAPIENTRY