Merge branch 'llvm-cliptest-viewport'
[mesa.git] / src / mesa / main / bufferobj.c
index d640f5358e54b6cc48c33f6c4ce0a4e3b7a31100..0a68008a1003fba28671f42a0f83517b54f68d87 100644 (file)
 
 
 #include "glheader.h"
+#include "enums.h"
 #include "hash.h"
 #include "imports.h"
 #include "image.h"
 #include "context.h"
 #include "bufferobj.h"
+#include "fbobject.h"
+#include "texobj.h"
 
 
 /* Debug flags */
@@ -44,7 +47,7 @@
 /*#define BOUNDS_CHECK*/
 
 
-#ifdef FEATURE_OES_mapbuffer
+#if FEATURE_OES_mapbuffer
 #define DEFAULT_ACCESS GL_MAP_WRITE_BIT
 #else
 #define DEFAULT_ACCESS (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)
 
 
 /**
- * Get the buffer object bound to the specified target in a GL context.
- *
- * \param ctx     GL context
- * \param target  Buffer object target to be retrieved.  Currently this must
- *                be either \c GL_ARRAY_BUFFER or \c GL_ELEMENT_ARRAY_BUFFER.
- * \return   A pointer to the buffer object bound to \c target in the
+ * Return pointer to address of a buffer object target.
+ * \param ctx  the GL context
+ * \param target  the buffer object target to be retrieved.
+ * \return   pointer to pointer to the buffer object bound to \c target in the
  *           specified context or \c NULL if \c target is invalid.
  */
-static INLINE struct gl_buffer_object *
-get_buffer(GLcontext *ctx, GLenum target)
+static INLINE struct gl_buffer_object **
+get_buffer_target(struct gl_context *ctx, GLenum target)
 {
-   struct gl_buffer_object * bufObj = NULL;
-
    switch (target) {
-      case GL_ARRAY_BUFFER_ARB:
-         bufObj = ctx->Array.ArrayBufferObj;
-         break;
-      case GL_ELEMENT_ARRAY_BUFFER_ARB:
-         bufObj = ctx->Array.ElementArrayBufferObj;
-         break;
-      case GL_PIXEL_PACK_BUFFER_EXT:
-         bufObj = ctx->Pack.BufferObj;
-         break;
-      case GL_PIXEL_UNPACK_BUFFER_EXT:
-         bufObj = ctx->Unpack.BufferObj;
-         break;
-      case GL_COPY_READ_BUFFER:
-         if (ctx->Extensions.ARB_copy_buffer) {
-            bufObj = ctx->CopyReadBuffer;
-         }
-         break;
-      case GL_COPY_WRITE_BUFFER:
-         if (ctx->Extensions.ARB_copy_buffer) {
-            bufObj = ctx->CopyWriteBuffer;
-         }
-         break;
-      default:
-         /* error must be recorded by caller */
-         return NULL;
+   case GL_ARRAY_BUFFER_ARB:
+      return &ctx->Array.ArrayBufferObj;
+   case GL_ELEMENT_ARRAY_BUFFER_ARB:
+      return &ctx->Array.ElementArrayBufferObj;
+   case GL_PIXEL_PACK_BUFFER_EXT:
+      return &ctx->Pack.BufferObj;
+   case GL_PIXEL_UNPACK_BUFFER_EXT:
+      return &ctx->Unpack.BufferObj;
+   case GL_COPY_READ_BUFFER:
+      return &ctx->CopyReadBuffer;
+   case GL_COPY_WRITE_BUFFER:
+      return &ctx->CopyWriteBuffer;
+#if FEATURE_EXT_transform_feedback
+   case GL_TRANSFORM_FEEDBACK_BUFFER:
+      if (ctx->Extensions.EXT_transform_feedback) {
+         return &ctx->TransformFeedback.CurrentBuffer;
+      }
+      break;
+#endif
+   default:
+      return NULL;
    }
+   return NULL;
+}
 
-   /* bufObj should point to NullBufferObj or a user-created buffer object */
-   ASSERT(bufObj);
 
-   return bufObj;
+/**
+ * Get the buffer object bound to the specified target in a GL context.
+ * \param ctx  the GL context
+ * \param target  the buffer object target to be retrieved.
+ * \return   pointer to the buffer object bound to \c target in the
+ *           specified context or \c NULL if \c target is invalid.
+ */
+static INLINE struct gl_buffer_object *
+get_buffer(struct gl_context *ctx, GLenum target)
+{
+   struct gl_buffer_object **bufObj = get_buffer_target(ctx, target);
+   if (bufObj)
+      return *bufObj;
+   return NULL;
 }
 
 
@@ -135,7 +143,7 @@ simplified_access_mode(GLbitfield access)
  * \sa glBufferSubDataARB, glGetBufferSubDataARB
  */
 static struct gl_buffer_object *
-buffer_object_subdata_range_good( GLcontext * ctx, GLenum target, 
+buffer_object_subdata_range_good( struct gl_context * ctx, GLenum target, 
                                   GLintptrARB offset, GLsizeiptrARB size,
                                   const char *caller )
 {
@@ -156,7 +164,7 @@ buffer_object_subdata_range_good( GLcontext * ctx, GLenum target,
       _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", caller);
       return NULL;
    }
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
       return NULL;
    }
@@ -165,7 +173,7 @@ buffer_object_subdata_range_good( GLcontext * ctx, GLenum target,
                  "%s(size + offset > buffer size)", caller);
       return NULL;
    }
