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