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