-   if (bufObj->Pointer) {
+   if (_mesa_bufferobj_mapped(bufObj)) {
       /* Buffer is currently mapped */
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
       return NULL;
@@ -181,7 +189,7 @@ buffer_object_subdata_range_good( GLcontext * ctx, GLenum target,
  * Default callback for the \c dd_function_table::NewBufferObject() hook.
  */
 static struct gl_buffer_object *
-_mesa_new_buffer_object( GLcontext *ctx, GLuint name, GLenum target )
+_mesa_new_buffer_object( struct gl_context *ctx, GLuint name, GLenum target )
 {
    struct gl_buffer_object *obj;
 
@@ -199,18 +207,19 @@ _mesa_new_buffer_object( GLcontext *ctx, GLuint name, GLenum target )
  * Default callback for the \c dd_function_table::DeleteBuffer() hook.
  */
 static void
-_mesa_delete_buffer_object( GLcontext *ctx, struct gl_buffer_object *bufObj )
+_mesa_delete_buffer_object( struct gl_context *ctx, struct gl_buffer_object *bufObj )
 {
    (void) ctx;
 
    if (bufObj->Data)
-      _mesa_free(bufObj->Data);
+      free(bufObj->Data);
 
    /* assign strange values here to help w/ debugging */
    bufObj->RefCount = -1000;
    bufObj->Name = ~0;
 
-   _mesa_free(bufObj);
+   _glthread_DESTROY_MUTEX(bufObj->Mutex);
+   free(bufObj);
 }
 
 
@@ -219,7 +228,7 @@ _mesa_delete_buffer_object( GLcontext *ctx, struct gl_buffer_object *bufObj )
  * Set ptr to bufObj w/ reference counting.
  */
 void
-_mesa_reference_buffer_object(GLcontext *ctx,
+_mesa_reference_buffer_object(struct gl_context *ctx,
                               struct gl_buffer_object **ptr,
                               struct gl_buffer_object *bufObj)
 {
@@ -231,7 +240,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
       GLboolean deleteFlag = GL_FALSE;
       struct gl_buffer_object *oldObj = *ptr;
 
-      /*_glthread_LOCK_MUTEX(oldObj->Mutex);*/
+      _glthread_LOCK_MUTEX(oldObj->Mutex);
       ASSERT(oldObj->RefCount > 0);
       oldObj->RefCount--;
 #if 0
@@ -239,7 +248,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
              (void *) oldObj, oldObj->Name, oldObj->RefCount);
 #endif
       deleteFlag = (oldObj->RefCount == 0);
-      /*_glthread_UNLOCK_MUTEX(oldObj->Mutex);*/
+      _glthread_UNLOCK_MUTEX(oldObj->Mutex);
 
       if (deleteFlag) {
 
@@ -261,7 +270,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
 
    if (bufObj) {
       /* reference new buffer */
-      /*_glthread_LOCK_MUTEX(tex->Mutex);*/
+      _glthread_LOCK_MUTEX(bufObj->Mutex);
       if (bufObj->RefCount == 0) {
          /* this buffer's being deleted (look just above) */
          /* Not sure this can every really happen.  Warn if it does. */
@@ -276,7 +285,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
 #endif
          *ptr = bufObj;
       }
-      /*_glthread_UNLOCK_MUTEX(tex->Mutex);*/
+      _glthread_UNLOCK_MUTEX(bufObj->Mutex);
    }
 }
 
@@ -290,7 +299,8 @@ _mesa_initialize_buffer_object( struct gl_buffer_object *obj,
 {
    (void) target;
 
-   _mesa_bzero(obj, sizeof(struct gl_buffer_object));
+   memset(obj, 0, sizeof(struct gl_buffer_object));
+   _glthread_INIT_MUTEX(obj->Mutex);
    obj->RefCount = 1;
    obj->Name = name;
    obj->Usage = GL_STATIC_DRAW_ARB;
@@ -314,10 +324,11 @@ _mesa_initialize_buffer_object( struct gl_buffer_object *obj,
  * \param usage   Hints about how the data will be used.
  * \param bufObj  Object to be used.
  *
+ * \return GL_TRUE for success, GL_FALSE for failure
  * \sa glBufferDataARB, dd_function_table::BufferData.
  */
-static void
-_mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
+static GLboolean
+_mesa_buffer_data( struct gl_context *ctx, GLenum target, GLsizeiptrARB size,
                   const GLvoid * data, GLenum usage,
                   struct gl_buffer_object * bufObj )
 {
@@ -332,8 +343,13 @@ _mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
       bufObj->Usage = usage;
 
       if (data) {
-        _mesa_memcpy( bufObj->Data, data, size );
+        memcpy( bufObj->Data, data, size );
       }
+
+      return GL_TRUE;
+   }
+   else {
+      return GL_FALSE;
    }
 }
 
@@ -356,7 +372,7 @@ _mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
  * \sa glBufferSubDataARB, dd_function_table::BufferSubData.
  */
 static void
-_mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
+_mesa_buffer_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset,
                      GLsizeiptrARB size, const GLvoid * data,
                      struct gl_buffer_object * bufObj )
 {
@@ -366,7 +382,7 @@ _mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
    ASSERT(size + offset <= bufObj->Size);
 
    if (bufObj->Data) {
-      _mesa_memcpy( (GLubyte *) bufObj->Data + offset, data, size );
+      memcpy( (GLubyte *) bufObj->Data + offset, data, size );
    }
 }
 
@@ -389,14 +405,14 @@ _mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
  * \sa glBufferGetSubDataARB, dd_function_table::GetBufferSubData.
  */
 static void
-_mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
+_mesa_buffer_get_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset,
                          GLsizeiptrARB size, GLvoid * data,
                          struct gl_buffer_object * bufObj )
 {
    (void) ctx; (void) target;
 
    if (bufObj->Data && ((GLsizeiptrARB) (size + offset) <= bufObj->Size)) {
-      _mesa_memcpy( data, (GLubyte *) bufObj->Data + offset, size );
+      memcpy( data, (GLubyte *) bufObj->Data + offset, size );
    }
 }
 
@@ -416,18 +432,20 @@ _mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
  * \sa glMapBufferARB, dd_function_table::MapBuffer
  */
 static void *
-_mesa_buffer_map( GLcontext *ctx, GLenum target, GLenum access,
+_mesa_buffer_map( struct gl_context *ctx, GLenum target, GLenum access,
                  struct gl_buffer_object *bufObj )
 {
    (void) ctx;
    (void) target;
    (void) access;
    /* Just return a direct pointer to the data */
-   if (bufObj->Pointer) {
+   if (_mesa_bufferobj_mapped(bufObj)) {
       /* already mapped! */
       return NULL;
    }
    bufObj->Pointer = bufObj->Data;
+   bufObj->Length = bufObj->Size;
+   bufObj->Offset = 0;
    return bufObj->Pointer;
 }
 
@@ -437,17 +455,19 @@ _mesa_buffer_map( GLcontext *ctx, GLenum target, GLenum access,
  * Called via glMapBufferRange().
  */
 static void *
-_mesa_buffer_map_range( GLcontext *ctx, GLenum target, GLintptr offset,
+_mesa_buffer_map_range( struct gl_context *ctx, GLenum target, GLintptr offset,
                         GLsizeiptr length, GLbitfield access,
                         struct gl_buffer_object *bufObj )
 {
    (void) ctx;
    (void) target;
-   (void) access;
-   (void) length;
-   assert(!bufObj->Pointer);
+   assert(!_mesa_bufferobj_mapped(bufObj));
    /* Just return a direct pointer to the data */
-   return bufObj->Data + offset;
+   bufObj->Pointer = bufObj->Data + offset;
+   bufObj->Length = length;
+   bufObj->Offset = offset;
+   bufObj->AccessFlags = access;
+   return bufObj->Pointer;
 }
 
 
@@ -456,7 +476,7 @@ _mesa_buffer_map_range( GLcontext *ctx, GLenum target, GLintptr offset,
  * Called via glFlushMappedBufferRange().
  */
 static void
-_mesa_buffer_flush_mapped_range( GLcontext *ctx, GLenum target, 
+_mesa_buffer_flush_mapped_range( struct gl_context *ctx, GLenum target, 
                                  GLintptr offset, GLsizeiptr length,
                                  struct gl_buffer_object *obj )
 {
@@ -477,13 +497,16 @@ _mesa_buffer_flush_mapped_range( GLcontext *ctx, GLenum target,
  * \sa glUnmapBufferARB, dd_function_table::UnmapBuffer
  */
 static GLboolean
-_mesa_buffer_unmap( GLcontext *ctx, GLenum target,
+_mesa_buffer_unmap( struct gl_context *ctx, GLenum target,
                     struct gl_buffer_object *bufObj )
 {
    (void) ctx;
    (void) target;
    /* XXX we might assert here that bufObj->Pointer is non-null */
    bufObj->Pointer = NULL;
+   bufObj->Length = 0;
+   bufObj->Offset = 0;
+   bufObj->AccessFlags = 0x0;
    return GL_TRUE;
 }
 
@@ -493,7 +516,7 @@ _mesa_buffer_unmap( GLcontext *ctx, GLenum target,
  * Called via glCopyBuffserSubData().
  */
 static void
-_mesa_copy_buffer_subdata(GLcontext *ctx,
+_mesa_copy_buffer_subdata(struct gl_context *ctx,
                           struct gl_buffer_object *src,
                           struct gl_buffer_object *dst,
                           GLintptr readOffset, GLintptr writeOffset,
@@ -502,8 +525,8 @@ _mesa_copy_buffer_subdata(GLcontext *ctx,
    GLubyte *srcPtr, *dstPtr;
 
    /* buffer should not already be mapped */
-   assert(!src->Pointer);
-   assert(!dst->Pointer);
+   assert(!_mesa_bufferobj_mapped(src));
+   assert(!_mesa_bufferobj_mapped(dst));
 
    srcPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_READ_BUFFER,
                                               GL_READ_ONLY, src);
@@ -511,7 +534,7 @@ _mesa_copy_buffer_subdata(GLcontext *ctx,
                                               GL_WRITE_ONLY, dst);
 
    if (srcPtr && dstPtr)
-      _mesa_memcpy(dstPtr + writeOffset, srcPtr + readOffset, size);
+      memcpy(dstPtr + writeOffset, srcPtr + readOffset, size);
 
    ctx->Driver.UnmapBuffer(ctx, GL_COPY_READ_BUFFER, src);
    ctx->Driver.UnmapBuffer(ctx, GL_COPY_WRITE_BUFFER, dst);
@@ -523,7 +546,7 @@ _mesa_copy_buffer_subdata(GLcontext *ctx,
  * Initialize the state associated with buffer objects
  */
 void
-_mesa_init_buffer_objects( GLcontext *ctx )
+_mesa_init_buffer_objects( struct gl_context *ctx )
 {
    _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj,
                                  ctx->Shared->NullBufferObj);
@@ -537,50 +560,36 @@ _mesa_init_buffer_objects( GLcontext *ctx )
 }
 
 
+void
+_mesa_free_buffer_objects( struct gl_context *ctx )
+{
+   _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, NULL);
+   _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, NULL);
+
+   _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, NULL);
+   _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, NULL);
+}
+
+
 /**
  * Bind the specified target to buffer for the specified context.
+ * Called by glBindBuffer() and other functions.
  */
 static void
-bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
+bind_buffer_object(struct gl_context *ctx, GLenum target, GLuint buffer)
 {
    struct gl_buffer_object *oldBufObj;
    struct gl_buffer_object *newBufObj = NULL;
    struct gl_buffer_object **bindTarget = NULL;
 
-   switch (target) {
-   case GL_ARRAY_BUFFER_ARB:
-      bindTarget = &ctx->Array.ArrayBufferObj;
-      break;
-   case GL_ELEMENT_ARRAY_BUFFER_ARB:
-      bindTarget = &ctx->Array.ElementArrayBufferObj;
-      break;
-   case GL_PIXEL_PACK_BUFFER_EXT:
-      bindTarget = &ctx->Pack.BufferObj;
-      break;
-   case GL_PIXEL_UNPACK_BUFFER_EXT:
-      bindTarget = &ctx->Unpack.BufferObj;
-      break;
-   case GL_COPY_READ_BUFFER:
-      if (ctx->Extensions.ARB_copy_buffer) {
-         bindTarget = &ctx->CopyReadBuffer;
-      }
-      break;
-   case GL_COPY_WRITE_BUFFER:
-      if (ctx->Extensions.ARB_copy_buffer) {
-         bindTarget = &ctx->CopyWriteBuffer;
-      }
-      break;
-   default:
-      ; /* no-op / we'll hit the follow error test next */
-   }
-
+   bindTarget = get_buffer_target(ctx, target);
    if (!bindTarget) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)");
+      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)", target);
       return;
    }
 
    /* Get pointer to old buffer object (to be unbound) */
-   oldBufObj = get_buffer(ctx, target);
+   oldBufObj = *bindTarget;
    if (oldBufObj && oldBufObj->Name == buffer)
       return;   /* rebinding the same buffer object- no change */
 
@@ -623,7 +632,7 @@ bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
  * shared state.
  */
 void
-_mesa_update_default_objects_buffer_objects(GLcontext *ctx)
+_mesa_update_default_objects_buffer_objects(struct gl_context *ctx)
 {
    /* Bind the NullBufferObj to remove references to those
     * in the shared context hash table.
@@ -645,6 +654,8 @@ _mesa_update_default_objects_buffer_objects(GLcontext *ctx)
  * currently mapped.  Whoever calls this function should check for that.
  * Remember, we can't use a PBO when it's mapped!
  *
+ * If we're not using a PBO, this is a no-op.
+ *
  * \param width  width of image to read/write
  * \param height  height of image to read/write
  * \param depth  depth of image to read/write
@@ -663,7 +674,8 @@ _mesa_validate_pbo_access(GLuint dimensions,
    GLvoid *start, *end;
    const GLubyte *sizeAddr; /* buffer size, cast to a pointer */
 
-   ASSERT(pack->BufferObj->Name != 0);
+   if (!_mesa_is_bufferobj(pack->BufferObj))
+      return GL_TRUE;  /* no PBO, OK */
 
    if (pack->BufferObj->Size == 0)
       /* no buffer! */
@@ -695,21 +707,22 @@ _mesa_validate_pbo_access(GLuint dimensions,
 
 
 /**
- * If the source of glBitmap data is a PBO, check that we won't read out
- * of buffer bounds, then map the buffer.
- * If not sourcing from a PBO, just return the bitmap pointer.
- * This is a helper function for (some) drivers.
- * Return NULL if error.
- * If non-null return, must call _mesa_unmap_bitmap_pbo() when done.
+ * For commands that read from a PBO (glDrawPixels, glTexImage,
+ * glPolygonStipple, etc), if we're reading from a PBO, map it read-only
+ * and return the pointer into the PBO.  If we're not reading from a
+ * PBO, return \p src as-is.
+ * If non-null return, must call _mesa_unmap_pbo_source() when done.
+ *
+ * \return NULL if error, else pointer to start of data
  */
-const GLubyte *
-_mesa_map_bitmap_pbo(GLcontext *ctx,
+const GLvoid *
+_mesa_map_pbo_source(struct gl_context *ctx,
                      const struct gl_pixelstore_attrib *unpack,
-                     const GLubyte *bitmap)
+                     const GLvoid *src)
 {
    const GLubyte *buf;
 
-   if (unpack->BufferObj->Name) {
+   if (_mesa_is_bufferobj(unpack->BufferObj)) {
       /* unpack from PBO */
       buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
                                               GL_READ_ONLY_ARB,
@@ -717,11 +730,11 @@ _mesa_map_bitmap_pbo(GLcontext *ctx,
       if (!buf)
          return NULL;
 
-      buf = ADD_POINTERS(buf, bitmap);
+      buf = ADD_POINTERS(buf, src);
    }
    else {
       /* unpack from normal memory */
-      buf = bitmap;
+      buf = src;
    }
 
    return buf;
@@ -729,57 +742,55 @@ _mesa_map_bitmap_pbo(GLcontext *ctx,
 
 
 /**
- * Counterpart to _mesa_map_bitmap_pbo()
- * This is a helper function for (some) drivers.
- */
-void
-_mesa_unmap_bitmap_pbo(GLcontext *ctx,
-                       const struct gl_pixelstore_attrib *unpack)
-{
-   if (unpack->BufferObj->Name) {
-      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
-                              unpack->BufferObj);
-   }
-}
-
-
-/**
- * \sa _mesa_map_bitmap_pbo
+ * Combine PBO-read validation and mapping.
+ * If any GL errors are detected, they'll be recorded and NULL returned.
+ * \sa _mesa_validate_pbo_access
+ * \sa _mesa_map_pbo_source
+ * A call to this function should have a matching call to
+ * _mesa_unmap_pbo_source().
  */
 const GLvoid *
-_mesa_map_drawpix_pbo(GLcontext *ctx,
-                      const struct gl_pixelstore_attrib *unpack,
-                      const GLvoid *pixels)
+_mesa_map_validate_pbo_source(struct gl_context *ctx,
+                              GLuint dimensions,
+                              const struct gl_pixelstore_attrib *unpack,
+                              GLsizei width, GLsizei height, GLsizei depth,
+                              GLenum format, GLenum type, const GLvoid *ptr,
+                              const char *where)
 {
-   const GLvoid *buf;
+   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
 
-   if (unpack->BufferObj->Name) {
-      /* unpack from PBO */
-      buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
-                                              GL_READ_ONLY_ARB,
-                                              unpack->BufferObj);
-      if (!buf)
-         return NULL;
+   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
+      /* non-PBO access: no validation to be done */
+      return ptr;
+   }
 
-      buf = ADD_POINTERS(buf, pixels);
+   if (!_mesa_validate_pbo_access(dimensions, unpack,
+                                  width, height, depth, format, type, ptr)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(out of bounds PBO access)", where);
+      return NULL;
    }
-   else {
-      /* unpack from normal memory */
-      buf = pixels;
+
+   if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
+      /* buffer is already mapped - that's an error */
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
+      return NULL;
    }
 
-   return buf;
+   ptr = _mesa_map_pbo_source(ctx, unpack, ptr);
+   return ptr;
 }
 
 
 /**
- * \sa _mesa_unmap_bitmap_pbo
+ * Counterpart to _mesa_map_pbo_source()
  */
 void
-_mesa_unmap_drawpix_pbo(GLcontext *ctx,
-                        const struct gl_pixelstore_attrib *unpack)
+_mesa_unmap_pbo_source(struct gl_context *ctx,
+                       const struct gl_pixelstore_attrib *unpack)
 {
-   if (unpack->BufferObj->Name) {
+   ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */
+   if (_mesa_is_bufferobj(unpack->BufferObj)) {
       ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
                               unpack->BufferObj);
    }
@@ -787,18 +798,21 @@ _mesa_unmap_drawpix_pbo(GLcontext *ctx,
 
 
 /**
- * If PBO is bound, map the buffer, return dest pointer in mapped buffer.
- * Call _mesa_unmap_readpix_pbo() when finished
- * \return NULL if error
+ * For commands that write to a PBO (glReadPixels, glGetColorTable, etc),
+ * if we're writing to a PBO, map it write-only and return the pointer
+ * into the PBO.  If we're not writing to a PBO, return \p dst as-is.
+ * If non-null return, must call _mesa_unmap_pbo_dest() when done.
+ *
+ * \return NULL if error, else pointer to start of data
  */
 void *
-_mesa_map_readpix_pbo(GLcontext *ctx,
-                      const struct gl_pixelstore_attrib *pack,
-                      GLvoid *dest)
+_mesa_map_pbo_dest(struct gl_context *ctx,
+                   const struct gl_pixelstore_attrib *pack,
+                   GLvoid *dest)
 {
    void *buf;
 
-   if (pack->BufferObj->Name) {
+   if (_mesa_is_bufferobj(pack->BufferObj)) {
       /* pack into PBO */
       buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
                                               GL_WRITE_ONLY_ARB,
@@ -818,13 +832,55 @@ _mesa_map_readpix_pbo(GLcontext *ctx,
 
 
 /**
- * Counterpart to _mesa_map_readpix_pbo()
+ * Combine PBO-write validation and mapping.
+ * If any GL errors are detected, they'll be recorded and NULL returned.
+ * \sa _mesa_validate_pbo_access
+ * \sa _mesa_map_pbo_dest
+ * A call to this function should have a matching call to
+ * _mesa_unmap_pbo_dest().
+ */
+GLvoid *
+_mesa_map_validate_pbo_dest(struct gl_context *ctx,
+                            GLuint dimensions,
+                            const struct gl_pixelstore_attrib *unpack,
+                            GLsizei width, GLsizei height, GLsizei depth,
+                            GLenum format, GLenum type, GLvoid *ptr,
+                            const char *where)
+{
+   ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3);
+
+   if (!_mesa_is_bufferobj(unpack->BufferObj)) {
+      /* non-PBO access: no validation to be done */
+      return ptr;
+   }
+
+   if (!_mesa_validate_pbo_access(dimensions, unpack,
+                                  width, height, depth, format, type, ptr)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(out of bounds PBO access)", where);
+      return NULL;
+   }
+
+   if (_mesa_bufferobj_mapped(unpack->BufferObj)) {
+      /* buffer is already mapped - that's an error */
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where);
+      return NULL;
+   }
+
+   ptr = _mesa_map_pbo_dest(ctx, unpack, ptr);
+   return ptr;
+}
+
+
+/**
+ * Counterpart to _mesa_map_pbo_dest()
  */
 void
-_mesa_unmap_readpix_pbo(GLcontext *ctx,
-                        const struct gl_pixelstore_attrib *pack)
+_mesa_unmap_pbo_dest(struct gl_context *ctx,
+                     const struct gl_pixelstore_attrib *pack)
 {
-   if (pack->BufferObj->Name) {
+   ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */
+   if (_mesa_is_bufferobj(pack->BufferObj)) {
       ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, pack->BufferObj);
    }
 }
@@ -836,7 +892,7 @@ _mesa_unmap_readpix_pbo(GLcontext *ctx,
  * Always return NULL for ID 0.
  */
 struct gl_buffer_object *
-_mesa_lookup_bufferobj(GLcontext *ctx, GLuint buffer)
+_mesa_lookup_bufferobj(struct gl_context *ctx, GLuint buffer)
 {
    if (buffer == 0)
       return NULL;
@@ -853,7 +909,7 @@ _mesa_lookup_bufferobj(GLcontext *ctx, GLuint buffer)
  * unbound from all arrays in the current context.
  */
 static void
-unbind(GLcontext *ctx,
+unbind(struct gl_context *ctx,
        struct gl_buffer_object **ptr,
        struct gl_buffer_object *obj)
 {
@@ -932,7 +988,7 @@ _mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
 
          ASSERT(bufObj->Name == ids[i]);
 
-         if (bufObj->Pointer) {
+         if (_mesa_bufferobj_mapped(bufObj)) {
             /* if mapped, unmap it now */
             ctx->Driver.UnmapBuffer(ctx, 0, bufObj);
             bufObj->AccessFlags = DEFAULT_ACCESS;
@@ -1065,20 +1121,20 @@ _mesa_BufferDataARB(GLenum target, GLsizeiptrARB size,
    }
 
    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:
-         _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)");
-         return;
+   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:
+      _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)");
+      return;
    }
 
    bufObj = get_buffer(ctx, target);
@@ -1086,34 +1142,35 @@ _mesa_BufferDataARB(GLenum target, GLsizeiptrARB size,
       _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(target)" );
       return;
    }
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer 0)" );
       return;
    }
    
-   if (bufObj->Pointer) {
+   if (_mesa_bufferobj_mapped(bufObj)) {
       /* Unmap the existing buffer.  We'll replace it now.  Not an error. */
       ctx->Driver.UnmapBuffer(ctx, target, bufObj);
       bufObj->AccessFlags = DEFAULT_ACCESS;
-      bufObj->Pointer = NULL;
+      ASSERT(bufObj->Pointer == NULL);
    }  
 
    FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT);
 
-   ASSERT(ctx->Driver.BufferData);
-
    bufObj->Written = GL_TRUE;
 
 #ifdef VBO_DEBUG
-   _mesa_printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n",
+   printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n",
                 bufObj->Name, size, data, usage);
 #endif
 
 #ifdef BOUNDS_CHECK
    size += 100;
 #endif
-   /* Give the buffer object to the driver!  <data> may be null! */
-   ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj );
+
+   ASSERT(ctx->Driver.BufferData);
+   if (!ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj )) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB()");
+   }
 }
 
 
@@ -1165,21 +1222,23 @@ _mesa_MapBufferARB(GLenum target, GLenum access)
    GET_CURRENT_CONTEXT(ctx);
    struct gl_buffer_object * bufObj;
    GLbitfield accessFlags;
+   void *map;
+
    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL);
 
    switch (access) {
-      case GL_READ_ONLY_ARB:
-         accessFlags = GL_MAP_READ_BIT;
-         break;
-      case GL_WRITE_ONLY_ARB:
-         accessFlags = GL_MAP_WRITE_BIT;
-         break;
-      case GL_READ_WRITE_ARB:
-         accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
-         break;
-      default:
-         _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)");
-         return NULL;
+   case GL_READ_ONLY_ARB:
+      accessFlags = GL_MAP_READ_BIT;
+      break;
+   case GL_WRITE_ONLY_ARB:
+      accessFlags = GL_MAP_WRITE_BIT;
+      break;
+   case GL_READ_WRITE_ARB:
+      accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)");
+      return NULL;
    }
 
    bufObj = get_buffer(ctx, target);
@@ -1187,31 +1246,38 @@ _mesa_MapBufferARB(GLenum target, GLenum access)
       _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(target)" );
       return NULL;
    }
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(buffer 0)" );
       return NULL;
    }
-   if (bufObj->Pointer) {
+   if (_mesa_bufferobj_mapped(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(already mapped)");
       return NULL;
    }
 
    ASSERT(ctx->Driver.MapBuffer);
-   bufObj->Pointer = ctx->Driver.MapBuffer( ctx, target, access, bufObj );
-   if (!bufObj->Pointer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(access)");
+   map = ctx->Driver.MapBuffer( ctx, target, access, bufObj );
+   if (!map) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)");
+      return NULL;
+   }
+   else {
+      /* The driver callback should have set these fields.
+       * This is important because other modules (like VBO) might call
+       * the driver function directly.
+       */
+      ASSERT(bufObj->Pointer == map);
+      ASSERT(bufObj->Length == bufObj->Size);
+      ASSERT(bufObj->Offset == 0);
+      bufObj->AccessFlags = accessFlags;
    }
-
-   bufObj->AccessFlags = accessFlags;
-   bufObj->Offset = 0;
-   bufObj->Length = bufObj->Size;
 
    if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB)
       bufObj->Written = GL_TRUE;
 
 #ifdef VBO_DEBUG
-   _mesa_printf("glMapBufferARB(%u, sz %ld, access 0x%x)\n",
-                bufObj->Name, bufObj->Size, access);
+   printf("glMapBufferARB(%u, sz %ld, access 0x%x)\n",
+         bufObj->Name, bufObj->Size, access);
    if (access == GL_WRITE_ONLY_ARB) {
       GLuint i;
       GLubyte *b = (GLubyte *) bufObj->Pointer;
@@ -1248,11 +1314,11 @@ _mesa_UnmapBufferARB(GLenum target)
       _mesa_error(ctx, GL_INVALID_ENUM, "glUnmapBufferARB(target)" );
       return GL_FALSE;
    }
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB" );
       return GL_FALSE;
    }
-   if (!bufObj->Pointer) {
+   if (!_mesa_bufferobj_mapped(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB");
       return GL_FALSE;
    }
@@ -1287,7 +1353,7 @@ _mesa_UnmapBufferARB(GLenum target)
          }
       }
       if (unchanged) {
-         _mesa_printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n",
+         printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n",
                       bufObj->Name, unchanged, bufObj->Size, pos);
       }
    }
@@ -1295,9 +1361,9 @@ _mesa_UnmapBufferARB(GLenum target)
 
    status = ctx->Driver.UnmapBuffer( ctx, target, bufObj );
    bufObj->AccessFlags = DEFAULT_ACCESS;
-   bufObj->Pointer = NULL;
-   bufObj->Offset = 0;
-   bufObj->Length = 0;
+   ASSERT(bufObj->Pointer == NULL);
+   ASSERT(bufObj->Offset == 0);
+   ASSERT(bufObj->Length == 0);
 
    return status;
 }
@@ -1312,31 +1378,109 @@ _mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
 
    bufObj = get_buffer(ctx, target);
    if (!bufObj) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "GetBufferParameterivARB(target)" );
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(target)" );
       return;
    }
-   if (bufObj->Name == 0) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "GetBufferParameterivARB" );
+   if (!_mesa_is_bufferobj(bufObj)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameterivARB" );
       return;
    }
 
    switch (pname) {
-      case GL_BUFFER_SIZE_ARB:
-         *params = (GLint) bufObj->Size;
-         break;
-      case GL_BUFFER_USAGE_ARB:
-         *params = bufObj->Usage;
-         break;
-      case GL_BUFFER_ACCESS_ARB:
-         *params = simplified_access_mode(bufObj->AccessFlags);
-         break;
-      case GL_BUFFER_MAPPED_ARB:
-         *params = (bufObj->Pointer != NULL);
-         break;
-      default:
-         _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname)");
-         return;
+   case GL_BUFFER_SIZE_ARB:
+      *params = (GLint) bufObj->Size;
+      return;
+   case GL_BUFFER_USAGE_ARB:
+      *params = bufObj->Usage;
+      return;
+   case GL_BUFFER_ACCESS_ARB:
+      *params = simplified_access_mode(bufObj->AccessFlags);
+      return;
+   case GL_BUFFER_MAPPED_ARB:
+      *params = _mesa_bufferobj_mapped(bufObj);
+      return;
+   case GL_BUFFER_ACCESS_FLAGS:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = bufObj->AccessFlags;
+      return;
+   case GL_BUFFER_MAP_OFFSET:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = (GLint) bufObj->Offset;
+      return;
+   case GL_BUFFER_MAP_LENGTH:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = (GLint) bufObj->Length;
+      return;
+   default:
+      ; /* fall-through */
+   }
+
+invalid_pname:
+   _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname=%s)",
+               _mesa_lookup_enum_by_nr(pname));
+}
+
+
+/**
+ * New in GL 3.2
+ * This is pretty much a duplicate of GetBufferParameteriv() but the
+ * GL_BUFFER_SIZE_ARB attribute will be 64-bits on a 64-bit system.
+ */
+void GLAPIENTRY
+_mesa_GetBufferParameteri64v(GLenum target, GLenum pname, GLint64 *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_buffer_object *bufObj;
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   bufObj = get_buffer(ctx, target);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(target)" );
+      return;
+   }
+   if (!_mesa_is_bufferobj(bufObj)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameteri64v" );
+      return;
+   }
+
+   switch (pname) {
+   case GL_BUFFER_SIZE_ARB:
+      *params = bufObj->Size;
+      return;
+   case GL_BUFFER_USAGE_ARB:
+      *params = bufObj->Usage;
+      return;
+   case GL_BUFFER_ACCESS_ARB:
+      *params = simplified_access_mode(bufObj->AccessFlags);
+      return;
+   case GL_BUFFER_ACCESS_FLAGS:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = bufObj->AccessFlags;
+      return;
+   case GL_BUFFER_MAPPED_ARB:
+      *params = _mesa_bufferobj_mapped(bufObj);
+      return;
+   case GL_BUFFER_MAP_OFFSET:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = bufObj->Offset;
+      return;
+   case GL_BUFFER_MAP_LENGTH:
+      if (ctx->VersionMajor < 3)
+         goto invalid_pname;
+      *params = bufObj->Length;
+      return;
+   default:
+      ; /* fall-through */
    }
