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