Merge commit 'origin/7.8'
[mesa.git] / src / mesa / main / transformfeedback.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2010 VMware, Inc. All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25 /*
26 * Vertex transform feedback support.
27 *
28 * Authors:
29 * Brian Paul
30 */
31
32
33 #include "buffers.h"
34 #include "bufferobj.h"
35 #include "context.h"
36 #include "transformfeedback.h"
37
38 #include "shader/prog_parameter.h"
39 #include "shader/shader_api.h"
40
41
42 /**
43 * Check if the given primitive mode (as in glBegin(mode)) is compatible
44 * with the current transform feedback mode (if it's enabled).
45 * This is to be called from glBegin(), glDrawArrays(), glDrawElements(), etc.
46 *
47 * \return GL_TRUE if the mode is OK, GL_FALSE otherwise.
48 */
49 GLboolean
50 _mesa_validate_primitive_mode(GLcontext *ctx, GLenum mode)
51 {
52 if (ctx->TransformFeedback.Active) {
53 switch (mode) {
54 case GL_POINTS:
55 return ctx->TransformFeedback.Mode == GL_POINTS;
56 case GL_LINES:
57 case GL_LINE_STRIP:
58 case GL_LINE_LOOP:
59 return ctx->TransformFeedback.Mode == GL_LINES;
60 default:
61 return ctx->TransformFeedback.Mode == GL_TRIANGLES;
62 }
63 }
64 return GL_TRUE;
65 }
66
67
68 /**
69 * Check that all the buffer objects currently bound for transform
70 * feedback actually exist. Raise a GL_INVALID_OPERATION error if
71 * any buffers are missing.
72 * \return GL_TRUE for success, GL_FALSE if error
73 */
74 GLboolean
75 _mesa_validate_transform_feedback_buffers(GLcontext *ctx)
76 {
77
78 return GL_TRUE;
79 }
80
81
82
83 /**
84 * Per-context init for transform feedback.
85 */
86 void
87 _mesa_init_transform_feedback(GLcontext *ctx)
88 {
89 _mesa_reference_buffer_object(ctx,
90 &ctx->TransformFeedback.CurrentBuffer,
91 ctx->Shared->NullBufferObj);
92 }
93
94
95 /**
96 * Per-context free/clean-up for transform feedback.
97 */
98 void
99 _mesa_free_transform_feedback(GLcontext *ctx)
100 {
101 _mesa_reference_buffer_object(ctx,
102 &ctx->TransformFeedback.CurrentBuffer,
103 NULL);
104 }
105
106
107 void GLAPIENTRY
108 _mesa_BeginTransformFeedback(GLenum mode)
109 {
110 GET_CURRENT_CONTEXT(ctx);
111
112 switch (mode) {
113 case GL_POINTS:
114 case GL_LINES:
115 case GL_TRIANGLES:
116 /* legal */
117 break;
118 default:
119 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
120 return;
121 }
122
123 if (ctx->TransformFeedback.Active) {
124 _mesa_error(ctx, GL_INVALID_OPERATION,
125 "glBeginTransformFeedback(already active)");
126 return;
127 }
128
129 ctx->TransformFeedback.Active = GL_TRUE;
130 ctx->TransformFeedback.Mode = mode;
131 }
132
133
134 void GLAPIENTRY
135 _mesa_EndTransformFeedback(void)
136 {
137 GET_CURRENT_CONTEXT(ctx);
138
139 if (!ctx->TransformFeedback.Active) {
140 _mesa_error(ctx, GL_INVALID_OPERATION,
141 "glEndTransformFeedback(not active)");
142 return;
143 }
144
145 ctx->TransformFeedback.Active = GL_FALSE;
146 }
147
148
149 /**
150 * Helper used by BindBufferRange() and BindBufferBase().
151 */
152 static void
153 bind_buffer_range(GLcontext *ctx, GLuint index,
154 struct gl_buffer_object *bufObj,
155 GLintptr offset, GLsizeiptr size)
156 {
157 /* The general binding point */
158 _mesa_reference_buffer_object(ctx,
159 &ctx->TransformFeedback.CurrentBuffer,
160 bufObj);
161
162 /* The per-attribute binding point */
163 _mesa_reference_buffer_object(ctx,
164 &ctx->TransformFeedback.Buffers[index],
165 bufObj);
166
167 ctx->TransformFeedback.BufferNames[index] = bufObj->Name;
168
169 ctx->TransformFeedback.Offset[index] = offset;
170 ctx->TransformFeedback.Size[index] = size;
171 }
172
173
174 /**
175 * Specify a buffer object to receive vertex shader results. Plus,
176 * specify the starting offset to place the results, and max size.
177 */
178 void GLAPIENTRY
179 _mesa_BindBufferRange(GLenum target, GLuint index,
180 GLuint buffer, GLintptr offset, GLsizeiptr size)
181 {
182 struct gl_buffer_object *bufObj;
183 GET_CURRENT_CONTEXT(ctx);
184
185 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
186 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
187 return;
188 }
189
190 if (ctx->TransformFeedback.Active) {
191 _mesa_error(ctx, GL_INVALID_OPERATION,
192 "glBindBufferRange(transform feedback active)");
193 return;
194 }
195
196 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
197 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
198 return;
199 }
200
201 if ((size <= 0) || (size & 0x3)) {
202 /* must be positive and multiple of four */
203 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size%d)", size);
204 return;
205 }
206
207 if (offset & 0x3) {
208 /* must be multiple of four */
209 _mesa_error(ctx, GL_INVALID_VALUE,
210 "glBindBufferRange(offset=%d)", offset);
211 return;
212 }
213
214 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
215 if (!bufObj) {
216 _mesa_error(ctx, GL_INVALID_OPERATION,
217 "glBindBufferRange(invalid buffer=%u)", buffer);
218 return;
219 }
220
221 if (offset + size >= bufObj->Size) {
222 _mesa_error(ctx, GL_INVALID_VALUE,
223 "glBindBufferRange(offset + size > buffer size)", size);
224 return;
225 }
226
227 bind_buffer_range(ctx, index, bufObj, offset, size);
228 }
229
230
231 /**
232 * Specify a buffer object to receive vertex shader results.
233 * As above, but start at offset = 0.
234 */
235 void GLAPIENTRY
236 _mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
237 {
238 struct gl_buffer_object *bufObj;
239 GET_CURRENT_CONTEXT(ctx);
240 GLsizeiptr size;
241
242 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
243 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
244 return;
245 }
246
247 if (ctx->TransformFeedback.Active) {
248 _mesa_error(ctx, GL_INVALID_OPERATION,
249 "glBindBufferRange(transform feedback active)");
250 return;
251 }
252
253 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
254 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
255 return;
256 }
257
258 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
259 if (!bufObj) {
260 _mesa_error(ctx, GL_INVALID_OPERATION,
261 "glBindBufferBase(invalid buffer=%u)", buffer);
262 return;
263 }
264
265 /* default size is the buffer size rounded down to nearest
266 * multiple of four.
267 */
268 size = bufObj->Size & ~0x3;
269
270 bind_buffer_range(ctx, index, bufObj, 0, size);
271 }
272
273
274 /**
275 * Specify a buffer object to receive vertex shader results, plus the
276 * offset in the buffer to start placing results.
277 * This function is part of GL_EXT_transform_feedback, but not GL3.
278 */
279 void GLAPIENTRY
280 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
281 GLintptr offset)
282 {
283 struct gl_buffer_object *bufObj;
284 GET_CURRENT_CONTEXT(ctx);
285 GLsizeiptr size;
286
287 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
288 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
289 return;
290 }
291
292 if (ctx->TransformFeedback.Active) {
293 _mesa_error(ctx, GL_INVALID_OPERATION,
294 "glBindBufferRange(transform feedback active)");
295 return;
296 }
297
298 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
299 _mesa_error(ctx, GL_INVALID_VALUE,
300 "glBindBufferOffsetEXT(index=%d)", index);
301 return;
302 }
303
304 bufObj = _mesa_lookup_bufferobj(ctx, buffer);
305 if (!bufObj) {
306 _mesa_error(ctx, GL_INVALID_OPERATION,
307 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
308 return;
309 }
310
311 /* default size is the buffer size rounded down to nearest
312 * multiple of four.
313 */
314 size = (bufObj->Size - offset) & ~0x3;
315
316 bind_buffer_range(ctx, index, bufObj, offset, size);
317 }
318
319
320 /**
321 * This function specifies the vertex shader outputs to be written
322 * to the feedback buffer(s), and in what order.
323 */
324 void GLAPIENTRY
325 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
326 const GLchar **varyings, GLenum bufferMode)
327 {
328 struct gl_shader_program *shProg;
329 GLuint i;
330 GET_CURRENT_CONTEXT(ctx);
331
332 switch (bufferMode) {
333 case GL_INTERLEAVED_ATTRIBS:
334 break;
335 case GL_SEPARATE_ATTRIBS:
336 break;
337 default:
338 _mesa_error(ctx, GL_INVALID_ENUM,
339 "glTransformFeedbackVaryings(bufferMode)");
340 return;
341 }
342
343 if (count < 0 || count > ctx->Const.MaxTransformFeedbackSeparateAttribs) {
344 _mesa_error(ctx, GL_INVALID_VALUE,
345 "glTransformFeedbackVaryings(count=%d)", count);
346 return;
347 }
348
349 shProg = _mesa_lookup_shader_program(ctx, program);
350 if (!shProg) {
351 _mesa_error(ctx, GL_INVALID_VALUE,
352 "glTransformFeedbackVaryings(program=%u)", program);
353 return;
354 }
355
356 /* free existing varyings, if any */
357 for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
358 free(shProg->TransformFeedback.VaryingNames[i]);
359 }
360 free(shProg->TransformFeedback.VaryingNames);
361
362 /* allocate new memory for varying names */
363 shProg->TransformFeedback.VaryingNames =
364 (GLchar **) malloc(count * sizeof(GLchar *));
365
366 if (!shProg->TransformFeedback.VaryingNames) {
367 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
368 return;
369 }
370
371 /* Save the new names and the count */
372 for (i = 0; i < (GLuint) count; i++) {
373 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
374 }
375 shProg->TransformFeedback.NumVarying = count;
376
377 shProg->TransformFeedback.BufferMode = bufferMode;
378
379 /* The varyings won't be used until shader link time */
380 }
381
382
383 /**
384 * Get info about the vertex shader's outputs which are to be written
385 * to the feedback buffer(s).
386 */
387 void GLAPIENTRY
388 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
389 GLsizei bufSize, GLsizei *length,
390 GLsizei *size, GLenum *type, GLchar *name)
391 {
392 const struct gl_shader_program *shProg;
393 const GLchar *varyingName;
394 GLint v;
395 GET_CURRENT_CONTEXT(ctx);
396
397 shProg = _mesa_lookup_shader_program(ctx, program);
398 if (!shProg) {
399 _mesa_error(ctx, GL_INVALID_VALUE,
400 "glGetTransformFeedbackVaryings(program=%u)", program);
401 return;
402 }
403
404 if (index >= shProg->TransformFeedback.NumVarying) {
405 _mesa_error(ctx, GL_INVALID_VALUE,
406 "glGetTransformFeedbackVaryings(index=%u)", index);
407 return;
408 }
409
410 varyingName = shProg->TransformFeedback.VaryingNames[index];
411
412 v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName);
413 if (v >= 0) {
414 struct gl_program_parameter *param = &shProg->Varying->Parameters[v];
415
416 /* return the varying's name and length */
417 _mesa_copy_string(name, bufSize, length, varyingName);
418
419 /* return the datatype and value's size (in datatype units) */
420 if (type)
421 *type = param->DataType;
422 if (size)
423 *size = param->Size;
424 }
425 else {
426 name[0] = 0;
427 if (length)
428 *length = 0;
429 if (type)
430 *type = 0;
431 if (size)
432 *size = 0;
433 }
434 }
435