+
+invalid_pname:
+   _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(pname=%s)",
+               _mesa_lookup_enum_by_nr(pname));
 }
 
 
@@ -1357,7 +1501,7 @@ _mesa_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
       _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(target)" );
       return;
    }
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferPointervARB" );
       return;
    }
@@ -1376,26 +1520,26 @@ _mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
    ASSERT_OUTSIDE_BEGIN_END(ctx);
 
    src = get_buffer(ctx, readTarget);
-   if (!src || src->Name == 0) {
+   if (!src || !_mesa_is_bufferobj(src)) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glCopyBuffserSubData(readTarget = 0x%x)", readTarget);
       return;
    }
 
    dst = get_buffer(ctx, writeTarget);
-   if (!dst || dst->Name == 0) {
+   if (!dst || !_mesa_is_bufferobj(dst)) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glCopyBuffserSubData(writeTarget = 0x%x)", writeTarget);
       return;
    }
 
-   if (src->Pointer) {
+   if (_mesa_bufferobj_mapped(src)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glCopyBuffserSubData(readBuffer is mapped)");
       return;
    }
 
-   if (dst->Pointer) {
+   if (_mesa_bufferobj_mapped(dst)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glCopyBuffserSubData(writeBuffer is mapped)");
       return;
@@ -1403,27 +1547,27 @@ _mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
 
    if (readOffset < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glCopyBuffserSubData(readOffset = %d)", readOffset);
+                  "glCopyBuffserSubData(readOffset = %d)", (int) readOffset);
       return;
    }
 
    if (writeOffset < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glCopyBuffserSubData(writeOffset = %d)", writeOffset);
+                  "glCopyBuffserSubData(writeOffset = %d)", (int) writeOffset);
       return;
    }
 
    if (readOffset + size > src->Size) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glCopyBuffserSubData(readOffset + size = %d)",
-                  readOffset, size);
+                  (int) (readOffset + size));
       return;
    }
 
    if (writeOffset + size > dst->Size) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glCopyBuffserSubData(writeOffset + size = %d)",
