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