transformfeedback.c revision 32a9b2799e5e1254fdf84af8248ea86e234d6dd4
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(GLcontext *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(GLcontext *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(GLcontext *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   GLcontext *ctx = (GLcontext *) 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(GLcontext *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(GLcontext *ctx, GLuint name);
204
205static void
206delete_transform_feedback(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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(GLcontext *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)", 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)", 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 > buffer size)", size);
479      return;
480   }
481
482   bind_buffer_range(ctx, index, bufObj, offset, size);
483}
484
485
486/**
487 * Specify a buffer object to receive vertex shader results.
488 * As above, but start at offset = 0.
489 */
490void GLAPIENTRY
491_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
492{
493   struct gl_transform_feedback_object *obj;
494   struct gl_buffer_object *bufObj;
495   GLsizeiptr size;
496   GET_CURRENT_CONTEXT(ctx);
497
498   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
499      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
500      return;
501   }
502
503   obj = ctx->TransformFeedback.CurrentObject;
504
505   if (obj->Active) {
506      _mesa_error(ctx, GL_INVALID_OPERATION,
507                  "glBindBufferRange(transform feedback active)");
508      return;
509   }
510
511   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
512      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
513      return;
514   }
515
516   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
517   if (!bufObj) {
518      _mesa_error(ctx, GL_INVALID_OPERATION,
519                  "glBindBufferBase(invalid buffer=%u)", buffer);
520      return;
521   }
522
523   /* default size is the buffer size rounded down to nearest
524    * multiple of four.
525    */
526   size = bufObj->Size & ~0x3;
527
528   bind_buffer_range(ctx, index, bufObj, 0, size);
529}
530
531
532/**
533 * Specify a buffer object to receive vertex shader results, plus the
534 * offset in the buffer to start placing results.
535 * This function is part of GL_EXT_transform_feedback, but not GL3.
536 */
537void GLAPIENTRY
538_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
539                          GLintptr offset)
540{
541   struct gl_transform_feedback_object *obj;
542   struct gl_buffer_object *bufObj;
543   GET_CURRENT_CONTEXT(ctx);
544   GLsizeiptr size;
545
546   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
547      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
548      return;
549   }
550
551   obj = ctx->TransformFeedback.CurrentObject;
552
553   if (obj->Active) {
554      _mesa_error(ctx, GL_INVALID_OPERATION,
555                  "glBindBufferRange(transform feedback active)");
556      return;
557   }
558
559   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
560      _mesa_error(ctx, GL_INVALID_VALUE,
561                  "glBindBufferOffsetEXT(index=%d)", index);
562      return;
563   }
564
565   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
566   if (!bufObj) {
567      _mesa_error(ctx, GL_INVALID_OPERATION,
568                  "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
569      return;
570   }
571
572   /* default size is the buffer size rounded down to nearest
573    * multiple of four.
574    */
575   size = (bufObj->Size - offset) & ~0x3;
576
577   bind_buffer_range(ctx, index, bufObj, offset, size);
578}
579
580
581/**
582 * This function specifies the vertex shader outputs to be written
583 * to the feedback buffer(s), and in what order.
584 */
585void GLAPIENTRY
586_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
587                                const GLchar **varyings, GLenum bufferMode)
588{
589   struct gl_shader_program *shProg;
590   GLuint i;
591   GET_CURRENT_CONTEXT(ctx);
592
593   switch (bufferMode) {
594   case GL_INTERLEAVED_ATTRIBS:
595      break;
596   case GL_SEPARATE_ATTRIBS:
597      break;
598   default:
599      _mesa_error(ctx, GL_INVALID_ENUM,
600                  "glTransformFeedbackVaryings(bufferMode)");
601      return;
602   }
603
604   if (count < 0 || count > ctx->Const.MaxTransformFeedbackSeparateAttribs) {
605      _mesa_error(ctx, GL_INVALID_VALUE,
606                  "glTransformFeedbackVaryings(count=%d)", count);
607      return;
608   }
609
610   shProg = _mesa_lookup_shader_program(ctx, program);
611   if (!shProg) {
612      _mesa_error(ctx, GL_INVALID_VALUE,
613                  "glTransformFeedbackVaryings(program=%u)", program);
614      return;
615   }
616
617   /* free existing varyings, if any */
618   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
619      free(shProg->TransformFeedback.VaryingNames[i]);
620   }
621   free(shProg->TransformFeedback.VaryingNames);
622
623   /* allocate new memory for varying names */
624   shProg->TransformFeedback.VaryingNames =
625      (GLchar **) malloc(count * sizeof(GLchar *));
626
627   if (!shProg->TransformFeedback.VaryingNames) {
628      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
629      return;
630   }
631
632   /* Save the new names and the count */
633   for (i = 0; i < (GLuint) count; i++) {
634      shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
635   }
636   shProg->TransformFeedback.NumVarying = count;
637
638   shProg->TransformFeedback.BufferMode = bufferMode;
639
640   /* The varyings won't be used until shader link time */
641}
642
643
644/**
645 * Get info about the vertex shader's outputs which are to be written
646 * to the feedback buffer(s).
647 */
648void GLAPIENTRY
649_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
650                                  GLsizei bufSize, GLsizei *length,
651                                  GLsizei *size, GLenum *type, GLchar *name)
652{
653   const struct gl_shader_program *shProg;
654   const GLchar *varyingName;
655   GLint v;
656   GET_CURRENT_CONTEXT(ctx);
657
658   shProg = _mesa_lookup_shader_program(ctx, program);
659   if (!shProg) {
660      _mesa_error(ctx, GL_INVALID_VALUE,
661                  "glGetTransformFeedbackVaryings(program=%u)", program);
662      return;
663   }
664
665   if (index >= shProg->TransformFeedback.NumVarying) {
666      _mesa_error(ctx, GL_INVALID_VALUE,
667                  "glGetTransformFeedbackVaryings(index=%u)", index);
668      return;
669   }
670
671   varyingName = shProg->TransformFeedback.VaryingNames[index];
672
673   v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName);
674   if (v >= 0) {
675      struct gl_program_parameter *param = &shProg->Varying->Parameters[v];
676
677      /* return the varying's name and length */
678      _mesa_copy_string(name, bufSize, length, varyingName);
679
680      /* return the datatype and value's size (in datatype units) */
681      if (type)
682         *type = param->DataType;
683      if (size)
684         *size = param->Size;
685   }
686   else {
687      name[0] = 0;
688      if (length)
689         *length = 0;
690      if (type)
691         *type = 0;
692      if (size)
693         *size = 0;
694   }
695}
696
697
698
699static struct gl_transform_feedback_object *
700lookup_transform_feedback_object(GLcontext *ctx, GLuint name)
701{
702   if (name == 0) {
703      return ctx->TransformFeedback.DefaultObject;
704   }
705   else
706      return (struct gl_transform_feedback_object *)
707         _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
708}
709
710
711/**
712 * Create new transform feedback objects.   Transform feedback objects
713 * encapsulate the state related to transform feedback to allow quickly
714 * switching state (and drawing the results, below).
715 * Part of GL_ARB_transform_feedback2.
716 */
717void GLAPIENTRY
718_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
719{
720   GLuint first;
721   GET_CURRENT_CONTEXT(ctx);
722
723   ASSERT_OUTSIDE_BEGIN_END(ctx);
724
725   if (n < 0) {
726      _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
727      return;
728   }
729
730   if (!names)
731      return;
732
733   /* we don't need contiguous IDs, but this might be faster */
734   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
735   if (first) {
736      GLsizei i;
737      for (i = 0; i < n; i++) {
738         struct gl_transform_feedback_object *obj
739            = ctx->Driver.NewTransformFeedback(ctx, first + i);
740         if (!obj) {
741            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
742            return;
743         }
744         names[i] = first + i;
745         _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
746      }
747   }
748   else {
749      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
750   }
751}
752
753
754/**
755 * Is the given ID a transform feedback object?
756 * Part of GL_ARB_transform_feedback2.
757 */
758GLboolean GLAPIENTRY
759_mesa_IsTransformFeedback(GLuint name)
760{
761   GET_CURRENT_CONTEXT(ctx);
762
763   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
764
765   if (name && lookup_transform_feedback_object(ctx, name))
766      return GL_TRUE;
767   else
768      return GL_FALSE;
769}
770
771
772/**
773 * Bind the given transform feedback object.
774 * Part of GL_ARB_transform_feedback2.
775 */
776void GLAPIENTRY
777_mesa_BindTransformFeedback(GLenum target, GLuint name)
778{
779   struct gl_transform_feedback_object *obj;
780   GET_CURRENT_CONTEXT(ctx);
781
782   if (target != GL_TRANSFORM_FEEDBACK) {
783      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
784      return;
785   }
786
787   if (ctx->TransformFeedback.CurrentObject->Active &&
788       !ctx->TransformFeedback.CurrentObject->Paused) {
789      _mesa_error(ctx, GL_INVALID_OPERATION,
790              "glBindTransformFeedback(transform is active, or not paused)");
791      return;
792   }
793
794   obj = lookup_transform_feedback_object(ctx, name);
795   if (!obj) {
796      _mesa_error(ctx, GL_INVALID_OPERATION,
797                  "glBindTransformFeedback(name=%u)", name);
798      return;
799   }
800
801   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
802                                       obj);
803}
804
805
806/**
807 * Delete the given transform feedback objects.
808 * Part of GL_ARB_transform_feedback2.
809 */
810void GLAPIENTRY
811_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
812{
813   GLint i;
814   GET_CURRENT_CONTEXT(ctx);
815
816   ASSERT_OUTSIDE_BEGIN_END(ctx);
817
818   if (n < 0) {
819      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
820      return;
821   }
822
823   if (!names)
824      return;
825
826   for (i = 0; i < n; i++) {
827      if (names[i] > 0) {
828         struct gl_transform_feedback_object *obj
829            = lookup_transform_feedback_object(ctx, names[i]);
830         if (obj) {
831            if (obj->Active) {
832               _mesa_error(ctx, GL_INVALID_OPERATION,
833                           "glDeleteTransformFeedbacks(object %u is active)",
834                           names[i]);
835               return;
836            }
837            _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
838            /* unref, but object may not be deleted until later */
839            reference_transform_feedback_object(&obj, NULL);
840         }
841      }
842   }
843}
844
845
846/**
847 * Pause transform feedback.
848 * Part of GL_ARB_transform_feedback2.
849 */
850void GLAPIENTRY
851_mesa_PauseTransformFeedback(void)
852{
853   struct gl_transform_feedback_object *obj;
854   GET_CURRENT_CONTEXT(ctx);
855
856   obj = ctx->TransformFeedback.CurrentObject;
857
858   if (!obj->Active || obj->Paused) {
859      _mesa_error(ctx, GL_INVALID_OPERATION,
860           "glPauseTransformFeedback(feedback not active or already paused)");
861      return;
862   }
863
864   obj->Paused = GL_TRUE;
865
866   assert(ctx->Driver.PauseTransformFeedback);
867   ctx->Driver.PauseTransformFeedback(ctx, obj);
868}
869
870
871/**
872 * Resume transform feedback.
873 * Part of GL_ARB_transform_feedback2.
874 */
875void GLAPIENTRY
876_mesa_ResumeTransformFeedback(void)
877{
878   struct gl_transform_feedback_object *obj;
879   GET_CURRENT_CONTEXT(ctx);
880
881   obj = ctx->TransformFeedback.CurrentObject;
882
883   if (!obj->Active || !obj->Paused) {
884      _mesa_error(ctx, GL_INVALID_OPERATION,
885               "glPauseTransformFeedback(feedback not active or not paused)");
886      return;
887   }
888
889   obj->Paused = GL_FALSE;
890
891   assert(ctx->Driver.ResumeTransformFeedback);
892   ctx->Driver.ResumeTransformFeedback(ctx, obj);
893}
894
895
896/**
897 * Draw the vertex data in a transform feedback object.
898 * \param mode  GL_POINTS, GL_LINES, GL_TRIANGLE_STRIP, etc.
899 * \param name  the transform feedback object
900 * The number of vertices comes from the transform feedback object.
901 * User still has to setup of the vertex attribute info with
902 * glVertexPointer, glColorPointer, etc.
903 * Part of GL_ARB_transform_feedback2.
904 */
905void GLAPIENTRY
906_mesa_DrawTransformFeedback(GLenum mode, GLuint name)
907{
908   GET_CURRENT_CONTEXT(ctx);
909   struct gl_transform_feedback_object *obj =
910      lookup_transform_feedback_object(ctx, name);
911
912   if (mode > GL_POLYGON) {
913      _mesa_error(ctx, GL_INVALID_ENUM,
914                  "glDrawTransformFeedback(mode=0x%x)", mode);
915      return;
916   }
917   if (!obj) {
918      _mesa_error(ctx, GL_INVALID_VALUE,
919                  "glDrawTransformFeedback(name = %u)", name);
920      return;
921   }
922
923   /* XXX check if EndTransformFeedback has never been called while
924    * the object was bound
925    */
926
927   assert(ctx->Driver.DrawTransformFeedback);
928   ctx->Driver.DrawTransformFeedback(ctx, mode, obj);
929}
930
931
932/*
933XXX misc to do:
934
935glGet*() for
936
937GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED
938GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE
939GL_TRANSFORM_FEEDBACK_BINDING
940*/
941
942
943#endif /* FEATURE_EXT_transform_feedback */
944