-                  writeOffset, size);
+                  (int) (writeOffset + size));
       return;
    }
 
@@ -1455,6 +1599,8 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
 {
    GET_CURRENT_CONTEXT(ctx);
    struct gl_buffer_object *bufObj;
+   void *map;
+
    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL);
 
    if (!ctx->Extensions.ARB_map_buffer_range) {
@@ -1465,13 +1611,13 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
 
    if (offset < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glMapBufferRange(offset = %ld)", offset);
+                  "glMapBufferRange(offset = %ld)", (long)offset);
       return NULL;
    }
 
    if (length < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glMapBufferRange(length = %ld)", length);
+                  "glMapBufferRange(length = %ld)", (long)length);
       return NULL;
    }
 
@@ -1499,7 +1645,7 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
    }
 
    bufObj = get_buffer(ctx, target);
-   if (!bufObj || bufObj->Name == 0) {
+   if (!bufObj || !_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glMapBufferRange(target = 0x%x)", target);
       return NULL;
@@ -1511,21 +1657,30 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
       return NULL;
    }
 
-   if (bufObj->Pointer) {
+   if (_mesa_bufferobj_mapped(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glMapBufferRange(buffer already mapped)");
       return NULL;
    }
       
    ASSERT(ctx->Driver.MapBufferRange);
-   bufObj->Pointer = ctx->Driver.MapBufferRange(ctx, target, offset, length,
-                                                access, bufObj);
-
-   bufObj->Offset = offset;
-   bufObj->Length = length;
-   bufObj->AccessFlags = access;
+   map = ctx->Driver.MapBufferRange(ctx, target, offset, length,
+                                    access, bufObj);
+   if (!map) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)");
+   }
+   else {
+      /* The driver callback should have set all these fields.
+       * This is important because other modules (like VBO) might call
+       * the driver function directly.
+       */
+      ASSERT(bufObj->Pointer == map);
+      ASSERT(bufObj->Length == length);
+      ASSERT(bufObj->Offset == offset);
+      ASSERT(bufObj->AccessFlags == access);
+   }
 
-   return bufObj->Pointer;
+   return map;
 }
 
 
