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