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