#include "buffers.h"
#include "bufferobj.h"
#include "context.h"
-#include "enums.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;
}
* \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;
}
* 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:
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);
}
* 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,
/* 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;
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;
}
/**
* 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;
}
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.
*/
_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;
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;
}
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);
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;
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.
+ */
}
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);
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;
+}
- /* return the varying's name and length */
- _mesa_copy_string(name, bufSize, length, varyingName);
- /* return the datatype and value's size (in datatype units) */
- if (type)
- *type = param->Type;
- if (size)
- *size = param->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;
+ }
+
+ if (!names)
+ return;
+
+ /* 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 {
+ _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 */