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