transformfeedback.c revision 86bb45ffc36280263ba99fdca0c341489ad99e7f
1affc150dc44fab1911775a49636d0ce85333b634Zonr Chang/*
2affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Mesa 3-D graphics library
3affc150dc44fab1911775a49636d0ce85333b634Zonr Chang *
4affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Copyright (C) 2010  VMware, Inc.  All Rights Reserved.
5affc150dc44fab1911775a49636d0ce85333b634Zonr Chang *
6affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Permission is hereby granted, free of charge, to any person obtaining a
7affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * copy of this software and associated documentation files (the "Software"),
8affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * to deal in the Software without restriction, including without limitation
9affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * and/or sell copies of the Software, and to permit persons to whom the
11affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Software is furnished to do so, subject to the following conditions:
12affc150dc44fab1911775a49636d0ce85333b634Zonr Chang *
13affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * The above copyright notice and this permission notice shall be included
14affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * in all copies or substantial portions of the Software.
15f7ac0f19a1c8d0ad14bcf6456ce368b830fea886Stephen Hines *
16affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22affc150dc44fab1911775a49636d0ce85333b634Zonr Chang */
23affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
24affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
25affc150dc44fab1911775a49636d0ce85333b634Zonr Chang/*
26affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Vertex transform feedback support.
27affc150dc44fab1911775a49636d0ce85333b634Zonr Chang *
28affc150dc44fab1911775a49636d0ce85333b634Zonr Chang * Authors:
29affc150dc44fab1911775a49636d0ce85333b634Zonr Chang *   Brian Paul
30affc150dc44fab1911775a49636d0ce85333b634Zonr Chang */
31affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
32affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
33affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "buffers.h"
34affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "bufferobj.h"
35affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "context.h"
36affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "hash.h"
37affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "mfeatures.h"
38affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "mtypes.h"
39affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "transformfeedback.h"
40affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "shaderapi.h"
41affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "shaderobj.h"
42affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "main/dispatch.h"
43affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
44affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#include "program/prog_parameter.h"
45affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
46affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
47affc150dc44fab1911775a49636d0ce85333b634Zonr Chang#if FEATURE_EXT_transform_feedback
48affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
49affc150dc44fab1911775a49636d0ce85333b634Zonr Chang
50/**
51 * Do reference counting of transform feedback buffers.
52 */
53static void
54reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
55                                    struct gl_transform_feedback_object *obj)
56{
57   if (*ptr == obj)
58      return;
59
60   if (*ptr) {
61      /* Unreference the old object */
62      struct gl_transform_feedback_object *oldObj = *ptr;
63
64      ASSERT(oldObj->RefCount > 0);
65      oldObj->RefCount--;
66
67      if (oldObj->RefCount == 0) {
68         GET_CURRENT_CONTEXT(ctx);
69         if (ctx)
70            ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
71      }
72
73      *ptr = NULL;
74   }
75   ASSERT(!*ptr);
76
77   if (obj) {
78      /* reference new object */
79      if (obj->RefCount == 0) {
80         _mesa_problem(NULL, "referencing deleted transform feedback object");
81         *ptr = NULL;
82      }
83      else {
84         obj->RefCount++;
85         *ptr = obj;
86      }
87   }
88}
89
90
91/**
92 * Check if the given primitive mode (as in glBegin(mode)) is compatible
93 * with the current transform feedback mode (if it's enabled).
94 * This is to be called from glBegin(), glDrawArrays(), glDrawElements(), etc.
95 *
96 * \return GL_TRUE if the mode is OK, GL_FALSE otherwise.
97 */
98GLboolean
99_mesa_validate_primitive_mode(struct gl_context *ctx, GLenum mode)
100{
101   if (ctx->TransformFeedback.CurrentObject->Active &&
102       !ctx->TransformFeedback.CurrentObject->Paused) {
103      switch (mode) {
104      case GL_POINTS:
105         return ctx->TransformFeedback.Mode == GL_POINTS;
106      case GL_LINES:
107      case GL_LINE_STRIP:
108      case GL_LINE_LOOP:
109         return ctx->TransformFeedback.Mode == GL_LINES;
110      default:
111         return ctx->TransformFeedback.Mode == GL_TRIANGLES;
112      }
113   }
114   return GL_TRUE;
115}
116
117
118/**
119 * Check that all the buffer objects currently bound for transform
120 * feedback actually exist.  Raise a GL_INVALID_OPERATION error if
121 * any buffers are missing.
122 * \return GL_TRUE for success, GL_FALSE if error
123 */
124GLboolean
125_mesa_validate_transform_feedback_buffers(struct gl_context *ctx)
126{
127   /* XXX to do */
128   return GL_TRUE;
129}
130
131
132
133/**
134 * Per-context init for transform feedback.
135 */
136void
137_mesa_init_transform_feedback(struct gl_context *ctx)
138{
139   /* core mesa expects this, even a dummy one, to be available */
140   ASSERT(ctx->Driver.NewTransformFeedback);
141
142   ctx->TransformFeedback.DefaultObject =
143      ctx->Driver.NewTransformFeedback(ctx, 0);
144
145   assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
146
147   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
148                                       ctx->TransformFeedback.DefaultObject);
149
150   assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
151
152   ctx->TransformFeedback.Objects = _mesa_NewHashTable();
153
154   _mesa_reference_buffer_object(ctx,
155                                 &ctx->TransformFeedback.CurrentBuffer,
156                                 ctx->Shared->NullBufferObj);
157}
158
159
160
161/**
162 * Callback for _mesa_HashDeleteAll().
163 */
164static void
165delete_cb(GLuint key, void *data, void *userData)
166{
167   struct gl_context *ctx = (struct gl_context *) userData;
168   struct gl_transform_feedback_object *obj =
169      (struct gl_transform_feedback_object *) data;
170
171   ctx->Driver.DeleteTransformFeedback(ctx, obj);
172}
173
174
175/**
176 * Per-context free/clean-up for transform feedback.
177 */
178void
179_mesa_free_transform_feedback(struct gl_context *ctx)
180{
181   /* core mesa expects this, even a dummy one, to be available */
182   ASSERT(ctx->Driver.NewTransformFeedback);
183
184   _mesa_reference_buffer_object(ctx,
185                                 &ctx->TransformFeedback.CurrentBuffer,
186                                 NULL);
187
188   /* Delete all feedback objects */
189   _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
190   _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
191
192   /* Delete the default feedback object */
193   assert(ctx->Driver.DeleteTransformFeedback);
194   ctx->Driver.DeleteTransformFeedback(ctx,
195                                       ctx->TransformFeedback.DefaultObject);
196
197   ctx->TransformFeedback.CurrentObject = NULL;
198}
199
200
201#else /* FEATURE_EXT_transform_feedback */
202
203/* forward declarations */
204static struct gl_transform_feedback_object *
205new_transform_feedback(struct gl_context *ctx, GLuint name);
206
207static void
208delete_transform_feedback(struct gl_context *ctx,
209                          struct gl_transform_feedback_object *obj);
210
211/* dummy per-context init/clean-up for transform feedback */
212void
213_mesa_init_transform_feedback(struct gl_context *ctx)
214{
215   ctx->TransformFeedback.DefaultObject = new_transform_feedback(ctx, 0);
216   ctx->TransformFeedback.CurrentObject = ctx->TransformFeedback.DefaultObject;
217   _mesa_reference_buffer_object(ctx,
218                                 &ctx->TransformFeedback.CurrentBuffer,
219                                 ctx->Shared->NullBufferObj);
220}
221
222void
223_mesa_free_transform_feedback(struct gl_context *ctx)
224{
225   _mesa_reference_buffer_object(ctx,
226                                 &ctx->TransformFeedback.CurrentBuffer,
227                                 NULL);
228   ctx->TransformFeedback.CurrentObject = NULL;
229   delete_transform_feedback(ctx, ctx->TransformFeedback.DefaultObject);
230}
231
232#endif /* FEATURE_EXT_transform_feedback */
233
234
235/** Default fallback for ctx->Driver.NewTransformFeedback() */
236static struct gl_transform_feedback_object *
237new_transform_feedback(struct gl_context *ctx, GLuint name)
238{
239   struct gl_transform_feedback_object *obj;
240   obj = CALLOC_STRUCT(gl_transform_feedback_object);
241   if (obj) {
242      obj->Name = name;
243      obj->RefCount = 1;
244   }
245   return obj;
246}
247
248/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
249static void
250delete_transform_feedback(struct gl_context *ctx,
251                          struct gl_transform_feedback_object *obj)
252{
253   GLuint i;
254
255   for (i = 0; i < Elements(obj->Buffers); i++) {
256      _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
257   }
258
259   free(obj);
260}
261
262
263#if FEATURE_EXT_transform_feedback
264
265
266/** Default fallback for ctx->Driver.BeginTransformFeedback() */
267static void
268begin_transform_feedback(struct gl_context *ctx, GLenum mode,
269                         struct gl_transform_feedback_object *obj)
270{
271   /* nop */
272}
273
274/** Default fallback for ctx->Driver.EndTransformFeedback() */
275static void
276end_transform_feedback(struct gl_context *ctx,
277                       struct gl_transform_feedback_object *obj)
278{
279   /* nop */
280}
281
282/** Default fallback for ctx->Driver.PauseTransformFeedback() */
283static void
284pause_transform_feedback(struct gl_context *ctx,
285                         struct gl_transform_feedback_object *obj)
286{
287   /* nop */
288}
289
290/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
291static void
292resume_transform_feedback(struct gl_context *ctx,
293                          struct gl_transform_feedback_object *obj)
294{
295   /* nop */
296}
297
298
299/**
300 * Plug in default device driver functions for transform feedback.
301 * Most drivers will override some/all of these.
302 */
303void
304_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
305{
306   driver->NewTransformFeedback = new_transform_feedback;
307   driver->DeleteTransformFeedback = delete_transform_feedback;
308   driver->BeginTransformFeedback = begin_transform_feedback;
309   driver->EndTransformFeedback = end_transform_feedback;
310   driver->PauseTransformFeedback = pause_transform_feedback;
311   driver->ResumeTransformFeedback = resume_transform_feedback;
312}
313
314
315void
316_mesa_init_transform_feedback_dispatch(struct _glapi_table *disp)
317{
318   /* EXT_transform_feedback */
319   SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback);
320   SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback);
321   SET_BindBufferRangeEXT(disp, _mesa_BindBufferRange);
322   SET_BindBufferBaseEXT(disp, _mesa_BindBufferBase);
323   SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT);
324   SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings);
325   SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying);
326   /* ARB_transform_feedback2 */
327   SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
328   SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
329   SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
330   SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
331   SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
332   SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
333}
334
335
336/**
337 ** Begin API functions
338 **/
339
340
341void GLAPIENTRY
342_mesa_BeginTransformFeedback(GLenum mode)
343{
344   struct gl_transform_feedback_object *obj;
345   struct gl_transform_feedback_info *info;
346   int i;
347   GET_CURRENT_CONTEXT(ctx);
348
349   obj = ctx->TransformFeedback.CurrentObject;
350
351   if (ctx->Shader.CurrentVertexProgram == NULL) {
352      _mesa_error(ctx, GL_INVALID_OPERATION,
353                  "glBeginTransformFeedback(no program active)");
354      return;
355   }
356
357   info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback;
358
359   if (info->NumOutputs == 0) {
360      _mesa_error(ctx, GL_INVALID_OPERATION,
361                  "glBeginTransformFeedback(no varyings to record)");
362      return;
363   }
364
365   switch (mode) {
366   case GL_POINTS:
367   case GL_LINES:
368   case GL_TRIANGLES:
369      /* legal */
370      break;
371   default:
372      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
373      return;
374   }
375
376   if (obj->Active) {
377      _mesa_error(ctx, GL_INVALID_OPERATION,
378                  "glBeginTransformFeedback(already active)");
379      return;
380   }
381
382   for (i = 0; i < info->NumBuffers; ++i) {
383      if (obj->BufferNames[i] == 0) {
384         _mesa_error(ctx, GL_INVALID_OPERATION,
385                     "glBeginTransformFeedback(binding point %d does not have "
386                     "a buffer object bound)", i);
387         return;
388      }
389   }
390
391   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
392   obj->Active = GL_TRUE;
393   ctx->TransformFeedback.Mode = mode;
394
395   assert(ctx->Driver.BeginTransformFeedback);
396   ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
397}
398
399
400void GLAPIENTRY
401_mesa_EndTransformFeedback(void)
402{
403   struct gl_transform_feedback_object *obj;
404   GET_CURRENT_CONTEXT(ctx);
405
406   obj = ctx->TransformFeedback.CurrentObject;
407
408   if (!obj->Active) {
409      _mesa_error(ctx, GL_INVALID_OPERATION,
410                  "glEndTransformFeedback(not active)");
411      return;
412   }
413
414   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
415   ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
416   ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
417   ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
418
419   assert(ctx->Driver.EndTransformFeedback);
420   ctx->Driver.EndTransformFeedback(ctx, obj);
421}
422
423
424/**
425 * Helper used by BindBufferRange() and BindBufferBase().
426 */
427static void
428bind_buffer_range(struct gl_context *ctx, GLuint index,
429                  struct gl_buffer_object *bufObj,
430                  GLintptr offset, GLsizeiptr size)
431{
432   struct gl_transform_feedback_object *obj =
433      ctx->TransformFeedback.CurrentObject;
434
435   /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
436    * transform feedback buffers can't be changed while transform feedback is
437    * active.
438    */
439
440   /* The general binding point */
441   _mesa_reference_buffer_object(ctx,
442                                 &ctx->TransformFeedback.CurrentBuffer,
443                                 bufObj);
444
445   /* The per-attribute binding point */
446   _mesa_reference_buffer_object(ctx,
447                                 &obj->Buffers[index],
448                                 bufObj);
449
450   obj->BufferNames[index] = bufObj->Name;
451
452   obj->Offset[index] = offset;
453   obj->Size[index] = size;
454}
455
456
457/**
458 * Specify a buffer object to receive vertex shader results.  Plus,
459 * specify the starting offset to place the results, and max size.
460 */
461void GLAPIENTRY
462_mesa_BindBufferRange(GLenum target, GLuint index,
463                      GLuint buffer, GLintptr offset, GLsizeiptr size)
464{
465   struct gl_transform_feedback_object *obj;
466   struct gl_buffer_object *bufObj;
467   GET_CURRENT_CONTEXT(ctx);
468
469   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
470      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
471      return;
472   }
473
474   obj = ctx->TransformFeedback.CurrentObject;
475
476   if (obj->Active) {
477      _mesa_error(ctx, GL_INVALID_OPERATION,
478                  "glBindBufferRange(transform feedback active)");
479      return;
480   }
481
482   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
483      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
484      return;
485   }
486
487   if ((size <= 0) || (size & 0x3)) {
488      /* must be positive and multiple of four */
489      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
490      return;
491   }
492
493   if (offset & 0x3) {
494      /* must be multiple of four */
495      _mesa_error(ctx, GL_INVALID_VALUE,
496                  "glBindBufferRange(offset=%d)", (int) offset);
497      return;
498   }
499
500   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
501   if (!bufObj) {
502      _mesa_error(ctx, GL_INVALID_OPERATION,
503                  "glBindBufferRange(invalid buffer=%u)", buffer);
504      return;
505   }
506
507   if (offset + size > bufObj->Size) {
508      _mesa_error(ctx, GL_INVALID_VALUE,
509                  "glBindBufferRange(offset + size %d > buffer size %d)",
510		  (int) (offset + size), (int) (bufObj->Size));
511      return;
512   }
513
514   bind_buffer_range(ctx, index, bufObj, offset, size);
515}
516
517
518/**
519 * Specify a buffer object to receive vertex shader results.
520 * As above, but start at offset = 0.
521 */
522void GLAPIENTRY
523_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
524{
525   struct gl_transform_feedback_object *obj;
526   struct gl_buffer_object *bufObj;
527   GLsizeiptr size;
528   GET_CURRENT_CONTEXT(ctx);
529
530   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
531      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
532      return;
533   }
534
535   obj = ctx->TransformFeedback.CurrentObject;
536
537   if (obj->Active) {
538      _mesa_error(ctx, GL_INVALID_OPERATION,
539                  "glBindBufferBase(transform feedback active)");
540      return;
541   }
542
543   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
544      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
545      return;
546   }
547
548   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
549   if (!bufObj) {
550      _mesa_error(ctx, GL_INVALID_OPERATION,
551                  "glBindBufferBase(invalid buffer=%u)", buffer);
552      return;
553   }
554
555   /* default size is the buffer size rounded down to nearest
556    * multiple of four.
557    */
558   size = bufObj->Size & ~0x3;
559
560   bind_buffer_range(ctx, index, bufObj, 0, size);
561}
562
563
564/**
565 * Specify a buffer object to receive vertex shader results, plus the
566 * offset in the buffer to start placing results.
567 * This function is part of GL_EXT_transform_feedback, but not GL3.
568 */
569void GLAPIENTRY
570_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
571                          GLintptr offset)
572{
573   struct gl_transform_feedback_object *obj;
574   struct gl_buffer_object *bufObj;
575   GET_CURRENT_CONTEXT(ctx);
576   GLsizeiptr size;
577
578   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
579      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
580      return;
581   }
582
583   obj = ctx->TransformFeedback.CurrentObject;
584
585   if (obj->Active) {
586      _mesa_error(ctx, GL_INVALID_OPERATION,
587                  "glBindBufferOffsetEXT(transform feedback active)");
588      return;
589   }
590
591   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
592      _mesa_error(ctx, GL_INVALID_VALUE,
593                  "glBindBufferOffsetEXT(index=%d)", index);
594      return;
595   }
596
597   if (offset & 0x3) {
598      /* must be multiple of four */
599      _mesa_error(ctx, GL_INVALID_VALUE,
600                  "glBindBufferOffsetEXT(offset=%d)", (int) offset);
601      return;
602   }
603
604   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
605   if (!bufObj) {
606      _mesa_error(ctx, GL_INVALID_OPERATION,
607                  "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
608      return;
609   }
610
611   /* default size is the buffer size rounded down to nearest
612    * multiple of four.
613    */
614   size = (bufObj->Size - offset) & ~0x3;
615
616   bind_buffer_range(ctx, index, bufObj, offset, size);
617}
618
619
620/**
621 * This function specifies the vertex shader outputs to be written
622 * to the feedback buffer(s), and in what order.
623 */
624void GLAPIENTRY
625_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
626                                const GLchar **varyings, GLenum bufferMode)
627{
628   struct gl_shader_program *shProg;
629   GLuint i;
630   GET_CURRENT_CONTEXT(ctx);
631
632   switch (bufferMode) {
633   case GL_INTERLEAVED_ATTRIBS:
634      break;
635   case GL_SEPARATE_ATTRIBS:
636      break;
637   default:
638      _mesa_error(ctx, GL_INVALID_ENUM,
639                  "glTransformFeedbackVaryings(bufferMode)");
640      return;
641   }
642
643   if (count < 0 ||
644       (bufferMode == GL_SEPARATE_ATTRIBS &&
645        (GLuint) count > ctx->Const.MaxTransformFeedbackSeparateAttribs)) {
646      _mesa_error(ctx, GL_INVALID_VALUE,
647                  "glTransformFeedbackVaryings(count=%d)", count);
648      return;
649   }
650
651   shProg = _mesa_lookup_shader_program(ctx, program);
652   if (!shProg) {
653      _mesa_error(ctx, GL_INVALID_VALUE,
654                  "glTransformFeedbackVaryings(program=%u)", program);
655      return;
656   }
657
658   /* free existing varyings, if any */
659   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
660      free(shProg->TransformFeedback.VaryingNames[i]);
661   }
662   free(shProg->TransformFeedback.VaryingNames);
663
664   /* allocate new memory for varying names */
665   shProg->TransformFeedback.VaryingNames =
666      (GLchar **) malloc(count * sizeof(GLchar *));
667
668   if (!shProg->TransformFeedback.VaryingNames) {
669      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
670      return;
671   }
672
673   /* Save the new names and the count */
674   for (i = 0; i < (GLuint) count; i++) {
675      shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
676   }
677   shProg->TransformFeedback.NumVarying = count;
678
679   shProg->TransformFeedback.BufferMode = bufferMode;
680
681   /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
682    * the varyings won't be used until shader link time.
683    */
684}
685
686
687/**
688 * Get info about the vertex shader's outputs which are to be written
689 * to the feedback buffer(s).
690 */
691void GLAPIENTRY
692_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
693                                  GLsizei bufSize, GLsizei *length,
694                                  GLsizei *size, GLenum *type, GLchar *name)
695{
696   const struct gl_shader_program *shProg;
697   const GLchar *varyingName;
698   GLint v;
699   GET_CURRENT_CONTEXT(ctx);
700
701   shProg = _mesa_lookup_shader_program(ctx, program);
702   if (!shProg) {
703      _mesa_error(ctx, GL_INVALID_VALUE,
704                  "glGetTransformFeedbackVaryings(program=%u)", program);
705      return;
706   }
707
708   if (index >= shProg->TransformFeedback.NumVarying) {
709      _mesa_error(ctx, GL_INVALID_VALUE,
710                  "glGetTransformFeedbackVaryings(index=%u)", index);
711      return;
712   }
713
714   varyingName = shProg->TransformFeedback.VaryingNames[index];
715
716   v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName);
717   if (v >= 0) {
718      struct gl_program_parameter *param = &shProg->Varying->Parameters[v];
719
720      /* return the varying's name and length */
721      _mesa_copy_string(name, bufSize, length, varyingName);
722
723      /* return the datatype and value's size (in datatype units) */
724      if (type)
725         *type = param->DataType;
726      if (size)
727         *size = param->Size;
728   }
729   else {
730      name[0] = 0;
731      if (length)
732         *length = 0;
733      if (type)
734         *type = 0;
735      if (size)
736         *size = 0;
737   }
738}
739
740
741
742struct gl_transform_feedback_object *
743_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
744{
745   if (name == 0) {
746      return ctx->TransformFeedback.DefaultObject;
747   }
748   else
749      return (struct gl_transform_feedback_object *)
750         _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
751}
752
753
754/**
755 * Create new transform feedback objects.   Transform feedback objects
756 * encapsulate the state related to transform feedback to allow quickly
757 * switching state (and drawing the results, below).
758 * Part of GL_ARB_transform_feedback2.
759 */
760void GLAPIENTRY
761_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
762{
763   GLuint first;
764   GET_CURRENT_CONTEXT(ctx);
765
766   ASSERT_OUTSIDE_BEGIN_END(ctx);
767
768   if (n < 0) {
769      _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
770      return;
771   }
772
773   if (!names)
774      return;
775
776   /* we don't need contiguous IDs, but this might be faster */
777   first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
778   if (first) {
779      GLsizei i;
780      for (i = 0; i < n; i++) {
781         struct gl_transform_feedback_object *obj
782            = ctx->Driver.NewTransformFeedback(ctx, first + i);
783         if (!obj) {
784            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
785            return;
786         }
787         names[i] = first + i;
788         _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
789      }
790   }
791   else {
792      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
793   }
794}
795
796
797/**
798 * Is the given ID a transform feedback object?
799 * Part of GL_ARB_transform_feedback2.
800 */
801GLboolean GLAPIENTRY
802_mesa_IsTransformFeedback(GLuint name)
803{
804   GET_CURRENT_CONTEXT(ctx);
805
806   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
807
808   if (name && _mesa_lookup_transform_feedback_object(ctx, name))
809      return GL_TRUE;
810   else
811      return GL_FALSE;
812}
813
814
815/**
816 * Bind the given transform feedback object.
817 * Part of GL_ARB_transform_feedback2.
818 */
819void GLAPIENTRY
820_mesa_BindTransformFeedback(GLenum target, GLuint name)
821{
822   struct gl_transform_feedback_object *obj;
823   GET_CURRENT_CONTEXT(ctx);
824
825   if (target != GL_TRANSFORM_FEEDBACK) {
826      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
827      return;
828   }
829
830   if (ctx->TransformFeedback.CurrentObject->Active &&
831       !ctx->TransformFeedback.CurrentObject->Paused) {
832      _mesa_error(ctx, GL_INVALID_OPERATION,
833              "glBindTransformFeedback(transform is active, or not paused)");
834      return;
835   }
836
837   obj = _mesa_lookup_transform_feedback_object(ctx, name);
838   if (!obj) {
839      _mesa_error(ctx, GL_INVALID_OPERATION,
840                  "glBindTransformFeedback(name=%u)", name);
841      return;
842   }
843
844   reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
845                                       obj);
846}
847
848
849/**
850 * Delete the given transform feedback objects.
851 * Part of GL_ARB_transform_feedback2.
852 */
853void GLAPIENTRY
854_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
855{
856   GLint i;
857   GET_CURRENT_CONTEXT(ctx);
858
859   ASSERT_OUTSIDE_BEGIN_END(ctx);
860
861   if (n < 0) {
862      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
863      return;
864   }
865
866   if (!names)
867      return;
868
869   for (i = 0; i < n; i++) {
870      if (names[i] > 0) {
871         struct gl_transform_feedback_object *obj
872            = _mesa_lookup_transform_feedback_object(ctx, names[i]);
873         if (obj) {
874            if (obj->Active) {
875               _mesa_error(ctx, GL_INVALID_OPERATION,
876                           "glDeleteTransformFeedbacks(object %u is active)",
877                           names[i]);
878               return;
879            }
880            _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
881            /* unref, but object may not be deleted until later */
882            reference_transform_feedback_object(&obj, NULL);
883         }
884      }
885   }
886}
887
888
889/**
890 * Pause transform feedback.
891 * Part of GL_ARB_transform_feedback2.
892 */
893void GLAPIENTRY
894_mesa_PauseTransformFeedback(void)
895{
896   struct gl_transform_feedback_object *obj;
897   GET_CURRENT_CONTEXT(ctx);
898
899   obj = ctx->TransformFeedback.CurrentObject;
900
901   if (!obj->Active || obj->Paused) {
902      _mesa_error(ctx, GL_INVALID_OPERATION,
903           "glPauseTransformFeedback(feedback not active or already paused)");
904      return;
905   }
906
907   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
908   obj->Paused = GL_TRUE;
909
910   assert(ctx->Driver.PauseTransformFeedback);
911   ctx->Driver.PauseTransformFeedback(ctx, obj);
912}
913
914
915/**
916 * Resume transform feedback.
917 * Part of GL_ARB_transform_feedback2.
918 */
919void GLAPIENTRY
920_mesa_ResumeTransformFeedback(void)
921{
922   struct gl_transform_feedback_object *obj;
923   GET_CURRENT_CONTEXT(ctx);
924
925   obj = ctx->TransformFeedback.CurrentObject;
926
927   if (!obj->Active || !obj->Paused) {
928      _mesa_error(ctx, GL_INVALID_OPERATION,
929               "glResumeTransformFeedback(feedback not active or not paused)");
930      return;
931   }
932
933   FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
934   obj->Paused = GL_FALSE;
935
936   assert(ctx->Driver.ResumeTransformFeedback);
937   ctx->Driver.ResumeTransformFeedback(ctx, obj);
938}
939
940#endif /* FEATURE_EXT_transform_feedback */
941