1848b8605Smrg/*
2848b8605Smrg * Mesa 3-D graphics library
3848b8605Smrg *
4848b8605Smrg * Copyright (C) 2010  VMware, Inc.  All Rights Reserved.
5848b8605Smrg *
6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg * copy of this software and associated documentation files (the "Software"),
8848b8605Smrg * to deal in the Software without restriction, including without limitation
9848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the
11848b8605Smrg * Software is furnished to do so, subject to the following conditions:
12848b8605Smrg *
13848b8605Smrg * The above copyright notice and this permission notice shall be included
14848b8605Smrg * in all copies or substantial portions of the Software.
15848b8605Smrg *
16848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22848b8605Smrg * OTHER DEALINGS IN THE SOFTWARE.
23848b8605Smrg */
24848b8605Smrg
25848b8605Smrg
26848b8605Smrg/*
27848b8605Smrg * Transform feedback support.
28848b8605Smrg *
29848b8605Smrg * Authors:
30848b8605Smrg *   Brian Paul
31848b8605Smrg */
32848b8605Smrg
33848b8605Smrg
34848b8605Smrg#include "buffers.h"
35848b8605Smrg#include "context.h"
36848b8605Smrg#include "hash.h"
37848b8605Smrg#include "macros.h"
38848b8605Smrg#include "mtypes.h"
39848b8605Smrg#include "transformfeedback.h"
40848b8605Smrg#include "shaderapi.h"
41848b8605Smrg#include "shaderobj.h"
42848b8605Smrg
43b8e80941Smrg#include "program/program.h"
44848b8605Smrg#include "program/prog_parameter.h"
45848b8605Smrg
46848b8605Smrgstruct using_program_tuple
47848b8605Smrg{
48b8e80941Smrg   struct gl_program *prog;
49848b8605Smrg   bool found;
50848b8605Smrg};
51848b8605Smrg
52848b8605Smrgstatic void
53848b8605Smrgactive_xfb_object_references_program(GLuint key, void *data, void *user_data)
54848b8605Smrg{
55848b8605Smrg   struct using_program_tuple *callback_data = user_data;
56848b8605Smrg   struct gl_transform_feedback_object *obj = data;
57b8e80941Smrg   if (obj->Active && obj->program == callback_data->prog)
58848b8605Smrg      callback_data->found = true;
59848b8605Smrg}
60848b8605Smrg
61848b8605Smrg/**
62848b8605Smrg * Return true if any active transform feedback object is using a program.
63848b8605Smrg */
64848b8605Smrgbool
65848b8605Smrg_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
66848b8605Smrg                                          struct gl_shader_program *shProg)
67848b8605Smrg{
68b8e80941Smrg   if (!shProg->last_vert_prog)
69b8e80941Smrg      return false;
70b8e80941Smrg
71848b8605Smrg   struct using_program_tuple callback_data;
72848b8605Smrg   callback_data.found = false;
73b8e80941Smrg   callback_data.prog = shProg->last_vert_prog;
74848b8605Smrg
75b8e80941Smrg   _mesa_HashWalkLocked(ctx->TransformFeedback.Objects,
76b8e80941Smrg                        active_xfb_object_references_program, &callback_data);
77848b8605Smrg
78848b8605Smrg   /* Also check DefaultObject, as it's not in the Objects hash table. */
79848b8605Smrg   active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject,
80848b8605Smrg                                        &callback_data);
81848b8605Smrg
82848b8605Smrg   return callback_data.found;
83848b8605Smrg}
84848b8605Smrg
85848b8605Smrg/**
86848b8605Smrg * Do reference counting of transform feedback buffers.
87848b8605Smrg */
88848b8605Smrgstatic void
89848b8605Smrgreference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
90848b8605Smrg                                    struct gl_transform_feedback_object *obj)
91848b8605Smrg{
92848b8605Smrg   if (*ptr == obj)
93848b8605Smrg      return;
94848b8605Smrg
95848b8605Smrg   if (*ptr) {
96848b8605Smrg      /* Unreference the old object */
97848b8605Smrg      struct gl_transform_feedback_object *oldObj = *ptr;
98848b8605Smrg
99b8e80941Smrg      assert(oldObj->RefCount > 0);
100848b8605Smrg      oldObj->RefCount--;
101848b8605Smrg
102848b8605Smrg      if (oldObj->RefCount == 0) {
103848b8605Smrg         GET_CURRENT_CONTEXT(ctx);
104848b8605Smrg         if (ctx)
105848b8605Smrg            ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
106848b8605Smrg      }
107848b8605Smrg
108848b8605Smrg      *ptr = NULL;
109848b8605Smrg   }
110b8e80941Smrg   assert(!*ptr);
111848b8605Smrg
112848b8605Smrg   if (obj) {
113b8e80941Smrg      assert(obj->RefCount > 0);
114b8e80941Smrg
115848b8605Smrg      /* reference new object */
116b8e80941Smrg      obj->RefCount++;
117b8e80941Smrg      obj->EverBound = GL_TRUE;
118b8e80941Smrg      *ptr = obj;
119848b8605Smrg   }
120848b8605Smrg}
121848b8605Smrg
122848b8605Smrg
123848b8605Smrg/**
124848b8605Smrg * Per-context init for transform feedback.
125848b8605Smrg */
126848b8605Smrgvoid
127848b8605Smrg_mesa_init_transform_feedback(struct gl_context *ctx)
128848b8605Smrg{
129848b8605Smrg   /* core mesa expects this, even a dummy one, to be available */
130b8e80941Smrg   assert(ctx->Driver.NewTransformFeedback);
131848b8605Smrg
132848b8605Smrg   ctx->TransformFeedback.DefaultObject =
133848b8605Smrg      ctx->Driver.NewTransformFeedback(ctx, 0);
134848b8605Smrg
135848b8605Smrg   assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
136848b8605Smrg
137848b8605Smrg   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
138848b8605Smrg                                       ctx->TransformFeedback.DefaultObject);
139848b8605Smrg
140848b8605Smrg   assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
141848b8605Smrg
142848b8605Smrg   ctx->TransformFeedback.Objects = _mesa_NewHashTable();
143848b8605Smrg
144848b8605Smrg   _mesa_reference_buffer_object(ctx,
145848b8605Smrg                                 &ctx->TransformFeedback.CurrentBuffer,
146848b8605Smrg                                 ctx->Shared->NullBufferObj);
147848b8605Smrg}
148848b8605Smrg
149848b8605Smrg
150848b8605Smrg
151848b8605Smrg/**
152848b8605Smrg * Callback for _mesa_HashDeleteAll().
153848b8605Smrg */
154848b8605Smrgstatic void
155848b8605Smrgdelete_cb(GLuint key, void *data, void *userData)
156848b8605Smrg{
157848b8605Smrg   struct gl_context *ctx = (struct gl_context *) userData;
158848b8605Smrg   struct gl_transform_feedback_object *obj =
159848b8605Smrg      (struct gl_transform_feedback_object *) data;
160848b8605Smrg
161848b8605Smrg   ctx->Driver.DeleteTransformFeedback(ctx, obj);
162848b8605Smrg}
163848b8605Smrg
164848b8605Smrg
165848b8605Smrg/**
166848b8605Smrg * Per-context free/clean-up for transform feedback.
167848b8605Smrg */
168848b8605Smrgvoid
169848b8605Smrg_mesa_free_transform_feedback(struct gl_context *ctx)
170848b8605Smrg{
171848b8605Smrg   /* core mesa expects this, even a dummy one, to be available */
172b8e80941Smrg   assert(ctx->Driver.NewTransformFeedback);
173848b8605Smrg
174848b8605Smrg   _mesa_reference_buffer_object(ctx,
175848b8605Smrg                                 &ctx->TransformFeedback.CurrentBuffer,
176848b8605Smrg                                 NULL);
177848b8605Smrg
178848b8605Smrg   /* Delete all feedback objects */
179848b8605Smrg   _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
180848b8605Smrg   _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
181848b8605Smrg
182848b8605Smrg   /* Delete the default feedback object */
183848b8605Smrg   assert(ctx->Driver.DeleteTransformFeedback);
184848b8605Smrg   ctx->Driver.DeleteTransformFeedback(ctx,
185848b8605Smrg                                       ctx->TransformFeedback.DefaultObject);
186848b8605Smrg
187848b8605Smrg   ctx->TransformFeedback.CurrentObject = NULL;
188848b8605Smrg}
189848b8605Smrg
190848b8605Smrg
191848b8605Smrg/** Initialize the fields of a gl_transform_feedback_object. */
192848b8605Smrgvoid
193848b8605Smrg_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
194848b8605Smrg                                     GLuint name)
195848b8605Smrg{
196848b8605Smrg   obj->Name = name;
197848b8605Smrg   obj->RefCount = 1;
198848b8605Smrg   obj->EverBound = GL_FALSE;
199848b8605Smrg}
200848b8605Smrg
201848b8605Smrg
202848b8605Smrg/** Default fallback for ctx->Driver.NewTransformFeedback() */
203848b8605Smrgstatic struct gl_transform_feedback_object *
204b8e80941Smrgnew_transform_feedback_fallback(struct gl_context *ctx, GLuint name)
205848b8605Smrg{
206848b8605Smrg   struct gl_transform_feedback_object *obj;
207b8e80941Smrg
208848b8605Smrg   obj = CALLOC_STRUCT(gl_transform_feedback_object);
209b8e80941Smrg   if (!obj)
210b8e80941Smrg      return NULL;
211b8e80941Smrg
212848b8605Smrg   _mesa_init_transform_feedback_object(obj, name);
213848b8605Smrg   return obj;
214848b8605Smrg}
215848b8605Smrg
216848b8605Smrg/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
217848b8605Smrgstatic void
218b8e80941Smrgdelete_transform_feedback_fallback(struct gl_context *ctx,
219b8e80941Smrg                                   struct gl_transform_feedback_object *obj)
220848b8605Smrg{
221848b8605Smrg   GLuint i;
222848b8605Smrg
223b8e80941Smrg   for (i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
224848b8605Smrg      _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
225848b8605Smrg   }
226848b8605Smrg
227848b8605Smrg   free(obj->Label);
228848b8605Smrg   free(obj);
229848b8605Smrg}
230848b8605Smrg
231848b8605Smrg
232848b8605Smrg/** Default fallback for ctx->Driver.BeginTransformFeedback() */
233848b8605Smrgstatic void
234b8e80941Smrgbegin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode,
235b8e80941Smrg                                  struct gl_transform_feedback_object *obj)
236848b8605Smrg{
237848b8605Smrg   /* nop */
238848b8605Smrg}
239848b8605Smrg
240848b8605Smrg/** Default fallback for ctx->Driver.EndTransformFeedback() */
241848b8605Smrgstatic void
242b8e80941Smrgend_transform_feedback_fallback(struct gl_context *ctx,
243b8e80941Smrg                                struct gl_transform_feedback_object *obj)
244848b8605Smrg{
245848b8605Smrg   /* nop */
246848b8605Smrg}
247848b8605Smrg
248848b8605Smrg/** Default fallback for ctx->Driver.PauseTransformFeedback() */
249848b8605Smrgstatic void
250b8e80941Smrgpause_transform_feedback_fallback(struct gl_context *ctx,
251b8e80941Smrg                                  struct gl_transform_feedback_object *obj)
252848b8605Smrg{
253848b8605Smrg   /* nop */
254848b8605Smrg}
255848b8605Smrg
256848b8605Smrg/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
257848b8605Smrgstatic void
258b8e80941Smrgresume_transform_feedback_fallback(struct gl_context *ctx,
259b8e80941Smrg                                   struct gl_transform_feedback_object *obj)
260848b8605Smrg{
261848b8605Smrg   /* nop */
262848b8605Smrg}
263848b8605Smrg
264848b8605Smrg
265848b8605Smrg/**
266848b8605Smrg * Plug in default device driver functions for transform feedback.
267848b8605Smrg * Most drivers will override some/all of these.
268848b8605Smrg */
269848b8605Smrgvoid
270848b8605Smrg_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
271848b8605Smrg{
272b8e80941Smrg   driver->NewTransformFeedback = new_transform_feedback_fallback;
273b8e80941Smrg   driver->DeleteTransformFeedback = delete_transform_feedback_fallback;
274b8e80941Smrg   driver->BeginTransformFeedback = begin_transform_feedback_fallback;
275b8e80941Smrg   driver->EndTransformFeedback = end_transform_feedback_fallback;
276b8e80941Smrg   driver->PauseTransformFeedback = pause_transform_feedback_fallback;
277b8e80941Smrg   driver->ResumeTransformFeedback = resume_transform_feedback_fallback;
278848b8605Smrg}
279848b8605Smrg
280848b8605Smrg
281848b8605Smrg/**
282848b8605Smrg * Fill in the correct Size value for each buffer in \c obj.
283848b8605Smrg *
284848b8605Smrg * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
285848b8605Smrg * Targets"):
286848b8605Smrg *
287848b8605Smrg *   BindBufferBase binds the entire buffer, even when the size of the buffer
288848b8605Smrg *   is changed after the binding is established. It is equivalent to calling
289848b8605Smrg *   BindBufferRange with offset zero, while size is determined by the size of
290848b8605Smrg *   the bound buffer at the time the binding is used.
291848b8605Smrg *
292848b8605Smrg *   Regardless of the size specified with BindBufferRange, or indirectly with
293848b8605Smrg *   BindBufferBase, the GL will never read or write beyond the end of a bound
294848b8605Smrg *   buffer. In some cases this constraint may result in visibly different
295848b8605Smrg *   behavior when a buffer overflow would otherwise result, such as described
296848b8605Smrg *   for transform feedback operations in section 13.2.2.
297848b8605Smrg */
298848b8605Smrgstatic void
299848b8605Smrgcompute_transform_feedback_buffer_sizes(
300848b8605Smrg      struct gl_transform_feedback_object *obj)
301848b8605Smrg{
302848b8605Smrg   unsigned i = 0;
303848b8605Smrg   for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
304848b8605Smrg      GLintptr offset = obj->Offset[i];
305848b8605Smrg      GLsizeiptr buffer_size
306848b8605Smrg         = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
307848b8605Smrg      GLsizeiptr available_space
308848b8605Smrg         = buffer_size <= offset ? 0 : buffer_size - offset;
309848b8605Smrg      GLsizeiptr computed_size;
310848b8605Smrg      if (obj->RequestedSize[i] == 0) {
311848b8605Smrg         /* No size was specified at the time the buffer was bound, so allow
312848b8605Smrg          * writing to all available space in the buffer.
313848b8605Smrg          */
314848b8605Smrg         computed_size = available_space;
315848b8605Smrg      } else {
316848b8605Smrg         /* A size was specified at the time the buffer was bound, however
317848b8605Smrg          * it's possible that the buffer has shrunk since then.  So only
318848b8605Smrg          * allow writing to the minimum of the specified size and the space
319848b8605Smrg          * available.
320848b8605Smrg          */
321848b8605Smrg         computed_size = MIN2(available_space, obj->RequestedSize[i]);
322848b8605Smrg      }
323848b8605Smrg
324848b8605Smrg      /* Legal sizes must be multiples of four, so round down if necessary. */
325848b8605Smrg      obj->Size[i] = computed_size & ~0x3;
326848b8605Smrg   }
327848b8605Smrg}
328848b8605Smrg
329848b8605Smrg
330848b8605Smrg/**
331848b8605Smrg * Compute the maximum number of vertices that can be written to the currently
332848b8605Smrg * enabled transform feedback buffers without overflowing any of them.
333848b8605Smrg */
334848b8605Smrgunsigned
335b8e80941Smrg_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
336848b8605Smrg      const struct gl_transform_feedback_object *obj,
337848b8605Smrg      const struct gl_transform_feedback_info *info)
338848b8605Smrg{
339848b8605Smrg   unsigned max_index = 0xffffffff;
340848b8605Smrg   unsigned i;
341848b8605Smrg
342b8e80941Smrg   for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
343b8e80941Smrg      if ((info->ActiveBuffers >> i) & 1) {
344b8e80941Smrg         unsigned stride = info->Buffers[i].Stride;
345b8e80941Smrg         unsigned max_for_this_buffer;
346848b8605Smrg
347b8e80941Smrg         /* Skip any inactive buffers, which have a stride of 0. */
348b8e80941Smrg         if (stride == 0)
349b8e80941Smrg            continue;
350848b8605Smrg
351b8e80941Smrg         max_for_this_buffer = obj->Size[i] / (4 * stride);
352b8e80941Smrg         max_index = MIN2(max_index, max_for_this_buffer);
353b8e80941Smrg      }
354848b8605Smrg   }
355848b8605Smrg
356848b8605Smrg   return max_index;
357848b8605Smrg}
358848b8605Smrg
359848b8605Smrg
360848b8605Smrg/**
361848b8605Smrg ** Begin API functions
362848b8605Smrg **/
363848b8605Smrg
364848b8605Smrg
365848b8605Smrg/**
366848b8605Smrg * Figure out which stage of the pipeline is the source of transform feedback
367b8e80941Smrg * data given the current context state, and return its gl_program.
368848b8605Smrg *
369848b8605Smrg * If no active program can generate transform feedback data (i.e. no vertex
370848b8605Smrg * shader is active), returns NULL.
371848b8605Smrg */
372b8e80941Smrgstatic struct gl_program *
373848b8605Smrgget_xfb_source(struct gl_context *ctx)
374848b8605Smrg{
375848b8605Smrg   int i;
376848b8605Smrg   for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
377848b8605Smrg      if (ctx->_Shader->CurrentProgram[i] != NULL)
378848b8605Smrg         return ctx->_Shader->CurrentProgram[i];
379848b8605Smrg   }
380848b8605Smrg   return NULL;
381848b8605Smrg}
382848b8605Smrg
383848b8605Smrg
384b8e80941Smrgstatic ALWAYS_INLINE void
385b8e80941Smrgbegin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
386848b8605Smrg{
387848b8605Smrg   struct gl_transform_feedback_object *obj;
388848b8605Smrg   struct gl_transform_feedback_info *info = NULL;
389b8e80941Smrg   struct gl_program *source;
390848b8605Smrg   GLuint i;
391848b8605Smrg   unsigned vertices_per_prim;
392848b8605Smrg
393848b8605Smrg   obj = ctx->TransformFeedback.CurrentObject;
394848b8605Smrg
395848b8605Smrg   /* Figure out what pipeline stage is the source of data for transform
396848b8605Smrg    * feedback.
397848b8605Smrg    */
398848b8605Smrg   source = get_xfb_source(ctx);
399b8e80941Smrg   if (!no_error && source == NULL) {
400848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
401848b8605Smrg                  "glBeginTransformFeedback(no program active)");
402848b8605Smrg      return;
403848b8605Smrg   }
404848b8605Smrg
405b8e80941Smrg   info = source->sh.LinkedTransformFeedback;
406848b8605Smrg
407b8e80941Smrg   if (!no_error && info->NumOutputs == 0) {
408848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
409848b8605Smrg                  "glBeginTransformFeedback(no varyings to record)");
410848b8605Smrg      return;
411848b8605Smrg   }
412848b8605Smrg
413848b8605Smrg   switch (mode) {
414848b8605Smrg   case GL_POINTS:
415848b8605Smrg      vertices_per_prim = 1;
416848b8605Smrg      break;
417848b8605Smrg   case GL_LINES:
418848b8605Smrg      vertices_per_prim = 2;
419848b8605Smrg      break;
420848b8605Smrg   case GL_TRIANGLES:
421848b8605Smrg      vertices_per_prim = 3;
422848b8605Smrg      break;
423848b8605Smrg   default:
424b8e80941Smrg      if (!no_error) {
425b8e80941Smrg         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
426b8e80941Smrg         return;
427b8e80941Smrg      } else {
428b8e80941Smrg         /* Stop compiler warnings */
429b8e80941Smrg         unreachable("Error in API use when using KHR_no_error");
430b8e80941Smrg      }
431848b8605Smrg   }
432848b8605Smrg
433b8e80941Smrg   if (!no_error) {
434b8e80941Smrg      if (obj->Active) {
435848b8605Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
436b8e80941Smrg                     "glBeginTransformFeedback(already active)");
437848b8605Smrg         return;
438848b8605Smrg      }
439b8e80941Smrg
440b8e80941Smrg      for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
441b8e80941Smrg         if ((info->ActiveBuffers >> i) & 1) {
442b8e80941Smrg            if (obj->BufferNames[i] == 0) {
443b8e80941Smrg               _mesa_error(ctx, GL_INVALID_OPERATION,
444b8e80941Smrg                           "glBeginTransformFeedback(binding point %d does not "
445b8e80941Smrg                           "have a buffer object bound)", i);
446b8e80941Smrg               return;
447b8e80941Smrg            }
448b8e80941Smrg         }
449b8e80941Smrg      }
450848b8605Smrg   }
451848b8605Smrg
452848b8605Smrg   FLUSH_VERTICES(ctx, 0);
453848b8605Smrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
454848b8605Smrg
455848b8605Smrg   obj->Active = GL_TRUE;
456848b8605Smrg   ctx->TransformFeedback.Mode = mode;
457848b8605Smrg
458848b8605Smrg   compute_transform_feedback_buffer_sizes(obj);
459848b8605Smrg
460848b8605Smrg   if (_mesa_is_gles3(ctx)) {
461848b8605Smrg      /* In GLES3, we are required to track the usage of the transform
462848b8605Smrg       * feedback buffer and report INVALID_OPERATION if a draw call tries to
463848b8605Smrg       * exceed it.  So compute the maximum number of vertices that we can
464848b8605Smrg       * write without overflowing any of the buffers currently being used for
465848b8605Smrg       * feedback.
466848b8605Smrg       */
467848b8605Smrg      unsigned max_vertices
468b8e80941Smrg         = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
469848b8605Smrg      obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
470848b8605Smrg   }
471848b8605Smrg
472b8e80941Smrg   if (obj->program != source) {
473848b8605Smrg      ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
474b8e80941Smrg      _mesa_reference_program_(ctx, &obj->program, source);
475b8e80941Smrg      obj->program = source;
476848b8605Smrg   }
477848b8605Smrg
478848b8605Smrg   assert(ctx->Driver.BeginTransformFeedback);
479848b8605Smrg   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
480848b8605Smrg}
481848b8605Smrg
482848b8605Smrg
483b8e80941Smrgvoid GLAPIENTRY
484b8e80941Smrg_mesa_BeginTransformFeedback_no_error(GLenum mode)
485b8e80941Smrg{
486b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
487b8e80941Smrg   begin_transform_feedback(ctx, mode, true);
488b8e80941Smrg}
489b8e80941Smrg
490b8e80941Smrg
491b8e80941Smrgvoid GLAPIENTRY
492b8e80941Smrg_mesa_BeginTransformFeedback(GLenum mode)
493b8e80941Smrg{
494b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
495b8e80941Smrg   begin_transform_feedback(ctx, mode, false);
496b8e80941Smrg}
497b8e80941Smrg
498b8e80941Smrg
499b8e80941Smrgstatic void
500b8e80941Smrgend_transform_feedback(struct gl_context *ctx,
501b8e80941Smrg                       struct gl_transform_feedback_object *obj)
502b8e80941Smrg{
503b8e80941Smrg   FLUSH_VERTICES(ctx, 0);
504b8e80941Smrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
505b8e80941Smrg
506b8e80941Smrg   assert(ctx->Driver.EndTransformFeedback);
507b8e80941Smrg   ctx->Driver.EndTransformFeedback(ctx, obj);
508b8e80941Smrg
509b8e80941Smrg   _mesa_reference_program_(ctx, &obj->program, NULL);
510b8e80941Smrg   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
511b8e80941Smrg   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
512b8e80941Smrg   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
513b8e80941Smrg}
514b8e80941Smrg
515b8e80941Smrg
516b8e80941Smrgvoid GLAPIENTRY
517b8e80941Smrg_mesa_EndTransformFeedback_no_error(void)
518b8e80941Smrg{
519b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
520b8e80941Smrg   end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
521b8e80941Smrg}
522b8e80941Smrg
523b8e80941Smrg
524848b8605Smrgvoid GLAPIENTRY
525848b8605Smrg_mesa_EndTransformFeedback(void)
526848b8605Smrg{
527848b8605Smrg   struct gl_transform_feedback_object *obj;
528848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
529848b8605Smrg
530848b8605Smrg   obj = ctx->TransformFeedback.CurrentObject;
531848b8605Smrg
532848b8605Smrg   if (!obj->Active) {
533848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
534848b8605Smrg                  "glEndTransformFeedback(not active)");
535848b8605Smrg      return;
536848b8605Smrg   }
537848b8605Smrg
538b8e80941Smrg   end_transform_feedback(ctx, obj);
539848b8605Smrg}
540848b8605Smrg
541848b8605Smrg
542848b8605Smrg/**
543848b8605Smrg * Helper used by BindBufferRange() and BindBufferBase().
544848b8605Smrg */
545848b8605Smrgstatic void
546b8e80941Smrgbind_buffer_range(struct gl_context *ctx,
547b8e80941Smrg                  struct gl_transform_feedback_object *obj,
548b8e80941Smrg                  GLuint index,
549848b8605Smrg                  struct gl_buffer_object *bufObj,
550b8e80941Smrg                  GLintptr offset, GLsizeiptr size,
551b8e80941Smrg                  bool dsa)
552848b8605Smrg{
553848b8605Smrg   /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
554848b8605Smrg    * transform feedback buffers can't be changed while transform feedback is
555848b8605Smrg    * active.
556848b8605Smrg    */
557848b8605Smrg
558b8e80941Smrg   if (!dsa) {
559b8e80941Smrg      /* The general binding point */
560b8e80941Smrg      _mesa_reference_buffer_object(ctx,
561b8e80941Smrg                                    &ctx->TransformFeedback.CurrentBuffer,
562b8e80941Smrg                                    bufObj);
563b8e80941Smrg   }
564848b8605Smrg
565848b8605Smrg   /* The per-attribute binding point */
566848b8605Smrg   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
567848b8605Smrg}
568848b8605Smrg
569848b8605Smrg
570848b8605Smrg/**
571b8e80941Smrg * Validate the buffer object to receive transform feedback results.  Plus,
572b8e80941Smrg * validate the starting offset to place the results, and max size.
573b8e80941Smrg * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
574b8e80941Smrg * functions.
575848b8605Smrg */
576b8e80941Smrgbool
577b8e80941Smrg_mesa_validate_buffer_range_xfb(struct gl_context *ctx,
578b8e80941Smrg                                struct gl_transform_feedback_object *obj,
579b8e80941Smrg                                GLuint index, struct gl_buffer_object *bufObj,
580b8e80941Smrg                                GLintptr offset, GLsizeiptr size, bool dsa)
581848b8605Smrg{
582b8e80941Smrg   const char *gl_methd_name;
583b8e80941Smrg   if (dsa)
584b8e80941Smrg      gl_methd_name = "glTransformFeedbackBufferRange";
585b8e80941Smrg   else
586b8e80941Smrg      gl_methd_name = "glBindBufferRange";
587848b8605Smrg
588848b8605Smrg
589848b8605Smrg   if (obj->Active) {
590b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
591b8e80941Smrg                  gl_methd_name);
592b8e80941Smrg      return false;
593848b8605Smrg   }
594848b8605Smrg
595848b8605Smrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
596b8e80941Smrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
597b8e80941Smrg       * generated if index is greater than or equal to the number of binding
598b8e80941Smrg       * points for transform feedback, as described in section 6.7.1."
599b8e80941Smrg       */
600b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
601b8e80941Smrg                  gl_methd_name, index);
602b8e80941Smrg      return false;
603848b8605Smrg   }
604848b8605Smrg
605848b8605Smrg   if (size & 0x3) {
606b8e80941Smrg      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
607b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
608b8e80941Smrg                  "four)", gl_methd_name, (int) size);
609b8e80941Smrg      return false;
610b8e80941Smrg   }
611848b8605Smrg
612848b8605Smrg   if (offset & 0x3) {
613b8e80941Smrg      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
614b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
615b8e80941Smrg                  "of four)", gl_methd_name, (int) offset);
616b8e80941Smrg      return false;
617b8e80941Smrg   }
618b8e80941Smrg
619b8e80941Smrg   if (offset < 0) {
620b8e80941Smrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
621b8e80941Smrg       * generated by BindBufferRange if offset is negative."
622b8e80941Smrg       *
623b8e80941Smrg       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
624b8e80941Smrg       * is generated by TransformFeedbackBufferRange if offset is negative."
625b8e80941Smrg       */
626b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
627b8e80941Smrg                  gl_methd_name,
628b8e80941Smrg                  (int) offset);
629b8e80941Smrg      return false;
630b8e80941Smrg   }
631848b8605Smrg
632b8e80941Smrg   if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) {
633b8e80941Smrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
634b8e80941Smrg       * generated by BindBufferRange if buffer is non-zero and size is less
635b8e80941Smrg       * than or equal to zero."
636b8e80941Smrg       *
637b8e80941Smrg       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
638b8e80941Smrg       * is generated by TransformFeedbackBufferRange if size is less than or
639b8e80941Smrg       * equal to zero."
640b8e80941Smrg       */
641b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
642b8e80941Smrg                  gl_methd_name, (int) size);
643b8e80941Smrg      return false;
644b8e80941Smrg   }
645b8e80941Smrg
646b8e80941Smrg   return true;
647848b8605Smrg}
648848b8605Smrg
649848b8605Smrg
650848b8605Smrg/**
651848b8605Smrg * Specify a buffer object to receive transform feedback results.
652848b8605Smrg * As above, but start at offset = 0.
653b8e80941Smrg * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
654b8e80941Smrg * functions.
655848b8605Smrg */
656848b8605Smrgvoid
657848b8605Smrg_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
658b8e80941Smrg                                          struct gl_transform_feedback_object *obj,
659b8e80941Smrg                                          GLuint index,
660b8e80941Smrg                                          struct gl_buffer_object *bufObj,
661b8e80941Smrg                                          bool dsa)
662848b8605Smrg{
663848b8605Smrg   if (obj->Active) {
664848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
665b8e80941Smrg                  "%s(transform feedback active)",
666b8e80941Smrg                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
667848b8605Smrg      return;
668848b8605Smrg   }
669848b8605Smrg
670848b8605Smrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
671b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
672b8e80941Smrg                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
673b8e80941Smrg                  index);
674b8e80941Smrg      return;
675b8e80941Smrg   }
676b8e80941Smrg
677b8e80941Smrg   bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
678b8e80941Smrg}
679b8e80941Smrg
680b8e80941Smrg/**
681b8e80941Smrg * Wrapper around lookup_transform_feedback_object that throws
682b8e80941Smrg * GL_INVALID_OPERATION if id is not in the hash table. After calling
683b8e80941Smrg * _mesa_error, it returns NULL.
684b8e80941Smrg */
685b8e80941Smrgstatic struct gl_transform_feedback_object *
686b8e80941Smrglookup_transform_feedback_object_err(struct gl_context *ctx,
687b8e80941Smrg                                     GLuint xfb, const char* func)
688b8e80941Smrg{
689b8e80941Smrg   struct gl_transform_feedback_object *obj;
690b8e80941Smrg
691b8e80941Smrg   obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
692b8e80941Smrg   if (!obj) {
693b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
694b8e80941Smrg                  "%s(xfb=%u: non-generated object name)", func, xfb);
695b8e80941Smrg   }
696b8e80941Smrg
697b8e80941Smrg   return obj;
698b8e80941Smrg}
699b8e80941Smrg
700b8e80941Smrg/**
701b8e80941Smrg * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
702b8e80941Smrg * is not in the hash table. Specialised version for the
703b8e80941Smrg * transform-feedback-related functions. After calling _mesa_error, it
704b8e80941Smrg * returns NULL.
705b8e80941Smrg */
706b8e80941Smrgstatic struct gl_buffer_object *
707b8e80941Smrglookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
708b8e80941Smrg                                        GLuint buffer, const char* func)
709b8e80941Smrg{
710b8e80941Smrg   struct gl_buffer_object *bufObj;
711b8e80941Smrg
712b8e80941Smrg   /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
713b8e80941Smrg    * name of an existing buffer object.
714b8e80941Smrg    */
715b8e80941Smrg   if (buffer == 0) {
716b8e80941Smrg      bufObj = ctx->Shared->NullBufferObj;
717b8e80941Smrg   } else {
718b8e80941Smrg      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
719b8e80941Smrg      if (!bufObj) {
720b8e80941Smrg         _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
721b8e80941Smrg                     buffer);
722b8e80941Smrg      }
723b8e80941Smrg   }
724b8e80941Smrg
725b8e80941Smrg   return bufObj;
726b8e80941Smrg}
727b8e80941Smrg
728b8e80941Smrgvoid GLAPIENTRY
729b8e80941Smrg_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
730b8e80941Smrg{
731b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
732b8e80941Smrg   struct gl_transform_feedback_object *obj;
733b8e80941Smrg   struct gl_buffer_object *bufObj;
734b8e80941Smrg
735b8e80941Smrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
736b8e80941Smrg                                              "glTransformFeedbackBufferBase");
737b8e80941Smrg   if (!obj) {
738b8e80941Smrg      return;
739b8e80941Smrg   }
740b8e80941Smrg
741b8e80941Smrg   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
742b8e80941Smrg                                              "glTransformFeedbackBufferBase");
743b8e80941Smrg   if (!bufObj) {
744848b8605Smrg      return;
745848b8605Smrg   }
746848b8605Smrg
747b8e80941Smrg   _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
748848b8605Smrg}
749848b8605Smrg
750b8e80941Smrgvoid GLAPIENTRY
751b8e80941Smrg_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
752b8e80941Smrg                                   GLintptr offset, GLsizeiptr size)
753b8e80941Smrg{
754b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
755b8e80941Smrg   struct gl_transform_feedback_object *obj;
756b8e80941Smrg   struct gl_buffer_object *bufObj;
757b8e80941Smrg
758b8e80941Smrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
759b8e80941Smrg                                              "glTransformFeedbackBufferRange");
760b8e80941Smrg   if (!obj) {
761b8e80941Smrg      return;
762b8e80941Smrg   }
763b8e80941Smrg
764b8e80941Smrg   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
765b8e80941Smrg                                              "glTransformFeedbackBufferRange");
766b8e80941Smrg   if (!bufObj) {
767b8e80941Smrg      return;
768b8e80941Smrg   }
769b8e80941Smrg
770b8e80941Smrg   if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
771b8e80941Smrg                                        size, true))
772b8e80941Smrg      return;
773b8e80941Smrg
774b8e80941Smrg   /* The per-attribute binding point */
775b8e80941Smrg   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
776b8e80941Smrg                                        size);
777b8e80941Smrg}
778848b8605Smrg
779848b8605Smrg/**
780848b8605Smrg * Specify a buffer object to receive transform feedback results, plus the
781848b8605Smrg * offset in the buffer to start placing results.
782848b8605Smrg * This function is part of GL_EXT_transform_feedback, but not GL3.
783848b8605Smrg */
784b8e80941Smrgstatic ALWAYS_INLINE void
785b8e80941Smrgbind_buffer_offset(struct gl_context *ctx,
786b8e80941Smrg                   struct gl_transform_feedback_object *obj, GLuint index,
787b8e80941Smrg                   GLuint buffer, GLintptr offset, bool no_error)
788b8e80941Smrg{
789b8e80941Smrg   struct gl_buffer_object *bufObj;
790b8e80941Smrg
791b8e80941Smrg   if (buffer == 0) {
792b8e80941Smrg      bufObj = ctx->Shared->NullBufferObj;
793b8e80941Smrg   } else {
794b8e80941Smrg      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
795b8e80941Smrg      if (!no_error && !bufObj) {
796b8e80941Smrg         _mesa_error(ctx, GL_INVALID_OPERATION,
797b8e80941Smrg                     "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
798b8e80941Smrg         return;
799b8e80941Smrg      }
800b8e80941Smrg   }
801b8e80941Smrg
802b8e80941Smrg   _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
803b8e80941Smrg}
804b8e80941Smrg
805b8e80941Smrg
806b8e80941Smrgvoid GLAPIENTRY
807b8e80941Smrg_mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer,
808b8e80941Smrg                                   GLintptr offset)
809b8e80941Smrg{
810b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
811b8e80941Smrg   bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer,
812b8e80941Smrg                      offset, true);
813b8e80941Smrg}
814b8e80941Smrg
815b8e80941Smrg
816848b8605Smrgvoid GLAPIENTRY
817848b8605Smrg_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
818848b8605Smrg                          GLintptr offset)
819848b8605Smrg{
820848b8605Smrg   struct gl_transform_feedback_object *obj;
821848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
822848b8605Smrg
823848b8605Smrg   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
824848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
825848b8605Smrg      return;
826848b8605Smrg   }
827848b8605Smrg
828848b8605Smrg   obj = ctx->TransformFeedback.CurrentObject;
829848b8605Smrg
830848b8605Smrg   if (obj->Active) {
831848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
832848b8605Smrg                  "glBindBufferOffsetEXT(transform feedback active)");
833848b8605Smrg      return;
834848b8605Smrg   }
835848b8605Smrg
836848b8605Smrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
837848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
838848b8605Smrg                  "glBindBufferOffsetEXT(index=%d)", index);
839848b8605Smrg      return;
840848b8605Smrg   }
841848b8605Smrg
842848b8605Smrg   if (offset & 0x3) {
843848b8605Smrg      /* must be multiple of four */
844848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
845848b8605Smrg                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
846848b8605Smrg      return;
847848b8605Smrg   }
848848b8605Smrg
849b8e80941Smrg   bind_buffer_offset(ctx, obj, index, buffer, offset, false);
850b8e80941Smrg}
851b8e80941Smrg
852b8e80941Smrg
853b8e80941Smrg/**
854b8e80941Smrg * This function specifies the transform feedback outputs to be written
855b8e80941Smrg * to the feedback buffer(s), and in what order.
856b8e80941Smrg */
857b8e80941Smrgstatic ALWAYS_INLINE void
858b8e80941Smrgtransform_feedback_varyings(struct gl_context *ctx,
859b8e80941Smrg                            struct gl_shader_program *shProg, GLsizei count,
860b8e80941Smrg                            const GLchar *const *varyings, GLenum bufferMode)
861b8e80941Smrg{
862b8e80941Smrg   GLint i;
863b8e80941Smrg
864b8e80941Smrg   /* free existing varyings, if any */
865b8e80941Smrg   for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
866b8e80941Smrg      free(shProg->TransformFeedback.VaryingNames[i]);
867848b8605Smrg   }
868b8e80941Smrg   free(shProg->TransformFeedback.VaryingNames);
869848b8605Smrg
870b8e80941Smrg   /* allocate new memory for varying names */
871b8e80941Smrg   shProg->TransformFeedback.VaryingNames =
872b8e80941Smrg      malloc(count * sizeof(GLchar *));
873b8e80941Smrg
874b8e80941Smrg   if (!shProg->TransformFeedback.VaryingNames) {
875b8e80941Smrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
876848b8605Smrg      return;
877848b8605Smrg   }
878848b8605Smrg
879b8e80941Smrg   /* Save the new names and the count */
880b8e80941Smrg   for (i = 0; i < count; i++) {
881b8e80941Smrg      shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
882b8e80941Smrg   }
883b8e80941Smrg   shProg->TransformFeedback.NumVarying = count;
884b8e80941Smrg
885b8e80941Smrg   shProg->TransformFeedback.BufferMode = bufferMode;
886b8e80941Smrg
887b8e80941Smrg   /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
888b8e80941Smrg    * the varyings won't be used until shader link time.
889b8e80941Smrg    */
890848b8605Smrg}
891848b8605Smrg
892848b8605Smrg
893b8e80941Smrgvoid GLAPIENTRY
894b8e80941Smrg_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count,
895b8e80941Smrg                                         const GLchar *const *varyings,
896b8e80941Smrg                                         GLenum bufferMode)
897b8e80941Smrg{
898b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
899b8e80941Smrg
900b8e80941Smrg   struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
901b8e80941Smrg   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
902b8e80941Smrg}
903b8e80941Smrg
904848b8605Smrgvoid GLAPIENTRY
905848b8605Smrg_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
906848b8605Smrg                                const GLchar * const *varyings,
907848b8605Smrg                                GLenum bufferMode)
908848b8605Smrg{
909848b8605Smrg   struct gl_shader_program *shProg;
910848b8605Smrg   GLint i;
911848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
912848b8605Smrg
913848b8605Smrg   /* From the ARB_transform_feedback2 specification:
914848b8605Smrg    * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
915848b8605Smrg    *  if the current transform feedback object is active, even if paused."
916848b8605Smrg    */
917848b8605Smrg   if (ctx->TransformFeedback.CurrentObject->Active) {
918848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
919848b8605Smrg               "glTransformFeedbackVaryings(current object is active)");
920848b8605Smrg      return;
921848b8605Smrg   }
922848b8605Smrg
923848b8605Smrg   switch (bufferMode) {
924848b8605Smrg   case GL_INTERLEAVED_ATTRIBS:
925848b8605Smrg      break;
926848b8605Smrg   case GL_SEPARATE_ATTRIBS:
927848b8605Smrg      break;
928848b8605Smrg   default:
929848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM,
930848b8605Smrg                  "glTransformFeedbackVaryings(bufferMode)");
931848b8605Smrg      return;
932848b8605Smrg   }
933848b8605Smrg
934848b8605Smrg   if (count < 0 ||
935848b8605Smrg       (bufferMode == GL_SEPARATE_ATTRIBS &&
936848b8605Smrg        (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
937848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
938848b8605Smrg                  "glTransformFeedbackVaryings(count=%d)", count);
939848b8605Smrg      return;
940848b8605Smrg   }
941848b8605Smrg
942b8e80941Smrg   shProg = _mesa_lookup_shader_program_err(ctx, program,
943b8e80941Smrg                                            "glTransformFeedbackVaryings");
944b8e80941Smrg   if (!shProg)
945848b8605Smrg      return;
946848b8605Smrg
947848b8605Smrg   if (ctx->Extensions.ARB_transform_feedback3) {
948848b8605Smrg      if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
949848b8605Smrg         unsigned buffers = 1;
950848b8605Smrg
951848b8605Smrg         for (i = 0; i < count; i++) {
952848b8605Smrg            if (strcmp(varyings[i], "gl_NextBuffer") == 0)
953848b8605Smrg               buffers++;
954848b8605Smrg         }
955848b8605Smrg
956848b8605Smrg         if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
957848b8605Smrg            _mesa_error(ctx, GL_INVALID_OPERATION,
958848b8605Smrg                        "glTransformFeedbackVaryings(too many gl_NextBuffer "
959b8e80941Smrg                        "occurrences)");
960848b8605Smrg            return;
961848b8605Smrg         }
962848b8605Smrg      } else {
963848b8605Smrg         for (i = 0; i < count; i++) {
964848b8605Smrg            if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
965848b8605Smrg                strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
966848b8605Smrg                strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
967848b8605Smrg                strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
968848b8605Smrg                strcmp(varyings[i], "gl_SkipComponents4") == 0) {
969848b8605Smrg               _mesa_error(ctx, GL_INVALID_OPERATION,
970848b8605Smrg                           "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
971848b8605Smrg                           "varying=%s)",
972848b8605Smrg                           varyings[i]);
973848b8605Smrg               return;
974848b8605Smrg            }
975848b8605Smrg         }
976848b8605Smrg      }
977848b8605Smrg   }
978848b8605Smrg
979b8e80941Smrg   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
980848b8605Smrg}
981848b8605Smrg
982848b8605Smrg
983848b8605Smrg/**
984848b8605Smrg * Get info about the transform feedback outputs which are to be written
985848b8605Smrg * to the feedback buffer(s).
986848b8605Smrg */
987848b8605Smrgvoid GLAPIENTRY
988848b8605Smrg_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
989848b8605Smrg                                  GLsizei bufSize, GLsizei *length,
990848b8605Smrg                                  GLsizei *size, GLenum *type, GLchar *name)
991848b8605Smrg{
992848b8605Smrg   const struct gl_shader_program *shProg;
993b8e80941Smrg   struct gl_program_resource *res;
994848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
995848b8605Smrg
996b8e80941Smrg   shProg = _mesa_lookup_shader_program_err(ctx, program,
997b8e80941Smrg                                            "glGetTransformFeedbackVarying");
998b8e80941Smrg   if (!shProg)
999848b8605Smrg      return;
1000848b8605Smrg
1001b8e80941Smrg   res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
1002b8e80941Smrg                                           GL_TRANSFORM_FEEDBACK_VARYING,
1003b8e80941Smrg                                           index);
1004b8e80941Smrg   if (!res) {
1005848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
1006848b8605Smrg                  "glGetTransformFeedbackVarying(index=%u)", index);
1007848b8605Smrg      return;
1008848b8605Smrg   }
1009848b8605Smrg
1010848b8605Smrg   /* return the varying's name and length */
1011b8e80941Smrg   _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
1012848b8605Smrg
1013848b8605Smrg   /* return the datatype and value's size (in datatype units) */
1014848b8605Smrg   if (type)
1015b8e80941Smrg      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1016b8e80941Smrg                                  res, index, GL_TYPE, (GLint*) type,
1017b8e80941Smrg                                  "glGetTransformFeedbackVarying");
1018848b8605Smrg   if (size)
1019b8e80941Smrg      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1020b8e80941Smrg                                  res, index, GL_ARRAY_SIZE, (GLint*) size,
1021b8e80941Smrg                                  "glGetTransformFeedbackVarying");
1022848b8605Smrg}
1023848b8605Smrg
1024848b8605Smrg
1025848b8605Smrg
1026848b8605Smrgstruct gl_transform_feedback_object *
1027848b8605Smrg_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
1028848b8605Smrg{
1029b8e80941Smrg   /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
1030b8e80941Smrg    * the default transform feedback object, or the name of an existing
1031b8e80941Smrg    * transform feedback object."
1032b8e80941Smrg    */
1033848b8605Smrg   if (name == 0) {
1034848b8605Smrg      return ctx->TransformFeedback.DefaultObject;
1035848b8605Smrg   }
1036848b8605Smrg   else
1037848b8605Smrg      return (struct gl_transform_feedback_object *)
1038b8e80941Smrg         _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
1039848b8605Smrg}
1040848b8605Smrg
1041b8e80941Smrgstatic void
1042b8e80941Smrgcreate_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
1043b8e80941Smrg                           bool dsa)
1044848b8605Smrg{
1045848b8605Smrg   GLuint first;
1046b8e80941Smrg   const char* func;
1047b8e80941Smrg
1048b8e80941Smrg   if (dsa)
1049b8e80941Smrg      func = "glCreateTransformFeedbacks";
1050b8e80941Smrg   else
1051b8e80941Smrg      func = "glGenTransformFeedbacks";
1052848b8605Smrg
1053848b8605Smrg   if (n < 0) {
1054b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
1055848b8605Smrg      return;
1056848b8605Smrg   }
1057848b8605Smrg
1058b8e80941Smrg   if (!ids)
1059848b8605Smrg      return;
1060848b8605Smrg
1061848b8605Smrg   /* we don't need contiguous IDs, but this might be faster */
1062848b8605Smrg   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
1063848b8605Smrg   if (first) {
1064848b8605Smrg      GLsizei i;
1065848b8605Smrg      for (i = 0; i < n; i++) {
1066848b8605Smrg         struct gl_transform_feedback_object *obj
1067848b8605Smrg            = ctx->Driver.NewTransformFeedback(ctx, first + i);
1068848b8605Smrg         if (!obj) {
1069b8e80941Smrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1070848b8605Smrg            return;
1071848b8605Smrg         }
1072b8e80941Smrg         ids[i] = first + i;
1073b8e80941Smrg         _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i,
1074b8e80941Smrg                                obj);
1075b8e80941Smrg         if (dsa) {
1076b8e80941Smrg            /* this is normally done at bind time in the non-dsa case */
1077b8e80941Smrg            obj->EverBound = GL_TRUE;
1078b8e80941Smrg         }
1079848b8605Smrg      }
1080848b8605Smrg   }
1081848b8605Smrg   else {
1082b8e80941Smrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1083848b8605Smrg   }
1084848b8605Smrg}
1085848b8605Smrg
1086b8e80941Smrg/**
1087b8e80941Smrg * Create new transform feedback objects.   Transform feedback objects
1088b8e80941Smrg * encapsulate the state related to transform feedback to allow quickly
1089b8e80941Smrg * switching state (and drawing the results, below).
1090b8e80941Smrg * Part of GL_ARB_transform_feedback2.
1091b8e80941Smrg */
1092b8e80941Smrgvoid GLAPIENTRY
1093b8e80941Smrg_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
1094b8e80941Smrg{
1095b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1096b8e80941Smrg
1097b8e80941Smrg   /* GenTransformFeedbacks should just reserve the object names that a
1098b8e80941Smrg    * subsequent call to BindTransformFeedback should actively create. For
1099b8e80941Smrg    * the sake of simplicity, we reserve the names and create the objects
1100b8e80941Smrg    * straight away.
1101b8e80941Smrg    */
1102b8e80941Smrg
1103b8e80941Smrg   create_transform_feedbacks(ctx, n, names, false);
1104b8e80941Smrg}
1105b8e80941Smrg
1106b8e80941Smrg/**
1107b8e80941Smrg * Create new transform feedback objects.   Transform feedback objects
1108b8e80941Smrg * encapsulate the state related to transform feedback to allow quickly
1109b8e80941Smrg * switching state (and drawing the results, below).
1110b8e80941Smrg * Part of GL_ARB_direct_state_access.
1111b8e80941Smrg */
1112b8e80941Smrgvoid GLAPIENTRY
1113b8e80941Smrg_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
1114b8e80941Smrg{
1115b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1116b8e80941Smrg
1117b8e80941Smrg   create_transform_feedbacks(ctx, n, names, true);
1118b8e80941Smrg}
1119b8e80941Smrg
1120848b8605Smrg
1121848b8605Smrg/**
1122848b8605Smrg * Is the given ID a transform feedback object?
1123848b8605Smrg * Part of GL_ARB_transform_feedback2.
1124848b8605Smrg */
1125848b8605SmrgGLboolean GLAPIENTRY
1126848b8605Smrg_mesa_IsTransformFeedback(GLuint name)
1127848b8605Smrg{
1128848b8605Smrg   struct gl_transform_feedback_object *obj;
1129848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
1130848b8605Smrg
1131848b8605Smrg   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1132848b8605Smrg
1133848b8605Smrg   if (name == 0)
1134848b8605Smrg      return GL_FALSE;
1135848b8605Smrg
1136848b8605Smrg   obj = _mesa_lookup_transform_feedback_object(ctx, name);
1137848b8605Smrg   if (obj == NULL)
1138848b8605Smrg      return GL_FALSE;
1139848b8605Smrg
1140848b8605Smrg   return obj->EverBound;
1141848b8605Smrg}
1142848b8605Smrg
1143848b8605Smrg
1144848b8605Smrg/**
1145848b8605Smrg * Bind the given transform feedback object.
1146848b8605Smrg * Part of GL_ARB_transform_feedback2.
1147848b8605Smrg */
1148b8e80941Smrgstatic ALWAYS_INLINE void
1149b8e80941Smrgbind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error)
1150b8e80941Smrg{
1151b8e80941Smrg   struct gl_transform_feedback_object *obj;
1152b8e80941Smrg
1153b8e80941Smrg   obj = _mesa_lookup_transform_feedback_object(ctx, name);
1154b8e80941Smrg   if (!no_error && !obj) {
1155b8e80941Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1156b8e80941Smrg                  "glBindTransformFeedback(name=%u)", name);
1157b8e80941Smrg      return;
1158b8e80941Smrg   }
1159b8e80941Smrg
1160b8e80941Smrg   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1161b8e80941Smrg                                       obj);
1162b8e80941Smrg}
1163b8e80941Smrg
1164b8e80941Smrg
1165b8e80941Smrgvoid GLAPIENTRY
1166b8e80941Smrg_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name)
1167b8e80941Smrg{
1168b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1169b8e80941Smrg   bind_transform_feedback(ctx, name, true);
1170b8e80941Smrg}
1171b8e80941Smrg
1172b8e80941Smrg
1173848b8605Smrgvoid GLAPIENTRY
1174848b8605Smrg_mesa_BindTransformFeedback(GLenum target, GLuint name)
1175848b8605Smrg{
1176848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
1177848b8605Smrg
1178848b8605Smrg   if (target != GL_TRANSFORM_FEEDBACK) {
1179848b8605Smrg      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
1180848b8605Smrg      return;
1181848b8605Smrg   }
1182848b8605Smrg
1183848b8605Smrg   if (_mesa_is_xfb_active_and_unpaused(ctx)) {
1184848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1185848b8605Smrg              "glBindTransformFeedback(transform is active, or not paused)");
1186848b8605Smrg      return;
1187848b8605Smrg   }
1188848b8605Smrg
1189b8e80941Smrg   bind_transform_feedback(ctx, name, false);
1190848b8605Smrg}
1191848b8605Smrg
1192848b8605Smrg
1193848b8605Smrg/**
1194848b8605Smrg * Delete the given transform feedback objects.
1195848b8605Smrg * Part of GL_ARB_transform_feedback2.
1196848b8605Smrg */
1197848b8605Smrgvoid GLAPIENTRY
1198848b8605Smrg_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
1199848b8605Smrg{
1200848b8605Smrg   GLint i;
1201848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
1202848b8605Smrg
1203848b8605Smrg   if (n < 0) {
1204848b8605Smrg      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
1205848b8605Smrg      return;
1206848b8605Smrg   }
1207848b8605Smrg
1208848b8605Smrg   if (!names)
1209848b8605Smrg      return;
1210848b8605Smrg
1211848b8605Smrg   for (i = 0; i < n; i++) {
1212848b8605Smrg      if (names[i] > 0) {
1213848b8605Smrg         struct gl_transform_feedback_object *obj
1214848b8605Smrg            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
1215848b8605Smrg         if (obj) {
1216848b8605Smrg            if (obj->Active) {
1217848b8605Smrg               _mesa_error(ctx, GL_INVALID_OPERATION,
1218848b8605Smrg                           "glDeleteTransformFeedbacks(object %u is active)",
1219848b8605Smrg                           names[i]);
1220848b8605Smrg               return;
1221848b8605Smrg            }
1222b8e80941Smrg            _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
1223848b8605Smrg            /* unref, but object may not be deleted until later */
1224b8e80941Smrg            if (obj == ctx->TransformFeedback.CurrentObject) {
1225b8e80941Smrg               reference_transform_feedback_object(
1226b8e80941Smrg                     &ctx->TransformFeedback.CurrentObject,
1227b8e80941Smrg                     ctx->TransformFeedback.DefaultObject);
1228b8e80941Smrg            }
1229848b8605Smrg            reference_transform_feedback_object(&obj, NULL);
1230848b8605Smrg         }
1231848b8605Smrg      }
1232848b8605Smrg   }
1233848b8605Smrg}
1234848b8605Smrg
1235848b8605Smrg
1236848b8605Smrg/**
1237848b8605Smrg * Pause transform feedback.
1238848b8605Smrg * Part of GL_ARB_transform_feedback2.
1239848b8605Smrg */
1240b8e80941Smrgstatic void
1241b8e80941Smrgpause_transform_feedback(struct gl_context *ctx,
1242b8e80941Smrg                         struct gl_transform_feedback_object *obj)
1243b8e80941Smrg{
1244b8e80941Smrg   FLUSH_VERTICES(ctx, 0);
1245b8e80941Smrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1246b8e80941Smrg
1247b8e80941Smrg   assert(ctx->Driver.PauseTransformFeedback);
1248b8e80941Smrg   ctx->Driver.PauseTransformFeedback(ctx, obj);
1249b8e80941Smrg
1250b8e80941Smrg   obj->Paused = GL_TRUE;
1251b8e80941Smrg}
1252b8e80941Smrg
1253b8e80941Smrg
1254b8e80941Smrgvoid GLAPIENTRY
1255b8e80941Smrg_mesa_PauseTransformFeedback_no_error(void)
1256b8e80941Smrg{
1257b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1258b8e80941Smrg   pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1259b8e80941Smrg}
1260b8e80941Smrg
1261b8e80941Smrg
1262848b8605Smrgvoid GLAPIENTRY
1263848b8605Smrg_mesa_PauseTransformFeedback(void)
1264848b8605Smrg{
1265848b8605Smrg   struct gl_transform_feedback_object *obj;
1266848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
1267848b8605Smrg
1268848b8605Smrg   obj = ctx->TransformFeedback.CurrentObject;
1269848b8605Smrg
1270848b8605Smrg   if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
1271848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1272848b8605Smrg           "glPauseTransformFeedback(feedback not active or already paused)");
1273848b8605Smrg      return;
1274848b8605Smrg   }
1275848b8605Smrg
1276b8e80941Smrg   pause_transform_feedback(ctx, obj);
1277848b8605Smrg}
1278848b8605Smrg
1279848b8605Smrg
1280848b8605Smrg/**
1281848b8605Smrg * Resume transform feedback.
1282848b8605Smrg * Part of GL_ARB_transform_feedback2.
1283848b8605Smrg */
1284b8e80941Smrgstatic void
1285b8e80941Smrgresume_transform_feedback(struct gl_context *ctx,
1286b8e80941Smrg                          struct gl_transform_feedback_object *obj)
1287b8e80941Smrg{
1288b8e80941Smrg   FLUSH_VERTICES(ctx, 0);
1289b8e80941Smrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1290b8e80941Smrg
1291b8e80941Smrg   obj->Paused = GL_FALSE;
1292b8e80941Smrg
1293b8e80941Smrg   assert(ctx->Driver.ResumeTransformFeedback);
1294b8e80941Smrg   ctx->Driver.ResumeTransformFeedback(ctx, obj);
1295b8e80941Smrg}
1296b8e80941Smrg
1297b8e80941Smrg
1298b8e80941Smrgvoid GLAPIENTRY
1299b8e80941Smrg_mesa_ResumeTransformFeedback_no_error(void)
1300b8e80941Smrg{
1301b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1302b8e80941Smrg   resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1303b8e80941Smrg}
1304b8e80941Smrg
1305b8e80941Smrg
1306848b8605Smrgvoid GLAPIENTRY
1307848b8605Smrg_mesa_ResumeTransformFeedback(void)
1308848b8605Smrg{
1309848b8605Smrg   struct gl_transform_feedback_object *obj;
1310848b8605Smrg   GET_CURRENT_CONTEXT(ctx);
1311848b8605Smrg
1312848b8605Smrg   obj = ctx->TransformFeedback.CurrentObject;
1313848b8605Smrg
1314848b8605Smrg   if (!obj->Active || !obj->Paused) {
1315848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1316848b8605Smrg               "glResumeTransformFeedback(feedback not active or not paused)");
1317848b8605Smrg      return;
1318848b8605Smrg   }
1319848b8605Smrg
1320848b8605Smrg   /* From the ARB_transform_feedback2 specification:
1321848b8605Smrg    * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1322848b8605Smrg    *  the program object being used by the current transform feedback object
1323848b8605Smrg    *  is not active."
1324848b8605Smrg    */
1325b8e80941Smrg   if (obj->program != get_xfb_source(ctx)) {
1326848b8605Smrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1327848b8605Smrg                  "glResumeTransformFeedback(wrong program bound)");
1328848b8605Smrg      return;
1329848b8605Smrg   }
1330848b8605Smrg
1331b8e80941Smrg   resume_transform_feedback(ctx, obj);
1332b8e80941Smrg}
1333848b8605Smrg
1334b8e80941Smrgextern void GLAPIENTRY
1335b8e80941Smrg_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
1336b8e80941Smrg{
1337b8e80941Smrg    struct gl_transform_feedback_object *obj;
1338b8e80941Smrg    GET_CURRENT_CONTEXT(ctx);
1339b8e80941Smrg
1340b8e80941Smrg    obj = lookup_transform_feedback_object_err(ctx, xfb,
1341b8e80941Smrg                                               "glGetTransformFeedbackiv");
1342b8e80941Smrg    if (!obj) {
1343b8e80941Smrg       return;
1344b8e80941Smrg    }
1345b8e80941Smrg
1346b8e80941Smrg    switch(pname) {
1347b8e80941Smrg    case GL_TRANSFORM_FEEDBACK_PAUSED:
1348b8e80941Smrg       *param = obj->Paused;
1349b8e80941Smrg       break;
1350b8e80941Smrg    case GL_TRANSFORM_FEEDBACK_ACTIVE:
1351b8e80941Smrg       *param = obj->Active;
1352b8e80941Smrg       break;
1353b8e80941Smrg    default:
1354b8e80941Smrg       _mesa_error(ctx, GL_INVALID_ENUM,
1355b8e80941Smrg                   "glGetTransformFeedbackiv(pname=%i)", pname);
1356b8e80941Smrg    }
1357b8e80941Smrg}
1358848b8605Smrg
1359b8e80941Smrgextern void GLAPIENTRY
1360b8e80941Smrg_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
1361b8e80941Smrg                              GLint *param)
1362b8e80941Smrg{
1363b8e80941Smrg   struct gl_transform_feedback_object *obj;
1364b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1365b8e80941Smrg
1366b8e80941Smrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
1367b8e80941Smrg                                              "glGetTransformFeedbacki_v");
1368b8e80941Smrg   if (!obj) {
1369b8e80941Smrg      return;
1370b8e80941Smrg   }
1371b8e80941Smrg
1372b8e80941Smrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1373b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
1374b8e80941Smrg                  "glGetTransformFeedbacki_v(index=%i)", index);
1375b8e80941Smrg      return;
1376b8e80941Smrg   }
1377b8e80941Smrg
1378b8e80941Smrg   switch(pname) {
1379b8e80941Smrg   case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
1380b8e80941Smrg      *param = obj->BufferNames[index];
1381b8e80941Smrg      break;
1382b8e80941Smrg   default:
1383b8e80941Smrg      _mesa_error(ctx, GL_INVALID_ENUM,
1384b8e80941Smrg                  "glGetTransformFeedbacki_v(pname=%i)", pname);
1385b8e80941Smrg   }
1386b8e80941Smrg}
1387b8e80941Smrg
1388b8e80941Smrgextern void GLAPIENTRY
1389b8e80941Smrg_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
1390b8e80941Smrg                                GLint64 *param)
1391b8e80941Smrg{
1392b8e80941Smrg   struct gl_transform_feedback_object *obj;
1393b8e80941Smrg   GET_CURRENT_CONTEXT(ctx);
1394b8e80941Smrg
1395b8e80941Smrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
1396b8e80941Smrg                                              "glGetTransformFeedbacki64_v");
1397b8e80941Smrg   if (!obj) {
1398b8e80941Smrg      return;
1399b8e80941Smrg   }
1400b8e80941Smrg
1401b8e80941Smrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1402b8e80941Smrg      _mesa_error(ctx, GL_INVALID_VALUE,
1403b8e80941Smrg                  "glGetTransformFeedbacki64_v(index=%i)", index);
1404b8e80941Smrg      return;
1405b8e80941Smrg   }
1406b8e80941Smrg
1407b8e80941Smrg   /**
1408b8e80941Smrg    * This follows the same general rules used for BindBufferBase:
1409b8e80941Smrg    *
1410b8e80941Smrg    *   "To query the starting offset or size of the range of a buffer
1411b8e80941Smrg    *    object binding in an indexed array, call GetInteger64i_v with
1412b8e80941Smrg    *    target set to respectively the starting offset or binding size
1413b8e80941Smrg    *    name from table 6.5 for that array. Index must be in the range
1414b8e80941Smrg    *    zero to the number of bind points supported minus one. If the
1415b8e80941Smrg    *    starting offset or size was not specified when the buffer object
1416b8e80941Smrg    *    was bound (e.g. if it was bound with BindBufferBase), or if no
1417b8e80941Smrg    *    buffer object is bound to the target array at index, zero is
1418b8e80941Smrg    *    returned."
1419b8e80941Smrg    */
1420b8e80941Smrg   if (obj->RequestedSize[index] == 0 &&
1421b8e80941Smrg       (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START ||
1422b8e80941Smrg        pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) {
1423b8e80941Smrg      *param = 0;
1424b8e80941Smrg      return;
1425b8e80941Smrg   }
1426b8e80941Smrg
1427b8e80941Smrg   compute_transform_feedback_buffer_sizes(obj);
1428b8e80941Smrg   switch(pname) {
1429b8e80941Smrg   case GL_TRANSFORM_FEEDBACK_BUFFER_START:
1430b8e80941Smrg      assert(obj->RequestedSize[index] > 0);
1431b8e80941Smrg      *param = obj->Offset[index];
1432b8e80941Smrg      break;
1433b8e80941Smrg   case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
1434b8e80941Smrg      assert(obj->RequestedSize[index] > 0);
1435b8e80941Smrg      *param = obj->Size[index];
1436b8e80941Smrg      break;
1437b8e80941Smrg   default:
1438b8e80941Smrg      _mesa_error(ctx, GL_INVALID_ENUM,
1439b8e80941Smrg                  "glGetTransformFeedbacki64_v(pname=%i)", pname);
1440b8e80941Smrg   }
1441848b8605Smrg}
1442