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