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