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 "hash.h" 37 #include "mfeatures.h" 38 #include "mtypes.h" 39 #include "transformfeedback.h" 40 #include "shaderapi.h" 41 #include "shaderobj.h" 42 #include "main/dispatch.h" 43 44 #include "program/prog_parameter.h" 45 46 47 #if FEATURE_EXT_transform_feedback 48 49 50 /** 51 * Do reference counting of transform feedback buffers. 52 */ 53 static void 54 reference_transform_feedback_object(struct gl_transform_feedback_object **ptr, 55 struct gl_transform_feedback_object *obj) 56 { 57 if (*ptr == obj) 58 return; 59 60 if (*ptr) { 61 /* Unreference the old object */ 62 struct gl_transform_feedback_object *oldObj = *ptr; 63 64 ASSERT(oldObj->RefCount > 0); 65 oldObj->RefCount--; 66 67 if (oldObj->RefCount == 0) { 68 GET_CURRENT_CONTEXT(ctx); 69 if (ctx) 70 ctx->Driver.DeleteTransformFeedback(ctx, oldObj); 71 } 72 73 *ptr = NULL; 74 } 75 ASSERT(!*ptr); 76 77 if (obj) { 78 /* reference new object */ 79 if (obj->RefCount == 0) { 80 _mesa_problem(NULL, "referencing deleted transform feedback object"); 81 *ptr = NULL; 82 } 83 else { 84 obj->RefCount++; 85 *ptr = obj; 86 } 87 } 88 } 89 90 91 /** 92 * Check that all the buffer objects currently bound for transform 93 * feedback actually exist. Raise a GL_INVALID_OPERATION error if 94 * any buffers are missing. 95 * \return GL_TRUE for success, GL_FALSE if error 96 */ 97 GLboolean 98 _mesa_validate_transform_feedback_buffers(struct gl_context *ctx) 99 { 100 /* XXX to do */ 101 return GL_TRUE; 102 } 103 104 105 106 /** 107 * Per-context init for transform feedback. 108 */ 109 void 110 _mesa_init_transform_feedback(struct gl_context *ctx) 111 { 112 /* core mesa expects this, even a dummy one, to be available */ 113 ASSERT(ctx->Driver.NewTransformFeedback); 114 115 ctx->TransformFeedback.DefaultObject = 116 ctx->Driver.NewTransformFeedback(ctx, 0); 117 118 assert(ctx->TransformFeedback.DefaultObject->RefCount == 1); 119 120 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 121 ctx->TransformFeedback.DefaultObject); 122 123 assert(ctx->TransformFeedback.DefaultObject->RefCount == 2); 124 125 ctx->TransformFeedback.Objects = _mesa_NewHashTable(); 126 127 _mesa_reference_buffer_object(ctx, 128 &ctx->TransformFeedback.CurrentBuffer, 129 ctx->Shared->NullBufferObj); 130 } 131 132 133 134 /** 135 * Callback for _mesa_HashDeleteAll(). 136 */ 137 static void 138 delete_cb(GLuint key, void *data, void *userData) 139 { 140 struct gl_context *ctx = (struct gl_context *) userData; 141 struct gl_transform_feedback_object *obj = 142 (struct gl_transform_feedback_object *) data; 143 144 ctx->Driver.DeleteTransformFeedback(ctx, obj); 145 } 146 147 148 /** 149 * Per-context free/clean-up for transform feedback. 150 */ 151 void 152 _mesa_free_transform_feedback(struct gl_context *ctx) 153 { 154 /* core mesa expects this, even a dummy one, to be available */ 155 ASSERT(ctx->Driver.NewTransformFeedback); 156 157 _mesa_reference_buffer_object(ctx, 158 &ctx->TransformFeedback.CurrentBuffer, 159 NULL); 160 161 /* Delete all feedback objects */ 162 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx); 163 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects); 164 165 /* Delete the default feedback object */ 166 assert(ctx->Driver.DeleteTransformFeedback); 167 ctx->Driver.DeleteTransformFeedback(ctx, 168 ctx->TransformFeedback.DefaultObject); 169 170 ctx->TransformFeedback.CurrentObject = NULL; 171 } 172 173 174 #else /* FEATURE_EXT_transform_feedback */ 175 176 /* forward declarations */ 177 static struct gl_transform_feedback_object * 178 new_transform_feedback(struct gl_context *ctx, GLuint name); 179 180 static void 181 delete_transform_feedback(struct gl_context *ctx, 182 struct gl_transform_feedback_object *obj); 183 184 /* dummy per-context init/clean-up for transform feedback */ 185 void 186 _mesa_init_transform_feedback(struct gl_context *ctx) 187 { 188 ctx->TransformFeedback.DefaultObject = new_transform_feedback(ctx, 0); 189 ctx->TransformFeedback.CurrentObject = ctx->TransformFeedback.DefaultObject; 190 _mesa_reference_buffer_object(ctx, 191 &ctx->TransformFeedback.CurrentBuffer, 192 ctx->Shared->NullBufferObj); 193 } 194 195 void 196 _mesa_free_transform_feedback(struct gl_context *ctx) 197 { 198 _mesa_reference_buffer_object(ctx, 199 &ctx->TransformFeedback.CurrentBuffer, 200 NULL); 201 ctx->TransformFeedback.CurrentObject = NULL; 202 delete_transform_feedback(ctx, ctx->TransformFeedback.DefaultObject); 203 } 204 205 #endif /* FEATURE_EXT_transform_feedback */ 206 207 208 /** Default fallback for ctx->Driver.NewTransformFeedback() */ 209 static struct gl_transform_feedback_object * 210 new_transform_feedback(struct gl_context *ctx, GLuint name) 211 { 212 struct gl_transform_feedback_object *obj; 213 obj = CALLOC_STRUCT(gl_transform_feedback_object); 214 if (obj) { 215 obj->Name = name; 216 obj->RefCount = 1; 217 } 218 return obj; 219 } 220 221 /** Default fallback for ctx->Driver.DeleteTransformFeedback() */ 222 static void 223 delete_transform_feedback(struct gl_context *ctx, 224 struct gl_transform_feedback_object *obj) 225 { 226 GLuint i; 227 228 for (i = 0; i < Elements(obj->Buffers); i++) { 229 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL); 230 } 231 232 free(obj); 233 } 234 235 236 #if FEATURE_EXT_transform_feedback 237 238 239 /** Default fallback for ctx->Driver.BeginTransformFeedback() */ 240 static void 241 begin_transform_feedback(struct gl_context *ctx, GLenum mode, 242 struct gl_transform_feedback_object *obj) 243 { 244 /* nop */ 245 } 246 247 /** Default fallback for ctx->Driver.EndTransformFeedback() */ 248 static void 249 end_transform_feedback(struct gl_context *ctx, 250 struct gl_transform_feedback_object *obj) 251 { 252 /* nop */ 253 } 254 255 /** Default fallback for ctx->Driver.PauseTransformFeedback() */ 256 static void 257 pause_transform_feedback(struct gl_context *ctx, 258 struct gl_transform_feedback_object *obj) 259 { 260 /* nop */ 261 } 262 263 /** Default fallback for ctx->Driver.ResumeTransformFeedback() */ 264 static void 265 resume_transform_feedback(struct gl_context *ctx, 266 struct gl_transform_feedback_object *obj) 267 { 268 /* nop */ 269 } 270 271 272 /** 273 * Plug in default device driver functions for transform feedback. 274 * Most drivers will override some/all of these. 275 */ 276 void 277 _mesa_init_transform_feedback_functions(struct dd_function_table *driver) 278 { 279 driver->NewTransformFeedback = new_transform_feedback; 280 driver->DeleteTransformFeedback = delete_transform_feedback; 281 driver->BeginTransformFeedback = begin_transform_feedback; 282 driver->EndTransformFeedback = end_transform_feedback; 283 driver->PauseTransformFeedback = pause_transform_feedback; 284 driver->ResumeTransformFeedback = resume_transform_feedback; 285 } 286 287 288 void 289 _mesa_init_transform_feedback_dispatch(struct _glapi_table *disp) 290 { 291 /* EXT_transform_feedback */ 292 SET_BeginTransformFeedbackEXT(disp, _mesa_BeginTransformFeedback); 293 SET_EndTransformFeedbackEXT(disp, _mesa_EndTransformFeedback); 294 SET_BindBufferOffsetEXT(disp, _mesa_BindBufferOffsetEXT); 295 SET_TransformFeedbackVaryingsEXT(disp, _mesa_TransformFeedbackVaryings); 296 SET_GetTransformFeedbackVaryingEXT(disp, _mesa_GetTransformFeedbackVarying); 297 /* ARB_transform_feedback2 */ 298 SET_BindTransformFeedback(disp, _mesa_BindTransformFeedback); 299 SET_DeleteTransformFeedbacks(disp, _mesa_DeleteTransformFeedbacks); 300 SET_GenTransformFeedbacks(disp, _mesa_GenTransformFeedbacks); 301 SET_IsTransformFeedback(disp, _mesa_IsTransformFeedback); 302 SET_PauseTransformFeedback(disp, _mesa_PauseTransformFeedback); 303 SET_ResumeTransformFeedback(disp, _mesa_ResumeTransformFeedback); 304 } 305 306 307 /** 308 ** Begin API functions 309 **/ 310 311 312 void GLAPIENTRY 313 _mesa_BeginTransformFeedback(GLenum mode) 314 { 315 struct gl_transform_feedback_object *obj; 316 struct gl_transform_feedback_info *info; 317 int i; 318 GET_CURRENT_CONTEXT(ctx); 319 320 obj = ctx->TransformFeedback.CurrentObject; 321 322 if (ctx->Shader.CurrentVertexProgram == NULL) { 323 _mesa_error(ctx, GL_INVALID_OPERATION, 324 "glBeginTransformFeedback(no program active)"); 325 return; 326 } 327 328 info = &ctx->Shader.CurrentVertexProgram->LinkedTransformFeedback; 329 330 if (info->NumOutputs == 0) { 331 _mesa_error(ctx, GL_INVALID_OPERATION, 332 "glBeginTransformFeedback(no varyings to record)"); 333 return; 334 } 335 336 switch (mode) { 337 case GL_POINTS: 338 case GL_LINES: 339 case GL_TRIANGLES: 340 /* legal */ 341 break; 342 default: 343 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)"); 344 return; 345 } 346 347 if (obj->Active) { 348 _mesa_error(ctx, GL_INVALID_OPERATION, 349 "glBeginTransformFeedback(already active)"); 350 return; 351 } 352 353 for (i = 0; i < info->NumBuffers; ++i) { 354 if (obj->BufferNames[i] == 0) { 355 _mesa_error(ctx, GL_INVALID_OPERATION, 356 "glBeginTransformFeedback(binding point %d does not have " 357 "a buffer object bound)", i); 358 return; 359 } 360 } 361 362 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); 363 obj->Active = GL_TRUE; 364 ctx->TransformFeedback.Mode = mode; 365 366 assert(ctx->Driver.BeginTransformFeedback); 367 ctx->Driver.BeginTransformFeedback(ctx, mode, obj); 368 } 369 370 371 void GLAPIENTRY 372 _mesa_EndTransformFeedback(void) 373 { 374 struct gl_transform_feedback_object *obj; 375 GET_CURRENT_CONTEXT(ctx); 376 377 obj = ctx->TransformFeedback.CurrentObject; 378 379 if (!obj->Active) { 380 _mesa_error(ctx, GL_INVALID_OPERATION, 381 "glEndTransformFeedback(not active)"); 382 return; 383 } 384 385 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); 386 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE; 387 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE; 388 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE; 389 390 assert(ctx->Driver.EndTransformFeedback); 391 ctx->Driver.EndTransformFeedback(ctx, obj); 392 } 393 394 395 /** 396 * Helper used by BindBufferRange() and BindBufferBase(). 397 */ 398 static void 399 bind_buffer_range(struct gl_context *ctx, GLuint index, 400 struct gl_buffer_object *bufObj, 401 GLintptr offset, GLsizeiptr size) 402 { 403 struct gl_transform_feedback_object *obj = 404 ctx->TransformFeedback.CurrentObject; 405 406 /* Note: no need to FLUSH_VERTICES or flag _NEW_TRANSFORM_FEEDBACK, because 407 * transform feedback buffers can't be changed while transform feedback is 408 * active. 409 */ 410 411 /* The general binding point */ 412 _mesa_reference_buffer_object(ctx, 413 &ctx->TransformFeedback.CurrentBuffer, 414 bufObj); 415 416 /* The per-attribute binding point */ 417 _mesa_reference_buffer_object(ctx, 418 &obj->Buffers[index], 419 bufObj); 420 421 obj->BufferNames[index] = bufObj->Name; 422 423 obj->Offset[index] = offset; 424 obj->Size[index] = size; 425 } 426 427 428 /** 429 * Specify a buffer object to receive vertex shader results. Plus, 430 * specify the starting offset to place the results, and max size. 431 * Called from the glBindBufferRange() function. 432 */ 433 void 434 _mesa_bind_buffer_range_transform_feedback(struct gl_context *ctx, 435 GLuint index, 436 struct gl_buffer_object *bufObj, 437 GLintptr offset, 438 GLsizeiptr size) 439 { 440 struct gl_transform_feedback_object *obj; 441 442 obj = ctx->TransformFeedback.CurrentObject; 443 444 if (obj->Active) { 445 _mesa_error(ctx, GL_INVALID_OPERATION, 446 "glBindBufferRange(transform feedback active)"); 447 return; 448 } 449 450 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 451 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(index=%d)", index); 452 return; 453 } 454 455 if (size & 0x3) { 456 /* must a multiple of four */ 457 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferRange(size=%d)", (int) size); 458 return; 459 } 460 461 if (offset & 0x3) { 462 /* must be multiple of four */ 463 _mesa_error(ctx, GL_INVALID_VALUE, 464 "glBindBufferRange(offset=%d)", (int) offset); 465 return; 466 } 467 468 bind_buffer_range(ctx, index, bufObj, offset, size); 469 } 470 471 472 /** 473 * Specify a buffer object to receive vertex shader results. 474 * As above, but start at offset = 0. 475 * Called from the glBindBufferBase() function. 476 */ 477 void 478 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx, 479 GLuint index, 480 struct gl_buffer_object *bufObj) 481 { 482 struct gl_transform_feedback_object *obj; 483 GLsizeiptr size; 484 485 obj = ctx->TransformFeedback.CurrentObject; 486 487 if (obj->Active) { 488 _mesa_error(ctx, GL_INVALID_OPERATION, 489 "glBindBufferBase(transform feedback active)"); 490 return; 491 } 492 493 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 494 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index); 495 return; 496 } 497 498 /* default size is the buffer size rounded down to nearest 499 * multiple of four. 500 */ 501 size = bufObj->Size & ~0x3; 502 503 bind_buffer_range(ctx, index, bufObj, 0, size); 504 } 505 506 507 /** 508 * Specify a buffer object to receive vertex shader results, plus the 509 * offset in the buffer to start placing results. 510 * This function is part of GL_EXT_transform_feedback, but not GL3. 511 */ 512 void GLAPIENTRY 513 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, 514 GLintptr offset) 515 { 516 struct gl_transform_feedback_object *obj; 517 struct gl_buffer_object *bufObj; 518 GET_CURRENT_CONTEXT(ctx); 519 GLsizeiptr size; 520 521 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 522 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)"); 523 return; 524 } 525 526 obj = ctx->TransformFeedback.CurrentObject; 527 528 if (obj->Active) { 529 _mesa_error(ctx, GL_INVALID_OPERATION, 530 "glBindBufferOffsetEXT(transform feedback active)"); 531 return; 532 } 533 534 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 535 _mesa_error(ctx, GL_INVALID_VALUE, 536 "glBindBufferOffsetEXT(index=%d)", index); 537 return; 538 } 539 540 if (offset & 0x3) { 541 /* must be multiple of four */ 542 _mesa_error(ctx, GL_INVALID_VALUE, 543 "glBindBufferOffsetEXT(offset=%d)", (int) offset); 544 return; 545 } 546 547 if (buffer == 0) { 548 bufObj = ctx->Shared->NullBufferObj; 549 } else { 550 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 551 } 552 553 if (!bufObj) { 554 _mesa_error(ctx, GL_INVALID_OPERATION, 555 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); 556 return; 557 } 558 559 /* default size is the buffer size rounded down to nearest 560 * multiple of four. 561 */ 562 size = (bufObj->Size - offset) & ~0x3; 563 564 bind_buffer_range(ctx, index, bufObj, offset, size); 565 } 566 567 568 /** 569 * This function specifies the vertex shader outputs to be written 570 * to the feedback buffer(s), and in what order. 571 */ 572 void GLAPIENTRY 573 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, 574 const GLchar **varyings, GLenum bufferMode) 575 { 576 struct gl_shader_program *shProg; 577 GLuint i; 578 GET_CURRENT_CONTEXT(ctx); 579 580 switch (bufferMode) { 581 case GL_INTERLEAVED_ATTRIBS: 582 break; 583 case GL_SEPARATE_ATTRIBS: 584 break; 585 default: 586 _mesa_error(ctx, GL_INVALID_ENUM, 587 "glTransformFeedbackVaryings(bufferMode)"); 588 return; 589 } 590 591 if (count < 0 || 592 (bufferMode == GL_SEPARATE_ATTRIBS && 593 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) { 594 _mesa_error(ctx, GL_INVALID_VALUE, 595 "glTransformFeedbackVaryings(count=%d)", count); 596 return; 597 } 598 599 shProg = _mesa_lookup_shader_program(ctx, program); 600 if (!shProg) { 601 _mesa_error(ctx, GL_INVALID_VALUE, 602 "glTransformFeedbackVaryings(program=%u)", program); 603 return; 604 } 605 606 if (ctx->Extensions.ARB_transform_feedback3) { 607 if (bufferMode == GL_INTERLEAVED_ATTRIBS) { 608 unsigned buffers = 1; 609 610 for (i = 0; i < count; i++) { 611 if (strcmp(varyings[i], "gl_NextBuffer") == 0) 612 buffers++; 613 } 614 615 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) { 616 _mesa_error(ctx, GL_INVALID_OPERATION, 617 "glTransformFeedbackVaryings(too many gl_NextBuffer " 618 "occurences)"); 619 return; 620 } 621 } else { 622 for (i = 0; i < count; i++) { 623 if (strcmp(varyings[i], "gl_NextBuffer") == 0 || 624 strcmp(varyings[i], "gl_SkipComponents1") == 0 || 625 strcmp(varyings[i], "gl_SkipComponents2") == 0 || 626 strcmp(varyings[i], "gl_SkipComponents3") == 0 || 627 strcmp(varyings[i], "gl_SkipComponents4") == 0) { 628 _mesa_error(ctx, GL_INVALID_OPERATION, 629 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS," 630 "varying=%s)", 631 varyings[i]); 632 return; 633 } 634 } 635 } 636 } 637 638 /* free existing varyings, if any */ 639 for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) { 640 free(shProg->TransformFeedback.VaryingNames[i]); 641 } 642 free(shProg->TransformFeedback.VaryingNames); 643 644 /* allocate new memory for varying names */ 645 shProg->TransformFeedback.VaryingNames = 646 (GLchar **) malloc(count * sizeof(GLchar *)); 647 648 if (!shProg->TransformFeedback.VaryingNames) { 649 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); 650 return; 651 } 652 653 /* Save the new names and the count */ 654 for (i = 0; i < (GLuint) count; i++) { 655 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]); 656 } 657 shProg->TransformFeedback.NumVarying = count; 658 659 shProg->TransformFeedback.BufferMode = bufferMode; 660 661 /* No need to set _NEW_TRANSFORM_FEEDBACK (or invoke FLUSH_VERTICES) since 662 * the varyings won't be used until shader link time. 663 */ 664 } 665 666 667 /** 668 * Get info about the vertex shader's outputs which are to be written 669 * to the feedback buffer(s). 670 */ 671 void GLAPIENTRY 672 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, 673 GLsizei bufSize, GLsizei *length, 674 GLsizei *size, GLenum *type, GLchar *name) 675 { 676 const struct gl_shader_program *shProg; 677 const struct gl_transform_feedback_info *linked_xfb_info; 678 GET_CURRENT_CONTEXT(ctx); 679 680 shProg = _mesa_lookup_shader_program(ctx, program); 681 if (!shProg) { 682 _mesa_error(ctx, GL_INVALID_VALUE, 683 "glGetTransformFeedbackVaryings(program=%u)", program); 684 return; 685 } 686 687 linked_xfb_info = &shProg->LinkedTransformFeedback; 688 if (index >= linked_xfb_info->NumVarying) { 689 _mesa_error(ctx, GL_INVALID_VALUE, 690 "glGetTransformFeedbackVaryings(index=%u)", index); 691 return; 692 } 693 694 /* return the varying's name and length */ 695 _mesa_copy_string(name, bufSize, length, 696 linked_xfb_info->Varyings[index].Name); 697 698 /* return the datatype and value's size (in datatype units) */ 699 if (type) 700 *type = linked_xfb_info->Varyings[index].Type; 701 if (size) 702 *size = linked_xfb_info->Varyings[index].Size; 703 } 704 705 706 707 struct gl_transform_feedback_object * 708 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) 709 { 710 if (name == 0) { 711 return ctx->TransformFeedback.DefaultObject; 712 } 713 else 714 return (struct gl_transform_feedback_object *) 715 _mesa_HashLookup(ctx->TransformFeedback.Objects, name); 716 } 717 718 719 /** 720 * Create new transform feedback objects. Transform feedback objects 721 * encapsulate the state related to transform feedback to allow quickly 722 * switching state (and drawing the results, below). 723 * Part of GL_ARB_transform_feedback2. 724 */ 725 void GLAPIENTRY 726 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) 727 { 728 GLuint first; 729 GET_CURRENT_CONTEXT(ctx); 730 731 ASSERT_OUTSIDE_BEGIN_END(ctx); 732 733 if (n < 0) { 734 _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)"); 735 return; 736 } 737 738 if (!names) 739 return; 740 741 /* we don't need contiguous IDs, but this might be faster */ 742 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n); 743 if (first) { 744 GLsizei i; 745 for (i = 0; i < n; i++) { 746 struct gl_transform_feedback_object *obj 747 = ctx->Driver.NewTransformFeedback(ctx, first + i); 748 if (!obj) { 749 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); 750 return; 751 } 752 names[i] = first + i; 753 _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj); 754 } 755 } 756 else { 757 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); 758 } 759 } 760 761 762 /** 763 * Is the given ID a transform feedback object? 764 * Part of GL_ARB_transform_feedback2. 765 */ 766 GLboolean GLAPIENTRY 767 _mesa_IsTransformFeedback(GLuint name) 768 { 769 GET_CURRENT_CONTEXT(ctx); 770 771 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 772 773 if (name && _mesa_lookup_transform_feedback_object(ctx, name)) 774 return GL_TRUE; 775 else 776 return GL_FALSE; 777 } 778 779 780 /** 781 * Bind the given transform feedback object. 782 * Part of GL_ARB_transform_feedback2. 783 */ 784 void GLAPIENTRY 785 _mesa_BindTransformFeedback(GLenum target, GLuint name) 786 { 787 struct gl_transform_feedback_object *obj; 788 GET_CURRENT_CONTEXT(ctx); 789 790 if (target != GL_TRANSFORM_FEEDBACK) { 791 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); 792 return; 793 } 794 795 if (ctx->TransformFeedback.CurrentObject->Active && 796 !ctx->TransformFeedback.CurrentObject->Paused) { 797 _mesa_error(ctx, GL_INVALID_OPERATION, 798 "glBindTransformFeedback(transform is active, or not paused)"); 799 return; 800 } 801 802 obj = _mesa_lookup_transform_feedback_object(ctx, name); 803 if (!obj) { 804 _mesa_error(ctx, GL_INVALID_OPERATION, 805 "glBindTransformFeedback(name=%u)", name); 806 return; 807 } 808 809 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 810 obj); 811 } 812 813 814 /** 815 * Delete the given transform feedback objects. 816 * Part of GL_ARB_transform_feedback2. 817 */ 818 void GLAPIENTRY 819 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) 820 { 821 GLint i; 822 GET_CURRENT_CONTEXT(ctx); 823 824 ASSERT_OUTSIDE_BEGIN_END(ctx); 825 826 if (n < 0) { 827 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); 828 return; 829 } 830 831 if (!names) 832 return; 833 834 for (i = 0; i < n; i++) { 835 if (names[i] > 0) { 836 struct gl_transform_feedback_object *obj 837 = _mesa_lookup_transform_feedback_object(ctx, names[i]); 838 if (obj) { 839 if (obj->Active) { 840 _mesa_error(ctx, GL_INVALID_OPERATION, 841 "glDeleteTransformFeedbacks(object %u is active)", 842 names[i]); 843 return; 844 } 845 _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]); 846 /* unref, but object may not be deleted until later */ 847 reference_transform_feedback_object(&obj, NULL); 848 } 849 } 850 } 851 } 852 853 854 /** 855 * Pause transform feedback. 856 * Part of GL_ARB_transform_feedback2. 857 */ 858 void GLAPIENTRY 859 _mesa_PauseTransformFeedback(void) 860 { 861 struct gl_transform_feedback_object *obj; 862 GET_CURRENT_CONTEXT(ctx); 863 864 obj = ctx->TransformFeedback.CurrentObject; 865 866 if (!obj->Active || obj->Paused) { 867 _mesa_error(ctx, GL_INVALID_OPERATION, 868 "glPauseTransformFeedback(feedback not active or already paused)"); 869 return; 870 } 871 872 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); 873 obj->Paused = GL_TRUE; 874 875 assert(ctx->Driver.PauseTransformFeedback); 876 ctx->Driver.PauseTransformFeedback(ctx, obj); 877 } 878 879 880 /** 881 * Resume transform feedback. 882 * Part of GL_ARB_transform_feedback2. 883 */ 884 void GLAPIENTRY 885 _mesa_ResumeTransformFeedback(void) 886 { 887 struct gl_transform_feedback_object *obj; 888 GET_CURRENT_CONTEXT(ctx); 889 890 obj = ctx->TransformFeedback.CurrentObject; 891 892 if (!obj->Active || !obj->Paused) { 893 _mesa_error(ctx, GL_INVALID_OPERATION, 894 "glResumeTransformFeedback(feedback not active or not paused)"); 895 return; 896 } 897 898 FLUSH_VERTICES(ctx, _NEW_TRANSFORM_FEEDBACK); 899 obj->Paused = GL_FALSE; 900 901 assert(ctx->Driver.ResumeTransformFeedback); 902 ctx->Driver.ResumeTransformFeedback(ctx, obj); 903 } 904 905 #endif /* FEATURE_EXT_transform_feedback */ 906