transformfeedback.c revision f9995b30756140724f41daf963fa06167912be7f
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(struct gl_context *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(struct gl_context *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(struct gl_context *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 struct gl_context *ctx = (struct gl_context *) 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(struct gl_context *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(struct gl_context *ctx, GLuint name); 204 205static void 206delete_transform_feedback(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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(struct gl_context *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)", (int) 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)", (int) 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 %d > buffer size %d)", 479 (int) (offset + size), (int) (bufObj->Size)); 480 return; 481 } 482 483 bind_buffer_range(ctx, index, bufObj, offset, size); 484} 485 486 487/** 488 * Specify a buffer object to receive vertex shader results. 489 * As above, but start at offset = 0. 490 */ 491void GLAPIENTRY 492_mesa_BindBufferBase(GLenum target, GLuint index, GLuint buffer) 493{ 494 struct gl_transform_feedback_object *obj; 495 struct gl_buffer_object *bufObj; 496 GLsizeiptr size; 497 GET_CURRENT_CONTEXT(ctx); 498 499 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 500 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferBase(target)"); 501 return; 502 } 503 504 obj = ctx->TransformFeedback.CurrentObject; 505 506 if (obj->Active) { 507 _mesa_error(ctx, GL_INVALID_OPERATION, 508 "glBindBufferRange(transform feedback active)"); 509 return; 510 } 511 512 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) { 513 _mesa_error(ctx, GL_INVALID_VALUE, "glBindBufferBase(index=%d)", index); 514 return; 515 } 516 517 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 518 if (!bufObj) { 519 _mesa_error(ctx, GL_INVALID_OPERATION, 520 "glBindBufferBase(invalid buffer=%u)", buffer); 521 return; 522 } 523 524 /* default size is the buffer size rounded down to nearest 525 * multiple of four. 526 */ 527 size = bufObj->Size & ~0x3; 528 529 bind_buffer_range(ctx, index, bufObj, 0, size); 530} 531 532 533/** 534 * Specify a buffer object to receive vertex shader results, plus the 535 * offset in the buffer to start placing results. 536 * This function is part of GL_EXT_transform_feedback, but not GL3. 537 */ 538void GLAPIENTRY 539_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, 540 GLintptr offset) 541{ 542 struct gl_transform_feedback_object *obj; 543 struct gl_buffer_object *bufObj; 544 GET_CURRENT_CONTEXT(ctx); 545 GLsizeiptr size; 546 547 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 548 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)"); 549 return; 550 } 551 552 obj = ctx->TransformFeedback.CurrentObject; 553 554 if (obj->Active) { 555 _mesa_error(ctx, GL_INVALID_OPERATION, 556 "glBindBufferRange(transform feedback active)"); 557 return; 558 } 559 560 if (index >= ctx->Const.MaxTransformFeedbackSeparateAttribs) { 561 _mesa_error(ctx, GL_INVALID_VALUE, 562 "glBindBufferOffsetEXT(index=%d)", index); 563 return; 564 } 565 566 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 567 if (!bufObj) { 568 _mesa_error(ctx, GL_INVALID_OPERATION, 569 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); 570 return; 571 } 572 573 /* default size is the buffer size rounded down to nearest 574 * multiple of four. 575 */ 576 size = (bufObj->Size - offset) & ~0x3; 577 578 bind_buffer_range(ctx, index, bufObj, offset, size); 579} 580 581 582/** 583 * This function specifies the vertex shader outputs to be written 584 * to the feedback buffer(s), and in what order. 585 */ 586void GLAPIENTRY 587_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, 588 const GLchar **varyings, GLenum bufferMode) 589{ 590 struct gl_shader_program *shProg; 591 GLuint i; 592 GET_CURRENT_CONTEXT(ctx); 593 594 switch (bufferMode) { 595 case GL_INTERLEAVED_ATTRIBS: 596 break; 597 case GL_SEPARATE_ATTRIBS: 598 break; 599 default: 600 _mesa_error(ctx, GL_INVALID_ENUM, 601 "glTransformFeedbackVaryings(bufferMode)"); 602 return; 603 } 604 605 if (count < 0 || count > ctx->Const.MaxTransformFeedbackSeparateAttribs) { 606 _mesa_error(ctx, GL_INVALID_VALUE, 607 "glTransformFeedbackVaryings(count=%d)", count); 608 return; 609 } 610 611 shProg = _mesa_lookup_shader_program(ctx, program); 612 if (!shProg) { 613 _mesa_error(ctx, GL_INVALID_VALUE, 614 "glTransformFeedbackVaryings(program=%u)", program); 615 return; 616 } 617 618 /* free existing varyings, if any */ 619 for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) { 620 free(shProg->TransformFeedback.VaryingNames[i]); 621 } 622 free(shProg->TransformFeedback.VaryingNames); 623 624 /* allocate new memory for varying names */ 625 shProg->TransformFeedback.VaryingNames = 626 (GLchar **) malloc(count * sizeof(GLchar *)); 627 628 if (!shProg->TransformFeedback.VaryingNames) { 629 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); 630 return; 631 } 632 633 /* Save the new names and the count */ 634 for (i = 0; i < (GLuint) count; i++) { 635 shProg->TransformFeedback.VaryingNames[i] = _mesa_strdup(varyings[i]); 636 } 637 shProg->TransformFeedback.NumVarying = count; 638 639 shProg->TransformFeedback.BufferMode = bufferMode; 640 641 /* The varyings won't be used until shader link time */ 642} 643 644 645/** 646 * Get info about the vertex shader's outputs which are to be written 647 * to the feedback buffer(s). 648 */ 649void GLAPIENTRY 650_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, 651 GLsizei bufSize, GLsizei *length, 652 GLsizei *size, GLenum *type, GLchar *name) 653{ 654 const struct gl_shader_program *shProg; 655 const GLchar *varyingName; 656 GLint v; 657 GET_CURRENT_CONTEXT(ctx); 658 659 shProg = _mesa_lookup_shader_program(ctx, program); 660 if (!shProg) { 661 _mesa_error(ctx, GL_INVALID_VALUE, 662 "glGetTransformFeedbackVaryings(program=%u)", program); 663 return; 664 } 665 666 if (index >= shProg->TransformFeedback.NumVarying) { 667 _mesa_error(ctx, GL_INVALID_VALUE, 668 "glGetTransformFeedbackVaryings(index=%u)", index); 669 return; 670 } 671 672 varyingName = shProg->TransformFeedback.VaryingNames[index]; 673 674 v = _mesa_lookup_parameter_index(shProg->Varying, -1, varyingName); 675 if (v >= 0) { 676 struct gl_program_parameter *param = &shProg->Varying->Parameters[v]; 677 678 /* return the varying's name and length */ 679 _mesa_copy_string(name, bufSize, length, varyingName); 680 681 /* return the datatype and value's size (in datatype units) */ 682 if (type) 683 *type = param->DataType; 684 if (size) 685 *size = param->Size; 686 } 687 else { 688 name[0] = 0; 689 if (length) 690 *length = 0; 691 if (type) 692 *type = 0; 693 if (size) 694 *size = 0; 695 } 696} 697 698 699 700static struct gl_transform_feedback_object * 701lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) 702{ 703 if (name == 0) { 704 return ctx->TransformFeedback.DefaultObject; 705 } 706 else 707 return (struct gl_transform_feedback_object *) 708 _mesa_HashLookup(ctx->TransformFeedback.Objects, name); 709} 710 711 712/** 713 * Create new transform feedback objects. Transform feedback objects 714 * encapsulate the state related to transform feedback to allow quickly 715 * switching state (and drawing the results, below). 716 * Part of GL_ARB_transform_feedback2. 717 */ 718void GLAPIENTRY 719_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) 720{ 721 GLuint first; 722 GET_CURRENT_CONTEXT(ctx); 723 724 ASSERT_OUTSIDE_BEGIN_END(ctx); 725 726 if (n < 0) { 727 _mesa_error(ctx, GL_INVALID_VALUE, "glGenTransformFeedbacks(n < 0)"); 728 return; 729 } 730 731 if (!names) 732 return; 733 734 /* we don't need contiguous IDs, but this might be faster */ 735 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n); 736 if (first) { 737 GLsizei i; 738 for (i = 0; i < n; i++) { 739 struct gl_transform_feedback_object *obj 740 = ctx->Driver.NewTransformFeedback(ctx, first + i); 741 if (!obj) { 742 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); 743 return; 744 } 745 names[i] = first + i; 746 _mesa_HashInsert(ctx->TransformFeedback.Objects, first + i, obj); 747 } 748 } 749 else { 750 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTransformFeedbacks"); 751 } 752} 753 754 755/** 756 * Is the given ID a transform feedback object? 757 * Part of GL_ARB_transform_feedback2. 758 */ 759GLboolean GLAPIENTRY 760_mesa_IsTransformFeedback(GLuint name) 761{ 762 GET_CURRENT_CONTEXT(ctx); 763 764 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 765 766 if (name && lookup_transform_feedback_object(ctx, name)) 767 return GL_TRUE; 768 else 769 return GL_FALSE; 770} 771 772 773/** 774 * Bind the given transform feedback object. 775 * Part of GL_ARB_transform_feedback2. 776 */ 777void GLAPIENTRY 778_mesa_BindTransformFeedback(GLenum target, GLuint name) 779{ 780 struct gl_transform_feedback_object *obj; 781 GET_CURRENT_CONTEXT(ctx); 782 783 if (target != GL_TRANSFORM_FEEDBACK) { 784 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); 785 return; 786 } 787 788 if (ctx->TransformFeedback.CurrentObject->Active && 789 !ctx->TransformFeedback.CurrentObject->Paused) { 790 _mesa_error(ctx, GL_INVALID_OPERATION, 791 "glBindTransformFeedback(transform is active, or not paused)"); 792 return; 793 } 794 795 obj = lookup_transform_feedback_object(ctx, name); 796 if (!obj) { 797 _mesa_error(ctx, GL_INVALID_OPERATION, 798 "glBindTransformFeedback(name=%u)", name); 799 return; 800 } 801 802 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 803 obj); 804} 805 806 807/** 808 * Delete the given transform feedback objects. 809 * Part of GL_ARB_transform_feedback2. 810 */ 811void GLAPIENTRY 812_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) 813{ 814 GLint i; 815 GET_CURRENT_CONTEXT(ctx); 816 817 ASSERT_OUTSIDE_BEGIN_END(ctx); 818 819 if (n < 0) { 820 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); 821 return; 822 } 823 824 if (!names) 825 return; 826 827 for (i = 0; i < n; i++) { 828 if (names[i] > 0) { 829 struct gl_transform_feedback_object *obj 830 = lookup_transform_feedback_object(ctx, names[i]); 831 if (obj) { 832 if (obj->Active) { 833 _mesa_error(ctx, GL_INVALID_OPERATION, 834 "glDeleteTransformFeedbacks(object %u is active)", 835 names[i]); 836 return; 837 } 838 _mesa_HashRemove(ctx->TransformFeedback.Objects, names[i]); 839 /* unref, but object may not be deleted until later */ 840 reference_transform_feedback_object(&obj, NULL); 841 } 842 } 843 } 844} 845 846 847/** 848 * Pause transform feedback. 849 * Part of GL_ARB_transform_feedback2. 850 */ 851void GLAPIENTRY 852_mesa_PauseTransformFeedback(void) 853{ 854 struct gl_transform_feedback_object *obj; 855 GET_CURRENT_CONTEXT(ctx); 856 857 obj = ctx->TransformFeedback.CurrentObject; 858 859 if (!obj->Active || obj->Paused) { 860 _mesa_error(ctx, GL_INVALID_OPERATION, 861 "glPauseTransformFeedback(feedback not active or already paused)"); 862 return; 863 } 864 865 obj->Paused = GL_TRUE; 866 867 assert(ctx->Driver.PauseTransformFeedback); 868 ctx->Driver.PauseTransformFeedback(ctx, obj); 869} 870 871 872/** 873 * Resume transform feedback. 874 * Part of GL_ARB_transform_feedback2. 875 */ 876void GLAPIENTRY 877_mesa_ResumeTransformFeedback(void) 878{ 879 struct gl_transform_feedback_object *obj; 880 GET_CURRENT_CONTEXT(ctx); 881 882 obj = ctx->TransformFeedback.CurrentObject; 883 884 if (!obj->Active || !obj->Paused) { 885 _mesa_error(ctx, GL_INVALID_OPERATION, 886 "glPauseTransformFeedback(feedback not active or not paused)"); 887 return; 888 } 889 890 obj->Paused = GL_FALSE; 891 892 assert(ctx->Driver.ResumeTransformFeedback); 893 ctx->Driver.ResumeTransformFeedback(ctx, obj); 894} 895 896 897/** 898 * Draw the vertex data in a transform feedback object. 899 * \param mode GL_POINTS, GL_LINES, GL_TRIANGLE_STRIP, etc. 900 * \param name the transform feedback object 901 * The number of vertices comes from the transform feedback object. 902 * User still has to setup of the vertex attribute info with 903 * glVertexPointer, glColorPointer, etc. 904 * Part of GL_ARB_transform_feedback2. 905 */ 906void GLAPIENTRY 907_mesa_DrawTransformFeedback(GLenum mode, GLuint name) 908{ 909 GET_CURRENT_CONTEXT(ctx); 910 struct gl_transform_feedback_object *obj = 911 lookup_transform_feedback_object(ctx, name); 912 913 if (mode > GL_POLYGON) { 914 _mesa_error(ctx, GL_INVALID_ENUM, 915 "glDrawTransformFeedback(mode=0x%x)", mode); 916 return; 917 } 918 if (!obj) { 919 _mesa_error(ctx, GL_INVALID_VALUE, 920 "glDrawTransformFeedback(name = %u)", name); 921 return; 922 } 923 924 /* XXX check if EndTransformFeedback has never been called while 925 * the object was bound 926 */ 927 928 assert(ctx->Driver.DrawTransformFeedback); 929 ctx->Driver.DrawTransformFeedback(ctx, mode, obj); 930} 931 932 933/* 934XXX misc to do: 935 936glGet*() for 937 938GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 939GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 940GL_TRANSFORM_FEEDBACK_BINDING 941*/ 942 943 944#endif /* FEATURE_EXT_transform_feedback */ 945