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