X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Fbufferobj.c;h=e1c5877797a9920972130f895c99e3c47ae210cb;hb=5bebd7099ab22c6f1498cd928170561718d6ff36;hp=7b5140fcdf1a36c5daf16de873a281d7fc0fe539;hpb=28d7335810c2419fa6931260c30717dcf618109a;p=mesa.git diff --git a/src/mesa/main/bufferobj.c b/src/mesa/main/bufferobj.c index 7b5140fcdf1..e1c5877797a 100644 --- a/src/mesa/main/bufferobj.c +++ b/src/mesa/main/bufferobj.c @@ -31,6 +31,7 @@ */ #include +#include /* for PRId64 macro */ #include "glheader.h" #include "enums.h" #include "hash.h" @@ -116,6 +117,11 @@ get_buffer_target(struct gl_context *ctx, GLenum target) return &ctx->AtomicBuffer; } break; + case GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD: + if (ctx->Extensions.AMD_pinned_memory) { + return &ctx->ExternalVirtualMemoryBuffer; + } + break; default: return NULL; } @@ -388,14 +394,14 @@ convert_clear_buffer_data(struct gl_context *ctx, * Default callback for the \c dd_function_table::NewBufferObject() hook. */ static struct gl_buffer_object * -_mesa_new_buffer_object( struct gl_context *ctx, GLuint name, GLenum target ) +_mesa_new_buffer_object(struct gl_context *ctx, GLuint name) { struct gl_buffer_object *obj; (void) ctx; obj = MALLOC_STRUCT(gl_buffer_object); - _mesa_initialize_buffer_object(ctx, obj, name, target); + _mesa_initialize_buffer_object(ctx, obj, name); return obj; } @@ -440,7 +446,7 @@ _mesa_reference_buffer_object_(struct gl_context *ctx, struct gl_buffer_object *oldObj = *ptr; mtx_lock(&oldObj->Mutex); - ASSERT(oldObj->RefCount > 0); + assert(oldObj->RefCount > 0); oldObj->RefCount--; #if 0 printf("BufferObj %p %d DECR to %d\n", @@ -454,18 +460,18 @@ _mesa_reference_buffer_object_(struct gl_context *ctx, /* some sanity checking: don't delete a buffer still in use */ #if 0 /* unfortunately, these tests are invalid during context tear-down */ - ASSERT(ctx->Array.ArrayBufferObj != bufObj); - ASSERT(ctx->Array.VAO->IndexBufferObj != bufObj); - ASSERT(ctx->Array.VAO->Vertex.BufferObj != bufObj); + assert(ctx->Array.ArrayBufferObj != bufObj); + assert(ctx->Array.VAO->IndexBufferObj != bufObj); + assert(ctx->Array.VAO->Vertex.BufferObj != bufObj); #endif - ASSERT(ctx->Driver.DeleteBuffer); + assert(ctx->Driver.DeleteBuffer); ctx->Driver.DeleteBuffer(ctx, oldObj); } *ptr = NULL; } - ASSERT(!*ptr); + assert(!*ptr); if (bufObj) { /* reference new buffer */ @@ -493,12 +499,10 @@ _mesa_reference_buffer_object_(struct gl_context *ctx, * Initialize a buffer object to default values. */ void -_mesa_initialize_buffer_object( struct gl_context *ctx, - struct gl_buffer_object *obj, - GLuint name, GLenum target ) +_mesa_initialize_buffer_object(struct gl_context *ctx, + struct gl_buffer_object *obj, + GLuint name) { - (void) target; - memset(obj, 0, sizeof(struct gl_buffer_object)); mtx_init(&obj->Mutex, mtx_plain); obj->RefCount = 1; @@ -610,7 +614,7 @@ _mesa_buffer_subdata( struct gl_context *ctx, GLintptrARB offset, (void) ctx; /* this should have been caught in _mesa_BufferSubData() */ - ASSERT(size + offset <= bufObj->Size); + assert(size + offset <= bufObj->Size); if (bufObj->Data) { memcpy( (GLubyte *) bufObj->Data + offset, data, size ); @@ -675,7 +679,7 @@ _mesa_buffer_clear_subdata(struct gl_context *ctx, GLsizeiptr i; GLubyte *dest; - ASSERT(ctx->Driver.MapBufferRange); + assert(ctx->Driver.MapBufferRange); dest = ctx->Driver.MapBufferRange(ctx, offset, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT, @@ -832,6 +836,9 @@ _mesa_init_buffer_objects( struct gl_context *ctx ) _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, ctx->Shared->NullBufferObj); + _mesa_reference_buffer_object(ctx, &ctx->AtomicBuffer, + ctx->Shared->NullBufferObj); + _mesa_reference_buffer_object(ctx, &ctx->DrawIndirectBuffer, ctx->Shared->NullBufferObj); @@ -842,6 +849,14 @@ _mesa_init_buffer_objects( struct gl_context *ctx ) ctx->UniformBufferBindings[i].Offset = -1; ctx->UniformBufferBindings[i].Size = -1; } + + for (i = 0; i < MAX_COMBINED_ATOMIC_BUFFERS; i++) { + _mesa_reference_buffer_object(ctx, + &ctx->AtomicBufferBindings[i].BufferObject, + ctx->Shared->NullBufferObj); + ctx->AtomicBufferBindings[i].Offset = -1; + ctx->AtomicBufferBindings[i].Size = -1; + } } @@ -857,6 +872,8 @@ _mesa_free_buffer_objects( struct gl_context *ctx ) _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, NULL); + _mesa_reference_buffer_object(ctx, &ctx->AtomicBuffer, NULL); + _mesa_reference_buffer_object(ctx, &ctx->DrawIndirectBuffer, NULL); for (i = 0; i < MAX_COMBINED_UNIFORM_BUFFERS; i++) { @@ -864,6 +881,13 @@ _mesa_free_buffer_objects( struct gl_context *ctx ) &ctx->UniformBufferBindings[i].BufferObject, NULL); } + + for (i = 0; i < MAX_COMBINED_ATOMIC_BUFFERS; i++) { + _mesa_reference_buffer_object(ctx, + &ctx->AtomicBufferBindings[i].BufferObject, + NULL); + } + } bool @@ -884,8 +908,8 @@ _mesa_handle_bind_buffer_gen(struct gl_context *ctx, /* 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); + assert(ctx->Driver.NewBufferObject); + buf = ctx->Driver.NewBufferObject(ctx, buffer); if (!buf) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", caller); return false; @@ -1100,7 +1124,7 @@ _mesa_buffer_unmap_all_mappings(struct gl_context *ctx, for (i = 0; i < MAP_COUNT; i++) { if (_mesa_bufferobj_mapped(bufObj, i)) { ctx->Driver.UnmapBuffer(ctx, bufObj, i); - ASSERT(bufObj->Mappings[i].Pointer == NULL); + assert(bufObj->Mappings[i].Pointer == NULL); bufObj->Mappings[i].AccessFlags = 0; } } @@ -1150,12 +1174,12 @@ _mesa_DeleteBuffers(GLsizei n, const GLuint *ids) struct gl_vertex_array_object *vao = ctx->Array.VAO; GLuint j; - ASSERT(bufObj->Name == ids[i] || bufObj == &DummyBufferObject); + assert(bufObj->Name == ids[i] || bufObj == &DummyBufferObject); _mesa_buffer_unmap_all_mappings(ctx, bufObj); /* unbind any vertex pointers bound to this buffer */ - for (j = 0; j < Elements(vao->VertexBinding); j++) { + for (j = 0; j < ARRAY_SIZE(vao->VertexBinding); j++) { unbind(ctx, &vao->VertexBinding[j].BufferObj, bufObj); } @@ -1200,6 +1224,17 @@ _mesa_DeleteBuffers(GLsizei n, const GLuint *ids) _mesa_BindBuffer( GL_UNIFORM_BUFFER, 0 ); } + /* unbind Atomci Buffer binding points */ + for (j = 0; j < ctx->Const.MaxAtomicBufferBindings; j++) { + if (ctx->AtomicBufferBindings[j].BufferObject == bufObj) { + _mesa_BindBufferBase( GL_ATOMIC_COUNTER_BUFFER, j, 0 ); + } + } + + if (ctx->AtomicBuffer == bufObj) { + _mesa_BindBuffer( GL_ATOMIC_COUNTER_BUFFER, 0 ); + } + /* unbind any pixel pack/unpack pointers bound to this buffer */ if (ctx->Pack.BufferObj == bufObj) { _mesa_BindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 ); @@ -1212,6 +1247,10 @@ _mesa_DeleteBuffers(GLsizei n, const GLuint *ids) _mesa_BindBuffer( GL_TEXTURE_BUFFER, 0 ); } + if (ctx->ExternalVirtualMemoryBuffer == bufObj) { + _mesa_BindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); + } + /* The ID is immediately freed for re-use */ _mesa_HashRemove(ctx->Shared->BufferObjects, ids[i]); /* Make sure we do not run into the classic ABA problem on bind. @@ -1348,10 +1387,19 @@ _mesa_BufferStorage(GLenum target, GLsizeiptr size, const GLvoid *data, bufObj->Written = GL_TRUE; bufObj->Immutable = GL_TRUE; - ASSERT(ctx->Driver.BufferData); + assert(ctx->Driver.BufferData); if (!ctx->Driver.BufferData(ctx, target, size, data, GL_DYNAMIC_DRAW, flags, bufObj)) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferStorage()"); + if (target == GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD) { + /* Even though the interaction between AMD_pinned_memory and + * glBufferStorage is not described in the spec, Graham Sellers + * said that it should behave the same as glBufferData. + */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferStorage()"); + } + else { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferStorage()"); + } } } @@ -1429,13 +1477,24 @@ _mesa_BufferData(GLenum target, GLsizeiptrARB size, size += 100; #endif - ASSERT(ctx->Driver.BufferData); + assert(ctx->Driver.BufferData); if (!ctx->Driver.BufferData(ctx, target, size, data, usage, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT, bufObj)) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB()"); + if (target == GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD) { + /* From GL_AMD_pinned_memory: + * + * INVALID_OPERATION is generated by BufferData if is + * EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, and the store cannot be + * mapped to the GPU address space. + */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferData()"); + } + else { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferData()"); + } } } @@ -1466,7 +1525,7 @@ _mesa_BufferSubData(GLenum target, GLintptrARB offset, bufObj->Written = GL_TRUE; - ASSERT(ctx->Driver.BufferSubData); + assert(ctx->Driver.BufferSubData); ctx->Driver.BufferSubData( ctx, offset, size, data, bufObj ); } @@ -1486,7 +1545,7 @@ _mesa_GetBufferSubData(GLenum target, GLintptrARB offset, return; } - ASSERT(ctx->Driver.GetBufferSubData); + assert(ctx->Driver.GetBufferSubData); ctx->Driver.GetBufferSubData( ctx, offset, size, data, bufObj ); } @@ -1663,7 +1722,7 @@ _mesa_MapBuffer(GLenum target, GLenum access) return NULL; } - ASSERT(ctx->Driver.MapBufferRange); + assert(ctx->Driver.MapBufferRange); map = ctx->Driver.MapBufferRange(ctx, 0, bufObj->Size, accessFlags, bufObj, MAP_USER); if (!map) { @@ -1675,9 +1734,9 @@ _mesa_MapBuffer(GLenum target, GLenum access) * This is important because other modules (like VBO) might call * the driver function directly. */ - ASSERT(bufObj->Mappings[MAP_USER].Pointer == map); - ASSERT(bufObj->Mappings[MAP_USER].Length == bufObj->Size); - ASSERT(bufObj->Mappings[MAP_USER].Offset == 0); + assert(bufObj->Mappings[MAP_USER].Pointer == map); + assert(bufObj->Mappings[MAP_USER].Length == bufObj->Size); + assert(bufObj->Mappings[MAP_USER].Offset == 0); bufObj->Mappings[MAP_USER].AccessFlags = accessFlags; } @@ -1765,9 +1824,9 @@ _mesa_UnmapBuffer(GLenum target) status = ctx->Driver.UnmapBuffer(ctx, bufObj, MAP_USER); bufObj->Mappings[MAP_USER].AccessFlags = 0; - ASSERT(bufObj->Mappings[MAP_USER].Pointer == NULL); - ASSERT(bufObj->Mappings[MAP_USER].Offset == 0); - ASSERT(bufObj->Mappings[MAP_USER].Length == 0); + assert(bufObj->Mappings[MAP_USER].Pointer == NULL); + assert(bufObj->Mappings[MAP_USER].Offset == 0); + assert(bufObj->Mappings[MAP_USER].Length == 0); return status; } @@ -2144,7 +2203,7 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, return bufObj->Mappings[MAP_USER].Pointer; } - ASSERT(ctx->Driver.MapBufferRange); + assert(ctx->Driver.MapBufferRange); map = ctx->Driver.MapBufferRange(ctx, offset, length, access, bufObj, MAP_USER); if (!map) { @@ -2155,10 +2214,10 @@ _mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, * This is important because other modules (like VBO) might call * the driver function directly. */ - ASSERT(bufObj->Mappings[MAP_USER].Pointer == map); - ASSERT(bufObj->Mappings[MAP_USER].Length == length); - ASSERT(bufObj->Mappings[MAP_USER].Offset == offset); - ASSERT(bufObj->Mappings[MAP_USER].AccessFlags == access); + assert(bufObj->Mappings[MAP_USER].Pointer == map); + assert(bufObj->Mappings[MAP_USER].Length == length); + assert(bufObj->Mappings[MAP_USER].Offset == offset); + assert(bufObj->Mappings[MAP_USER].AccessFlags == access); } return map; @@ -2219,7 +2278,7 @@ _mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) return; } - ASSERT(bufObj->Mappings[MAP_USER].AccessFlags & GL_MAP_WRITE_BIT); + assert(bufObj->Mappings[MAP_USER].AccessFlags & GL_MAP_WRITE_BIT); if (ctx->Driver.FlushMappedBufferRange) ctx->Driver.FlushMappedBufferRange(ctx, offset, length, bufObj, @@ -2601,17 +2660,51 @@ _mesa_GetObjectParameterivAPPLE(GLenum objectType, GLuint name, GLenum pname, } } +/** + * Binds a buffer object to a uniform buffer binding point. + * + * The caller is responsible for flushing vertices and updating + * NewDriverState. + */ 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, + struct gl_buffer_object *bufObj, + GLintptr offset, + GLsizeiptr size, + GLboolean autoSize) { - struct gl_uniform_buffer_binding *binding; + _mesa_reference_buffer_object(ctx, &binding->BufferObject, bufObj); + + binding->Offset = offset; + binding->Size = size; + binding->AutomaticSize = autoSize; + + /* If this is a real buffer object, mark it has having been used + * at some point as a UBO. + */ + if (size >= 0) + bufObj->UsageHistory |= USAGE_UNIFORM_BUFFER; +} + +/** + * Binds a buffer object to a uniform buffer binding point. + * + * Unlike set_ubo_binding(), this function also flushes vertices + * and updates NewDriverState. It also checks if the binding + * has actually changed before updating it. + */ +static void +bind_uniform_buffer(struct gl_context *ctx, + GLuint index, + struct gl_buffer_object *bufObj, + GLintptr offset, + GLsizeiptr size, + GLboolean autoSize) +{ + struct gl_uniform_buffer_binding *binding = + &ctx->UniformBufferBindings[index]; - binding = &ctx->UniformBufferBindings[index]; if (binding->BufferObject == bufObj && binding->Offset == offset && binding->Size == size && @@ -2622,10 +2715,7 @@ set_ubo_binding(struct gl_context *ctx, FLUSH_VERTICES(ctx, 0); ctx->NewDriverState |= ctx->DriverFlags.NewUniformBuffer; - _mesa_reference_buffer_object(ctx, &binding->BufferObject, bufObj); - binding->Offset = offset; - binding->Size = size; - binding->AutomaticSize = autoSize; + set_ubo_binding(ctx, binding, bufObj, offset, size, autoSize); } /** @@ -2649,7 +2739,7 @@ bind_buffer_range_uniform_buffer(struct gl_context *ctx, if (offset & (ctx->Const.UniformBufferOffsetAlignment - 1)) { _mesa_error(ctx, GL_INVALID_VALUE, - "glBindBufferRange(offset misalgned %d/%d)", (int) offset, + "glBindBufferRange(offset misaligned %d/%d)", (int) offset, ctx->Const.UniformBufferOffsetAlignment); return; } @@ -2660,7 +2750,7 @@ bind_buffer_range_uniform_buffer(struct gl_context *ctx, } _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, bufObj); - set_ubo_binding(ctx, index, bufObj, offset, size, GL_FALSE); + bind_uniform_buffer(ctx, index, bufObj, offset, size, GL_FALSE); } @@ -2679,10 +2769,11 @@ bind_buffer_base_uniform_buffer(struct gl_context *ctx, } _mesa_reference_buffer_object(ctx, &ctx->UniformBuffer, bufObj); + if (bufObj == ctx->Shared->NullBufferObj) - set_ubo_binding(ctx, index, bufObj, -1, -1, GL_TRUE); + bind_uniform_buffer(ctx, index, bufObj, -1, -1, GL_TRUE); else - set_ubo_binding(ctx, index, bufObj, 0, 0, GL_TRUE); + bind_uniform_buffer(ctx, index, bufObj, 0, 0, GL_TRUE); } /** @@ -2706,6 +2797,7 @@ set_atomic_buffer_binding(struct gl_context *ctx, } else { binding->Offset = offset; binding->Size = size; + bufObj->UsageHistory |= USAGE_ATOMIC_COUNTER_BUFFER; } } @@ -2734,7 +2826,7 @@ bind_atomic_buffer(struct gl_context *ctx, if (offset & (ATOMIC_COUNTER_SIZE - 1)) { _mesa_error(ctx, GL_INVALID_VALUE, - "%s(offset misalgned %d/%d)", name, (int) offset, + "%s(offset misaligned %d/%d)", name, (int) offset, ATOMIC_COUNTER_SIZE); return; } @@ -2754,6 +2846,703 @@ bind_atomic_buffer(struct gl_context *ctx, set_atomic_buffer_binding(ctx, binding, bufObj, offset, size); } +static inline bool +bind_buffers_check_offset_and_size(struct gl_context *ctx, + GLuint index, + const GLintptr *offsets, + const GLsizeiptr *sizes) +{ + if (offsets[index] < 0) { + /* The ARB_multi_bind spec says: + * + * "An INVALID_VALUE error is generated by BindBuffersRange if any + * value in is less than zero (per binding)." + */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(offsets[%u]=%" PRId64 " < 0)", + index, (int64_t) offsets[index]); + return false; + } + + if (sizes[index] <= 0) { + /* The ARB_multi_bind spec says: + * + * "An INVALID_VALUE error is generated by BindBuffersRange if any + * value in is less than or equal to zero (per binding)." + */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(sizes[%u]=%" PRId64 " <= 0)", + index, (int64_t) sizes[index]); + return false; + } + + return true; +} + +static bool +error_check_bind_uniform_buffers(struct gl_context *ctx, + GLuint first, GLsizei count, + const char *caller) +{ + if (!ctx->Extensions.ARB_uniform_buffer_object) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(target=GL_UNIFORM_BUFFER)", caller); + return false; + } + + /* The ARB_multi_bind_spec says: + * + * "An INVALID_OPERATION error is generated if + is + * greater than the number of target-specific indexed binding points, + * as described in section 6.7.1." + */ + if (first + count > ctx->Const.MaxUniformBufferBindings) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(first=%u + count=%d > the value of " + "GL_MAX_UNIFORM_BUFFER_BINDINGS=%u)", + caller, first, count, + ctx->Const.MaxUniformBufferBindings); + return false; + } + + return true; +} + +/** + * Unbind all uniform buffers in the range + * through +-1 + */ +static void +unbind_uniform_buffers(struct gl_context *ctx, GLuint first, GLsizei count) +{ + struct gl_buffer_object *bufObj = ctx->Shared->NullBufferObj; + GLint i; + + for (i = 0; i < count; i++) + set_ubo_binding(ctx, &ctx->UniformBufferBindings[first + i], + bufObj, -1, -1, GL_TRUE); +} + +static void +bind_uniform_buffers_base(struct gl_context *ctx, GLuint first, GLsizei count, + const GLuint *buffers) +{ + GLint i; + + if (!error_check_bind_uniform_buffers(ctx, first, count, "glBindBuffersBase")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewUniformBuffer; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state." + */ + unbind_uniform_buffers(ctx, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + struct gl_uniform_buffer_binding *binding = + &ctx->UniformBufferBindings[first + i]; + struct gl_buffer_object *bufObj; + + if (binding->BufferObject && binding->BufferObject->Name == buffers[i]) + bufObj = binding->BufferObject; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersBase"); + + if (bufObj) { + if (bufObj == ctx->Shared->NullBufferObj) + set_ubo_binding(ctx, binding, bufObj, -1, -1, GL_TRUE); + else + set_ubo_binding(ctx, binding, bufObj, 0, 0, GL_TRUE); + } + } + + _mesa_end_bufferobj_lookups(ctx); +} + +static void +bind_uniform_buffers_range(struct gl_context *ctx, GLuint first, GLsizei count, + const GLuint *buffers, + const GLintptr *offsets, const GLsizeiptr *sizes) +{ + GLint i; + + if (!error_check_bind_uniform_buffers(ctx, first, count, + "glBindBuffersRange")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewUniformBuffer; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state. + * In this case, the offsets and sizes associated with the + * binding points are set to default values, ignoring + * and ." + */ + unbind_uniform_buffers(ctx, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + struct gl_uniform_buffer_binding *binding = + &ctx->UniformBufferBindings[first + i]; + struct gl_buffer_object *bufObj; + + if (!bind_buffers_check_offset_and_size(ctx, i, offsets, sizes)) + continue; + + /* The ARB_multi_bind spec says: + * + * "An INVALID_VALUE error is generated by BindBuffersRange if any + * pair of values in and does not respectively + * satisfy the constraints described for those parameters for the + * specified target, as described in section 6.7.1 (per binding)." + * + * Section 6.7.1 refers to table 6.5, which says: + * + * "┌───────────────────────────────────────────────────────────────┐ + * │ Uniform buffer array bindings (see sec. 7.6) │ + * ├─────────────────────┬─────────────────────────────────────────┤ + * │ ... │ ... │ + * │ offset restriction │ multiple of value of UNIFORM_BUFFER_- │ + * │ │ OFFSET_ALIGNMENT │ + * │ ... │ ... │ + * │ size restriction │ none │ + * └─────────────────────┴─────────────────────────────────────────┘" + */ + if (offsets[i] & (ctx->Const.UniformBufferOffsetAlignment - 1)) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(offsets[%u]=%" PRId64 + " is misaligned; it must be a multiple of the value of " + "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT=%u when " + "target=GL_UNIFORM_BUFFER)", + i, (int64_t) offsets[i], + ctx->Const.UniformBufferOffsetAlignment); + continue; + } + + if (binding->BufferObject && binding->BufferObject->Name == buffers[i]) + bufObj = binding->BufferObject; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersRange"); + + if (bufObj) { + if (bufObj == ctx->Shared->NullBufferObj) + set_ubo_binding(ctx, binding, bufObj, -1, -1, GL_FALSE); + else + set_ubo_binding(ctx, binding, bufObj, + offsets[i], sizes[i], GL_FALSE); + } + } + + _mesa_end_bufferobj_lookups(ctx); +} + +static bool +error_check_bind_xfb_buffers(struct gl_context *ctx, + struct gl_transform_feedback_object *tfObj, + GLuint first, GLsizei count, const char *caller) +{ + if (!ctx->Extensions.EXT_transform_feedback) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(target=GL_TRANSFORM_FEEDBACK_BUFFER)", caller); + return false; + } + + /* Page 398 of the PDF of the OpenGL 4.4 (Core Profile) spec says: + * + * "An INVALID_OPERATION error is generated : + * + * ... + * • by BindBufferRange or BindBufferBase if target is TRANSFORM_- + * FEEDBACK_BUFFER and transform feedback is currently active." + * + * We assume that this is also meant to apply to BindBuffersRange + * and BindBuffersBase. + */ + if (tfObj->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(Changing transform feedback buffers while " + "transform feedback is active)", caller); + return false; + } + + /* The ARB_multi_bind_spec says: + * + * "An INVALID_OPERATION error is generated if + is + * greater than the number of target-specific indexed binding points, + * as described in section 6.7.1." + */ + if (first + count > ctx->Const.MaxTransformFeedbackBuffers) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(first=%u + count=%d > the value of " + "GL_MAX_TRANSFORM_FEEDBACK_BUFFERS=%u)", + caller, first, count, + ctx->Const.MaxTransformFeedbackBuffers); + return false; + } + + return true; +} + +/** + * Unbind all transform feedback buffers in the range + * through +-1 + */ +static void +unbind_xfb_buffers(struct gl_context *ctx, + struct gl_transform_feedback_object *tfObj, + GLuint first, GLsizei count) +{ + struct gl_buffer_object * const bufObj = ctx->Shared->NullBufferObj; + GLint i; + + for (i = 0; i < count; i++) + _mesa_set_transform_feedback_binding(ctx, tfObj, first + i, + bufObj, 0, 0); +} + +static void +bind_xfb_buffers_base(struct gl_context *ctx, + GLuint first, GLsizei count, + const GLuint *buffers) +{ + struct gl_transform_feedback_object *tfObj = + ctx->TransformFeedback.CurrentObject; + GLint i; + + if (!error_check_bind_xfb_buffers(ctx, tfObj, first, count, + "glBindBuffersBase")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state." + */ + unbind_xfb_buffers(ctx, tfObj, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + struct gl_buffer_object * const boundBufObj = tfObj->Buffers[first + i]; + struct gl_buffer_object *bufObj; + + if (boundBufObj && boundBufObj->Name == buffers[i]) + bufObj = boundBufObj; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersBase"); + + if (bufObj) + _mesa_set_transform_feedback_binding(ctx, tfObj, first + i, + bufObj, 0, 0); + } + + _mesa_end_bufferobj_lookups(ctx); +} + +static void +bind_xfb_buffers_range(struct gl_context *ctx, + GLuint first, GLsizei count, + const GLuint *buffers, + const GLintptr *offsets, + const GLsizeiptr *sizes) +{ + struct gl_transform_feedback_object *tfObj = + ctx->TransformFeedback.CurrentObject; + GLint i; + + if (!error_check_bind_xfb_buffers(ctx, tfObj, first, count, + "glBindBuffersRange")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state. + * In this case, the offsets and sizes associated with the + * binding points are set to default values, ignoring + * and ." + */ + unbind_xfb_buffers(ctx, tfObj, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + const GLuint index = first + i; + struct gl_buffer_object * const boundBufObj = tfObj->Buffers[index]; + struct gl_buffer_object *bufObj; + + if (!bind_buffers_check_offset_and_size(ctx, i, offsets, sizes)) + continue; + + /* The ARB_multi_bind spec says: + * + * "An INVALID_VALUE error is generated by BindBuffersRange if any + * pair of values in and does not respectively + * satisfy the constraints described for those parameters for the + * specified target, as described in section 6.7.1 (per binding)." + * + * Section 6.7.1 refers to table 6.5, which says: + * + * "┌───────────────────────────────────────────────────────────────┐ + * │ Transform feedback array bindings (see sec. 13.2.2) │ + * ├───────────────────────┬───────────────────────────────────────┤ + * │ ... │ ... │ + * │ offset restriction │ multiple of 4 │ + * │ ... │ ... │ + * │ size restriction │ multiple of 4 │ + * └───────────────────────┴───────────────────────────────────────┘" + */ + if (offsets[i] & 0x3) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(offsets[%u]=%" PRId64 + " is misaligned; it must be a multiple of 4 when " + "target=GL_TRANSFORM_FEEDBACK_BUFFER)", + i, (int64_t) offsets[i]); + continue; + } + + if (sizes[i] & 0x3) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(sizes[%u]=%" PRId64 + " is misaligned; it must be a multiple of 4 when " + "target=GL_TRANSFORM_FEEDBACK_BUFFER)", + i, (int64_t) sizes[i]); + continue; + } + + if (boundBufObj && boundBufObj->Name == buffers[i]) + bufObj = boundBufObj; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersRange"); + + if (bufObj) + _mesa_set_transform_feedback_binding(ctx, tfObj, index, bufObj, + offsets[i], sizes[i]); + } + + _mesa_end_bufferobj_lookups(ctx); +} + +static bool +error_check_bind_atomic_buffers(struct gl_context *ctx, + GLuint first, GLsizei count, + const char *caller) +{ + if (!ctx->Extensions.ARB_shader_atomic_counters) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(target=GL_ATOMIC_COUNTER_BUFFER)", caller); + return false; + } + + /* The ARB_multi_bind_spec says: + * + * "An INVALID_OPERATION error is generated if + is + * greater than the number of target-specific indexed binding points, + * as described in section 6.7.1." + */ + if (first + count > ctx->Const.MaxAtomicBufferBindings) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(first=%u + count=%d > the value of " + "GL_MAX_ATOMIC_BUFFER_BINDINGS=%u)", + caller, first, count, ctx->Const.MaxAtomicBufferBindings); + return false; + } + + return true; +} + +/** + * Unbind all atomic counter buffers in the range + * through +-1 + */ +static void +unbind_atomic_buffers(struct gl_context *ctx, GLuint first, GLsizei count) +{ + struct gl_buffer_object * const bufObj = ctx->Shared->NullBufferObj; + GLint i; + + for (i = 0; i < count; i++) + set_atomic_buffer_binding(ctx, &ctx->AtomicBufferBindings[first + i], + bufObj, -1, -1); +} + +static void +bind_atomic_buffers_base(struct gl_context *ctx, + GLuint first, + GLsizei count, + const GLuint *buffers) +{ + GLint i; + + if (!error_check_bind_atomic_buffers(ctx, first, count, + "glBindBuffersBase")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewAtomicBuffer; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state." + */ + unbind_atomic_buffers(ctx, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + struct gl_atomic_buffer_binding *binding = + &ctx->AtomicBufferBindings[first + i]; + struct gl_buffer_object *bufObj; + + if (binding->BufferObject && binding->BufferObject->Name == buffers[i]) + bufObj = binding->BufferObject; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersBase"); + + if (bufObj) + set_atomic_buffer_binding(ctx, binding, bufObj, 0, 0); + } + + _mesa_end_bufferobj_lookups(ctx); +} + +static void +bind_atomic_buffers_range(struct gl_context *ctx, + GLuint first, + GLsizei count, + const GLuint *buffers, + const GLintptr *offsets, + const GLsizeiptr *sizes) +{ + GLint i; + + if (!error_check_bind_atomic_buffers(ctx, first, count, + "glBindBuffersRange")) + return; + + /* Assume that at least one binding will be changed */ + FLUSH_VERTICES(ctx, 0); + ctx->NewDriverState |= ctx->DriverFlags.NewAtomicBuffer; + + if (!buffers) { + /* The ARB_multi_bind spec says: + * + * "If is NULL, all bindings from through + * +-1 are reset to their unbound (zero) state. + * In this case, the offsets and sizes associated with the + * binding points are set to default values, ignoring + * and ." + */ + unbind_atomic_buffers(ctx, first, count); + return; + } + + /* Note that the error semantics for multi-bind commands differ from + * those of other GL commands. + * + * The Issues section in the ARB_multi_bind spec says: + * + * "(11) Typically, OpenGL specifies that if an error is generated by a + * command, that command has no effect. This is somewhat + * unfortunate for multi-bind commands, because it would require a + * first pass to scan the entire list of bound objects for errors + * and then a second pass to actually perform the bindings. + * Should we have different error semantics? + * + * RESOLVED: Yes. In this specification, when the parameters for + * one of the binding points are invalid, that binding point + * is not updated and an error will be generated. However, other + * binding points in the same command will be updated if their + * parameters are valid and no other error occurs." + */ + + _mesa_begin_bufferobj_lookups(ctx); + + for (i = 0; i < count; i++) { + struct gl_atomic_buffer_binding *binding = + &ctx->AtomicBufferBindings[first + i]; + struct gl_buffer_object *bufObj; + + if (!bind_buffers_check_offset_and_size(ctx, i, offsets, sizes)) + continue; + + /* The ARB_multi_bind spec says: + * + * "An INVALID_VALUE error is generated by BindBuffersRange if any + * pair of values in and does not respectively + * satisfy the constraints described for those parameters for the + * specified target, as described in section 6.7.1 (per binding)." + * + * Section 6.7.1 refers to table 6.5, which says: + * + * "┌───────────────────────────────────────────────────────────────┐ + * │ Atomic counter array bindings (see sec. 7.7.2) │ + * ├───────────────────────┬───────────────────────────────────────┤ + * │ ... │ ... │ + * │ offset restriction │ multiple of 4 │ + * │ ... │ ... │ + * │ size restriction │ none │ + * └───────────────────────┴───────────────────────────────────────┘" + */ + if (offsets[i] & (ATOMIC_COUNTER_SIZE - 1)) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBuffersRange(offsets[%u]=%" PRId64 + " is misaligned; it must be a multiple of %d when " + "target=GL_ATOMIC_COUNTER_BUFFER)", + i, (int64_t) offsets[i], ATOMIC_COUNTER_SIZE); + continue; + } + + if (binding->BufferObject && binding->BufferObject->Name == buffers[i]) + bufObj = binding->BufferObject; + else + bufObj = _mesa_multi_bind_lookup_bufferobj(ctx, buffers, i, + "glBindBuffersRange"); + + if (bufObj) + set_atomic_buffer_binding(ctx, binding, bufObj, offsets[i], sizes[i]); + } + + _mesa_end_bufferobj_lookups(ctx); +} + void GLAPIENTRY _mesa_BindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) @@ -2871,12 +3660,47 @@ _mesa_BindBuffersRange(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes) { + GET_CURRENT_CONTEXT(ctx); + + switch (target) { + case GL_TRANSFORM_FEEDBACK_BUFFER: + bind_xfb_buffers_range(ctx, first, count, buffers, offsets, sizes); + return; + case GL_UNIFORM_BUFFER: + bind_uniform_buffers_range(ctx, first, count, buffers, offsets, sizes); + return; + case GL_ATOMIC_COUNTER_BUFFER: + bind_atomic_buffers_range(ctx, first, count, buffers, + offsets, sizes); + return; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glBindBuffersRange(target=%s)", + _mesa_lookup_enum_by_nr(target)); + break; + } } void GLAPIENTRY _mesa_BindBuffersBase(GLenum target, GLuint first, GLsizei count, const GLuint *buffers) { + GET_CURRENT_CONTEXT(ctx); + + switch (target) { + case GL_TRANSFORM_FEEDBACK_BUFFER: + bind_xfb_buffers_base(ctx, first, count, buffers); + return; + case GL_UNIFORM_BUFFER: + bind_uniform_buffers_base(ctx, first, count, buffers); + return; + case GL_ATOMIC_COUNTER_BUFFER: + bind_atomic_buffers_base(ctx, first, count, buffers); + return; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glBindBuffersBase(target=%s)", + _mesa_lookup_enum_by_nr(target)); + break; + } } void GLAPIENTRY