13464ebd5Sriastradh/*
23464ebd5Sriastradh * Mesa 3-D graphics library
33464ebd5Sriastradh *
43464ebd5Sriastradh * Copyright (C) 2010  VMware, Inc.  All Rights Reserved.
53464ebd5Sriastradh *
63464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
73464ebd5Sriastradh * copy of this software and associated documentation files (the "Software"),
83464ebd5Sriastradh * to deal in the Software without restriction, including without limitation
93464ebd5Sriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
103464ebd5Sriastradh * and/or sell copies of the Software, and to permit persons to whom the
113464ebd5Sriastradh * Software is furnished to do so, subject to the following conditions:
123464ebd5Sriastradh *
133464ebd5Sriastradh * The above copyright notice and this permission notice shall be included
143464ebd5Sriastradh * in all copies or substantial portions of the Software.
153464ebd5Sriastradh *
163464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
173464ebd5Sriastradh * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183464ebd5Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE.
233464ebd5Sriastradh */
243464ebd5Sriastradh
253464ebd5Sriastradh
263464ebd5Sriastradh/*
27af69d88dSmrg * Transform feedback support.
283464ebd5Sriastradh *
293464ebd5Sriastradh * Authors:
303464ebd5Sriastradh *   Brian Paul
313464ebd5Sriastradh */
323464ebd5Sriastradh
333464ebd5Sriastradh
343464ebd5Sriastradh#include "buffers.h"
353464ebd5Sriastradh#include "context.h"
367ec681f3Smrg#include "draw_validate.h"
373464ebd5Sriastradh#include "hash.h"
38af69d88dSmrg#include "macros.h"
393464ebd5Sriastradh#include "mtypes.h"
403464ebd5Sriastradh#include "transformfeedback.h"
413464ebd5Sriastradh#include "shaderapi.h"
423464ebd5Sriastradh#include "shaderobj.h"
433464ebd5Sriastradh
4401e04c3fSmrg#include "program/program.h"
453464ebd5Sriastradh#include "program/prog_parameter.h"
463464ebd5Sriastradh
477ec681f3Smrg#include "util/u_memory.h"
487ec681f3Smrg
49af69d88dSmrgstruct using_program_tuple
50af69d88dSmrg{
5101e04c3fSmrg   struct gl_program *prog;
52af69d88dSmrg   bool found;
53af69d88dSmrg};
54af69d88dSmrg
55af69d88dSmrgstatic void
567ec681f3Smrgactive_xfb_object_references_program(void *data, void *user_data)
57af69d88dSmrg{
58af69d88dSmrg   struct using_program_tuple *callback_data = user_data;
59af69d88dSmrg   struct gl_transform_feedback_object *obj = data;
6001e04c3fSmrg   if (obj->Active && obj->program == callback_data->prog)
61af69d88dSmrg      callback_data->found = true;
62af69d88dSmrg}
63af69d88dSmrg
64af69d88dSmrg/**
65af69d88dSmrg * Return true if any active transform feedback object is using a program.
66af69d88dSmrg */
67af69d88dSmrgbool
68af69d88dSmrg_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
69af69d88dSmrg                                          struct gl_shader_program *shProg)
70af69d88dSmrg{
7101e04c3fSmrg   if (!shProg->last_vert_prog)
7201e04c3fSmrg      return false;
7301e04c3fSmrg
74af69d88dSmrg   struct using_program_tuple callback_data;
75af69d88dSmrg   callback_data.found = false;
7601e04c3fSmrg   callback_data.prog = shProg->last_vert_prog;
773464ebd5Sriastradh
7801e04c3fSmrg   _mesa_HashWalkLocked(ctx->TransformFeedback.Objects,
7901e04c3fSmrg                        active_xfb_object_references_program, &callback_data);
803464ebd5Sriastradh
81af69d88dSmrg   /* Also check DefaultObject, as it's not in the Objects hash table. */
827ec681f3Smrg   active_xfb_object_references_program(ctx->TransformFeedback.DefaultObject,
83af69d88dSmrg                                        &callback_data);
84af69d88dSmrg
85af69d88dSmrg   return callback_data.found;
86af69d88dSmrg}
873464ebd5Sriastradh
883464ebd5Sriastradh/**
893464ebd5Sriastradh * Do reference counting of transform feedback buffers.
903464ebd5Sriastradh */
913464ebd5Sriastradhstatic void
923464ebd5Sriastradhreference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
933464ebd5Sriastradh                                    struct gl_transform_feedback_object *obj)
943464ebd5Sriastradh{
953464ebd5Sriastradh   if (*ptr == obj)
963464ebd5Sriastradh      return;
973464ebd5Sriastradh
983464ebd5Sriastradh   if (*ptr) {
993464ebd5Sriastradh      /* Unreference the old object */
1003464ebd5Sriastradh      struct gl_transform_feedback_object *oldObj = *ptr;
1013464ebd5Sriastradh
10201e04c3fSmrg      assert(oldObj->RefCount > 0);
1033464ebd5Sriastradh      oldObj->RefCount--;
1043464ebd5Sriastradh
1053464ebd5Sriastradh      if (oldObj->RefCount == 0) {
1063464ebd5Sriastradh         GET_CURRENT_CONTEXT(ctx);
1073464ebd5Sriastradh         if (ctx)
1083464ebd5Sriastradh            ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
1093464ebd5Sriastradh      }
1103464ebd5Sriastradh
1113464ebd5Sriastradh      *ptr = NULL;
1123464ebd5Sriastradh   }
11301e04c3fSmrg   assert(!*ptr);
1143464ebd5Sriastradh
1153464ebd5Sriastradh   if (obj) {
11601e04c3fSmrg      assert(obj->RefCount > 0);
11701e04c3fSmrg
1183464ebd5Sriastradh      /* reference new object */
11901e04c3fSmrg      obj->RefCount++;
12001e04c3fSmrg      obj->EverBound = GL_TRUE;
12101e04c3fSmrg      *ptr = obj;
1223464ebd5Sriastradh   }
1233464ebd5Sriastradh}
1243464ebd5Sriastradh
1253464ebd5Sriastradh
1263464ebd5Sriastradh/**
1273464ebd5Sriastradh * Per-context init for transform feedback.
1283464ebd5Sriastradh */
1293464ebd5Sriastradhvoid
1303464ebd5Sriastradh_mesa_init_transform_feedback(struct gl_context *ctx)
1313464ebd5Sriastradh{
1323464ebd5Sriastradh   /* core mesa expects this, even a dummy one, to be available */
13301e04c3fSmrg   assert(ctx->Driver.NewTransformFeedback);
1343464ebd5Sriastradh
1353464ebd5Sriastradh   ctx->TransformFeedback.DefaultObject =
1363464ebd5Sriastradh      ctx->Driver.NewTransformFeedback(ctx, 0);
1373464ebd5Sriastradh
1383464ebd5Sriastradh   assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
1393464ebd5Sriastradh
1403464ebd5Sriastradh   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1413464ebd5Sriastradh                                       ctx->TransformFeedback.DefaultObject);
1423464ebd5Sriastradh
1433464ebd5Sriastradh   assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
1443464ebd5Sriastradh
1453464ebd5Sriastradh   ctx->TransformFeedback.Objects = _mesa_NewHashTable();
1463464ebd5Sriastradh
1473464ebd5Sriastradh   _mesa_reference_buffer_object(ctx,
1487ec681f3Smrg                                 &ctx->TransformFeedback.CurrentBuffer, NULL);
1493464ebd5Sriastradh}
1503464ebd5Sriastradh
1513464ebd5Sriastradh
1523464ebd5Sriastradh
1533464ebd5Sriastradh/**
1543464ebd5Sriastradh * Callback for _mesa_HashDeleteAll().
1553464ebd5Sriastradh */
1563464ebd5Sriastradhstatic void
1577ec681f3Smrgdelete_cb(void *data, void *userData)
1583464ebd5Sriastradh{
1593464ebd5Sriastradh   struct gl_context *ctx = (struct gl_context *) userData;
1603464ebd5Sriastradh   struct gl_transform_feedback_object *obj =
1613464ebd5Sriastradh      (struct gl_transform_feedback_object *) data;
1623464ebd5Sriastradh
1633464ebd5Sriastradh   ctx->Driver.DeleteTransformFeedback(ctx, obj);
1643464ebd5Sriastradh}
1653464ebd5Sriastradh
1663464ebd5Sriastradh
1673464ebd5Sriastradh/**
1683464ebd5Sriastradh * Per-context free/clean-up for transform feedback.
1693464ebd5Sriastradh */
1703464ebd5Sriastradhvoid
1713464ebd5Sriastradh_mesa_free_transform_feedback(struct gl_context *ctx)
1723464ebd5Sriastradh{
1733464ebd5Sriastradh   /* core mesa expects this, even a dummy one, to be available */
17401e04c3fSmrg   assert(ctx->Driver.NewTransformFeedback);
1753464ebd5Sriastradh
1763464ebd5Sriastradh   _mesa_reference_buffer_object(ctx,
1773464ebd5Sriastradh                                 &ctx->TransformFeedback.CurrentBuffer,
1783464ebd5Sriastradh                                 NULL);
1793464ebd5Sriastradh
1803464ebd5Sriastradh   /* Delete all feedback objects */
1813464ebd5Sriastradh   _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
1823464ebd5Sriastradh   _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
1833464ebd5Sriastradh
1843464ebd5Sriastradh   /* Delete the default feedback object */
1853464ebd5Sriastradh   assert(ctx->Driver.DeleteTransformFeedback);
1863464ebd5Sriastradh   ctx->Driver.DeleteTransformFeedback(ctx,
1873464ebd5Sriastradh                                       ctx->TransformFeedback.DefaultObject);
1883464ebd5Sriastradh
1893464ebd5Sriastradh   ctx->TransformFeedback.CurrentObject = NULL;
1903464ebd5Sriastradh}
1913464ebd5Sriastradh
1923464ebd5Sriastradh
193af69d88dSmrg/** Initialize the fields of a gl_transform_feedback_object. */
1943464ebd5Sriastradhvoid
195af69d88dSmrg_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
196af69d88dSmrg                                     GLuint name)
1973464ebd5Sriastradh{
198af69d88dSmrg   obj->Name = name;
199af69d88dSmrg   obj->RefCount = 1;
200af69d88dSmrg   obj->EverBound = GL_FALSE;
2013464ebd5Sriastradh}
2023464ebd5Sriastradh
2037ec681f3Smrg/**
2047ec681f3Smrg * Delete a transform feedback object.  Called via
2057ec681f3Smrg * ctx->Driver->DeleteTransformFeedback, if not overwritten by driver.  In
2067ec681f3Smrg * the latter case, called from the driver after all driver-specific clean-up
2077ec681f3Smrg * has been done.
2087ec681f3Smrg *
2097ec681f3Smrg * \param ctx GL context to wich transform feedback object belongs.
2107ec681f3Smrg * \param obj Transform feedback object due to be deleted.
2117ec681f3Smrg */
2127ec681f3Smrgvoid
2137ec681f3Smrg_mesa_delete_transform_feedback_object(struct gl_context *ctx,
2147ec681f3Smrg                                       struct gl_transform_feedback_object
2157ec681f3Smrg                                              *obj)
2167ec681f3Smrg{
2177ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
2187ec681f3Smrg      _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
2197ec681f3Smrg   }
2207ec681f3Smrg
2217ec681f3Smrg   free(obj->Label);
2227ec681f3Smrg   free(obj);
2237ec681f3Smrg}
2243464ebd5Sriastradh
2253464ebd5Sriastradh/** Default fallback for ctx->Driver.NewTransformFeedback() */
2263464ebd5Sriastradhstatic struct gl_transform_feedback_object *
22701e04c3fSmrgnew_transform_feedback_fallback(struct gl_context *ctx, GLuint name)
2283464ebd5Sriastradh{
2293464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
23001e04c3fSmrg
2313464ebd5Sriastradh   obj = CALLOC_STRUCT(gl_transform_feedback_object);
23201e04c3fSmrg   if (!obj)
23301e04c3fSmrg      return NULL;
23401e04c3fSmrg
235af69d88dSmrg   _mesa_init_transform_feedback_object(obj, name);
2363464ebd5Sriastradh   return obj;
2373464ebd5Sriastradh}
2383464ebd5Sriastradh
2393464ebd5Sriastradh/** Default fallback for ctx->Driver.BeginTransformFeedback() */
2403464ebd5Sriastradhstatic void
24101e04c3fSmrgbegin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode,
24201e04c3fSmrg                                  struct gl_transform_feedback_object *obj)
2433464ebd5Sriastradh{
2443464ebd5Sriastradh   /* nop */
2453464ebd5Sriastradh}
2463464ebd5Sriastradh
2473464ebd5Sriastradh/** Default fallback for ctx->Driver.EndTransformFeedback() */
2483464ebd5Sriastradhstatic void
24901e04c3fSmrgend_transform_feedback_fallback(struct gl_context *ctx,
25001e04c3fSmrg                                struct gl_transform_feedback_object *obj)
2513464ebd5Sriastradh{
2523464ebd5Sriastradh   /* nop */
2533464ebd5Sriastradh}
2543464ebd5Sriastradh
2553464ebd5Sriastradh/** Default fallback for ctx->Driver.PauseTransformFeedback() */
2563464ebd5Sriastradhstatic void
25701e04c3fSmrgpause_transform_feedback_fallback(struct gl_context *ctx,
25801e04c3fSmrg                                  struct gl_transform_feedback_object *obj)
2593464ebd5Sriastradh{
2603464ebd5Sriastradh   /* nop */
2613464ebd5Sriastradh}
2623464ebd5Sriastradh
2633464ebd5Sriastradh/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
2643464ebd5Sriastradhstatic void
26501e04c3fSmrgresume_transform_feedback_fallback(struct gl_context *ctx,
26601e04c3fSmrg                                   struct gl_transform_feedback_object *obj)
2673464ebd5Sriastradh{
2683464ebd5Sriastradh   /* nop */
2693464ebd5Sriastradh}
2703464ebd5Sriastradh
2713464ebd5Sriastradh
2723464ebd5Sriastradh/**
2733464ebd5Sriastradh * Plug in default device driver functions for transform feedback.
2743464ebd5Sriastradh * Most drivers will override some/all of these.
2753464ebd5Sriastradh */
2763464ebd5Sriastradhvoid
2773464ebd5Sriastradh_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
2783464ebd5Sriastradh{
27901e04c3fSmrg   driver->NewTransformFeedback = new_transform_feedback_fallback;
2807ec681f3Smrg   driver->DeleteTransformFeedback = _mesa_delete_transform_feedback_object;
28101e04c3fSmrg   driver->BeginTransformFeedback = begin_transform_feedback_fallback;
28201e04c3fSmrg   driver->EndTransformFeedback = end_transform_feedback_fallback;
28301e04c3fSmrg   driver->PauseTransformFeedback = pause_transform_feedback_fallback;
28401e04c3fSmrg   driver->ResumeTransformFeedback = resume_transform_feedback_fallback;
2853464ebd5Sriastradh}
2863464ebd5Sriastradh
2873464ebd5Sriastradh
288af69d88dSmrg/**
289af69d88dSmrg * Fill in the correct Size value for each buffer in \c obj.
290af69d88dSmrg *
291af69d88dSmrg * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
292af69d88dSmrg * Targets"):
293af69d88dSmrg *
294af69d88dSmrg *   BindBufferBase binds the entire buffer, even when the size of the buffer
295af69d88dSmrg *   is changed after the binding is established. It is equivalent to calling
296af69d88dSmrg *   BindBufferRange with offset zero, while size is determined by the size of
297af69d88dSmrg *   the bound buffer at the time the binding is used.
298af69d88dSmrg *
299af69d88dSmrg *   Regardless of the size specified with BindBufferRange, or indirectly with
300af69d88dSmrg *   BindBufferBase, the GL will never read or write beyond the end of a bound
301af69d88dSmrg *   buffer. In some cases this constraint may result in visibly different
302af69d88dSmrg *   behavior when a buffer overflow would otherwise result, such as described
303af69d88dSmrg *   for transform feedback operations in section 13.2.2.
304af69d88dSmrg */
305af69d88dSmrgstatic void
306af69d88dSmrgcompute_transform_feedback_buffer_sizes(
307af69d88dSmrg      struct gl_transform_feedback_object *obj)
3083464ebd5Sriastradh{
309af69d88dSmrg   unsigned i = 0;
310af69d88dSmrg   for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
311af69d88dSmrg      GLintptr offset = obj->Offset[i];
312af69d88dSmrg      GLsizeiptr buffer_size
313af69d88dSmrg         = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
314af69d88dSmrg      GLsizeiptr available_space
315af69d88dSmrg         = buffer_size <= offset ? 0 : buffer_size - offset;
316af69d88dSmrg      GLsizeiptr computed_size;
317af69d88dSmrg      if (obj->RequestedSize[i] == 0) {
318af69d88dSmrg         /* No size was specified at the time the buffer was bound, so allow
319af69d88dSmrg          * writing to all available space in the buffer.
320af69d88dSmrg          */
321af69d88dSmrg         computed_size = available_space;
322af69d88dSmrg      } else {
323af69d88dSmrg         /* A size was specified at the time the buffer was bound, however
324af69d88dSmrg          * it's possible that the buffer has shrunk since then.  So only
325af69d88dSmrg          * allow writing to the minimum of the specified size and the space
326af69d88dSmrg          * available.
327af69d88dSmrg          */
328af69d88dSmrg         computed_size = MIN2(available_space, obj->RequestedSize[i]);
329af69d88dSmrg      }
330af69d88dSmrg
331af69d88dSmrg      /* Legal sizes must be multiples of four, so round down if necessary. */
332af69d88dSmrg      obj->Size[i] = computed_size & ~0x3;
333af69d88dSmrg   }
334af69d88dSmrg}
335af69d88dSmrg
336af69d88dSmrg
337af69d88dSmrg/**
338af69d88dSmrg * Compute the maximum number of vertices that can be written to the currently
339af69d88dSmrg * enabled transform feedback buffers without overflowing any of them.
340af69d88dSmrg */
341af69d88dSmrgunsigned
34201e04c3fSmrg_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
343af69d88dSmrg      const struct gl_transform_feedback_object *obj,
344af69d88dSmrg      const struct gl_transform_feedback_info *info)
345af69d88dSmrg{
346af69d88dSmrg   unsigned max_index = 0xffffffff;
347af69d88dSmrg   unsigned i;
348af69d88dSmrg
34901e04c3fSmrg   for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
35001e04c3fSmrg      if ((info->ActiveBuffers >> i) & 1) {
35101e04c3fSmrg         unsigned stride = info->Buffers[i].Stride;
35201e04c3fSmrg         unsigned max_for_this_buffer;
353af69d88dSmrg
35401e04c3fSmrg         /* Skip any inactive buffers, which have a stride of 0. */
35501e04c3fSmrg         if (stride == 0)
35601e04c3fSmrg            continue;
357af69d88dSmrg
35801e04c3fSmrg         max_for_this_buffer = obj->Size[i] / (4 * stride);
35901e04c3fSmrg         max_index = MIN2(max_index, max_for_this_buffer);
36001e04c3fSmrg      }
361af69d88dSmrg   }
362af69d88dSmrg
363af69d88dSmrg   return max_index;
3643464ebd5Sriastradh}
3653464ebd5Sriastradh
3663464ebd5Sriastradh
3673464ebd5Sriastradh/**
3683464ebd5Sriastradh ** Begin API functions
3693464ebd5Sriastradh **/
3703464ebd5Sriastradh
3713464ebd5Sriastradh
372af69d88dSmrg/**
373af69d88dSmrg * Figure out which stage of the pipeline is the source of transform feedback
37401e04c3fSmrg * data given the current context state, and return its gl_program.
375af69d88dSmrg *
376af69d88dSmrg * If no active program can generate transform feedback data (i.e. no vertex
377af69d88dSmrg * shader is active), returns NULL.
378af69d88dSmrg */
37901e04c3fSmrgstatic struct gl_program *
380af69d88dSmrgget_xfb_source(struct gl_context *ctx)
381af69d88dSmrg{
382af69d88dSmrg   int i;
383af69d88dSmrg   for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
384af69d88dSmrg      if (ctx->_Shader->CurrentProgram[i] != NULL)
385af69d88dSmrg         return ctx->_Shader->CurrentProgram[i];
386af69d88dSmrg   }
387af69d88dSmrg   return NULL;
388af69d88dSmrg}
389af69d88dSmrg
390af69d88dSmrg
39101e04c3fSmrgstatic ALWAYS_INLINE void
39201e04c3fSmrgbegin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
3933464ebd5Sriastradh{
3943464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
395af69d88dSmrg   struct gl_transform_feedback_info *info = NULL;
39601e04c3fSmrg   struct gl_program *source;
397af69d88dSmrg   GLuint i;
398af69d88dSmrg   unsigned vertices_per_prim;
3993464ebd5Sriastradh
4003464ebd5Sriastradh   obj = ctx->TransformFeedback.CurrentObject;
4013464ebd5Sriastradh
402af69d88dSmrg   /* Figure out what pipeline stage is the source of data for transform
403af69d88dSmrg    * feedback.
404af69d88dSmrg    */
405af69d88dSmrg   source = get_xfb_source(ctx);
40601e04c3fSmrg   if (!no_error && source == NULL) {
407af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
408af69d88dSmrg                  "glBeginTransformFeedback(no program active)");
409af69d88dSmrg      return;
410af69d88dSmrg   }
411af69d88dSmrg
41201e04c3fSmrg   info = source->sh.LinkedTransformFeedback;
413af69d88dSmrg
41401e04c3fSmrg   if (!no_error && info->NumOutputs == 0) {
415af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
416af69d88dSmrg                  "glBeginTransformFeedback(no varyings to record)");
417af69d88dSmrg      return;
418af69d88dSmrg   }
419af69d88dSmrg
4203464ebd5Sriastradh   switch (mode) {
4213464ebd5Sriastradh   case GL_POINTS:
422af69d88dSmrg      vertices_per_prim = 1;
423af69d88dSmrg      break;
4243464ebd5Sriastradh   case GL_LINES:
425af69d88dSmrg      vertices_per_prim = 2;
426af69d88dSmrg      break;
4273464ebd5Sriastradh   case GL_TRIANGLES:
428af69d88dSmrg      vertices_per_prim = 3;
4293464ebd5Sriastradh      break;
4303464ebd5Sriastradh   default:
43101e04c3fSmrg      if (!no_error) {
43201e04c3fSmrg         _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
43301e04c3fSmrg         return;
43401e04c3fSmrg      } else {
43501e04c3fSmrg         /* Stop compiler warnings */
43601e04c3fSmrg         unreachable("Error in API use when using KHR_no_error");
43701e04c3fSmrg      }
4383464ebd5Sriastradh   }
4393464ebd5Sriastradh
44001e04c3fSmrg   if (!no_error) {
44101e04c3fSmrg      if (obj->Active) {
442af69d88dSmrg         _mesa_error(ctx, GL_INVALID_OPERATION,
44301e04c3fSmrg                     "glBeginTransformFeedback(already active)");
444af69d88dSmrg         return;
445af69d88dSmrg      }
44601e04c3fSmrg
44701e04c3fSmrg      for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
44801e04c3fSmrg         if ((info->ActiveBuffers >> i) & 1) {
44901e04c3fSmrg            if (obj->BufferNames[i] == 0) {
45001e04c3fSmrg               _mesa_error(ctx, GL_INVALID_OPERATION,
45101e04c3fSmrg                           "glBeginTransformFeedback(binding point %d does not "
45201e04c3fSmrg                           "have a buffer object bound)", i);
45301e04c3fSmrg               return;
45401e04c3fSmrg            }
45501e04c3fSmrg         }
45601e04c3fSmrg      }
457af69d88dSmrg   }
458af69d88dSmrg
4597ec681f3Smrg   FLUSH_VERTICES(ctx, 0, 0);
460af69d88dSmrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
461af69d88dSmrg
4623464ebd5Sriastradh   obj->Active = GL_TRUE;
4633464ebd5Sriastradh   ctx->TransformFeedback.Mode = mode;
4643464ebd5Sriastradh
465af69d88dSmrg   compute_transform_feedback_buffer_sizes(obj);
466af69d88dSmrg
467af69d88dSmrg   if (_mesa_is_gles3(ctx)) {
468af69d88dSmrg      /* In GLES3, we are required to track the usage of the transform
469af69d88dSmrg       * feedback buffer and report INVALID_OPERATION if a draw call tries to
470af69d88dSmrg       * exceed it.  So compute the maximum number of vertices that we can
471af69d88dSmrg       * write without overflowing any of the buffers currently being used for
472af69d88dSmrg       * feedback.
473af69d88dSmrg       */
474af69d88dSmrg      unsigned max_vertices
47501e04c3fSmrg         = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
476af69d88dSmrg      obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
477af69d88dSmrg   }
478af69d88dSmrg
47901e04c3fSmrg   if (obj->program != source) {
480af69d88dSmrg      ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
48101e04c3fSmrg      _mesa_reference_program_(ctx, &obj->program, source);
48201e04c3fSmrg      obj->program = source;
483af69d88dSmrg   }
484af69d88dSmrg
4853464ebd5Sriastradh   assert(ctx->Driver.BeginTransformFeedback);
4863464ebd5Sriastradh   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
4877ec681f3Smrg   _mesa_update_valid_to_render_state(ctx);
4883464ebd5Sriastradh}
4893464ebd5Sriastradh
4903464ebd5Sriastradh
49101e04c3fSmrgvoid GLAPIENTRY
49201e04c3fSmrg_mesa_BeginTransformFeedback_no_error(GLenum mode)
49301e04c3fSmrg{
49401e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
49501e04c3fSmrg   begin_transform_feedback(ctx, mode, true);
49601e04c3fSmrg}
49701e04c3fSmrg
49801e04c3fSmrg
49901e04c3fSmrgvoid GLAPIENTRY
50001e04c3fSmrg_mesa_BeginTransformFeedback(GLenum mode)
50101e04c3fSmrg{
50201e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
50301e04c3fSmrg   begin_transform_feedback(ctx, mode, false);
50401e04c3fSmrg}
50501e04c3fSmrg
50601e04c3fSmrg
50701e04c3fSmrgstatic void
50801e04c3fSmrgend_transform_feedback(struct gl_context *ctx,
50901e04c3fSmrg                       struct gl_transform_feedback_object *obj)
51001e04c3fSmrg{
5117ec681f3Smrg   FLUSH_VERTICES(ctx, 0, 0);
51201e04c3fSmrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
51301e04c3fSmrg
51401e04c3fSmrg   assert(ctx->Driver.EndTransformFeedback);
51501e04c3fSmrg   ctx->Driver.EndTransformFeedback(ctx, obj);
51601e04c3fSmrg
51701e04c3fSmrg   _mesa_reference_program_(ctx, &obj->program, NULL);
51801e04c3fSmrg   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
51901e04c3fSmrg   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
52001e04c3fSmrg   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
5217ec681f3Smrg   _mesa_update_valid_to_render_state(ctx);
52201e04c3fSmrg}
52301e04c3fSmrg
52401e04c3fSmrg
52501e04c3fSmrgvoid GLAPIENTRY
52601e04c3fSmrg_mesa_EndTransformFeedback_no_error(void)
52701e04c3fSmrg{
52801e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
52901e04c3fSmrg   end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
53001e04c3fSmrg}
53101e04c3fSmrg
53201e04c3fSmrg
5333464ebd5Sriastradhvoid GLAPIENTRY
5343464ebd5Sriastradh_mesa_EndTransformFeedback(void)
5353464ebd5Sriastradh{
5363464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
5373464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
5383464ebd5Sriastradh
5393464ebd5Sriastradh   obj = ctx->TransformFeedback.CurrentObject;
5403464ebd5Sriastradh
5413464ebd5Sriastradh   if (!obj->Active) {
5423464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
5433464ebd5Sriastradh                  "glEndTransformFeedback(not active)");
5443464ebd5Sriastradh      return;
5453464ebd5Sriastradh   }
5463464ebd5Sriastradh
54701e04c3fSmrg   end_transform_feedback(ctx, obj);
5483464ebd5Sriastradh}
5493464ebd5Sriastradh
5503464ebd5Sriastradh
5513464ebd5Sriastradh/**
5523464ebd5Sriastradh * Helper used by BindBufferRange() and BindBufferBase().
5533464ebd5Sriastradh */
5543464ebd5Sriastradhstatic void
55501e04c3fSmrgbind_buffer_range(struct gl_context *ctx,
55601e04c3fSmrg                  struct gl_transform_feedback_object *obj,
55701e04c3fSmrg                  GLuint index,
5583464ebd5Sriastradh                  struct gl_buffer_object *bufObj,
55901e04c3fSmrg                  GLintptr offset, GLsizeiptr size,
56001e04c3fSmrg                  bool dsa)
5613464ebd5Sriastradh{
562af69d88dSmrg   /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
563af69d88dSmrg    * transform feedback buffers can't be changed while transform feedback is
564af69d88dSmrg    * active.
565af69d88dSmrg    */
566af69d88dSmrg
56701e04c3fSmrg   if (!dsa) {
56801e04c3fSmrg      /* The general binding point */
56901e04c3fSmrg      _mesa_reference_buffer_object(ctx,
57001e04c3fSmrg                                    &ctx->TransformFeedback.CurrentBuffer,
57101e04c3fSmrg                                    bufObj);
57201e04c3fSmrg   }
5733464ebd5Sriastradh
5743464ebd5Sriastradh   /* The per-attribute binding point */
575af69d88dSmrg   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
5763464ebd5Sriastradh}
5773464ebd5Sriastradh
5783464ebd5Sriastradh
5793464ebd5Sriastradh/**
58001e04c3fSmrg * Validate the buffer object to receive transform feedback results.  Plus,
58101e04c3fSmrg * validate the starting offset to place the results, and max size.
58201e04c3fSmrg * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
58301e04c3fSmrg * functions.
5843464ebd5Sriastradh */
58501e04c3fSmrgbool
58601e04c3fSmrg_mesa_validate_buffer_range_xfb(struct gl_context *ctx,
58701e04c3fSmrg                                struct gl_transform_feedback_object *obj,
58801e04c3fSmrg                                GLuint index, struct gl_buffer_object *bufObj,
58901e04c3fSmrg                                GLintptr offset, GLsizeiptr size, bool dsa)
5903464ebd5Sriastradh{
59101e04c3fSmrg   const char *gl_methd_name;
59201e04c3fSmrg   if (dsa)
59301e04c3fSmrg      gl_methd_name = "glTransformFeedbackBufferRange";
59401e04c3fSmrg   else
59501e04c3fSmrg      gl_methd_name = "glBindBufferRange";
5963464ebd5Sriastradh
5973464ebd5Sriastradh
5983464ebd5Sriastradh   if (obj->Active) {
59901e04c3fSmrg      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
60001e04c3fSmrg                  gl_methd_name);
60101e04c3fSmrg      return false;
6023464ebd5Sriastradh   }
6033464ebd5Sriastradh
604af69d88dSmrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
60501e04c3fSmrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
60601e04c3fSmrg       * generated if index is greater than or equal to the number of binding
60701e04c3fSmrg       * points for transform feedback, as described in section 6.7.1."
60801e04c3fSmrg       */
60901e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
61001e04c3fSmrg                  gl_methd_name, index);
61101e04c3fSmrg      return false;
6123464ebd5Sriastradh   }
6133464ebd5Sriastradh
614af69d88dSmrg   if (size & 0x3) {
61501e04c3fSmrg      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
61601e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
61701e04c3fSmrg                  "four)", gl_methd_name, (int) size);
61801e04c3fSmrg      return false;
61901e04c3fSmrg   }
6203464ebd5Sriastradh
6213464ebd5Sriastradh   if (offset & 0x3) {
62201e04c3fSmrg      /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
62301e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
62401e04c3fSmrg                  "of four)", gl_methd_name, (int) offset);
62501e04c3fSmrg      return false;
62601e04c3fSmrg   }
62701e04c3fSmrg
62801e04c3fSmrg   if (offset < 0) {
62901e04c3fSmrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
63001e04c3fSmrg       * generated by BindBufferRange if offset is negative."
63101e04c3fSmrg       *
63201e04c3fSmrg       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
63301e04c3fSmrg       * is generated by TransformFeedbackBufferRange if offset is negative."
63401e04c3fSmrg       */
63501e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
63601e04c3fSmrg                  gl_methd_name,
63701e04c3fSmrg                  (int) offset);
63801e04c3fSmrg      return false;
63901e04c3fSmrg   }
6403464ebd5Sriastradh
6417ec681f3Smrg   if (size <= 0 && (dsa || bufObj)) {
64201e04c3fSmrg      /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
64301e04c3fSmrg       * generated by BindBufferRange if buffer is non-zero and size is less
64401e04c3fSmrg       * than or equal to zero."
64501e04c3fSmrg       *
64601e04c3fSmrg       * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
64701e04c3fSmrg       * is generated by TransformFeedbackBufferRange if size is less than or
64801e04c3fSmrg       * equal to zero."
64901e04c3fSmrg       */
65001e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
65101e04c3fSmrg                  gl_methd_name, (int) size);
65201e04c3fSmrg      return false;
65301e04c3fSmrg   }
65401e04c3fSmrg
65501e04c3fSmrg   return true;
6563464ebd5Sriastradh}
6573464ebd5Sriastradh
6583464ebd5Sriastradh
6593464ebd5Sriastradh/**
660af69d88dSmrg * Specify a buffer object to receive transform feedback results.
6613464ebd5Sriastradh * As above, but start at offset = 0.
66201e04c3fSmrg * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
66301e04c3fSmrg * functions.
6643464ebd5Sriastradh */
665af69d88dSmrgvoid
666af69d88dSmrg_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
66701e04c3fSmrg                                          struct gl_transform_feedback_object *obj,
66801e04c3fSmrg                                          GLuint index,
66901e04c3fSmrg                                          struct gl_buffer_object *bufObj,
67001e04c3fSmrg                                          bool dsa)
6713464ebd5Sriastradh{
6723464ebd5Sriastradh   if (obj->Active) {
6733464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
67401e04c3fSmrg                  "%s(transform feedback active)",
67501e04c3fSmrg                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
6763464ebd5Sriastradh      return;
6773464ebd5Sriastradh   }
6783464ebd5Sriastradh
679af69d88dSmrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
68001e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
68101e04c3fSmrg                  dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
68201e04c3fSmrg                  index);
68301e04c3fSmrg      return;
68401e04c3fSmrg   }
68501e04c3fSmrg
68601e04c3fSmrg   bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
68701e04c3fSmrg}
68801e04c3fSmrg
68901e04c3fSmrg/**
69001e04c3fSmrg * Wrapper around lookup_transform_feedback_object that throws
69101e04c3fSmrg * GL_INVALID_OPERATION if id is not in the hash table. After calling
69201e04c3fSmrg * _mesa_error, it returns NULL.
69301e04c3fSmrg */
69401e04c3fSmrgstatic struct gl_transform_feedback_object *
69501e04c3fSmrglookup_transform_feedback_object_err(struct gl_context *ctx,
69601e04c3fSmrg                                     GLuint xfb, const char* func)
69701e04c3fSmrg{
69801e04c3fSmrg   struct gl_transform_feedback_object *obj;
69901e04c3fSmrg
70001e04c3fSmrg   obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
70101e04c3fSmrg   if (!obj) {
70201e04c3fSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
70301e04c3fSmrg                  "%s(xfb=%u: non-generated object name)", func, xfb);
70401e04c3fSmrg   }
70501e04c3fSmrg
70601e04c3fSmrg   return obj;
70701e04c3fSmrg}
70801e04c3fSmrg
70901e04c3fSmrg/**
71001e04c3fSmrg * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
71101e04c3fSmrg * is not in the hash table. Specialised version for the
71201e04c3fSmrg * transform-feedback-related functions. After calling _mesa_error, it
71301e04c3fSmrg * returns NULL.
71401e04c3fSmrg */
71501e04c3fSmrgstatic struct gl_buffer_object *
71601e04c3fSmrglookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
7177ec681f3Smrg                                        GLuint buffer, const char* func,
7187ec681f3Smrg                                        bool *error)
71901e04c3fSmrg{
7207ec681f3Smrg   struct gl_buffer_object *bufObj = NULL;
7217ec681f3Smrg
7227ec681f3Smrg   *error = false;
72301e04c3fSmrg
72401e04c3fSmrg   /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
72501e04c3fSmrg    * name of an existing buffer object.
72601e04c3fSmrg    */
7277ec681f3Smrg   if (buffer) {
72801e04c3fSmrg      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
72901e04c3fSmrg      if (!bufObj) {
73001e04c3fSmrg         _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
73101e04c3fSmrg                     buffer);
7327ec681f3Smrg         *error = true;
73301e04c3fSmrg      }
73401e04c3fSmrg   }
73501e04c3fSmrg
73601e04c3fSmrg   return bufObj;
73701e04c3fSmrg}
73801e04c3fSmrg
73901e04c3fSmrgvoid GLAPIENTRY
74001e04c3fSmrg_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
74101e04c3fSmrg{
74201e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
74301e04c3fSmrg   struct gl_transform_feedback_object *obj;
74401e04c3fSmrg   struct gl_buffer_object *bufObj;
74501e04c3fSmrg
74601e04c3fSmrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
74701e04c3fSmrg                                              "glTransformFeedbackBufferBase");
74801e04c3fSmrg   if (!obj) {
74901e04c3fSmrg      return;
75001e04c3fSmrg   }
75101e04c3fSmrg
7527ec681f3Smrg   bool error;
75301e04c3fSmrg   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
7547ec681f3Smrg                                              "glTransformFeedbackBufferBase",
7557ec681f3Smrg                                                    &error);
7567ec681f3Smrg   if (error) {
7573464ebd5Sriastradh      return;
7583464ebd5Sriastradh   }
7593464ebd5Sriastradh
76001e04c3fSmrg   _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
7613464ebd5Sriastradh}
7623464ebd5Sriastradh
76301e04c3fSmrgvoid GLAPIENTRY
76401e04c3fSmrg_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
76501e04c3fSmrg                                   GLintptr offset, GLsizeiptr size)
76601e04c3fSmrg{
76701e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
76801e04c3fSmrg   struct gl_transform_feedback_object *obj;
76901e04c3fSmrg   struct gl_buffer_object *bufObj;
77001e04c3fSmrg
77101e04c3fSmrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
77201e04c3fSmrg                                              "glTransformFeedbackBufferRange");
77301e04c3fSmrg   if (!obj) {
77401e04c3fSmrg      return;
77501e04c3fSmrg   }
77601e04c3fSmrg
7777ec681f3Smrg   bool error;
77801e04c3fSmrg   bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
7797ec681f3Smrg                                              "glTransformFeedbackBufferRange",
7807ec681f3Smrg                                                    &error);
7817ec681f3Smrg   if (error) {
78201e04c3fSmrg      return;
78301e04c3fSmrg   }
78401e04c3fSmrg
78501e04c3fSmrg   if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
78601e04c3fSmrg                                        size, true))
78701e04c3fSmrg      return;
78801e04c3fSmrg
78901e04c3fSmrg   /* The per-attribute binding point */
79001e04c3fSmrg   _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
79101e04c3fSmrg                                        size);
79201e04c3fSmrg}
7933464ebd5Sriastradh
7943464ebd5Sriastradh/**
795af69d88dSmrg * Specify a buffer object to receive transform feedback results, plus the
7963464ebd5Sriastradh * offset in the buffer to start placing results.
7973464ebd5Sriastradh * This function is part of GL_EXT_transform_feedback, but not GL3.
7983464ebd5Sriastradh */
79901e04c3fSmrgstatic ALWAYS_INLINE void
80001e04c3fSmrgbind_buffer_offset(struct gl_context *ctx,
80101e04c3fSmrg                   struct gl_transform_feedback_object *obj, GLuint index,
80201e04c3fSmrg                   GLuint buffer, GLintptr offset, bool no_error)
80301e04c3fSmrg{
80401e04c3fSmrg   struct gl_buffer_object *bufObj;
80501e04c3fSmrg
80601e04c3fSmrg   if (buffer == 0) {
8077ec681f3Smrg      bufObj = NULL;
80801e04c3fSmrg   } else {
80901e04c3fSmrg      bufObj = _mesa_lookup_bufferobj(ctx, buffer);
81001e04c3fSmrg      if (!no_error && !bufObj) {
81101e04c3fSmrg         _mesa_error(ctx, GL_INVALID_OPERATION,
81201e04c3fSmrg                     "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
81301e04c3fSmrg         return;
81401e04c3fSmrg      }
81501e04c3fSmrg   }
81601e04c3fSmrg
81701e04c3fSmrg   _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
81801e04c3fSmrg}
81901e04c3fSmrg
82001e04c3fSmrg
82101e04c3fSmrgvoid GLAPIENTRY
82201e04c3fSmrg_mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer,
82301e04c3fSmrg                                   GLintptr offset)
82401e04c3fSmrg{
82501e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
82601e04c3fSmrg   bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer,
82701e04c3fSmrg                      offset, true);
82801e04c3fSmrg}
82901e04c3fSmrg
83001e04c3fSmrg
8313464ebd5Sriastradhvoid GLAPIENTRY
8323464ebd5Sriastradh_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
8333464ebd5Sriastradh                          GLintptr offset)
8343464ebd5Sriastradh{
8353464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
8363464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
8373464ebd5Sriastradh
8383464ebd5Sriastradh   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
8393464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
8403464ebd5Sriastradh      return;
8413464ebd5Sriastradh   }
8423464ebd5Sriastradh
8433464ebd5Sriastradh   obj = ctx->TransformFeedback.CurrentObject;
8443464ebd5Sriastradh
8453464ebd5Sriastradh   if (obj->Active) {
8463464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
8473464ebd5Sriastradh                  "glBindBufferOffsetEXT(transform feedback active)");
8483464ebd5Sriastradh      return;
8493464ebd5Sriastradh   }
8503464ebd5Sriastradh
851af69d88dSmrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
8523464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_VALUE,
8533464ebd5Sriastradh                  "glBindBufferOffsetEXT(index=%d)", index);
8543464ebd5Sriastradh      return;
8553464ebd5Sriastradh   }
8563464ebd5Sriastradh
857af69d88dSmrg   if (offset & 0x3) {
858af69d88dSmrg      /* must be multiple of four */
859af69d88dSmrg      _mesa_error(ctx, GL_INVALID_VALUE,
860af69d88dSmrg                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
861af69d88dSmrg      return;
862af69d88dSmrg   }
863af69d88dSmrg
86401e04c3fSmrg   bind_buffer_offset(ctx, obj, index, buffer, offset, false);
86501e04c3fSmrg}
86601e04c3fSmrg
86701e04c3fSmrg
86801e04c3fSmrg/**
86901e04c3fSmrg * This function specifies the transform feedback outputs to be written
87001e04c3fSmrg * to the feedback buffer(s), and in what order.
87101e04c3fSmrg */
87201e04c3fSmrgstatic ALWAYS_INLINE void
87301e04c3fSmrgtransform_feedback_varyings(struct gl_context *ctx,
87401e04c3fSmrg                            struct gl_shader_program *shProg, GLsizei count,
87501e04c3fSmrg                            const GLchar *const *varyings, GLenum bufferMode)
87601e04c3fSmrg{
87701e04c3fSmrg   GLint i;
87801e04c3fSmrg
87901e04c3fSmrg   /* free existing varyings, if any */
88001e04c3fSmrg   for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
88101e04c3fSmrg      free(shProg->TransformFeedback.VaryingNames[i]);
882af69d88dSmrg   }
88301e04c3fSmrg   free(shProg->TransformFeedback.VaryingNames);
884af69d88dSmrg
88501e04c3fSmrg   /* allocate new memory for varying names */
88601e04c3fSmrg   shProg->TransformFeedback.VaryingNames =
88701e04c3fSmrg      malloc(count * sizeof(GLchar *));
88801e04c3fSmrg
88901e04c3fSmrg   if (!shProg->TransformFeedback.VaryingNames) {
89001e04c3fSmrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
8913464ebd5Sriastradh      return;
8923464ebd5Sriastradh   }
8933464ebd5Sriastradh
89401e04c3fSmrg   /* Save the new names and the count */
89501e04c3fSmrg   for (i = 0; i < count; i++) {
89601e04c3fSmrg      shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
89701e04c3fSmrg   }
89801e04c3fSmrg   shProg->TransformFeedback.NumVarying = count;
89901e04c3fSmrg
90001e04c3fSmrg   shProg->TransformFeedback.BufferMode = bufferMode;
90101e04c3fSmrg
90201e04c3fSmrg   /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
90301e04c3fSmrg    * the varyings won't be used until shader link time.
90401e04c3fSmrg    */
9053464ebd5Sriastradh}
9063464ebd5Sriastradh
9073464ebd5Sriastradh
90801e04c3fSmrgvoid GLAPIENTRY
90901e04c3fSmrg_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count,
91001e04c3fSmrg                                         const GLchar *const *varyings,
91101e04c3fSmrg                                         GLenum bufferMode)
91201e04c3fSmrg{
91301e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
91401e04c3fSmrg
91501e04c3fSmrg   struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
91601e04c3fSmrg   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
91701e04c3fSmrg}
91801e04c3fSmrg
9193464ebd5Sriastradhvoid GLAPIENTRY
9203464ebd5Sriastradh_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
921af69d88dSmrg                                const GLchar * const *varyings,
922af69d88dSmrg                                GLenum bufferMode)
9233464ebd5Sriastradh{
9243464ebd5Sriastradh   struct gl_shader_program *shProg;
925af69d88dSmrg   GLint i;
9263464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
9273464ebd5Sriastradh
928af69d88dSmrg   /* From the ARB_transform_feedback2 specification:
929af69d88dSmrg    * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
930af69d88dSmrg    *  if the current transform feedback object is active, even if paused."
931af69d88dSmrg    */
932af69d88dSmrg   if (ctx->TransformFeedback.CurrentObject->Active) {
933af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
934af69d88dSmrg               "glTransformFeedbackVaryings(current object is active)");
935af69d88dSmrg      return;
936af69d88dSmrg   }
937af69d88dSmrg
9383464ebd5Sriastradh   switch (bufferMode) {
9393464ebd5Sriastradh   case GL_INTERLEAVED_ATTRIBS:
9403464ebd5Sriastradh      break;
9413464ebd5Sriastradh   case GL_SEPARATE_ATTRIBS:
9423464ebd5Sriastradh      break;
9433464ebd5Sriastradh   default:
9443464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_ENUM,
9453464ebd5Sriastradh                  "glTransformFeedbackVaryings(bufferMode)");
9463464ebd5Sriastradh      return;
9473464ebd5Sriastradh   }
9483464ebd5Sriastradh
949af69d88dSmrg   if (count < 0 ||
950af69d88dSmrg       (bufferMode == GL_SEPARATE_ATTRIBS &&
951af69d88dSmrg        (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
9523464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_VALUE,
9533464ebd5Sriastradh                  "glTransformFeedbackVaryings(count=%d)", count);
9543464ebd5Sriastradh      return;
9553464ebd5Sriastradh   }
9563464ebd5Sriastradh
95701e04c3fSmrg   shProg = _mesa_lookup_shader_program_err(ctx, program,
95801e04c3fSmrg                                            "glTransformFeedbackVaryings");
95901e04c3fSmrg   if (!shProg)
9603464ebd5Sriastradh      return;
9613464ebd5Sriastradh
962af69d88dSmrg   if (ctx->Extensions.ARB_transform_feedback3) {
963af69d88dSmrg      if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
964af69d88dSmrg         unsigned buffers = 1;
965af69d88dSmrg
966af69d88dSmrg         for (i = 0; i < count; i++) {
967af69d88dSmrg            if (strcmp(varyings[i], "gl_NextBuffer") == 0)
968af69d88dSmrg               buffers++;
969af69d88dSmrg         }
970af69d88dSmrg
971af69d88dSmrg         if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
972af69d88dSmrg            _mesa_error(ctx, GL_INVALID_OPERATION,
973af69d88dSmrg                        "glTransformFeedbackVaryings(too many gl_NextBuffer "
97401e04c3fSmrg                        "occurrences)");
975af69d88dSmrg            return;
976af69d88dSmrg         }
977af69d88dSmrg      } else {
978af69d88dSmrg         for (i = 0; i < count; i++) {
979af69d88dSmrg            if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
980af69d88dSmrg                strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
981af69d88dSmrg                strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
982af69d88dSmrg                strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
983af69d88dSmrg                strcmp(varyings[i], "gl_SkipComponents4") == 0) {
984af69d88dSmrg               _mesa_error(ctx, GL_INVALID_OPERATION,
985af69d88dSmrg                           "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
986af69d88dSmrg                           "varying=%s)",
987af69d88dSmrg                           varyings[i]);
988af69d88dSmrg               return;
989af69d88dSmrg            }
990af69d88dSmrg         }
991af69d88dSmrg      }
992af69d88dSmrg   }
993af69d88dSmrg
99401e04c3fSmrg   transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
9953464ebd5Sriastradh}
9963464ebd5Sriastradh
9973464ebd5Sriastradh
9983464ebd5Sriastradh/**
999af69d88dSmrg * Get info about the transform feedback outputs which are to be written
10003464ebd5Sriastradh * to the feedback buffer(s).
10013464ebd5Sriastradh */
10023464ebd5Sriastradhvoid GLAPIENTRY
10033464ebd5Sriastradh_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
10043464ebd5Sriastradh                                  GLsizei bufSize, GLsizei *length,
10053464ebd5Sriastradh                                  GLsizei *size, GLenum *type, GLchar *name)
10063464ebd5Sriastradh{
10073464ebd5Sriastradh   const struct gl_shader_program *shProg;
100801e04c3fSmrg   struct gl_program_resource *res;
10093464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
10103464ebd5Sriastradh
101101e04c3fSmrg   shProg = _mesa_lookup_shader_program_err(ctx, program,
101201e04c3fSmrg                                            "glGetTransformFeedbackVarying");
101301e04c3fSmrg   if (!shProg)
10143464ebd5Sriastradh      return;
10153464ebd5Sriastradh
101601e04c3fSmrg   res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
101701e04c3fSmrg                                           GL_TRANSFORM_FEEDBACK_VARYING,
101801e04c3fSmrg                                           index);
101901e04c3fSmrg   if (!res) {
10203464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_VALUE,
1021af69d88dSmrg                  "glGetTransformFeedbackVarying(index=%u)", index);
10223464ebd5Sriastradh      return;
10233464ebd5Sriastradh   }
10243464ebd5Sriastradh
1025af69d88dSmrg   /* return the varying's name and length */
102601e04c3fSmrg   _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
10273464ebd5Sriastradh
1028af69d88dSmrg   /* return the datatype and value's size (in datatype units) */
1029af69d88dSmrg   if (type)
103001e04c3fSmrg      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
103101e04c3fSmrg                                  res, index, GL_TYPE, (GLint*) type,
10327ec681f3Smrg                                  false, "glGetTransformFeedbackVarying");
1033af69d88dSmrg   if (size)
103401e04c3fSmrg      _mesa_program_resource_prop((struct gl_shader_program *) shProg,
103501e04c3fSmrg                                  res, index, GL_ARRAY_SIZE, (GLint*) size,
10367ec681f3Smrg                                  false, "glGetTransformFeedbackVarying");
10373464ebd5Sriastradh}
10383464ebd5Sriastradh
10393464ebd5Sriastradh
10403464ebd5Sriastradh
1041af69d88dSmrgstruct gl_transform_feedback_object *
1042af69d88dSmrg_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
10433464ebd5Sriastradh{
104401e04c3fSmrg   /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
104501e04c3fSmrg    * the default transform feedback object, or the name of an existing
104601e04c3fSmrg    * transform feedback object."
104701e04c3fSmrg    */
10483464ebd5Sriastradh   if (name == 0) {
10493464ebd5Sriastradh      return ctx->TransformFeedback.DefaultObject;
10503464ebd5Sriastradh   }
10513464ebd5Sriastradh   else
10523464ebd5Sriastradh      return (struct gl_transform_feedback_object *)
105301e04c3fSmrg         _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
10543464ebd5Sriastradh}
10553464ebd5Sriastradh
105601e04c3fSmrgstatic void
105701e04c3fSmrgcreate_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
105801e04c3fSmrg                           bool dsa)
10593464ebd5Sriastradh{
106001e04c3fSmrg   const char* func;
106101e04c3fSmrg
106201e04c3fSmrg   if (dsa)
106301e04c3fSmrg      func = "glCreateTransformFeedbacks";
106401e04c3fSmrg   else
106501e04c3fSmrg      func = "glGenTransformFeedbacks";
10663464ebd5Sriastradh
10673464ebd5Sriastradh   if (n < 0) {
106801e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
10693464ebd5Sriastradh      return;
10703464ebd5Sriastradh   }
10713464ebd5Sriastradh
107201e04c3fSmrg   if (!ids)
10733464ebd5Sriastradh      return;
10743464ebd5Sriastradh
10757ec681f3Smrg   if (_mesa_HashFindFreeKeys(ctx->TransformFeedback.Objects, ids, n)) {
10763464ebd5Sriastradh      GLsizei i;
10773464ebd5Sriastradh      for (i = 0; i < n; i++) {
10783464ebd5Sriastradh         struct gl_transform_feedback_object *obj
10797ec681f3Smrg            = ctx->Driver.NewTransformFeedback(ctx, ids[i]);
10803464ebd5Sriastradh         if (!obj) {
108101e04c3fSmrg            _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
10823464ebd5Sriastradh            return;
10833464ebd5Sriastradh         }
10847ec681f3Smrg         _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, ids[i],
10857ec681f3Smrg                                obj, true);
108601e04c3fSmrg         if (dsa) {
108701e04c3fSmrg            /* this is normally done at bind time in the non-dsa case */
108801e04c3fSmrg            obj->EverBound = GL_TRUE;
108901e04c3fSmrg         }
10903464ebd5Sriastradh      }
10913464ebd5Sriastradh   }
10923464ebd5Sriastradh   else {
109301e04c3fSmrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
10943464ebd5Sriastradh   }
10953464ebd5Sriastradh}
10963464ebd5Sriastradh
109701e04c3fSmrg/**
109801e04c3fSmrg * Create new transform feedback objects.   Transform feedback objects
109901e04c3fSmrg * encapsulate the state related to transform feedback to allow quickly
110001e04c3fSmrg * switching state (and drawing the results, below).
110101e04c3fSmrg * Part of GL_ARB_transform_feedback2.
110201e04c3fSmrg */
110301e04c3fSmrgvoid GLAPIENTRY
110401e04c3fSmrg_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
110501e04c3fSmrg{
110601e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
110701e04c3fSmrg
110801e04c3fSmrg   /* GenTransformFeedbacks should just reserve the object names that a
110901e04c3fSmrg    * subsequent call to BindTransformFeedback should actively create. For
111001e04c3fSmrg    * the sake of simplicity, we reserve the names and create the objects
111101e04c3fSmrg    * straight away.
111201e04c3fSmrg    */
111301e04c3fSmrg
111401e04c3fSmrg   create_transform_feedbacks(ctx, n, names, false);
111501e04c3fSmrg}
111601e04c3fSmrg
111701e04c3fSmrg/**
111801e04c3fSmrg * Create new transform feedback objects.   Transform feedback objects
111901e04c3fSmrg * encapsulate the state related to transform feedback to allow quickly
112001e04c3fSmrg * switching state (and drawing the results, below).
112101e04c3fSmrg * Part of GL_ARB_direct_state_access.
112201e04c3fSmrg */
112301e04c3fSmrgvoid GLAPIENTRY
112401e04c3fSmrg_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
112501e04c3fSmrg{
112601e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
112701e04c3fSmrg
112801e04c3fSmrg   create_transform_feedbacks(ctx, n, names, true);
112901e04c3fSmrg}
113001e04c3fSmrg
11313464ebd5Sriastradh
11323464ebd5Sriastradh/**
11333464ebd5Sriastradh * Is the given ID a transform feedback object?
11343464ebd5Sriastradh * Part of GL_ARB_transform_feedback2.
11353464ebd5Sriastradh */
11363464ebd5SriastradhGLboolean GLAPIENTRY
11373464ebd5Sriastradh_mesa_IsTransformFeedback(GLuint name)
11383464ebd5Sriastradh{
1139af69d88dSmrg   struct gl_transform_feedback_object *obj;
11403464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
11413464ebd5Sriastradh
11423464ebd5Sriastradh   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
11433464ebd5Sriastradh
1144af69d88dSmrg   if (name == 0)
11453464ebd5Sriastradh      return GL_FALSE;
1146af69d88dSmrg
1147af69d88dSmrg   obj = _mesa_lookup_transform_feedback_object(ctx, name);
1148af69d88dSmrg   if (obj == NULL)
1149af69d88dSmrg      return GL_FALSE;
1150af69d88dSmrg
1151af69d88dSmrg   return obj->EverBound;
11523464ebd5Sriastradh}
11533464ebd5Sriastradh
11543464ebd5Sriastradh
11553464ebd5Sriastradh/**
11563464ebd5Sriastradh * Bind the given transform feedback object.
11573464ebd5Sriastradh * Part of GL_ARB_transform_feedback2.
11583464ebd5Sriastradh */
115901e04c3fSmrgstatic ALWAYS_INLINE void
116001e04c3fSmrgbind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error)
116101e04c3fSmrg{
116201e04c3fSmrg   struct gl_transform_feedback_object *obj;
116301e04c3fSmrg
116401e04c3fSmrg   obj = _mesa_lookup_transform_feedback_object(ctx, name);
116501e04c3fSmrg   if (!no_error && !obj) {
116601e04c3fSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
116701e04c3fSmrg                  "glBindTransformFeedback(name=%u)", name);
116801e04c3fSmrg      return;
116901e04c3fSmrg   }
117001e04c3fSmrg
117101e04c3fSmrg   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
117201e04c3fSmrg                                       obj);
117301e04c3fSmrg}
117401e04c3fSmrg
117501e04c3fSmrg
117601e04c3fSmrgvoid GLAPIENTRY
117701e04c3fSmrg_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name)
117801e04c3fSmrg{
117901e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
118001e04c3fSmrg   bind_transform_feedback(ctx, name, true);
118101e04c3fSmrg}
118201e04c3fSmrg
118301e04c3fSmrg
11843464ebd5Sriastradhvoid GLAPIENTRY
11853464ebd5Sriastradh_mesa_BindTransformFeedback(GLenum target, GLuint name)
11863464ebd5Sriastradh{
11873464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
11883464ebd5Sriastradh
11893464ebd5Sriastradh   if (target != GL_TRANSFORM_FEEDBACK) {
11903464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
11913464ebd5Sriastradh      return;
11923464ebd5Sriastradh   }
11933464ebd5Sriastradh
1194af69d88dSmrg   if (_mesa_is_xfb_active_and_unpaused(ctx)) {
11953464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
11963464ebd5Sriastradh              "glBindTransformFeedback(transform is active, or not paused)");
11973464ebd5Sriastradh      return;
11983464ebd5Sriastradh   }
11993464ebd5Sriastradh
120001e04c3fSmrg   bind_transform_feedback(ctx, name, false);
12013464ebd5Sriastradh}
12023464ebd5Sriastradh
12033464ebd5Sriastradh
12043464ebd5Sriastradh/**
12053464ebd5Sriastradh * Delete the given transform feedback objects.
12063464ebd5Sriastradh * Part of GL_ARB_transform_feedback2.
12073464ebd5Sriastradh */
12083464ebd5Sriastradhvoid GLAPIENTRY
12093464ebd5Sriastradh_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
12103464ebd5Sriastradh{
12113464ebd5Sriastradh   GLint i;
12123464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
12133464ebd5Sriastradh
12143464ebd5Sriastradh   if (n < 0) {
12153464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
12163464ebd5Sriastradh      return;
12173464ebd5Sriastradh   }
12183464ebd5Sriastradh
12193464ebd5Sriastradh   if (!names)
12203464ebd5Sriastradh      return;
12213464ebd5Sriastradh
12223464ebd5Sriastradh   for (i = 0; i < n; i++) {
12233464ebd5Sriastradh      if (names[i] > 0) {
12243464ebd5Sriastradh         struct gl_transform_feedback_object *obj
1225af69d88dSmrg            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
12263464ebd5Sriastradh         if (obj) {
12273464ebd5Sriastradh            if (obj->Active) {
12283464ebd5Sriastradh               _mesa_error(ctx, GL_INVALID_OPERATION,
12293464ebd5Sriastradh                           "glDeleteTransformFeedbacks(object %u is active)",
12303464ebd5Sriastradh                           names[i]);
12313464ebd5Sriastradh               return;
12323464ebd5Sriastradh            }
123301e04c3fSmrg            _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
12343464ebd5Sriastradh            /* unref, but object may not be deleted until later */
123501e04c3fSmrg            if (obj == ctx->TransformFeedback.CurrentObject) {
123601e04c3fSmrg               reference_transform_feedback_object(
123701e04c3fSmrg                     &ctx->TransformFeedback.CurrentObject,
123801e04c3fSmrg                     ctx->TransformFeedback.DefaultObject);
123901e04c3fSmrg            }
12403464ebd5Sriastradh            reference_transform_feedback_object(&obj, NULL);
12413464ebd5Sriastradh         }
12423464ebd5Sriastradh      }
12433464ebd5Sriastradh   }
12443464ebd5Sriastradh}
12453464ebd5Sriastradh
12463464ebd5Sriastradh
12473464ebd5Sriastradh/**
12483464ebd5Sriastradh * Pause transform feedback.
12493464ebd5Sriastradh * Part of GL_ARB_transform_feedback2.
12503464ebd5Sriastradh */
125101e04c3fSmrgstatic void
125201e04c3fSmrgpause_transform_feedback(struct gl_context *ctx,
125301e04c3fSmrg                         struct gl_transform_feedback_object *obj)
125401e04c3fSmrg{
12557ec681f3Smrg   FLUSH_VERTICES(ctx, 0, 0);
125601e04c3fSmrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
125701e04c3fSmrg
125801e04c3fSmrg   assert(ctx->Driver.PauseTransformFeedback);
125901e04c3fSmrg   ctx->Driver.PauseTransformFeedback(ctx, obj);
126001e04c3fSmrg
126101e04c3fSmrg   obj->Paused = GL_TRUE;
12627ec681f3Smrg   _mesa_update_valid_to_render_state(ctx);
126301e04c3fSmrg}
126401e04c3fSmrg
126501e04c3fSmrg
126601e04c3fSmrgvoid GLAPIENTRY
126701e04c3fSmrg_mesa_PauseTransformFeedback_no_error(void)
126801e04c3fSmrg{
126901e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
127001e04c3fSmrg   pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
127101e04c3fSmrg}
127201e04c3fSmrg
127301e04c3fSmrg
12743464ebd5Sriastradhvoid GLAPIENTRY
12753464ebd5Sriastradh_mesa_PauseTransformFeedback(void)
12763464ebd5Sriastradh{
12773464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
12783464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
12793464ebd5Sriastradh
12803464ebd5Sriastradh   obj = ctx->TransformFeedback.CurrentObject;
12813464ebd5Sriastradh
1282af69d88dSmrg   if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
12833464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
12843464ebd5Sriastradh           "glPauseTransformFeedback(feedback not active or already paused)");
12853464ebd5Sriastradh      return;
12863464ebd5Sriastradh   }
12873464ebd5Sriastradh
128801e04c3fSmrg   pause_transform_feedback(ctx, obj);
12893464ebd5Sriastradh}
12903464ebd5Sriastradh
12913464ebd5Sriastradh
12923464ebd5Sriastradh/**
12933464ebd5Sriastradh * Resume transform feedback.
12943464ebd5Sriastradh * Part of GL_ARB_transform_feedback2.
12953464ebd5Sriastradh */
129601e04c3fSmrgstatic void
129701e04c3fSmrgresume_transform_feedback(struct gl_context *ctx,
129801e04c3fSmrg                          struct gl_transform_feedback_object *obj)
129901e04c3fSmrg{
13007ec681f3Smrg   FLUSH_VERTICES(ctx, 0, 0);
130101e04c3fSmrg   ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
130201e04c3fSmrg
130301e04c3fSmrg   obj->Paused = GL_FALSE;
130401e04c3fSmrg
130501e04c3fSmrg   assert(ctx->Driver.ResumeTransformFeedback);
130601e04c3fSmrg   ctx->Driver.ResumeTransformFeedback(ctx, obj);
13077ec681f3Smrg   _mesa_update_valid_to_render_state(ctx);
130801e04c3fSmrg}
130901e04c3fSmrg
131001e04c3fSmrg
131101e04c3fSmrgvoid GLAPIENTRY
131201e04c3fSmrg_mesa_ResumeTransformFeedback_no_error(void)
131301e04c3fSmrg{
131401e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
131501e04c3fSmrg   resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
131601e04c3fSmrg}
131701e04c3fSmrg
131801e04c3fSmrg
13193464ebd5Sriastradhvoid GLAPIENTRY
13203464ebd5Sriastradh_mesa_ResumeTransformFeedback(void)
13213464ebd5Sriastradh{
13223464ebd5Sriastradh   struct gl_transform_feedback_object *obj;
13233464ebd5Sriastradh   GET_CURRENT_CONTEXT(ctx);
13243464ebd5Sriastradh
13253464ebd5Sriastradh   obj = ctx->TransformFeedback.CurrentObject;
13263464ebd5Sriastradh
13273464ebd5Sriastradh   if (!obj->Active || !obj->Paused) {
13283464ebd5Sriastradh      _mesa_error(ctx, GL_INVALID_OPERATION,
13293464ebd5Sriastradh               "glResumeTransformFeedback(feedback not active or not paused)");
13303464ebd5Sriastradh      return;
13313464ebd5Sriastradh   }
13323464ebd5Sriastradh
1333af69d88dSmrg   /* From the ARB_transform_feedback2 specification:
1334af69d88dSmrg    * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1335af69d88dSmrg    *  the program object being used by the current transform feedback object
1336af69d88dSmrg    *  is not active."
1337af69d88dSmrg    */
133801e04c3fSmrg   if (obj->program != get_xfb_source(ctx)) {
1339af69d88dSmrg      _mesa_error(ctx, GL_INVALID_OPERATION,
1340af69d88dSmrg                  "glResumeTransformFeedback(wrong program bound)");
13413464ebd5Sriastradh      return;
13423464ebd5Sriastradh   }
13433464ebd5Sriastradh
134401e04c3fSmrg   resume_transform_feedback(ctx, obj);
134501e04c3fSmrg}
13463464ebd5Sriastradh
134701e04c3fSmrgextern void GLAPIENTRY
134801e04c3fSmrg_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
134901e04c3fSmrg{
135001e04c3fSmrg    struct gl_transform_feedback_object *obj;
135101e04c3fSmrg    GET_CURRENT_CONTEXT(ctx);
135201e04c3fSmrg
135301e04c3fSmrg    obj = lookup_transform_feedback_object_err(ctx, xfb,
135401e04c3fSmrg                                               "glGetTransformFeedbackiv");
135501e04c3fSmrg    if (!obj) {
135601e04c3fSmrg       return;
135701e04c3fSmrg    }
135801e04c3fSmrg
135901e04c3fSmrg    switch(pname) {
136001e04c3fSmrg    case GL_TRANSFORM_FEEDBACK_PAUSED:
136101e04c3fSmrg       *param = obj->Paused;
136201e04c3fSmrg       break;
136301e04c3fSmrg    case GL_TRANSFORM_FEEDBACK_ACTIVE:
136401e04c3fSmrg       *param = obj->Active;
136501e04c3fSmrg       break;
136601e04c3fSmrg    default:
136701e04c3fSmrg       _mesa_error(ctx, GL_INVALID_ENUM,
136801e04c3fSmrg                   "glGetTransformFeedbackiv(pname=%i)", pname);
136901e04c3fSmrg    }
137001e04c3fSmrg}
13713464ebd5Sriastradh
137201e04c3fSmrgextern void GLAPIENTRY
137301e04c3fSmrg_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
137401e04c3fSmrg                              GLint *param)
137501e04c3fSmrg{
137601e04c3fSmrg   struct gl_transform_feedback_object *obj;
137701e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
137801e04c3fSmrg
137901e04c3fSmrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
138001e04c3fSmrg                                              "glGetTransformFeedbacki_v");
138101e04c3fSmrg   if (!obj) {
138201e04c3fSmrg      return;
138301e04c3fSmrg   }
138401e04c3fSmrg
138501e04c3fSmrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
138601e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE,
138701e04c3fSmrg                  "glGetTransformFeedbacki_v(index=%i)", index);
138801e04c3fSmrg      return;
138901e04c3fSmrg   }
139001e04c3fSmrg
139101e04c3fSmrg   switch(pname) {
139201e04c3fSmrg   case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
139301e04c3fSmrg      *param = obj->BufferNames[index];
139401e04c3fSmrg      break;
139501e04c3fSmrg   default:
139601e04c3fSmrg      _mesa_error(ctx, GL_INVALID_ENUM,
139701e04c3fSmrg                  "glGetTransformFeedbacki_v(pname=%i)", pname);
139801e04c3fSmrg   }
139901e04c3fSmrg}
140001e04c3fSmrg
140101e04c3fSmrgextern void GLAPIENTRY
140201e04c3fSmrg_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
140301e04c3fSmrg                                GLint64 *param)
140401e04c3fSmrg{
140501e04c3fSmrg   struct gl_transform_feedback_object *obj;
140601e04c3fSmrg   GET_CURRENT_CONTEXT(ctx);
140701e04c3fSmrg
140801e04c3fSmrg   obj = lookup_transform_feedback_object_err(ctx, xfb,
140901e04c3fSmrg                                              "glGetTransformFeedbacki64_v");
141001e04c3fSmrg   if (!obj) {
141101e04c3fSmrg      return;
141201e04c3fSmrg   }
141301e04c3fSmrg
141401e04c3fSmrg   if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
141501e04c3fSmrg      _mesa_error(ctx, GL_INVALID_VALUE,
141601e04c3fSmrg                  "glGetTransformFeedbacki64_v(index=%i)", index);
141701e04c3fSmrg      return;
141801e04c3fSmrg   }
141901e04c3fSmrg
142001e04c3fSmrg   /**
142101e04c3fSmrg    * This follows the same general rules used for BindBufferBase:
142201e04c3fSmrg    *
142301e04c3fSmrg    *   "To query the starting offset or size of the range of a buffer
142401e04c3fSmrg    *    object binding in an indexed array, call GetInteger64i_v with
142501e04c3fSmrg    *    target set to respectively the starting offset or binding size
142601e04c3fSmrg    *    name from table 6.5 for that array. Index must be in the range
142701e04c3fSmrg    *    zero to the number of bind points supported minus one. If the
142801e04c3fSmrg    *    starting offset or size was not specified when the buffer object
142901e04c3fSmrg    *    was bound (e.g. if it was bound with BindBufferBase), or if no
143001e04c3fSmrg    *    buffer object is bound to the target array at index, zero is
143101e04c3fSmrg    *    returned."
143201e04c3fSmrg    */
143301e04c3fSmrg   if (obj->RequestedSize[index] == 0 &&
143401e04c3fSmrg       (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START ||
143501e04c3fSmrg        pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) {
143601e04c3fSmrg      *param = 0;
143701e04c3fSmrg      return;
143801e04c3fSmrg   }
143901e04c3fSmrg
144001e04c3fSmrg   compute_transform_feedback_buffer_sizes(obj);
144101e04c3fSmrg   switch(pname) {
144201e04c3fSmrg   case GL_TRANSFORM_FEEDBACK_BUFFER_START:
144301e04c3fSmrg      assert(obj->RequestedSize[index] > 0);
144401e04c3fSmrg      *param = obj->Offset[index];
144501e04c3fSmrg      break;
144601e04c3fSmrg   case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
144701e04c3fSmrg      assert(obj->RequestedSize[index] > 0);
144801e04c3fSmrg      *param = obj->Size[index];
144901e04c3fSmrg      break;
145001e04c3fSmrg   default:
145101e04c3fSmrg      _mesa_error(ctx, GL_INVALID_ENUM,
145201e04c3fSmrg                  "glGetTransformFeedbacki64_v(pname=%i)", pname);
145301e04c3fSmrg   }
1454af69d88dSmrg}
1455