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