blob: b79b41d35ef7f71b2d7d0fd3d877015748745fe2 [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{
Kenneth Graunkef02ee302013-10-25 15:54:10 -0700196 obj->Name = name;
197 obj->RefCount = 1;
198 obj->EverBound = GL_FALSE;
199}
200
201
Brian Paulfef6e362010-05-10 21:11:21 -0600202/** Default fallback for ctx->Driver.NewTransformFeedback() */
203static struct gl_transform_feedback_object *
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200204new_transform_feedback_fallback(struct gl_context *ctx, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -0600205{
206 struct gl_transform_feedback_object *obj;
Samuel Pitoiset41c7c2d2017-08-24 13:43:19 +0200207
Brian Paulfef6e362010-05-10 21:11:21 -0600208 obj = CALLOC_STRUCT(gl_transform_feedback_object);
Samuel Pitoiset41c7c2d2017-08-24 13:43:19 +0200209 if (!obj)
210 return NULL;
211
Kenneth Graunkef02ee302013-10-25 15:54:10 -0700212 _mesa_init_transform_feedback_object(obj, name);
Brian Paulfef6e362010-05-10 21:11:21 -0600213 return obj;
214}
215
216/** Default fallback for ctx->Driver.DeleteTransformFeedback() */
217static void
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200218delete_transform_feedback_fallback(struct gl_context *ctx,
219 struct gl_transform_feedback_object *obj)
Brian Paulfef6e362010-05-10 21:11:21 -0600220{
221 GLuint i;
222
Brian Paulc2e130f2015-02-28 08:57:11 -0700223 for (i = 0; i < ARRAY_SIZE(obj->Buffers); i++) {
Brian Paulfef6e362010-05-10 21:11:21 -0600224 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL);
225 }
226
Timothy Arceri6d8dd592013-08-26 17:16:08 +1000227 free(obj->Label);
Brian Paulfef6e362010-05-10 21:11:21 -0600228 free(obj);
229}
230
Chia-I Wu32a9b272010-07-06 16:27:20 +0800231
Brian Paulfef6e362010-05-10 21:11:21 -0600232/** Default fallback for ctx->Driver.BeginTransformFeedback() */
233static void
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200234begin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode,
235 struct gl_transform_feedback_object *obj)
Brian Paulfef6e362010-05-10 21:11:21 -0600236{
237 /* nop */
238}
239
240/** Default fallback for ctx->Driver.EndTransformFeedback() */
241static void
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200242end_transform_feedback_fallback(struct gl_context *ctx,
243 struct gl_transform_feedback_object *obj)
Brian Paulfef6e362010-05-10 21:11:21 -0600244{
245 /* nop */
246}
247
248/** Default fallback for ctx->Driver.PauseTransformFeedback() */
249static void
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200250pause_transform_feedback_fallback(struct gl_context *ctx,
251 struct gl_transform_feedback_object *obj)
Brian Paulfef6e362010-05-10 21:11:21 -0600252{
253 /* nop */
254}
255
256/** Default fallback for ctx->Driver.ResumeTransformFeedback() */
257static void
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200258resume_transform_feedback_fallback(struct gl_context *ctx,
259 struct gl_transform_feedback_object *obj)
Brian Paulfef6e362010-05-10 21:11:21 -0600260{
261 /* nop */
262}
263
Brian Paulfef6e362010-05-10 21:11:21 -0600264
265/**
266 * Plug in default device driver functions for transform feedback.
267 * Most drivers will override some/all of these.
268 */
269void
270_mesa_init_transform_feedback_functions(struct dd_function_table *driver)
271{
Samuel Pitoisetc7b201a2017-08-24 13:43:21 +0200272 driver->NewTransformFeedback = new_transform_feedback_fallback;
273 driver->DeleteTransformFeedback = delete_transform_feedback_fallback;
274 driver->BeginTransformFeedback = begin_transform_feedback_fallback;
275 driver->EndTransformFeedback = end_transform_feedback_fallback;
276 driver->PauseTransformFeedback = pause_transform_feedback_fallback;
277 driver->ResumeTransformFeedback = resume_transform_feedback_fallback;
Brian Paulfef6e362010-05-10 21:11:21 -0600278}
279
280
Brian Paulfef6e362010-05-10 21:11:21 -0600281/**
Paul Berry1ad51622012-12-15 13:06:10 -0800282 * Fill in the correct Size value for each buffer in \c obj.
283 *
284 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
285 * Targets"):
286 *
287 * BindBufferBase binds the entire buffer, even when the size of the buffer
288 * is changed after the binding is established. It is equivalent to calling
289 * BindBufferRange with offset zero, while size is determined by the size of
290 * the bound buffer at the time the binding is used.
291 *
292 * Regardless of the size specified with BindBufferRange, or indirectly with
293 * BindBufferBase, the GL will never read or write beyond the end of a bound
294 * buffer. In some cases this constraint may result in visibly different
295 * behavior when a buffer overflow would otherwise result, such as described
296 * for transform feedback operations in section 13.2.2.
297 */
298static void
299compute_transform_feedback_buffer_sizes(
300 struct gl_transform_feedback_object *obj)
301{
302 unsigned i = 0;
303 for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) {
304 GLintptr offset = obj->Offset[i];
305 GLsizeiptr buffer_size
306 = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size;
307 GLsizeiptr available_space
308 = buffer_size <= offset ? 0 : buffer_size - offset;
309 GLsizeiptr computed_size;
310 if (obj->RequestedSize[i] == 0) {
311 /* No size was specified at the time the buffer was bound, so allow
312 * writing to all available space in the buffer.
313 */
314 computed_size = available_space;
315 } else {
316 /* A size was specified at the time the buffer was bound, however
317 * it's possible that the buffer has shrunk since then. So only
318 * allow writing to the minimum of the specified size and the space
319 * available.
320 */
321 computed_size = MIN2(available_space, obj->RequestedSize[i]);
322 }
323
324 /* Legal sizes must be multiples of four, so round down if necessary. */
325 obj->Size[i] = computed_size & ~0x3;
326 }
327}
328
329
330/**
Paul Berry3870f292012-12-12 14:14:12 -0800331 * Compute the maximum number of vertices that can be written to the currently
332 * enabled transform feedback buffers without overflowing any of them.
333 */
334unsigned
Timothy Arceri258299d2016-03-03 13:20:01 +1100335_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx,
Paul Berry3870f292012-12-12 14:14:12 -0800336 const struct gl_transform_feedback_object *obj,
337 const struct gl_transform_feedback_info *info)
338{
339 unsigned max_index = 0xffffffff;
340 unsigned i;
341
Timothy Arceri258299d2016-03-03 13:20:01 +1100342 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
343 if ((info->ActiveBuffers >> i) & 1) {
Timothy Arcericf039a32016-03-10 15:00:00 +1100344 unsigned stride = info->Buffers[i].Stride;
Timothy Arceri258299d2016-03-03 13:20:01 +1100345 unsigned max_for_this_buffer;
Paul Berry3870f292012-12-12 14:14:12 -0800346
Timothy Arceri258299d2016-03-03 13:20:01 +1100347 /* Skip any inactive buffers, which have a stride of 0. */
348 if (stride == 0)
349 continue;
Paul Berry3870f292012-12-12 14:14:12 -0800350
Timothy Arceri258299d2016-03-03 13:20:01 +1100351 max_for_this_buffer = obj->Size[i] / (4 * stride);
352 max_index = MIN2(max_index, max_for_this_buffer);
353 }
Paul Berry3870f292012-12-12 14:14:12 -0800354 }
355
356 return max_index;
357}
358
359
360/**
Brian Paulfef6e362010-05-10 21:11:21 -0600361 ** Begin API functions
362 **/
363
364
Paul Berrye1907092014-01-22 05:14:48 -0800365/**
366 * Figure out which stage of the pipeline is the source of transform feedback
Timothy Arceri3177eef2016-11-03 14:35:08 +1100367 * data given the current context state, and return its gl_program.
Paul Berrye1907092014-01-22 05:14:48 -0800368 *
369 * If no active program can generate transform feedback data (i.e. no vertex
370 * shader is active), returns NULL.
371 */
Timothy Arceri3177eef2016-11-03 14:35:08 +1100372static struct gl_program *
Paul Berrye1907092014-01-22 05:14:48 -0800373get_xfb_source(struct gl_context *ctx)
374{
375 int i;
376 for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
Gregory Hainautc0347702013-05-03 19:44:10 +0200377 if (ctx->_Shader->CurrentProgram[i] != NULL)
Timothy Arceric505d6d2016-10-31 22:39:17 +1100378 return ctx->_Shader->CurrentProgram[i];
Paul Berrye1907092014-01-22 05:14:48 -0800379 }
380 return NULL;
381}
382
383
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200384static ALWAYS_INLINE void
385begin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
Brian Paulde8530e2010-03-30 19:50:11 -0600386{
Brian Paulfef6e362010-05-10 21:11:21 -0600387 struct gl_transform_feedback_object *obj;
Paul Berrye1907092014-01-22 05:14:48 -0800388 struct gl_transform_feedback_info *info = NULL;
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200389 struct gl_program *source;
Brian Paul8e45e382012-10-27 08:58:19 -0600390 GLuint i;
Paul Berryb87e65c2012-12-13 09:30:09 -0800391 unsigned vertices_per_prim;
Brian Paulde8530e2010-03-30 19:50:11 -0600392
Brian Paulfef6e362010-05-10 21:11:21 -0600393 obj = ctx->TransformFeedback.CurrentObject;
Paul Berry86bb45f2011-12-30 10:14:35 -0800394
Paul Berrye1907092014-01-22 05:14:48 -0800395 /* Figure out what pipeline stage is the source of data for transform
396 * feedback.
397 */
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200398 source = get_xfb_source(ctx);
399 if (!no_error && source == NULL) {
Paul Berry86bb45f2011-12-30 10:14:35 -0800400 _mesa_error(ctx, GL_INVALID_OPERATION,
401 "glBeginTransformFeedback(no program active)");
402 return;
403 }
404
Timothy Arceri3177eef2016-11-03 14:35:08 +1100405 info = source->sh.LinkedTransformFeedback;
Brian Paulfef6e362010-05-10 21:11:21 -0600406
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200407 if (!no_error && info->NumOutputs == 0) {
Paul Berry86bb45f2011-12-30 10:14:35 -0800408 _mesa_error(ctx, GL_INVALID_OPERATION,
409 "glBeginTransformFeedback(no varyings to record)");
410 return;
411 }
412
Brian Paulde8530e2010-03-30 19:50:11 -0600413 switch (mode) {
414 case GL_POINTS:
Paul Berryb87e65c2012-12-13 09:30:09 -0800415 vertices_per_prim = 1;
416 break;
Brian Paulde8530e2010-03-30 19:50:11 -0600417 case GL_LINES:
Paul Berryb87e65c2012-12-13 09:30:09 -0800418 vertices_per_prim = 2;
419 break;
Brian Paulde8530e2010-03-30 19:50:11 -0600420 case GL_TRIANGLES:
Paul Berryb87e65c2012-12-13 09:30:09 -0800421 vertices_per_prim = 3;
Brian Paulde8530e2010-03-30 19:50:11 -0600422 break;
423 default:
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200424 if (!no_error) {
425 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
426 return;
427 } else {
428 /* Stop compiler warnings */
429 unreachable("Error in API use when using KHR_no_error");
430 }
Brian Paulde8530e2010-03-30 19:50:11 -0600431 }
432
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200433 if (!no_error) {
434 if (obj->Active) {
435 _mesa_error(ctx, GL_INVALID_OPERATION,
436 "glBeginTransformFeedback(already active)");
437 return;
438 }
Brian Paulde8530e2010-03-30 19:50:11 -0600439
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200440 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
441 if ((info->ActiveBuffers >> i) & 1) {
442 if (obj->BufferNames[i] == 0) {
443 _mesa_error(ctx, GL_INVALID_OPERATION,
444 "glBeginTransformFeedback(binding point %d does not "
445 "have a buffer object bound)", i);
446 return;
447 }
Timothy Arceri258299d2016-03-03 13:20:01 +1100448 }
Paul Berryebfad9f2011-12-29 15:55:01 -0800449 }
450 }
451
Marek Olšákb95cbe52013-04-15 00:50:20 +0200452 FLUSH_VERTICES(ctx, 0);
453 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
454
Brian Paulfef6e362010-05-10 21:11:21 -0600455 obj->Active = GL_TRUE;
Brian Paulde8530e2010-03-30 19:50:11 -0600456 ctx->TransformFeedback.Mode = mode;
Brian Paulfef6e362010-05-10 21:11:21 -0600457
Paul Berry1ad51622012-12-15 13:06:10 -0800458 compute_transform_feedback_buffer_sizes(obj);
459
Paul Berryb87e65c2012-12-13 09:30:09 -0800460 if (_mesa_is_gles3(ctx)) {
461 /* In GLES3, we are required to track the usage of the transform
462 * feedback buffer and report INVALID_OPERATION if a draw call tries to
463 * exceed it. So compute the maximum number of vertices that we can
464 * write without overflowing any of the buffers currently being used for
465 * feedback.
466 */
467 unsigned max_vertices
Timothy Arceri258299d2016-03-03 13:20:01 +1100468 = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info);
Paul Berryb87e65c2012-12-13 09:30:09 -0800469 obj->GlesRemainingPrims = max_vertices / vertices_per_prim;
470 }
471
Timothy Arceri3177eef2016-11-03 14:35:08 +1100472 if (obj->program != source) {
Paul Berrye1907092014-01-22 05:14:48 -0800473 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg;
Timothy Arceri3177eef2016-11-03 14:35:08 +1100474 obj->program = source;
Paul Berrye1907092014-01-22 05:14:48 -0800475 }
Kenneth Graunkec732f682013-09-06 14:47:19 -0700476
Brian Paulfef6e362010-05-10 21:11:21 -0600477 assert(ctx->Driver.BeginTransformFeedback);
478 ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
Brian Paulde8530e2010-03-30 19:50:11 -0600479}
480
481
482void GLAPIENTRY
Samuel Pitoiset3906e8a2017-08-24 11:57:55 +0200483_mesa_BeginTransformFeedback_no_error(GLenum mode)
484{
485 GET_CURRENT_CONTEXT(ctx);
486 begin_transform_feedback(ctx, mode, true);
487}
488
489
490void GLAPIENTRY
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200491_mesa_BeginTransformFeedback(GLenum mode)
492{
493 GET_CURRENT_CONTEXT(ctx);
494 begin_transform_feedback(ctx, mode, false);
495}
496
497
Samuel Pitoiset65458762017-08-24 12:01:27 +0200498static void
499end_transform_feedback(struct gl_context *ctx,
500 struct gl_transform_feedback_object *obj)
501{
502 FLUSH_VERTICES(ctx, 0);
503 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
504
505 assert(ctx->Driver.EndTransformFeedback);
506 ctx->Driver.EndTransformFeedback(ctx, obj);
507
508 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
509 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
510 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
511}
512
513
Samuel Pitoiset088d5cb2017-08-24 11:55:44 +0200514void GLAPIENTRY
Samuel Pitoiset08cecec2017-08-24 12:02:40 +0200515_mesa_EndTransformFeedback_no_error(void)
516{
517 GET_CURRENT_CONTEXT(ctx);
518 end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
519}
520
521
522void GLAPIENTRY
Brian Paulde8530e2010-03-30 19:50:11 -0600523_mesa_EndTransformFeedback(void)
524{
Brian Paulfef6e362010-05-10 21:11:21 -0600525 struct gl_transform_feedback_object *obj;
Brian Paulde8530e2010-03-30 19:50:11 -0600526 GET_CURRENT_CONTEXT(ctx);
527
Brian Paulfef6e362010-05-10 21:11:21 -0600528 obj = ctx->TransformFeedback.CurrentObject;
529
530 if (!obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600531 _mesa_error(ctx, GL_INVALID_OPERATION,
532 "glEndTransformFeedback(not active)");
533 return;
534 }
535
Samuel Pitoiset65458762017-08-24 12:01:27 +0200536 end_transform_feedback(ctx, obj);
Brian Paulde8530e2010-03-30 19:50:11 -0600537}
538
539
540/**
541 * Helper used by BindBufferRange() and BindBufferBase().
542 */
543static void
Martin Peresa5d165a2015-01-20 16:30:32 +0200544bind_buffer_range(struct gl_context *ctx,
545 struct gl_transform_feedback_object *obj,
546 GLuint index,
Brian Paulde8530e2010-03-30 19:50:11 -0600547 struct gl_buffer_object *bufObj,
Martin Peresa5d165a2015-01-20 16:30:32 +0200548 GLintptr offset, GLsizeiptr size,
549 bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600550{
Marek Olšákb95cbe52013-04-15 00:50:20 +0200551 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because
Paul Berry291ae4e2011-12-16 13:55:37 -0800552 * transform feedback buffers can't be changed while transform feedback is
553 * active.
554 */
Brian Paulfef6e362010-05-10 21:11:21 -0600555
Martin Peresa5d165a2015-01-20 16:30:32 +0200556 if (!dsa) {
557 /* The general binding point */
558 _mesa_reference_buffer_object(ctx,
559 &ctx->TransformFeedback.CurrentBuffer,
560 bufObj);
561 }
Brian Paulde8530e2010-03-30 19:50:11 -0600562
563 /* The per-attribute binding point */
Fredrik Höglund835abfa2013-11-15 19:53:29 +0100564 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size);
Brian Paulde8530e2010-03-30 19:50:11 -0600565}
566
567
568/**
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000569 * Validate the buffer object to receive transform feedback results. Plus,
570 * validate the starting offset to place the results, and max size.
Martin Peres296d8232015-01-21 12:22:11 +0200571 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange
572 * functions.
Brian Paulde8530e2010-03-30 19:50:11 -0600573 */
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000574bool
575_mesa_validate_buffer_range_xfb(struct gl_context *ctx,
576 struct gl_transform_feedback_object *obj,
577 GLuint index, struct gl_buffer_object *bufObj,
578 GLintptr offset, GLsizeiptr size, bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600579{
Martin Peres296d8232015-01-21 12:22:11 +0200580 const char *gl_methd_name;
581 if (dsa)
582 gl_methd_name = "glTransformFeedbackBufferRange";
583 else
584 gl_methd_name = "glBindBufferRange";
585
586
Brian Paulfef6e362010-05-10 21:11:21 -0600587 if (obj->Active) {
Martin Peres296d8232015-01-21 12:22:11 +0200588 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)",
589 gl_methd_name);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000590 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600591 }
592
Marek Olšák15ac66e2011-12-18 02:13:17 +0100593 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Martin Peres296d8232015-01-21 12:22:11 +0200594 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
595 * generated if index is greater than or equal to the number of binding
596 * points for transform feedback, as described in section 6.7.1."
597 */
598 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
599 gl_methd_name, index);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000600 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600601 }
602
Eric Anholtb82c4722012-06-14 16:55:56 -0700603 if (size & 0x3) {
Martin Peres296d8232015-01-21 12:22:11 +0200604 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
605 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of "
606 "four)", gl_methd_name, (int) size);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000607 return false;
Brian Paulde8530e2010-03-30 19:50:11 -0600608 }
609
610 if (offset & 0x3) {
Martin Peres296d8232015-01-21 12:22:11 +0200611 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */
612 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple "
613 "of four)", gl_methd_name, (int) offset);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000614 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200615 }
Brian Paulde8530e2010-03-30 19:50:11 -0600616
Martin Peres296d8232015-01-21 12:22:11 +0200617 if (offset < 0) {
618 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
619 * generated by BindBufferRange if offset is negative."
620 *
621 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
622 * is generated by TransformFeedbackBufferRange if offset is negative."
623 */
624 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)",
625 gl_methd_name,
626 (int) offset);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000627 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200628 }
629
630 if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) {
631 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is
632 * generated by BindBufferRange if buffer is non-zero and size is less
633 * than or equal to zero."
634 *
635 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error
636 * is generated by TransformFeedbackBufferRange if size is less than or
637 * equal to zero."
638 */
639 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)",
640 gl_methd_name, (int) size);
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000641 return false;
Martin Peres296d8232015-01-21 12:22:11 +0200642 }
643
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000644 return true;
Brian Paulde8530e2010-03-30 19:50:11 -0600645}
646
647
648/**
Paul Berrye1907092014-01-22 05:14:48 -0800649 * Specify a buffer object to receive transform feedback results.
Brian Paulde8530e2010-03-30 19:50:11 -0600650 * As above, but start at offset = 0.
Martin Peresa5d165a2015-01-20 16:30:32 +0200651 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase()
652 * functions.
Brian Paulde8530e2010-03-30 19:50:11 -0600653 */
Eric Anholtb82c4722012-06-14 16:55:56 -0700654void
655_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx,
Martin Peresa5d165a2015-01-20 16:30:32 +0200656 struct gl_transform_feedback_object *obj,
657 GLuint index,
658 struct gl_buffer_object *bufObj,
659 bool dsa)
Brian Paulde8530e2010-03-30 19:50:11 -0600660{
Brian Paulfef6e362010-05-10 21:11:21 -0600661 if (obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600662 _mesa_error(ctx, GL_INVALID_OPERATION,
Martin Peresa5d165a2015-01-20 16:30:32 +0200663 "%s(transform feedback active)",
664 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase");
Brian Paulde8530e2010-03-30 19:50:11 -0600665 return;
666 }
667
Marek Olšák15ac66e2011-12-18 02:13:17 +0100668 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Martin Peresa5d165a2015-01-20 16:30:32 +0200669 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)",
670 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase",
671 index);
Brian Paulde8530e2010-03-30 19:50:11 -0600672 return;
673 }
674
Martin Peresa5d165a2015-01-20 16:30:32 +0200675 bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa);
Brian Paulde8530e2010-03-30 19:50:11 -0600676}
677
Martin Peresa5d165a2015-01-20 16:30:32 +0200678/**
679 * Wrapper around lookup_transform_feedback_object that throws
680 * GL_INVALID_OPERATION if id is not in the hash table. After calling
681 * _mesa_error, it returns NULL.
682 */
683static struct gl_transform_feedback_object *
684lookup_transform_feedback_object_err(struct gl_context *ctx,
685 GLuint xfb, const char* func)
686{
687 struct gl_transform_feedback_object *obj;
688
689 obj = _mesa_lookup_transform_feedback_object(ctx, xfb);
690 if (!obj) {
691 _mesa_error(ctx, GL_INVALID_OPERATION,
692 "%s(xfb=%u: non-generated object name)", func, xfb);
693 }
694
695 return obj;
696}
697
698/**
699 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id
700 * is not in the hash table. Specialised version for the
701 * transform-feedback-related functions. After calling _mesa_error, it
702 * returns NULL.
703 */
704static struct gl_buffer_object *
705lookup_transform_feedback_bufferobj_err(struct gl_context *ctx,
706 GLuint buffer, const char* func)
707{
708 struct gl_buffer_object *bufObj;
709
710 /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the
711 * name of an existing buffer object.
712 */
713 if (buffer == 0) {
714 bufObj = ctx->Shared->NullBufferObj;
715 } else {
716 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
717 if (!bufObj) {
718 _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func,
719 buffer);
720 }
721 }
722
723 return bufObj;
724}
725
726void GLAPIENTRY
727_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer)
728{
729 GET_CURRENT_CONTEXT(ctx);
730 struct gl_transform_feedback_object *obj;
731 struct gl_buffer_object *bufObj;
732
733 obj = lookup_transform_feedback_object_err(ctx, xfb,
734 "glTransformFeedbackBufferBase");
735 if(!obj) {
736 return;
737 }
738
739 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
740 "glTransformFeedbackBufferBase");
741 if(!bufObj) {
742 return;
743 }
744
745 _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true);
746}
Brian Paulde8530e2010-03-30 19:50:11 -0600747
Martin Peres296d8232015-01-21 12:22:11 +0200748void GLAPIENTRY
749_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer,
750 GLintptr offset, GLsizeiptr size)
751{
752 GET_CURRENT_CONTEXT(ctx);
753 struct gl_transform_feedback_object *obj;
754 struct gl_buffer_object *bufObj;
755
756 obj = lookup_transform_feedback_object_err(ctx, xfb,
757 "glTransformFeedbackBufferRange");
758 if(!obj) {
759 return;
760 }
761
762 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer,
763 "glTransformFeedbackBufferRange");
764 if(!bufObj) {
765 return;
766 }
767
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000768 if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset,
769 size, true))
770 return;
771
772 /* The per-attribute binding point */
773 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset,
774 size);
Martin Peres296d8232015-01-21 12:22:11 +0200775}
776
Brian Paulde8530e2010-03-30 19:50:11 -0600777/**
Paul Berrye1907092014-01-22 05:14:48 -0800778 * Specify a buffer object to receive transform feedback results, plus the
Brian Paulde8530e2010-03-30 19:50:11 -0600779 * offset in the buffer to start placing results.
780 * This function is part of GL_EXT_transform_feedback, but not GL3.
781 */
782void GLAPIENTRY
783_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
784 GLintptr offset)
785{
Brian Paulfef6e362010-05-10 21:11:21 -0600786 struct gl_transform_feedback_object *obj;
Brian Paulde8530e2010-03-30 19:50:11 -0600787 struct gl_buffer_object *bufObj;
788 GET_CURRENT_CONTEXT(ctx);
Brian Paulde8530e2010-03-30 19:50:11 -0600789
790 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
791 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
792 return;
793 }
794
Brian Paulfef6e362010-05-10 21:11:21 -0600795 obj = ctx->TransformFeedback.CurrentObject;
796
797 if (obj->Active) {
Brian Paulde8530e2010-03-30 19:50:11 -0600798 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul2be2e1d2011-04-29 12:03:28 -0600799 "glBindBufferOffsetEXT(transform feedback active)");
Brian Paulde8530e2010-03-30 19:50:11 -0600800 return;
801 }
802
Marek Olšák15ac66e2011-12-18 02:13:17 +0100803 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
Brian Paulde8530e2010-03-30 19:50:11 -0600804 _mesa_error(ctx, GL_INVALID_VALUE,
805 "glBindBufferOffsetEXT(index=%d)", index);
806 return;
807 }
808
Paul Berry86bb45f2011-12-30 10:14:35 -0800809 if (offset & 0x3) {
810 /* must be multiple of four */
811 _mesa_error(ctx, GL_INVALID_VALUE,
812 "glBindBufferOffsetEXT(offset=%d)", (int) offset);
813 return;
814 }
815
Kenneth Graunke05b086c2012-06-04 00:48:23 -0700816 if (buffer == 0) {
817 bufObj = ctx->Shared->NullBufferObj;
818 } else {
819 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
Samuel Pitoiset08ee28b2017-08-24 13:44:21 +0200820 if (!bufObj) {
821 _mesa_error(ctx, GL_INVALID_OPERATION,
822 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
823 return;
824 }
Brian Paulde8530e2010-03-30 19:50:11 -0600825 }
826
Timothy Arceri3eb6d342017-05-22 15:47:00 +1000827 _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0);
Brian Paulde8530e2010-03-30 19:50:11 -0600828}
829
830
831/**
Paul Berrye1907092014-01-22 05:14:48 -0800832 * This function specifies the transform feedback outputs to be written
Brian Paulde8530e2010-03-30 19:50:11 -0600833 * to the feedback buffer(s), and in what order.
834 */
Samuel Pitoiseta5319d92017-08-24 14:47:15 +0200835static ALWAYS_INLINE void
836transform_feedback_varyings(struct gl_context *ctx,
837 struct gl_shader_program *shProg, GLsizei count,
838 const GLchar *const *varyings, GLenum bufferMode)
839{
840 GLint i;
841
842 /* free existing varyings, if any */
843 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) {
844 free(shProg->TransformFeedback.VaryingNames[i]);
845 }
846 free(shProg->TransformFeedback.VaryingNames);
847
848 /* allocate new memory for varying names */
849 shProg->TransformFeedback.VaryingNames =
850 malloc(count * sizeof(GLchar *));
851
852 if (!shProg->TransformFeedback.VaryingNames) {
853 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
854 return;
855 }
856
857 /* Save the new names and the count */
858 for (i = 0; i < count; i++) {
859 shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]);
860 }
861 shProg->TransformFeedback.NumVarying = count;
862
863 shProg->TransformFeedback.BufferMode = bufferMode;
864
865 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since
866 * the varyings won't be used until shader link time.
867 */
868}
869
870
Brian Paulde8530e2010-03-30 19:50:11 -0600871void GLAPIENTRY
Samuel Pitoiset83690d42017-08-24 14:50:11 +0200872_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count,
873 const GLchar *const *varyings,
874 GLenum bufferMode)
875{
876 GET_CURRENT_CONTEXT(ctx);
877
878 struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program);
879 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
880}
881
882void GLAPIENTRY
Brian Paulde8530e2010-03-30 19:50:11 -0600883_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
Paul Berrybb3db382012-11-06 07:26:56 -0800884 const GLchar * const *varyings,
885 GLenum bufferMode)
Brian Paulde8530e2010-03-30 19:50:11 -0600886{
887 struct gl_shader_program *shProg;
Brian Paul8e45e382012-10-27 08:58:19 -0600888 GLint i;
Brian Paulde8530e2010-03-30 19:50:11 -0600889 GET_CURRENT_CONTEXT(ctx);
890
Kenneth Graunkea7d616d2013-09-06 12:38:12 -0700891 /* From the ARB_transform_feedback2 specification:
892 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings
893 * if the current transform feedback object is active, even if paused."
894 */
895 if (ctx->TransformFeedback.CurrentObject->Active) {
896 _mesa_error(ctx, GL_INVALID_OPERATION,
897 "glTransformFeedbackVaryings(current object is active)");
898 return;
899 }
900
Brian Paulde8530e2010-03-30 19:50:11 -0600901 switch (bufferMode) {
902 case GL_INTERLEAVED_ATTRIBS:
903 break;
904 case GL_SEPARATE_ATTRIBS:
905 break;
906 default:
907 _mesa_error(ctx, GL_INVALID_ENUM,
908 "glTransformFeedbackVaryings(bufferMode)");
909 return;
910 }
911
Marek Olšákf77aa272011-10-24 02:21:48 +0200912 if (count < 0 ||
913 (bufferMode == GL_SEPARATE_ATTRIBS &&
Marek Olšák15ac66e2011-12-18 02:13:17 +0100914 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) {
Brian Paulde8530e2010-03-30 19:50:11 -0600915 _mesa_error(ctx, GL_INVALID_VALUE,
916 "glTransformFeedbackVaryings(count=%d)", count);
917 return;
918 }
919
Tapani Pällifc8b3582016-10-10 09:49:36 +0300920 shProg = _mesa_lookup_shader_program_err(ctx, program,
921 "glTransformFeedbackVaryings");
922 if (!shProg)
Brian Paulde8530e2010-03-30 19:50:11 -0600923 return;
Brian Paulde8530e2010-03-30 19:50:11 -0600924
Marek Olšák375e73d2011-12-18 02:43:31 +0100925 if (ctx->Extensions.ARB_transform_feedback3) {
926 if (bufferMode == GL_INTERLEAVED_ATTRIBS) {
927 unsigned buffers = 1;
928
929 for (i = 0; i < count; i++) {
930 if (strcmp(varyings[i], "gl_NextBuffer") == 0)
931 buffers++;
932 }
933
934 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) {
935 _mesa_error(ctx, GL_INVALID_OPERATION,
936 "glTransformFeedbackVaryings(too many gl_NextBuffer "
Andreas Bollc83e1612015-12-09 17:10:33 +0100937 "occurrences)");
Marek Olšák375e73d2011-12-18 02:43:31 +0100938 return;
939 }
940 } else {
941 for (i = 0; i < count; i++) {
942 if (strcmp(varyings[i], "gl_NextBuffer") == 0 ||
943 strcmp(varyings[i], "gl_SkipComponents1") == 0 ||
944 strcmp(varyings[i], "gl_SkipComponents2") == 0 ||
945 strcmp(varyings[i], "gl_SkipComponents3") == 0 ||
946 strcmp(varyings[i], "gl_SkipComponents4") == 0) {
947 _mesa_error(ctx, GL_INVALID_OPERATION,
948 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS,"
949 "varying=%s)",
950 varyings[i]);
951 return;
952 }
953 }
954 }
955 }
956
Samuel Pitoiseta5319d92017-08-24 14:47:15 +0200957 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode);
Brian Paulde8530e2010-03-30 19:50:11 -0600958}
959
960
961/**
Paul Berrye1907092014-01-22 05:14:48 -0800962 * Get info about the transform feedback outputs which are to be written
Brian Paulde8530e2010-03-30 19:50:11 -0600963 * to the feedback buffer(s).
964 */
965void GLAPIENTRY
966_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
967 GLsizei bufSize, GLsizei *length,
968 GLsizei *size, GLenum *type, GLchar *name)
969{
970 const struct gl_shader_program *shProg;
Tapani Pällidc39d842015-03-12 12:08:56 +0200971 struct gl_program_resource *res;
Brian Paulde8530e2010-03-30 19:50:11 -0600972 GET_CURRENT_CONTEXT(ctx);
973
Tapani Pällifc8b3582016-10-10 09:49:36 +0300974 shProg = _mesa_lookup_shader_program_err(ctx, program,
975 "glGetTransformFeedbackVarying");
976 if (!shProg)
Brian Paulde8530e2010-03-30 19:50:11 -0600977 return;
Brian Paulde8530e2010-03-30 19:50:11 -0600978
Tapani Pällidc39d842015-03-12 12:08:56 +0200979 res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg,
980 GL_TRANSFORM_FEEDBACK_VARYING,
981 index);
982 if (!res) {
Brian Paulde8530e2010-03-30 19:50:11 -0600983 _mesa_error(ctx, GL_INVALID_VALUE,
Matt Turner8f3570e2012-11-22 00:06:03 -0800984 "glGetTransformFeedbackVarying(index=%u)", index);
Brian Paulde8530e2010-03-30 19:50:11 -0600985 return;
986 }
987
Eric Anholt9d36c962012-01-02 17:08:13 -0800988 /* return the varying's name and length */
Tapani Pällidc39d842015-03-12 12:08:56 +0200989 _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res));
Brian Paulde8530e2010-03-30 19:50:11 -0600990
Eric Anholt9d36c962012-01-02 17:08:13 -0800991 /* return the datatype and value's size (in datatype units) */
992 if (type)
Tapani Pällidc39d842015-03-12 12:08:56 +0200993 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
994 res, index, GL_TYPE, (GLint*) type,
995 "glGetTransformFeedbackVarying");
Eric Anholt9d36c962012-01-02 17:08:13 -0800996 if (size)
Tapani Pällidc39d842015-03-12 12:08:56 +0200997 _mesa_program_resource_prop((struct gl_shader_program *) shProg,
998 res, index, GL_ARRAY_SIZE, (GLint*) size,
999 "glGetTransformFeedbackVarying");
Brian Paulde8530e2010-03-30 19:50:11 -06001000}
1001
Brian Paulfef6e362010-05-10 21:11:21 -06001002
1003
Marek Olšák14bb9572011-12-09 17:00:23 +01001004struct gl_transform_feedback_object *
1005_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -06001006{
Martin Peresa5d165a2015-01-20 16:30:32 +02001007 /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating
1008 * the default transform feedback object, or the name of an existing
1009 * transform feedback object."
1010 */
Brian Paulfef6e362010-05-10 21:11:21 -06001011 if (name == 0) {
1012 return ctx->TransformFeedback.DefaultObject;
1013 }
1014 else
1015 return (struct gl_transform_feedback_object *)
Timothy Arceri918cec82017-04-07 11:40:40 +10001016 _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name);
Brian Paulfef6e362010-05-10 21:11:21 -06001017}
1018
Martin Peresc86cb2d2015-01-14 18:17:21 +02001019static void
1020create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids,
1021 bool dsa)
Brian Paulfef6e362010-05-10 21:11:21 -06001022{
1023 GLuint first;
Martin Peresc86cb2d2015-01-14 18:17:21 +02001024 const char* func;
1025
1026 if (dsa)
1027 func = "glCreateTransformFeedbacks";
1028 else
1029 func = "glGenTransformFeedbacks";
Brian Paulfef6e362010-05-10 21:11:21 -06001030
Brian Paulfef6e362010-05-10 21:11:21 -06001031 if (n < 0) {
Martin Peresc86cb2d2015-01-14 18:17:21 +02001032 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func);
Brian Paulfef6e362010-05-10 21:11:21 -06001033 return;
1034 }
1035
Martin Peresc86cb2d2015-01-14 18:17:21 +02001036 if (!ids)
Brian Paulfef6e362010-05-10 21:11:21 -06001037 return;
1038
1039 /* we don't need contiguous IDs, but this might be faster */
1040 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n);
1041 if (first) {
1042 GLsizei i;
1043 for (i = 0; i < n; i++) {
1044 struct gl_transform_feedback_object *obj
1045 = ctx->Driver.NewTransformFeedback(ctx, first + i);
1046 if (!obj) {
Martin Peresc86cb2d2015-01-14 18:17:21 +02001047 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
Brian Paulfef6e362010-05-10 21:11:21 -06001048 return;
1049 }
Martin Peresc86cb2d2015-01-14 18:17:21 +02001050 ids[i] = first + i;
Timothy Arceri918cec82017-04-07 11:40:40 +10001051 _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i,
1052 obj);
Martin Peresc86cb2d2015-01-14 18:17:21 +02001053 if (dsa) {
1054 /* this is normally done at bind time in the non-dsa case */
1055 obj->EverBound = GL_TRUE;
1056 }
Brian Paulfef6e362010-05-10 21:11:21 -06001057 }
1058 }
1059 else {
Martin Peresc86cb2d2015-01-14 18:17:21 +02001060 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
Brian Paulfef6e362010-05-10 21:11:21 -06001061 }
1062}
1063
Martin Peresc86cb2d2015-01-14 18:17:21 +02001064/**
1065 * Create new transform feedback objects. Transform feedback objects
1066 * encapsulate the state related to transform feedback to allow quickly
1067 * switching state (and drawing the results, below).
1068 * Part of GL_ARB_transform_feedback2.
1069 */
1070void GLAPIENTRY
1071_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names)
1072{
1073 GET_CURRENT_CONTEXT(ctx);
1074
1075 /* GenTransformFeedbacks should just reserve the object names that a
1076 * subsequent call to BindTransformFeedback should actively create. For
1077 * the sake of simplicity, we reserve the names and create the objects
1078 * straight away.
1079 */
1080
1081 create_transform_feedbacks(ctx, n, names, false);
1082}
1083
1084/**
1085 * Create new transform feedback objects. Transform feedback objects
1086 * encapsulate the state related to transform feedback to allow quickly
1087 * switching state (and drawing the results, below).
1088 * Part of GL_ARB_direct_state_access.
1089 */
1090void GLAPIENTRY
1091_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names)
1092{
1093 GET_CURRENT_CONTEXT(ctx);
1094
1095 create_transform_feedbacks(ctx, n, names, true);
1096}
1097
Brian Paulfef6e362010-05-10 21:11:21 -06001098
1099/**
1100 * Is the given ID a transform feedback object?
1101 * Part of GL_ARB_transform_feedback2.
1102 */
1103GLboolean GLAPIENTRY
1104_mesa_IsTransformFeedback(GLuint name)
1105{
Matt Turnerfd93d552012-12-19 13:43:31 -08001106 struct gl_transform_feedback_object *obj;
Brian Paulfef6e362010-05-10 21:11:21 -06001107 GET_CURRENT_CONTEXT(ctx);
1108
1109 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
1110
Matt Turnerfd93d552012-12-19 13:43:31 -08001111 if (name == 0)
Brian Paulfef6e362010-05-10 21:11:21 -06001112 return GL_FALSE;
Matt Turnerfd93d552012-12-19 13:43:31 -08001113
1114 obj = _mesa_lookup_transform_feedback_object(ctx, name);
1115 if (obj == NULL)
1116 return GL_FALSE;
1117
1118 return obj->EverBound;
Brian Paulfef6e362010-05-10 21:11:21 -06001119}
1120
1121
1122/**
1123 * Bind the given transform feedback object.
1124 * Part of GL_ARB_transform_feedback2.
1125 */
Samuel Pitoisetefb98112017-08-24 11:39:59 +02001126static ALWAYS_INLINE void
1127bind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error)
1128{
1129 struct gl_transform_feedback_object *obj;
1130
1131 obj = _mesa_lookup_transform_feedback_object(ctx, name);
1132 if (!no_error && !obj) {
1133 _mesa_error(ctx, GL_INVALID_OPERATION,
1134 "glBindTransformFeedback(name=%u)", name);
1135 return;
1136 }
1137
1138 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject,
1139 obj);
1140}
1141
1142
Brian Paulfef6e362010-05-10 21:11:21 -06001143void GLAPIENTRY
Samuel Pitoisetb0590ac2017-08-24 11:41:15 +02001144_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name)
1145{
1146 GET_CURRENT_CONTEXT(ctx);
1147 bind_transform_feedback(ctx, name, true);
1148}
1149
1150
1151void GLAPIENTRY
Vinson Leed74f5252010-05-10 22:58:23 -07001152_mesa_BindTransformFeedback(GLenum target, GLuint name)
Brian Paulfef6e362010-05-10 21:11:21 -06001153{
Brian Paulfef6e362010-05-10 21:11:21 -06001154 GET_CURRENT_CONTEXT(ctx);
1155
1156 if (target != GL_TRANSFORM_FEEDBACK) {
1157 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)");
1158 return;
1159 }
1160
Paul Berry5b7099c2012-12-15 14:21:32 -08001161 if (_mesa_is_xfb_active_and_unpaused(ctx)) {
Brian Paulfef6e362010-05-10 21:11:21 -06001162 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul39c13a12010-05-14 08:03:42 -06001163 "glBindTransformFeedback(transform is active, or not paused)");
Brian Paulfef6e362010-05-10 21:11:21 -06001164 return;
1165 }
1166
Samuel Pitoisetefb98112017-08-24 11:39:59 +02001167 bind_transform_feedback(ctx, name, false);
Brian Paulfef6e362010-05-10 21:11:21 -06001168}
1169
1170
1171/**
1172 * Delete the given transform feedback objects.
1173 * Part of GL_ARB_transform_feedback2.
1174 */
1175void GLAPIENTRY
1176_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names)
1177{
1178 GLint i;
1179 GET_CURRENT_CONTEXT(ctx);
1180
Brian Paulfef6e362010-05-10 21:11:21 -06001181 if (n < 0) {
1182 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)");
1183 return;
1184 }
1185
1186 if (!names)
1187 return;
1188
1189 for (i = 0; i < n; i++) {
1190 if (names[i] > 0) {
1191 struct gl_transform_feedback_object *obj
Marek Olšák14bb9572011-12-09 17:00:23 +01001192 = _mesa_lookup_transform_feedback_object(ctx, names[i]);
Brian Paulfef6e362010-05-10 21:11:21 -06001193 if (obj) {
1194 if (obj->Active) {
1195 _mesa_error(ctx, GL_INVALID_OPERATION,
1196 "glDeleteTransformFeedbacks(object %u is active)",
1197 names[i]);
1198 return;
1199 }
Timothy Arceri918cec82017-04-07 11:40:40 +10001200 _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]);
Brian Paulfef6e362010-05-10 21:11:21 -06001201 /* unref, but object may not be deleted until later */
Ilia Mirkin0941ef32016-03-06 12:36:19 -05001202 if (obj == ctx->TransformFeedback.CurrentObject) {
1203 reference_transform_feedback_object(
1204 &ctx->TransformFeedback.CurrentObject,
1205 ctx->TransformFeedback.DefaultObject);
1206 }
Brian Paulfef6e362010-05-10 21:11:21 -06001207 reference_transform_feedback_object(&obj, NULL);
1208 }
1209 }
1210 }
1211}
1212
1213
1214/**
1215 * Pause transform feedback.
1216 * Part of GL_ARB_transform_feedback2.
1217 */
Samuel Pitoiset061a1ee2017-08-24 12:29:50 +02001218static void
1219pause_transform_feedback(struct gl_context *ctx,
1220 struct gl_transform_feedback_object *obj)
1221{
1222 FLUSH_VERTICES(ctx, 0);
1223 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1224
1225 assert(ctx->Driver.PauseTransformFeedback);
1226 ctx->Driver.PauseTransformFeedback(ctx, obj);
1227
1228 obj->Paused = GL_TRUE;
1229}
1230
1231
Brian Paulfef6e362010-05-10 21:11:21 -06001232void GLAPIENTRY
Samuel Pitoiset1c88ed92017-08-24 12:06:26 +02001233_mesa_PauseTransformFeedback_no_error(void)
1234{
1235 GET_CURRENT_CONTEXT(ctx);
1236 pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1237}
1238
1239
1240void GLAPIENTRY
Brian Paulfef6e362010-05-10 21:11:21 -06001241_mesa_PauseTransformFeedback(void)
1242{
1243 struct gl_transform_feedback_object *obj;
1244 GET_CURRENT_CONTEXT(ctx);
1245
1246 obj = ctx->TransformFeedback.CurrentObject;
1247
Paul Berry5b7099c2012-12-15 14:21:32 -08001248 if (!_mesa_is_xfb_active_and_unpaused(ctx)) {
Brian Paulfef6e362010-05-10 21:11:21 -06001249 _mesa_error(ctx, GL_INVALID_OPERATION,
1250 "glPauseTransformFeedback(feedback not active or already paused)");
1251 return;
1252 }
1253
Samuel Pitoiset061a1ee2017-08-24 12:29:50 +02001254 pause_transform_feedback(ctx, obj);
Brian Paulfef6e362010-05-10 21:11:21 -06001255}
1256
1257
1258/**
1259 * Resume transform feedback.
1260 * Part of GL_ARB_transform_feedback2.
1261 */
Samuel Pitoisetf0476e02017-08-24 12:09:23 +02001262static void
1263resume_transform_feedback(struct gl_context *ctx,
1264 struct gl_transform_feedback_object *obj)
1265{
1266 FLUSH_VERTICES(ctx, 0);
1267 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback;
1268
1269 obj->Paused = GL_FALSE;
1270
1271 assert(ctx->Driver.ResumeTransformFeedback);
1272 ctx->Driver.ResumeTransformFeedback(ctx, obj);
1273}
1274
1275
Brian Paulfef6e362010-05-10 21:11:21 -06001276void GLAPIENTRY
Samuel Pitoiset4b5140d2017-08-24 12:10:57 +02001277_mesa_ResumeTransformFeedback_no_error(void)
1278{
1279 GET_CURRENT_CONTEXT(ctx);
1280 resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject);
1281}
1282
1283
1284void GLAPIENTRY
Brian Paulfef6e362010-05-10 21:11:21 -06001285_mesa_ResumeTransformFeedback(void)
1286{
1287 struct gl_transform_feedback_object *obj;
1288 GET_CURRENT_CONTEXT(ctx);
1289
1290 obj = ctx->TransformFeedback.CurrentObject;
1291
1292 if (!obj->Active || !obj->Paused) {
1293 _mesa_error(ctx, GL_INVALID_OPERATION,
Brian Paul076bd112011-04-29 18:42:09 -06001294 "glResumeTransformFeedback(feedback not active or not paused)");
Brian Paulfef6e362010-05-10 21:11:21 -06001295 return;
1296 }
1297
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001298 /* From the ARB_transform_feedback2 specification:
1299 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if
1300 * the program object being used by the current transform feedback object
1301 * is not active."
1302 */
Timothy Arceri3177eef2016-11-03 14:35:08 +11001303 if (obj->program != get_xfb_source(ctx)) {
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001304 _mesa_error(ctx, GL_INVALID_OPERATION,
Paul Berrye1907092014-01-22 05:14:48 -08001305 "glResumeTransformFeedback(wrong program bound)");
Kenneth Graunke9cc74c92013-09-06 14:51:26 -07001306 return;
1307 }
1308
Samuel Pitoisetf0476e02017-08-24 12:09:23 +02001309 resume_transform_feedback(ctx, obj);
Brian Paulfef6e362010-05-10 21:11:21 -06001310}
Martin Perese59d2432015-02-16 16:13:58 +02001311
1312extern void GLAPIENTRY
1313_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param)
1314{
1315 struct gl_transform_feedback_object *obj;
1316 GET_CURRENT_CONTEXT(ctx);
1317
1318 obj = lookup_transform_feedback_object_err(ctx, xfb,
1319 "glGetTransformFeedbackiv");
1320 if(!obj) {
1321 return;
1322 }
1323
1324 switch(pname) {
1325 case GL_TRANSFORM_FEEDBACK_PAUSED:
1326 *param = obj->Paused;
1327 break;
1328 case GL_TRANSFORM_FEEDBACK_ACTIVE:
1329 *param = obj->Active;
1330 break;
1331 default:
1332 _mesa_error(ctx, GL_INVALID_ENUM,
1333 "glGetTransformFeedbackiv(pname=%i)", pname);
1334 }
1335}
Martin Peres8799ecd2015-02-16 16:13:59 +02001336
1337extern void GLAPIENTRY
1338_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index,
1339 GLint *param)
1340{
1341 struct gl_transform_feedback_object *obj;
1342 GET_CURRENT_CONTEXT(ctx);
1343
1344 obj = lookup_transform_feedback_object_err(ctx, xfb,
1345 "glGetTransformFeedbacki_v");
1346 if(!obj) {
1347 return;
1348 }
1349
1350 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1351 _mesa_error(ctx, GL_INVALID_VALUE,
1352 "glGetTransformFeedbacki_v(index=%i)", index);
1353 return;
1354 }
1355
1356 switch(pname) {
1357 case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
1358 *param = obj->BufferNames[index];
1359 break;
1360 default:
1361 _mesa_error(ctx, GL_INVALID_ENUM,
1362 "glGetTransformFeedbacki_v(pname=%i)", pname);
1363 }
1364}
Martin Peres6ead10d2015-01-22 16:55:29 +02001365
1366extern void GLAPIENTRY
1367_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index,
1368 GLint64 *param)
1369{
1370 struct gl_transform_feedback_object *obj;
1371 GET_CURRENT_CONTEXT(ctx);
1372
1373 obj = lookup_transform_feedback_object_err(ctx, xfb,
1374 "glGetTransformFeedbacki64_v");
1375 if(!obj) {
1376 return;
1377 }
1378
1379 if (index >= ctx->Const.MaxTransformFeedbackBuffers) {
1380 _mesa_error(ctx, GL_INVALID_VALUE,
1381 "glGetTransformFeedbacki64_v(index=%i)", index);
1382 return;
1383 }
1384
Dave Airliebac39dd2016-05-30 06:56:52 +10001385 compute_transform_feedback_buffer_sizes(obj);
Martin Peres6ead10d2015-01-22 16:55:29 +02001386 switch(pname) {
1387 case GL_TRANSFORM_FEEDBACK_BUFFER_START:
1388 *param = obj->Offset[index];
1389 break;
1390 case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
Dave Airliebac39dd2016-05-30 06:56:52 +10001391 *param = obj->Size[index];
Martin Peres6ead10d2015-01-22 16:55:29 +02001392 break;
1393 default:
1394 _mesa_error(ctx, GL_INVALID_ENUM,
1395 "glGetTransformFeedbacki64_v(pname=%i)", pname);
1396 }
1397}