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