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