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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26/* 27 * Transform feedback support. 28 * 29 * Authors: 30 * Brian Paul 31 */ 32 33 34#include "buffers.h" 35#include "context.h" 36#include "hash.h" 37#include "macros.h" 38#include "mtypes.h" 39#include "transformfeedback.h" 40#include "shaderapi.h" 41#include "shaderobj.h" 42 43#include "program/program.h" 44#include "program/prog_parameter.h" 45 46struct using_program_tuple 47{ 48 struct gl_program *prog; 49 bool found; 50}; 51 52static void 53active_xfb_object_references_program(GLuint key, void *data, void *user_data) 54{ 55 struct using_program_tuple *callback_data = user_data; 56 struct gl_transform_feedback_object *obj = data; 57 if (obj->Active && obj->program == callback_data->prog) 58 callback_data->found = true; 59} 60 61/** 62 * Return true if any active transform feedback object is using a program. 63 */ 64bool 65_mesa_transform_feedback_is_using_program(struct gl_context *ctx, 66 struct gl_shader_program *shProg) 67{ 68 if (!shProg->last_vert_prog) 69 return false; 70 71 struct using_program_tuple callback_data; 72 callback_data.found = false; 73 callback_data.prog = shProg->last_vert_prog; 74 75 _mesa_HashWalkLocked(ctx->TransformFeedback.Objects, 76 active_xfb_object_references_program, &callback_data); 77 78 /* Also check DefaultObject, as it's not in the Objects hash table. */ 79 active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject, 80 &callback_data); 81 82 return callback_data.found; 83} 84 85/** 86 * Do reference counting of transform feedback buffers. 87 */ 88static void 89reference_transform_feedback_object(struct gl_transform_feedback_object **ptr, 90 struct gl_transform_feedback_object *obj) 91{ 92 if (*ptr == obj) 93 return; 94 95 if (*ptr) { 96 /* Unreference the old object */ 97 struct gl_transform_feedback_object *oldObj = *ptr; 98 99 assert(oldObj->RefCount > 0); 100 oldObj->RefCount--; 101 102 if (oldObj->RefCount == 0) { 103 GET_CURRENT_CONTEXT(ctx); 104 if (ctx) 105 ctx->Driver.DeleteTransformFeedback(ctx, oldObj); 106 } 107 108 *ptr = NULL; 109 } 110 assert(!*ptr); 111 112 if (obj) { 113 assert(obj->RefCount > 0); 114 115 /* reference new object */ 116 obj->RefCount++; 117 obj->EverBound = GL_TRUE; 118 *ptr = obj; 119 } 120} 121 122 123/** 124 * Per-context init for transform feedback. 125 */ 126void 127_mesa_init_transform_feedback(struct gl_context *ctx) 128{ 129 /* core mesa expects this, even a dummy one, to be available */ 130 assert(ctx->Driver.NewTransformFeedback); 131 132 ctx->TransformFeedback.DefaultObject = 133 ctx->Driver.NewTransformFeedback(ctx, 0); 134 135 assert(ctx->TransformFeedback.DefaultObject->RefCount == 1); 136 137 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 138 ctx->TransformFeedback.DefaultObject); 139 140 assert(ctx->TransformFeedback.DefaultObject->RefCount == 2); 141 142 ctx->TransformFeedback.Objects = _mesa_NewHashTable(); 143 144 _mesa_reference_buffer_object(ctx, 145 &ctx->TransformFeedback.CurrentBuffer, 146 ctx->Shared->NullBufferObj); 147} 148 149 150 151/** 152 * Callback for _mesa_HashDeleteAll(). 153 */ 154static void 155delete_cb(GLuint key, void *data, void *userData) 156{ 157 struct gl_context *ctx = (struct gl_context *) userData; 158 struct gl_transform_feedback_object *obj = 159 (struct gl_transform_feedback_object *) data; 160 161 ctx->Driver.DeleteTransformFeedback(ctx, obj); 162} 163 164 165/** 166 * Per-context free/clean-up for transform feedback. 167 */ 168void 169_mesa_free_transform_feedback(struct gl_context *ctx) 170{ 171 /* core mesa expects this, even a dummy one, to be available */ 172 assert(ctx->Driver.NewTransformFeedback); 173 174 _mesa_reference_buffer_object(ctx, 175 &ctx->TransformFeedback.CurrentBuffer, 176 NULL); 177 178 /* Delete all feedback objects */ 179 _mesa_HashDeleteAll(ctx->TransformFeedback.Objects, delete_cb, ctx); 180 _mesa_DeleteHashTable(ctx->TransformFeedback.Objects); 181 182 /* Delete the default feedback object */ 183 assert(ctx->Driver.DeleteTransformFeedback); 184 ctx->Driver.DeleteTransformFeedback(ctx, 185 ctx->TransformFeedback.DefaultObject); 186 187 ctx->TransformFeedback.CurrentObject = NULL; 188} 189 190 191/** Initialize the fields of a gl_transform_feedback_object. */ 192void 193_mesa_init_transform_feedback_object(struct gl_transform_feedback_object *obj, 194 GLuint name) 195{ 196 obj->Name = name; 197 obj->RefCount = 1; 198 obj->EverBound = GL_FALSE; 199} 200 201 202/** Default fallback for ctx->Driver.NewTransformFeedback() */ 203static struct gl_transform_feedback_object * 204new_transform_feedback_fallback(struct gl_context *ctx, GLuint name) 205{ 206 struct gl_transform_feedback_object *obj; 207 208 obj = CALLOC_STRUCT(gl_transform_feedback_object); 209 if (!obj) 210 return NULL; 211 212 _mesa_init_transform_feedback_object(obj, name); 213 return obj; 214} 215 216/** Default fallback for ctx->Driver.DeleteTransformFeedback() */ 217static void 218delete_transform_feedback_fallback(struct gl_context *ctx, 219 struct gl_transform_feedback_object *obj) 220{ 221 GLuint i; 222 223 for (i = 0; i < ARRAY_SIZE(obj->Buffers); i++) { 224 _mesa_reference_buffer_object(ctx, &obj->Buffers[i], NULL); 225 } 226 227 free(obj->Label); 228 free(obj); 229} 230 231 232/** Default fallback for ctx->Driver.BeginTransformFeedback() */ 233static void 234begin_transform_feedback_fallback(struct gl_context *ctx, GLenum mode, 235 struct gl_transform_feedback_object *obj) 236{ 237 /* nop */ 238} 239 240/** Default fallback for ctx->Driver.EndTransformFeedback() */ 241static void 242end_transform_feedback_fallback(struct gl_context *ctx, 243 struct gl_transform_feedback_object *obj) 244{ 245 /* nop */ 246} 247 248/** Default fallback for ctx->Driver.PauseTransformFeedback() */ 249static void 250pause_transform_feedback_fallback(struct gl_context *ctx, 251 struct gl_transform_feedback_object *obj) 252{ 253 /* nop */ 254} 255 256/** Default fallback for ctx->Driver.ResumeTransformFeedback() */ 257static void 258resume_transform_feedback_fallback(struct gl_context *ctx, 259 struct gl_transform_feedback_object *obj) 260{ 261 /* nop */ 262} 263 264 265/** 266 * Plug in default device driver functions for transform feedback. 267 * Most drivers will override some/all of these. 268 */ 269void 270_mesa_init_transform_feedback_functions(struct dd_function_table *driver) 271{ 272 driver->NewTransformFeedback = new_transform_feedback_fallback; 273 driver->DeleteTransformFeedback = delete_transform_feedback_fallback; 274 driver->BeginTransformFeedback = begin_transform_feedback_fallback; 275 driver->EndTransformFeedback = end_transform_feedback_fallback; 276 driver->PauseTransformFeedback = pause_transform_feedback_fallback; 277 driver->ResumeTransformFeedback = resume_transform_feedback_fallback; 278} 279 280 281/** 282 * Fill in the correct Size value for each buffer in \c obj. 283 * 284 * From the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed 285 * Targets"): 286 * 287 * BindBufferBase binds the entire buffer, even when the size of the buffer 288 * is changed after the binding is established. It is equivalent to calling 289 * BindBufferRange with offset zero, while size is determined by the size of 290 * the bound buffer at the time the binding is used. 291 * 292 * Regardless of the size specified with BindBufferRange, or indirectly with 293 * BindBufferBase, the GL will never read or write beyond the end of a bound 294 * buffer. In some cases this constraint may result in visibly different 295 * behavior when a buffer overflow would otherwise result, such as described 296 * for transform feedback operations in section 13.2.2. 297 */ 298static void 299compute_transform_feedback_buffer_sizes( 300 struct gl_transform_feedback_object *obj) 301{ 302 unsigned i = 0; 303 for (i = 0; i < MAX_FEEDBACK_BUFFERS; ++i) { 304 GLintptr offset = obj->Offset[i]; 305 GLsizeiptr buffer_size 306 = obj->Buffers[i] == NULL ? 0 : obj->Buffers[i]->Size; 307 GLsizeiptr available_space 308 = buffer_size <= offset ? 0 : buffer_size - offset; 309 GLsizeiptr computed_size; 310 if (obj->RequestedSize[i] == 0) { 311 /* No size was specified at the time the buffer was bound, so allow 312 * writing to all available space in the buffer. 313 */ 314 computed_size = available_space; 315 } else { 316 /* A size was specified at the time the buffer was bound, however 317 * it's possible that the buffer has shrunk since then. So only 318 * allow writing to the minimum of the specified size and the space 319 * available. 320 */ 321 computed_size = MIN2(available_space, obj->RequestedSize[i]); 322 } 323 324 /* Legal sizes must be multiples of four, so round down if necessary. */ 325 obj->Size[i] = computed_size & ~0x3; 326 } 327} 328 329 330/** 331 * Compute the maximum number of vertices that can be written to the currently 332 * enabled transform feedback buffers without overflowing any of them. 333 */ 334unsigned 335_mesa_compute_max_transform_feedback_vertices(struct gl_context *ctx, 336 const struct gl_transform_feedback_object *obj, 337 const struct gl_transform_feedback_info *info) 338{ 339 unsigned max_index = 0xffffffff; 340 unsigned i; 341 342 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) { 343 if ((info->ActiveBuffers >> i) & 1) { 344 unsigned stride = info->Buffers[i].Stride; 345 unsigned max_for_this_buffer; 346 347 /* Skip any inactive buffers, which have a stride of 0. */ 348 if (stride == 0) 349 continue; 350 351 max_for_this_buffer = obj->Size[i] / (4 * stride); 352 max_index = MIN2(max_index, max_for_this_buffer); 353 } 354 } 355 356 return max_index; 357} 358 359 360/** 361 ** Begin API functions 362 **/ 363 364 365/** 366 * Figure out which stage of the pipeline is the source of transform feedback 367 * data given the current context state, and return its gl_program. 368 * 369 * If no active program can generate transform feedback data (i.e. no vertex 370 * shader is active), returns NULL. 371 */ 372static struct gl_program * 373get_xfb_source(struct gl_context *ctx) 374{ 375 int i; 376 for (i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) { 377 if (ctx->_Shader->CurrentProgram[i] != NULL) 378 return ctx->_Shader->CurrentProgram[i]; 379 } 380 return NULL; 381} 382 383 384static ALWAYS_INLINE void 385begin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error) 386{ 387 struct gl_transform_feedback_object *obj; 388 struct gl_transform_feedback_info *info = NULL; 389 struct gl_program *source; 390 GLuint i; 391 unsigned vertices_per_prim; 392 393 obj = ctx->TransformFeedback.CurrentObject; 394 395 /* Figure out what pipeline stage is the source of data for transform 396 * feedback. 397 */ 398 source = get_xfb_source(ctx); 399 if (!no_error && source == NULL) { 400 _mesa_error(ctx, GL_INVALID_OPERATION, 401 "glBeginTransformFeedback(no program active)"); 402 return; 403 } 404 405 info = source->sh.LinkedTransformFeedback; 406 407 if (!no_error && info->NumOutputs == 0) { 408 _mesa_error(ctx, GL_INVALID_OPERATION, 409 "glBeginTransformFeedback(no varyings to record)"); 410 return; 411 } 412 413 switch (mode) { 414 case GL_POINTS: 415 vertices_per_prim = 1; 416 break; 417 case GL_LINES: 418 vertices_per_prim = 2; 419 break; 420 case GL_TRIANGLES: 421 vertices_per_prim = 3; 422 break; 423 default: 424 if (!no_error) { 425 _mesa_error(ctx, GL_INVALID_ENUM, "glBeginTransformFeedback(mode)"); 426 return; 427 } else { 428 /* Stop compiler warnings */ 429 unreachable("Error in API use when using KHR_no_error"); 430 } 431 } 432 433 if (!no_error) { 434 if (obj->Active) { 435 _mesa_error(ctx, GL_INVALID_OPERATION, 436 "glBeginTransformFeedback(already active)"); 437 return; 438 } 439 440 for (i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) { 441 if ((info->ActiveBuffers >> i) & 1) { 442 if (obj->BufferNames[i] == 0) { 443 _mesa_error(ctx, GL_INVALID_OPERATION, 444 "glBeginTransformFeedback(binding point %d does not " 445 "have a buffer object bound)", i); 446 return; 447 } 448 } 449 } 450 } 451 452 FLUSH_VERTICES(ctx, 0); 453 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 454 455 obj->Active = GL_TRUE; 456 ctx->TransformFeedback.Mode = mode; 457 458 compute_transform_feedback_buffer_sizes(obj); 459 460 if (_mesa_is_gles3(ctx)) { 461 /* In GLES3, we are required to track the usage of the transform 462 * feedback buffer and report INVALID_OPERATION if a draw call tries to 463 * exceed it. So compute the maximum number of vertices that we can 464 * write without overflowing any of the buffers currently being used for 465 * feedback. 466 */ 467 unsigned max_vertices 468 = _mesa_compute_max_transform_feedback_vertices(ctx, obj, info); 469 obj->GlesRemainingPrims = max_vertices / vertices_per_prim; 470 } 471 472 if (obj->program != source) { 473 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedbackProg; 474 _mesa_reference_program_(ctx, &obj->program, source); 475 obj->program = source; 476 } 477 478 assert(ctx->Driver.BeginTransformFeedback); 479 ctx->Driver.BeginTransformFeedback(ctx, mode, obj); 480} 481 482 483void GLAPIENTRY 484_mesa_BeginTransformFeedback_no_error(GLenum mode) 485{ 486 GET_CURRENT_CONTEXT(ctx); 487 begin_transform_feedback(ctx, mode, true); 488} 489 490 491void GLAPIENTRY 492_mesa_BeginTransformFeedback(GLenum mode) 493{ 494 GET_CURRENT_CONTEXT(ctx); 495 begin_transform_feedback(ctx, mode, false); 496} 497 498 499static void 500end_transform_feedback(struct gl_context *ctx, 501 struct gl_transform_feedback_object *obj) 502{ 503 FLUSH_VERTICES(ctx, 0); 504 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 505 506 assert(ctx->Driver.EndTransformFeedback); 507 ctx->Driver.EndTransformFeedback(ctx, obj); 508 509 _mesa_reference_program_(ctx, &obj->program, NULL); 510 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE; 511 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE; 512 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE; 513} 514 515 516void GLAPIENTRY 517_mesa_EndTransformFeedback_no_error(void) 518{ 519 GET_CURRENT_CONTEXT(ctx); 520 end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 521} 522 523 524void GLAPIENTRY 525_mesa_EndTransformFeedback(void) 526{ 527 struct gl_transform_feedback_object *obj; 528 GET_CURRENT_CONTEXT(ctx); 529 530 obj = ctx->TransformFeedback.CurrentObject; 531 532 if (!obj->Active) { 533 _mesa_error(ctx, GL_INVALID_OPERATION, 534 "glEndTransformFeedback(not active)"); 535 return; 536 } 537 538 end_transform_feedback(ctx, obj); 539} 540 541 542/** 543 * Helper used by BindBufferRange() and BindBufferBase(). 544 */ 545static void 546bind_buffer_range(struct gl_context *ctx, 547 struct gl_transform_feedback_object *obj, 548 GLuint index, 549 struct gl_buffer_object *bufObj, 550 GLintptr offset, GLsizeiptr size, 551 bool dsa) 552{ 553 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because 554 * transform feedback buffers can't be changed while transform feedback is 555 * active. 556 */ 557 558 if (!dsa) { 559 /* The general binding point */ 560 _mesa_reference_buffer_object(ctx, 561 &ctx->TransformFeedback.CurrentBuffer, 562 bufObj); 563 } 564 565 /* The per-attribute binding point */ 566 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size); 567} 568 569 570/** 571 * Validate the buffer object to receive transform feedback results. Plus, 572 * validate the starting offset to place the results, and max size. 573 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange 574 * functions. 575 */ 576bool 577_mesa_validate_buffer_range_xfb(struct gl_context *ctx, 578 struct gl_transform_feedback_object *obj, 579 GLuint index, struct gl_buffer_object *bufObj, 580 GLintptr offset, GLsizeiptr size, bool dsa) 581{ 582 const char *gl_methd_name; 583 if (dsa) 584 gl_methd_name = "glTransformFeedbackBufferRange"; 585 else 586 gl_methd_name = "glBindBufferRange"; 587 588 589 if (obj->Active) { 590 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)", 591 gl_methd_name); 592 return false; 593 } 594 595 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 596 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 597 * generated if index is greater than or equal to the number of binding 598 * points for transform feedback, as described in section 6.7.1." 599 */ 600 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 601 gl_methd_name, index); 602 return false; 603 } 604 605 if (size & 0x3) { 606 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 607 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of " 608 "four)", gl_methd_name, (int) size); 609 return false; 610 } 611 612 if (offset & 0x3) { 613 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 614 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple " 615 "of four)", gl_methd_name, (int) offset); 616 return false; 617 } 618 619 if (offset < 0) { 620 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 621 * generated by BindBufferRange if offset is negative." 622 * 623 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 624 * is generated by TransformFeedbackBufferRange if offset is negative." 625 */ 626 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)", 627 gl_methd_name, 628 (int) offset); 629 return false; 630 } 631 632 if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) { 633 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 634 * generated by BindBufferRange if buffer is non-zero and size is less 635 * than or equal to zero." 636 * 637 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 638 * is generated by TransformFeedbackBufferRange if size is less than or 639 * equal to zero." 640 */ 641 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)", 642 gl_methd_name, (int) size); 643 return false; 644 } 645 646 return true; 647} 648 649 650/** 651 * Specify a buffer object to receive transform feedback results. 652 * As above, but start at offset = 0. 653 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase() 654 * functions. 655 */ 656void 657_mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx, 658 struct gl_transform_feedback_object *obj, 659 GLuint index, 660 struct gl_buffer_object *bufObj, 661 bool dsa) 662{ 663 if (obj->Active) { 664 _mesa_error(ctx, GL_INVALID_OPERATION, 665 "%s(transform feedback active)", 666 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase"); 667 return; 668 } 669 670 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 671 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 672 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase", 673 index); 674 return; 675 } 676 677 bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa); 678} 679 680/** 681 * Wrapper around lookup_transform_feedback_object that throws 682 * GL_INVALID_OPERATION if id is not in the hash table. After calling 683 * _mesa_error, it returns NULL. 684 */ 685static struct gl_transform_feedback_object * 686lookup_transform_feedback_object_err(struct gl_context *ctx, 687 GLuint xfb, const char* func) 688{ 689 struct gl_transform_feedback_object *obj; 690 691 obj = _mesa_lookup_transform_feedback_object(ctx, xfb); 692 if (!obj) { 693 _mesa_error(ctx, GL_INVALID_OPERATION, 694 "%s(xfb=%u: non-generated object name)", func, xfb); 695 } 696 697 return obj; 698} 699 700/** 701 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id 702 * is not in the hash table. Specialised version for the 703 * transform-feedback-related functions. After calling _mesa_error, it 704 * returns NULL. 705 */ 706static struct gl_buffer_object * 707lookup_transform_feedback_bufferobj_err(struct gl_context *ctx, 708 GLuint buffer, const char* func) 709{ 710 struct gl_buffer_object *bufObj; 711 712 /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the 713 * name of an existing buffer object. 714 */ 715 if (buffer == 0) { 716 bufObj = ctx->Shared->NullBufferObj; 717 } else { 718 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 719 if (!bufObj) { 720 _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func, 721 buffer); 722 } 723 } 724 725 return bufObj; 726} 727 728void GLAPIENTRY 729_mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer) 730{ 731 GET_CURRENT_CONTEXT(ctx); 732 struct gl_transform_feedback_object *obj; 733 struct gl_buffer_object *bufObj; 734 735 obj = lookup_transform_feedback_object_err(ctx, xfb, 736 "glTransformFeedbackBufferBase"); 737 if (!obj) { 738 return; 739 } 740 741 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 742 "glTransformFeedbackBufferBase"); 743 if (!bufObj) { 744 return; 745 } 746 747 _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true); 748} 749 750void GLAPIENTRY 751_mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer, 752 GLintptr offset, GLsizeiptr size) 753{ 754 GET_CURRENT_CONTEXT(ctx); 755 struct gl_transform_feedback_object *obj; 756 struct gl_buffer_object *bufObj; 757 758 obj = lookup_transform_feedback_object_err(ctx, xfb, 759 "glTransformFeedbackBufferRange"); 760 if (!obj) { 761 return; 762 } 763 764 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 765 "glTransformFeedbackBufferRange"); 766 if (!bufObj) { 767 return; 768 } 769 770 if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset, 771 size, true)) 772 return; 773 774 /* The per-attribute binding point */ 775 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, 776 size); 777} 778 779/** 780 * Specify a buffer object to receive transform feedback results, plus the 781 * offset in the buffer to start placing results. 782 * This function is part of GL_EXT_transform_feedback, but not GL3. 783 */ 784static ALWAYS_INLINE void 785bind_buffer_offset(struct gl_context *ctx, 786 struct gl_transform_feedback_object *obj, GLuint index, 787 GLuint buffer, GLintptr offset, bool no_error) 788{ 789 struct gl_buffer_object *bufObj; 790 791 if (buffer == 0) { 792 bufObj = ctx->Shared->NullBufferObj; 793 } else { 794 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 795 if (!no_error && !bufObj) { 796 _mesa_error(ctx, GL_INVALID_OPERATION, 797 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); 798 return; 799 } 800 } 801 802 _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0); 803} 804 805 806void GLAPIENTRY 807_mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer, 808 GLintptr offset) 809{ 810 GET_CURRENT_CONTEXT(ctx); 811 bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer, 812 offset, true); 813} 814 815 816void GLAPIENTRY 817_mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, 818 GLintptr offset) 819{ 820 struct gl_transform_feedback_object *obj; 821 GET_CURRENT_CONTEXT(ctx); 822 823 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 824 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)"); 825 return; 826 } 827 828 obj = ctx->TransformFeedback.CurrentObject; 829 830 if (obj->Active) { 831 _mesa_error(ctx, GL_INVALID_OPERATION, 832 "glBindBufferOffsetEXT(transform feedback active)"); 833 return; 834 } 835 836 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 837 _mesa_error(ctx, GL_INVALID_VALUE, 838 "glBindBufferOffsetEXT(index=%d)", index); 839 return; 840 } 841 842 if (offset & 0x3) { 843 /* must be multiple of four */ 844 _mesa_error(ctx, GL_INVALID_VALUE, 845 "glBindBufferOffsetEXT(offset=%d)", (int) offset); 846 return; 847 } 848 849 bind_buffer_offset(ctx, obj, index, buffer, offset, false); 850} 851 852 853/** 854 * This function specifies the transform feedback outputs to be written 855 * to the feedback buffer(s), and in what order. 856 */ 857static ALWAYS_INLINE void 858transform_feedback_varyings(struct gl_context *ctx, 859 struct gl_shader_program *shProg, GLsizei count, 860 const GLchar *const *varyings, GLenum bufferMode) 861{ 862 GLint i; 863 864 /* free existing varyings, if any */ 865 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) { 866 free(shProg->TransformFeedback.VaryingNames[i]); 867 } 868 free(shProg->TransformFeedback.VaryingNames); 869 870 /* allocate new memory for varying names */ 871 shProg->TransformFeedback.VaryingNames = 872 malloc(count * sizeof(GLchar *)); 873 874 if (!shProg->TransformFeedback.VaryingNames) { 875 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); 876 return; 877 } 878 879 /* Save the new names and the count */ 880 for (i = 0; i < count; i++) { 881 shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]); 882 } 883 shProg->TransformFeedback.NumVarying = count; 884 885 shProg->TransformFeedback.BufferMode = bufferMode; 886 887 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since 888 * the varyings won't be used until shader link time. 889 */ 890} 891 892 893void GLAPIENTRY 894_mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count, 895 const GLchar *const *varyings, 896 GLenum bufferMode) 897{ 898 GET_CURRENT_CONTEXT(ctx); 899 900 struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); 901 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 902} 903 904void GLAPIENTRY 905_mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, 906 const GLchar * const *varyings, 907 GLenum bufferMode) 908{ 909 struct gl_shader_program *shProg; 910 GLint i; 911 GET_CURRENT_CONTEXT(ctx); 912 913 /* From the ARB_transform_feedback2 specification: 914 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings 915 * if the current transform feedback object is active, even if paused." 916 */ 917 if (ctx->TransformFeedback.CurrentObject->Active) { 918 _mesa_error(ctx, GL_INVALID_OPERATION, 919 "glTransformFeedbackVaryings(current object is active)"); 920 return; 921 } 922 923 switch (bufferMode) { 924 case GL_INTERLEAVED_ATTRIBS: 925 break; 926 case GL_SEPARATE_ATTRIBS: 927 break; 928 default: 929 _mesa_error(ctx, GL_INVALID_ENUM, 930 "glTransformFeedbackVaryings(bufferMode)"); 931 return; 932 } 933 934 if (count < 0 || 935 (bufferMode == GL_SEPARATE_ATTRIBS && 936 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) { 937 _mesa_error(ctx, GL_INVALID_VALUE, 938 "glTransformFeedbackVaryings(count=%d)", count); 939 return; 940 } 941 942 shProg = _mesa_lookup_shader_program_err(ctx, program, 943 "glTransformFeedbackVaryings"); 944 if (!shProg) 945 return; 946 947 if (ctx->Extensions.ARB_transform_feedback3) { 948 if (bufferMode == GL_INTERLEAVED_ATTRIBS) { 949 unsigned buffers = 1; 950 951 for (i = 0; i < count; i++) { 952 if (strcmp(varyings[i], "gl_NextBuffer") == 0) 953 buffers++; 954 } 955 956 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) { 957 _mesa_error(ctx, GL_INVALID_OPERATION, 958 "glTransformFeedbackVaryings(too many gl_NextBuffer " 959 "occurrences)"); 960 return; 961 } 962 } else { 963 for (i = 0; i < count; i++) { 964 if (strcmp(varyings[i], "gl_NextBuffer") == 0 || 965 strcmp(varyings[i], "gl_SkipComponents1") == 0 || 966 strcmp(varyings[i], "gl_SkipComponents2") == 0 || 967 strcmp(varyings[i], "gl_SkipComponents3") == 0 || 968 strcmp(varyings[i], "gl_SkipComponents4") == 0) { 969 _mesa_error(ctx, GL_INVALID_OPERATION, 970 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS," 971 "varying=%s)", 972 varyings[i]); 973 return; 974 } 975 } 976 } 977 } 978 979 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 980} 981 982 983/** 984 * Get info about the transform feedback outputs which are to be written 985 * to the feedback buffer(s). 986 */ 987void GLAPIENTRY 988_mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, 989 GLsizei bufSize, GLsizei *length, 990 GLsizei *size, GLenum *type, GLchar *name) 991{ 992 const struct gl_shader_program *shProg; 993 struct gl_program_resource *res; 994 GET_CURRENT_CONTEXT(ctx); 995 996 shProg = _mesa_lookup_shader_program_err(ctx, program, 997 "glGetTransformFeedbackVarying"); 998 if (!shProg) 999 return; 1000 1001 res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg, 1002 GL_TRANSFORM_FEEDBACK_VARYING, 1003 index); 1004 if (!res) { 1005 _mesa_error(ctx, GL_INVALID_VALUE, 1006 "glGetTransformFeedbackVarying(index=%u)", index); 1007 return; 1008 } 1009 1010 /* return the varying's name and length */ 1011 _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res)); 1012 1013 /* return the datatype and value's size (in datatype units) */ 1014 if (type) 1015 _mesa_program_resource_prop((struct gl_shader_program *) shProg, 1016 res, index, GL_TYPE, (GLint*) type, 1017 "glGetTransformFeedbackVarying"); 1018 if (size) 1019 _mesa_program_resource_prop((struct gl_shader_program *) shProg, 1020 res, index, GL_ARRAY_SIZE, (GLint*) size, 1021 "glGetTransformFeedbackVarying"); 1022} 1023 1024 1025 1026struct gl_transform_feedback_object * 1027_mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) 1028{ 1029 /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating 1030 * the default transform feedback object, or the name of an existing 1031 * transform feedback object." 1032 */ 1033 if (name == 0) { 1034 return ctx->TransformFeedback.DefaultObject; 1035 } 1036 else 1037 return (struct gl_transform_feedback_object *) 1038 _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name); 1039} 1040 1041static void 1042create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids, 1043 bool dsa) 1044{ 1045 GLuint first; 1046 const char* func; 1047 1048 if (dsa) 1049 func = "glCreateTransformFeedbacks"; 1050 else 1051 func = "glGenTransformFeedbacks"; 1052 1053 if (n < 0) { 1054 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 1055 return; 1056 } 1057 1058 if (!ids) 1059 return; 1060 1061 /* we don't need contiguous IDs, but this might be faster */ 1062 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n); 1063 if (first) { 1064 GLsizei i; 1065 for (i = 0; i < n; i++) { 1066 struct gl_transform_feedback_object *obj 1067 = ctx->Driver.NewTransformFeedback(ctx, first + i); 1068 if (!obj) { 1069 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 1070 return; 1071 } 1072 ids[i] = first + i; 1073 _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i, 1074 obj); 1075 if (dsa) { 1076 /* this is normally done at bind time in the non-dsa case */ 1077 obj->EverBound = GL_TRUE; 1078 } 1079 } 1080 } 1081 else { 1082 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 1083 } 1084} 1085 1086/** 1087 * Create new transform feedback objects. Transform feedback objects 1088 * encapsulate the state related to transform feedback to allow quickly 1089 * switching state (and drawing the results, below). 1090 * Part of GL_ARB_transform_feedback2. 1091 */ 1092void GLAPIENTRY 1093_mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) 1094{ 1095 GET_CURRENT_CONTEXT(ctx); 1096 1097 /* GenTransformFeedbacks should just reserve the object names that a 1098 * subsequent call to BindTransformFeedback should actively create. For 1099 * the sake of simplicity, we reserve the names and create the objects 1100 * straight away. 1101 */ 1102 1103 create_transform_feedbacks(ctx, n, names, false); 1104} 1105 1106/** 1107 * Create new transform feedback objects. Transform feedback objects 1108 * encapsulate the state related to transform feedback to allow quickly 1109 * switching state (and drawing the results, below). 1110 * Part of GL_ARB_direct_state_access. 1111 */ 1112void GLAPIENTRY 1113_mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names) 1114{ 1115 GET_CURRENT_CONTEXT(ctx); 1116 1117 create_transform_feedbacks(ctx, n, names, true); 1118} 1119 1120 1121/** 1122 * Is the given ID a transform feedback object? 1123 * Part of GL_ARB_transform_feedback2. 1124 */ 1125GLboolean GLAPIENTRY 1126_mesa_IsTransformFeedback(GLuint name) 1127{ 1128 struct gl_transform_feedback_object *obj; 1129 GET_CURRENT_CONTEXT(ctx); 1130 1131 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 1132 1133 if (name == 0) 1134 return GL_FALSE; 1135 1136 obj = _mesa_lookup_transform_feedback_object(ctx, name); 1137 if (obj == NULL) 1138 return GL_FALSE; 1139 1140 return obj->EverBound; 1141} 1142 1143 1144/** 1145 * Bind the given transform feedback object. 1146 * Part of GL_ARB_transform_feedback2. 1147 */ 1148static ALWAYS_INLINE void 1149bind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error) 1150{ 1151 struct gl_transform_feedback_object *obj; 1152 1153 obj = _mesa_lookup_transform_feedback_object(ctx, name); 1154 if (!no_error && !obj) { 1155 _mesa_error(ctx, GL_INVALID_OPERATION, 1156 "glBindTransformFeedback(name=%u)", name); 1157 return; 1158 } 1159 1160 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 1161 obj); 1162} 1163 1164 1165void GLAPIENTRY 1166_mesa_BindTransformFeedback_no_error(GLenum target, GLuint name) 1167{ 1168 GET_CURRENT_CONTEXT(ctx); 1169 bind_transform_feedback(ctx, name, true); 1170} 1171 1172 1173void GLAPIENTRY 1174_mesa_BindTransformFeedback(GLenum target, GLuint name) 1175{ 1176 GET_CURRENT_CONTEXT(ctx); 1177 1178 if (target != GL_TRANSFORM_FEEDBACK) { 1179 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); 1180 return; 1181 } 1182 1183 if (_mesa_is_xfb_active_and_unpaused(ctx)) { 1184 _mesa_error(ctx, GL_INVALID_OPERATION, 1185 "glBindTransformFeedback(transform is active, or not paused)"); 1186 return; 1187 } 1188 1189 bind_transform_feedback(ctx, name, false); 1190} 1191 1192 1193/** 1194 * Delete the given transform feedback objects. 1195 * Part of GL_ARB_transform_feedback2. 1196 */ 1197void GLAPIENTRY 1198_mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) 1199{ 1200 GLint i; 1201 GET_CURRENT_CONTEXT(ctx); 1202 1203 if (n < 0) { 1204 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); 1205 return; 1206 } 1207 1208 if (!names) 1209 return; 1210 1211 for (i = 0; i < n; i++) { 1212 if (names[i] > 0) { 1213 struct gl_transform_feedback_object *obj 1214 = _mesa_lookup_transform_feedback_object(ctx, names[i]); 1215 if (obj) { 1216 if (obj->Active) { 1217 _mesa_error(ctx, GL_INVALID_OPERATION, 1218 "glDeleteTransformFeedbacks(object %u is active)", 1219 names[i]); 1220 return; 1221 } 1222 _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]); 1223 /* unref, but object may not be deleted until later */ 1224 if (obj == ctx->TransformFeedback.CurrentObject) { 1225 reference_transform_feedback_object( 1226 &ctx->TransformFeedback.CurrentObject, 1227 ctx->TransformFeedback.DefaultObject); 1228 } 1229 reference_transform_feedback_object(&obj, NULL); 1230 } 1231 } 1232 } 1233} 1234 1235 1236/** 1237 * Pause transform feedback. 1238 * Part of GL_ARB_transform_feedback2. 1239 */ 1240static void 1241pause_transform_feedback(struct gl_context *ctx, 1242 struct gl_transform_feedback_object *obj) 1243{ 1244 FLUSH_VERTICES(ctx, 0); 1245 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 1246 1247 assert(ctx->Driver.PauseTransformFeedback); 1248 ctx->Driver.PauseTransformFeedback(ctx, obj); 1249 1250 obj->Paused = GL_TRUE; 1251} 1252 1253 1254void GLAPIENTRY 1255_mesa_PauseTransformFeedback_no_error(void) 1256{ 1257 GET_CURRENT_CONTEXT(ctx); 1258 pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 1259} 1260 1261 1262void GLAPIENTRY 1263_mesa_PauseTransformFeedback(void) 1264{ 1265 struct gl_transform_feedback_object *obj; 1266 GET_CURRENT_CONTEXT(ctx); 1267 1268 obj = ctx->TransformFeedback.CurrentObject; 1269 1270 if (!_mesa_is_xfb_active_and_unpaused(ctx)) { 1271 _mesa_error(ctx, GL_INVALID_OPERATION, 1272 "glPauseTransformFeedback(feedback not active or already paused)"); 1273 return; 1274 } 1275 1276 pause_transform_feedback(ctx, obj); 1277} 1278 1279 1280/** 1281 * Resume transform feedback. 1282 * Part of GL_ARB_transform_feedback2. 1283 */ 1284static void 1285resume_transform_feedback(struct gl_context *ctx, 1286 struct gl_transform_feedback_object *obj) 1287{ 1288 FLUSH_VERTICES(ctx, 0); 1289 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 1290 1291 obj->Paused = GL_FALSE; 1292 1293 assert(ctx->Driver.ResumeTransformFeedback); 1294 ctx->Driver.ResumeTransformFeedback(ctx, obj); 1295} 1296 1297 1298void GLAPIENTRY 1299_mesa_ResumeTransformFeedback_no_error(void) 1300{ 1301 GET_CURRENT_CONTEXT(ctx); 1302 resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 1303} 1304 1305 1306void GLAPIENTRY 1307_mesa_ResumeTransformFeedback(void) 1308{ 1309 struct gl_transform_feedback_object *obj; 1310 GET_CURRENT_CONTEXT(ctx); 1311 1312 obj = ctx->TransformFeedback.CurrentObject; 1313 1314 if (!obj->Active || !obj->Paused) { 1315 _mesa_error(ctx, GL_INVALID_OPERATION, 1316 "glResumeTransformFeedback(feedback not active or not paused)"); 1317 return; 1318 } 1319 1320 /* From the ARB_transform_feedback2 specification: 1321 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if 1322 * the program object being used by the current transform feedback object 1323 * is not active." 1324 */ 1325 if (obj->program != get_xfb_source(ctx)) { 1326 _mesa_error(ctx, GL_INVALID_OPERATION, 1327 "glResumeTransformFeedback(wrong program bound)"); 1328 return; 1329 } 1330 1331 resume_transform_feedback(ctx, obj); 1332} 1333 1334extern void GLAPIENTRY 1335_mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param) 1336{ 1337 struct gl_transform_feedback_object *obj; 1338 GET_CURRENT_CONTEXT(ctx); 1339 1340 obj = lookup_transform_feedback_object_err(ctx, xfb, 1341 "glGetTransformFeedbackiv"); 1342 if (!obj) { 1343 return; 1344 } 1345 1346 switch(pname) { 1347 case GL_TRANSFORM_FEEDBACK_PAUSED: 1348 *param = obj->Paused; 1349 break; 1350 case GL_TRANSFORM_FEEDBACK_ACTIVE: 1351 *param = obj->Active; 1352 break; 1353 default: 1354 _mesa_error(ctx, GL_INVALID_ENUM, 1355 "glGetTransformFeedbackiv(pname=%i)", pname); 1356 } 1357} 1358 1359extern void GLAPIENTRY 1360_mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index, 1361 GLint *param) 1362{ 1363 struct gl_transform_feedback_object *obj; 1364 GET_CURRENT_CONTEXT(ctx); 1365 1366 obj = lookup_transform_feedback_object_err(ctx, xfb, 1367 "glGetTransformFeedbacki_v"); 1368 if (!obj) { 1369 return; 1370 } 1371 1372 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 1373 _mesa_error(ctx, GL_INVALID_VALUE, 1374 "glGetTransformFeedbacki_v(index=%i)", index); 1375 return; 1376 } 1377 1378 switch(pname) { 1379 case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: 1380 *param = obj->BufferNames[index]; 1381 break; 1382 default: 1383 _mesa_error(ctx, GL_INVALID_ENUM, 1384 "glGetTransformFeedbacki_v(pname=%i)", pname); 1385 } 1386} 1387 1388extern void GLAPIENTRY 1389_mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index, 1390 GLint64 *param) 1391{ 1392 struct gl_transform_feedback_object *obj; 1393 GET_CURRENT_CONTEXT(ctx); 1394 1395 obj = lookup_transform_feedback_object_err(ctx, xfb, 1396 "glGetTransformFeedbacki64_v"); 1397 if (!obj) { 1398 return; 1399 } 1400 1401 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 1402 _mesa_error(ctx, GL_INVALID_VALUE, 1403 "glGetTransformFeedbacki64_v(index=%i)", index); 1404 return; 1405 } 1406 1407 /** 1408 * This follows the same general rules used for BindBufferBase: 1409 * 1410 * "To query the starting offset or size of the range of a buffer 1411 * object binding in an indexed array, call GetInteger64i_v with 1412 * target set to respectively the starting offset or binding size 1413 * name from table 6.5 for that array. Index must be in the range 1414 * zero to the number of bind points supported minus one. If the 1415 * starting offset or size was not specified when the buffer object 1416 * was bound (e.g. if it was bound with BindBufferBase), or if no 1417 * buffer object is bound to the target array at index, zero is 1418 * returned." 1419 */ 1420 if (obj->RequestedSize[index] == 0 && 1421 (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START || 1422 pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) { 1423 *param = 0; 1424 return; 1425 } 1426 1427 compute_transform_feedback_buffer_sizes(obj); 1428 switch(pname) { 1429 case GL_TRANSFORM_FEEDBACK_BUFFER_START: 1430 assert(obj->RequestedSize[index] > 0); 1431 *param = obj->Offset[index]; 1432 break; 1433 case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: 1434 assert(obj->RequestedSize[index] > 0); 1435 *param = obj->Size[index]; 1436 break; 1437 default: 1438 _mesa_error(ctx, GL_INVALID_ENUM, 1439 "glGetTransformFeedbacki64_v(pname=%i)", pname); 1440 } 1441} 1442