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