* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
* \author Brian Paul, Ian Romanick
*/
-
+#include <stdbool.h>
#include "glheader.h"
#include "enums.h"
#include "hash.h"
#include "context.h"
#include "bufferobj.h"
#include "fbobject.h"
-#include "mfeatures.h"
#include "mtypes.h"
#include "texobj.h"
+#include "transformfeedback.h"
+#include "dispatch.h"
/* Debug flags */
static inline struct gl_buffer_object **
get_buffer_target(struct gl_context *ctx, GLenum target)
{
+ /* Other targets are only supported in desktop OpenGL and OpenGL ES 3.0.
+ */
+ if (!_mesa_is_desktop_gl(ctx) && !_mesa_is_gles3(ctx)
+ && target != GL_ARRAY_BUFFER && target != GL_ELEMENT_ARRAY_BUFFER)
+ return NULL;
+
switch (target) {
case GL_ARRAY_BUFFER_ARB:
return &ctx->Array.ArrayBufferObj;
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
case GL_TEXTURE_BUFFER:
- if (ctx->Extensions.ARB_texture_buffer_object) {
+ if (ctx->API == API_OPENGL_CORE &&
+ ctx->Extensions.ARB_texture_buffer_object) {
return &ctx->Texture.BufferObject;
}
break;
+ case GL_UNIFORM_BUFFER:
+ if (ctx->Extensions.ARB_uniform_buffer_object) {
+ return &ctx->UniformBuffer;
+ }
+ break;
default:
return NULL;
}
}
-static inline GLenum
-default_access_mode(const struct gl_context *ctx)
+/**
+ * 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(struct gl_context *ctx, GLbitfield access)
{
- /* Table 2.6 on page 31 (page 44 of the PDF) of the OpenGL 1.5 spec says:
+ 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;
+
+ /* Otherwise, AccessFlags is zero (the default state).
+ *
+ * Table 2.6 on page 31 (page 44 of the PDF) of the OpenGL 1.5 spec says:
*
* Name Type Initial Value Legal Values
* ... ... ... ...
* The difference is because GL_OES_mapbuffer only supports mapping buffers
* write-only.
*/
- return (ctx->API == API_OPENGLES)
- ? GL_MAP_WRITE_BIT : (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
-}
-
+ assert(access == 0);
-/**
- * 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 */
+ return _mesa_is_gles(ctx) ? GL_WRITE_ONLY : GL_READ_WRITE;
}
{
(void) ctx;
- if (bufObj->Data)
- free(bufObj->Data);
+ free(bufObj->Data);
/* assign strange values here to help w/ debugging */
bufObj->RefCount = -1000;
obj->RefCount = 1;
obj->Name = name;
obj->Usage = GL_STATIC_DRAW_ARB;
- obj->AccessFlags = default_access_mode(ctx);
+ obj->AccessFlags = 0;
+}
+
+
+
+/**
+ * Callback called from _mesa_HashWalk()
+ */
+static void
+count_buffer_size(GLuint key, void *data, void *userData)
+{
+ const struct gl_buffer_object *bufObj =
+ (const struct gl_buffer_object *) data;
+ GLuint *total = (GLuint *) userData;
+
+ *total = *total + bufObj->Size;
+}
+
+
+/**
+ * Compute total size (in bytes) of all buffer objects for the given context.
+ * For debugging purposes.
+ */
+GLuint
+_mesa_total_buffer_object_memory(struct gl_context *ctx)
+{
+ GLuint total = 0;
+
+ _mesa_HashWalk(ctx->Shared->BufferObjects, count_buffer_size, &total);
+
+ return total;
}
/**
* Default fallback for \c dd_function_table::CopyBufferSubData().
- * Called via glCopyBuffserSubData().
+ * Called via glCopyBufferSubData().
*/
static void
_mesa_copy_buffer_subdata(struct gl_context *ctx,
void
_mesa_init_buffer_objects( struct gl_context *ctx )
{
+ GLuint i;
+
memset(&DummyBufferObject, 0, sizeof(DummyBufferObject));
_glthread_INIT_MUTEX(DummyBufferObject.Mutex);
DummyBufferObject.RefCount = 1000*1000*1000; /* never delete */
ctx->Shared->NullBufferObj);
_mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer,
ctx->Shared->NullBufferObj);
+
+ ctx->UniformBufferBindings = calloc(ctx->Const.MaxUniformBufferBindings,
+ sizeof(*ctx->UniformBufferBindings));
+
+ _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer,
+ ctx->Shared->NullBufferObj);
+
+ for (i = 0; i < ctx->Const.MaxUniformBufferBindings; i++) {
+ _mesa_reference_buffer_object(ctx,
+ &ctx->UniformBufferBindings[i].BufferObject,
+ ctx->Shared->NullBufferObj);
+ ctx->UniformBufferBindings[i].Offset = -1;
+ ctx->UniformBufferBindings[i].Size = -1;
+ }
}
void
_mesa_free_buffer_objects( struct gl_context *ctx )
{
+ GLuint i;
+
_mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, NULL);
_mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, NULL);
_mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, NULL);
+
+ _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, NULL);
+
+ for (i = 0; i < ctx->Const.MaxUniformBufferBindings; i++) {
+ _mesa_reference_buffer_object(ctx,
+ &ctx->UniformBufferBindings[i].BufferObject,
+ NULL);
+ }
+
+ free(ctx->UniformBufferBindings);
+ ctx->UniformBufferBindings = NULL;
}
+static bool
+handle_bind_buffer_gen(struct gl_context *ctx,
+ GLenum target,
+ GLuint buffer,
+ struct gl_buffer_object **buf_handle)
+{
+ struct gl_buffer_object *buf = *buf_handle;
+
+ if (!buf && ctx->API == API_OPENGL_CORE) {
+ _mesa_error(ctx, GL_INVALID_OPERATION, "glBindBuffer(non-gen name)");
+ return false;
+ }
+
+ if (!buf || buf == &DummyBufferObject) {
+ /* If this is a new buffer object id, or one which was generated but
+ * never used before, allocate a buffer object now.
+ */
+ ASSERT(ctx->Driver.NewBufferObject);
+ buf = ctx->Driver.NewBufferObject(ctx, buffer, target);
+ if (!buf) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB");
+ return false;
+ }
+ _mesa_HashInsert(ctx->Shared->BufferObjects, buffer, buf);
+ *buf_handle = buf;
+ }
+
+ return true;
+}
/**
* Bind the specified target to buffer for the specified context.
else {
/* non-default buffer object */
newBufObj = _mesa_lookup_bufferobj(ctx, buffer);
- if (!newBufObj || newBufObj == &DummyBufferObject) {
- /* If this is a new buffer object id, or one which was generated but
- * never used before, allocate a buffer object now.
- */
- ASSERT(ctx->Driver.NewBufferObject);
- newBufObj = ctx->Driver.NewBufferObject(ctx, buffer, target);
- if (!newBufObj) {
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB");
- return;
- }
- _mesa_HashInsert(ctx->Shared->BufferObjects, buffer, newBufObj);
- }
+ if (!handle_bind_buffer_gen(ctx, target, buffer, &newBufObj))
+ return;
}
/* bind new buffer */
/**********************************************************************/
void GLAPIENTRY
-_mesa_BindBufferARB(GLenum target, GLuint buffer)
+_mesa_BindBuffer(GLenum target, GLuint buffer)
{
GET_CURRENT_CONTEXT(ctx);
- ASSERT_OUTSIDE_BEGIN_END(ctx);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glBindBuffer(%s, %u)\n",
* \param ids Array of \c n buffer object IDs.
*/
void GLAPIENTRY
-_mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids)
+_mesa_DeleteBuffers(GLsizei n, const GLuint *ids)
{
GET_CURRENT_CONTEXT(ctx);
GLsizei i;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
FLUSH_VERTICES(ctx, 0);
if (n < 0) {
if (_mesa_bufferobj_mapped(bufObj)) {
/* if mapped, unmap it now */
ctx->Driver.UnmapBuffer(ctx, bufObj);
- bufObj->AccessFlags = default_access_mode(ctx);
+ bufObj->AccessFlags = 0;
bufObj->Pointer = NULL;
}
}
if (ctx->Array.ArrayBufferObj == bufObj) {
- _mesa_BindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
+ _mesa_BindBuffer( GL_ARRAY_BUFFER_ARB, 0 );
}
if (arrayObj->ElementArrayBufferObj == bufObj) {
- _mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
+ _mesa_BindBuffer( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
+ }
+
+ /* unbind ARB_copy_buffer binding points */
+ if (ctx->CopyReadBuffer == bufObj) {
+ _mesa_BindBuffer( GL_COPY_READ_BUFFER, 0 );
+ }
+ if (ctx->CopyWriteBuffer == bufObj) {
+ _mesa_BindBuffer( GL_COPY_WRITE_BUFFER, 0 );
+ }
+
+ /* unbind transform feedback binding points */
+ if (ctx->TransformFeedback.CurrentBuffer == bufObj) {
+ _mesa_BindBuffer( GL_TRANSFORM_FEEDBACK_BUFFER, 0 );
+ }
+ for (j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
+ if (ctx->TransformFeedback.CurrentObject->Buffers[j] == bufObj) {
+ _mesa_BindBufferBase( GL_TRANSFORM_FEEDBACK_BUFFER, j, 0 );
+ }
+ }
+
+ /* unbind UBO binding points */
+ for (j = 0; j < ctx->Const.MaxUniformBufferBindings; j++) {
+ if (ctx->UniformBufferBindings[j].BufferObject == bufObj) {
+ _mesa_BindBufferBase( GL_UNIFORM_BUFFER, j, 0 );
+ }
+ }
+
+ if (ctx->UniformBuffer == bufObj) {
+ _mesa_BindBuffer( GL_UNIFORM_BUFFER, 0 );
}
/* unbind any pixel pack/unpack pointers bound to this buffer */
if (ctx->Pack.BufferObj == bufObj) {
- _mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 );
+ _mesa_BindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 );
}
if (ctx->Unpack.BufferObj == bufObj) {
- _mesa_BindBufferARB( GL_PIXEL_UNPACK_BUFFER_EXT, 0 );
+ _mesa_BindBuffer( GL_PIXEL_UNPACK_BUFFER_EXT, 0 );
+ }
+
+ if (ctx->Texture.BufferObject == bufObj) {
+ _mesa_BindBuffer( GL_TEXTURE_BUFFER, 0 );
}
/* The ID is immediately freed for re-use */
* \param buffer Array of \c n locations to store the IDs.
*/
void GLAPIENTRY
-_mesa_GenBuffersARB(GLsizei n, GLuint *buffer)
+_mesa_GenBuffers(GLsizei n, GLuint *buffer)
{
GET_CURRENT_CONTEXT(ctx);
GLuint first;
GLint i;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glGenBuffers(%d)\n", n);
* \c GL_FALSE otherwise.
*/
GLboolean GLAPIENTRY
-_mesa_IsBufferARB(GLuint id)
+_mesa_IsBuffer(GLuint id)
{
struct gl_buffer_object *bufObj;
GET_CURRENT_CONTEXT(ctx);
void GLAPIENTRY
-_mesa_BufferDataARB(GLenum target, GLsizeiptrARB size,
+_mesa_BufferData(GLenum target, GLsizeiptrARB size,
const GLvoid * data, GLenum usage)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
+ bool valid_usage;
if (MESA_VERBOSE & VERBOSE_API)
_mesa_debug(ctx, "glBufferData(%s, %ld, %p, %s)\n",
switch (usage) {
case GL_STREAM_DRAW_ARB:
+ valid_usage = (ctx->API != API_OPENGLES);
+ break;
+
+ case GL_STATIC_DRAW_ARB:
+ case GL_DYNAMIC_DRAW_ARB:
+ valid_usage = true;
+ break;
+
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 */
+ valid_usage = _mesa_is_desktop_gl(ctx) || _mesa_is_gles3(ctx);
break;
+
default:
- _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)");
+ valid_usage = false;
+ break;
+ }
+
+ if (!valid_usage) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBufferData(usage)");
return;
}
if (_mesa_bufferobj_mapped(bufObj)) {
/* Unmap the existing buffer. We'll replace it now. Not an error. */
ctx->Driver.UnmapBuffer(ctx, bufObj);
- bufObj->AccessFlags = default_access_mode(ctx);
+ bufObj->AccessFlags = 0;
ASSERT(bufObj->Pointer == NULL);
}
void GLAPIENTRY
-_mesa_BufferSubDataARB(GLenum target, GLintptrARB offset,
+_mesa_BufferSubData(GLenum target, GLintptrARB offset,
GLsizeiptrARB size, const GLvoid * data)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
bufObj = buffer_object_subdata_range_good( ctx, target, offset, size,
"glBufferSubDataARB" );
void GLAPIENTRY
-_mesa_GetBufferSubDataARB(GLenum target, GLintptrARB offset,
+_mesa_GetBufferSubData(GLenum target, GLintptrARB offset,
GLsizeiptrARB size, void * data)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
bufObj = buffer_object_subdata_range_good( ctx, target, offset, size,
"glGetBufferSubDataARB" );
void * GLAPIENTRY
-_mesa_MapBufferARB(GLenum target, GLenum access)
+_mesa_MapBuffer(GLenum target, GLenum access)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object * bufObj;
GLbitfield accessFlags;
void *map;
+ bool valid_access;
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL);
switch (access) {
case GL_READ_ONLY_ARB:
accessFlags = GL_MAP_READ_BIT;
+ valid_access = _mesa_is_desktop_gl(ctx);
break;
case GL_WRITE_ONLY_ARB:
accessFlags = GL_MAP_WRITE_BIT;
+ valid_access = true;
break;
case GL_READ_WRITE_ARB:
accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
+ valid_access = _mesa_is_desktop_gl(ctx);
break;
default:
+ valid_access = false;
+ break;
+ }
+
+ if (!valid_access) {
_mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)");
return NULL;
}
GLboolean GLAPIENTRY
-_mesa_UnmapBufferARB(GLenum target)
+_mesa_UnmapBuffer(GLenum target)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
#endif
status = ctx->Driver.UnmapBuffer( ctx, bufObj );
- bufObj->AccessFlags = default_access_mode(ctx);
+ bufObj->AccessFlags = 0;
ASSERT(bufObj->Pointer == NULL);
ASSERT(bufObj->Offset == 0);
ASSERT(bufObj->Length == 0);
void GLAPIENTRY
-_mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params)
+_mesa_GetBufferParameteriv(GLenum target, GLenum pname, GLint *params)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
bufObj = get_buffer(ctx, "glGetBufferParameterivARB", target);
if (!bufObj)
*params = bufObj->Usage;
return;
case GL_BUFFER_ACCESS_ARB:
- *params = simplified_access_mode(bufObj->AccessFlags);
+ *params = simplified_access_mode(ctx, bufObj->AccessFlags);
return;
case GL_BUFFER_MAPPED_ARB:
*params = _mesa_bufferobj_mapped(bufObj);
return;
case GL_BUFFER_ACCESS_FLAGS:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = bufObj->AccessFlags;
return;
case GL_BUFFER_MAP_OFFSET:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = (GLint) bufObj->Offset;
return;
case GL_BUFFER_MAP_LENGTH:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = (GLint) bufObj->Length;
return;
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
bufObj = get_buffer(ctx, "glGetBufferParameteri64v", target);
if (!bufObj)
*params = bufObj->Usage;
return;
case GL_BUFFER_ACCESS_ARB:
- *params = simplified_access_mode(bufObj->AccessFlags);
+ *params = simplified_access_mode(ctx, bufObj->AccessFlags);
return;
case GL_BUFFER_ACCESS_FLAGS:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = bufObj->AccessFlags;
return;
*params = _mesa_bufferobj_mapped(bufObj);
return;
case GL_BUFFER_MAP_OFFSET:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = bufObj->Offset;
return;
case GL_BUFFER_MAP_LENGTH:
- if (ctx->VersionMajor < 3)
+ if (!ctx->Extensions.ARB_map_buffer_range)
goto invalid_pname;
*params = bufObj->Length;
return;
void GLAPIENTRY
-_mesa_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params)
+_mesa_GetBufferPointerv(GLenum target, GLenum pname, GLvoid **params)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object * bufObj;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
if (pname != GL_BUFFER_MAP_POINTER_ARB) {
_mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(pname)");
{
GET_CURRENT_CONTEXT(ctx);
struct gl_buffer_object *src, *dst;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
- src = get_buffer(ctx, "glCopyBuffserSubData", readTarget);
+ src = get_buffer(ctx, "glCopyBufferSubData", readTarget);
if (!src)
return;
- dst = get_buffer(ctx, "glCopyBuffserSubData", writeTarget);
+ dst = get_buffer(ctx, "glCopyBufferSubData", writeTarget);
if (!dst)
return;
if (_mesa_bufferobj_mapped(src)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
- "glCopyBuffserSubData(readBuffer is mapped)");
+ "glCopyBufferSubData(readBuffer is mapped)");
return;
}
if (_mesa_bufferobj_mapped(dst)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
- "glCopyBuffserSubData(writeBuffer is mapped)");
+ "glCopyBufferSubData(writeBuffer is mapped)");
return;
}
if (readOffset < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glCopyBuffserSubData(readOffset = %d)", (int) readOffset);
+ "glCopyBufferSubData(readOffset = %d)", (int) readOffset);
return;
}
if (writeOffset < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glCopyBuffserSubData(writeOffset = %d)", (int) writeOffset);
+ "glCopyBufferSubData(writeOffset = %d)", (int) writeOffset);
+ return;
+ }
+
+ if (size < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glCopyBufferSubData(writeOffset = %d)", (int) size);
return;
}
if (readOffset + size > src->Size) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glCopyBuffserSubData(readOffset + size = %d)",
+ "glCopyBufferSubData(readOffset + size = %d)",
(int) (readOffset + size));
return;
}
if (writeOffset + size > dst->Size) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glCopyBuffserSubData(writeOffset + size = %d)",
+ "glCopyBufferSubData(writeOffset + size = %d)",
(int) (writeOffset + size));
return;
}
else {
/* overlapping src/dst is illegal */
_mesa_error(ctx, GL_INVALID_VALUE,
- "glCopyBuffserSubData(overlapping src/dst)");
+ "glCopyBufferSubData(overlapping src/dst)");
return;
}
}
return NULL;
}
+ /* Page 38 of the PDF of the OpenGL ES 3.0 spec says:
+ *
+ * "An INVALID_OPERATION error is generated for any of the following
+ * conditions:
+ *
+ * * <length> is zero."
+ */
+ if (_mesa_is_gles(ctx) && length == 0) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glMapBufferRange(length = 0)");
+ return NULL;
+ }
+
if (access & ~(GL_MAP_READ_BIT |
GL_MAP_WRITE_BIT |
GL_MAP_INVALIDATE_RANGE_BIT |
{
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)");
+ "glFlushMappedBufferRange(extension not supported)");
return;
}
if (offset < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glMapBufferRange(offset = %ld)", (long)offset);
+ "glFlushMappedBufferRange(offset = %ld)", (long)offset);
return;
}
if (length < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
- "glMapBufferRange(length = %ld)", (long)length);
+ "glFlushMappedBufferRange(length = %ld)", (long)length);
return;
}
- bufObj = get_buffer(ctx, "glMapBufferRange", target);
+ bufObj = get_buffer(ctx, "glFlushMappedBufferRange", target);
if (!bufObj)
return;
if (!_mesa_bufferobj_mapped(bufObj)) {
/* buffer is not mapped */
_mesa_error(ctx, GL_INVALID_OPERATION,
- "glMapBufferRange(buffer is not mapped)");
+ "glFlushMappedBufferRange(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)");
+ "glFlushMappedBufferRange(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)",
+ "glFlushMappedBufferRange(offset %ld + length %ld > mapped length %ld)",
(long)offset, (long)length, (long)bufObj->Length);
return;
}
}
-#if FEATURE_APPLE_object_purgeable
static GLenum
buffer_object_purgeable(struct gl_context *ctx, GLuint name, GLenum option)
{
}
}
-#endif /* FEATURE_APPLE_object_purgeable */
+static void
+set_ubo_binding(struct gl_context *ctx,
+ int index,
+ struct gl_buffer_object *bufObj,
+ GLintptr offset,
+ GLsizeiptr size,
+ GLboolean autoSize)
+{
+ struct gl_uniform_buffer_binding *binding;
+
+ binding = &ctx->UniformBufferBindings[index];
+ if (binding->BufferObject == bufObj &&
+ binding->Offset == offset &&
+ binding->Size == size &&
+ binding->AutomaticSize == autoSize) {
+ return;
+ }
+
+ FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT);
+
+ _mesa_reference_buffer_object(ctx, &binding->BufferObject, bufObj);
+ binding->Offset = offset;
+ binding->Size = size;
+ binding->AutomaticSize = autoSize;
+}
+
+/**
+ * Bind a region of a buffer object to a uniform block binding point.
+ * \param index the uniform buffer binding point index
+ * \param bufObj the buffer object
+ * \param offset offset to the start of buffer object region
+ * \param size size of the buffer object region
+ */
+static void
+bind_buffer_range_uniform_buffer(struct gl_context *ctx,
+ GLuint index,
+ struct gl_buffer_object *bufObj,
+ GLintptr offset,
+ GLsizeiptr size)
+{
+ if (index >= ctx->Const.MaxUniformBufferBindings) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
+ return;
+ }
+
+ if (offset & (ctx->Const.UniformBufferOffsetAlignment - 1)) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glBindBufferRange(offset misalgned %d/%d)", (int) offset,
+ ctx->Const.UniformBufferOffsetAlignment);
+ return;
+ }
+
+ if (bufObj == ctx->Shared->NullBufferObj) {
+ offset = -1;
+ size = -1;
+ }
+
+ _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, bufObj);
+ set_ubo_binding(ctx, index, bufObj, offset, size, GL_FALSE);
+}
+
+
+/**
+ * Bind a buffer object to a uniform block binding point.
+ * As above, but offset = 0.
+ */
+static void
+bind_buffer_base_uniform_buffer(struct gl_context *ctx,
+ GLuint index,
+ struct gl_buffer_object *bufObj)
+{
+ if (index >= ctx->Const.MaxUniformBufferBindings) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
+ return;
+ }
+
+ _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, bufObj);
+ if (bufObj == ctx->Shared->NullBufferObj)
+ set_ubo_binding(ctx, index, bufObj, -1, -1, GL_TRUE);
+ else
+ set_ubo_binding(ctx, index, bufObj, 0, 0, GL_TRUE);
+}
+
+void GLAPIENTRY
+_mesa_BindBufferRange(GLenum target, GLuint index,
+ GLuint buffer, GLintptr offset, GLsizeiptr size)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *bufObj;
+
+ if (buffer == 0) {
+ bufObj = ctx->Shared->NullBufferObj;
+ } else {
+ bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+ }
+ if (!handle_bind_buffer_gen(ctx, target, buffer, &bufObj))
+ return;
+
+ if (!bufObj) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindBufferRange(invalid buffer=%u)", buffer);
+ return;
+ }
+
+ if (buffer != 0) {
+ if (size <= 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)",
+ (int) size);
+ return;
+ }
+ }
+
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK_BUFFER:
+ _mesa_bind_buffer_range_transform_feedback(ctx, index, bufObj,
+ offset, size);
+ return;
+ case GL_UNIFORM_BUFFER:
+ bind_buffer_range_uniform_buffer(ctx, index, bufObj, offset, size);
+ return;
+ default:
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
+ return;
+ }
+}
+
+void GLAPIENTRY
+_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *bufObj;
+
+ if (buffer == 0) {
+ bufObj = ctx->Shared->NullBufferObj;
+ } else {
+ bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+ }
+ if (!handle_bind_buffer_gen(ctx, target, buffer, &bufObj))
+ return;
+
+ if (!bufObj) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindBufferBase(invalid buffer=%u)", buffer);
+ return;
+ }
+
+ /* Note that there's some oddness in the GL 3.1-GL 3.3 specifications with
+ * regards to BindBufferBase. It says (GL 3.1 core spec, page 63):
+ *
+ * "BindBufferBase is equivalent to calling BindBufferRange with offset
+ * zero and size equal to the size of buffer."
+ *
+ * but it says for glGetIntegeri_v (GL 3.1 core spec, page 230):
+ *
+ * "If the parameter (starting offset or size) was not specified when the
+ * buffer object was bound, zero is returned."
+ *
+ * What happens if the size of the buffer changes? Does the size of the
+ * buffer at the moment glBindBufferBase was called still play a role, like
+ * the first quote would imply, or is the size meaningless in the
+ * glBindBufferBase case like the second quote would suggest? The GL 4.1
+ * core spec page 45 says:
+ *
+ * "It is equivalent to calling BindBufferRange with offset zero, while
+ * size is determined by the size of the bound buffer at the time the
+ * binding is used."
+ *
+ * My interpretation is that the GL 4.1 spec was a clarification of the
+ * behavior, not a change. In particular, this choice will only make
+ * rendering work in cases where it would have had undefined results.
+ */
+
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK_BUFFER:
+ _mesa_bind_buffer_base_transform_feedback(ctx, index, bufObj);
+ return;
+ case GL_UNIFORM_BUFFER:
+ bind_buffer_base_uniform_buffer(ctx, index, bufObj);
+ return;
+ default:
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
+ return;
+ }
+}
+
+void GLAPIENTRY
+_mesa_InvalidateBufferSubData(GLuint buffer, GLintptr offset,
+ GLsizeiptr length)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *bufObj;
+ const GLintptr end = offset + length;
+
+ bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+ if (!bufObj) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateBufferSubData(name = 0x%x) invalid object",
+ buffer);
+ return;
+ }
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "An INVALID_VALUE error is generated if <offset> or <length> is
+ * negative, or if <offset> + <length> is greater than the value of
+ * BUFFER_SIZE."
+ */
+ if (end < 0 || end > bufObj->Size) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateBufferSubData(invalid offset or length)");
+ return;
+ }
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "An INVALID_OPERATION error is generated if the buffer is currently
+ * mapped by MapBuffer, or if the invalidate range intersects the range
+ * currently mapped by MapBufferRange."
+ */
+ if (_mesa_bufferobj_mapped(bufObj)) {
+ const GLintptr mapEnd = bufObj->Offset + bufObj->Length;
+
+ /* The regions do not overlap if and only if the end of the discard
+ * region is before the mapped region or the start of the discard region
+ * is after the mapped region.
+ *
+ * Note that 'end' and 'mapEnd' are the first byte *after* the discard
+ * region and the mapped region, repsectively. It is okay for that byte
+ * to be mapped (for 'end') or discarded (for 'mapEnd').
+ */
+ if (!(end <= bufObj->Offset || offset >= mapEnd)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glInvalidateBufferSubData(intersection with mapped "
+ "range)");
+ return;
+ }
+ }
+
+ /* We don't actually do anything for this yet. Just return after
+ * validating the parameters and generating the required errors.
+ */
+ return;
+}
+
+void GLAPIENTRY
+_mesa_InvalidateBufferData(GLuint buffer)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_buffer_object *bufObj;
+
+ bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+ if (!bufObj) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateBufferData(name = 0x%x) invalid object",
+ buffer);
+ return;
+ }
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "An INVALID_OPERATION error is generated if the buffer is currently
+ * mapped by MapBuffer, or if the invalidate range intersects the range
+ * currently mapped by MapBufferRange."
+ */
+ if (_mesa_bufferobj_mapped(bufObj)) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glInvalidateBufferData(intersection with mapped "
+ "range)");
+ return;
+ }
+
+ /* We don't actually do anything for this yet. Just return after
+ * validating the parameters and generating the required errors.
+ */
+ return;
+}