1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2010  VMware, Inc.  All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25/*
26 * Vertex transform feedback support.
27 *
28 * Authors:
29 *   Brian Paul
30 */
31
32
33#include "buffers.h"
34#include "bufferobj.h"
35#include "context.h"
36#include "hash.h"
37#include "mfeatures.h"
38#include "mtypes.h"
39#include "transformfeedback.h"
40#include "shaderapi.h"
41#include "shaderobj.h"
42#include "main/dispatch.h"
43
44#include "program/prog_parameter.h"
45
46
47#if FEATURE_EXT_transform_feedback
48
49
50/**
51 * Do reference counting of transform feedback buffers.
52 */
53static void
54reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
55                                    struct gl_transform_feedback_object *obj)
56{
57   if (*ptr == obj)
58      return;
59
60   if (*ptr) {
61      /* Unreference the old object */
62      struct gl_transform_feedback_object *oldObj = *ptr;
63
64      ASSERT(oldObj->RefCount > 0);
65      oldObj->RefCount--;
66
67      if (oldObj->RefCount == 0) {
68         GET_CURRENT_CONTEXT(ctx);
69         if (ctx)
70            ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
71      }
72
73      *ptr = NULL;
74   }
75   ASSERT(!*ptr);
76
77   if (obj) {
78      /* reference new object */
79      if (obj->RefCount == 0) {
80         _mesa_problem(NULL, "referencing deleted transform feedback object");
81         *ptr = NULL;
82      }
83      else {
84         obj->RefCount++;
85         *ptr = obj;
86      }
87   }
88}
89
90
91/**
92 * Check that all the buffer objects currently bound for transform
93 * feedback actually exist.  Raise a GL_INVALID_OPERATION error if
94 * any buffers are missing.
95 * \return GL_TRUE for success, GL_FALSE if error
96 */
97GLboolean
98_mesa_validate_transform_feedback_buffers(struct gl_context *ctx)
99{
100   /* XXX to do */
101   return GL_TRUE;
102}
103
104
105
106/**
107 * Per-context init for transform feedback.
108 */
109void
110_mesa_init_transform_feedback(struct gl_context *ctx)
111{
112   /* core mesa expects this, even a dummy one, to be available */
113   ASSERT(ctx->Driver.NewTransformFeedback);
114
115   ctx->TransformFeedback.DefaultObject =
116      ctx->Driver.NewTransformFeedback(ctx, 0);
117
118   assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
119
120   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
121                                       ctx->TransformFeedback.DefaultObject);
122
123   assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
124
125   ctx->TransformFeedback.Objects = _mesa_NewHashTable();
126
127   _mesa_reference_buffer_object(ctx,
128                                 &ctx->TransformFeedback.CurrentBuffer,
129                                 ctx->Shared->NullBufferObj);
130}
131
132
133
134/**
135 * Callback for _mesa_HashDeleteAll().
136 */
137static void
138delete_cb(GLuint key, void *data, void *userData)
139{
140   struct gl_context *ctx = (struct gl_context *) userData;
141   struct gl_transform_feedback_object *obj =
142      (struct gl_transform_feedback_object *) data;
143
144   ctx->Driver.DeleteTransformFeedback(ctx, obj);
145}
146
147
148/**
149 * Per-context free/clean-up for transform feedback.
150 */
151void
152_mesa_free_transform_feedback(struct gl_context *ctx)
153{
154   /* core mesa expects this, even a dummy one, to be available */
155   ASSERT(ctx->Driver.NewTransformFeedback);
156
157   _mesa_reference_buffer_object(ctx,
158                                 &ctx->TransformFeedback.CurrentBuffer,
159                                 NULL);
160
161   /* Delete all feedback objects */
162   _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
163   _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
164
165   /* Delete the default feedback object */
166   assert(ctx->Driver.DeleteTransformFeedback);
167   ctx->Driver.DeleteTransformFeedback(ctx,
168                                       ctx->TransformFeedback.DefaultObject);
169
170   ctx->TransformFeedback.CurrentObject = NULL;
171}
172
173
174#else /* FEATURE_EXT_transform_feedback */
175
176/* forward declarations */
177static struct gl_transform_feedback_object *
178new_transform_feedback(struct gl_context *ctx, GLuint name);
179
180static void
181delete_transform_feedback(struct gl_context *ctx,
182                          struct gl_transform_feedback_object *obj);
183
184/* dummy per-context init/clean-up for transform feedback */
185void
186_mesa_init_transform_feedback(struct gl_context *ctx)
187{
188   ctx->TransformFeedback.DefaultObject = new_transform_feedback(ctx, 0);
189   ctx->TransformFeedback.CurrentObject = ctx->TransformFeedback.DefaultObject;
190   _mesa_reference_buffer_object(ctx,
191                                 &ctx->TransformFeedback.CurrentBuffer,
192                                 ctx->Shared->NullBufferObj);
193}
194
195void
196_mesa_free_transform_feedback(struct gl_context *ctx)
197{
198   _mesa_reference_buffer_object(ctx,
199                                 &ctx->TransformFeedback.CurrentBuffer,
200                                 NULL);
201   ctx->TransformFeedback.CurrentObject = NULL;
202   delete_transform_feedback(ctx, ctx->TransformFeedback.DefaultObject);
203}
204
205#endif /* FEATURE_EXT_transform_feedback */
206
207
208/** Default fallback for ctx->Driver.NewTransformFeedback() */
209static struct gl_transform_feedback_object *
210new_transform_feedback(struct gl_context *ctx, GLuint name)
211{
212   struct gl_transform_feedback_object *obj;
213   obj = CALLOC_STRUCT(gl_transform_feedback_object);
214   if (obj) {
215      obj->Name = name;
216      obj->RefCount = 1;
217   }
218   return obj;
219}
220
221/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
222static void
223delete_transform_feedback(struct gl_context *ctx,
224                          struct gl_transform_feedback_object *obj)
225{
226   GLuint i;
227
228   for (i = 0; i < Elements(obj->Buffers); i++) {
229      _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
230   }
231
232   free(obj);
233}
234
235
236#if FEATURE_EXT_transform_feedback
237
238
239/** Default fallback for ctx->Driver.BeginTransformFeedback() */
240static void
241begin_transform_feedback(struct gl_context *ctx, GLenum mode,
242                         struct gl_transform_feedback_object *obj)
243{
244   /* nop */
245}
246
247/** Default fallback for ctx->Driver.EndTransformFeedback() */
248static void
249end_transform_feedback(struct gl_context *ctx,
250                       struct gl_transform_feedback_object *obj)
251{
252   /* nop */
253}
254
255/** Default fallback for ctx->Driver.PauseTransformFeedback() */
256static void
257pause_transform_feedback(struct gl_context *ctx,
258                         struct gl_transform_feedback_object *obj)
259{
260   /* nop */
261}
262
263/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
264static void
265resume_transform_feedback(struct gl_context *ctx,
266                          struct gl_transform_feedback_object *obj)
267{
268   /* nop */
269}
270
271
272/**
273 * Plug in default device driver functions for transform feedback.
274 * Most drivers will override some/all of these.
275 */
276void
277_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
278{
279   driver->NewTransformFeedback = new_transform_feedback;
280   driver->DeleteTransformFeedback = delete_transform_feedback;
281   driver->BeginTransformFeedback = begin_transform_feedback;
282   driver->EndTransformFeedback = end_transform_feedback;
283   driver->PauseTransformFeedback = pause_transform_feedback;
284   driver->ResumeTransformFeedback = resume_transform_feedback;
285}
286
287
288void
289_mesa_init_transform_feedback_dispatch(struct _glapi_table *disp)
290{
291   /* EXT_transform_feedback */
292   SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback);
293   SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback);
294   SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT);
295   SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings);
296   SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying);
297   /* ARB_transform_feedback2 */
298   SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
299   SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
300   SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
301   SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
302   SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
303   SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
304}
305
306
307/**
308 ** Begin API functions
309 **/
310
311
312void GLAPIENTRY
313_mesa_BeginTransformFeedback(GLenum mode)
314{
315   struct gl_transform_feedback_object *obj;
316   struct gl_transform_feedback_info *info;
317   int i;
318   GET_CURRENT_CONTEXT(ctx);
319
320   obj = ctx->TransformFeedback.CurrentObject;
321
322   if (ctx->Shader.CurrentVertexProgram == NULL) {
323      _mesa_error(ctx, GL_INVALID_OPERATION,
324                  "glBeginTransformFeedback(no program active)");
325      return;
326   }
327
328   info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback;
329
330   if (info->NumOutputs == 0) {
331      _mesa_error(ctx, GL_INVALID_OPERATION,
332                  "glBeginTransformFeedback(no varyings to record)");
333      return;
334   }
335
336   switch (mode) {
337   case GL_POINTS:
338   case GL_LINES:
339   case GL_TRIANGLES:
340      /* legal */
341      break;
342   default:
343      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
344      return;
345   }
346
347   if (obj->Active) {
348      _mesa_error(ctx, GL_INVALID_OPERATION,
349                  "glBeginTransformFeedback(already active)");
350      return;
351   }
352
353   for (i = 0; i < info->NumBuffers; ++i) {
354      if (obj->BufferNames[i] == 0) {
355         _mesa_error(ctx, GL_INVALID_OPERATION,
356                     "glBeginTransformFeedback(binding point %d does not have "
357                     "a buffer object bound)", i);
358         return;
359      }
360   }
361
362   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
363   obj->Active = GL_TRUE;
364   ctx->TransformFeedback.Mode = mode;
365
366   assert(ctx->Driver.BeginTransformFeedback);
367   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
368}
369
370
371void GLAPIENTRY
372_mesa_EndTransformFeedback(void)
373{
374   struct gl_transform_feedback_object *obj;
375   GET_CURRENT_CONTEXT(ctx);
376
377   obj = ctx->TransformFeedback.CurrentObject;
378
379   if (!obj->Active) {
380      _mesa_error(ctx, GL_INVALID_OPERATION,
381                  "glEndTransformFeedback(not active)");
382      return;
383   }
384
385   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
386   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
387   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
388   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
389
390   assert(ctx->Driver.EndTransformFeedback);
391   ctx->Driver.EndTransformFeedback(ctx, obj);
392}
393
394
395/**
396 * Helper used by BindBufferRange() and BindBufferBase().
397 */
398static void
399bind_buffer_range(struct gl_context *ctx, GLuint index,
400                  struct gl_buffer_object *bufObj,
401                  GLintptr offset, GLsizeiptr size)
402{
403   struct gl_transform_feedback_object *obj =
404      ctx->TransformFeedback.CurrentObject;
405
406   /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
407    * transform feedback buffers can't be changed while transform feedback is
408    * active.
409    */
410
411   /* The general binding point */
412   _mesa_reference_buffer_object(ctx,
413                                 &ctx->TransformFeedback.CurrentBuffer,
414                                 bufObj);
415
416   /* The per-attribute binding point */
417   _mesa_reference_buffer_object(ctx,
418                                 &obj->Buffers[index],
419                                 bufObj);
420
421   obj->BufferNames[index] = bufObj->Name;
422
423   obj->Offset[index] = offset;
424   obj->Size[index] = size;
425}
426
427
428/**
429 * Specify a buffer object to receive vertex shader results.  Plus,
430 * specify the starting offset to place the results, and max size.
431 * Called from the glBindBufferRange() function.
432 */
433void
434_mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx,
435					   GLuint index,
436					   struct gl_buffer_object *bufObj,
437					   GLintptr offset,
438					   GLsizeiptr size)
439{
440   struct gl_transform_feedback_object *obj;
441
442   obj = ctx->TransformFeedback.CurrentObject;
443
444   if (obj->Active) {
445      _mesa_error(ctx, GL_INVALID_OPERATION,
446                  "glBindBufferRange(transform feedback active)");
447      return;
448   }
449
450   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
451      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
452      return;
453   }
454
455   if (size & 0x3) {
456      /* must a multiple of four */
457      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
458      return;
459   }
460
461   if (offset & 0x3) {
462      /* must be multiple of four */
463      _mesa_error(ctx, GL_INVALID_VALUE,
464                  "glBindBufferRange(offset=%d)", (int) offset);
465      return;
466   }
467
468   bind_buffer_range(ctx, index, bufObj, offset, size);
469}
470
471
472/**
473 * Specify a buffer object to receive vertex shader results.
474 * As above, but start at offset = 0.
475 * Called from the glBindBufferBase() function.
476 */
477void
478_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
479					  GLuint index,
480					  struct gl_buffer_object *bufObj)
481{
482   struct gl_transform_feedback_object *obj;
483   GLsizeiptr size;
484
485   obj = ctx->TransformFeedback.CurrentObject;
486
487   if (obj->Active) {
488      _mesa_error(ctx, GL_INVALID_OPERATION,
489                  "glBindBufferBase(transform feedback active)");
490      return;
491   }
492
493   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
494      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
495      return;
496   }
497
498   /* default size is the buffer size rounded down to nearest
499    * multiple of four.
500    */
501   size = bufObj->Size & ~0x3;
502
503   bind_buffer_range(ctx, index, bufObj, 0, size);
504}
505
506
507/**
508 * Specify a buffer object to receive vertex shader results, plus the
509 * offset in the buffer to start placing results.
510 * This function is part of GL_EXT_transform_feedback, but not GL3.
511 */
512void GLAPIENTRY
513_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
514                          GLintptr offset)
515{
516   struct gl_transform_feedback_object *obj;
517   struct gl_buffer_object *bufObj;
518   GET_CURRENT_CONTEXT(ctx);
519   GLsizeiptr size;
520
521   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
522      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
523      return;
524   }
525
526   obj = ctx->TransformFeedback.CurrentObject;
527
528   if (obj->Active) {
529      _mesa_error(ctx, GL_INVALID_OPERATION,
530                  "glBindBufferOffsetEXT(transform feedback active)");
531      return;
532   }
533
534   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
535      _mesa_error(ctx, GL_INVALID_VALUE,
536                  "glBindBufferOffsetEXT(index=%d)", index);
537      return;
538   }
539
540   if (offset & 0x3) {
541      /* must be multiple of four */
542      _mesa_error(ctx, GL_INVALID_VALUE,
543                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
544      return;
545   }
546
547   if (buffer == 0) {
548      bufObj = ctx->Shared->NullBufferObj;
549   } else {
550      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
551   }
552
553   if (!bufObj) {
554      _mesa_error(ctx, GL_INVALID_OPERATION,
555                  "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
556      return;
557   }
558
559   /* default size is the buffer size rounded down to nearest
560    * multiple of four.
561    */
562   size = (bufObj->Size - offset) & ~0x3;
563
564   bind_buffer_range(ctx, index, bufObj, offset, size);
565}
566
567
568/**
569 * This function specifies the vertex shader outputs to be written
570 * to the feedback buffer(s), and in what order.
571 */
572void GLAPIENTRY
573_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
574                                const GLchar **varyings, GLenum bufferMode)
575{
576   struct gl_shader_program *shProg;
577   GLuint i;
578   GET_CURRENT_CONTEXT(ctx);
579
580   switch (bufferMode) {
581   case GL_INTERLEAVED_ATTRIBS:
582      break;
583   case GL_SEPARATE_ATTRIBS:
584      break;
585   default:
586      _mesa_error(ctx, GL_INVALID_ENUM,
587                  "glTransformFeedbackVaryings(bufferMode)");
588      return;
589   }
590
591   if (count < 0 ||
592       (bufferMode == GL_SEPARATE_ATTRIBS &&
593        (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
594      _mesa_error(ctx, GL_INVALID_VALUE,
595                  "glTransformFeedbackVaryings(count=%d)", count);
596      return;
597   }
598
599   shProg = _mesa_lookup_shader_program(ctx, program);
600   if (!shProg) {
601      _mesa_error(ctx, GL_INVALID_VALUE,
602                  "glTransformFeedbackVaryings(program=%u)", program);
603      return;
604   }
605
606   if (ctx->Extensions.ARB_transform_feedback3) {
607      if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
608         unsigned buffers = 1;
609
610         for (i = 0; i < count; i++) {
611            if (strcmp(varyings[i], "gl_NextBuffer") == 0)
612               buffers++;
613         }
614
615         if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
616            _mesa_error(ctx, GL_INVALID_OPERATION,
617                        "glTransformFeedbackVaryings(too many gl_NextBuffer "
618                        "occurences)");
619            return;
620         }
621      } else {
622         for (i = 0; i < count; i++) {
623            if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
624                strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
625                strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
626                strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
627                strcmp(varyings[i], "gl_SkipComponents4") == 0) {
628               _mesa_error(ctx, GL_INVALID_OPERATION,
629                           "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
630                           "varying=%s)",
631                           varyings[i]);
632               return;
633            }
634         }
635      }
636   }
637
638   /* free existing varyings, if any */
639   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
640      free(shProg->TransformFeedback.VaryingNames[i]);
641   }
642   free(shProg->TransformFeedback.VaryingNames);
643
644   /* allocate new memory for varying names */
645   shProg->TransformFeedback.VaryingNames =
646      (GLchar **) malloc(count * sizeof(GLchar *));
647
648   if (!shProg->TransformFeedback.VaryingNames) {
649      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
650      return;
651   }
652
653   /* Save the new names and the count */
654   for (i = 0; i < (GLuint) count; i++) {
655      shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
656   }
657   shProg->TransformFeedback.NumVarying = count;
658
659   shProg->TransformFeedback.BufferMode = bufferMode;
660
661   /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
662    * the varyings won't be used until shader link time.
663    */
664}
665
666
667/**
668 * Get info about the vertex shader's outputs which are to be written
669 * to the feedback buffer(s).
670 */
671void GLAPIENTRY
672_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
673                                  GLsizei bufSize, GLsizei *length,
674                                  GLsizei *size, GLenum *type, GLchar *name)
675{
676   const struct gl_shader_program *shProg;
677   const struct gl_transform_feedback_info *linked_xfb_info;
678   GET_CURRENT_CONTEXT(ctx);
679
680   shProg = _mesa_lookup_shader_program(ctx, program);
681   if (!shProg) {
682      _mesa_error(ctx, GL_INVALID_VALUE,
683                  "glGetTransformFeedbackVaryings(program=%u)", program);
684      return;
685   }
686
687   linked_xfb_info = &shProg->LinkedTransformFeedback;
688   if (index >= linked_xfb_info->NumVarying) {
689      _mesa_error(ctx, GL_INVALID_VALUE,
690                  "glGetTransformFeedbackVaryings(index=%u)", index);
691      return;
692   }
693
694   /* return the varying's name and length */
695   _mesa_copy_string(name, bufSize, length,
696		     linked_xfb_info->Varyings[index].Name);
697
698   /* return the datatype and value's size (in datatype units) */
699   if (type)
700      *type = linked_xfb_info->Varyings[index].Type;
701   if (size)
702      *size = linked_xfb_info->Varyings[index].Size;
703}
704
705
706
707struct gl_transform_feedback_object *
708_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
709{
710   if (name == 0) {
711      return ctx->TransformFeedback.DefaultObject;
712   }
713   else
714      return (struct gl_transform_feedback_object *)
715         _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
716}
717
718
719/**
720 * Create new transform feedback objects.   Transform feedback objects
721 * encapsulate the state related to transform feedback to allow quickly
722 * switching state (and drawing the results, below).
723 * Part of GL_ARB_transform_feedback2.
724 */
725void GLAPIENTRY
726_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
727{
728   GLuint first;
729   GET_CURRENT_CONTEXT(ctx);
730
731   ASSERT_OUTSIDE_BEGIN_END(ctx);
732
733   if (n < 0) {
734      _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
735      return;
736   }
737
738   if (!names)
739      return;
740
741   /* we don't need contiguous IDs, but this might be faster */
742   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
743   if (first) {
744      GLsizei i;
745      for (i = 0; i < n; i++) {
746         struct gl_transform_feedback_object *obj
747            = ctx->Driver.NewTransformFeedback(ctx, first + i);
748         if (!obj) {
749            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
750            return;
751         }
752         names[i] = first + i;
753         _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
754      }
755   }
756   else {
757      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
758   }
759}
760
761
762/**
763 * Is the given ID a transform feedback object?
764 * Part of GL_ARB_transform_feedback2.
765 */
766GLboolean GLAPIENTRY
767_mesa_IsTransformFeedback(GLuint name)
768{
769   GET_CURRENT_CONTEXT(ctx);
770
771   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
772
773   if (name && _mesa_lookup_transform_feedback_object(ctx, name))
774      return GL_TRUE;
775   else
776      return GL_FALSE;
777}
778
779
780/**
781 * Bind the given transform feedback object.
782 * Part of GL_ARB_transform_feedback2.
783 */
784void GLAPIENTRY
785_mesa_BindTransformFeedback(GLenum target, GLuint name)
786{
787   struct gl_transform_feedback_object *obj;
788   GET_CURRENT_CONTEXT(ctx);
789
790   if (target != GL_TRANSFORM_FEEDBACK) {
791      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
792      return;
793   }
794
795   if (ctx->TransformFeedback.CurrentObject->Active &&
796       !ctx->TransformFeedback.CurrentObject->Paused) {
797      _mesa_error(ctx, GL_INVALID_OPERATION,
798              "glBindTransformFeedback(transform is active, or not paused)");
799      return;
800   }
801
802   obj = _mesa_lookup_transform_feedback_object(ctx, name);
803   if (!obj) {
804      _mesa_error(ctx, GL_INVALID_OPERATION,
805                  "glBindTransformFeedback(name=%u)", name);
806      return;
807   }
808
809   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
810                                       obj);
811}
812
813
814/**
815 * Delete the given transform feedback objects.
816 * Part of GL_ARB_transform_feedback2.
817 */
818void GLAPIENTRY
819_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
820{
821   GLint i;
822   GET_CURRENT_CONTEXT(ctx);
823
824   ASSERT_OUTSIDE_BEGIN_END(ctx);
825
826   if (n < 0) {
827      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
828      return;
829   }
830
831   if (!names)
832      return;
833
834   for (i = 0; i < n; i++) {
835      if (names[i] > 0) {
836         struct gl_transform_feedback_object *obj
837            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
838         if (obj) {
839            if (obj->Active) {
840               _mesa_error(ctx, GL_INVALID_OPERATION,
841                           "glDeleteTransformFeedbacks(object %u is active)",
842                           names[i]);
843               return;
844            }
845            _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
846            /* unref, but object may not be deleted until later */
847            reference_transform_feedback_object(&obj, NULL);
848         }
849      }
850   }
851}
852
853
854/**
855 * Pause transform feedback.
856 * Part of GL_ARB_transform_feedback2.
857 */
858void GLAPIENTRY
859_mesa_PauseTransformFeedback(void)
860{
861   struct gl_transform_feedback_object *obj;
862   GET_CURRENT_CONTEXT(ctx);
863
864   obj = ctx->TransformFeedback.CurrentObject;
865
866   if (!obj->Active || obj->Paused) {
867      _mesa_error(ctx, GL_INVALID_OPERATION,
868           "glPauseTransformFeedback(feedback not active or already paused)");
869      return;
870   }
871
872   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
873   obj->Paused = GL_TRUE;
874
875   assert(ctx->Driver.PauseTransformFeedback);
876   ctx->Driver.PauseTransformFeedback(ctx, obj);
877}
878
879
880/**
881 * Resume transform feedback.
882 * Part of GL_ARB_transform_feedback2.
883 */
884void GLAPIENTRY
885_mesa_ResumeTransformFeedback(void)
886{
887   struct gl_transform_feedback_object *obj;
888   GET_CURRENT_CONTEXT(ctx);
889
890   obj = ctx->TransformFeedback.CurrentObject;
891
892   if (!obj->Active || !obj->Paused) {
893      _mesa_error(ctx, GL_INVALID_OPERATION,
894               "glResumeTransformFeedback(feedback not active or not paused)");
895      return;
896   }
897
898   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
899   obj->Paused = GL_FALSE;
900
901   assert(ctx->Driver.ResumeTransformFeedback);
902   ctx->Driver.ResumeTransformFeedback(ctx, obj);
903}
904
905#endif /* FEATURE_EXT_transform_feedback */
906