@@ -1547,13 +1702,13 @@ _mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
 
    if (offset < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glMapBufferRange(offset = %ld)", offset);
+                  "glMapBufferRange(offset = %ld)", (long)offset);
       return;
    }
 
    if (length < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glMapBufferRange(length = %ld)", length);
+                  "glMapBufferRange(length = %ld)", (long)length);
       return;
    }
 
@@ -1564,13 +1719,13 @@ _mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
       return;
    }
 
-   if (bufObj->Name == 0) {
+   if (!_mesa_is_bufferobj(bufObj)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glMapBufferRange(current buffer is 0)");
       return;
    }
 
-   if (!bufObj->Pointer) {
+   if (!_mesa_bufferobj_mapped(bufObj)) {
       /* buffer is not mapped */
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glMapBufferRange(buffer is not mapped)");
@@ -1585,8 +1740,8 @@ _mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
 
    if (offset + length > bufObj->Length) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-             "glMapBufferRange(offset %ld + length %ld > mapped length %ld)",
-             offset, length, bufObj->Length);
+                 "glMapBufferRange(offset %ld + length %ld > mapped length %ld)",
+                 (long)offset, (long)length, (long)bufObj->Length);
       return;
    }
 
@@ -1595,3 +1750,387 @@ _mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
    if (ctx->Driver.FlushMappedBufferRange)
       ctx->Driver.FlushMappedBufferRange(ctx, target, offset, length, bufObj);
 }
