i965: Disable 3DSTATE_WM_HZ_OP fields.
[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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /*
27 * Transform feedback support.
28 *
29 * Authors:
30 * Brian Paul
31 */
32
33
34 #include "buffers.h"
35 #include "bufferobj.h"
36 #include "context.h"
37 #include "hash.h"
38 #include "macros.h"
39 #include "mtypes.h"
40 #include "transformfeedback.h"
41 #include "shaderapi.h"
42 #include "shaderobj.h"
43 #include "main/dispatch.h"
44
45 #include "program/prog_parameter.h"
46
47 struct using_program_tuple
48 {
49 struct gl_shader_program *shProg;
50 bool found;
51 };
52
53 static void
54 active_xfb_object_references_program(GLuint key, void *data, void *user_data)
55 {
56 struct using_program_tuple *callback_data = user_data;
57 struct gl_transform_feedback_object *obj = data;
58 if (obj->Active && obj->shader_program == callback_data->shProg)
59 callback_data->found = true;
60 }
61
62 /**
63 * Return true if any active transform feedback object is using a program.
64 */
65 bool
66 _mesa_transform_feedback_is_using_program(struct gl_context *ctx,
67 struct gl_shader_program *shProg)
68 {
69 struct using_program_tuple callback_data;
70 callback_data.shProg = shProg;
71 callback_data.found = false;
72
73 _mesa_HashWalk(ctx->TransformFeedback.Objects,
74 active_xfb_object_references_program, &callback_data);
75
76 /* Also check DefaultObject, as it's not in the Objects hash table. */
77 active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject,
78 &callback_data);
79
80 return callback_data.found;
81 }
82
83 /**
84 * Do reference counting of transform feedback buffers.
85 */
86 static void
87 reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
88 struct gl_transform_feedback_object *obj)
89 {
90 if (*ptr == obj)
91 return;
92
93 if (*ptr) {
94 /* Unreference the old object */
95 struct gl_transform_feedback_object *oldObj = *ptr;
96
97 ASSERT(oldObj->RefCount > 0);
98 oldObj->RefCount--;
99
100 if (oldObj->RefCount == 0) {
101 GET_CURRENT_CONTEXT(ctx);
102 if (ctx)
103 ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
104 }
105
106 *ptr = NULL;
107 }
108 ASSERT(!*ptr);
109
110 if (obj) {
111 /* reference new object */
112 if (obj->RefCount == 0) {
113 _mesa_problem(NULL, "referencing deleted transform feedback object");
114 *ptr = NULL;
115 }
116 else {
117 obj->RefCount++;
118 obj->EverBound = GL_TRUE;
119 *ptr = obj;
120 }
121 }
122 }
123
124
125 /**
126 * Check that all the buffer objects currently bound for transform
127 * feedback actually exist. Raise a GL_INVALID_OPERATION error if
128 * any buffers are missing.
129 * \return GL_TRUE for success, GL_FALSE if error
130 */
131 GLboolean
132 _mesa_validate_transform_feedback_buffers(struct gl_context *ctx)
133 {
134 /* XXX to do */
135 return GL_TRUE;
136 }
137
138
139
140 /**
141 * Per-context init for transform feedback.
142 */
143 void
144 _mesa_init_transform_feedback(struct gl_context *ctx)
145 {
146 /* core mesa expects this, even a dummy one, to be available */
147 ASSERT(ctx->Driver.NewTransformFeedback);
148
149 ctx->TransformFeedback.DefaultObject =
150 ctx->Driver.NewTransformFeedback(ctx, 0);
151
152 assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
153
154 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
155 ctx->TransformFeedback.DefaultObject);
156
157 assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
158
159 ctx->TransformFeedback.Objects = _mesa_NewHashTable();
160
161 _mesa_reference_buffer_object(ctx,
162 &ctx->TransformFeedback.CurrentBuffer,
163 ctx->Shared->NullBufferObj);
164 }
165
166
167
168 /**
169 * Callback for _mesa_HashDeleteAll().
170 */
171 static void
172 delete_cb(GLuint key, void *data, void *userData)
173 {
174 struct gl_context *ctx = (struct gl_context *) userData;
175 struct gl_transform_feedback_object *obj =
176 (struct gl_transform_feedback_object *) data;
177
178 ctx->Driver.DeleteTransformFeedback(ctx, obj);
179 }
180
181
182 /**
183 * Per-context free/clean-up for transform feedback.
184 */
185 void
186 _mesa_free_transform_feedback(struct gl_context *ctx)
187 {
188 /* core mesa expects this, even a dummy one, to be available */
189 ASSERT(ctx->Driver.NewTransformFeedback);
190
191 _mesa_reference_buffer_object(ctx,
192 &ctx->TransformFeedback.CurrentBuffer,
193 NULL);
194
195 /* Delete all feedback objects */
196 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
197 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
198
199 /* Delete the default feedback object */
200 assert(ctx->Driver.DeleteTransformFeedback);
201 ctx->Driver.DeleteTransformFeedback(ctx,
202 ctx->TransformFeedback.DefaultObject);
203
204 ctx->TransformFeedback.CurrentObject = NULL;
205 }
206
207
208 /** Initialize the fields of a gl_transform_feedback_object. */
209 void
210 _mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
211 GLuint name)
212 {
213 if (!obj)
214 return;
215
216 obj->Name = name;
217 obj->RefCount = 1;
218 obj->EverBound = GL_FALSE;
219 }
220
221
222 /** Default fallback for ctx->Driver.NewTransformFeedback() */
223 static struct gl_transform_feedback_object *
224 new_transform_feedback(struct gl_context *ctx, GLuint name)
225 {
226 struct gl_transform_feedback_object *obj;
227 obj = CALLOC_STRUCT(gl_transform_feedback_object);
228 _mesa_init_transform_feedback_object(obj, name);
229 return obj;
230 }
231
232 /** Default fallback for ctx->Driver.DeleteTransformFeedback() */
233 static void
234 delete_transform_feedback(struct gl_context *ctx,
235 struct gl_transform_feedback_object *obj)
236 {
237 GLuint i;
238
239 for (i = 0; i < Elements(obj->Buffers); i++) {
240 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
241 }
242
243 free(obj->Label);
244 free(obj);
245 }
246
247
248 /** Default fallback for ctx->Driver.BeginTransformFeedback() */
249 static void
250 begin_transform_feedback(struct gl_context *ctx, GLenum mode,
251 struct gl_transform_feedback_object *obj)
252 {
253 /* nop */
254 }
255
256 /** Default fallback for ctx->Driver.EndTransformFeedback() */
257 static void
258 end_transform_feedback(struct gl_context *ctx,
259 struct gl_transform_feedback_object *obj)
260 {
261 /* nop */
262 }
263
264 /** Default fallback for ctx->Driver.PauseTransformFeedback() */
265 static void
266 pause_transform_feedback(struct gl_context *ctx,
267 struct gl_transform_feedback_object *obj)
268 {
269 /* nop */
270 }
271
272 /** Default fallback for ctx->Driver.ResumeTransformFeedback() */
273 static void
274 resume_transform_feedback(struct gl_context *ctx,
275 struct gl_transform_feedback_object *obj)
276 {
277 /* nop */
278 }
279
280
281 /**
282 * Plug in default device driver functions for transform feedback.
283 * Most drivers will override some/all of these.
284 */
285 void
286 _mesa_init_transform_feedback_functions(struct dd_function_table *driver)
287 {
288 driver->NewTransformFeedback = new_transform_feedback;
289 driver->DeleteTransformFeedback = delete_transform_feedback;
290 driver->BeginTransformFeedback = begin_transform_feedback;
291 driver->EndTransformFeedback = end_transform_feedback;
292 driver->PauseTransformFeedback = pause_transform_feedback;
293 driver->ResumeTransformFeedback = resume_transform_feedback;
294 }
295
296
297 /**
298 * Fill in the correct Size value for each buffer in \c obj.
299 *
300 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
301 * Targets"):
302 *
303 * BindBufferBase binds the entire buffer, even when the size of the buffer
304 * is changed after the binding is established. It is equivalent to calling
305 * BindBufferRange with offset zero, while size is determined by the size of
306 * the bound buffer at the time the binding is used.
307 *
308 * Regardless of the size specified with BindBufferRange, or indirectly with
309 * BindBufferBase, the GL will never read or write beyond the end of a bound
310 * buffer. In some cases this constraint may result in visibly different
311 * behavior when a buffer overflow would otherwise result, such as described
312 * for transform feedback operations in section 13.2.2.
313 */
314 static void
315 compute_transform_feedback_buffer_sizes(
316 struct gl_transform_feedback_object *obj)
317 {
318 unsigned i = 0;
319 for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
320 GLintptr offset = obj->Offset[i];
321 GLsizeiptr buffer_size
322 = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
323 GLsizeiptr available_space
324 = buffer_size <= offset ? 0 : buffer_size - offset;
325 GLsizeiptr computed_size;
326 if (obj->RequestedSize[i] == 0) {
327 /* No size was specified at the time the buffer was bound, so allow
328 * writing to all available space in the buffer.
329 */
330 computed_size = available_space;
331 } else {
332 /* A size was specified at the time the buffer was bound, however
333 * it's possible that the buffer has shrunk since then. So only
334 * allow writing to the minimum of the specified size and the space
335 * available.
336 */
337 computed_size = MIN2(available_space, obj->RequestedSize[i]);
338 }
339
340 /* Legal sizes must be multiples of four, so round down if necessary. */
341 obj->Size[i] = computed_size & ~0x3;
342 }
343 }
344
345
346 /**
347 * Compute the maximum number of vertices that can be written to the currently
348 * enabled transform feedback buffers without overflowing any of them.
349 */
350 unsigned
351 _mesa_compute_max_transform_feedback_vertices(
352 const struct gl_transform_feedback_object *obj,
353 const struct gl_transform_feedback_info *info)
354 {
355 unsigned max_index = 0xffffffff;
356 unsigned i;
357
358 for (i = 0; i < info->NumBuffers; ++i) {
359 unsigned stride = info->BufferStride[i];
360 unsigned max_for_this_buffer;
361
362 /* Skip any inactive buffers, which have a stride of 0. */
363 if (stride == 0)
364 continue;
365
366 max_for_this_buffer = obj->Size[i] / (4 * stride);
367 max_index = MIN2(max_index, max_for_this_buffer);
368 }
369
370 return max_index;
371 }
372
373
374 /**
375 ** Begin API functions
376 **/
377
378
379 /**
380 * Figure out which stage of the pipeline is the source of transform feedback
381 * data given the current context state, and return its gl_shader_program.
382 *
383 * If no active program can generate transform feedback data (i.e. no vertex
384 * shader is active), returns NULL.
385 */
386 static struct gl_shader_program *
387 get_xfb_source(struct gl_context *ctx)
388 {
389 int i;
390 for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
391 if (ctx->Shader.CurrentProgram[i] != NULL)
392 return ctx->Shader.CurrentProgram[i];
393 }
394 return NULL;
395 }
396
397
398 void GLAPIENTRY
399 _mesa_BeginTransformFeedback(GLenum mode)
400 {
401 struct gl_transform_feedback_object *obj;
402 struct gl_transform_feedback_info *info = NULL;
403 struct gl_shader_program *source;
404 GLuint i;
405 unsigned vertices_per_prim;
406 GET_CURRENT_CONTEXT(ctx);
407
408 obj = ctx->TransformFeedback.CurrentObject;
409
410 /* Figure out what pipeline stage is the source of data for transform
411 * feedback.
412 */
413 source = get_xfb_source(ctx);
414 if (source == NULL) {
415 _mesa_error(ctx, GL_INVALID_OPERATION,
416 "glBeginTransformFeedback(no program active)");
417 return;
418 }
419
420 info = &source->LinkedTransformFeedback;
421
422 if (info->NumOutputs == 0) {
423 _mesa_error(ctx, GL_INVALID_OPERATION,
424 "glBeginTransformFeedback(no varyings to record)");
425 return;
426 }
427
428 switch (mode) {
429 case GL_POINTS:
430 vertices_per_prim = 1;
431 break;
432 case GL_LINES:
433 vertices_per_prim = 2;
434 break;
435 case GL_TRIANGLES:
436 vertices_per_prim = 3;
437 break;
438 default:
439 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
440 return;
441 }
442
443 if (obj->Active) {
444 _mesa_error(ctx, GL_INVALID_OPERATION,
445 "glBeginTransformFeedback(already active)");
446 return;
447 }
448
449 for (i = 0; i < info->NumBuffers; ++i) {
450 if (obj->BufferNames[i] == 0) {
451 _mesa_error(ctx, GL_INVALID_OPERATION,
452 "glBeginTransformFeedback(binding point %d does not have "
453 "a buffer object bound)", i);
454 return;
455 }
456 }
457
458 FLUSH_VERTICES(ctx, 0);
459 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
460
461 obj->Active = GL_TRUE;
462 ctx->TransformFeedback.Mode = mode;
463
464 compute_transform_feedback_buffer_sizes(obj);
465
466 if (_mesa_is_gles3(ctx)) {
467 /* In GLES3, we are required to track the usage of the transform
468 * feedback buffer and report INVALID_OPERATION if a draw call tries to
469 * exceed it. So compute the maximum number of vertices that we can
470 * write without overflowing any of the buffers currently being used for
471 * feedback.
472 */
473 unsigned max_vertices
474 = _mesa_compute_max_transform_feedback_vertices(obj, info);
475 obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
476 }
477
478 if (obj->shader_program != source) {
479 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
480 obj->shader_program = source;
481 }
482
483 assert(ctx->Driver.BeginTransformFeedback);
484 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
485 }
486
487
488 void GLAPIENTRY
489 _mesa_EndTransformFeedback(void)
490 {
491 struct gl_transform_feedback_object *obj;
492 GET_CURRENT_CONTEXT(ctx);
493
494 obj = ctx->TransformFeedback.CurrentObject;
495
496 if (!obj->Active) {
497 _mesa_error(ctx, GL_INVALID_OPERATION,
498 "glEndTransformFeedback(not active)");
499 return;
500 }
501
502 FLUSH_VERTICES(ctx, 0);
503 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
504
505 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
506 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
507 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
508
509 assert(ctx->Driver.EndTransformFeedback);
510 ctx->Driver.EndTransformFeedback(ctx, obj);
511 }
512
513
514 /**
515 * Helper used by BindBufferRange() and BindBufferBase().
516 */
517 static void
518 bind_buffer_range(struct gl_context *ctx, GLuint index,
519 struct gl_buffer_object *bufObj,
520 GLintptr offset, GLsizeiptr size)
521 {
522 struct gl_transform_feedback_object *obj =
523 ctx->TransformFeedback.CurrentObject;
524
525 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
526 * transform feedback buffers can't be changed while transform feedback is
527 * active.
528 */
529
530 /* The general binding point */
531 _mesa_reference_buffer_object(ctx,
532 &ctx->TransformFeedback.CurrentBuffer,
533 bufObj);
534
535 /* The per-attribute binding point */
536 _mesa_reference_buffer_object(ctx,
537 &obj->Buffers[index],
538 bufObj);
539
540 obj->BufferNames[index] = bufObj->Name;
541
542 obj->Offset[index] = offset;
543 obj->RequestedSize[index] = size;
544 }
545
546
547 /**
548 * Specify a buffer object to receive transform feedback results. Plus,
549 * specify the starting offset to place the results, and max size.
550 * Called from the glBindBufferRange() function.
551 */
552 void
553 _mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx,
554 GLuint index,
555 struct gl_buffer_object *bufObj,
556 GLintptr offset,
557 GLsizeiptr size)
558 {
559 struct gl_transform_feedback_object *obj;
560
561 obj = ctx->TransformFeedback.CurrentObject;
562
563 if (obj->Active) {
564 _mesa_error(ctx, GL_INVALID_OPERATION,
565 "glBindBufferRange(transform feedback active)");
566 return;
567 }
568
569 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
570 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
571 return;
572 }
573
574 if (size & 0x3) {
575 /* must a multiple of four */
576 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size);
577 return;
578 }
579
580 if (offset & 0x3) {
581 /* must be multiple of four */
582 _mesa_error(ctx, GL_INVALID_VALUE,
583 "glBindBufferRange(offset=%d)", (int) offset);
584 return;
585 }
586
587 bind_buffer_range(ctx, index, bufObj, offset, size);
588 }
589
590
591 /**
592 * Specify a buffer object to receive transform feedback results.
593 * As above, but start at offset = 0.
594 * Called from the glBindBufferBase() function.
595 */
596 void
597 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
598 GLuint index,
599 struct gl_buffer_object *bufObj)
600 {
601 struct gl_transform_feedback_object *obj;
602
603 obj = ctx->TransformFeedback.CurrentObject;
604
605 if (obj->Active) {
606 _mesa_error(ctx, GL_INVALID_OPERATION,
607 "glBindBufferBase(transform feedback active)");
608 return;
609 }
610
611 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
612 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
613 return;
614 }
615
616 bind_buffer_range(ctx, index, bufObj, 0, 0);
617 }
618
619
620 /**
621 * Specify a buffer object to receive transform feedback results, plus the
622 * offset in the buffer to start placing results.
623 * This function is part of GL_EXT_transform_feedback, but not GL3.
624 */
625 void GLAPIENTRY
626 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
627 GLintptr offset)
628 {
629 struct gl_transform_feedback_object *obj;
630 struct gl_buffer_object *bufObj;
631 GET_CURRENT_CONTEXT(ctx);
632
633 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
634 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
635 return;
636 }
637
638 obj = ctx->TransformFeedback.CurrentObject;
639
640 if (obj->Active) {
641 _mesa_error(ctx, GL_INVALID_OPERATION,
642 "glBindBufferOffsetEXT(transform feedback active)");
643 return;
644 }
645
646 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
647 _mesa_error(ctx, GL_INVALID_VALUE,
648 "glBindBufferOffsetEXT(index=%d)", index);
649 return;
650 }
651
652 if (offset & 0x3) {
653 /* must be multiple of four */
654 _mesa_error(ctx, GL_INVALID_VALUE,
655 "glBindBufferOffsetEXT(offset=%d)", (int) offset);
656 return;
657 }
658
659 if (buffer == 0) {
660 bufObj = ctx->Shared->NullBufferObj;
661 } else {
662 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
663 }
664
665 if (!bufObj) {
666 _mesa_error(ctx, GL_INVALID_OPERATION,
667 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
668 return;
669 }
670
671 bind_buffer_range(ctx, index, bufObj, offset, 0);
672 }
673
674
675 /**
676 * This function specifies the transform feedback outputs to be written
677 * to the feedback buffer(s), and in what order.
678 */
679 void GLAPIENTRY
680 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
681 const GLchar * const *varyings,
682 GLenum bufferMode)
683 {
684 struct gl_shader_program *shProg;
685 GLint i;
686 GET_CURRENT_CONTEXT(ctx);
687
688 /* From the ARB_transform_feedback2 specification:
689 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
690 * if the current transform feedback object is active, even if paused."
691 */
692 if (ctx->TransformFeedback.CurrentObject->Active) {
693 _mesa_error(ctx, GL_INVALID_OPERATION,
694 "glTransformFeedbackVaryings(current object is active)");
695 return;
696 }
697
698 switch (bufferMode) {
699 case GL_INTERLEAVED_ATTRIBS:
700 break;
701 case GL_SEPARATE_ATTRIBS:
702 break;
703 default:
704 _mesa_error(ctx, GL_INVALID_ENUM,
705 "glTransformFeedbackVaryings(bufferMode)");
706 return;
707 }
708
709 if (count < 0 ||
710 (bufferMode == GL_SEPARATE_ATTRIBS &&
711 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
712 _mesa_error(ctx, GL_INVALID_VALUE,
713 "glTransformFeedbackVaryings(count=%d)", count);
714 return;
715 }
716
717 shProg = _mesa_lookup_shader_program(ctx, program);
718 if (!shProg) {
719 _mesa_error(ctx, GL_INVALID_VALUE,
720 "glTransformFeedbackVaryings(program=%u)", program);
721 return;
722 }
723
724 if (ctx->Extensions.ARB_transform_feedback3) {
725 if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
726 unsigned buffers = 1;
727
728 for (i = 0; i < count; i++) {
729 if (strcmp(varyings[i], "gl_NextBuffer") == 0)
730 buffers++;
731 }
732
733 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
734 _mesa_error(ctx, GL_INVALID_OPERATION,
735 "glTransformFeedbackVaryings(too many gl_NextBuffer "
736 "occurences)");
737 return;
738 }
739 } else {
740 for (i = 0; i < count; i++) {
741 if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
742 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
743 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
744 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
745 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
746 _mesa_error(ctx, GL_INVALID_OPERATION,
747 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
748 "varying=%s)",
749 varyings[i]);
750 return;
751 }
752 }
753 }
754 }
755
756 /* free existing varyings, if any */
757 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
758 free(shProg->TransformFeedback.VaryingNames[i]);
759 }
760 free(shProg->TransformFeedback.VaryingNames);
761
762 /* allocate new memory for varying names */
763 shProg->TransformFeedback.VaryingNames =
764 malloc(count * sizeof(GLchar *));
765
766 if (!shProg->TransformFeedback.VaryingNames) {
767 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
768 return;
769 }
770
771 /* Save the new names and the count */
772 for (i = 0; i < count; i++) {
773 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
774 }
775 shProg->TransformFeedback.NumVarying = count;
776
777 shProg->TransformFeedback.BufferMode = bufferMode;
778
779 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
780 * the varyings won't be used until shader link time.
781 */
782 }
783
784
785 /**
786 * Get info about the transform feedback outputs which are to be written
787 * to the feedback buffer(s).
788 */
789 void GLAPIENTRY
790 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
791 GLsizei bufSize, GLsizei *length,
792 GLsizei *size, GLenum *type, GLchar *name)
793 {
794 const struct gl_shader_program *shProg;
795 const struct gl_transform_feedback_info *linked_xfb_info;
796 GET_CURRENT_CONTEXT(ctx);
797
798 shProg = _mesa_lookup_shader_program(ctx, program);
799 if (!shProg) {
800 _mesa_error(ctx, GL_INVALID_VALUE,
801 "glGetTransformFeedbackVarying(program=%u)", program);
802 return;
803 }
804
805 linked_xfb_info = &shProg->LinkedTransformFeedback;
806 if (index >= (GLuint) linked_xfb_info->NumVarying) {
807 _mesa_error(ctx, GL_INVALID_VALUE,
808 "glGetTransformFeedbackVarying(index=%u)", index);
809 return;
810 }
811
812 /* return the varying's name and length */
813 _mesa_copy_string(name, bufSize, length,
814 linked_xfb_info->Varyings[index].Name);
815
816 /* return the datatype and value's size (in datatype units) */
817 if (type)
818 *type = linked_xfb_info->Varyings[index].Type;
819 if (size)
820 *size = linked_xfb_info->Varyings[index].Size;
821 }
822
823
824
825 struct gl_transform_feedback_object *
826 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
827 {
828 if (name == 0) {
829 return ctx->TransformFeedback.DefaultObject;
830 }
831 else
832 return (struct gl_transform_feedback_object *)
833 _mesa_HashLookup(ctx->TransformFeedback.Objects, name);
834 }
835
836
837 /**
838 * Create new transform feedback objects. Transform feedback objects
839 * encapsulate the state related to transform feedback to allow quickly
840 * switching state (and drawing the results, below).
841 * Part of GL_ARB_transform_feedback2.
842 */
843 void GLAPIENTRY
844 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
845 {
846 GLuint first;
847 GET_CURRENT_CONTEXT(ctx);
848
849 if (n < 0) {
850 _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)");
851 return;
852 }
853
854 if (!names)
855 return;
856
857 /* we don't need contiguous IDs, but this might be faster */
858 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
859 if (first) {
860 GLsizei i;
861 for (i = 0; i < n; i++) {
862 struct gl_transform_feedback_object *obj
863 = ctx->Driver.NewTransformFeedback(ctx, first + i);
864 if (!obj) {
865 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
866 return;
867 }
868 names[i] = first + i;
869 _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj);
870 }
871 }
872 else {
873 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks");
874 }
875 }
876
877
878 /**
879 * Is the given ID a transform feedback object?
880 * Part of GL_ARB_transform_feedback2.
881 */
882 GLboolean GLAPIENTRY
883 _mesa_IsTransformFeedback(GLuint name)
884 {
885 struct gl_transform_feedback_object *obj;
886 GET_CURRENT_CONTEXT(ctx);
887
888 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
889
890 if (name == 0)
891 return GL_FALSE;
892
893 obj = _mesa_lookup_transform_feedback_object(ctx, name);
894 if (obj == NULL)
895 return GL_FALSE;
896
897 return obj->EverBound;
898 }
899
900
901 /**
902 * Bind the given transform feedback object.
903 * Part of GL_ARB_transform_feedback2.
904 */
905 void GLAPIENTRY
906 _mesa_BindTransformFeedback(GLenum target, GLuint name)
907 {
908 struct gl_transform_feedback_object *obj;
909 GET_CURRENT_CONTEXT(ctx);
910
911 if (target != GL_TRANSFORM_FEEDBACK) {
912 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
913 return;
914 }
915
916 if (_mesa_is_xfb_active_and_unpaused(ctx)) {
917 _mesa_error(ctx, GL_INVALID_OPERATION,
918 "glBindTransformFeedback(transform is active, or not paused)");
919 return;
920 }
921
922 obj = _mesa_lookup_transform_feedback_object(ctx, name);
923 if (!obj) {
924 _mesa_error(ctx, GL_INVALID_OPERATION,
925 "glBindTransformFeedback(name=%u)", name);
926 return;
927 }
928
929 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
930 obj);
931 }
932
933
934 /**
935 * Delete the given transform feedback objects.
936 * Part of GL_ARB_transform_feedback2.
937 */
938 void GLAPIENTRY
939 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
940 {
941 GLint i;
942 GET_CURRENT_CONTEXT(ctx);
943
944 if (n < 0) {
945 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
946 return;
947 }
948
949 if (!names)
950 return;
951
952 for (i = 0; i < n; i++) {
953 if (names[i] > 0) {
954 struct gl_transform_feedback_object *obj
955 = _mesa_lookup_transform_feedback_object(ctx, names[i]);
956 if (obj) {
957 if (obj->Active) {
958 _mesa_error(ctx, GL_INVALID_OPERATION,
959 "glDeleteTransformFeedbacks(object %u is active)",
960 names[i]);
961 return;
962 }
963 _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]);
964 /* unref, but object may not be deleted until later */
965 reference_transform_feedback_object(&obj, NULL);
966 }
967 }
968 }
969 }
970
971
972 /**
973 * Pause transform feedback.
974 * Part of GL_ARB_transform_feedback2.
975 */
976 void GLAPIENTRY
977 _mesa_PauseTransformFeedback(void)
978 {
979 struct gl_transform_feedback_object *obj;
980 GET_CURRENT_CONTEXT(ctx);
981
982 obj = ctx->TransformFeedback.CurrentObject;
983
984 if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
985 _mesa_error(ctx, GL_INVALID_OPERATION,
986 "glPauseTransformFeedback(feedback not active or already paused)");
987 return;
988 }
989
990 FLUSH_VERTICES(ctx, 0);
991 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
992
993 obj->Paused = GL_TRUE;
994
995 assert(ctx->Driver.PauseTransformFeedback);
996 ctx->Driver.PauseTransformFeedback(ctx, obj);
997 }
998
999
1000 /**
1001 * Resume transform feedback.
1002 * Part of GL_ARB_transform_feedback2.
1003 */
1004 void GLAPIENTRY
1005 _mesa_ResumeTransformFeedback(void)
1006 {
1007 struct gl_transform_feedback_object *obj;
1008 GET_CURRENT_CONTEXT(ctx);
1009
1010 obj = ctx->TransformFeedback.CurrentObject;
1011
1012 if (!obj->Active || !obj->Paused) {
1013 _mesa_error(ctx, GL_INVALID_OPERATION,
1014 "glResumeTransformFeedback(feedback not active or not paused)");
1015 return;
1016 }
1017
1018 /* From the ARB_transform_feedback2 specification:
1019 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1020 * the program object being used by the current transform feedback object
1021 * is not active."
1022 */
1023 if (obj->shader_program != get_xfb_source(ctx)) {
1024 _mesa_error(ctx, GL_INVALID_OPERATION,
1025 "glResumeTransformFeedback(wrong program bound)");
1026 return;
1027 }
1028
1029 FLUSH_VERTICES(ctx, 0);
1030 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1031
1032 obj->Paused = GL_FALSE;
1033
1034 assert(ctx->Driver.ResumeTransformFeedback);
1035 ctx->Driver.ResumeTransformFeedback(ctx, obj);
1036 }