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