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