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