X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ftransformfeedback.c;h=d02b6a056a57f29779021513ae2c267e91efcf93;hb=bebb0438113efdcc1c2aaed95c70ba87de1d3053;hp=74519ba38a6d40cd6dfc5dfc633f2e9dea8bd2e9;hpb=ff20543c815a14fddad1efaaef1610454a1924d4;p=mesa.git diff --git a/src/mesa/main/transformfeedback.c b/src/mesa/main/transformfeedback.c index 74519ba38a6..d02b6a056a5 100644 --- a/src/mesa/main/transformfeedback.c +++ b/src/mesa/main/transformfeedback.c @@ -33,35 +33,58 @@ #include "buffers.h" #include "bufferobj.h" #include "context.h" +#include "hash.h" +#include "mfeatures.h" +#include "mtypes.h" #include "transformfeedback.h" +#include "shaderapi.h" +#include "shaderobj.h" +#include "main/dispatch.h" -#include "shader/prog_parameter.h" -#include "shader/shader_api.h" +#include "program/prog_parameter.h" + + +#if FEATURE_EXT_transform_feedback /** - * Check if the given primitive mode (as in glBegin(mode)) is compatible - * with the current transform feedback mode (if it's enabled). - * This is to be called from glBegin(), glDrawArrays(), glDrawElements(), etc. - * - * \return GL_TRUE if the mode is OK, GL_FALSE otherwise. + * Do reference counting of transform feedback buffers. */ -GLboolean -_mesa_validate_primitive_mode(GLcontext *ctx, GLenum mode) +static void +reference_transform_feedback_object(struct gl_transform_feedback_object **ptr, + struct gl_transform_feedback_object *obj) { - if (ctx->TransformFeedback.Active) { - switch (mode) { - case GL_POINTS: - return ctx->TransformFeedback.Mode == GL_POINTS; - case GL_LINES: - case GL_LINE_STRIP: - case GL_LINE_LOOP: - return ctx->TransformFeedback.Mode == GL_LINES; - default: - return ctx->TransformFeedback.Mode == GL_TRIANGLES; + if (*ptr == obj) + return; + + if (*ptr) { + /* Unreference the old object */ + struct gl_transform_feedback_object *oldObj = *ptr; + + ASSERT(oldObj->RefCount > 0); + oldObj->RefCount--; + + if (oldObj->RefCount == 0) { + GET_CURRENT_CONTEXT(ctx); + if (ctx) + ctx->Driver.DeleteTransformFeedback(ctx, oldObj); + } + + *ptr = NULL; + } + ASSERT(!*ptr); + + if (obj) { + /* reference new object */ + if (obj->RefCount == 0) { + _mesa_problem(NULL, "referencing deleted transform feedback object"); + *ptr = NULL; + } + else { + obj->RefCount++; + *ptr = obj; } } - return GL_TRUE; } @@ -72,9 +95,9 @@ _mesa_validate_primitive_mode(GLcontext *ctx, GLenum mode) * \return GL_TRUE for success, GL_FALSE if error */ GLboolean -_mesa_validate_transform_feedback_buffers(GLcontext *ctx) +_mesa_validate_transform_feedback_buffers(struct gl_context *ctx) { - + /* XXX to do */ return GL_TRUE; } @@ -84,31 +107,232 @@ _mesa_validate_transform_feedback_buffers(GLcontext *ctx) * Per-context init for transform feedback. */ void -_mesa_init_transform_feedback(GLcontext *ctx) +_mesa_init_transform_feedback(struct gl_context *ctx) { + /* core mesa expects this, even a dummy one, to be available */ + ASSERT(ctx->Driver.NewTransformFeedback); + + ctx->TransformFeedback.DefaultObject = + ctx->Driver.NewTransformFeedback(ctx, 0); + + assert(ctx->TransformFeedback.DefaultObject->RefCount == 1); + + reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, + ctx->TransformFeedback.DefaultObject); + + assert(ctx->TransformFeedback.DefaultObject->RefCount == 2); + + ctx->TransformFeedback.Objects = _mesa_NewHashTable(); + _mesa_reference_buffer_object(ctx, &ctx->TransformFeedback.CurrentBuffer, ctx->Shared->NullBufferObj); } + +/** + * Callback for _mesa_HashDeleteAll(). + */ +static void +delete_cb(GLuint key, void *data, void *userData) +{ + struct gl_context *ctx = (struct gl_context *) userData; + struct gl_transform_feedback_object *obj = + (struct gl_transform_feedback_object *) data; + + ctx->Driver.DeleteTransformFeedback(ctx, obj); +} + + /** * Per-context free/clean-up for transform feedback. */ void -_mesa_free_transform_feedback(GLcontext *ctx) +_mesa_free_transform_feedback(struct gl_context *ctx) { + /* core mesa expects this, even a dummy one, to be available */ + ASSERT(ctx->Driver.NewTransformFeedback); + _mesa_reference_buffer_object(ctx, &ctx->TransformFeedback.CurrentBuffer, NULL); + + /* Delete all feedback objects */ + _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx); + _mesa_DeleteHashTable(ctx->TransformFeedback.Objects); + + /* Delete the default feedback object */ + assert(ctx->Driver.DeleteTransformFeedback); + ctx->Driver.DeleteTransformFeedback(ctx, + ctx->TransformFeedback.DefaultObject); + + ctx->TransformFeedback.CurrentObject = NULL; +} + + +#else /* FEATURE_EXT_transform_feedback */ + +/* forward declarations */ +static struct gl_transform_feedback_object * +new_transform_feedback(struct gl_context *ctx, GLuint name); + +static void +delete_transform_feedback(struct gl_context *ctx, + struct gl_transform_feedback_object *obj); + +/* dummy per-context init/clean-up for transform feedback */ +void +_mesa_init_transform_feedback(struct gl_context *ctx) +{ + ctx->TransformFeedback.DefaultObject = new_transform_feedback(ctx, 0); + ctx->TransformFeedback.CurrentObject = ctx->TransformFeedback.DefaultObject; + _mesa_reference_buffer_object(ctx, + &ctx->TransformFeedback.CurrentBuffer, + ctx->Shared->NullBufferObj); +} + +void +_mesa_free_transform_feedback(struct gl_context *ctx) +{ + _mesa_reference_buffer_object(ctx, + &ctx->TransformFeedback.CurrentBuffer, + NULL); + ctx->TransformFeedback.CurrentObject = NULL; + delete_transform_feedback(ctx, ctx->TransformFeedback.DefaultObject); +} + +#endif /* FEATURE_EXT_transform_feedback */ + + +/** Default fallback for ctx->Driver.NewTransformFeedback() */ +static struct gl_transform_feedback_object * +new_transform_feedback(struct gl_context *ctx, GLuint name) +{ + struct gl_transform_feedback_object *obj; + obj = CALLOC_STRUCT(gl_transform_feedback_object); + if (obj) { + obj->Name = name; + obj->RefCount = 1; + } + return obj; } +/** Default fallback for ctx->Driver.DeleteTransformFeedback() */ +static void +delete_transform_feedback(struct gl_context *ctx, + struct gl_transform_feedback_object *obj) +{ + GLuint i; + + for (i = 0; i < Elements(obj->Buffers); i++) { + _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL); + } + + free(obj); +} + + +#if FEATURE_EXT_transform_feedback + + +/** Default fallback for ctx->Driver.BeginTransformFeedback() */ +static void +begin_transform_feedback(struct gl_context *ctx, GLenum mode, + struct gl_transform_feedback_object *obj) +{ + /* nop */ +} + +/** Default fallback for ctx->Driver.EndTransformFeedback() */ +static void +end_transform_feedback(struct gl_context *ctx, + struct gl_transform_feedback_object *obj) +{ + /* nop */ +} + +/** Default fallback for ctx->Driver.PauseTransformFeedback() */ +static void +pause_transform_feedback(struct gl_context *ctx, + struct gl_transform_feedback_object *obj) +{ + /* nop */ +} + +/** Default fallback for ctx->Driver.ResumeTransformFeedback() */ +static void +resume_transform_feedback(struct gl_context *ctx, + struct gl_transform_feedback_object *obj) +{ + /* nop */ +} + + +/** + * Plug in default device driver functions for transform feedback. + * Most drivers will override some/all of these. + */ +void +_mesa_init_transform_feedback_functions(struct dd_function_table *driver) +{ + driver->NewTransformFeedback = new_transform_feedback; + driver->DeleteTransformFeedback = delete_transform_feedback; + driver->BeginTransformFeedback = begin_transform_feedback; + driver->EndTransformFeedback = end_transform_feedback; + driver->PauseTransformFeedback = pause_transform_feedback; + driver->ResumeTransformFeedback = resume_transform_feedback; +} + + +void +_mesa_init_transform_feedback_dispatch(struct _glapi_table *disp) +{ + /* EXT_transform_feedback */ + SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback); + SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback); + SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT); + SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings); + SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying); + /* ARB_transform_feedback2 */ + SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback); + SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks); + SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks); + SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback); + SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback); + SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback); +} + + +/** + ** Begin API functions + **/ + void GLAPIENTRY _mesa_BeginTransformFeedback(GLenum mode) { + struct gl_transform_feedback_object *obj; + struct gl_transform_feedback_info *info; + int i; GET_CURRENT_CONTEXT(ctx); + obj = ctx->TransformFeedback.CurrentObject; + + if (ctx->Shader.CurrentVertexProgram == NULL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBeginTransformFeedback(no program active)"); + return; + } + + info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback; + + if (info->NumOutputs == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBeginTransformFeedback(no varyings to record)"); + return; + } + switch (mode) { case GL_POINTS: case GL_LINES: @@ -120,29 +344,51 @@ _mesa_BeginTransformFeedback(GLenum mode) return; } - if (ctx->TransformFeedback.Active) { + if (obj->Active) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginTransformFeedback(already active)"); return; } - ctx->TransformFeedback.Active = GL_TRUE; + for (i = 0; i < info->NumBuffers; ++i) { + if (obj->BufferNames[i] == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBeginTransformFeedback(binding point %d does not have " + "a buffer object bound)", i); + return; + } + } + + FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); + obj->Active = GL_TRUE; ctx->TransformFeedback.Mode = mode; + + assert(ctx->Driver.BeginTransformFeedback); + ctx->Driver.BeginTransformFeedback(ctx, mode, obj); } void GLAPIENTRY _mesa_EndTransformFeedback(void) { + struct gl_transform_feedback_object *obj; GET_CURRENT_CONTEXT(ctx); - if (!ctx->TransformFeedback.Active) { + obj = ctx->TransformFeedback.CurrentObject; + + if (!obj->Active) { _mesa_error(ctx, GL_INVALID_OPERATION, "glEndTransformFeedback(not active)"); return; } - ctx->TransformFeedback.Active = GL_FALSE; + FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); + ctx->TransformFeedback.CurrentObject->Active = GL_FALSE; + ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE; + ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE; + + assert(ctx->Driver.EndTransformFeedback); + ctx->Driver.EndTransformFeedback(ctx, obj); } @@ -150,10 +396,18 @@ _mesa_EndTransformFeedback(void) * Helper used by BindBufferRange() and BindBufferBase(). */ static void -bind_buffer_range(GLcontext *ctx, GLuint index, +bind_buffer_range(struct gl_context *ctx, GLuint index, struct gl_buffer_object *bufObj, GLintptr offset, GLsizeiptr size) { + struct gl_transform_feedback_object *obj = + ctx->TransformFeedback.CurrentObject; + + /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because + * transform feedback buffers can't be changed while transform feedback is + * active. + */ + /* The general binding point */ _mesa_reference_buffer_object(ctx, &ctx->TransformFeedback.CurrentBuffer, @@ -161,33 +415,33 @@ bind_buffer_range(GLcontext *ctx, GLuint index, /* The per-attribute binding point */ _mesa_reference_buffer_object(ctx, - &ctx->TransformFeedback.Buffers[index], + &obj->Buffers[index], bufObj); - ctx->TransformFeedback.BufferNames[index] = bufObj->Name; + obj->BufferNames[index] = bufObj->Name; - ctx->TransformFeedback.Offset[index] = offset; - ctx->TransformFeedback.Size[index] = size; + obj->Offset[index] = offset; + obj->Size[index] = size; } /** * Specify a buffer object to receive vertex shader results. Plus, * specify the starting offset to place the results, and max size. + * Called from the glBindBufferRange() function. */ -void GLAPIENTRY -_mesa_BindBufferRange(GLenum target, GLuint index, - GLuint buffer, GLintptr offset, GLsizeiptr size) +void +_mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx, + GLuint index, + struct gl_buffer_object *bufObj, + GLintptr offset, + GLsizeiptr size) { - struct gl_buffer_object *bufObj; - GET_CURRENT_CONTEXT(ctx); + struct gl_transform_feedback_object *obj; - if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)"); - return; - } + obj = ctx->TransformFeedback.CurrentObject; - if (ctx->TransformFeedback.Active) { + if (obj->Active) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBindBufferRange(transform feedback active)"); return; @@ -198,29 +452,16 @@ _mesa_BindBufferRange(GLenum target, GLuint index, return; } - if ((size <= 0) || (size & 0x3)) { - /* must be positive and multiple of four */ - _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size%d)", size); + if (size & 0x3) { + /* must a multiple of four */ + _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size); return; } if (offset & 0x3) { /* must be multiple of four */ _mesa_error(ctx, GL_INVALID_VALUE, - "glBindBufferRange(offset=%d)", offset); - return; - } - - bufObj = _mesa_lookup_bufferobj(ctx, buffer); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBindBufferRange(invalid buffer=%u)", buffer); - return; - } - - if (offset + size >= bufObj->Size) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glBindBufferRange(offset + size > buffer size)", size); + "glBindBufferRange(offset=%d)", (int) offset); return; } @@ -231,22 +472,21 @@ _mesa_BindBufferRange(GLenum target, GLuint index, /** * Specify a buffer object to receive vertex shader results. * As above, but start at offset = 0. + * Called from the glBindBufferBase() function. */ -void GLAPIENTRY -_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer) +void +_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx, + GLuint index, + struct gl_buffer_object *bufObj) { - struct gl_buffer_object *bufObj; - GET_CURRENT_CONTEXT(ctx); + struct gl_transform_feedback_object *obj; GLsizeiptr size; - if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)"); - return; - } + obj = ctx->TransformFeedback.CurrentObject; - if (ctx->TransformFeedback.Active) { + if (obj->Active) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glBindBufferRange(transform feedback active)"); + "glBindBufferBase(transform feedback active)"); return; } @@ -255,13 +495,6 @@ _mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer) return; } - bufObj = _mesa_lookup_bufferobj(ctx, buffer); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBindBufferBase(invalid buffer=%u)", buffer); - return; - } - /* default size is the buffer size rounded down to nearest * multiple of four. */ @@ -280,6 +513,7 @@ void GLAPIENTRY _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, GLintptr offset) { + struct gl_transform_feedback_object *obj; struct gl_buffer_object *bufObj; GET_CURRENT_CONTEXT(ctx); GLsizeiptr size; @@ -289,9 +523,11 @@ _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, return; } - if (ctx->TransformFeedback.Active) { + obj = ctx->TransformFeedback.CurrentObject; + + if (obj->Active) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glBindBufferRange(transform feedback active)"); + "glBindBufferOffsetEXT(transform feedback active)"); return; } @@ -301,7 +537,19 @@ _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, return; } - bufObj = _mesa_lookup_bufferobj(ctx, buffer); + if (offset & 0x3) { + /* must be multiple of four */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glBindBufferOffsetEXT(offset=%d)", (int) offset); + return; + } + + if (buffer == 0) { + bufObj = ctx->Shared->NullBufferObj; + } else { + bufObj = _mesa_lookup_bufferobj(ctx, buffer); + } + if (!bufObj) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); @@ -340,7 +588,9 @@ _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, return; } - if (count < 0 || count > ctx->Const.MaxTransformFeedbackSeparateAttribs) { + if (count < 0 || + (bufferMode == GL_SEPARATE_ATTRIBS && + (GLuint) count > ctx->Const.MaxTransformFeedbackSeparateAttribs)) { _mesa_error(ctx, GL_INVALID_VALUE, "glTransformFeedbackVaryings(count=%d)", count); return; @@ -376,7 +626,9 @@ _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, shProg->TransformFeedback.BufferMode = bufferMode; - /* The varyings won't be used until shader link time */ + /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since + * the varyings won't be used until shader link time. + */ } @@ -390,8 +642,7 @@ _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei *size, GLenum *type, GLchar *name) { const struct gl_shader_program *shProg; - const GLchar *varyingName; - GLint v; + const struct gl_transform_feedback_info *linked_xfb_info; GET_CURRENT_CONTEXT(ctx); shProg = _mesa_lookup_shader_program(ctx, program); @@ -401,35 +652,222 @@ _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, return; } - if (index >= shProg->TransformFeedback.NumVarying) { + linked_xfb_info = &shProg->LinkedTransformFeedback; + if (index >= linked_xfb_info->NumVarying) { _mesa_error(ctx, GL_INVALID_VALUE, "glGetTransformFeedbackVaryings(index=%u)", index); return; } - varyingName = shProg->TransformFeedback.VaryingNames[index]; + /* return the varying's name and length */ + _mesa_copy_string(name, bufSize, length, + linked_xfb_info->Varyings[index].Name); - v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName); - if (v >= 0) { - struct gl_program_parameter *param = &shProg->Varying->Parameters[v]; + /* return the datatype and value's size (in datatype units) */ + if (type) + *type = linked_xfb_info->Varyings[index].Type; + if (size) + *size = linked_xfb_info->Varyings[index].Size; +} + + + +struct gl_transform_feedback_object * +_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) +{ + if (name == 0) { + return ctx->TransformFeedback.DefaultObject; + } + else + return (struct gl_transform_feedback_object *) + _mesa_HashLookup(ctx->TransformFeedback.Objects, name); +} + + +/** + * Create new transform feedback objects. Transform feedback objects + * encapsulate the state related to transform feedback to allow quickly + * switching state (and drawing the results, below). + * Part of GL_ARB_transform_feedback2. + */ +void GLAPIENTRY +_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) +{ + GLuint first; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)"); + return; + } - /* return the varying's name and length */ - _mesa_copy_string(name, bufSize, length, varyingName); + if (!names) + return; - /* return the datatype and value's size (in datatype units) */ - if (type) - *type = param->DataType; - if (size) - *size = param->Size; + /* we don't need contiguous IDs, but this might be faster */ + first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n); + if (first) { + GLsizei i; + for (i = 0; i < n; i++) { + struct gl_transform_feedback_object *obj + = ctx->Driver.NewTransformFeedback(ctx, first + i); + if (!obj) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); + return; + } + names[i] = first + i; + _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj); + } } else { - name[0] = 0; - if (length) - *length = 0; - if (type) - *type = 0; - if (size) - *size = 0; + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); + } +} + + +/** + * Is the given ID a transform feedback object? + * Part of GL_ARB_transform_feedback2. + */ +GLboolean GLAPIENTRY +_mesa_IsTransformFeedback(GLuint name) +{ + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + + if (name && _mesa_lookup_transform_feedback_object(ctx, name)) + return GL_TRUE; + else + return GL_FALSE; +} + + +/** + * Bind the given transform feedback object. + * Part of GL_ARB_transform_feedback2. + */ +void GLAPIENTRY +_mesa_BindTransformFeedback(GLenum target, GLuint name) +{ + struct gl_transform_feedback_object *obj; + GET_CURRENT_CONTEXT(ctx); + + if (target != GL_TRANSFORM_FEEDBACK) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); + return; + } + + if (ctx->TransformFeedback.CurrentObject->Active && + !ctx->TransformFeedback.CurrentObject->Paused) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBindTransformFeedback(transform is active, or not paused)"); + return; + } + + obj = _mesa_lookup_transform_feedback_object(ctx, name); + if (!obj) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBindTransformFeedback(name=%u)", name); + return; + } + + reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, + obj); +} + + +/** + * Delete the given transform feedback objects. + * Part of GL_ARB_transform_feedback2. + */ +void GLAPIENTRY +_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) +{ + GLint i; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); + return; + } + + if (!names) + return; + + for (i = 0; i < n; i++) { + if (names[i] > 0) { + struct gl_transform_feedback_object *obj + = _mesa_lookup_transform_feedback_object(ctx, names[i]); + if (obj) { + if (obj->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glDeleteTransformFeedbacks(object %u is active)", + names[i]); + return; + } + _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]); + /* unref, but object may not be deleted until later */ + reference_transform_feedback_object(&obj, NULL); + } + } + } +} + + +/** + * Pause transform feedback. + * Part of GL_ARB_transform_feedback2. + */ +void GLAPIENTRY +_mesa_PauseTransformFeedback(void) +{ + struct gl_transform_feedback_object *obj; + GET_CURRENT_CONTEXT(ctx); + + obj = ctx->TransformFeedback.CurrentObject; + + if (!obj->Active || obj->Paused) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glPauseTransformFeedback(feedback not active or already paused)"); + return; + } + + FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); + obj->Paused = GL_TRUE; + + assert(ctx->Driver.PauseTransformFeedback); + ctx->Driver.PauseTransformFeedback(ctx, obj); +} + + +/** + * Resume transform feedback. + * Part of GL_ARB_transform_feedback2. + */ +void GLAPIENTRY +_mesa_ResumeTransformFeedback(void) +{ + struct gl_transform_feedback_object *obj; + GET_CURRENT_CONTEXT(ctx); + + obj = ctx->TransformFeedback.CurrentObject; + + if (!obj->Active || !obj->Paused) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glResumeTransformFeedback(feedback not active or not paused)"); + return; } + + FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); + obj->Paused = GL_FALSE; + + assert(ctx->Driver.ResumeTransformFeedback); + ctx->Driver.ResumeTransformFeedback(ctx, obj); } +#endif /* FEATURE_EXT_transform_feedback */