blob: 9ffbfe7e6c679166527c32c1ef8284e7694cfcc2 [file] [log] [blame]
Brian Paulde8530e2010-03-30 19:50:11 -06001/*
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
Kenneth Graunke3d8d5b22013-04-21 13:46:48 -070019 * 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.
Brian Paulde8530e2010-03-30 19:50:11 -060023 */
24
25
26/*
Paul Berrye1907092014-01-22 05:14:48 -080027 * Transform feedback support.
Brian Paulde8530e2010-03-30 19:50:11 -060028 *
29 * Authors:
30 * Brian Paul
31 */
32
33
34#include "buffers.h"
Brian Paulde8530e2010-03-30 19:50:11 -060035#include "context.h"
Brian Paulfef6e362010-05-10 21:11:21 -060036#include "hash.h"
Paul Berry3870f292012-12-12 14:14:12 -080037#include "macros.h"
Vinson Lee0117da42011-01-05 23:11:54 -080038#include "mtypes.h"
Brian Paulde8530e2010-03-30 19:50:11 -060039#include "transformfeedback.h"
Brian Paula37b2212010-06-10 20:23:03 -060040#include "shaderapi.h"
41#include "shaderobj.h"
Chia-I Wub0930162010-05-11 13:20:40 +080042#include "main/dispatch.h"
Brian Paulde8530e2010-03-30 19:50:11 -060043
Brian Paulec2b92f2010-06-10 23:02:41 -060044#include "program/prog_parameter.h"
Brian Paulde8530e2010-03-30 19:50:11 -060045
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070046struct using_program_tuple
47{
Timothy Arceri3177eef2016-11-03 14:35:08 +110048 struct gl_program *prog;
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070049 bool found;
50};
51
52static void
53active_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;
Timothy Arceri3177eef2016-11-03 14:35:08 +110057 if (obj->Active && obj->program == callback_data->prog)
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070058 callback_data->found = true;
59}
60
61/**
62 * Return true if any active transform feedback object is using a program.
63 */
64bool
65_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
66 struct gl_shader_program *shProg)
67{
Timothy Arcerif86d15e2016-11-20 21:44:29 +110068 if (!shProg->last_vert_prog)
69 return false;
70
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070071 struct using_program_tuple callback_data;
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070072 callback_data.found = false;
Timothy Arcerif86d15e2016-11-20 21:44:29 +110073 callback_data.prog = shProg->last_vert_prog;
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070074
Timothy Arceri347fe242017-04-24 15:59:24 +100075 _mesa_HashWalkLocked(ctx->TransformFeedback.Objects,
76 active_xfb_object_references_program, &callback_data);
Kenneth Graunke2b71b3d2013-09-06 15:41:19 -070077
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}
Brian Paulde8530e2010-03-30 19:50:11 -060084
85/**
Brian Paulfef6e362010-05-10 21:11:21 -060086 * Do reference counting of transform feedback buffers.
87 */
88static void
89reference_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
Matt Turnerbfcdb842015-02-20 20:18:47 -080099 assert(oldObj->RefCount > 0);
Brian Paulfef6e362010-05-10 21:11:21 -0600100 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 }
Matt Turnerbfcdb842015-02-20 20:18:47 -0800110 assert(!*ptr);
Brian Paulfef6e362010-05-10 21:11:21 -0600111
112 if (obj) {
Timothy Arceri622a68e2017-04-21 13:29:46 +1000113 assert(obj->RefCount > 0);
114
Brian Paulfef6e362010-05-10 21:11:21 -0600115 /* reference new object */
Timothy Arceri622a68e2017-04-21 13:29:46 +1000116 obj->RefCount++;
117 obj->EverBound = GL_TRUE;
118 *ptr = obj;
Brian Paulfef6e362010-05-10 21:11:21 -0600119 }
120}
121
122
123/**
Brian Paulde8530e2010-03-30 19:50:11 -0600124 * Per-context init for transform feedback.
125 */
126void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400127_mesa_init_transform_feedback(struct gl_context *ctx)
Brian Paulde8530e2010-03-30 19:50:11 -0600128{
Chia-I Wu32a9b272010-07-06 16:27:20 +0800129 /* core mesa expects this, even a dummy one, to be available */
Matt Turnerbfcdb842015-02-20 20:18:47 -0800130 assert(ctx->Driver.NewTransformFeedback);
Brian Paulfef6e362010-05-10 21:11:21 -0600131
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
Brian Paulde8530e2010-03-30 19:50:11 -0600144 _mesa_reference_buffer_object(ctx,
145 &ctx->TransformFeedback.CurrentBuffer,
146 ctx->Shared->NullBufferObj);
147}
148
149
Brian Paulfef6e362010-05-10 21:11:21 -0600150
151/**
152 * Callback for _mesa_HashDeleteAll().
153 */
154static void
155delete_cb(GLuint key, void *data, void *userData)
156{
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400157 struct gl_context *ctx = (struct gl_context *) userData;
Brian Paulfef6e362010-05-10 21:11:21 -0600158 struct gl_transform_feedback_object *obj =
159 (struct gl_transform_feedback_object *) data;
160
161 ctx->Driver.DeleteTransformFeedback(ctx, obj);
162}
163
164
Brian Paulde8530e2010-03-30 19:50:11 -0600165/**
166 * Per-context free/clean-up for transform feedback.
167 */
168void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400169_mesa_free_transform_feedback(struct gl_context *ctx)
Brian Paulde8530e2010-03-30 19:50:11 -0600170{
Chia-I Wu32a9b272010-07-06 16:27:20 +0800171 /* core mesa expects this, even a dummy one, to be available */
Matt Turnerbfcdb842015-02-20 20:18:47 -0800172 assert(ctx->Driver.NewTransformFeedback);
Brian Paulfef6e362010-05-10 21:11:21 -0600173
Brian Paulde8530e2010-03-30 19:50:11 -0600174 _mesa_reference_buffer_object(ctx,
175 &ctx->TransformFeedback.CurrentBuffer,
176 NULL);
Brian Paulfef6e362010-05-10 21:11:21 -0600177
178 /* Delete all feedback objects */
179 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx);
Brian Paul5b90f832010-07-01 11:07:32 -0600180 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects);
Brian Paulfef6e362010-05-10 21:11:21 -0600181
182 /* Delete the default feedback object */
183 assert(ctx->Driver.DeleteTransformFeedback);
Brian Paul39c13a12010-05-14 08:03:42 -0600184 ctx->Driver.DeleteTransformFeedback(ctx,
185 ctx->TransformFeedback.DefaultObject);
Brian Paulfef6e362010-05-10 21:11:21 -0600186
187 ctx->TransformFeedback.CurrentObject = NULL;
Brian Paulde8530e2010-03-30 19:50:11 -0600188}
189
190
Kenneth Graunkef02ee302013-10-25 15:54:10 -0700191/** Initialize the fields of a gl_transform_feedback_object. */
192void
193_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj,
194 GLuint name)
195{
196 if (!obj)
197 return;
198
199 obj->Name = name;
200 obj->RefCount = 1;
201 obj->EverBound = GL_FALSE;
202}
203
204
Brian Paulfef6e362010-05-10 21:11:21 -0600205/** Default fallback for ctx->Driver.NewTransformFeedback() */
206static struct gl_transform_feedback_object *
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400207new_transform_feedback(struct gl_context *ctx, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -0600208{
209 struct gl_transform_feedback_object *obj;
210 obj = CALLOC_STRUCT(gl_transform_feedback_object);
Kenneth Graunkef02ee302013-10-25 15:54:10 -0700211 _mesa_init_transform_feedback_object(obj, name);
Brian Paulfef6e362010-05-10 21:11:21 -0600212 return obj;
213}
214
215/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
216static void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400217delete_transform_feedback(struct gl_context *ctx,
Brian Paulfef6e362010-05-10 21:11:21 -0600218 struct gl_transform_feedback_object *obj)
219{
220 GLuint i;
221
Brian Paulc2e130f2015-02-28 08:57:11 -0700222 for (i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
Brian Paulfef6e362010-05-10 21:11:21 -0600223 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
224 }
225
Timothy Arceri6d8dd592013-08-26 17:16:08 +1000226 free(obj->Label);
Brian Paulfef6e362010-05-10 21:11:21 -0600227 free(obj);
228}
229
Chia-I Wu32a9b272010-07-06 16:27:20 +0800230
Brian Paulfef6e362010-05-10 21:11:21 -0600231/** Default fallback for ctx->Driver.BeginTransformFeedback() */
232static void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400233begin_transform_feedback(struct gl_context *ctx, GLenum mode,
Brian Paulfef6e362010-05-10 21:11:21 -0600234 struct gl_transform_feedback_object *obj)
235{
236 /* nop */
237}
238
239/** Default fallback for ctx->Driver.EndTransformFeedback() */
240static void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400241end_transform_feedback(struct gl_context *ctx,
Brian Paulfef6e362010-05-10 21:11:21 -0600242 struct gl_transform_feedback_object *obj)
243{
244 /* nop */
245}
246
247/** Default fallback for ctx->Driver.PauseTransformFeedback() */
248static void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400249pause_transform_feedback(struct gl_context *ctx,
Brian Paulfef6e362010-05-10 21:11:21 -0600250 struct gl_transform_feedback_object *obj)
251{
252 /* nop */
253}
254
255/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
256static void
Kristian Høgsbergf9995b32010-10-12 12:26:10 -0400257resume_transform_feedback(struct gl_context *ctx,
Brian Paulfef6e362010-05-10 21:11:21 -0600258 struct gl_transform_feedback_object *obj)
259{
260 /* nop */
261}
262
Brian Paulfef6e362010-05-10 21:11:21 -0600263
264/**
265 * Plug in default device driver functions for transform feedback.
266 * Most drivers will override some/all of these.
267 */
268void
269_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
270{
271 driver->NewTransformFeedback = new_transform_feedback;
272 driver->DeleteTransformFeedback = delete_transform_feedback;
273 driver->BeginTransformFeedback = begin_transform_feedback;
274 driver->EndTransformFeedback = end_transform_feedback;
275 driver->PauseTransformFeedback = pause_transform_feedback;
276 driver->ResumeTransformFeedback = resume_transform_feedback;
Brian Paulfef6e362010-05-10 21:11:21 -0600277}
278
279
Brian Paulfef6e362010-05-10 21:11:21 -0600280/**
Paul Berry1ad51622012-12-15 13:06:10 -0800281 * Fill in the correct Size value for each buffer in \c obj.
282 *
283 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
284 * Targets"):
285 *
286 * BindBufferBase binds the entire buffer, even when the size of the buffer
287 * is changed after the binding is established. It is equivalent to calling
288 * BindBufferRange with offset zero, while size is determined by the size of
289 * the bound buffer at the time the binding is used.
290 *
291 * Regardless of the size specified with BindBufferRange, or indirectly with
292 * BindBufferBase, the GL will never read or write beyond the end of a bound
293 * buffer. In some cases this constraint may result in visibly different
294 * behavior when a buffer overflow would otherwise result, such as described
295 * for transform feedback operations in section 13.2.2.
296 */
297static void
298compute_transform_feedback_buffer_sizes(
299 struct gl_transform_feedback_object *obj)
300{
301 unsigned i = 0;
302 for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
303 GLintptr offset = obj->Offset[i];
304 GLsizeiptr buffer_size
305 = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
306 GLsizeiptr available_space
307 = buffer_size <= offset ? 0 : buffer_size - offset;
308 GLsizeiptr computed_size;
309 if (obj->RequestedSize[i] == 0) {
310 /* No size was specified at the time the buffer was bound, so allow
311 * writing to all available space in the buffer.
312 */
313 computed_size = available_space;
314 } else {
315 /* A size was specified at the time the buffer was bound, however
316 * it's possible that the buffer has shrunk since then. So only
317 * allow writing to the minimum of the specified size and the space
318 * available.
319 */
320 computed_size = MIN2(available_space, obj->RequestedSize[i]);
321 }
322
323 /* Legal sizes must be multiples of four, so round down if necessary. */
324 obj->Size[i] = computed_size & ~0x3;
325 }
326}
327
328
329/**
Paul Berry3870f292012-12-12 14:14:12 -0800330 * Compute the maximum number of vertices that can be written to the currently
331 * enabled transform feedback buffers without overflowing any of them.
332 */
333unsigned
Timothy Arceri258299d2016-03-03 13:20:01 +1100334_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
Paul Berry3870f292012-12-12 14:14:12 -0800335 const struct gl_transform_feedback_object *obj,
336 const struct gl_transform_feedback_info *info)
337{
338 unsigned max_index = 0xffffffff;
339 unsigned i;
340
Timothy Arceri258299d2016-03-03 13:20:01 +1100341 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
342 if ((info->ActiveBuffers >> i) & 1) {
Timothy Arcericf039a32016-03-10 15:00:00 +1100343 unsigned stride = info->Buffers[i].Stride;
Timothy Arceri258299d2016-03-03 13:20:01 +1100344 unsigned max_for_this_buffer;
Paul Berry3870f292012-12-12 14:14:12 -0800345
Timothy Arceri258299d2016-03-03 13:20:01 +1100346 /* Skip any inactive buffers, which have a stride of 0. */
347 if (stride == 0)
348 continue;
Paul Berry3870f292012-12-12 14:14:12 -0800349
Timothy Arceri258299d2016-03-03 13:20:01 +1100350 max_for_this_buffer = obj->Size[i] / (4 * stride);
351 max_index = MIN2(max_index, max_for_this_buffer);
352 }
Paul Berry3870f292012-12-12 14:14:12 -0800353 }
354
355 return max_index;
356}
357
358
359/**
Brian Paulfef6e362010-05-10 21:11:21 -0600360 ** Begin API functions
361 **/
362
363
Paul Berrye1907092014-01-22 05:14:48 -0800364/**
365 * Figure out which stage of the pipeline is the source of transform feedback
Timothy Arceri3177eef2016-11-03 14:35:08 +1100366 * data given the current context state, and return its gl_program.
Paul Berrye1907092014-01-22 05:14:48 -0800367 *
368 * If no active program can generate transform feedback data (i.e. no vertex
369 * shader is active), returns NULL.
370 */
Timothy Arceri3177eef2016-11-03 14:35:08 +1100371static struct gl_program *
Paul Berrye1907092014-01-22 05:14:48 -0800372get_xfb_source(struct gl_context *ctx)
373{
374 int i;
375 for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
Gregory Hainautc0347702013-05-03 19:44:10 +0200376 if (ctx->_Shader->CurrentProgram[i] != NULL)
Timothy Arceric505d6d2016-10-31 22:39:17 +1100377 return ctx->_Shader->CurrentProgram[i];
Paul Berrye1907092014-01-22 05:14:48 -0800378 }
379 return NULL;
380}
381
382
Brian Paulde8530e2010-03-30 19:50:11 -0600383void GLAPIENTRY
384_mesa_BeginTransformFeedback(GLenum mode)
385{
Brian Paulfef6e362010-05-10 21:11:21 -0600386 struct gl_transform_feedback_object *obj;
Paul Berrye1907092014-01-22 05:14:48 -0800387 struct gl_transform_feedback_info *info = NULL;
Brian Paul8e45e382012-10-27 08:58:19 -0600388 GLuint i;
Paul Berryb87e65c2012-12-13 09:30:09 -0800389 unsigned vertices_per_prim;
Brian Paulde8530e2010-03-30 19:50:11 -0600390 GET_CURRENT_CONTEXT(ctx);
391
Brian Paulfef6e362010-05-10 21:11:21 -0600392 obj = ctx->TransformFeedback.CurrentObject;
Paul Berry86bb45f2011-12-30 10:14:35 -0800393
Paul Berrye1907092014-01-22 05:14:48 -0800394 /* Figure out what pipeline stage is the source of data for transform
395 * feedback.
396 */
Timothy Arceri3177eef2016-11-03 14:35:08 +1100397 struct gl_program *source = get_xfb_source(ctx);
Paul Berrye1907092014-01-22 05:14:48 -0800398 if (source == NULL) {
Paul Berry86bb45f2011-12-30 10:14:35 -0800399 _mesa_error(ctx, GL_INVALID_OPERATION,
400 "glBeginTransformFeedback(no program active)");
401 return;
402 }
403
Timothy Arceri3177eef2016-11-03 14:35:08 +1100404 info = source->sh.LinkedTransformFeedback;
Brian Paulfef6e362010-05-10 21:11:21 -0600405
Paul Berry86bb45f2011-12-30 10:14:35 -0800406 if (info->NumOutputs == 0) {
407 _mesa_error(ctx, GL_INVALID_OPERATION,
408 "glBeginTransformFeedback(no varyings to record)");
409 return;
410 }
411
Brian Paulde8530e2010-03-30 19:50:11 -0600412 switch (mode) {
413 case GL_POINTS:
Paul Berryb87e65c2012-12-13 09:30:09 -0800414 vertices_per_prim = 1;
415 break;
Brian Paulde8530e2010-03-30 19:50:11 -0600416 case GL_LINES:
Paul Berryb87e65c2012-12-13 09:30:09 -0800417 vertices_per_prim = 2;
418 break;
Brian Paulde8530e2010-03-30 19:50:11 -0600419 case GL_TRIANGLES:
Paul Berryb87e65c2012-12-13 09:30:09 -0800420 vertices_per_prim = 3;
Brian Paulde8530e2010-03-30 19:50:11 -0600421 break;
422 default:
423 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
424 return;
425 }
426
Brian Paulfef6e362010-05-10 21:11:21 -0600427 if (obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600428 _mesa_error(ctx, GL_INVALID_OPERATION,
429 "glBeginTransformFeedback(already active)");
430 return;
431 }
432
Timothy Arceri258299d2016-03-03 13:20:01 +1100433 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
434 if ((info->ActiveBuffers >> i) & 1) {
435 if (obj->BufferNames[i] == 0) {
436 _mesa_error(ctx, GL_INVALID_OPERATION,
437 "glBeginTransformFeedback(binding point %d does not "
438 "have a buffer object bound)", i);
439 return;
440 }
Paul Berryebfad9f2011-12-29 15:55:01 -0800441 }
442 }
443
Marek Olšákb95cbe52013-04-15 00:50:20 +0200444 FLUSH_VERTICES(ctx, 0);
445 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
446
Brian Paulfef6e362010-05-10 21:11:21 -0600447 obj->Active = GL_TRUE;
Brian Paulde8530e2010-03-30 19:50:11 -0600448 ctx->TransformFeedback.Mode = mode;
Brian Paulfef6e362010-05-10 21:11:21 -0600449
Paul Berry1ad51622012-12-15 13:06:10 -0800450 compute_transform_feedback_buffer_sizes(obj);
451
Paul Berryb87e65c2012-12-13 09:30:09 -0800452 if (_mesa_is_gles3(ctx)) {
453 /* In GLES3, we are required to track the usage of the transform
454 * feedback buffer and report INVALID_OPERATION if a draw call tries to
455 * exceed it. So compute the maximum number of vertices that we can
456 * write without overflowing any of the buffers currently being used for
457 * feedback.
458 */
459 unsigned max_vertices
Timothy Arceri258299d2016-03-03 13:20:01 +1100460 = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
Paul Berryb87e65c2012-12-13 09:30:09 -0800461 obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
462 }
463
Timothy Arceri3177eef2016-11-03 14:35:08 +1100464 if (obj->program != source) {
Paul Berrye1907092014-01-22 05:14:48 -0800465 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
Timothy Arceri3177eef2016-11-03 14:35:08 +1100466 obj->program = source;
Paul Berrye1907092014-01-22 05:14:48 -0800467 }
Kenneth Graunkec732f682013-09-06 14:47:19 -0700468
Brian Paulfef6e362010-05-10 21:11:21 -0600469 assert(ctx->Driver.BeginTransformFeedback);
470 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
Brian Paulde8530e2010-03-30 19:50:11 -0600471}
472
473
474void GLAPIENTRY
475_mesa_EndTransformFeedback(void)
476{
Brian Paulfef6e362010-05-10 21:11:21 -0600477 struct gl_transform_feedback_object *obj;
Brian Paulde8530e2010-03-30 19:50:11 -0600478 GET_CURRENT_CONTEXT(ctx);
479
Brian Paulfef6e362010-05-10 21:11:21 -0600480 obj = ctx->TransformFeedback.CurrentObject;
481
482 if (!obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600483 _mesa_error(ctx, GL_INVALID_OPERATION,
484 "glEndTransformFeedback(not active)");
485 return;
486 }
487
Marek Olšákb95cbe52013-04-15 00:50:20 +0200488 FLUSH_VERTICES(ctx, 0);
489 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
490
Kenneth Graunke47fbe172016-05-08 22:45:01 -0700491 assert(ctx->Driver.EndTransformFeedback);
492 ctx->Driver.EndTransformFeedback(ctx, obj);
493
Brian Paulfef6e362010-05-10 21:11:21 -0600494 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
Paul Berry08ce4872011-12-21 11:02:50 -0800495 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
Marek Olšák14bb9572011-12-09 17:00:23 +0100496 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
Brian Paulde8530e2010-03-30 19:50:11 -0600497}
498
499
500/**
501 * Helper used by BindBufferRange() and BindBufferBase().
502 */
503static void
Martin Peresa5d165a2015-01-20 16:30:32 +0200504bind_buffer_range(struct gl_context *ctx,
505 struct gl_transform_feedback_object *obj,
506 GLuint index,
Brian Paulde8530e2010-03-30 19:50:11 -0600507 struct gl_buffer_object *bufObj,
Martin Peresa5d165a2015-01-20 16:30:32 +0200508 GLintptr offset, GLsizeiptr size,
509 bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600510{
Marek Olšákb95cbe52013-04-15 00:50:20 +0200511 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
Paul Berry291ae4e2011-12-16 13:55:37 -0800512 * transform feedback buffers can't be changed while transform feedback is
513 * active.
514 */
Brian Paulfef6e362010-05-10 21:11:21 -0600515
Martin Peresa5d165a2015-01-20 16:30:32 +0200516 if (!dsa) {
517 /* The general binding point */
518 _mesa_reference_buffer_object(ctx,
519 &ctx->TransformFeedback.CurrentBuffer,
520 bufObj);
521 }
Brian Paulde8530e2010-03-30 19:50:11 -0600522
523 /* The per-attribute binding point */
Fredrik Höglund835abfa2013-11-15 19:53:29 +0100524 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
Brian Paulde8530e2010-03-30 19:50:11 -0600525}
526
527
528/**
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000529 * Validate the buffer object to receive transform feedback results. Plus,
530 * validate the starting offset to place the results, and max size.
Martin Peres296d8232015-01-21 12:22:11 +0200531 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
532 * functions.
Brian Paulde8530e2010-03-30 19:50:11 -0600533 */
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000534bool
535_mesa_validate_buffer_range_xfb(struct gl_context *ctx,
536 struct gl_transform_feedback_object *obj,
537 GLuint index, struct gl_buffer_object *bufObj,
538 GLintptr offset, GLsizeiptr size, bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600539{
Martin Peres296d8232015-01-21 12:22:11 +0200540 const char *gl_methd_name;
541 if (dsa)
542 gl_methd_name = "glTransformFeedbackBufferRange";
543 else
544 gl_methd_name = "glBindBufferRange";
545
546
Brian Paulfef6e362010-05-10 21:11:21 -0600547 if (obj->Active) {
Martin Peres296d8232015-01-21 12:22:11 +0200548 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
549 gl_methd_name);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000550 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600551 }
552
Marek Olšák15ac66e2011-12-18 02:13:17 +0100553 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Martin Peres296d8232015-01-21 12:22:11 +0200554 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
555 * generated if index is greater than or equal to the number of binding
556 * points for transform feedback, as described in section 6.7.1."
557 */
558 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
559 gl_methd_name, index);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000560 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600561 }
562
Eric Anholtb82c4722012-06-14 16:55:56 -0700563 if (size & 0x3) {
Martin Peres296d8232015-01-21 12:22:11 +0200564 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
565 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
566 "four)", gl_methd_name, (int) size);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000567 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600568 }
569
570 if (offset & 0x3) {
Martin Peres296d8232015-01-21 12:22:11 +0200571 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
572 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
573 "of four)", gl_methd_name, (int) offset);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000574 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200575 }
Brian Paulde8530e2010-03-30 19:50:11 -0600576
Martin Peres296d8232015-01-21 12:22:11 +0200577 if (offset < 0) {
578 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
579 * generated by BindBufferRange if offset is negative."
580 *
581 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
582 * is generated by TransformFeedbackBufferRange if offset is negative."
583 */
584 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
585 gl_methd_name,
586 (int) offset);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000587 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200588 }
589
590 if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) {
591 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
592 * generated by BindBufferRange if buffer is non-zero and size is less
593 * than or equal to zero."
594 *
595 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
596 * is generated by TransformFeedbackBufferRange if size is less than or
597 * equal to zero."
598 */
599 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
600 gl_methd_name, (int) size);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000601 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200602 }
603
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000604 return true;
Brian Paulde8530e2010-03-30 19:50:11 -0600605}
606
607
608/**
Paul Berrye1907092014-01-22 05:14:48 -0800609 * Specify a buffer object to receive transform feedback results.
Brian Paulde8530e2010-03-30 19:50:11 -0600610 * As above, but start at offset = 0.
Martin Peresa5d165a2015-01-20 16:30:32 +0200611 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
612 * functions.
Brian Paulde8530e2010-03-30 19:50:11 -0600613 */
Eric Anholtb82c4722012-06-14 16:55:56 -0700614void
615_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
Martin Peresa5d165a2015-01-20 16:30:32 +0200616 struct gl_transform_feedback_object *obj,
617 GLuint index,
618 struct gl_buffer_object *bufObj,
619 bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600620{
Brian Paulfef6e362010-05-10 21:11:21 -0600621 if (obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600622 _mesa_error(ctx, GL_INVALID_OPERATION,
Martin Peresa5d165a2015-01-20 16:30:32 +0200623 "%s(transform feedback active)",
624 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
Brian Paulde8530e2010-03-30 19:50:11 -0600625 return;
626 }
627
Marek Olšák15ac66e2011-12-18 02:13:17 +0100628 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Martin Peresa5d165a2015-01-20 16:30:32 +0200629 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
630 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
631 index);
Brian Paulde8530e2010-03-30 19:50:11 -0600632 return;
633 }
634
Martin Peresa5d165a2015-01-20 16:30:32 +0200635 bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
Brian Paulde8530e2010-03-30 19:50:11 -0600636}
637
Martin Peresa5d165a2015-01-20 16:30:32 +0200638/**
639 * Wrapper around lookup_transform_feedback_object that throws
640 * GL_INVALID_OPERATION if id is not in the hash table. After calling
641 * _mesa_error, it returns NULL.
642 */
643static struct gl_transform_feedback_object *
644lookup_transform_feedback_object_err(struct gl_context *ctx,
645 GLuint xfb, const char* func)
646{
647 struct gl_transform_feedback_object *obj;
648
649 obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
650 if (!obj) {
651 _mesa_error(ctx, GL_INVALID_OPERATION,
652 "%s(xfb=%u: non-generated object name)", func, xfb);
653 }
654
655 return obj;
656}
657
658/**
659 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
660 * is not in the hash table. Specialised version for the
661 * transform-feedback-related functions. After calling _mesa_error, it
662 * returns NULL.
663 */
664static struct gl_buffer_object *
665lookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
666 GLuint buffer, const char* func)
667{
668 struct gl_buffer_object *bufObj;
669
670 /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
671 * name of an existing buffer object.
672 */
673 if (buffer == 0) {
674 bufObj = ctx->Shared->NullBufferObj;
675 } else {
676 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
677 if (!bufObj) {
678 _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
679 buffer);
680 }
681 }
682
683 return bufObj;
684}
685
686void GLAPIENTRY
687_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
688{
689 GET_CURRENT_CONTEXT(ctx);
690 struct gl_transform_feedback_object *obj;
691 struct gl_buffer_object *bufObj;
692
693 obj = lookup_transform_feedback_object_err(ctx, xfb,
694 "glTransformFeedbackBufferBase");
695 if(!obj) {
696 return;
697 }
698
699 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
700 "glTransformFeedbackBufferBase");
701 if(!bufObj) {
702 return;
703 }
704
705 _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
706}
Brian Paulde8530e2010-03-30 19:50:11 -0600707
Martin Peres296d8232015-01-21 12:22:11 +0200708void GLAPIENTRY
709_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
710 GLintptr offset, GLsizeiptr size)
711{
712 GET_CURRENT_CONTEXT(ctx);
713 struct gl_transform_feedback_object *obj;
714 struct gl_buffer_object *bufObj;
715
716 obj = lookup_transform_feedback_object_err(ctx, xfb,
717 "glTransformFeedbackBufferRange");
718 if(!obj) {
719 return;
720 }
721
722 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
723 "glTransformFeedbackBufferRange");
724 if(!bufObj) {
725 return;
726 }
727
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000728 if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
729 size, true))
730 return;
731
732 /* The per-attribute binding point */
733 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
734 size);
Martin Peres296d8232015-01-21 12:22:11 +0200735}
736
Brian Paulde8530e2010-03-30 19:50:11 -0600737/**
Paul Berrye1907092014-01-22 05:14:48 -0800738 * Specify a buffer object to receive transform feedback results, plus the
Brian Paulde8530e2010-03-30 19:50:11 -0600739 * offset in the buffer to start placing results.
740 * This function is part of GL_EXT_transform_feedback, but not GL3.
741 */
742void GLAPIENTRY
743_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
744 GLintptr offset)
745{
Brian Paulfef6e362010-05-10 21:11:21 -0600746 struct gl_transform_feedback_object *obj;
Brian Paulde8530e2010-03-30 19:50:11 -0600747 struct gl_buffer_object *bufObj;
748 GET_CURRENT_CONTEXT(ctx);
Brian Paulde8530e2010-03-30 19:50:11 -0600749
750 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
751 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
752 return;
753 }
754
Brian Paulfef6e362010-05-10 21:11:21 -0600755 obj = ctx->TransformFeedback.CurrentObject;
756
757 if (obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600758 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul2be2e1d2011-04-29 12:03:28 -0600759 "glBindBufferOffsetEXT(transform feedback active)");
Brian Paulde8530e2010-03-30 19:50:11 -0600760 return;
761 }
762
Marek Olšák15ac66e2011-12-18 02:13:17 +0100763 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Brian Paulde8530e2010-03-30 19:50:11 -0600764 _mesa_error(ctx, GL_INVALID_VALUE,
765 "glBindBufferOffsetEXT(index=%d)", index);
766 return;
767 }
768
Paul Berry86bb45f2011-12-30 10:14:35 -0800769 if (offset & 0x3) {
770 /* must be multiple of four */
771 _mesa_error(ctx, GL_INVALID_VALUE,
772 "glBindBufferOffsetEXT(offset=%d)", (int) offset);
773 return;
774 }
775
Kenneth Graunke05b086c2012-06-04 00:48:23 -0700776 if (buffer == 0) {
777 bufObj = ctx->Shared->NullBufferObj;
778 } else {
779 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
780 }
781
Brian Paulde8530e2010-03-30 19:50:11 -0600782 if (!bufObj) {
783 _mesa_error(ctx, GL_INVALID_OPERATION,
784 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
785 return;
786 }
787
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000788 _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
Brian Paulde8530e2010-03-30 19:50:11 -0600789}
790
791
792/**
Paul Berrye1907092014-01-22 05:14:48 -0800793 * This function specifies the transform feedback outputs to be written
Brian Paulde8530e2010-03-30 19:50:11 -0600794 * to the feedback buffer(s), and in what order.
795 */
796void GLAPIENTRY
797_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
Paul Berrybb3db382012-11-06 07:26:56 -0800798 const GLchar * const *varyings,
799 GLenum bufferMode)
Brian Paulde8530e2010-03-30 19:50:11 -0600800{
801 struct gl_shader_program *shProg;
Brian Paul8e45e382012-10-27 08:58:19 -0600802 GLint i;
Brian Paulde8530e2010-03-30 19:50:11 -0600803 GET_CURRENT_CONTEXT(ctx);
804
Kenneth Graunkea7d616d2013-09-06 12:38:12 -0700805 /* From the ARB_transform_feedback2 specification:
806 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
807 * if the current transform feedback object is active, even if paused."
808 */
809 if (ctx->TransformFeedback.CurrentObject->Active) {
810 _mesa_error(ctx, GL_INVALID_OPERATION,
811 "glTransformFeedbackVaryings(current object is active)");
812 return;
813 }
814
Brian Paulde8530e2010-03-30 19:50:11 -0600815 switch (bufferMode) {
816 case GL_INTERLEAVED_ATTRIBS:
817 break;
818 case GL_SEPARATE_ATTRIBS:
819 break;
820 default:
821 _mesa_error(ctx, GL_INVALID_ENUM,
822 "glTransformFeedbackVaryings(bufferMode)");
823 return;
824 }
825
Marek Olšákf77aa272011-10-24 02:21:48 +0200826 if (count < 0 ||
827 (bufferMode == GL_SEPARATE_ATTRIBS &&
Marek Olšák15ac66e2011-12-18 02:13:17 +0100828 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
Brian Paulde8530e2010-03-30 19:50:11 -0600829 _mesa_error(ctx, GL_INVALID_VALUE,
830 "glTransformFeedbackVaryings(count=%d)", count);
831 return;
832 }
833
Tapani Pällifc8b3582016-10-10 09:49:36 +0300834 shProg = _mesa_lookup_shader_program_err(ctx, program,
835 "glTransformFeedbackVaryings");
836 if (!shProg)
Brian Paulde8530e2010-03-30 19:50:11 -0600837 return;
Brian Paulde8530e2010-03-30 19:50:11 -0600838
Marek Olšák375e73d2011-12-18 02:43:31 +0100839 if (ctx->Extensions.ARB_transform_feedback3) {
840 if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
841 unsigned buffers = 1;
842
843 for (i = 0; i < count; i++) {
844 if (strcmp(varyings[i], "gl_NextBuffer") == 0)
845 buffers++;
846 }
847
848 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
849 _mesa_error(ctx, GL_INVALID_OPERATION,
850 "glTransformFeedbackVaryings(too many gl_NextBuffer "
Andreas Bollc83e1612015-12-09 17:10:33 +0100851 "occurrences)");
Marek Olšák375e73d2011-12-18 02:43:31 +0100852 return;
853 }
854 } else {
855 for (i = 0; i < count; i++) {
856 if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
857 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
858 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
859 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
860 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
861 _mesa_error(ctx, GL_INVALID_OPERATION,
862 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
863 "varying=%s)",
864 varyings[i]);
865 return;
866 }
867 }
868 }
869 }
870
Brian Paulde8530e2010-03-30 19:50:11 -0600871 /* free existing varyings, if any */
Brian Paul27d70b72012-11-04 16:43:44 -0700872 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
Brian Paulde8530e2010-03-30 19:50:11 -0600873 free(shProg->TransformFeedback.VaryingNames[i]);
874 }
875 free(shProg->TransformFeedback.VaryingNames);
876
877 /* allocate new memory for varying names */
878 shProg->TransformFeedback.VaryingNames =
Matt Turner2b7a9722012-09-03 19:44:00 -0700879 malloc(count * sizeof(GLchar *));
Brian Paulde8530e2010-03-30 19:50:11 -0600880
881 if (!shProg->TransformFeedback.VaryingNames) {
882 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
883 return;
884 }
885
886 /* Save the new names and the count */
Brian Paul27d70b72012-11-04 16:43:44 -0700887 for (i = 0; i < count; i++) {
Brian Pauld7193ce2015-03-07 13:15:22 -0700888 shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
Brian Paulde8530e2010-03-30 19:50:11 -0600889 }
890 shProg->TransformFeedback.NumVarying = count;
891
892 shProg->TransformFeedback.BufferMode = bufferMode;
893
Marek Olšákb95cbe52013-04-15 00:50:20 +0200894 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
Paul Berrya98ceee2011-11-22 11:53:46 -0800895 * the varyings won't be used until shader link time.
896 */
Brian Paulde8530e2010-03-30 19:50:11 -0600897}
898
899
900/**
Paul Berrye1907092014-01-22 05:14:48 -0800901 * Get info about the transform feedback outputs which are to be written
Brian Paulde8530e2010-03-30 19:50:11 -0600902 * to the feedback buffer(s).
903 */
904void GLAPIENTRY
905_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
906 GLsizei bufSize, GLsizei *length,
907 GLsizei *size, GLenum *type, GLchar *name)
908{
909 const struct gl_shader_program *shProg;
Tapani Pällidc39d842015-03-12 12:08:56 +0200910 struct gl_program_resource *res;
Brian Paulde8530e2010-03-30 19:50:11 -0600911 GET_CURRENT_CONTEXT(ctx);
912
Tapani Pällifc8b3582016-10-10 09:49:36 +0300913 shProg = _mesa_lookup_shader_program_err(ctx, program,
914 "glGetTransformFeedbackVarying");
915 if (!shProg)
Brian Paulde8530e2010-03-30 19:50:11 -0600916 return;
Brian Paulde8530e2010-03-30 19:50:11 -0600917
Tapani Pällidc39d842015-03-12 12:08:56 +0200918 res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
919 GL_TRANSFORM_FEEDBACK_VARYING,
920 index);
921 if (!res) {
Brian Paulde8530e2010-03-30 19:50:11 -0600922 _mesa_error(ctx, GL_INVALID_VALUE,
Matt Turner8f3570e2012-11-22 00:06:03 -0800923 "glGetTransformFeedbackVarying(index=%u)", index);
Brian Paulde8530e2010-03-30 19:50:11 -0600924 return;
925 }
926
Eric Anholt9d36c962012-01-02 17:08:13 -0800927 /* return the varying's name and length */
Tapani Pällidc39d842015-03-12 12:08:56 +0200928 _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
Brian Paulde8530e2010-03-30 19:50:11 -0600929
Eric Anholt9d36c962012-01-02 17:08:13 -0800930 /* return the datatype and value's size (in datatype units) */
931 if (type)
Tapani Pällidc39d842015-03-12 12:08:56 +0200932 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
933 res, index, GL_TYPE, (GLint*) type,
934 "glGetTransformFeedbackVarying");
Eric Anholt9d36c962012-01-02 17:08:13 -0800935 if (size)
Tapani Pällidc39d842015-03-12 12:08:56 +0200936 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
937 res, index, GL_ARRAY_SIZE, (GLint*) size,
938 "glGetTransformFeedbackVarying");
Brian Paulde8530e2010-03-30 19:50:11 -0600939}
940
Brian Paulfef6e362010-05-10 21:11:21 -0600941
942
Marek Olšák14bb9572011-12-09 17:00:23 +0100943struct gl_transform_feedback_object *
944_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -0600945{
Martin Peresa5d165a2015-01-20 16:30:32 +0200946 /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
947 * the default transform feedback object, or the name of an existing
948 * transform feedback object."
949 */
Brian Paulfef6e362010-05-10 21:11:21 -0600950 if (name == 0) {
951 return ctx->TransformFeedback.DefaultObject;
952 }
953 else
954 return (struct gl_transform_feedback_object *)
Timothy Arceri918cec82017-04-07 11:40:40 +1000955 _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
Brian Paulfef6e362010-05-10 21:11:21 -0600956}
957
Martin Peresc86cb2d2015-01-14 18:17:21 +0200958static void
959create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
960 bool dsa)
Brian Paulfef6e362010-05-10 21:11:21 -0600961{
962 GLuint first;
Martin Peresc86cb2d2015-01-14 18:17:21 +0200963 const char* func;
964
965 if (dsa)
966 func = "glCreateTransformFeedbacks";
967 else
968 func = "glGenTransformFeedbacks";
Brian Paulfef6e362010-05-10 21:11:21 -0600969
Brian Paulfef6e362010-05-10 21:11:21 -0600970 if (n < 0) {
Martin Peresc86cb2d2015-01-14 18:17:21 +0200971 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
Brian Paulfef6e362010-05-10 21:11:21 -0600972 return;
973 }
974
Martin Peresc86cb2d2015-01-14 18:17:21 +0200975 if (!ids)
Brian Paulfef6e362010-05-10 21:11:21 -0600976 return;
977
978 /* we don't need contiguous IDs, but this might be faster */
979 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
980 if (first) {
981 GLsizei i;
982 for (i = 0; i < n; i++) {
983 struct gl_transform_feedback_object *obj
984 = ctx->Driver.NewTransformFeedback(ctx, first + i);
985 if (!obj) {
Martin Peresc86cb2d2015-01-14 18:17:21 +0200986 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
Brian Paulfef6e362010-05-10 21:11:21 -0600987 return;
988 }
Martin Peresc86cb2d2015-01-14 18:17:21 +0200989 ids[i] = first + i;
Timothy Arceri918cec82017-04-07 11:40:40 +1000990 _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i,
991 obj);
Martin Peresc86cb2d2015-01-14 18:17:21 +0200992 if (dsa) {
993 /* this is normally done at bind time in the non-dsa case */
994 obj->EverBound = GL_TRUE;
995 }
Brian Paulfef6e362010-05-10 21:11:21 -0600996 }
997 }
998 else {
Martin Peresc86cb2d2015-01-14 18:17:21 +0200999 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
Brian Paulfef6e362010-05-10 21:11:21 -06001000 }
1001}
1002
Martin Peresc86cb2d2015-01-14 18:17:21 +02001003/**
1004 * Create new transform feedback objects. Transform feedback objects
1005 * encapsulate the state related to transform feedback to allow quickly
1006 * switching state (and drawing the results, below).
1007 * Part of GL_ARB_transform_feedback2.
1008 */
1009void GLAPIENTRY
1010_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
1011{
1012 GET_CURRENT_CONTEXT(ctx);
1013
1014 /* GenTransformFeedbacks should just reserve the object names that a
1015 * subsequent call to BindTransformFeedback should actively create. For
1016 * the sake of simplicity, we reserve the names and create the objects
1017 * straight away.
1018 */
1019
1020 create_transform_feedbacks(ctx, n, names, false);
1021}
1022
1023/**
1024 * Create new transform feedback objects. Transform feedback objects
1025 * encapsulate the state related to transform feedback to allow quickly
1026 * switching state (and drawing the results, below).
1027 * Part of GL_ARB_direct_state_access.
1028 */
1029void GLAPIENTRY
1030_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
1031{
1032 GET_CURRENT_CONTEXT(ctx);
1033
1034 create_transform_feedbacks(ctx, n, names, true);
1035}
1036
Brian Paulfef6e362010-05-10 21:11:21 -06001037
1038/**
1039 * Is the given ID a transform feedback object?
1040 * Part of GL_ARB_transform_feedback2.
1041 */
1042GLboolean GLAPIENTRY
1043_mesa_IsTransformFeedback(GLuint name)
1044{
Matt Turnerfd93d552012-12-19 13:43:31 -08001045 struct gl_transform_feedback_object *obj;
Brian Paulfef6e362010-05-10 21:11:21 -06001046 GET_CURRENT_CONTEXT(ctx);
1047
1048 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1049
Matt Turnerfd93d552012-12-19 13:43:31 -08001050 if (name == 0)
Brian Paulfef6e362010-05-10 21:11:21 -06001051 return GL_FALSE;
Matt Turnerfd93d552012-12-19 13:43:31 -08001052
1053 obj = _mesa_lookup_transform_feedback_object(ctx, name);
1054 if (obj == NULL)
1055 return GL_FALSE;
1056
1057 return obj->EverBound;
Brian Paulfef6e362010-05-10 21:11:21 -06001058}
1059
1060
1061/**
1062 * Bind the given transform feedback object.
1063 * Part of GL_ARB_transform_feedback2.
1064 */
1065void GLAPIENTRY
Vinson Leed74f5252010-05-10 22:58:23 -07001066_mesa_BindTransformFeedback(GLenum target, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -06001067{
1068 struct gl_transform_feedback_object *obj;
1069 GET_CURRENT_CONTEXT(ctx);
1070
1071 if (target != GL_TRANSFORM_FEEDBACK) {
1072 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
1073 return;
1074 }
1075
Paul Berry5b7099c2012-12-15 14:21:32 -08001076 if (_mesa_is_xfb_active_and_unpaused(ctx)) {
Brian Paulfef6e362010-05-10 21:11:21 -06001077 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul39c13a12010-05-14 08:03:42 -06001078 "glBindTransformFeedback(transform is active, or not paused)");
Brian Paulfef6e362010-05-10 21:11:21 -06001079 return;
1080 }
1081
Marek Olšák14bb9572011-12-09 17:00:23 +01001082 obj = _mesa_lookup_transform_feedback_object(ctx, name);
Brian Paulfef6e362010-05-10 21:11:21 -06001083 if (!obj) {
1084 _mesa_error(ctx, GL_INVALID_OPERATION,
1085 "glBindTransformFeedback(name=%u)", name);
1086 return;
1087 }
1088
1089 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1090 obj);
1091}
1092
1093
1094/**
1095 * Delete the given transform feedback objects.
1096 * Part of GL_ARB_transform_feedback2.
1097 */
1098void GLAPIENTRY
1099_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
1100{
1101 GLint i;
1102 GET_CURRENT_CONTEXT(ctx);
1103
Brian Paulfef6e362010-05-10 21:11:21 -06001104 if (n < 0) {
1105 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
1106 return;
1107 }
1108
1109 if (!names)
1110 return;
1111
1112 for (i = 0; i < n; i++) {
1113 if (names[i] > 0) {
1114 struct gl_transform_feedback_object *obj
Marek Olšák14bb9572011-12-09 17:00:23 +01001115 = _mesa_lookup_transform_feedback_object(ctx, names[i]);
Brian Paulfef6e362010-05-10 21:11:21 -06001116 if (obj) {
1117 if (obj->Active) {
1118 _mesa_error(ctx, GL_INVALID_OPERATION,
1119 "glDeleteTransformFeedbacks(object %u is active)",
1120 names[i]);
1121 return;
1122 }
Timothy Arceri918cec82017-04-07 11:40:40 +10001123 _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
Brian Paulfef6e362010-05-10 21:11:21 -06001124 /* unref, but object may not be deleted until later */
Ilia Mirkin0941ef32016-03-06 12:36:19 -05001125 if (obj == ctx->TransformFeedback.CurrentObject) {
1126 reference_transform_feedback_object(
1127 &ctx->TransformFeedback.CurrentObject,
1128 ctx->TransformFeedback.DefaultObject);
1129 }
Brian Paulfef6e362010-05-10 21:11:21 -06001130 reference_transform_feedback_object(&obj, NULL);
1131 }
1132 }
1133 }
1134}
1135
1136
1137/**
1138 * Pause transform feedback.
1139 * Part of GL_ARB_transform_feedback2.
1140 */
1141void GLAPIENTRY
1142_mesa_PauseTransformFeedback(void)
1143{
1144 struct gl_transform_feedback_object *obj;
1145 GET_CURRENT_CONTEXT(ctx);
1146
1147 obj = ctx->TransformFeedback.CurrentObject;
1148
Paul Berry5b7099c2012-12-15 14:21:32 -08001149 if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
Brian Paulfef6e362010-05-10 21:11:21 -06001150 _mesa_error(ctx, GL_INVALID_OPERATION,
1151 "glPauseTransformFeedback(feedback not active or already paused)");
1152 return;
1153 }
1154
Marek Olšákb95cbe52013-04-15 00:50:20 +02001155 FLUSH_VERTICES(ctx, 0);
1156 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1157
Brian Paulfef6e362010-05-10 21:11:21 -06001158 assert(ctx->Driver.PauseTransformFeedback);
1159 ctx->Driver.PauseTransformFeedback(ctx, obj);
Kenneth Graunke47fbe172016-05-08 22:45:01 -07001160
1161 obj->Paused = GL_TRUE;
Brian Paulfef6e362010-05-10 21:11:21 -06001162}
1163
1164
1165/**
1166 * Resume transform feedback.
1167 * Part of GL_ARB_transform_feedback2.
1168 */
1169void GLAPIENTRY
1170_mesa_ResumeTransformFeedback(void)
1171{
1172 struct gl_transform_feedback_object *obj;
1173 GET_CURRENT_CONTEXT(ctx);
1174
1175 obj = ctx->TransformFeedback.CurrentObject;
1176
1177 if (!obj->Active || !obj->Paused) {
1178 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul076bd112011-04-29 18:42:09 -06001179 "glResumeTransformFeedback(feedback not active or not paused)");
Brian Paulfef6e362010-05-10 21:11:21 -06001180 return;
1181 }
1182
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001183 /* From the ARB_transform_feedback2 specification:
1184 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1185 * the program object being used by the current transform feedback object
1186 * is not active."
1187 */
Timothy Arceri3177eef2016-11-03 14:35:08 +11001188 if (obj->program != get_xfb_source(ctx)) {
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001189 _mesa_error(ctx, GL_INVALID_OPERATION,
Paul Berrye1907092014-01-22 05:14:48 -08001190 "glResumeTransformFeedback(wrong program bound)");
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001191 return;
1192 }
1193
Marek Olšákb95cbe52013-04-15 00:50:20 +02001194 FLUSH_VERTICES(ctx, 0);
1195 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1196
Brian Paulfef6e362010-05-10 21:11:21 -06001197 obj->Paused = GL_FALSE;
1198
1199 assert(ctx->Driver.ResumeTransformFeedback);
1200 ctx->Driver.ResumeTransformFeedback(ctx, obj);
1201}
Martin Perese59d2432015-02-16 16:13:58 +02001202
1203extern void GLAPIENTRY
1204_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
1205{
1206 struct gl_transform_feedback_object *obj;
1207 GET_CURRENT_CONTEXT(ctx);
1208
1209 obj = lookup_transform_feedback_object_err(ctx, xfb,
1210 "glGetTransformFeedbackiv");
1211 if(!obj) {
1212 return;
1213 }
1214
1215 switch(pname) {
1216 case GL_TRANSFORM_FEEDBACK_PAUSED:
1217 *param = obj->Paused;
1218 break;
1219 case GL_TRANSFORM_FEEDBACK_ACTIVE:
1220 *param = obj->Active;
1221 break;
1222 default:
1223 _mesa_error(ctx, GL_INVALID_ENUM,
1224 "glGetTransformFeedbackiv(pname=%i)", pname);
1225 }
1226}
Martin Peres8799ecd2015-02-16 16:13:59 +02001227
1228extern void GLAPIENTRY
1229_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
1230 GLint *param)
1231{
1232 struct gl_transform_feedback_object *obj;
1233 GET_CURRENT_CONTEXT(ctx);
1234
1235 obj = lookup_transform_feedback_object_err(ctx, xfb,
1236 "glGetTransformFeedbacki_v");
1237 if(!obj) {
1238 return;
1239 }
1240
1241 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1242 _mesa_error(ctx, GL_INVALID_VALUE,
1243 "glGetTransformFeedbacki_v(index=%i)", index);
1244 return;
1245 }
1246
1247 switch(pname) {
1248 case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
1249 *param = obj->BufferNames[index];
1250 break;
1251 default:
1252 _mesa_error(ctx, GL_INVALID_ENUM,
1253 "glGetTransformFeedbacki_v(pname=%i)", pname);
1254 }
1255}
Martin Peres6ead10d2015-01-22 16:55:29 +02001256
1257extern void GLAPIENTRY
1258_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
1259 GLint64 *param)
1260{
1261 struct gl_transform_feedback_object *obj;
1262 GET_CURRENT_CONTEXT(ctx);
1263
1264 obj = lookup_transform_feedback_object_err(ctx, xfb,
1265 "glGetTransformFeedbacki64_v");
1266 if(!obj) {
1267 return;
1268 }
1269
1270 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1271 _mesa_error(ctx, GL_INVALID_VALUE,
1272 "glGetTransformFeedbacki64_v(index=%i)", index);
1273 return;
1274 }
1275
Dave Airliebac39dd2016-05-30 06:56:52 +10001276 compute_transform_feedback_buffer_sizes(obj);
Martin Peres6ead10d2015-01-22 16:55:29 +02001277 switch(pname) {
1278 case GL_TRANSFORM_FEEDBACK_BUFFER_START:
1279 *param = obj->Offset[index];
1280 break;
1281 case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
Dave Airliebac39dd2016-05-30 06:56:52 +10001282 *param = obj->Size[index];
Martin Peres6ead10d2015-01-22 16:55:29 +02001283 break;
1284 default:
1285 _mesa_error(ctx, GL_INVALID_ENUM,
1286 "glGetTransformFeedbacki64_v(pname=%i)", pname);
1287 }
1288}