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