mesa/tests: Sanity check the ES2 dispatch table
[mesa.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 /**
48 * Do reference counting of transform feedback buffers.
49 */
50 static void
51 reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
52 struct gl_transform_feedback_object *obj)
53 {
54 if (*ptr == obj)
55 return;
56
57 if (*ptr) {
58 /* Unreference the old object */
59 struct gl_transform_feedback_object *oldObj = *ptr;
60
61 ASSERT(oldObj->RefCount > 0);
62 oldObj->RefCount--;
63
64 if (oldObj->RefCount == 0) {
65 GET_CURRENT_CONTEXT(ctx);
66 if (ctx)
67 ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
68 }
69
70 *ptr = NULL;
71 }
72 ASSERT(!*ptr);
73
74 if (obj) {
75 /* reference new object */
76 if (obj->RefCount == 0) {
77 _mesa_problem(NULL, "referencing deleted transform feedback object");
78 *ptr = NULL;
79 }
80 else {
81 obj->RefCount++;
82 *ptr = obj;
83 }
84 }
85 }
86
87
88 /**
89 * Check that all the buffer objects currently bound for transform
90 * feedback actually exist. Raise a GL_INVALID_OPERATION error if
91 * any buffers are missing.
92 * \return GL_TRUE for success, GL_FALSE if error
93 */
94 GLboolean
95 _mesa_validate_transform_feedback_buffers(struct gl_context *ctx)
96 {
97 /* XXX to do */
98 return GL_TRUE;
99 }
100
101
102
103 /**
104 * Per-context init for transform feedback.
105 */
106 void
107 _mesa_init_transform_feedback(struct gl_context *ctx)
108 {
109 /* core mesa expects this, even a dummy one, to be available */
110 ASSERT(ctx->Driver.NewTransformFeedback);
111
112 ctx->TransformFeedback.DefaultObject =
113 ctx->Driver.NewTransformFeedback(ctx, 0);
114
115 assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
116
117 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
118 ctx->TransformFeedback.DefaultObject);
119
120 assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
121
122 ctx->TransformFeedback.Objects = _mesa_NewHashTable();
123
124 _mesa_reference_buffer_object(ctx,
125 &ctx->TransformFeedback.CurrentBuffer,
126 ctx->Shared->NullBufferObj);
127 }
128
129
130
131 /**
132 * Callback for _mesa_HashDeleteAll().
133 */
134 static void
135 delete_cb(GLuint key, void *data, void *userData)
136 {
137 struct gl_context *ctx = (struct gl_context *) userData;
138 struct gl_transform_feedback_object *obj =
139 (struct gl_transform_feedback_object *) data;
140
141 ctx->Driver.DeleteTransformFeedback(ctx, obj);
142 }
143
144
145 /**
146 * Per-context free/clean-up for transform feedback.
147 */
148 void
149 _mesa_free_transform_feedback(struct gl_context *ctx)
150 {
151 /* core mesa expects this, even a dummy one, to be available */
152 ASSERT(ctx->Driver.NewTransformFeedback);
153
154 _mesa_reference_buffer_object(ctx,
155 &ctx->TransformFeedback.CurrentBuffer,
156 NULL);
157
158 /* Delete all feedback objects */
159 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
160 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
161
162 /* Delete the default feedback object */
163 assert(ctx->Driver.DeleteTransformFeedback);
164 ctx->Driver.DeleteTransformFeedback(ctx,
165 ctx->TransformFeedback.DefaultObject);
166
167 ctx->TransformFeedback.CurrentObject = NULL;
168 }
169
170
171 /** Default fallback for ctx->Driver.NewTransformFeedback() */
172 static struct gl_transform_feedback_object *
173 new_transform_feedback(struct gl_context *ctx, GLuint name)
174 {
175 struct gl_transform_feedback_object *obj;
176 obj = CALLOC_STRUCT(gl_transform_feedback_object);
177 if (obj) {
178 obj->Name = name;
179 obj->RefCount = 1;
180 }
181 return obj;
182 }
183
184 /** Default fallback for ctx->Driver.DeleteTransformFeedback() */
185 static void
186 delete_transform_feedback(struct gl_context *ctx,
187 struct gl_transform_feedback_object *obj)
188 {
189 GLuint i;
190
191 for (i = 0; i < Elements(obj->Buffers); i++) {
192 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
193 }
194
195 free(obj);
196 }
197
198
199 /** Default fallback for ctx->Driver.BeginTransformFeedback() */
200 static void
201 begin_transform_feedback(struct gl_context *ctx, GLenum mode,
202 struct gl_transform_feedback_object *obj)
203 {
204 /* nop */
205 }
206
207 /** Default fallback for ctx->Driver.EndTransformFeedback() */
208 static void
209 end_transform_feedback(struct gl_context *ctx,
210 struct gl_transform_feedback_object *obj)
211 {
212 /* nop */
213 }
214
215 /** Default fallback for ctx->Driver.PauseTransformFeedback() */
216 static void
217 pause_transform_feedback(struct gl_context *ctx,
218 struct gl_transform_feedback_object *obj)
219 {
220 /* nop */
221 }
222
223 /** Default fallback for ctx->Driver.ResumeTransformFeedback() */
224 static void
225 resume_transform_feedback(struct gl_context *ctx,
226 struct gl_transform_feedback_object *obj)
227 {
228 /* nop */
229 }
230
231
232 /**
233 * Plug in default device driver functions for transform feedback.
234 * Most drivers will override some/all of these.
235 */
236 void
237 _mesa_init_transform_feedback_functions(struct dd_function_table *driver)
238 {
239 driver->NewTransformFeedback = new_transform_feedback;
240 driver->DeleteTransformFeedback = delete_transform_feedback;
241 driver->BeginTransformFeedback = begin_transform_feedback;
242 driver->EndTransformFeedback = end_transform_feedback;
243 driver->PauseTransformFeedback = pause_transform_feedback;
244 driver->ResumeTransformFeedback = resume_transform_feedback;
245 }
246
247
248 void
249 _mesa_init_transform_feedback_dispatch(struct _glapi_table *disp)
250 {
251 /* EXT_transform_feedback */
252 SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback);
253 SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback);
254 SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT);
255 SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings);
256 SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying);
257 /* ARB_transform_feedback2 */
258 SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
259 SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
260 SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
261 SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
262 SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
263 SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
264 }
265
266
267 /**
268 ** Begin API functions
269 **/
270
271
272 void GLAPIENTRY
273 _mesa_BeginTransformFeedback(GLenum mode)
274 {
275 struct gl_transform_feedback_object *obj;
276 struct gl_transform_feedback_info *info;
277 int i;
278 GET_CURRENT_CONTEXT(ctx);
279
280 obj = ctx->TransformFeedback.CurrentObject;
281
282 if (ctx->Shader.CurrentVertexProgram == NULL) {
283 _mesa_error(ctx, GL_INVALID_OPERATION,
284 "glBeginTransformFeedback(no program active)");
285 return;
286 }
287
288 info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback;
289
290 if (info->NumOutputs == 0) {
291 _mesa_error(ctx, GL_INVALID_OPERATION,
292 "glBeginTransformFeedback(no varyings to record)");
293 return;
294 }
295
296 switch (mode) {
297 case GL_POINTS:
298 case GL_LINES:
299 case GL_TRIANGLES:
300 /* legal */
301 break;
302 default:
303 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
304 return;
305 }
306
307 if (obj->Active) {
308 _mesa_error(ctx, GL_INVALID_OPERATION,
309 "glBeginTransformFeedback(already active)");
310 return;
311 }
312
313 for (i = 0; i < info->NumBuffers; ++i) {
314 if (obj->BufferNames[i] == 0) {
315 _mesa_error(ctx, GL_INVALID_OPERATION,
316 "glBeginTransformFeedback(binding point %d does not have "
317 "a buffer object bound)", i);
318 return;
319 }
320 }
321
322 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
323 obj->Active = GL_TRUE;
324 ctx->TransformFeedback.Mode = mode;
325
326 assert(ctx->Driver.BeginTransformFeedback);
327 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
328 }
329
330
331 void GLAPIENTRY
332 _mesa_EndTransformFeedback(void)
333 {
334 struct gl_transform_feedback_object *obj;
335 GET_CURRENT_CONTEXT(ctx);
336
337 obj = ctx->TransformFeedback.CurrentObject;
338
339 if (!obj->Active) {
340 _mesa_error(ctx, GL_INVALID_OPERATION,
341 "glEndTransformFeedback(not active)");
342 return;
343 }
344
345 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
346 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
347 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
348 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
349
350 assert(ctx->Driver.EndTransformFeedback);
351 ctx->Driver.EndTransformFeedback(ctx, obj);
352 }
353
354
355 /**
356 * Helper used by BindBufferRange() and BindBufferBase().
357 */
358 static void
359 bind_buffer_range(struct gl_context *ctx, GLuint index,
360 struct gl_buffer_object *bufObj,
361 GLintptr offset, GLsizeiptr size)
362 {
363 struct gl_transform_feedback_object *obj =
364 ctx->TransformFeedback.CurrentObject;
365
366 /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because
367 * transform feedback buffers can't be changed while transform feedback is
368 * active.
369 */
370
371 /* The general binding point */
372 _mesa_reference_buffer_object(ctx,
373 &ctx->TransformFeedback.CurrentBuffer,
374 bufObj);
375
376 /* The per-attribute binding point */
377 _mesa_reference_buffer_object(ctx,
378 &obj->Buffers[index],
379 bufObj);
380
381 obj->BufferNames[index] = bufObj->Name;
382
383 obj->Offset[index] = offset;
384 obj->Size[index] = size;
385 }
386
387
388 /**
389 * Specify a buffer object to receive vertex shader results. Plus,
390 * specify the starting offset to place the results, and max size.
391 * Called from the glBindBufferRange() function.
392 */
393 void
394 _mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx,
395 GLuint index,
396 struct gl_buffer_object *bufObj,
397 GLintptr offset,
398 GLsizeiptr size)
399 {
400 struct gl_transform_feedback_object *obj;
401
402 obj = ctx->TransformFeedback.CurrentObject;
403
404 if (obj->Active) {
405 _mesa_error(ctx, GL_INVALID_OPERATION,
406 "glBindBufferRange(transform feedback active)");
407 return;
408 }
409
410 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
411 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
412 return;
413 }
414
415 if (size & 0x3) {
416 /* must a multiple of four */
417 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
418 return;
419 }
420
421 if (offset & 0x3) {
422 /* must be multiple of four */
423 _mesa_error(ctx, GL_INVALID_VALUE,
424 "glBindBufferRange(offset=%d)", (int) offset);
425 return;
426 }
427
428 bind_buffer_range(ctx, index, bufObj, offset, size);
429 }
430
431
432 /**
433 * Specify a buffer object to receive vertex shader results.
434 * As above, but start at offset = 0.
435 * Called from the glBindBufferBase() function.
436 */
437 void
438 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
439 GLuint index,
440 struct gl_buffer_object *bufObj)
441 {
442 struct gl_transform_feedback_object *obj;
443 GLsizeiptr size;
444
445 obj = ctx->TransformFeedback.CurrentObject;
446
447 if (obj->Active) {
448 _mesa_error(ctx, GL_INVALID_OPERATION,
449 "glBindBufferBase(transform feedback active)");
450 return;
451 }
452
453 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
454 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
455 return;
456 }
457
458 /* default size is the buffer size rounded down to nearest
459 * multiple of four.
460 */
461 size = bufObj->Size & ~0x3;
462
463 bind_buffer_range(ctx, index, bufObj, 0, size);
464 }
465
466
467 /**
468 * Specify a buffer object to receive vertex shader results, plus the
469 * offset in the buffer to start placing results.
470 * This function is part of GL_EXT_transform_feedback, but not GL3.
471 */
472 void GLAPIENTRY
473 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
474 GLintptr offset)
475 {
476 struct gl_transform_feedback_object *obj;
477 struct gl_buffer_object *bufObj;
478 GET_CURRENT_CONTEXT(ctx);
479 GLsizeiptr size;
480
481 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
482 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
483 return;
484 }
485
486 obj = ctx->TransformFeedback.CurrentObject;
487
488 if (obj->Active) {
489 _mesa_error(ctx, GL_INVALID_OPERATION,
490 "glBindBufferOffsetEXT(transform feedback active)");
491 return;
492 }
493
494 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
495 _mesa_error(ctx, GL_INVALID_VALUE,
496 "glBindBufferOffsetEXT(index=%d)", index);
497 return;
498 }
499
500 if (offset & 0x3) {
501 /* must be multiple of four */
502 _mesa_error(ctx, GL_INVALID_VALUE,
503 "glBindBufferOffsetEXT(offset=%d)", (int) offset);
504 return;
505 }
506
507 if (buffer == 0) {
508 bufObj = ctx->Shared->NullBufferObj;
509 } else {
510 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
511 }
512
513 if (!bufObj) {
514 _mesa_error(ctx, GL_INVALID_OPERATION,
515 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
516 return;
517 }
518
519 /* default size is the buffer size rounded down to nearest
520 * multiple of four.
521 */
522 size = (bufObj->Size - offset) & ~0x3;
523
524 bind_buffer_range(ctx, index, bufObj, offset, size);
525 }
526
527
528 /**
529 * This function specifies the vertex shader outputs to be written
530 * to the feedback buffer(s), and in what order.
531 */
532 void GLAPIENTRY
533 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
534 const GLchar **varyings, GLenum bufferMode)
535 {
536 struct gl_shader_program *shProg;
537 GLuint i;
538 GET_CURRENT_CONTEXT(ctx);
539
540 switch (bufferMode) {
541 case GL_INTERLEAVED_ATTRIBS:
542 break;
543 case GL_SEPARATE_ATTRIBS:
544 break;
545 default:
546 _mesa_error(ctx, GL_INVALID_ENUM,
547 "glTransformFeedbackVaryings(bufferMode)");
548 return;
549 }
550
551 if (count < 0 ||
552 (bufferMode == GL_SEPARATE_ATTRIBS &&
553 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
554 _mesa_error(ctx, GL_INVALID_VALUE,
555 "glTransformFeedbackVaryings(count=%d)", count);
556 return;
557 }
558
559 shProg = _mesa_lookup_shader_program(ctx, program);
560 if (!shProg) {
561 _mesa_error(ctx, GL_INVALID_VALUE,
562 "glTransformFeedbackVaryings(program=%u)", program);
563 return;
564 }
565
566 if (ctx->Extensions.ARB_transform_feedback3) {
567 if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
568 unsigned buffers = 1;
569
570 for (i = 0; i < count; i++) {
571 if (strcmp(varyings[i], "gl_NextBuffer") == 0)
572 buffers++;
573 }
574
575 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
576 _mesa_error(ctx, GL_INVALID_OPERATION,
577 "glTransformFeedbackVaryings(too many gl_NextBuffer "
578 "occurences)");
579 return;
580 }
581 } else {
582 for (i = 0; i < count; i++) {
583 if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
584 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
585 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
586 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
587 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
588 _mesa_error(ctx, GL_INVALID_OPERATION,
589 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
590 "varying=%s)",
591 varyings[i]);
592 return;
593 }
594 }
595 }
596 }
597
598 /* free existing varyings, if any */
599 for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
600 free(shProg->TransformFeedback.VaryingNames[i]);
601 }
602 free(shProg->TransformFeedback.VaryingNames);
603
604 /* allocate new memory for varying names */
605 shProg->TransformFeedback.VaryingNames =
606 malloc(count * sizeof(GLchar *));
607
608 if (!shProg->TransformFeedback.VaryingNames) {
609 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
610 return;
611 }
612
613 /* Save the new names and the count */
614 for (i = 0; i < (GLuint) count; i++) {
615 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
616 }
617 shProg->TransformFeedback.NumVarying = count;
618
619 shProg->TransformFeedback.BufferMode = bufferMode;
620
621 /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since
622 * the varyings won't be used until shader link time.
623 */
624 }
625
626
627 /**
628 * Get info about the vertex shader's outputs which are to be written
629 * to the feedback buffer(s).
630 */
631 void GLAPIENTRY
632 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
633 GLsizei bufSize, GLsizei *length,
634 GLsizei *size, GLenum *type, GLchar *name)
635 {
636 const struct gl_shader_program *shProg;
637 const struct gl_transform_feedback_info *linked_xfb_info;
638 GET_CURRENT_CONTEXT(ctx);
639
640 shProg = _mesa_lookup_shader_program(ctx, program);
641 if (!shProg) {
642 _mesa_error(ctx, GL_INVALID_VALUE,
643 "glGetTransformFeedbackVaryings(program=%u)", program);
644 return;
645 }
646
647 linked_xfb_info = &shProg->LinkedTransformFeedback;
648 if (index >= linked_xfb_info->NumVarying) {
649 _mesa_error(ctx, GL_INVALID_VALUE,
650 "glGetTransformFeedbackVaryings(index=%u)", index);
651 return;
652 }
653
654 /* return the varying's name and length */
655 _mesa_copy_string(name, bufSize, length,
656 linked_xfb_info->Varyings[index].Name);
657
658 /* return the datatype and value's size (in datatype units) */
659 if (type)
660 *type = linked_xfb_info->Varyings[index].Type;
661 if (size)
662 *size = linked_xfb_info->Varyings[index].Size;
663 }
664
665
666
667 struct gl_transform_feedback_object *
668 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
669 {
670 if (name == 0) {
671 return ctx->TransformFeedback.DefaultObject;
672 }
673 else
674 return (struct gl_transform_feedback_object *)
675 _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
676 }
677
678
679 /**
680 * Create new transform feedback objects. Transform feedback objects
681 * encapsulate the state related to transform feedback to allow quickly
682 * switching state (and drawing the results, below).
683 * Part of GL_ARB_transform_feedback2.
684 */
685 void GLAPIENTRY
686 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
687 {
688 GLuint first;
689 GET_CURRENT_CONTEXT(ctx);
690
691 ASSERT_OUTSIDE_BEGIN_END(ctx);
692
693 if (n < 0) {
694 _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
695 return;
696 }
697
698 if (!names)
699 return;
700
701 /* we don't need contiguous IDs, but this might be faster */
702 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
703 if (first) {
704 GLsizei i;
705 for (i = 0; i < n; i++) {
706 struct gl_transform_feedback_object *obj
707 = ctx->Driver.NewTransformFeedback(ctx, first + i);
708 if (!obj) {
709 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
710 return;
711 }
712 names[i] = first + i;
713 _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
714 }
715 }
716 else {
717 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
718 }
719 }
720
721
722 /**
723 * Is the given ID a transform feedback object?
724 * Part of GL_ARB_transform_feedback2.
725 */
726 GLboolean GLAPIENTRY
727 _mesa_IsTransformFeedback(GLuint name)
728 {
729 GET_CURRENT_CONTEXT(ctx);
730
731 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
732
733 if (name && _mesa_lookup_transform_feedback_object(ctx, name))
734 return GL_TRUE;
735 else
736 return GL_FALSE;
737 }
738
739
740 /**
741 * Bind the given transform feedback object.
742 * Part of GL_ARB_transform_feedback2.
743 */
744 void GLAPIENTRY
745 _mesa_BindTransformFeedback(GLenum target, GLuint name)
746 {
747 struct gl_transform_feedback_object *obj;
748 GET_CURRENT_CONTEXT(ctx);
749
750 if (target != GL_TRANSFORM_FEEDBACK) {
751 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
752 return;
753 }
754
755 if (ctx->TransformFeedback.CurrentObject->Active &&
756 !ctx->TransformFeedback.CurrentObject->Paused) {
757 _mesa_error(ctx, GL_INVALID_OPERATION,
758 "glBindTransformFeedback(transform is active, or not paused)");
759 return;
760 }
761
762 obj = _mesa_lookup_transform_feedback_object(ctx, name);
763 if (!obj) {
764 _mesa_error(ctx, GL_INVALID_OPERATION,
765 "glBindTransformFeedback(name=%u)", name);
766 return;
767 }
768
769 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
770 obj);
771 }
772
773
774 /**
775 * Delete the given transform feedback objects.
776 * Part of GL_ARB_transform_feedback2.
777 */
778 void GLAPIENTRY
779 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
780 {
781 GLint i;
782 GET_CURRENT_CONTEXT(ctx);
783
784 ASSERT_OUTSIDE_BEGIN_END(ctx);
785
786 if (n < 0) {
787 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
788 return;
789 }
790
791 if (!names)
792 return;
793
794 for (i = 0; i < n; i++) {
795 if (names[i] > 0) {
796 struct gl_transform_feedback_object *obj
797 = _mesa_lookup_transform_feedback_object(ctx, names[i]);
798 if (obj) {
799 if (obj->Active) {
800 _mesa_error(ctx, GL_INVALID_OPERATION,
801 "glDeleteTransformFeedbacks(object %u is active)",
802 names[i]);
803 return;
804 }
805 _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
806 /* unref, but object may not be deleted until later */
807 reference_transform_feedback_object(&obj, NULL);
808 }
809 }
810 }
811 }
812
813
814 /**
815 * Pause transform feedback.
816 * Part of GL_ARB_transform_feedback2.
817 */
818 void GLAPIENTRY
819 _mesa_PauseTransformFeedback(void)
820 {
821 struct gl_transform_feedback_object *obj;
822 GET_CURRENT_CONTEXT(ctx);
823
824 obj = ctx->TransformFeedback.CurrentObject;
825
826 if (!obj->Active || obj->Paused) {
827 _mesa_error(ctx, GL_INVALID_OPERATION,
828 "glPauseTransformFeedback(feedback not active or already paused)");
829 return;
830 }
831
832 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
833 obj->Paused = GL_TRUE;
834
835 assert(ctx->Driver.PauseTransformFeedback);
836 ctx->Driver.PauseTransformFeedback(ctx, obj);
837 }
838
839
840 /**
841 * Resume transform feedback.
842 * Part of GL_ARB_transform_feedback2.
843 */
844 void GLAPIENTRY
845 _mesa_ResumeTransformFeedback(void)
846 {
847 struct gl_transform_feedback_object *obj;
848 GET_CURRENT_CONTEXT(ctx);
849
850 obj = ctx->TransformFeedback.CurrentObject;
851
852 if (!obj->Active || !obj->Paused) {
853 _mesa_error(ctx, GL_INVALID_OPERATION,
854 "glResumeTransformFeedback(feedback not active or not paused)");
855 return;
856 }
857
858 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK);
859 obj->Paused = GL_FALSE;
860
861 assert(ctx->Driver.ResumeTransformFeedback);
862 ctx->Driver.ResumeTransformFeedback(ctx, obj);
863 }