+
+
+#if FEATURE_APPLE_object_purgeable
+static GLenum
+_mesa_BufferObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_buffer_object *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_bufferobj(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectPurgeable(name = 0x%x)", name);
+      return 0;
+   }
+   if (!_mesa_is_bufferobj(bufObj)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glObjectPurgeable(buffer 0)" );
+      return 0;
+   }
+
+   if (bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
+      return GL_VOLATILE_APPLE;
+   }
+
+   bufObj->Purgeable = GL_TRUE;
+
+   retval = GL_VOLATILE_APPLE;
+   if (ctx->Driver.BufferObjectPurgeable)
+      retval = ctx->Driver.BufferObjectPurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+static GLenum
+_mesa_RenderObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_renderbuffer *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_renderbuffer(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   if (bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
+      return GL_VOLATILE_APPLE;
+   }
+
+   bufObj->Purgeable = GL_TRUE;
+
+   retval = GL_VOLATILE_APPLE;
+   if (ctx->Driver.RenderObjectPurgeable)
+      retval = ctx->Driver.RenderObjectPurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+static GLenum
+_mesa_TextureObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_texture_object *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_texture(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectPurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   if (bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectPurgeable(name = 0x%x) is already purgeable", name);
+      return GL_VOLATILE_APPLE;
+   }
+
+   bufObj->Purgeable = GL_TRUE;
+
+   retval = GL_VOLATILE_APPLE;
+   if (ctx->Driver.TextureObjectPurgeable)
+      retval = ctx->Driver.TextureObjectPurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+GLenum GLAPIENTRY
+_mesa_ObjectPurgeableAPPLE(GLenum objectType, GLuint name, GLenum option)
+{
+   GLenum retval;
+
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
+
+   if (name == 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectPurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   switch (option) {
+   case GL_VOLATILE_APPLE:
+   case GL_RELEASED_APPLE:
+      /* legal */
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glObjectPurgeable(name = 0x%x) invalid option: %d",
+                  name, option);
+      return 0;
+   }
+
+   switch (objectType) {
+   case GL_TEXTURE:
+      retval = _mesa_TextureObjectPurgeable (ctx, name, option);
+      break;
+   case GL_RENDERBUFFER_EXT:
+      retval = _mesa_RenderObjectPurgeable (ctx, name, option);
+      break;
+   case GL_BUFFER_OBJECT_APPLE:
+      retval = _mesa_BufferObjectPurgeable (ctx, name, option);
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glObjectPurgeable(name = 0x%x) invalid type: %d",
+                  name, objectType);
+      return 0;
+   }
+
+   /* In strict conformance to the spec, we must only return VOLATILE when
+    * when passed the VOLATILE option. Madness.
+    *
+    * XXX First fix the spec, then fix me.
+    */
+   return option == GL_VOLATILE_APPLE ? GL_VOLATILE_APPLE : retval;
+}
+
+
+static GLenum
+_mesa_BufferObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_buffer_object *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_bufferobj(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   if (! bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectUnpurgeable(name = 0x%x) object is "
+                  " already \"unpurged\"", name);
+      return 0;
+   }
+
+   bufObj->Purgeable = GL_FALSE;
+
+   retval = option;
+   if (ctx->Driver.BufferObjectUnpurgeable)
+      retval = ctx->Driver.BufferObjectUnpurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+static GLenum
+_mesa_RenderObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_renderbuffer *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_renderbuffer(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   if (! bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectUnpurgeable(name = 0x%x) object is "
+                  " already \"unpurged\"", name);
+      return 0;
+   }
+
+   bufObj->Purgeable = GL_FALSE;
+
+   retval = option;
+   if (ctx->Driver.RenderObjectUnpurgeable)
+      retval = ctx->Driver.RenderObjectUnpurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+static GLenum
+_mesa_TextureObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option)
+{
+   struct gl_texture_object *bufObj;
+   GLenum retval;
+
+   bufObj = _mesa_lookup_texture(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   if (! bufObj->Purgeable) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glObjectUnpurgeable(name = 0x%x) object is"
+                  " already \"unpurged\"", name);
+      return 0;
+   }
+
+   bufObj->Purgeable = GL_FALSE;
+
+   retval = option;
+   if (ctx->Driver.TextureObjectUnpurgeable)
+      retval = ctx->Driver.TextureObjectUnpurgeable(ctx, bufObj, option);
+
+   return retval;
+}
+
+
+GLenum GLAPIENTRY
+_mesa_ObjectUnpurgeableAPPLE(GLenum objectType, GLuint name, GLenum option)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
+
+   if (name == 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return 0;
+   }
+
+   switch (option) {
+   case GL_RETAINED_APPLE:
+   case GL_UNDEFINED_APPLE:
+      /* legal */
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glObjectUnpurgeable(name = 0x%x) invalid option: %d",
+                  name, option);
+      return 0;
+   }
+
+   switch (objectType) {
+   case GL_BUFFER_OBJECT_APPLE:
+      return _mesa_BufferObjectUnpurgeable(ctx, name, option);
+   case GL_TEXTURE:
+      return _mesa_TextureObjectUnpurgeable(ctx, name, option);
+   case GL_RENDERBUFFER_EXT:
+      return _mesa_RenderObjectUnpurgeable(ctx, name, option);
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glObjectUnpurgeable(name = 0x%x) invalid type: %d",
+                  name, objectType);
+      return 0;
+   }
+}
+
+
+static void
+_mesa_GetBufferObjectParameterivAPPLE(struct gl_context *ctx, GLuint name,
+                                      GLenum pname, GLint* params)
+{
+   struct gl_buffer_object *bufObj;
+
+   bufObj = _mesa_lookup_bufferobj(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glGetObjectParameteriv(name = 0x%x) invalid object", name);
+      return;
+   }
+
+   switch (pname) {
+   case GL_PURGEABLE_APPLE:
+      *params = bufObj->Purgeable;
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
+                  name, pname);
+      break;
+   }
+}
+
+
+static void
+_mesa_GetRenderObjectParameterivAPPLE(struct gl_context *ctx, GLuint name,
+                                      GLenum pname, GLint* params)
+{
+   struct gl_renderbuffer *bufObj;
+
+   bufObj = _mesa_lookup_renderbuffer(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return;
+   }
+
+   switch (pname) {
+   case GL_PURGEABLE_APPLE:
+      *params = bufObj->Purgeable;
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
+                  name, pname);
+      break;
+   }
+}
+
+
+static void
+_mesa_GetTextureObjectParameterivAPPLE(struct gl_context *ctx, GLuint name,
+                                       GLenum pname, GLint* params)
+{
+   struct gl_texture_object *bufObj;
+
+   bufObj = _mesa_lookup_texture(ctx, name);
+   if (!bufObj) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glObjectUnpurgeable(name = 0x%x)", name);
+      return;
+   }
+
+   switch (pname) {
+   case GL_PURGEABLE_APPLE:
+      *params = bufObj->Purgeable;
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glGetObjectParameteriv(name = 0x%x) invalid enum: %d",
+                  name, pname);
+      break;
+   }
+}
+
+
+void GLAPIENTRY
+_mesa_GetObjectParameterivAPPLE(GLenum objectType, GLuint name, GLenum pname,
+                                GLint* params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (name == 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glGetObjectParameteriv(name = 0x%x)", name);
+      return;
+   }
+
+   switch (objectType) {
+   case GL_TEXTURE:
+      _mesa_GetTextureObjectParameterivAPPLE (ctx, name, pname, params);
+      break;
+   case GL_BUFFER_OBJECT_APPLE:
+      _mesa_GetBufferObjectParameterivAPPLE (ctx, name, pname, params);
+      break;
+   case GL_RENDERBUFFER_EXT:
+      _mesa_GetRenderObjectParameterivAPPLE (ctx, name, pname, params);
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "glGetObjectParameteriv(name = 0x%x) invalid type: %d",
+                  name, objectType);
+   }
+}
+
+#endif /* FEATURE_APPLE_object_purgeable */