transformfeedback.c revision de8530e1546733bf21b2e0518d6c5bda560770b9
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 "enums.h"
37#include "transformfeedback.h"
38
39#include "shader/prog_parameter.h"
40#include "shader/shader_api.h"
41
42
43/**
44 * Check if the given primitive mode (as in glBegin(mode)) is compatible
45 * with the current transform feedback mode (if it's enabled).
46 * This is to be called from glBegin(), glDrawArrays(), glDrawElements(), etc.
47 *
48 * \return GL_TRUE if the mode is OK, GL_FALSE otherwise.
49 */
50GLboolean
51_mesa_validate_primitive_mode(GLcontext *ctx, GLenum mode)
52{
53   if (ctx->TransformFeedback.Active) {
54      switch (mode) {
55      case GL_POINTS:
56         return ctx->TransformFeedback.Mode == GL_POINTS;
57      case GL_LINES:
58      case GL_LINE_STRIP:
59      case GL_LINE_LOOP:
60         return ctx->TransformFeedback.Mode == GL_LINES;
61      default:
62         return ctx->TransformFeedback.Mode == GL_TRIANGLES;
63      }
64   }
65   return GL_TRUE;
66}
67
68
69/**
70 * Check that all the buffer objects currently bound for transform
71 * feedback actually exist.  Raise a GL_INVALID_OPERATION error if
72 * any buffers are missing.
73 * \return GL_TRUE for success, GL_FALSE if error
74 */
75GLboolean
76_mesa_validate_transform_feedback_buffers(GLcontext *ctx)
77{
78
79   return GL_TRUE;
80}
81
82
83
84/**
85 * Per-context init for transform feedback.
86 */
87void
88_mesa_init_transform_feedback(GLcontext *ctx)
89{
90   _mesa_reference_buffer_object(ctx,
91                                 &ctx->TransformFeedback.CurrentBuffer,
92                                 ctx->Shared->NullBufferObj);
93}
94
95
96/**
97 * Per-context free/clean-up for transform feedback.
98 */
99void
100_mesa_free_transform_feedback(GLcontext *ctx)
101{
102   _mesa_reference_buffer_object(ctx,
103                                 &ctx->TransformFeedback.CurrentBuffer,
104                                 NULL);
105}
106
107
108void GLAPIENTRY
109_mesa_BeginTransformFeedback(GLenum mode)
110{
111   GET_CURRENT_CONTEXT(ctx);
112
113   switch (mode) {
114   case GL_POINTS:
115   case GL_LINES:
116   case GL_TRIANGLES:
117      /* legal */
118      break;
119   default:
120      _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)");
121      return;
122   }
123
124   if (ctx->TransformFeedback.Active) {
125      _mesa_error(ctx, GL_INVALID_OPERATION,
126                  "glBeginTransformFeedback(already active)");
127      return;
128   }
129
130   ctx->TransformFeedback.Active = GL_TRUE;
131   ctx->TransformFeedback.Mode = mode;
132}
133
134
135void GLAPIENTRY
136_mesa_EndTransformFeedback(void)
137{
138   GET_CURRENT_CONTEXT(ctx);
139
140   if (!ctx->TransformFeedback.Active) {
141      _mesa_error(ctx, GL_INVALID_OPERATION,
142                  "glEndTransformFeedback(not active)");
143      return;
144   }
145
146   ctx->TransformFeedback.Active = GL_FALSE;
147}
148
149
150/**
151 * Helper used by BindBufferRange() and BindBufferBase().
152 */
153static void
154bind_buffer_range(GLcontext *ctx, GLuint index,
155                  struct gl_buffer_object *bufObj,
156                  GLintptr offset, GLsizeiptr size)
157{
158   /* The general binding point */
159   _mesa_reference_buffer_object(ctx,
160                                 &ctx->TransformFeedback.CurrentBuffer,
161                                 bufObj);
162
163   /* The per-attribute binding point */
164   _mesa_reference_buffer_object(ctx,
165                                 &ctx->TransformFeedback.Buffers[index],
166                                 bufObj);
167
168   ctx->TransformFeedback.BufferNames[index] = bufObj->Name;
169
170   ctx->TransformFeedback.Offset[index] = offset;
171   ctx->TransformFeedback.Size[index] = size;
172}
173
174
175/**
176 * Specify a buffer object to receive vertex shader results.  Plus,
177 * specify the starting offset to place the results, and max size.
178 */
179void GLAPIENTRY
180_mesa_BindBufferRange(GLenum target, GLuint index,
181                      GLuint buffer, GLintptr offset, GLsizeiptr size)
182{
183   struct gl_buffer_object *bufObj;
184   GET_CURRENT_CONTEXT(ctx);
185
186   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
187      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferRange(target)");
188      return;
189   }
190
191   if (ctx->TransformFeedback.Active) {
192      _mesa_error(ctx, GL_INVALID_OPERATION,
193                  "glBindBufferRange(transform feedback active)");
194      return;
195   }
196
197   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
198      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index);
199      return;
200   }
201
202   if ((size <= 0) || (size & 0x3)) {
203      /* must be positive and multiple of four */
204      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size%d)", size);
205      return;
206   }
207
208   if (offset & 0x3) {
209      /* must be multiple of four */
210      _mesa_error(ctx, GL_INVALID_VALUE,
211                  "glBindBufferRange(offset=%d)", offset);
212      return;
213   }
214
215   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
216   if (!bufObj) {
217      _mesa_error(ctx, GL_INVALID_OPERATION,
218                  "glBindBufferRange(invalid buffer=%u)", buffer);
219      return;
220   }
221
222   if (offset + size >= bufObj->Size) {
223      _mesa_error(ctx, GL_INVALID_VALUE,
224                  "glBindBufferRange(offset + size > buffer size)", size);
225      return;
226   }
227
228   bind_buffer_range(ctx, index, bufObj, offset, size);
229}
230
231
232/**
233 * Specify a buffer object to receive vertex shader results.
234 * As above, but start at offset = 0.
235 */
236void GLAPIENTRY
237_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer)
238{
239   struct gl_buffer_object *bufObj;
240   GET_CURRENT_CONTEXT(ctx);
241   GLsizeiptr size;
242
243   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
244      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)");
245      return;
246   }
247
248   if (ctx->TransformFeedback.Active) {
249      _mesa_error(ctx, GL_INVALID_OPERATION,
250                  "glBindBufferRange(transform feedback active)");
251      return;
252   }
253
254   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
255      _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index);
256      return;
257   }
258
259   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
260   if (!bufObj) {
261      _mesa_error(ctx, GL_INVALID_OPERATION,
262                  "glBindBufferBase(invalid buffer=%u)", buffer);
263      return;
264   }
265
266   /* default size is the buffer size rounded down to nearest
267    * multiple of four.
268    */
269   size = bufObj->Size & ~0x3;
270
271   bind_buffer_range(ctx, index, bufObj, 0, size);
272}
273
274
275/**
276 * Specify a buffer object to receive vertex shader results, plus the
277 * offset in the buffer to start placing results.
278 * This function is part of GL_EXT_transform_feedback, but not GL3.
279 */
280void GLAPIENTRY
281_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer,
282                          GLintptr offset)
283{
284   struct gl_buffer_object *bufObj;
285   GET_CURRENT_CONTEXT(ctx);
286   GLsizeiptr size;
287
288   if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
289      _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)");
290      return;
291   }
292
293   if (ctx->TransformFeedback.Active) {
294      _mesa_error(ctx, GL_INVALID_OPERATION,
295                  "glBindBufferRange(transform feedback active)");
296      return;
297   }
298
299   if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) {
300      _mesa_error(ctx, GL_INVALID_VALUE,
301                  "glBindBufferOffsetEXT(index=%d)", index);
302      return;
303   }
304
305   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
306   if (!bufObj) {
307      _mesa_error(ctx, GL_INVALID_OPERATION,
308                  "glBindBufferOffsetEXT(invalid buffer=%u)", buffer);
309      return;
310   }
311
312   /* default size is the buffer size rounded down to nearest
313    * multiple of four.
314    */
315   size = (bufObj->Size - offset) & ~0x3;
316
317   bind_buffer_range(ctx, index, bufObj, offset, size);
318}
319
320
321/**
322 * This function specifies the vertex shader outputs to be written
323 * to the feedback buffer(s), and in what order.
324 */
325void GLAPIENTRY
326_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count,
327                                const GLchar **varyings, GLenum bufferMode)
328{
329   struct gl_shader_program *shProg;
330   GLuint i;
331   GET_CURRENT_CONTEXT(ctx);
332
333   switch (bufferMode) {
334   case GL_INTERLEAVED_ATTRIBS:
335      break;
336   case GL_SEPARATE_ATTRIBS:
337      break;
338   default:
339      _mesa_error(ctx, GL_INVALID_ENUM,
340                  "glTransformFeedbackVaryings(bufferMode)");
341      return;
342   }
343
344   if (count < 0 || count > ctx->Const.MaxTransformFeedbackSeparateAttribs) {
345      _mesa_error(ctx, GL_INVALID_VALUE,
346                  "glTransformFeedbackVaryings(count=%d)", count);
347      return;
348   }
349
350   shProg = _mesa_lookup_shader_program(ctx, program);
351   if (!shProg) {
352      _mesa_error(ctx, GL_INVALID_VALUE,
353                  "glTransformFeedbackVaryings(program=%u)", program);
354      return;
355   }
356
357   /* free existing varyings, if any */
358   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
359      free(shProg->TransformFeedback.VaryingNames[i]);
360   }
361   free(shProg->TransformFeedback.VaryingNames);
362
363   /* allocate new memory for varying names */
364   shProg->TransformFeedback.VaryingNames =
365      (GLchar **) malloc(count * sizeof(GLchar *));
366
367   if (!shProg->TransformFeedback.VaryingNames) {
368      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()");
369      return;
370   }
371
372   /* Save the new names and the count */
373   for (i = 0; i < (GLuint) count; i++) {
374      shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]);
375   }
376   shProg->TransformFeedback.NumVarying = count;
377
378   shProg->TransformFeedback.BufferMode = bufferMode;
379
380   /* The varyings won't be used until shader link time */
381}
382
383
384/**
385 * Get info about the vertex shader's outputs which are to be written
386 * to the feedback buffer(s).
387 */
388void GLAPIENTRY
389_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index,
390                                  GLsizei bufSize, GLsizei *length,
391                                  GLsizei *size, GLenum *type, GLchar *name)
392{
393   const struct gl_shader_program *shProg;
394   const GLchar *varyingName;
395   GLint v;
396   GET_CURRENT_CONTEXT(ctx);
397
398   shProg = _mesa_lookup_shader_program(ctx, program);
399   if (!shProg) {
400      _mesa_error(ctx, GL_INVALID_VALUE,
401                  "glGetTransformFeedbackVaryings(program=%u)", program);
402      return;
403   }
404
405   if (index >= shProg->TransformFeedback.NumVarying) {
406      _mesa_error(ctx, GL_INVALID_VALUE,
407                  "glGetTransformFeedbackVaryings(index=%u)", index);
408      return;
409   }
410
411   varyingName = shProg->TransformFeedback.VaryingNames[index];
412
413   v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName);
414   if (v >= 0) {
415      struct gl_program_parameter *param = &shProg->Varying->Parameters[v];
416
417      /* return the varying's name and length */
418      _mesa_copy_string(name, bufSize, length, varyingName);
419
420      /* return the datatype and value's size (in datatype units) */
421      if (type)
422         *type = param->Type;
423      if (size)
424         *size = param->Size;
425   }
426}
427
428