summaryrefslogtreecommitdiff
path: root/src/mesa/main/bufferobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/main/bufferobj.c')
-rw-r--r--src/mesa/main/bufferobj.c839
1 files changed, 691 insertions, 148 deletions
diff --git a/src/mesa/main/bufferobj.c b/src/mesa/main/bufferobj.c
index 59fe8e25de..b95e00af5b 100644
--- a/src/mesa/main/bufferobj.c
+++ b/src/mesa/main/bufferobj.c
@@ -1,8 +1,9 @@
/*
* Mesa 3-D graphics library
- * Version: 7.2
+ * Version: 7.6
*
* Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
+ * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -25,7 +26,7 @@
/**
* \file bufferobj.c
- * \brief Functions for the GL_ARB_vertex_buffer_object extension.
+ * \brief Functions for the GL_ARB_vertex/pixel_buffer_object extensions.
* \author Brian Paul, Ian Romanick
*/
@@ -38,6 +39,18 @@
#include "bufferobj.h"
+/* Debug flags */
+/*#define VBO_DEBUG*/
+/*#define BOUNDS_CHECK*/
+
+
+#ifdef FEATURE_OES_mapbuffer
+#define DEFAULT_ACCESS GL_MAP_WRITE_BIT
+#else
+#define DEFAULT_ACCESS (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)
+#endif
+
+
/**
* Get the buffer object bound to the specified target in a GL context.
*
@@ -65,6 +78,16 @@ get_buffer(GLcontext *ctx, GLenum target)
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;
@@ -78,6 +101,24 @@ get_buffer(GLcontext *ctx, GLenum target)
/**
+ * Convert a GLbitfield describing the mapped buffer access flags
+ * into one of GL_READ_WRITE, GL_READ_ONLY, or GL_WRITE_ONLY.
+ */
+static GLenum
+simplified_access_mode(GLbitfield access)
+{
+ const GLbitfield rwFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
+ if ((access & rwFlags) == rwFlags)
+ return GL_READ_WRITE;
+ if ((access & GL_MAP_READ_BIT) == GL_MAP_READ_BIT)
+ return GL_READ_ONLY;
+ if ((access & GL_MAP_WRITE_BIT) == GL_MAP_WRITE_BIT)
+ return GL_WRITE_ONLY;
+ return GL_READ_WRITE; /* this should never happen, but no big deal */
+}
+
+
+/**
* Tests the subdata range parameters and sets the GL error code for
* \c glBufferSubDataARB and \c glGetBufferSubDataARB.
*
@@ -115,7 +156,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;
}
@@ -124,7 +165,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;
@@ -137,10 +178,9 @@ buffer_object_subdata_range_good( GLcontext * ctx, GLenum target,
/**
* Allocate and initialize a new buffer object.
*
- * This function is intended to be called via
- * \c dd_function_table::NewBufferObject.
+ * Default callback for the \c dd_function_table::NewBufferObject() hook.
*/
-struct gl_buffer_object *
+static struct gl_buffer_object *
_mesa_new_buffer_object( GLcontext *ctx, GLuint name, GLenum target )
{
struct gl_buffer_object *obj;
@@ -156,10 +196,9 @@ _mesa_new_buffer_object( GLcontext *ctx, GLuint name, GLenum target )
/**
* Delete a buffer object.
*
- * This function is intended to be called via
- * \c dd_function_table::DeleteBuffer.
+ * Default callback for the \c dd_function_table::DeleteBuffer() hook.
*/
-void
+static void
_mesa_delete_buffer_object( GLcontext *ctx, struct gl_buffer_object *bufObj )
{
(void) ctx;
@@ -188,7 +227,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
return;
if (*ptr) {
- /* Unreference the old texture */
+ /* Unreference the old buffer */
GLboolean deleteFlag = GL_FALSE;
struct gl_buffer_object *oldObj = *ptr;
@@ -221,7 +260,7 @@ _mesa_reference_buffer_object(GLcontext *ctx,
ASSERT(!*ptr);
if (bufObj) {
- /* reference new texture */
+ /* reference new buffer */
/*_glthread_LOCK_MUTEX(tex->Mutex);*/
if (bufObj->RefCount == 0) {
/* this buffer's being deleted (look just above) */
@@ -255,7 +294,7 @@ _mesa_initialize_buffer_object( struct gl_buffer_object *obj,
obj->RefCount = 1;
obj->Name = name;
obj->Usage = GL_STATIC_DRAW_ARB;
- obj->Access = GL_READ_WRITE_ARB;
+ obj->AccessFlags = DEFAULT_ACCESS;
}
@@ -264,9 +303,8 @@ _mesa_initialize_buffer_object( struct gl_buffer_object *obj,
* previously stored in the buffer object is lost. If \c data is \c NULL,
* memory will be allocated, but no copy will occur.
*
- * This function is intended to be called via
- * \c dd_function_table::BufferData. This function need not set GL error
- * codes. The input parameters will have been tested before calling.
+ * This is the default callback for \c dd_function_table::BufferData()
+ * Note that all GL error checking will have been done already.
*
* \param ctx GL context.
* \param target Buffer object target on which to operate.
@@ -276,9 +314,10 @@ _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.
*/
-void
+static GLboolean
_mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
const GLvoid * data, GLenum usage,
struct gl_buffer_object * bufObj )
@@ -296,6 +335,11 @@ _mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
if (data) {
_mesa_memcpy( bufObj->Data, data, size );
}
+
+ return GL_TRUE;
+ }
+ else {
+ return GL_FALSE;
}
}
@@ -305,9 +349,8 @@ _mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
* specified by \c size + \c offset extends beyond the end of the buffer or
* if \c data is \c NULL, no copy is performed.
*
- * This function is intended to be called by
- * \c dd_function_table::BufferSubData. This function need not set GL error
- * codes. The input parameters will have been tested before calling.
+ * This is the default callback for \c dd_function_table::BufferSubData()
+ * Note that all GL error checking will have been done already.
*
* \param ctx GL context.
* \param target Buffer object target on which to operate.
@@ -318,7 +361,7 @@ _mesa_buffer_data( GLcontext *ctx, GLenum target, GLsizeiptrARB size,
*
* \sa glBufferSubDataARB, dd_function_table::BufferSubData.
*/
-void
+static void
_mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
GLsizeiptrARB size, const GLvoid * data,
struct gl_buffer_object * bufObj )
@@ -339,20 +382,19 @@ _mesa_buffer_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
* specified by \c size + \c offset extends beyond the end of the buffer or
* if \c data is \c NULL, no copy is performed.
*
- * This function is intended to be called by
- * \c dd_function_table::BufferGetSubData. This function need not set GL error
- * codes. The input parameters will have been tested before calling.
+ * This is the default callback for \c dd_function_table::GetBufferSubData()
+ * Note that all GL error checking will have been done already.
*
* \param ctx GL context.
* \param target Buffer object target on which to operate.
- * \param offset Offset of the first byte to be modified.
+ * \param offset Offset of the first byte to be fetched.
* \param size Size, in bytes, of the data range.
- * \param data Pointer to the data to store in the buffer object.
+ * \param data Destination for data
* \param bufObj Object to be used.
*
* \sa glBufferGetSubDataARB, dd_function_table::GetBufferSubData.
*/
-void
+static void
_mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
GLsizeiptrARB size, GLvoid * data,
struct gl_buffer_object * bufObj )
@@ -366,9 +408,7 @@ _mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
/**
- * Fallback function called via ctx->Driver.MapBuffer().
- * Hardware drivers that really implement buffer objects should never use
- * this function.
+ * Default callback for \c dd_function_tabel::MapBuffer().
*
* The function parameters will have been already tested for errors.
*
@@ -381,64 +421,135 @@ _mesa_buffer_get_subdata( GLcontext *ctx, GLenum target, GLintptrARB offset,
*
* \sa glMapBufferARB, dd_function_table::MapBuffer
*/
-void *
+static void *
_mesa_buffer_map( GLcontext *ctx, GLenum target, GLenum access,
struct gl_buffer_object *bufObj )
{
(void) ctx;
(void) target;
(void) access;
- ASSERT(!bufObj->OnCard);
/* 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;
}
/**
- * Fallback function called via ctx->Driver.MapBuffer().
- * Hardware drivers that really implement buffer objects should never use
- * function.
+ * Default fallback for \c dd_function_table::MapBufferRange().
+ * Called via glMapBufferRange().
+ */
+static void *
+_mesa_buffer_map_range( GLcontext *ctx, GLenum target, GLintptr offset,
+ GLsizeiptr length, GLbitfield access,
+ struct gl_buffer_object *bufObj )
+{
+ (void) ctx;
+ (void) target;
+ assert(!_mesa_bufferobj_mapped(bufObj));
+ /* Just return a direct pointer to the data */
+ bufObj->Pointer = bufObj->Data + offset;
+ bufObj->Length = length;
+ bufObj->Offset = offset;
+ bufObj->AccessFlags = access;
+ return bufObj->Pointer;
+}
+
+
+/**
+ * Default fallback for \c dd_function_table::FlushMappedBufferRange().
+ * Called via glFlushMappedBufferRange().
+ */
+static void
+_mesa_buffer_flush_mapped_range( GLcontext *ctx, GLenum target,
+ GLintptr offset, GLsizeiptr length,
+ struct gl_buffer_object *obj )
+{
+ (void) ctx;
+ (void) target;
+ (void) offset;
+ (void) length;
+ (void) obj;
+ /* no-op */
+}
+
+
+/**
+ * Default callback for \c dd_function_table::MapBuffer().
*
* The input parameters will have been already tested for errors.
*
* \sa glUnmapBufferARB, dd_function_table::UnmapBuffer
*/
-GLboolean
+static GLboolean
_mesa_buffer_unmap( GLcontext *ctx, GLenum target,
struct gl_buffer_object *bufObj )
{
(void) ctx;
(void) target;
- ASSERT(!bufObj->OnCard);
/* 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;
}
/**
+ * Default fallback for \c dd_function_table::CopyBufferSubData().
+ * Called via glCopyBuffserSubData().
+ */
+static void
+_mesa_copy_buffer_subdata(GLcontext *ctx,
+ struct gl_buffer_object *src,
+ struct gl_buffer_object *dst,
+ GLintptr readOffset, GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ GLubyte *srcPtr, *dstPtr;
+
+ /* buffer should not already be mapped */
+ assert(!_mesa_bufferobj_mapped(src));
+ assert(!_mesa_bufferobj_mapped(dst));
+
+ srcPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_READ_BUFFER,
+ GL_READ_ONLY, src);
+ dstPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_WRITE_BUFFER,
+ GL_WRITE_ONLY, dst);
+
+ if (srcPtr && dstPtr)
+ _mesa_memcpy(dstPtr + writeOffset, srcPtr + readOffset, size);
+
+ ctx->Driver.UnmapBuffer(ctx, GL_COPY_READ_BUFFER, src);
+ ctx->Driver.UnmapBuffer(ctx, GL_COPY_WRITE_BUFFER, dst);
+}
+
+
+
+/**
* Initialize the state associated with buffer objects
*/
void
_mesa_init_buffer_objects( GLcontext *ctx )
{
- /* Allocate the default buffer object and set refcount so high that
- * it never gets deleted.
- * XXX with recent/improved refcounting this may not longer be needed.
- */
- 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;
+ _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj,
+ ctx->Shared->NullBufferObj);
+ _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj,
+ ctx->Shared->NullBufferObj);
+
+ _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer,
+ ctx->Shared->NullBufferObj);
+ _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer,
+ ctx->Shared->NullBufferObj);
}
+
/**
* Bind the specified target to buffer for the specified context.
*/
@@ -462,8 +573,22 @@ bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
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:
- _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target)");
+ ; /* no-op / we'll hit the follow error test next */
+ }
+
+ if (!bindTarget) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)");
return;
}
@@ -479,7 +604,7 @@ bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
/* The spec says there's not a buffer object named 0, but we use
* one internally because it simplifies things.
*/
- newBufObj = ctx->Array.NullBufferObj;
+ newBufObj = ctx->Shared->NullBufferObj;
}
else {
/* non-default buffer object */
@@ -500,7 +625,7 @@ bind_buffer_object(GLcontext *ctx, GLenum target, GLuint buffer)
_mesa_reference_buffer_object(ctx, bindTarget, newBufObj);
/* Pass BindBuffer call to device driver */
- if (ctx->Driver.BindBuffer && newBufObj)
+ if (ctx->Driver.BindBuffer)
ctx->Driver.BindBuffer( ctx, target, newBufObj );
}
@@ -533,6 +658,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
@@ -551,7 +678,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! */
@@ -583,21 +711,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(GLcontext *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,
@@ -605,11 +734,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;
@@ -617,57 +746,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(GLcontext *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_drapix_pbo(GLcontext *ctx,
+_mesa_unmap_pbo_source(GLcontext *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);
}
@@ -675,18 +802,21 @@ _mesa_unmap_drapix_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(GLcontext *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,
@@ -706,13 +836,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(GLcontext *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(GLcontext *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);
}
}
@@ -746,11 +918,37 @@ unbind(GLcontext *ctx,
struct gl_buffer_object *obj)
{
if (*ptr == obj) {
- _mesa_reference_buffer_object(ctx, ptr, ctx->Array.NullBufferObj);
+ _mesa_reference_buffer_object(ctx, ptr, ctx->Shared->NullBufferObj);
}
}
+/**
+ * Plug default/fallback buffer object functions into the device
+ * driver hooks.
+ */
+void
+_mesa_init_buffer_object_functions(struct dd_function_table *driver)
+{
+ /* GL_ARB_vertex/pixel_buffer_object */
+ driver->NewBufferObject = _mesa_new_buffer_object;
+ driver->DeleteBuffer = _mesa_delete_buffer_object;
+ driver->BindBuffer = NULL;
+ driver->BufferData = _mesa_buffer_data;
+ driver->BufferSubData = _mesa_buffer_subdata;
+ driver->GetBufferSubData = _mesa_buffer_get_subdata;
+ driver->MapBuffer = _mesa_buffer_map;
+ driver->UnmapBuffer = _mesa_buffer_unmap;
+
+ /* GL_ARB_map_buffer_range */
+ driver->MapBufferRange = _mesa_buffer_map_range;
+ driver->FlushMappedBufferRange = _mesa_buffer_flush_mapped_range;
+
+ /* GL_ARB_copy_buffer */
+ driver->CopyBufferSubData = _mesa_copy_buffer_subdata;
+}
+
+
/**********************************************************************/
/* API Functions */
@@ -789,23 +987,32 @@ _mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
for (i = 0; i < n; i++) {
struct gl_buffer_object *bufObj = _mesa_lookup_bufferobj(ctx, ids[i]);
if (bufObj) {
- /* unbind any vertex pointers bound to this buffer */
+ struct gl_array_object *arrayObj = ctx->Array.ArrayObj;
GLuint j;
ASSERT(bufObj->Name == ids[i]);
- unbind(ctx, &ctx->Array.ArrayObj->Vertex.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->Normal.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->Color.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->SecondaryColor.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->FogCoord.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->Index.BufferObj, bufObj);
- unbind(ctx, &ctx->Array.ArrayObj->EdgeFlag.BufferObj, bufObj);
- for (j = 0; j < MAX_TEXTURE_COORD_UNITS; j++) {
- unbind(ctx, &ctx->Array.ArrayObj->TexCoord[j].BufferObj, bufObj);
+ if (_mesa_bufferobj_mapped(bufObj)) {
+ /* if mapped, unmap it now */
+ ctx->Driver.UnmapBuffer(ctx, 0, bufObj);
+ bufObj->AccessFlags = DEFAULT_ACCESS;
+ bufObj->Pointer = NULL;
}
- for (j = 0; j < VERT_ATTRIB_MAX; j++) {
- unbind(ctx, &ctx->Array.ArrayObj->VertexAttrib[j].BufferObj, bufObj);
+
+ /* unbind any vertex pointers bound to this buffer */
+ unbind(ctx, &arrayObj->Vertex.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->Weight.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->Normal.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->Color.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->SecondaryColor.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->FogCoord.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->Index.BufferObj, bufObj);
+ unbind(ctx, &arrayObj->EdgeFlag.BufferObj, bufObj);
+ for (j = 0; j < Elements(arrayObj->TexCoord); j++) {
+ unbind(ctx, &arrayObj->TexCoord[j].BufferObj, bufObj);
+ }
+ for (j = 0; j < Elements(arrayObj->VertexAttrib); j++) {
+ unbind(ctx, &arrayObj->VertexAttrib[j].BufferObj, bufObj);
}
if (ctx->Array.ArrayBufferObj == bufObj) {
@@ -815,6 +1022,7 @@ _mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
_mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
}
+ /* unbind any pixel pack/unpack pointers bound to this buffer */
if (ctx->Pack.BufferObj == bufObj) {
_mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 );
}
@@ -938,20 +1146,35 @@ _mesa_BufferDataARB(GLenum target, GLsizeiptrARB size,
_mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(target)" );
return;
}
- if (bufObj->Name == 0) {
- _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB" );
+ if (!_mesa_is_bufferobj(bufObj)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer 0)" );
return;
}
- if (bufObj->Pointer) {
- _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer is mapped)" );
- return;
+ 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;
+ ASSERT(bufObj->Pointer == NULL);
}
- ASSERT(ctx->Driver.BufferData);
+ FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT);
+
+ bufObj->Written = GL_TRUE;
+
+#ifdef VBO_DEBUG
+ _mesa_printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n",
+ bufObj->Name, size, data, usage);
+#endif
- /* Give the buffer object to the driver! <data> may be null! */
- ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj );
+#ifdef BOUNDS_CHECK
+ size += 100;
+#endif
+
+ ASSERT(ctx->Driver.BufferData);
+ if (!ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj )) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB(access)");
+ }
}
@@ -970,6 +1193,8 @@ _mesa_BufferSubDataARB(GLenum target, GLintptrARB offset,
return;
}
+ bufObj->Written = GL_TRUE;
+
ASSERT(ctx->Driver.BufferSubData);
ctx->Driver.BufferSubData( ctx, target, offset, size, data, bufObj );
}
@@ -1000,13 +1225,20 @@ _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:
- /* OK */
+ accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
break;
default:
_mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)");
@@ -1018,22 +1250,56 @@ _mesa_MapBufferARB(GLenum target, GLenum access)
_mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(target)" );
return NULL;
}
- if (bufObj->Name == 0) {
- _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB" );
+ 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) {
+ map = ctx->Driver.MapBuffer( ctx, target, access, bufObj );
+ if (!map) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(access)");
+ 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->Access = access;
+ 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);
+ if (access == GL_WRITE_ONLY_ARB) {
+ GLuint i;
+ GLubyte *b = (GLubyte *) bufObj->Pointer;
+ for (i = 0; i < bufObj->Size; i++)
+ b[i] = i & 0xff;
+ }
+#endif
+
+#ifdef BOUNDS_CHECK
+ {
+ GLubyte *buf = (GLubyte *) bufObj->Pointer;
+ GLuint i;
+ /* buffer is 100 bytes larger than requested, fill with magic value */
+ for (i = 0; i < 100; i++) {
+ buf[bufObj->Size - i - 1] = 123;
+ }
+ }
+#endif
return bufObj->Pointer;
}
@@ -1052,21 +1318,56 @@ _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;
}
- if (ctx->Driver.UnmapBuffer) {
- status = ctx->Driver.UnmapBuffer( ctx, target, bufObj );
+#ifdef BOUNDS_CHECK
+ if (bufObj->Access != GL_READ_ONLY_ARB) {
+ GLubyte *buf = (GLubyte *) bufObj->Pointer;
+ GLuint i;
+ /* check that last 100 bytes are still = magic value */
+ for (i = 0; i < 100; i++) {
+ GLuint pos = bufObj->Size - i - 1;
+ if (buf[pos] != 123) {
+ _mesa_warning(ctx, "Out of bounds buffer object write detected"
+ " at position %d (value = %u)\n",
+ pos, buf[pos]);
+ }
+ }
}
+#endif
- bufObj->Access = GL_READ_WRITE_ARB; /* initial value, OK? */
- bufObj->Pointer = NULL;
+#ifdef VBO_DEBUG
+ if (bufObj->AccessFlags & GL_MAP_WRITE_BIT) {
+ GLuint i, unchanged = 0;
+ GLubyte *b = (GLubyte *) bufObj->Pointer;
+ GLint pos = -1;
+ /* check which bytes changed */
+ for (i = 0; i < bufObj->Size - 1; i++) {
+ if (b[i] == (i & 0xff) && b[i+1] == ((i+1) & 0xff)) {
+ unchanged++;
+ if (pos == -1)
+ pos = i;
+ }
+ }
+ if (unchanged) {
+ _mesa_printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n",
+ bufObj->Name, unchanged, bufObj->Size, pos);
+ }
+ }
+#endif
+
+ status = ctx->Driver.UnmapBuffer( ctx, target, bufObj );
+ bufObj->AccessFlags = DEFAULT_ACCESS;
+ ASSERT(bufObj->Pointer == NULL);
+ ASSERT(bufObj->Offset == 0);
+ ASSERT(bufObj->Length == 0);
return status;
}
@@ -1084,7 +1385,7 @@ _mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
_mesa_error(ctx, GL_INVALID_ENUM, "GetBufferParameterivARB(target)" );
return;
}
- if (bufObj->Name == 0) {
+ if (!_mesa_is_bufferobj(bufObj)) {
_mesa_error(ctx, GL_INVALID_OPERATION, "GetBufferParameterivARB" );
return;
}
@@ -1097,10 +1398,10 @@ _mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
*params = bufObj->Usage;
break;
case GL_BUFFER_ACCESS_ARB:
- *params = bufObj->Access;
+ *params = simplified_access_mode(bufObj->AccessFlags);
break;
case GL_BUFFER_MAPPED_ARB:
- *params = (bufObj->Pointer != NULL);
+ *params = _mesa_bufferobj_mapped(bufObj);
break;
default:
_mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname)");
@@ -1126,10 +1427,252 @@ _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;
}
*params = bufObj->Pointer;
}
+
+
+void GLAPIENTRY
+_mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
+ GLintptr readOffset, GLintptr writeOffset,
+ GLsizeiptr size)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *src, *dst;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ src = get_buffer(ctx, readTarget);
+ 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 || !_mesa_is_bufferobj(dst)) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glCopyBuffserSubData(writeTarget = 0x%x)", writeTarget);
+ return;
+ }
+
+ if (_mesa_bufferobj_mapped(src)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glCopyBuffserSubData(readBuffer is mapped)");
+ return;
+ }
+
+ if (_mesa_bufferobj_mapped(dst)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glCopyBuffserSubData(writeBuffer is mapped)");
+ return;
+ }
+
+ if (readOffset < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBuffserSubData(readOffset = %d)", readOffset);
+ return;
+ }
+
+ if (writeOffset < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBuffserSubData(writeOffset = %d)", writeOffset);
+ return;
+ }
+
+ if (readOffset + size > src->Size) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBuffserSubData(readOffset + size = %d)",
+ readOffset, size);
+ return;
+ }
+
+ if (writeOffset + size > dst->Size) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBuffserSubData(writeOffset + size = %d)",
+ writeOffset, size);
+ return;
+ }
+
+ if (src == dst) {
+ if (readOffset + size <= writeOffset) {
+ /* OK */
+ }
+ else if (writeOffset + size <= readOffset) {
+ /* OK */
+ }
+ else {
+ /* overlapping src/dst is illegal */
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBuffserSubData(overlapping src/dst)");
+ return;
+ }
+ }
+
+ ctx->Driver.CopyBufferSubData(ctx, src, dst, readOffset, writeOffset, size);
+}
+
+
+/**
+ * See GL_ARB_map_buffer_range spec
+ */
+void * GLAPIENTRY
+_mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
+ GLbitfield access)
+{
+ 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) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(extension not supported)");
+ return NULL;
+ }
+
+ if (offset < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(offset = %ld)", offset);
+ return NULL;
+ }
+
+ if (length < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(length = %ld)", length);
+ return NULL;
+ }
+
+ if ((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(access indicates neither read or write)");
+ return NULL;
+ }
+
+ if (access & GL_MAP_READ_BIT) {
+ if ((access & GL_MAP_INVALIDATE_RANGE_BIT) ||
+ (access & GL_MAP_INVALIDATE_BUFFER_BIT) ||
+ (access & GL_MAP_UNSYNCHRONIZED_BIT)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(invalid access flags)");
+ return NULL;
+ }
+ }
+
+ if ((access & GL_MAP_FLUSH_EXPLICIT_BIT) &&
+ ((access & GL_MAP_WRITE_BIT) == 0)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(invalid access flags)");
+ return NULL;
+ }
+
+ bufObj = get_buffer(ctx, target);
+ if (!bufObj || !_mesa_is_bufferobj(bufObj)) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glMapBufferRange(target = 0x%x)", target);
+ return NULL;
+ }
+
+ if (offset + length > bufObj->Size) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(offset + length > size)");
+ return NULL;
+ }
+
+ if (_mesa_bufferobj_mapped(bufObj)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(buffer already mapped)");
+ return NULL;
+ }
+
+ ASSERT(ctx->Driver.MapBufferRange);
+ map = ctx->Driver.MapBufferRange(ctx, target, offset, length,
+ access, bufObj);
+ if (!map) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(access)");
+ }
+ 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 map;
+}
+
+
+/**
+ * See GL_ARB_map_buffer_range spec
+ */
+void GLAPIENTRY
+_mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *bufObj;
+ ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+ if (!ctx->Extensions.ARB_map_buffer_range) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(extension not supported)");
+ return;
+ }
+
+ if (offset < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(offset = %ld)", offset);
+ return;
+ }
+
+ if (length < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(length = %ld)", length);
+ return;
+ }
+
+ bufObj = get_buffer(ctx, target);
+ if (!bufObj) {
+ _mesa_error(ctx, GL_INVALID_ENUM,
+ "glMapBufferRange(target = 0x%x)", target);
+ return;
+ }
+
+ if (!_mesa_is_bufferobj(bufObj)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(current buffer is 0)");
+ return;
+ }
+
+ if (!_mesa_bufferobj_mapped(bufObj)) {
+ /* buffer is not mapped */
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(buffer is not mapped)");
+ return;
+ }
+
+ if ((bufObj->AccessFlags & GL_MAP_FLUSH_EXPLICIT_BIT) == 0) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(GL_MAP_FLUSH_EXPLICIT_BIT not set)");
+ return;
+ }
+
+ if (offset + length > bufObj->Length) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glMapBufferRange(offset %ld + length %ld > mapped length %ld)",
+ offset, length, bufObj->Length);
+ return;
+ }
+
+ ASSERT(bufObj->AccessFlags & GL_MAP_WRITE_BIT);
+
+ if (ctx->Driver.FlushMappedBufferRange)
+ ctx->Driver.FlushMappedBufferRange(ctx, target, offset, length, bufObj);
+}