mesa: add ARB_texture_buffer_range glTextureBufferRangeEXT function
[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 "context.h"
36 #include "hash.h"
37 #include "macros.h"
38 #include "mtypes.h"
39 #include "transformfeedback.h"
40 #include "shaderapi.h"
41 #include "shaderobj.h"
42
43 #include "program/program.h"
44 #include "program/prog_parameter.h"
45
46 struct using_program_tuple
47 {
48 struct gl_program *prog;
49 bool found;
50 };
51
52 static void
53 active_xfb_object_references_program(GLuint key, void *data, void *user_data)
54 {
55 struct using_program_tuple *callback_data = user_data;
56 struct gl_transform_feedback_object *obj = data;
57 if (obj->Active && obj->program == callback_data->prog)
58 callback_data->found = true;
59 }
60
61 /**
62 * Return true if any active transform feedback object is using a program.
63 */
64 bool
65 _mesa_transform_feedback_is_using_program(struct gl_context *ctx,
66 struct gl_shader_program *shProg)
67 {
68 if (!shProg->last_vert_prog)
69 return false;
70
71 struct using_program_tuple callback_data;
72 callback_data.found = false;
73 callback_data.prog = shProg->last_vert_prog;
74
75 _mesa_HashWalkLocked(ctx->TransformFeedback.Objects,
76 active_xfb_object_references_program, &callback_data);
77
78 /* Also check DefaultObject, as it's not in the Objects hash table. */
79 active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject,
80 &callback_data);
81
82 return callback_data.found;
83 }
84
85 /**
86 * Do reference counting of transform feedback buffers.
87 */
88 static void
89 reference_transform_feedback_object(struct gl_transform_feedback_object **ptr,
90 struct gl_transform_feedback_object *obj)
91 {
92 if (*ptr == obj)
93 return;
94
95 if (*ptr) {
96 /* Unreference the old object */
97 struct gl_transform_feedback_object *oldObj = *ptr;
98
99 assert(oldObj->RefCount > 0);
100 oldObj->RefCount--;
101
102 if (oldObj->RefCount == 0) {
103 GET_CURRENT_CONTEXT(ctx);
104 if (ctx)
105 ctx->Driver.DeleteTransformFeedback(ctx, oldObj);
106 }
107
108 *ptr = NULL;
109 }
110 assert(!*ptr);
111
112 if (obj) {
113 assert(obj->RefCount > 0);
114
115 /* reference new object */
116 obj->RefCount++;
117 obj->EverBound = GL_TRUE;
118 *ptr = obj;
119 }
120 }
121
122
123 /**
124 * Per-context init for transform feedback.
125 */
126 void
127 _mesa_init_transform_feedback(struct gl_context *ctx)
128 {
129 /* core mesa expects this, even a dummy one, to be available */
130 assert(ctx->Driver.NewTransformFeedback);
131
132 ctx->TransformFeedback.DefaultObject =
133 ctx->Driver.NewTransformFeedback(ctx, 0);
134
135 assert(ctx->TransformFeedback.DefaultObject->RefCount == 1);
136
137 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
138 ctx->TransformFeedback.DefaultObject);
139
140 assert(ctx->TransformFeedback.DefaultObject->RefCount == 2);
141
142 ctx->TransformFeedback.Objects = _mesa_NewHashTable();
143
144 _mesa_reference_buffer_object(ctx,
145 &ctx->TransformFeedback.CurrentBuffer,
146 ctx->Shared->NullBufferObj);
147 }
148
149
150
151 /**
152 * Callback for _mesa_HashDeleteAll().
153 */
154 static void
155 delete_cb(GLuint key, void *data, void *userData)
156 {
157 struct gl_context *ctx = (struct gl_context *) userData;
158 struct gl_transform_feedback_object *obj =
159 (struct gl_transform_feedback_object *) data;
160
161 ctx->Driver.DeleteTransformFeedback(ctx, obj);
162 }
163
164
165 /**
166 * Per-context free/clean-up for transform feedback.
167 */
168 void
169 _mesa_free_transform_feedback(struct gl_context *ctx)
170 {
171 /* core mesa expects this, even a dummy one, to be available */
172 assert(ctx->Driver.NewTransformFeedback);
173
174 _mesa_reference_buffer_object(ctx,
175 &ctx->TransformFeedback.CurrentBuffer,
176 NULL);
177
178 /* Delete all feedback objects */
179 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
180 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
181
182 /* Delete the default feedback object */
183 assert(ctx->Driver.DeleteTransformFeedback);
184 ctx->Driver.DeleteTransformFeedback(ctx,
185 ctx->TransformFeedback.DefaultObject);
186
187 ctx->TransformFeedback.CurrentObject = NULL;
188 }
189
190
191 /** Initialize the fields of a gl_transform_feedback_object. */
192 void
193 _mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
194 GLuint name)
195 {
196 obj->Name = name;
197 obj->RefCount = 1;
198 obj->EverBound = GL_FALSE;
199 }
200
201 /**
202 * Delete a transform feedback object. Called via
203 * ctx->Driver->DeleteTransformFeedback, if not overwritten by driver. In
204 * the latter case, called from the driver after all driver-specific clean-up
205 * has been done.
206 *
207 * \param ctx GL context to wich transform feedback object belongs.
208 * \param obj Transform feedback object due to be deleted.
209 */
210 void
211 _mesa_delete_transform_feedback_object(struct gl_context *ctx,
212 struct gl_transform_feedback_object
213 *obj)
214 {
215 for (unsigned i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
216 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
217 }
218
219 free(obj->Label);
220 free(obj);
221 }
222
223 /** Default fallback for ctx->Driver.NewTransformFeedback() */
224 static struct gl_transform_feedback_object *
225 new_transform_feedback_fallback(struct gl_context *ctx, GLuint name)
226 {
227 struct gl_transform_feedback_object *obj;
228
229 obj = CALLOC_STRUCT(gl_transform_feedback_object);
230 if (!obj)
231 return NULL;
232
233 _mesa_init_transform_feedback_object(obj, name);
234 return obj;
235 }
236
237 /** Default fallback for ctx->Driver.BeginTransformFeedback() */
238 static void
239 begin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode,
240 struct gl_transform_feedback_object *obj)
241 {
242 /* nop */
243 }
244
245 /** Default fallback for ctx->Driver.EndTransformFeedback() */
246 static void
247 end_transform_feedback_fallback(struct gl_context *ctx,
248 struct gl_transform_feedback_object *obj)
249 {
250 /* nop */
251 }
252
253 /** Default fallback for ctx->Driver.PauseTransformFeedback() */
254 static void
255 pause_transform_feedback_fallback(struct gl_context *ctx,
256 struct gl_transform_feedback_object *obj)
257 {
258 /* nop */
259 }
260
261 /** Default fallback for ctx->Driver.ResumeTransformFeedback() */
262 static void
263 resume_transform_feedback_fallback(struct gl_context *ctx,
264 struct gl_transform_feedback_object *obj)
265 {
266 /* nop */
267 }
268
269
270 /**
271 * Plug in default device driver functions for transform feedback.
272 * Most drivers will override some/all of these.
273 */
274 void
275 _mesa_init_transform_feedback_functions(struct dd_function_table *driver)
276 {
277 driver->NewTransformFeedback = new_transform_feedback_fallback;
278 driver->DeleteTransformFeedback = _mesa_delete_transform_feedback_object;
279 driver->BeginTransformFeedback = begin_transform_feedback_fallback;
280 driver->EndTransformFeedback = end_transform_feedback_fallback;
281 driver->PauseTransformFeedback = pause_transform_feedback_fallback;
282 driver->ResumeTransformFeedback = resume_transform_feedback_fallback;
283 }
284
285
286 /**
287 * Fill in the correct Size value for each buffer in \c obj.
288 *
289 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
290 * Targets"):
291 *
292 * BindBufferBase binds the entire buffer, even when the size of the buffer
293 * is changed after the binding is established. It is equivalent to calling
294 * BindBufferRange with offset zero, while size is determined by the size of
295 * the bound buffer at the time the binding is used.
296 *
297 * Regardless of the size specified with BindBufferRange, or indirectly with
298 * BindBufferBase, the GL will never read or write beyond the end of a bound
299 * buffer. In some cases this constraint may result in visibly different
300 * behavior when a buffer overflow would otherwise result, such as described
301 * for transform feedback operations in section 13.2.2.
302 */
303 static void
304 compute_transform_feedback_buffer_sizes(
305 struct gl_transform_feedback_object *obj)
306 {
307 unsigned i = 0;
308 for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
309 GLintptr offset = obj->Offset[i];
310 GLsizeiptr buffer_size
311 = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
312 GLsizeiptr available_space
313 = buffer_size <= offset ? 0 : buffer_size - offset;
314 GLsizeiptr computed_size;
315 if (obj->RequestedSize[i] == 0) {
316 /* No size was specified at the time the buffer was bound, so allow
317 * writing to all available space in the buffer.
318 */
319 computed_size = available_space;
320 } else {
321 /* A size was specified at the time the buffer was bound, however
322 * it's possible that the buffer has shrunk since then. So only
323 * allow writing to the minimum of the specified size and the space
324 * available.
325 */
326 computed_size = MIN2(available_space, obj->RequestedSize[i]);
327 }
328
329 /* Legal sizes must be multiples of four, so round down if necessary. */
330 obj->Size[i] = computed_size & ~0x3;
331 }
332 }
333
334
335 /**
336 * Compute the maximum number of vertices that can be written to the currently
337 * enabled transform feedback buffers without overflowing any of them.
338 */
339 unsigned
340 _mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
341 const struct gl_transform_feedback_object *obj,
342 const struct gl_transform_feedback_info *info)
343 {
344 unsigned max_index = 0xffffffff;
345 unsigned i;
346
347 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
348 if ((info->ActiveBuffers >> i) & 1) {
349 unsigned stride = info->Buffers[i].Stride;
350 unsigned max_for_this_buffer;
351
352 /* Skip any inactive buffers, which have a stride of 0. */
353 if (stride == 0)
354 continue;
355
356 max_for_this_buffer = obj->Size[i] / (4 * stride);
357 max_index = MIN2(max_index, max_for_this_buffer);
358 }
359 }
360
361 return max_index;
362 }
363
364
365 /**
366 ** Begin API functions
367 **/
368
369
370 /**
371 * Figure out which stage of the pipeline is the source of transform feedback
372 * data given the current context state, and return its gl_program.
373 *
374 * If no active program can generate transform feedback data (i.e. no vertex
375 * shader is active), returns NULL.
376 */
377 static struct gl_program *
378 get_xfb_source(struct gl_context *ctx)
379 {
380 int i;
381 for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
382 if (ctx->_Shader->CurrentProgram[i] != NULL)
383 return ctx->_Shader->CurrentProgram[i];
384 }
385 return NULL;
386 }
387
388
389 static ALWAYS_INLINE void
390 begin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
391 {
392 struct gl_transform_feedback_object *obj;
393 struct gl_transform_feedback_info *info = NULL;
394 struct gl_program *source;
395 GLuint i;
396 unsigned vertices_per_prim;
397
398 obj = ctx->TransformFeedback.CurrentObject;
399
400 /* Figure out what pipeline stage is the source of data for transform
401 * feedback.
402 */
403 source = get_xfb_source(ctx);
404 if (!no_error && source == NULL) {
405 _mesa_error(ctx, GL_INVALID_OPERATION,
406 "glBeginTransformFeedback(no program active)");
407 return;
408 }
409
410 info = source->sh.LinkedTransformFeedback;
411
412 if (!no_error && info->NumOutputs == 0) {
413 _mesa_error(ctx, GL_INVALID_OPERATION,
414 "glBeginTransformFeedback(no varyings to record)");
415 return;
416 }
417
418 switch (mode) {
419 case GL_POINTS:
420 vertices_per_prim = 1;
421 break;
422 case GL_LINES:
423 vertices_per_prim = 2;
424 break;
425 case GL_TRIANGLES:
426 vertices_per_prim = 3;
427 break;
428 default:
429 if (!no_error) {
430 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
431 return;
432 } else {
433 /* Stop compiler warnings */
434 unreachable("Error in API use when using KHR_no_error");
435 }
436 }
437
438 if (!no_error) {
439 if (obj->Active) {
440 _mesa_error(ctx, GL_INVALID_OPERATION,
441 "glBeginTransformFeedback(already active)");
442 return;
443 }
444
445 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
446 if ((info->ActiveBuffers >> i) & 1) {
447 if (obj->BufferNames[i] == 0) {
448 _mesa_error(ctx, GL_INVALID_OPERATION,
449 "glBeginTransformFeedback(binding point %d does not "
450 "have a buffer object bound)", i);
451 return;
452 }
453 }
454 }
455 }
456
457 FLUSH_VERTICES(ctx, 0);
458 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
459
460 obj->Active = GL_TRUE;
461 ctx->TransformFeedback.Mode = mode;
462
463 compute_transform_feedback_buffer_sizes(obj);
464
465 if (_mesa_is_gles3(ctx)) {
466 /* In GLES3, we are required to track the usage of the transform
467 * feedback buffer and report INVALID_OPERATION if a draw call tries to
468 * exceed it. So compute the maximum number of vertices that we can
469 * write without overflowing any of the buffers currently being used for
470 * feedback.
471 */
472 unsigned max_vertices
473 = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
474 obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
475 }
476
477 if (obj->program != source) {
478 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
479 _mesa_reference_program_(ctx, &obj->program, source);
480 obj->program = source;
481 }
482
483 assert(ctx->Driver.BeginTransformFeedback);
484 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
485 }
486
487
488 void GLAPIENTRY
489 _mesa_BeginTransformFeedback_no_error(GLenum mode)
490 {
491 GET_CURRENT_CONTEXT(ctx);
492 begin_transform_feedback(ctx, mode, true);
493 }
494
495
496 void GLAPIENTRY
497 _mesa_BeginTransformFeedback(GLenum mode)
498 {
499 GET_CURRENT_CONTEXT(ctx);
500 begin_transform_feedback(ctx, mode, false);
501 }
502
503
504 static void
505 end_transform_feedback(struct gl_context *ctx,
506 struct gl_transform_feedback_object *obj)
507 {
508 FLUSH_VERTICES(ctx, 0);
509 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
510
511 assert(ctx->Driver.EndTransformFeedback);
512 ctx->Driver.EndTransformFeedback(ctx, obj);
513
514 _mesa_reference_program_(ctx, &obj->program, NULL);
515 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
516 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
517 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
518 }
519
520
521 void GLAPIENTRY
522 _mesa_EndTransformFeedback_no_error(void)
523 {
524 GET_CURRENT_CONTEXT(ctx);
525 end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
526 }
527
528
529 void GLAPIENTRY
530 _mesa_EndTransformFeedback(void)
531 {
532 struct gl_transform_feedback_object *obj;
533 GET_CURRENT_CONTEXT(ctx);
534
535 obj = ctx->TransformFeedback.CurrentObject;
536
537 if (!obj->Active) {
538 _mesa_error(ctx, GL_INVALID_OPERATION,
539 "glEndTransformFeedback(not active)");
540 return;
541 }
542
543 end_transform_feedback(ctx, obj);
544 }
545
546
547 /**
548 * Helper used by BindBufferRange() and BindBufferBase().
549 */
550 static void
551 bind_buffer_range(struct gl_context *ctx,
552 struct gl_transform_feedback_object *obj,
553 GLuint index,
554 struct gl_buffer_object *bufObj,
555 GLintptr offset, GLsizeiptr size,
556 bool dsa)
557 {
558 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
559 * transform feedback buffers can't be changed while transform feedback is
560 * active.
561 */
562
563 if (!dsa) {
564 /* The general binding point */
565 _mesa_reference_buffer_object(ctx,
566 &ctx->TransformFeedback.CurrentBuffer,
567 bufObj);
568 }
569
570 /* The per-attribute binding point */
571 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
572 }
573
574
575 /**
576 * Validate the buffer object to receive transform feedback results. Plus,
577 * validate the starting offset to place the results, and max size.
578 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
579 * functions.
580 */
581 bool
582 _mesa_validate_buffer_range_xfb(struct gl_context *ctx,
583 struct gl_transform_feedback_object *obj,
584 GLuint index, struct gl_buffer_object *bufObj,
585 GLintptr offset, GLsizeiptr size, bool dsa)
586 {
587 const char *gl_methd_name;
588 if (dsa)
589 gl_methd_name = "glTransformFeedbackBufferRange";
590 else
591 gl_methd_name = "glBindBufferRange";
592
593
594 if (obj->Active) {
595 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
596 gl_methd_name);
597 return false;
598 }
599
600 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
601 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
602 * generated if index is greater than or equal to the number of binding
603 * points for transform feedback, as described in section 6.7.1."
604 */
605 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
606 gl_methd_name, index);
607 return false;
608 }
609
610 if (size & 0x3) {
611 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
612 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
613 "four)", gl_methd_name, (int) size);
614 return false;
615 }
616
617 if (offset & 0x3) {
618 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
619 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
620 "of four)", gl_methd_name, (int) offset);
621 return false;
622 }
623
624 if (offset < 0) {
625 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
626 * generated by BindBufferRange if offset is negative."
627 *
628 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
629 * is generated by TransformFeedbackBufferRange if offset is negative."
630 */
631 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
632 gl_methd_name,
633 (int) offset);
634 return false;
635 }
636
637 if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) {
638 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
639 * generated by BindBufferRange if buffer is non-zero and size is less
640 * than or equal to zero."
641 *
642 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
643 * is generated by TransformFeedbackBufferRange if size is less than or
644 * equal to zero."
645 */
646 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
647 gl_methd_name, (int) size);
648 return false;
649 }
650
651 return true;
652 }
653
654
655 /**
656 * Specify a buffer object to receive transform feedback results.
657 * As above, but start at offset = 0.
658 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
659 * functions.
660 */
661 void
662 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
663 struct gl_transform_feedback_object *obj,
664 GLuint index,
665 struct gl_buffer_object *bufObj,
666 bool dsa)
667 {
668 if (obj->Active) {
669 _mesa_error(ctx, GL_INVALID_OPERATION,
670 "%s(transform feedback active)",
671 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
672 return;
673 }
674
675 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
676 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
677 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
678 index);
679 return;
680 }
681
682 bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
683 }
684
685 /**
686 * Wrapper around lookup_transform_feedback_object that throws
687 * GL_INVALID_OPERATION if id is not in the hash table. After calling
688 * _mesa_error, it returns NULL.
689 */
690 static struct gl_transform_feedback_object *
691 lookup_transform_feedback_object_err(struct gl_context *ctx,
692 GLuint xfb, const char* func)
693 {
694 struct gl_transform_feedback_object *obj;
695
696 obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
697 if (!obj) {
698 _mesa_error(ctx, GL_INVALID_OPERATION,
699 "%s(xfb=%u: non-generated object name)", func, xfb);
700 }
701
702 return obj;
703 }
704
705 /**
706 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
707 * is not in the hash table. Specialised version for the
708 * transform-feedback-related functions. After calling _mesa_error, it
709 * returns NULL.
710 */
711 static struct gl_buffer_object *
712 lookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
713 GLuint buffer, const char* func)
714 {
715 struct gl_buffer_object *bufObj;
716
717 /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
718 * name of an existing buffer object.
719 */
720 if (buffer == 0) {
721 bufObj = ctx->Shared->NullBufferObj;
722 } else {
723 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
724 if (!bufObj) {
725 _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
726 buffer);
727 }
728 }
729
730 return bufObj;
731 }
732
733 void GLAPIENTRY
734 _mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
735 {
736 GET_CURRENT_CONTEXT(ctx);
737 struct gl_transform_feedback_object *obj;
738 struct gl_buffer_object *bufObj;
739
740 obj = lookup_transform_feedback_object_err(ctx, xfb,
741 "glTransformFeedbackBufferBase");
742 if (!obj) {
743 return;
744 }
745
746 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
747 "glTransformFeedbackBufferBase");
748 if (!bufObj) {
749 return;
750 }
751
752 _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
753 }
754
755 void GLAPIENTRY
756 _mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
757 GLintptr offset, GLsizeiptr size)
758 {
759 GET_CURRENT_CONTEXT(ctx);
760 struct gl_transform_feedback_object *obj;
761 struct gl_buffer_object *bufObj;
762
763 obj = lookup_transform_feedback_object_err(ctx, xfb,
764 "glTransformFeedbackBufferRange");
765 if (!obj) {
766 return;
767 }
768
769 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
770 "glTransformFeedbackBufferRange");
771 if (!bufObj) {
772 return;
773 }
774
775 if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
776 size, true))
777 return;
778
779 /* The per-attribute binding point */
780 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
781 size);
782 }
783
784 /**
785 * Specify a buffer object to receive transform feedback results, plus the
786 * offset in the buffer to start placing results.
787 * This function is part of GL_EXT_transform_feedback, but not GL3.
788 */
789 static ALWAYS_INLINE void
790 bind_buffer_offset(struct gl_context *ctx,
791 struct gl_transform_feedback_object *obj, GLuint index,
792 GLuint buffer, GLintptr offset, bool no_error)
793 {
794 struct gl_buffer_object *bufObj;
795
796 if (buffer == 0) {
797 bufObj = ctx->Shared->NullBufferObj;
798 } else {
799 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
800 if (!no_error && !bufObj) {
801 _mesa_error(ctx, GL_INVALID_OPERATION,
802 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
803 return;
804 }
805 }
806
807 _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
808 }
809
810
811 void GLAPIENTRY
812 _mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer,
813 GLintptr offset)
814 {
815 GET_CURRENT_CONTEXT(ctx);
816 bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer,
817 offset, true);
818 }
819
820
821 void GLAPIENTRY
822 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
823 GLintptr offset)
824 {
825 struct gl_transform_feedback_object *obj;
826 GET_CURRENT_CONTEXT(ctx);
827
828 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
829 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
830 return;
831 }
832
833 obj = ctx->TransformFeedback.CurrentObject;
834
835 if (obj->Active) {
836 _mesa_error(ctx, GL_INVALID_OPERATION,
837 "glBindBufferOffsetEXT(transform feedback active)");
838 return;
839 }
840
841 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
842 _mesa_error(ctx, GL_INVALID_VALUE,
843 "glBindBufferOffsetEXT(index=%d)", index);
844 return;
845 }
846
847 if (offset & 0x3) {
848 /* must be multiple of four */
849 _mesa_error(ctx, GL_INVALID_VALUE,
850 "glBindBufferOffsetEXT(offset=%d)", (int) offset);
851 return;
852 }
853
854 bind_buffer_offset(ctx, obj, index, buffer, offset, false);
855 }
856
857
858 /**
859 * This function specifies the transform feedback outputs to be written
860 * to the feedback buffer(s), and in what order.
861 */
862 static ALWAYS_INLINE void
863 transform_feedback_varyings(struct gl_context *ctx,
864 struct gl_shader_program *shProg, GLsizei count,
865 const GLchar *const *varyings, GLenum bufferMode)
866 {
867 GLint i;
868
869 /* free existing varyings, if any */
870 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
871 free(shProg->TransformFeedback.VaryingNames[i]);
872 }
873 free(shProg->TransformFeedback.VaryingNames);
874
875 /* allocate new memory for varying names */
876 shProg->TransformFeedback.VaryingNames =
877 malloc(count * sizeof(GLchar *));
878
879 if (!shProg->TransformFeedback.VaryingNames) {
880 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
881 return;
882 }
883
884 /* Save the new names and the count */
885 for (i = 0; i < count; i++) {
886 shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
887 }
888 shProg->TransformFeedback.NumVarying = count;
889
890 shProg->TransformFeedback.BufferMode = bufferMode;
891
892 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
893 * the varyings won't be used until shader link time.
894 */
895 }
896
897
898 void GLAPIENTRY
899 _mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count,
900 const GLchar *const *varyings,
901 GLenum bufferMode)
902 {
903 GET_CURRENT_CONTEXT(ctx);
904
905 struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
906 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
907 }
908
909 void GLAPIENTRY
910 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
911 const GLchar * const *varyings,
912 GLenum bufferMode)
913 {
914 struct gl_shader_program *shProg;
915 GLint i;
916 GET_CURRENT_CONTEXT(ctx);
917
918 /* From the ARB_transform_feedback2 specification:
919 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
920 * if the current transform feedback object is active, even if paused."
921 */
922 if (ctx->TransformFeedback.CurrentObject->Active) {
923 _mesa_error(ctx, GL_INVALID_OPERATION,
924 "glTransformFeedbackVaryings(current object is active)");
925 return;
926 }
927
928 switch (bufferMode) {
929 case GL_INTERLEAVED_ATTRIBS:
930 break;
931 case GL_SEPARATE_ATTRIBS:
932 break;
933 default:
934 _mesa_error(ctx, GL_INVALID_ENUM,
935 "glTransformFeedbackVaryings(bufferMode)");
936 return;
937 }
938
939 if (count < 0 ||
940 (bufferMode == GL_SEPARATE_ATTRIBS &&
941 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
942 _mesa_error(ctx, GL_INVALID_VALUE,
943 "glTransformFeedbackVaryings(count=%d)", count);
944 return;
945 }
946
947 shProg = _mesa_lookup_shader_program_err(ctx, program,
948 "glTransformFeedbackVaryings");
949 if (!shProg)
950 return;
951
952 if (ctx->Extensions.ARB_transform_feedback3) {
953 if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
954 unsigned buffers = 1;
955
956 for (i = 0; i < count; i++) {
957 if (strcmp(varyings[i], "gl_NextBuffer") == 0)
958 buffers++;
959 }
960
961 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
962 _mesa_error(ctx, GL_INVALID_OPERATION,
963 "glTransformFeedbackVaryings(too many gl_NextBuffer "
964 "occurrences)");
965 return;
966 }
967 } else {
968 for (i = 0; i < count; i++) {
969 if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
970 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
971 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
972 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
973 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
974 _mesa_error(ctx, GL_INVALID_OPERATION,
975 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
976 "varying=%s)",
977 varyings[i]);
978 return;
979 }
980 }
981 }
982 }
983
984 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
985 }
986
987
988 /**
989 * Get info about the transform feedback outputs which are to be written
990 * to the feedback buffer(s).
991 */
992 void GLAPIENTRY
993 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
994 GLsizei bufSize, GLsizei *length,
995 GLsizei *size, GLenum *type, GLchar *name)
996 {
997 const struct gl_shader_program *shProg;
998 struct gl_program_resource *res;
999 GET_CURRENT_CONTEXT(ctx);
1000
1001 shProg = _mesa_lookup_shader_program_err(ctx, program,
1002 "glGetTransformFeedbackVarying");
1003 if (!shProg)
1004 return;
1005
1006 res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
1007 GL_TRANSFORM_FEEDBACK_VARYING,
1008 index);
1009 if (!res) {
1010 _mesa_error(ctx, GL_INVALID_VALUE,
1011 "glGetTransformFeedbackVarying(index=%u)", index);
1012 return;
1013 }
1014
1015 /* return the varying's name and length */
1016 _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
1017
1018 /* return the datatype and value's size (in datatype units) */
1019 if (type)
1020 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1021 res, index, GL_TYPE, (GLint*) type,
1022 "glGetTransformFeedbackVarying");
1023 if (size)
1024 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
1025 res, index, GL_ARRAY_SIZE, (GLint*) size,
1026 "glGetTransformFeedbackVarying");
1027 }
1028
1029
1030
1031 struct gl_transform_feedback_object *
1032 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
1033 {
1034 /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
1035 * the default transform feedback object, or the name of an existing
1036 * transform feedback object."
1037 */
1038 if (name == 0) {
1039 return ctx->TransformFeedback.DefaultObject;
1040 }
1041 else
1042 return (struct gl_transform_feedback_object *)
1043 _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
1044 }
1045
1046 static void
1047 create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
1048 bool dsa)
1049 {
1050 GLuint first;
1051 const char* func;
1052
1053 if (dsa)
1054 func = "glCreateTransformFeedbacks";
1055 else
1056 func = "glGenTransformFeedbacks";
1057
1058 if (n < 0) {
1059 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
1060 return;
1061 }
1062
1063 if (!ids)
1064 return;
1065
1066 /* we don't need contiguous IDs, but this might be faster */
1067 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
1068 if (first) {
1069 GLsizei i;
1070 for (i = 0; i < n; i++) {
1071 struct gl_transform_feedback_object *obj
1072 = ctx->Driver.NewTransformFeedback(ctx, first + i);
1073 if (!obj) {
1074 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1075 return;
1076 }
1077 ids[i] = first + i;
1078 _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i,
1079 obj);
1080 if (dsa) {
1081 /* this is normally done at bind time in the non-dsa case */
1082 obj->EverBound = GL_TRUE;
1083 }
1084 }
1085 }
1086 else {
1087 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
1088 }
1089 }
1090
1091 /**
1092 * Create new transform feedback objects. Transform feedback objects
1093 * encapsulate the state related to transform feedback to allow quickly
1094 * switching state (and drawing the results, below).
1095 * Part of GL_ARB_transform_feedback2.
1096 */
1097 void GLAPIENTRY
1098 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
1099 {
1100 GET_CURRENT_CONTEXT(ctx);
1101
1102 /* GenTransformFeedbacks should just reserve the object names that a
1103 * subsequent call to BindTransformFeedback should actively create. For
1104 * the sake of simplicity, we reserve the names and create the objects
1105 * straight away.
1106 */
1107
1108 create_transform_feedbacks(ctx, n, names, false);
1109 }
1110
1111 /**
1112 * Create new transform feedback objects. Transform feedback objects
1113 * encapsulate the state related to transform feedback to allow quickly
1114 * switching state (and drawing the results, below).
1115 * Part of GL_ARB_direct_state_access.
1116 */
1117 void GLAPIENTRY
1118 _mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
1119 {
1120 GET_CURRENT_CONTEXT(ctx);
1121
1122 create_transform_feedbacks(ctx, n, names, true);
1123 }
1124
1125
1126 /**
1127 * Is the given ID a transform feedback object?
1128 * Part of GL_ARB_transform_feedback2.
1129 */
1130 GLboolean GLAPIENTRY
1131 _mesa_IsTransformFeedback(GLuint name)
1132 {
1133 struct gl_transform_feedback_object *obj;
1134 GET_CURRENT_CONTEXT(ctx);
1135
1136 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1137
1138 if (name == 0)
1139 return GL_FALSE;
1140
1141 obj = _mesa_lookup_transform_feedback_object(ctx, name);
1142 if (obj == NULL)
1143 return GL_FALSE;
1144
1145 return obj->EverBound;
1146 }
1147
1148
1149 /**
1150 * Bind the given transform feedback object.
1151 * Part of GL_ARB_transform_feedback2.
1152 */
1153 static ALWAYS_INLINE void
1154 bind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error)
1155 {
1156 struct gl_transform_feedback_object *obj;
1157
1158 obj = _mesa_lookup_transform_feedback_object(ctx, name);
1159 if (!no_error && !obj) {
1160 _mesa_error(ctx, GL_INVALID_OPERATION,
1161 "glBindTransformFeedback(name=%u)", name);
1162 return;
1163 }
1164
1165 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1166 obj);
1167 }
1168
1169
1170 void GLAPIENTRY
1171 _mesa_BindTransformFeedback_no_error(GLenum target, GLuint name)
1172 {
1173 GET_CURRENT_CONTEXT(ctx);
1174 bind_transform_feedback(ctx, name, true);
1175 }
1176
1177
1178 void GLAPIENTRY
1179 _mesa_BindTransformFeedback(GLenum target, GLuint name)
1180 {
1181 GET_CURRENT_CONTEXT(ctx);
1182
1183 if (target != GL_TRANSFORM_FEEDBACK) {
1184 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
1185 return;
1186 }
1187
1188 if (_mesa_is_xfb_active_and_unpaused(ctx)) {
1189 _mesa_error(ctx, GL_INVALID_OPERATION,
1190 "glBindTransformFeedback(transform is active, or not paused)");
1191 return;
1192 }
1193
1194 bind_transform_feedback(ctx, name, false);
1195 }
1196
1197
1198 /**
1199 * Delete the given transform feedback objects.
1200 * Part of GL_ARB_transform_feedback2.
1201 */
1202 void GLAPIENTRY
1203 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
1204 {
1205 GLint i;
1206 GET_CURRENT_CONTEXT(ctx);
1207
1208 if (n < 0) {
1209 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
1210 return;
1211 }
1212
1213 if (!names)
1214 return;
1215
1216 for (i = 0; i < n; i++) {
1217 if (names[i] > 0) {
1218 struct gl_transform_feedback_object *obj
1219 = _mesa_lookup_transform_feedback_object(ctx, names[i]);
1220 if (obj) {
1221 if (obj->Active) {
1222 _mesa_error(ctx, GL_INVALID_OPERATION,
1223 "glDeleteTransformFeedbacks(object %u is active)",
1224 names[i]);
1225 return;
1226 }
1227 _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
1228 /* unref, but object may not be deleted until later */
1229 if (obj == ctx->TransformFeedback.CurrentObject) {
1230 reference_transform_feedback_object(
1231 &ctx->TransformFeedback.CurrentObject,
1232 ctx->TransformFeedback.DefaultObject);
1233 }
1234 reference_transform_feedback_object(&obj, NULL);
1235 }
1236 }
1237 }
1238 }
1239
1240
1241 /**
1242 * Pause transform feedback.
1243 * Part of GL_ARB_transform_feedback2.
1244 */
1245 static void
1246 pause_transform_feedback(struct gl_context *ctx,
1247 struct gl_transform_feedback_object *obj)
1248 {
1249 FLUSH_VERTICES(ctx, 0);
1250 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1251
1252 assert(ctx->Driver.PauseTransformFeedback);
1253 ctx->Driver.PauseTransformFeedback(ctx, obj);
1254
1255 obj->Paused = GL_TRUE;
1256 }
1257
1258
1259 void GLAPIENTRY
1260 _mesa_PauseTransformFeedback_no_error(void)
1261 {
1262 GET_CURRENT_CONTEXT(ctx);
1263 pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1264 }
1265
1266
1267 void GLAPIENTRY
1268 _mesa_PauseTransformFeedback(void)
1269 {
1270 struct gl_transform_feedback_object *obj;
1271 GET_CURRENT_CONTEXT(ctx);
1272
1273 obj = ctx->TransformFeedback.CurrentObject;
1274
1275 if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
1276 _mesa_error(ctx, GL_INVALID_OPERATION,
1277 "glPauseTransformFeedback(feedback not active or already paused)");
1278 return;
1279 }
1280
1281 pause_transform_feedback(ctx, obj);
1282 }
1283
1284
1285 /**
1286 * Resume transform feedback.
1287 * Part of GL_ARB_transform_feedback2.
1288 */
1289 static void
1290 resume_transform_feedback(struct gl_context *ctx,
1291 struct gl_transform_feedback_object *obj)
1292 {
1293 FLUSH_VERTICES(ctx, 0);
1294 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1295
1296 obj->Paused = GL_FALSE;
1297
1298 assert(ctx->Driver.ResumeTransformFeedback);
1299 ctx->Driver.ResumeTransformFeedback(ctx, obj);
1300 }
1301
1302
1303 void GLAPIENTRY
1304 _mesa_ResumeTransformFeedback_no_error(void)
1305 {
1306 GET_CURRENT_CONTEXT(ctx);
1307 resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1308 }
1309
1310
1311 void GLAPIENTRY
1312 _mesa_ResumeTransformFeedback(void)
1313 {
1314 struct gl_transform_feedback_object *obj;
1315 GET_CURRENT_CONTEXT(ctx);
1316
1317 obj = ctx->TransformFeedback.CurrentObject;
1318
1319 if (!obj->Active || !obj->Paused) {
1320 _mesa_error(ctx, GL_INVALID_OPERATION,
1321 "glResumeTransformFeedback(feedback not active or not paused)");
1322 return;
1323 }
1324
1325 /* From the ARB_transform_feedback2 specification:
1326 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1327 * the program object being used by the current transform feedback object
1328 * is not active."
1329 */
1330 if (obj->program != get_xfb_source(ctx)) {
1331 _mesa_error(ctx, GL_INVALID_OPERATION,
1332 "glResumeTransformFeedback(wrong program bound)");
1333 return;
1334 }
1335
1336 resume_transform_feedback(ctx, obj);
1337 }
1338
1339 extern void GLAPIENTRY
1340 _mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
1341 {
1342 struct gl_transform_feedback_object *obj;
1343 GET_CURRENT_CONTEXT(ctx);
1344
1345 obj = lookup_transform_feedback_object_err(ctx, xfb,
1346 "glGetTransformFeedbackiv");
1347 if (!obj) {
1348 return;
1349 }
1350
1351 switch(pname) {
1352 case GL_TRANSFORM_FEEDBACK_PAUSED:
1353 *param = obj->Paused;
1354 break;
1355 case GL_TRANSFORM_FEEDBACK_ACTIVE:
1356 *param = obj->Active;
1357 break;
1358 default:
1359 _mesa_error(ctx, GL_INVALID_ENUM,
1360 "glGetTransformFeedbackiv(pname=%i)", pname);
1361 }
1362 }
1363
1364 extern void GLAPIENTRY
1365 _mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
1366 GLint *param)
1367 {
1368 struct gl_transform_feedback_object *obj;
1369 GET_CURRENT_CONTEXT(ctx);
1370
1371 obj = lookup_transform_feedback_object_err(ctx, xfb,
1372 "glGetTransformFeedbacki_v");
1373 if (!obj) {
1374 return;
1375 }
1376
1377 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1378 _mesa_error(ctx, GL_INVALID_VALUE,
1379 "glGetTransformFeedbacki_v(index=%i)", index);
1380 return;
1381 }
1382
1383 switch(pname) {
1384 case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
1385 *param = obj->BufferNames[index];
1386 break;
1387 default:
1388 _mesa_error(ctx, GL_INVALID_ENUM,
1389 "glGetTransformFeedbacki_v(pname=%i)", pname);
1390 }
1391 }
1392
1393 extern void GLAPIENTRY
1394 _mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
1395 GLint64 *param)
1396 {
1397 struct gl_transform_feedback_object *obj;
1398 GET_CURRENT_CONTEXT(ctx);
1399
1400 obj = lookup_transform_feedback_object_err(ctx, xfb,
1401 "glGetTransformFeedbacki64_v");
1402 if (!obj) {
1403 return;
1404 }
1405
1406 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1407 _mesa_error(ctx, GL_INVALID_VALUE,
1408 "glGetTransformFeedbacki64_v(index=%i)", index);
1409 return;
1410 }
1411
1412 /**
1413 * This follows the same general rules used for BindBufferBase:
1414 *
1415 * "To query the starting offset or size of the range of a buffer
1416 * object binding in an indexed array, call GetInteger64i_v with
1417 * target set to respectively the starting offset or binding size
1418 * name from table 6.5 for that array. Index must be in the range
1419 * zero to the number of bind points supported minus one. If the
1420 * starting offset or size was not specified when the buffer object
1421 * was bound (e.g. if it was bound with BindBufferBase), or if no
1422 * buffer object is bound to the target array at index, zero is
1423 * returned."
1424 */
1425 if (obj->RequestedSize[index] == 0 &&
1426 (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START ||
1427 pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) {
1428 *param = 0;
1429 return;
1430 }
1431
1432 compute_transform_feedback_buffer_sizes(obj);
1433 switch(pname) {
1434 case GL_TRANSFORM_FEEDBACK_BUFFER_START:
1435 assert(obj->RequestedSize[index] > 0);
1436 *param = obj->Offset[index];
1437 break;
1438 case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
1439 assert(obj->RequestedSize[index] > 0);
1440 *param = obj->Size[index];
1441 break;
1442 default:
1443 _mesa_error(ctx, GL_INVALID_ENUM,
1444 "glGetTransformFeedbacki64_v(pname=%i)", pname);
1445 }
1446 }