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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26/*
27 * Transform feedback support.
28 *
29 * Authors:
30 *   Brian Paul
31 */
32
33
34#include "buffers.h"
35#include "context.h"
36#include "hash.h"
37#include "macros.h"
38#include "mtypes.h"
39#include "transformfeedback.h"
40#include "shaderapi.h"
41#include "shaderobj.h"
42
43#include "program/program.h"
44#include "program/prog_parameter.h"
45
46struct using_program_tuple
47{
48   struct gl_program *prog;
49   bool found;
50};
51
52static void
53active_xfb_object_references_program(GLuint key, void *data, void *user_data)
54{
55   struct using_program_tuple *callback_data = user_data;
56   struct gl_transform_feedback_object *obj = data;
57   if (obj->Active && obj->program == callback_data->prog)
58      callback_data->found = true;
59}
60
61/**
62 * Return true if any active transform feedback object is using a program.
63 */
64bool
65_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
66                                          struct gl_shader_program *shProg)
67{
68   if (!shProg->last_vert_prog)
69      return false;
70
71   struct using_program_tuple callback_data;
72   callback_data.found = false;
73   callback_data.prog = shProg->last_vert_prog;
74
75   _mesa_HashWalkLocked(ctx->TransformFeedback.Objects,
76                        active_xfb_object_references_program, &callback_data);
77
78   /* Also check DefaultObject, as it's not in the Objects hash table. */
79   active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject,
80                                        &callback_data);
81
82   return callback_data.found;
83}
84
85/**
86 * Do reference counting of transform feedback buffers.
87 */
88static void
89reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
90                                    struct gl_transform_feedback_object *obj)
91{
92   if (*ptr == obj)
93      return;
94
95   if (*ptr) {
96      /* Unreference the old object */
97      struct gl_transform_feedback_object *oldObj = *ptr;
98
99      assert(oldObj->RefCount > 0);
100      oldObj->RefCount--;
101
102      if (oldObj->RefCount == 0) {
103         GET_CURRENT_CONTEXT(ctx);
104         if (ctx)
105            ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
106      }
107
108      *ptr = NULL;
109   }
110   assert(!*ptr);
111
112   if (obj) {
113      assert(obj->RefCount > 0);
114
115      /* reference new object */
116      obj->RefCount++;
117      obj->EverBound = GL_TRUE;
118      *ptr = obj;
119   }
120}
121
122
123/**
124 * Per-context init for transform feedback.
125 */
126void
127_mesa_init_transform_feedback(struct gl_context *ctx)
128{
129   /* core mesa expects this, even a dummy one, to be available */
130   assert(ctx->Driver.NewTransformFeedback);
131
132   ctx->TransformFeedback.DefaultObject =
133      ctx->Driver.NewTransformFeedback(ctx, 0);
134
135   assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
136
137   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
138                                       ctx->TransformFeedback.DefaultObject);
139
140   assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
141
142   ctx->TransformFeedback.Objects = _mesa_NewHashTable();
143
144   _mesa_reference_buffer_object(ctx,
145                                 &ctx->TransformFeedback.CurrentBuffer,
146                                 ctx->Shared->NullBufferObj);
147}
148
149
150
151/**
152 * Callback for _mesa_HashDeleteAll().
153 */
154static void
155delete_cb(GLuint key, void *data, void *userData)
156{
157   struct gl_context *ctx = (struct gl_context *) userData;
158   struct gl_transform_feedback_object *obj =
159      (struct gl_transform_feedback_object *) data;
160
161   ctx->Driver.DeleteTransformFeedback(ctx, obj);
162}
163
164
165/**
166 * Per-context free/clean-up for transform feedback.
167 */
168void
169_mesa_free_transform_feedback(struct gl_context *ctx)
170{
171   /* core mesa expects this, even a dummy one, to be available */
172   assert(ctx->Driver.NewTransformFeedback);
173
174   _mesa_reference_buffer_object(ctx,
175                                 &ctx->TransformFeedback.CurrentBuffer,
176                                 NULL);
177
178   /* Delete all feedback objects */
179   _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
180   _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
181
182   /* Delete the default feedback object */
183   assert(ctx->Driver.DeleteTransformFeedback);
184   ctx->Driver.DeleteTransformFeedback(ctx,
185                                       ctx->TransformFeedback.DefaultObject);
186
187   ctx->TransformFeedback.CurrentObject = NULL;
188}
189
190
191/** Initialize the fields of a gl_transform_feedback_object. */
192void
193_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
194                                     GLuint name)
195{
196   obj->Name = name;
197   obj->RefCount = 1;
198   obj->EverBound = GL_FALSE;
199}
200
201
202/** Default fallback for ctx->Driver.NewTransformFeedback() */
203static struct gl_transform_feedback_object *
204new_transform_feedback_fallback(struct gl_context *ctx, GLuint name)
205{
206   struct gl_transform_feedback_object *obj;
207
208   obj = CALLOC_STRUCT(gl_transform_feedback_object);
209   if (!obj)
210      return NULL;
211
212   _mesa_init_transform_feedback_object(obj, name);
213   return obj;
214}
215
216/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
217static void
218delete_transform_feedback_fallback(struct gl_context *ctx,
219                                   struct gl_transform_feedback_object *obj)
220{
221   GLuint i;
222
223   for (i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
224      _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
225   }
226
227   free(obj->Label);
228   free(obj);
229}
230
231
232/** Default fallback for ctx->Driver.BeginTransformFeedback() */
233static void
234begin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode,
235                                  struct gl_transform_feedback_object *obj)
236{
237   /* nop */
238}
239
240/** Default fallback for ctx->Driver.EndTransformFeedback() */
241static void
242end_transform_feedback_fallback(struct gl_context *ctx,
243                                struct gl_transform_feedback_object *obj)
244{
245   /* nop */
246}
247
248/** Default fallback for ctx->Driver.PauseTransformFeedback() */
249static void
250pause_transform_feedback_fallback(struct gl_context *ctx,
251                                  struct gl_transform_feedback_object *obj)
252{
253   /* nop */
254}
255
256/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
257static void
258resume_transform_feedback_fallback(struct gl_context *ctx,
259                                   struct gl_transform_feedback_object *obj)
260{
261   /* nop */
262}
263
264
265/**
266 * Plug in default device driver functions for transform feedback.
267 * Most drivers will override some/all of these.
268 */
269void
270_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
271{
272   driver->NewTransformFeedback = new_transform_feedback_fallback;
273   driver->DeleteTransformFeedback = delete_transform_feedback_fallback;
274   driver->BeginTransformFeedback = begin_transform_feedback_fallback;
275   driver->EndTransformFeedback = end_transform_feedback_fallback;
276   driver->PauseTransformFeedback = pause_transform_feedback_fallback;
277   driver->ResumeTransformFeedback = resume_transform_feedback_fallback;
278}
279
280
281/**
282 * Fill in the correct Size value for each buffer in \c obj.
283 *
284 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
285 * Targets"):
286 *
287 *   BindBufferBase binds the entire buffer, even when the size of the buffer
288 *   is changed after the binding is established. It is equivalent to calling
289 *   BindBufferRange with offset zero, while size is determined by the size of
290 *   the bound buffer at the time the binding is used.
291 *
292 *   Regardless of the size specified with BindBufferRange, or indirectly with
293 *   BindBufferBase, the GL will never read or write beyond the end of a bound
294 *   buffer. In some cases this constraint may result in visibly different
295 *   behavior when a buffer overflow would otherwise result, such as described
296 *   for transform feedback operations in section 13.2.2.
297 */
298static void
299compute_transform_feedback_buffer_sizes(
300      struct gl_transform_feedback_object *obj)
301{
302   unsigned i = 0;
303   for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
304      GLintptr offset = obj->Offset[i];
305      GLsizeiptr buffer_size
306         = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
307      GLsizeiptr available_space
308         = buffer_size <= offset ? 0 : buffer_size - offset;
309      GLsizeiptr computed_size;
310      if (obj->RequestedSize[i] == 0) {
311         /* No size was specified at the time the buffer was bound, so allow
312          * writing to all available space in the buffer.
313          */
314         computed_size = available_space;
315      } else {
316         /* A size was specified at the time the buffer was bound, however
317          * it's possible that the buffer has shrunk since then.  So only
318          * allow writing to the minimum of the specified size and the space
319          * available.
320          */
321         computed_size = MIN2(available_space, obj->RequestedSize[i]);
322      }
323
324      /* Legal sizes must be multiples of four, so round down if necessary. */
325      obj->Size[i] = computed_size & ~0x3;
326   }
327}
328
329
330/**
331 * Compute the maximum number of vertices that can be written to the currently
332 * enabled transform feedback buffers without overflowing any of them.
333 */
334unsigned
335_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
336      const struct gl_transform_feedback_object *obj,
337      const struct gl_transform_feedback_info *info)
338{
339   unsigned max_index = 0xffffffff;
340   unsigned i;
341
342   for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
343      if ((info->ActiveBuffers >> i) & 1) {
344         unsigned stride = info->Buffers[i].Stride;
345         unsigned max_for_this_buffer;
346
347         /* Skip any inactive buffers, which have a stride of 0. */
348         if (stride == 0)
349            continue;
350
351         max_for_this_buffer = obj->Size[i] / (4 * stride);
352         max_index = MIN2(max_index, max_for_this_buffer);
353      }
354   }
355
356   return max_index;
357}
358
359
360/**
361 ** Begin API functions
362 **/
363
364
365/**
366 * Figure out which stage of the pipeline is the source of transform feedback
367 * data given the current context state, and return its gl_program.
368 *
369 * If no active program can generate transform feedback data (i.e. no vertex
370 * shader is active), returns NULL.
371 */
372static struct gl_program *
373get_xfb_source(struct gl_context *ctx)
374{
375   int i;
376   for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
377      if (ctx->_Shader->CurrentProgram[i] != NULL)
378         return ctx->_Shader->CurrentProgram[i];
379   }
380   return NULL;
381}
382
383
384static ALWAYS_INLINE void
385begin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
386{
387   struct gl_transform_feedback_object *obj;
388   struct gl_transform_feedback_info *info = NULL;
389   struct gl_program *source;
390   GLuint i;
391   unsigned vertices_per_prim;
392
393   obj = ctx->TransformFeedback.CurrentObject;
394
395   /* Figure out what pipeline stage is the source of data for transform
396    * feedback.
397    */
398   source = get_xfb_source(ctx);
399   if (!no_error && source == NULL) {
400      _mesa_error(ctx, GL_INVALID_OPERATION,
401                  "glBeginTransformFeedback(no program active)");
402      return;
403   }
404
405   info = source->sh.LinkedTransformFeedback;
406
407   if (!no_error && info->NumOutputs == 0) {
408      _mesa_error(ctx, GL_INVALID_OPERATION,
409                  "glBeginTransformFeedback(no varyings to record)");
410      return;
411   }
412
413   switch (mode) {
414   case GL_POINTS:
415      vertices_per_prim = 1;
416      break;
417   case GL_LINES:
418      vertices_per_prim = 2;
419      break;
420   case GL_TRIANGLES:
421      vertices_per_prim = 3;
422      break;
423   default:
424      if (!no_error) {
425         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
426         return;
427      } else {
428         /* Stop compiler warnings */
429         unreachable("Error in API use when using KHR_no_error");
430      }
431   }
432
433   if (!no_error) {
434      if (obj->Active) {
435         _mesa_error(ctx, GL_INVALID_OPERATION,
436                     "glBeginTransformFeedback(already active)");
437         return;
438      }
439
440      for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
441         if ((info->ActiveBuffers >> i) & 1) {
442            if (obj->BufferNames[i] == 0) {
443               _mesa_error(ctx, GL_INVALID_OPERATION,
444                           "glBeginTransformFeedback(binding point %d does not "
445                           "have a buffer object bound)", i);
446               return;
447            }
448         }
449      }
450   }
451
452   FLUSH_VERTICES(ctx, 0);
453   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
454
455   obj->Active = GL_TRUE;
456   ctx->TransformFeedback.Mode = mode;
457
458   compute_transform_feedback_buffer_sizes(obj);
459
460   if (_mesa_is_gles3(ctx)) {
461      /* In GLES3, we are required to track the usage of the transform
462       * feedback buffer and report INVALID_OPERATION if a draw call tries to
463       * exceed it.  So compute the maximum number of vertices that we can
464       * write without overflowing any of the buffers currently being used for
465       * feedback.
466       */
467      unsigned max_vertices
468         = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
469      obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
470   }
471
472   if (obj->program != source) {
473      ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
474      _mesa_reference_program_(ctx, &obj->program, source);
475      obj->program = source;
476   }
477
478   assert(ctx->Driver.BeginTransformFeedback);
479   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
480}
481
482
483void GLAPIENTRY
484_mesa_BeginTransformFeedback_no_error(GLenum mode)
485{
486   GET_CURRENT_CONTEXT(ctx);
487   begin_transform_feedback(ctx, mode, true);
488}
489
490
491void GLAPIENTRY
492_mesa_BeginTransformFeedback(GLenum mode)
493{
494   GET_CURRENT_CONTEXT(ctx);
495   begin_transform_feedback(ctx, mode, false);
496}
497
498
499static void
500end_transform_feedback(struct gl_context *ctx,
501                       struct gl_transform_feedback_object *obj)
502{
503   FLUSH_VERTICES(ctx, 0);
504   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
505
506   assert(ctx->Driver.EndTransformFeedback);
507   ctx->Driver.EndTransformFeedback(ctx, obj);
508
509   _mesa_reference_program_(ctx, &obj->program, NULL);
510   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
511   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
512   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
513}
514
515
516void GLAPIENTRY
517_mesa_EndTransformFeedback_no_error(void)
518{
519   GET_CURRENT_CONTEXT(ctx);
520   end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
521}
522
523
524void GLAPIENTRY
525_mesa_EndTransformFeedback(void)
526{
527   struct gl_transform_feedback_object *obj;
528   GET_CURRENT_CONTEXT(ctx);
529
530   obj = ctx->TransformFeedback.CurrentObject;
531
532   if (!obj->Active) {
533      _mesa_error(ctx, GL_INVALID_OPERATION,
534                  "glEndTransformFeedback(not active)");
535      return;
536   }
537
538   end_transform_feedback(ctx, obj);
539}
540
541
542/**
543 * Helper used by BindBufferRange() and BindBufferBase().
544 */
545static void
546bind_buffer_range(struct gl_context *ctx,
547                  struct gl_transform_feedback_object *obj,
548                  GLuint index,
549                  struct gl_buffer_object *bufObj,
550                  GLintptr offset, GLsizeiptr size,
551                  bool dsa)
552{
553   /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
554    * transform feedback buffers can't be changed while transform feedback is
555    * active.
556    */
557
558   if (!dsa) {
559      /* The general binding point */
560      _mesa_reference_buffer_object(ctx,
561                                    &ctx->TransformFeedback.CurrentBuffer,
562                                    bufObj);
563   }
564
565   /* The per-attribute binding point */
566   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
567}
568
569
570/**
571 * Validate the buffer object to receive transform feedback results.  Plus,
572 * validate the starting offset to place the results, and max size.
573 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
574 * functions.
575 */
576bool
577_mesa_validate_buffer_range_xfb(struct gl_context *ctx,
578                                struct gl_transform_feedback_object *obj,
579                                GLuint index, struct gl_buffer_object *bufObj,
580                                GLintptr offset, GLsizeiptr size, bool dsa)
581{
582   const char *gl_methd_name;
583   if (dsa)
584      gl_methd_name = "glTransformFeedbackBufferRange";
585   else
586      gl_methd_name = "glBindBufferRange";
587
588
589   if (obj->Active) {
590      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
591                  gl_methd_name);
592      return false;
593   }
594
595   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
596      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
597       * generated if index is greater than or equal to the number of binding
598       * points for transform feedback, as described in section 6.7.1."
599       */
600      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
601                  gl_methd_name, index);
602      return false;
603   }
604
605   if (size & 0x3) {
606      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
607      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
608                  "four)", gl_methd_name, (int) size);
609      return false;
610   }
611
612   if (offset & 0x3) {
613      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
614      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
615                  "of four)", gl_methd_name, (int) offset);
616      return false;
617   }
618
619   if (offset < 0) {
620      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
621       * generated by BindBufferRange if offset is negative."
622       *
623       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
624       * is generated by TransformFeedbackBufferRange if offset is negative."
625       */
626      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
627                  gl_methd_name,
628                  (int) offset);
629      return false;
630   }
631
632   if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) {
633      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
634       * generated by BindBufferRange if buffer is non-zero and size is less
635       * than or equal to zero."
636       *
637       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
638       * is generated by TransformFeedbackBufferRange if size is less than or
639       * equal to zero."
640       */
641      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
642                  gl_methd_name, (int) size);
643      return false;
644   }
645
646   return true;
647}
648
649
650/**
651 * Specify a buffer object to receive transform feedback results.
652 * As above, but start at offset = 0.
653 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
654 * functions.
655 */
656void
657_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
658                                          struct gl_transform_feedback_object *obj,
659                                          GLuint index,
660                                          struct gl_buffer_object *bufObj,
661                                          bool dsa)
662{
663   if (obj->Active) {
664      _mesa_error(ctx, GL_INVALID_OPERATION,
665                  "%s(transform feedback active)",
666                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
667      return;
668   }
669
670   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
671      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
672                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
673                  index);
674      return;
675   }
676
677   bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
678}
679
680/**
681 * Wrapper around lookup_transform_feedback_object that throws
682 * GL_INVALID_OPERATION if id is not in the hash table. After calling
683 * _mesa_error, it returns NULL.
684 */
685static struct gl_transform_feedback_object *
686lookup_transform_feedback_object_err(struct gl_context *ctx,
687                                     GLuint xfb, const char* func)
688{
689   struct gl_transform_feedback_object *obj;
690
691   obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
692   if (!obj) {
693      _mesa_error(ctx, GL_INVALID_OPERATION,
694                  "%s(xfb=%u: non-generated object name)", func, xfb);
695   }
696
697   return obj;
698}
699
700/**
701 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
702 * is not in the hash table. Specialised version for the
703 * transform-feedback-related functions. After calling _mesa_error, it
704 * returns NULL.
705 */
706static struct gl_buffer_object *
707lookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
708                                        GLuint buffer, const char* func)
709{
710   struct gl_buffer_object *bufObj;
711
712   /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
713    * name of an existing buffer object.
714    */
715   if (buffer == 0) {
716      bufObj = ctx->Shared->NullBufferObj;
717   } else {
718      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
719      if (!bufObj) {
720         _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
721                     buffer);
722      }
723   }
724
725   return bufObj;
726}
727
728void GLAPIENTRY
729_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
730{
731   GET_CURRENT_CONTEXT(ctx);
732   struct gl_transform_feedback_object *obj;
733   struct gl_buffer_object *bufObj;
734
735   obj = lookup_transform_feedback_object_err(ctx, xfb,
736                                              "glTransformFeedbackBufferBase");
737   if (!obj) {
738      return;
739   }
740
741   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
742                                              "glTransformFeedbackBufferBase");
743   if (!bufObj) {
744      return;
745   }
746
747   _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
748}
749
750void GLAPIENTRY
751_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
752                                   GLintptr offset, GLsizeiptr size)
753{
754   GET_CURRENT_CONTEXT(ctx);
755   struct gl_transform_feedback_object *obj;
756   struct gl_buffer_object *bufObj;
757
758   obj = lookup_transform_feedback_object_err(ctx, xfb,
759                                              "glTransformFeedbackBufferRange");
760   if (!obj) {
761      return;
762   }
763
764   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
765                                              "glTransformFeedbackBufferRange");
766   if (!bufObj) {
767      return;
768   }
769
770   if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
771                                        size, true))
772      return;
773
774   /* The per-attribute binding point */
775   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
776                                        size);
777}
778
779/**
780 * Specify a buffer object to receive transform feedback results, plus the
781 * offset in the buffer to start placing results.
782 * This function is part of GL_EXT_transform_feedback, but not GL3.
783 */
784static ALWAYS_INLINE void
785bind_buffer_offset(struct gl_context *ctx,
786                   struct gl_transform_feedback_object *obj, GLuint index,
787                   GLuint buffer, GLintptr offset, bool no_error)
788{
789   struct gl_buffer_object *bufObj;
790
791   if (buffer == 0) {
792      bufObj = ctx->Shared->NullBufferObj;
793   } else {
794      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
795      if (!no_error && !bufObj) {
796         _mesa_error(ctx, GL_INVALID_OPERATION,
797                     "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
798         return;
799      }
800   }
801
802   _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
803}
804
805
806void GLAPIENTRY
807_mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer,
808                                   GLintptr offset)
809{
810   GET_CURRENT_CONTEXT(ctx);
811   bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer,
812                      offset, true);
813}
814
815
816void GLAPIENTRY
817_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
818                          GLintptr offset)
819{
820   struct gl_transform_feedback_object *obj;
821   GET_CURRENT_CONTEXT(ctx);
822
823   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
824      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
825      return;
826   }
827
828   obj = ctx->TransformFeedback.CurrentObject;
829
830   if (obj->Active) {
831      _mesa_error(ctx, GL_INVALID_OPERATION,
832                  "glBindBufferOffsetEXT(transform feedback active)");
833      return;
834   }
835
836   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
837      _mesa_error(ctx, GL_INVALID_VALUE,
838                  "glBindBufferOffsetEXT(index=%d)", index);
839      return;
840   }
841
842   if (offset & 0x3) {
843      /* must be multiple of four */
844      _mesa_error(ctx, GL_INVALID_VALUE,
845                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
846      return;
847   }
848
849   bind_buffer_offset(ctx, obj, index, buffer, offset, false);
850}
851
852
853/**
854 * This function specifies the transform feedback outputs to be written
855 * to the feedback buffer(s), and in what order.
856 */
857static ALWAYS_INLINE void
858transform_feedback_varyings(struct gl_context *ctx,
859                            struct gl_shader_program *shProg, GLsizei count,
860                            const GLchar *const *varyings, GLenum bufferMode)
861{
862   GLint i;
863
864   /* free existing varyings, if any */
865   for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
866      free(shProg->TransformFeedback.VaryingNames[i]);
867   }
868   free(shProg->TransformFeedback.VaryingNames);
869
870   /* allocate new memory for varying names */
871   shProg->TransformFeedback.VaryingNames =
872      malloc(count * sizeof(GLchar *));
873
874   if (!shProg->TransformFeedback.VaryingNames) {
875      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
876      return;
877   }
878
879   /* Save the new names and the count */
880   for (i = 0; i < count; i++) {
881      shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
882   }
883   shProg->TransformFeedback.NumVarying = count;
884
885   shProg->TransformFeedback.BufferMode = bufferMode;
886
887   /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
888    * the varyings won't be used until shader link time.
889    */
890}
891
892
893void GLAPIENTRY
894_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count,
895                                         const GLchar *const *varyings,
896                                         GLenum bufferMode)
897{
898   GET_CURRENT_CONTEXT(ctx);
899
900   struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
901   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
902}
903
904void GLAPIENTRY
905_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
906                                const GLchar * const *varyings,
907                                GLenum bufferMode)
908{
909   struct gl_shader_program *shProg;
910   GLint i;
911   GET_CURRENT_CONTEXT(ctx);
912
913   /* From the ARB_transform_feedback2 specification:
914    * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
915    *  if the current transform feedback object is active, even if paused."
916    */
917   if (ctx->TransformFeedback.CurrentObject->Active) {
918      _mesa_error(ctx, GL_INVALID_OPERATION,
919               "glTransformFeedbackVaryings(current object is active)");
920      return;
921   }
922
923   switch (bufferMode) {
924   case GL_INTERLEAVED_ATTRIBS:
925      break;
926   case GL_SEPARATE_ATTRIBS:
927      break;
928   default:
929      _mesa_error(ctx, GL_INVALID_ENUM,
930                  "glTransformFeedbackVaryings(bufferMode)");
931      return;
932   }
933
934   if (count < 0 ||
935       (bufferMode == GL_SEPARATE_ATTRIBS &&
936        (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
937      _mesa_error(ctx, GL_INVALID_VALUE,
938                  "glTransformFeedbackVaryings(count=%d)", count);
939      return;
940   }
941
942   shProg = _mesa_lookup_shader_program_err(ctx, program,
943                                            "glTransformFeedbackVaryings");
944   if (!shProg)
945      return;
946
947   if (ctx->Extensions.ARB_transform_feedback3) {
948      if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
949         unsigned buffers = 1;
950
951         for (i = 0; i < count; i++) {
952            if (strcmp(varyings[i], "gl_NextBuffer") == 0)
953               buffers++;
954         }
955
956         if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
957            _mesa_error(ctx, GL_INVALID_OPERATION,
958                        "glTransformFeedbackVaryings(too many gl_NextBuffer "
959                        "occurrences)");
960            return;
961         }
962      } else {
963         for (i = 0; i < count; i++) {
964            if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
965                strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
966                strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
967                strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
968                strcmp(varyings[i], "gl_SkipComponents4") == 0) {
969               _mesa_error(ctx, GL_INVALID_OPERATION,
970                           "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
971                           "varying=%s)",
972                           varyings[i]);
973               return;
974            }
975         }
976      }
977   }
978
979   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
980}
981
982
983/**
984 * Get info about the transform feedback outputs which are to be written
985 * to the feedback buffer(s).
986 */
987void GLAPIENTRY
988_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
989                                  GLsizei bufSize, GLsizei *length,
990                                  GLsizei *size, GLenum *type, GLchar *name)
991{
992   const struct gl_shader_program *shProg;
993   struct gl_program_resource *res;
994   GET_CURRENT_CONTEXT(ctx);
995
996   shProg = _mesa_lookup_shader_program_err(ctx, program,
997                                            "glGetTransformFeedbackVarying");
998   if (!shProg)
999      return;
1000
1001   res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
1002                                           GL_TRANSFORM_FEEDBACK_VARYING,
1003                                           index);
1004   if (!res) {
1005      _mesa_error(ctx, GL_INVALID_VALUE,
1006                  "glGetTransformFeedbackVarying(index=%u)", index);
1007      return;
1008   }
1009
1010   /* return the varying's name and length */
1011   _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
1012
1013   /* return the datatype and value's size (in datatype units) */
1014   if (type)
1015      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1016                                  res, index, GL_TYPE, (GLint*) type,
1017                                  "glGetTransformFeedbackVarying");
1018   if (size)
1019      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1020                                  res, index, GL_ARRAY_SIZE, (GLint*) size,
1021                                  "glGetTransformFeedbackVarying");
1022}
1023
1024
1025
1026struct gl_transform_feedback_object *
1027_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
1028{
1029   /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
1030    * the default transform feedback object, or the name of an existing
1031    * transform feedback object."
1032    */
1033   if (name == 0) {
1034      return ctx->TransformFeedback.DefaultObject;
1035   }
1036   else
1037      return (struct gl_transform_feedback_object *)
1038         _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
1039}
1040
1041static void
1042create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
1043                           bool dsa)
1044{
1045   GLuint first;
1046   const char* func;
1047
1048   if (dsa)
1049      func = "glCreateTransformFeedbacks";
1050   else
1051      func = "glGenTransformFeedbacks";
1052
1053   if (n < 0) {
1054      _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
1055      return;
1056   }
1057
1058   if (!ids)
1059      return;
1060
1061   /* we don't need contiguous IDs, but this might be faster */
1062   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
1063   if (first) {
1064      GLsizei i;
1065      for (i = 0; i < n; i++) {
1066         struct gl_transform_feedback_object *obj
1067            = ctx->Driver.NewTransformFeedback(ctx, first + i);
1068         if (!obj) {
1069            _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1070            return;
1071         }
1072         ids[i] = first + i;
1073         _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i,
1074                                obj);
1075         if (dsa) {
1076            /* this is normally done at bind time in the non-dsa case */
1077            obj->EverBound = GL_TRUE;
1078         }
1079      }
1080   }
1081   else {
1082      _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1083   }
1084}
1085
1086/**
1087 * Create new transform feedback objects.   Transform feedback objects
1088 * encapsulate the state related to transform feedback to allow quickly
1089 * switching state (and drawing the results, below).
1090 * Part of GL_ARB_transform_feedback2.
1091 */
1092void GLAPIENTRY
1093_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
1094{
1095   GET_CURRENT_CONTEXT(ctx);
1096
1097   /* GenTransformFeedbacks should just reserve the object names that a
1098    * subsequent call to BindTransformFeedback should actively create. For
1099    * the sake of simplicity, we reserve the names and create the objects
1100    * straight away.
1101    */
1102
1103   create_transform_feedbacks(ctx, n, names, false);
1104}
1105
1106/**
1107 * Create new transform feedback objects.   Transform feedback objects
1108 * encapsulate the state related to transform feedback to allow quickly
1109 * switching state (and drawing the results, below).
1110 * Part of GL_ARB_direct_state_access.
1111 */
1112void GLAPIENTRY
1113_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
1114{
1115   GET_CURRENT_CONTEXT(ctx);
1116
1117   create_transform_feedbacks(ctx, n, names, true);
1118}
1119
1120
1121/**
1122 * Is the given ID a transform feedback object?
1123 * Part of GL_ARB_transform_feedback2.
1124 */
1125GLboolean GLAPIENTRY
1126_mesa_IsTransformFeedback(GLuint name)
1127{
1128   struct gl_transform_feedback_object *obj;
1129   GET_CURRENT_CONTEXT(ctx);
1130
1131   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1132
1133   if (name == 0)
1134      return GL_FALSE;
1135
1136   obj = _mesa_lookup_transform_feedback_object(ctx, name);
1137   if (obj == NULL)
1138      return GL_FALSE;
1139
1140   return obj->EverBound;
1141}
1142
1143
1144/**
1145 * Bind the given transform feedback object.
1146 * Part of GL_ARB_transform_feedback2.
1147 */
1148static ALWAYS_INLINE void
1149bind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error)
1150{
1151   struct gl_transform_feedback_object *obj;
1152
1153   obj = _mesa_lookup_transform_feedback_object(ctx, name);
1154   if (!no_error && !obj) {
1155      _mesa_error(ctx, GL_INVALID_OPERATION,
1156                  "glBindTransformFeedback(name=%u)", name);
1157      return;
1158   }
1159
1160   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1161                                       obj);
1162}
1163
1164
1165void GLAPIENTRY
1166_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name)
1167{
1168   GET_CURRENT_CONTEXT(ctx);
1169   bind_transform_feedback(ctx, name, true);
1170}
1171
1172
1173void GLAPIENTRY
1174_mesa_BindTransformFeedback(GLenum target, GLuint name)
1175{
1176   GET_CURRENT_CONTEXT(ctx);
1177
1178   if (target != GL_TRANSFORM_FEEDBACK) {
1179      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
1180      return;
1181   }
1182
1183   if (_mesa_is_xfb_active_and_unpaused(ctx)) {
1184      _mesa_error(ctx, GL_INVALID_OPERATION,
1185              "glBindTransformFeedback(transform is active, or not paused)");
1186      return;
1187   }
1188
1189   bind_transform_feedback(ctx, name, false);
1190}
1191
1192
1193/**
1194 * Delete the given transform feedback objects.
1195 * Part of GL_ARB_transform_feedback2.
1196 */
1197void GLAPIENTRY
1198_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
1199{
1200   GLint i;
1201   GET_CURRENT_CONTEXT(ctx);
1202
1203   if (n < 0) {
1204      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
1205      return;
1206   }
1207
1208   if (!names)
1209      return;
1210
1211   for (i = 0; i < n; i++) {
1212      if (names[i] > 0) {
1213         struct gl_transform_feedback_object *obj
1214            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
1215         if (obj) {
1216            if (obj->Active) {
1217               _mesa_error(ctx, GL_INVALID_OPERATION,
1218                           "glDeleteTransformFeedbacks(object %u is active)",
1219                           names[i]);
1220               return;
1221            }
1222            _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
1223            /* unref, but object may not be deleted until later */
1224            if (obj == ctx->TransformFeedback.CurrentObject) {
1225               reference_transform_feedback_object(
1226                     &ctx->TransformFeedback.CurrentObject,
1227                     ctx->TransformFeedback.DefaultObject);
1228            }
1229            reference_transform_feedback_object(&obj, NULL);
1230         }
1231      }
1232   }
1233}
1234
1235
1236/**
1237 * Pause transform feedback.
1238 * Part of GL_ARB_transform_feedback2.
1239 */
1240static void
1241pause_transform_feedback(struct gl_context *ctx,
1242                         struct gl_transform_feedback_object *obj)
1243{
1244   FLUSH_VERTICES(ctx, 0);
1245   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1246
1247   assert(ctx->Driver.PauseTransformFeedback);
1248   ctx->Driver.PauseTransformFeedback(ctx, obj);
1249
1250   obj->Paused = GL_TRUE;
1251}
1252
1253
1254void GLAPIENTRY
1255_mesa_PauseTransformFeedback_no_error(void)
1256{
1257   GET_CURRENT_CONTEXT(ctx);
1258   pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1259}
1260
1261
1262void GLAPIENTRY
1263_mesa_PauseTransformFeedback(void)
1264{
1265   struct gl_transform_feedback_object *obj;
1266   GET_CURRENT_CONTEXT(ctx);
1267
1268   obj = ctx->TransformFeedback.CurrentObject;
1269
1270   if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
1271      _mesa_error(ctx, GL_INVALID_OPERATION,
1272           "glPauseTransformFeedback(feedback not active or already paused)");
1273      return;
1274   }
1275
1276   pause_transform_feedback(ctx, obj);
1277}
1278
1279
1280/**
1281 * Resume transform feedback.
1282 * Part of GL_ARB_transform_feedback2.
1283 */
1284static void
1285resume_transform_feedback(struct gl_context *ctx,
1286                          struct gl_transform_feedback_object *obj)
1287{
1288   FLUSH_VERTICES(ctx, 0);
1289   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1290
1291   obj->Paused = GL_FALSE;
1292
1293   assert(ctx->Driver.ResumeTransformFeedback);
1294   ctx->Driver.ResumeTransformFeedback(ctx, obj);
1295}
1296
1297
1298void GLAPIENTRY
1299_mesa_ResumeTransformFeedback_no_error(void)
1300{
1301   GET_CURRENT_CONTEXT(ctx);
1302   resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1303}
1304
1305
1306void GLAPIENTRY
1307_mesa_ResumeTransformFeedback(void)
1308{
1309   struct gl_transform_feedback_object *obj;
1310   GET_CURRENT_CONTEXT(ctx);
1311
1312   obj = ctx->TransformFeedback.CurrentObject;
1313
1314   if (!obj->Active || !obj->Paused) {
1315      _mesa_error(ctx, GL_INVALID_OPERATION,
1316               "glResumeTransformFeedback(feedback not active or not paused)");
1317      return;
1318   }
1319
1320   /* From the ARB_transform_feedback2 specification:
1321    * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1322    *  the program object being used by the current transform feedback object
1323    *  is not active."
1324    */
1325   if (obj->program != get_xfb_source(ctx)) {
1326      _mesa_error(ctx, GL_INVALID_OPERATION,
1327                  "glResumeTransformFeedback(wrong program bound)");
1328      return;
1329   }
1330
1331   resume_transform_feedback(ctx, obj);
1332}
1333
1334extern void GLAPIENTRY
1335_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
1336{
1337    struct gl_transform_feedback_object *obj;
1338    GET_CURRENT_CONTEXT(ctx);
1339
1340    obj = lookup_transform_feedback_object_err(ctx, xfb,
1341                                               "glGetTransformFeedbackiv");
1342    if (!obj) {
1343       return;
1344    }
1345
1346    switch(pname) {
1347    case GL_TRANSFORM_FEEDBACK_PAUSED:
1348       *param = obj->Paused;
1349       break;
1350    case GL_TRANSFORM_FEEDBACK_ACTIVE:
1351       *param = obj->Active;
1352       break;
1353    default:
1354       _mesa_error(ctx, GL_INVALID_ENUM,
1355                   "glGetTransformFeedbackiv(pname=%i)", pname);
1356    }
1357}
1358
1359extern void GLAPIENTRY
1360_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
1361                              GLint *param)
1362{
1363   struct gl_transform_feedback_object *obj;
1364   GET_CURRENT_CONTEXT(ctx);
1365
1366   obj = lookup_transform_feedback_object_err(ctx, xfb,
1367                                              "glGetTransformFeedbacki_v");
1368   if (!obj) {
1369      return;
1370   }
1371
1372   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1373      _mesa_error(ctx, GL_INVALID_VALUE,
1374                  "glGetTransformFeedbacki_v(index=%i)", index);
1375      return;
1376   }
1377
1378   switch(pname) {
1379   case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
1380      *param = obj->BufferNames[index];
1381      break;
1382   default:
1383      _mesa_error(ctx, GL_INVALID_ENUM,
1384                  "glGetTransformFeedbacki_v(pname=%i)", pname);
1385   }
1386}
1387
1388extern void GLAPIENTRY
1389_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
1390                                GLint64 *param)
1391{
1392   struct gl_transform_feedback_object *obj;
1393   GET_CURRENT_CONTEXT(ctx);
1394
1395   obj = lookup_transform_feedback_object_err(ctx, xfb,
1396                                              "glGetTransformFeedbacki64_v");
1397   if (!obj) {
1398      return;
1399   }
1400
1401   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1402      _mesa_error(ctx, GL_INVALID_VALUE,
1403                  "glGetTransformFeedbacki64_v(index=%i)", index);
1404      return;
1405   }
1406
1407   /**
1408    * This follows the same general rules used for BindBufferBase:
1409    *
1410    *   "To query the starting offset or size of the range of a buffer
1411    *    object binding in an indexed array, call GetInteger64i_v with
1412    *    target set to respectively the starting offset or binding size
1413    *    name from table 6.5 for that array. Index must be in the range
1414    *    zero to the number of bind points supported minus one. If the
1415    *    starting offset or size was not specified when the buffer object
1416    *    was bound (e.g. if it was bound with BindBufferBase), or if no
1417    *    buffer object is bound to the target array at index, zero is
1418    *    returned."
1419    */
1420   if (obj->RequestedSize[index] == 0 &&
1421       (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START ||
1422        pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) {
1423      *param = 0;
1424      return;
1425   }
1426
1427   compute_transform_feedback_buffer_sizes(obj);
1428   switch(pname) {
1429   case GL_TRANSFORM_FEEDBACK_BUFFER_START:
1430      assert(obj->RequestedSize[index] > 0);
1431      *param = obj->Offset[index];
1432      break;
1433   case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
1434      assert(obj->RequestedSize[index] > 0);
1435      *param = obj->Size[index];
1436      break;
1437   default:
1438      _mesa_error(ctx, GL_INVALID_ENUM,
1439                  "glGetTransformFeedbacki64_v(pname=%i)", pname);
1440   }
1441}
1442