mesa: fix potential mem leak in generate_mipmap_compressed()
[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 /* EXT_transform_feedback */
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 /* ARB_transform_feedback2 */
339 SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback);
340 SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks);
341 SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks);
342 SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback);
343 SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback);
344 SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback);
345 SET_DrawTransformFeedback(disp, _mesa_DrawTransformFeedback);
346 }
347
348
349 /**
350 ** Begin API functions
351 **/
352
353
354 void GLAPIENTRY
355 _mesa_BeginTransformFeedback(GLenum mode)
356 {
357 struct gl_transform_feedback_object *obj;
358 GET_CURRENT_CONTEXT(ctx);
359
360 obj = ctx->TransformFeedback.CurrentObject;
361
362 switch (mode) {
363 case GL_POINTS:
364 case GL_LINES:
365 case GL_TRIANGLES:
366 /* legal */
367 break;
368 default:
369 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
370 return;
371 }
372
373 if (obj->Active) {
374 _mesa_error(ctx, GL_INVALID_OPERATION,
375 "glBeginTransformFeedback(already active)");
376 return;
377 }
378
379 obj->Active = GL_TRUE;
380 ctx->TransformFeedback.Mode = mode;
381
382 assert(ctx->Driver.BeginTransformFeedback);
383 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
384 }
385
386
387 void GLAPIENTRY
388 _mesa_EndTransformFeedback(void)
389 {
390 struct gl_transform_feedback_object *obj;
391 GET_CURRENT_CONTEXT(ctx);
392
393 obj = ctx->TransformFeedback.CurrentObject;
394
395 if (!obj->Active) {
396 _mesa_error(ctx, GL_INVALID_OPERATION,
397 "glEndTransformFeedback(not active)");
398 return;
399 }
400
401 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
402
403 assert(ctx->Driver.EndTransformFeedback);
404 ctx->Driver.EndTransformFeedback(ctx, obj);
405 }
406
407
408 /**
409 * Helper used by BindBufferRange() and BindBufferBase().
410 */
411 static void
412 bind_buffer_range(struct gl_context *ctx, GLuint index,
413 struct gl_buffer_object *bufObj,
414 GLintptr offset, GLsizeiptr size)
415 {
416 struct gl_transform_feedback_object *obj =
417 ctx->TransformFeedback.CurrentObject;
418
419 /* The general binding point */
420 _mesa_reference_buffer_object(ctx,
421 &ctx->TransformFeedback.CurrentBuffer,
422 bufObj);
423
424 /* The per-attribute binding point */
425 _mesa_reference_buffer_object(ctx,
426 &obj->Buffers[index],
427 bufObj);
428
429 obj->BufferNames[index] = bufObj->Name;
430
431 obj->Offset[index] = offset;
432 obj->Size[index] = size;
433 }
434
435
436 /**
437 * Specify a buffer object to receive vertex shader results. Plus,
438 * specify the starting offset to place the results, and max size.
439 */
440 void GLAPIENTRY
441 _mesa_BindBufferRange(GLenum target, GLuint index,
442 GLuint buffer, GLintptr offset, GLsizeiptr size)
443 {
444 struct gl_transform_feedback_object *obj;
445 struct gl_buffer_object *bufObj;
446 GET_CURRENT_CONTEXT(ctx);
447
448 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
449 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
450 return;
451 }
452
453 obj = ctx->TransformFeedback.CurrentObject;
454
455 if (obj->Active) {
456 _mesa_error(ctx, GL_INVALID_OPERATION,
457 "glBindBufferRange(transform feedback active)");
458 return;
459 }
460
461 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
462 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
463 return;
464 }
465
466 if ((size <= 0) || (size & 0x3)) {
467 /* must be positive and multiple of four */
468 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size%d)", (int) size);
469 return;
470 }
471
472 if (offset & 0x3) {
473 /* must be multiple of four */
474 _mesa_error(ctx, GL_INVALID_VALUE,
475 "glBindBufferRange(offset=%d)", (int) offset);
476 return;
477 }
478
479 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
480 if (!bufObj) {
481 _mesa_error(ctx, GL_INVALID_OPERATION,
482 "glBindBufferRange(invalid buffer=%u)", buffer);
483 return;
484 }
485
486 if (offset + size >= bufObj->Size) {
487 _mesa_error(ctx, GL_INVALID_VALUE,
488 "glBindBufferRange(offset + size %d > buffer size %d)",
489 (int) (offset + size), (int) (bufObj->Size));
490 return;
491 }
492
493 bind_buffer_range(ctx, index, bufObj, offset, size);
494 }
495
496
497 /**
498 * Specify a buffer object to receive vertex shader results.
499 * As above, but start at offset = 0.
500 */
501 void GLAPIENTRY
502 _mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
503 {
504 struct gl_transform_feedback_object *obj;
505 struct gl_buffer_object *bufObj;
506 GLsizeiptr size;
507 GET_CURRENT_CONTEXT(ctx);
508
509 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
510 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
511 return;
512 }
513
514 obj = ctx->TransformFeedback.CurrentObject;
515
516 if (obj->Active) {
517 _mesa_error(ctx, GL_INVALID_OPERATION,
518 "glBindBufferBase(transform feedback active)");
519 return;
520 }
521
522 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
523 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
524 return;
525 }
526
527 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
528 if (!bufObj) {
529 _mesa_error(ctx, GL_INVALID_OPERATION,
530 "glBindBufferBase(invalid buffer=%u)", buffer);
531 return;
532 }
533
534 /* default size is the buffer size rounded down to nearest
535 * multiple of four.
536 */
537 size = bufObj->Size & ~0x3;
538
539 bind_buffer_range(ctx, index, bufObj, 0, size);
540 }
541
542
543 /**
544 * Specify a buffer object to receive vertex shader results, plus the
545 * offset in the buffer to start placing results.
546 * This function is part of GL_EXT_transform_feedback, but not GL3.
547 */
548 void GLAPIENTRY
549 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
550 GLintptr offset)
551 {
552 struct gl_transform_feedback_object *obj;
553 struct gl_buffer_object *bufObj;
554 GET_CURRENT_CONTEXT(ctx);
555 GLsizeiptr size;
556
557 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
558 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
559 return;
560 }
561
562 obj = ctx->TransformFeedback.CurrentObject;
563
564 if (obj->Active) {
565 _mesa_error(ctx, GL_INVALID_OPERATION,
566 "glBindBufferOffsetEXT(transform feedback active)");
567 return;
568 }
569
570 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
571 _mesa_error(ctx, GL_INVALID_VALUE,
572 "glBindBufferOffsetEXT(index=%d)", index);
573 return;
574 }
575
576 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
577 if (!bufObj) {
578 _mesa_error(ctx, GL_INVALID_OPERATION,
579 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
580 return;
581 }
582
583 /* default size is the buffer size rounded down to nearest
584 * multiple of four.
585 */
586 size = (bufObj->Size - offset) & ~0x3;
587
588 bind_buffer_range(ctx, index, bufObj, offset, size);
589 }
590
591
592 /**
593 * This function specifies the vertex shader outputs to be written
594 * to the feedback buffer(s), and in what order.
595 */
596 void GLAPIENTRY
597 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
598 const GLchar **varyings, GLenum bufferMode)
599 {
600 struct gl_shader_program *shProg;
601 GLuint i;
602 GET_CURRENT_CONTEXT(ctx);
603
604 switch (bufferMode) {
605 case GL_INTERLEAVED_ATTRIBS:
606 break;
607 case GL_SEPARATE_ATTRIBS:
608 break;
609 default:
610 _mesa_error(ctx, GL_INVALID_ENUM,
611 "glTransformFeedbackVaryings(bufferMode)");
612 return;
613 }
614
615 if (count < 0 ||
616 (bufferMode == GL_SEPARATE_ATTRIBS &&
617 (GLuint) count > ctx->Const.MaxTransformFeedbackSeparateAttribs)) {
618 _mesa_error(ctx, GL_INVALID_VALUE,
619 "glTransformFeedbackVaryings(count=%d)", count);
620 return;
621 }
622
623 shProg = _mesa_lookup_shader_program(ctx, program);
624 if (!shProg) {
625 _mesa_error(ctx, GL_INVALID_VALUE,
626 "glTransformFeedbackVaryings(program=%u)", program);
627 return;
628 }
629
630 /* free existing varyings, if any */
631 for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
632 free(shProg->TransformFeedback.VaryingNames[i]);
633 }
634 free(shProg->TransformFeedback.VaryingNames);
635
636 /* allocate new memory for varying names */
637 shProg->TransformFeedback.VaryingNames =
638 (GLchar **) malloc(count * sizeof(GLchar *));
639
640 if (!shProg->TransformFeedback.VaryingNames) {
641 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
642 return;
643 }
644
645 /* Save the new names and the count */
646 for (i = 0; i < (GLuint) count; i++) {
647 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
648 }
649 shProg->TransformFeedback.NumVarying = count;
650
651 shProg->TransformFeedback.BufferMode = bufferMode;
652
653 /* The varyings won't be used until shader link time */
654 }
655
656
657 /**
658 * Get info about the vertex shader's outputs which are to be written
659 * to the feedback buffer(s).
660 */
661 void GLAPIENTRY
662 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
663 GLsizei bufSize, GLsizei *length,
664 GLsizei *size, GLenum *type, GLchar *name)
665 {
666 const struct gl_shader_program *shProg;
667 const GLchar *varyingName;
668 GLint v;
669 GET_CURRENT_CONTEXT(ctx);
670
671 shProg = _mesa_lookup_shader_program(ctx, program);
672 if (!shProg) {
673 _mesa_error(ctx, GL_INVALID_VALUE,
674 "glGetTransformFeedbackVaryings(program=%u)", program);
675 return;
676 }
677
678 if (index >= shProg->TransformFeedback.NumVarying) {
679 _mesa_error(ctx, GL_INVALID_VALUE,
680 "glGetTransformFeedbackVaryings(index=%u)", index);
681 return;
682 }
683
684 varyingName = shProg->TransformFeedback.VaryingNames[index];
685
686 v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName);
687 if (v >= 0) {
688 struct gl_program_parameter *param = &shProg->Varying->Parameters[v];
689
690 /* return the varying's name and length */
691 _mesa_copy_string(name, bufSize, length, varyingName);
692
693 /* return the datatype and value's size (in datatype units) */
694 if (type)
695 *type = param->DataType;
696 if (size)
697 *size = param->Size;
698 }
699 else {
700 name[0] = 0;
701 if (length)
702 *length = 0;
703 if (type)
704 *type = 0;
705 if (size)
706 *size = 0;
707 }
708 }
709
710
711
712 static struct gl_transform_feedback_object *
713 lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
714 {
715 if (name == 0) {
716 return ctx->TransformFeedback.DefaultObject;
717 }
718 else
719 return (struct gl_transform_feedback_object *)
720 _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
721 }
722
723
724 /**
725 * Create new transform feedback objects. Transform feedback objects
726 * encapsulate the state related to transform feedback to allow quickly
727 * switching state (and drawing the results, below).
728 * Part of GL_ARB_transform_feedback2.
729 */
730 void GLAPIENTRY
731 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
732 {
733 GLuint first;
734 GET_CURRENT_CONTEXT(ctx);
735
736 ASSERT_OUTSIDE_BEGIN_END(ctx);
737
738 if (n < 0) {
739 _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
740 return;
741 }
742
743 if (!names)
744 return;
745
746 /* we don't need contiguous IDs, but this might be faster */
747 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
748 if (first) {
749 GLsizei i;
750 for (i = 0; i < n; i++) {
751 struct gl_transform_feedback_object *obj
752 = ctx->Driver.NewTransformFeedback(ctx, first + i);
753 if (!obj) {
754 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
755 return;
756 }
757 names[i] = first + i;
758 _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
759 }
760 }
761 else {
762 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
763 }
764 }
765
766
767 /**
768 * Is the given ID a transform feedback object?
769 * Part of GL_ARB_transform_feedback2.
770 */
771 GLboolean GLAPIENTRY
772 _mesa_IsTransformFeedback(GLuint name)
773 {
774 GET_CURRENT_CONTEXT(ctx);
775
776 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
777
778 if (name && lookup_transform_feedback_object(ctx, name))
779 return GL_TRUE;
780 else
781 return GL_FALSE;
782 }
783
784
785 /**
786 * Bind the given transform feedback object.
787 * Part of GL_ARB_transform_feedback2.
788 */
789 void GLAPIENTRY
790 _mesa_BindTransformFeedback(GLenum target, GLuint name)
791 {
792 struct gl_transform_feedback_object *obj;
793 GET_CURRENT_CONTEXT(ctx);
794
795 if (target != GL_TRANSFORM_FEEDBACK) {
796 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
797 return;
798 }
799
800 if (ctx->TransformFeedback.CurrentObject->Active &&
801 !ctx->TransformFeedback.CurrentObject->Paused) {
802 _mesa_error(ctx, GL_INVALID_OPERATION,
803 "glBindTransformFeedback(transform is active, or not paused)");
804 return;
805 }
806
807 obj = lookup_transform_feedback_object(ctx, name);
808 if (!obj) {
809 _mesa_error(ctx, GL_INVALID_OPERATION,
810 "glBindTransformFeedback(name=%u)", name);
811 return;
812 }
813
814 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
815 obj);
816 }
817
818
819 /**
820 * Delete the given transform feedback objects.
821 * Part of GL_ARB_transform_feedback2.
822 */
823 void GLAPIENTRY
824 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
825 {
826 GLint i;
827 GET_CURRENT_CONTEXT(ctx);
828
829 ASSERT_OUTSIDE_BEGIN_END(ctx);
830
831 if (n < 0) {
832 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
833 return;
834 }
835
836 if (!names)
837 return;
838
839 for (i = 0; i < n; i++) {
840 if (names[i] > 0) {
841 struct gl_transform_feedback_object *obj
842 = lookup_transform_feedback_object(ctx, names[i]);
843 if (obj) {
844 if (obj->Active) {
845 _mesa_error(ctx, GL_INVALID_OPERATION,
846 "glDeleteTransformFeedbacks(object %u is active)",
847 names[i]);
848 return;
849 }
850 _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
851 /* unref, but object may not be deleted until later */
852 reference_transform_feedback_object(&obj, NULL);
853 }
854 }
855 }
856 }
857
858
859 /**
860 * Pause transform feedback.
861 * Part of GL_ARB_transform_feedback2.
862 */
863 void GLAPIENTRY
864 _mesa_PauseTransformFeedback(void)
865 {
866 struct gl_transform_feedback_object *obj;
867 GET_CURRENT_CONTEXT(ctx);
868
869 obj = ctx->TransformFeedback.CurrentObject;
870
871 if (!obj->Active || obj->Paused) {
872 _mesa_error(ctx, GL_INVALID_OPERATION,
873 "glPauseTransformFeedback(feedback not active or already paused)");
874 return;
875 }
876
877 obj->Paused = GL_TRUE;
878
879 assert(ctx->Driver.PauseTransformFeedback);
880 ctx->Driver.PauseTransformFeedback(ctx, obj);
881 }
882
883
884 /**
885 * Resume transform feedback.
886 * Part of GL_ARB_transform_feedback2.
887 */
888 void GLAPIENTRY
889 _mesa_ResumeTransformFeedback(void)
890 {
891 struct gl_transform_feedback_object *obj;
892 GET_CURRENT_CONTEXT(ctx);
893
894 obj = ctx->TransformFeedback.CurrentObject;
895
896 if (!obj->Active || !obj->Paused) {
897 _mesa_error(ctx, GL_INVALID_OPERATION,
898 "glResumeTransformFeedback(feedback not active or not paused)");
899 return;
900 }
901
902 obj->Paused = GL_FALSE;
903
904 assert(ctx->Driver.ResumeTransformFeedback);
905 ctx->Driver.ResumeTransformFeedback(ctx, obj);
906 }
907
908
909 /**
910 * Draw the vertex data in a transform feedback object.
911 * \param mode GL_POINTS, GL_LINES, GL_TRIANGLE_STRIP, etc.
912 * \param name the transform feedback object
913 * The number of vertices comes from the transform feedback object.
914 * User still has to setup of the vertex attribute info with
915 * glVertexPointer, glColorPointer, etc.
916 * Part of GL_ARB_transform_feedback2.
917 */
918 void GLAPIENTRY
919 _mesa_DrawTransformFeedback(GLenum mode, GLuint name)
920 {
921 GET_CURRENT_CONTEXT(ctx);
922 struct gl_transform_feedback_object *obj =
923 lookup_transform_feedback_object(ctx, name);
924
925 if (mode > GL_POLYGON) {
926 _mesa_error(ctx, GL_INVALID_ENUM,
927 "glDrawTransformFeedback(mode=0x%x)", mode);
928 return;
929 }
930 if (!obj) {
931 _mesa_error(ctx, GL_INVALID_VALUE,
932 "glDrawTransformFeedback(name = %u)", name);
933 return;
934 }
935
936 /* XXX check if EndTransformFeedback has never been called while
937 * the object was bound
938 */
939
940 assert(ctx->Driver.DrawTransformFeedback);
941 ctx->Driver.DrawTransformFeedback(ctx, mode, obj);
942 }
943
944 #endif /* FEATURE_EXT_transform_feedback */