transformfeedback.c revision 05b086ce934fa2967da736db8db429d0886735a9
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_BindBufferRangeEXT(disp, _mesa_BindBufferRange);
295   SET_BindBufferBaseEXT(disp, _mesa_BindBufferBase);
296   SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT);
297   SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings);
298   SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying);
299   /* ARB_transform_feedback2 */
300   SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
301   SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
302   SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
303   SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
304   SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
305   SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
306}
307
308
309/**
310 ** Begin API functions
311 **/
312
313
314void GLAPIENTRY
315_mesa_BeginTransformFeedback(GLenum mode)
316{
317   struct gl_transform_feedback_object *obj;
318   struct gl_transform_feedback_info *info;
319   int i;
320   GET_CURRENT_CONTEXT(ctx);
321
322   obj = ctx->TransformFeedback.CurrentObject;
323
324   if (ctx->Shader.CurrentVertexProgram == NULL) {
325      _mesa_error(ctx, GL_INVALID_OPERATION,
326                  "glBeginTransformFeedback(no program active)");
327      return;
328   }
329
330   info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback;
331
332   if (info->NumOutputs == 0) {
333      _mesa_error(ctx, GL_INVALID_OPERATION,
334                  "glBeginTransformFeedback(no varyings to record)");
335      return;
336   }
337
338   switch (mode) {
339   case GL_POINTS:
340   case GL_LINES:
341   case GL_TRIANGLES:
342      /* legal */
343      break;
344   default:
345      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
346      return;
347   }
348
349   if (obj->Active) {
350      _mesa_error(ctx, GL_INVALID_OPERATION,
351                  "glBeginTransformFeedback(already active)");
352      return;
353   }
354
355   for (i = 0; i < info->NumBuffers; ++i) {
356      if (obj->BufferNames[i] == 0) {
357         _mesa_error(ctx, GL_INVALID_OPERATION,
358                     "glBeginTransformFeedback(binding point %d does not have "
359                     "a buffer object bound)", i);
360         return;
361      }
362   }
363
364   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
365   obj->Active = GL_TRUE;
366   ctx->TransformFeedback.Mode = mode;
367
368   assert(ctx->Driver.BeginTransformFeedback);
369   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
370}
371
372
373void GLAPIENTRY
374_mesa_EndTransformFeedback(void)
375{
376   struct gl_transform_feedback_object *obj;
377   GET_CURRENT_CONTEXT(ctx);
378
379   obj = ctx->TransformFeedback.CurrentObject;
380
381   if (!obj->Active) {
382      _mesa_error(ctx, GL_INVALID_OPERATION,
383                  "glEndTransformFeedback(not active)");
384      return;
385   }
386
387   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
388   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
389   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
390   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
391
392   assert(ctx->Driver.EndTransformFeedback);
393   ctx->Driver.EndTransformFeedback(ctx, obj);
394}
395
396
397/**
398 * Helper used by BindBufferRange() and BindBufferBase().
399 */
400static void
401bind_buffer_range(struct gl_context *ctx, GLuint index,
402                  struct gl_buffer_object *bufObj,
403                  GLintptr offset, GLsizeiptr size)
404{
405   struct gl_transform_feedback_object *obj =
406      ctx->TransformFeedback.CurrentObject;
407
408   /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
409    * transform feedback buffers can't be changed while transform feedback is
410    * active.
411    */
412
413   /* The general binding point */
414   _mesa_reference_buffer_object(ctx,
415                                 &ctx->TransformFeedback.CurrentBuffer,
416                                 bufObj);
417
418   /* The per-attribute binding point */
419   _mesa_reference_buffer_object(ctx,
420                                 &obj->Buffers[index],
421                                 bufObj);
422
423   obj->BufferNames[index] = bufObj->Name;
424
425   obj->Offset[index] = offset;
426   obj->Size[index] = size;
427}
428
429
430/**
431 * Specify a buffer object to receive vertex shader results.  Plus,
432 * specify the starting offset to place the results, and max size.
433 */
434void GLAPIENTRY
435_mesa_BindBufferRange(GLenum target, GLuint index,
436                      GLuint buffer, GLintptr offset, GLsizeiptr size)
437{
438   struct gl_transform_feedback_object *obj;
439   struct gl_buffer_object *bufObj;
440   GET_CURRENT_CONTEXT(ctx);
441
442   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
443      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
444      return;
445   }
446
447   obj = ctx->TransformFeedback.CurrentObject;
448
449   if (obj->Active) {
450      _mesa_error(ctx, GL_INVALID_OPERATION,
451                  "glBindBufferRange(transform feedback active)");
452      return;
453   }
454
455   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
456      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
457      return;
458   }
459
460   if ((size <= 0) || (size & 0x3)) {
461      /* must be positive and multiple of four */
462      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
463      return;
464   }
465
466   if (offset & 0x3) {
467      /* must be multiple of four */
468      _mesa_error(ctx, GL_INVALID_VALUE,
469                  "glBindBufferRange(offset=%d)", (int) offset);
470      return;
471   }
472
473   if (buffer == 0) {
474      bufObj = ctx->Shared->NullBufferObj;
475   } else {
476      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
477   }
478
479   if (!bufObj) {
480      _mesa_error(ctx, GL_INVALID_OPERATION,
481                  "glBindBufferRange(invalid buffer=%u)", buffer);
482      return;
483   }
484
485   if (offset + size > bufObj->Size) {
486      _mesa_error(ctx, GL_INVALID_VALUE,
487                  "glBindBufferRange(offset + size %d > buffer size %d)",
488		  (int) (offset + size), (int) (bufObj->Size));
489      return;
490   }
491
492   bind_buffer_range(ctx, index, bufObj, offset, size);
493}
494
495
496/**
497 * Specify a buffer object to receive vertex shader results.
498 * As above, but start at offset = 0.
499 */
500void GLAPIENTRY
501_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
502{
503   struct gl_transform_feedback_object *obj;
504   struct gl_buffer_object *bufObj;
505   GLsizeiptr size;
506   GET_CURRENT_CONTEXT(ctx);
507
508   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
509      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
510      return;
511   }
512
513   obj = ctx->TransformFeedback.CurrentObject;
514
515   if (obj->Active) {
516      _mesa_error(ctx, GL_INVALID_OPERATION,
517                  "glBindBufferBase(transform feedback active)");
518      return;
519   }
520
521   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
522      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
523      return;
524   }
525
526   if (buffer == 0) {
527      bufObj = ctx->Shared->NullBufferObj;
528   } else {
529      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
530   }
531
532   if (!bufObj) {
533      _mesa_error(ctx, GL_INVALID_OPERATION,
534                  "glBindBufferBase(invalid buffer=%u)", buffer);
535      return;
536   }
537
538   /* default size is the buffer size rounded down to nearest
539    * multiple of four.
540    */
541   size = bufObj->Size & ~0x3;
542
543   bind_buffer_range(ctx, index, bufObj, 0, size);
544}
545
546
547/**
548 * Specify a buffer object to receive vertex shader results, plus the
549 * offset in the buffer to start placing results.
550 * This function is part of GL_EXT_transform_feedback, but not GL3.
551 */
552void GLAPIENTRY
553_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
554                          GLintptr offset)
555{
556   struct gl_transform_feedback_object *obj;
557   struct gl_buffer_object *bufObj;
558   GET_CURRENT_CONTEXT(ctx);
559   GLsizeiptr size;
560
561   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
562      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
563      return;
564   }
565
566   obj = ctx->TransformFeedback.CurrentObject;
567
568   if (obj->Active) {
569      _mesa_error(ctx, GL_INVALID_OPERATION,
570                  "glBindBufferOffsetEXT(transform feedback active)");
571      return;
572   }
573
574   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
575      _mesa_error(ctx, GL_INVALID_VALUE,
576                  "glBindBufferOffsetEXT(index=%d)", index);
577      return;
578   }
579
580   if (offset & 0x3) {
581      /* must be multiple of four */
582      _mesa_error(ctx, GL_INVALID_VALUE,
583                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
584      return;
585   }
586
587   if (buffer == 0) {
588      bufObj = ctx->Shared->NullBufferObj;
589   } else {
590      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
591   }
592
593   if (!bufObj) {
594      _mesa_error(ctx, GL_INVALID_OPERATION,
595                  "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
596      return;
597   }
598
599   /* default size is the buffer size rounded down to nearest
600    * multiple of four.
601    */
602   size = (bufObj->Size - offset) & ~0x3;
603
604   bind_buffer_range(ctx, index, bufObj, offset, size);
605}
606
607
608/**
609 * This function specifies the vertex shader outputs to be written
610 * to the feedback buffer(s), and in what order.
611 */
612void GLAPIENTRY
613_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
614                                const GLchar **varyings, GLenum bufferMode)
615{
616   struct gl_shader_program *shProg;
617   GLuint i;
618   GET_CURRENT_CONTEXT(ctx);
619
620   switch (bufferMode) {
621   case GL_INTERLEAVED_ATTRIBS:
622      break;
623   case GL_SEPARATE_ATTRIBS:
624      break;
625   default:
626      _mesa_error(ctx, GL_INVALID_ENUM,
627                  "glTransformFeedbackVaryings(bufferMode)");
628      return;
629   }
630
631   if (count < 0 ||
632       (bufferMode == GL_SEPARATE_ATTRIBS &&
633        (GLuint) count > ctx->Const.MaxTransformFeedbackSeparateAttribs)) {
634      _mesa_error(ctx, GL_INVALID_VALUE,
635                  "glTransformFeedbackVaryings(count=%d)", count);
636      return;
637   }
638
639   shProg = _mesa_lookup_shader_program(ctx, program);
640   if (!shProg) {
641      _mesa_error(ctx, GL_INVALID_VALUE,
642                  "glTransformFeedbackVaryings(program=%u)", program);
643      return;
644   }
645
646   /* free existing varyings, if any */
647   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
648      free(shProg->TransformFeedback.VaryingNames[i]);
649   }
650   free(shProg->TransformFeedback.VaryingNames);
651
652   /* allocate new memory for varying names */
653   shProg->TransformFeedback.VaryingNames =
654      (GLchar **) malloc(count * sizeof(GLchar *));
655
656   if (!shProg->TransformFeedback.VaryingNames) {
657      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
658      return;
659   }
660
661   /* Save the new names and the count */
662   for (i = 0; i < (GLuint) count; i++) {
663      shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
664   }
665   shProg->TransformFeedback.NumVarying = count;
666
667   shProg->TransformFeedback.BufferMode = bufferMode;
668
669   /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
670    * the varyings won't be used until shader link time.
671    */
672}
673
674
675/**
676 * Get info about the vertex shader's outputs which are to be written
677 * to the feedback buffer(s).
678 */
679void GLAPIENTRY
680_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
681                                  GLsizei bufSize, GLsizei *length,
682                                  GLsizei *size, GLenum *type, GLchar *name)
683{
684   const struct gl_shader_program *shProg;
685   const struct gl_transform_feedback_info *linked_xfb_info;
686   GET_CURRENT_CONTEXT(ctx);
687
688   shProg = _mesa_lookup_shader_program(ctx, program);
689   if (!shProg) {
690      _mesa_error(ctx, GL_INVALID_VALUE,
691                  "glGetTransformFeedbackVaryings(program=%u)", program);
692      return;
693   }
694
695   linked_xfb_info = &shProg->LinkedTransformFeedback;
696   if (index >= linked_xfb_info->NumVarying) {
697      _mesa_error(ctx, GL_INVALID_VALUE,
698                  "glGetTransformFeedbackVaryings(index=%u)", index);
699      return;
700   }
701
702   /* return the varying's name and length */
703   _mesa_copy_string(name, bufSize, length,
704		     linked_xfb_info->Varyings[index].Name);
705
706   /* return the datatype and value's size (in datatype units) */
707   if (type)
708      *type = linked_xfb_info->Varyings[index].Type;
709   if (size)
710      *size = linked_xfb_info->Varyings[index].Size;
711}
712
713
714
715struct gl_transform_feedback_object *
716_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
717{
718   if (name == 0) {
719      return ctx->TransformFeedback.DefaultObject;
720   }
721   else
722      return (struct gl_transform_feedback_object *)
723         _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
724}
725
726
727/**
728 * Create new transform feedback objects.   Transform feedback objects
729 * encapsulate the state related to transform feedback to allow quickly
730 * switching state (and drawing the results, below).
731 * Part of GL_ARB_transform_feedback2.
732 */
733void GLAPIENTRY
734_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
735{
736   GLuint first;
737   GET_CURRENT_CONTEXT(ctx);
738
739   ASSERT_OUTSIDE_BEGIN_END(ctx);
740
741   if (n < 0) {
742      _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
743      return;
744   }
745
746   if (!names)
747      return;
748
749   /* we don't need contiguous IDs, but this might be faster */
750   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
751   if (first) {
752      GLsizei i;
753      for (i = 0; i < n; i++) {
754         struct gl_transform_feedback_object *obj
755            = ctx->Driver.NewTransformFeedback(ctx, first + i);
756         if (!obj) {
757            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
758            return;
759         }
760         names[i] = first + i;
761         _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
762      }
763   }
764   else {
765      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
766   }
767}
768
769
770/**
771 * Is the given ID a transform feedback object?
772 * Part of GL_ARB_transform_feedback2.
773 */
774GLboolean GLAPIENTRY
775_mesa_IsTransformFeedback(GLuint name)
776{
777   GET_CURRENT_CONTEXT(ctx);
778
779   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
780
781   if (name && _mesa_lookup_transform_feedback_object(ctx, name))
782      return GL_TRUE;
783   else
784      return GL_FALSE;
785}
786
787
788/**
789 * Bind the given transform feedback object.
790 * Part of GL_ARB_transform_feedback2.
791 */
792void GLAPIENTRY
793_mesa_BindTransformFeedback(GLenum target, GLuint name)
794{
795   struct gl_transform_feedback_object *obj;
796   GET_CURRENT_CONTEXT(ctx);
797
798   if (target != GL_TRANSFORM_FEEDBACK) {
799      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
800      return;
801   }
802
803   if (ctx->TransformFeedback.CurrentObject->Active &&
804       !ctx->TransformFeedback.CurrentObject->Paused) {
805      _mesa_error(ctx, GL_INVALID_OPERATION,
806              "glBindTransformFeedback(transform is active, or not paused)");
807      return;
808   }
809
810   obj = _mesa_lookup_transform_feedback_object(ctx, name);
811   if (!obj) {
812      _mesa_error(ctx, GL_INVALID_OPERATION,
813                  "glBindTransformFeedback(name=%u)", name);
814      return;
815   }
816
817   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
818                                       obj);
819}
820
821
822/**
823 * Delete the given transform feedback objects.
824 * Part of GL_ARB_transform_feedback2.
825 */
826void GLAPIENTRY
827_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
828{
829   GLint i;
830   GET_CURRENT_CONTEXT(ctx);
831
832   ASSERT_OUTSIDE_BEGIN_END(ctx);
833
834   if (n < 0) {
835      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
836      return;
837   }
838
839   if (!names)
840      return;
841
842   for (i = 0; i < n; i++) {
843      if (names[i] > 0) {
844         struct gl_transform_feedback_object *obj
845            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
846         if (obj) {
847            if (obj->Active) {
848               _mesa_error(ctx, GL_INVALID_OPERATION,
849                           "glDeleteTransformFeedbacks(object %u is active)",
850                           names[i]);
851               return;
852            }
853            _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
854            /* unref, but object may not be deleted until later */
855            reference_transform_feedback_object(&obj, NULL);
856         }
857      }
858   }
859}
860
861
862/**
863 * Pause transform feedback.
864 * Part of GL_ARB_transform_feedback2.
865 */
866void GLAPIENTRY
867_mesa_PauseTransformFeedback(void)
868{
869   struct gl_transform_feedback_object *obj;
870   GET_CURRENT_CONTEXT(ctx);
871
872   obj = ctx->TransformFeedback.CurrentObject;
873
874   if (!obj->Active || obj->Paused) {
875      _mesa_error(ctx, GL_INVALID_OPERATION,
876           "glPauseTransformFeedback(feedback not active or already paused)");
877      return;
878   }
879
880   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
881   obj->Paused = GL_TRUE;
882
883   assert(ctx->Driver.PauseTransformFeedback);
884   ctx->Driver.PauseTransformFeedback(ctx, obj);
885}
886
887
888/**
889 * Resume transform feedback.
890 * Part of GL_ARB_transform_feedback2.
891 */
892void GLAPIENTRY
893_mesa_ResumeTransformFeedback(void)
894{
895   struct gl_transform_feedback_object *obj;
896   GET_CURRENT_CONTEXT(ctx);
897
898   obj = ctx->TransformFeedback.CurrentObject;
899
900   if (!obj->Active || !obj->Paused) {
901      _mesa_error(ctx, GL_INVALID_OPERATION,
902               "glResumeTransformFeedback(feedback not active or not paused)");
903      return;
904   }
905
906   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
907   obj->Paused = GL_FALSE;
908
909   assert(ctx->Driver.ResumeTransformFeedback);
910   ctx->Driver.ResumeTransformFeedback(ctx, obj);
911}
912
913#endif /* FEATURE_EXT_transform_feedback */
914