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