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 #include "main/dispatch.h" 43 44 #include "program/prog_parameter.h" 45 46 struct using_program_tuple 47 { 48 struct gl_program *prog; 49 bool found; 50 }; 51 52 static void 53 active_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 */ 64 bool 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 */ 88 static void 89 reference_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 */ 126 void 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 */ 154 static void 155 delete_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 */ 168 void 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. */ 192 void 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() */ 203 static struct gl_transform_feedback_object * 204 new_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() */ 217 static void 218 delete_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() */ 233 static void 234 begin_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() */ 241 static void 242 end_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() */ 249 static void 250 pause_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() */ 257 static void 258 resume_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 */ 269 void 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 */ 298 static void 299 compute_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 */ 334 unsigned 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 */ 372 static struct gl_program * 373 get_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 384 static ALWAYS_INLINE void 385 begin_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 obj->program = source; 475 } 476 477 assert(ctx->Driver.BeginTransformFeedback); 478 ctx->Driver.BeginTransformFeedback(ctx, mode, obj); 479 } 480 481 482 void GLAPIENTRY 483 _mesa_BeginTransformFeedback_no_error(GLenum mode) 484 { 485 GET_CURRENT_CONTEXT(ctx); 486 begin_transform_feedback(ctx, mode, true); 487 } 488 489 490 void GLAPIENTRY 491 _mesa_BeginTransformFeedback(GLenum mode) 492 { 493 GET_CURRENT_CONTEXT(ctx); 494 begin_transform_feedback(ctx, mode, false); 495 } 496 497 498 static void 499 end_transform_feedback(struct gl_context *ctx, 500 struct gl_transform_feedback_object *obj) 501 { 502 FLUSH_VERTICES(ctx, 0); 503 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 504 505 assert(ctx->Driver.EndTransformFeedback); 506 ctx->Driver.EndTransformFeedback(ctx, obj); 507 508 ctx->TransformFeedback.CurrentObject->Active = GL_FALSE; 509 ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE; 510 ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE; 511 } 512 513 514 void GLAPIENTRY 515 _mesa_EndTransformFeedback_no_error(void) 516 { 517 GET_CURRENT_CONTEXT(ctx); 518 end_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 519 } 520 521 522 void GLAPIENTRY 523 _mesa_EndTransformFeedback(void) 524 { 525 struct gl_transform_feedback_object *obj; 526 GET_CURRENT_CONTEXT(ctx); 527 528 obj = ctx->TransformFeedback.CurrentObject; 529 530 if (!obj->Active) { 531 _mesa_error(ctx, GL_INVALID_OPERATION, 532 "glEndTransformFeedback(not active)"); 533 return; 534 } 535 536 end_transform_feedback(ctx, obj); 537 } 538 539 540 /** 541 * Helper used by BindBufferRange() and BindBufferBase(). 542 */ 543 static void 544 bind_buffer_range(struct gl_context *ctx, 545 struct gl_transform_feedback_object *obj, 546 GLuint index, 547 struct gl_buffer_object *bufObj, 548 GLintptr offset, GLsizeiptr size, 549 bool dsa) 550 { 551 /* Note: no need to FLUSH_VERTICES or flag NewTransformFeedback, because 552 * transform feedback buffers can't be changed while transform feedback is 553 * active. 554 */ 555 556 if (!dsa) { 557 /* The general binding point */ 558 _mesa_reference_buffer_object(ctx, 559 &ctx->TransformFeedback.CurrentBuffer, 560 bufObj); 561 } 562 563 /* The per-attribute binding point */ 564 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, size); 565 } 566 567 568 /** 569 * Validate the buffer object to receive transform feedback results. Plus, 570 * validate the starting offset to place the results, and max size. 571 * Called from the glBindBufferRange() and glTransformFeedbackBufferRange 572 * functions. 573 */ 574 bool 575 _mesa_validate_buffer_range_xfb(struct gl_context *ctx, 576 struct gl_transform_feedback_object *obj, 577 GLuint index, struct gl_buffer_object *bufObj, 578 GLintptr offset, GLsizeiptr size, bool dsa) 579 { 580 const char *gl_methd_name; 581 if (dsa) 582 gl_methd_name = "glTransformFeedbackBufferRange"; 583 else 584 gl_methd_name = "glBindBufferRange"; 585 586 587 if (obj->Active) { 588 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)", 589 gl_methd_name); 590 return false; 591 } 592 593 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 594 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 595 * generated if index is greater than or equal to the number of binding 596 * points for transform feedback, as described in section 6.7.1." 597 */ 598 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 599 gl_methd_name, index); 600 return false; 601 } 602 603 if (size & 0x3) { 604 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 605 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be a multiple of " 606 "four)", gl_methd_name, (int) size); 607 return false; 608 } 609 610 if (offset & 0x3) { 611 /* OpenGL 4.5 core profile, 6.7, pdf page 103: multiple of 4 */ 612 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be a multiple " 613 "of four)", gl_methd_name, (int) offset); 614 return false; 615 } 616 617 if (offset < 0) { 618 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 619 * generated by BindBufferRange if offset is negative." 620 * 621 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 622 * is generated by TransformFeedbackBufferRange if offset is negative." 623 */ 624 _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset=%d must be >= 0)", 625 gl_methd_name, 626 (int) offset); 627 return false; 628 } 629 630 if (size <= 0 && (dsa || bufObj != ctx->Shared->NullBufferObj)) { 631 /* OpenGL 4.5 core profile, 6.1, pdf page 82: "An INVALID_VALUE error is 632 * generated by BindBufferRange if buffer is non-zero and size is less 633 * than or equal to zero." 634 * 635 * OpenGL 4.5 core profile, 13.2, pdf page 445: "An INVALID_VALUE error 636 * is generated by TransformFeedbackBufferRange if size is less than or 637 * equal to zero." 638 */ 639 _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d must be > 0)", 640 gl_methd_name, (int) size); 641 return false; 642 } 643 644 return true; 645 } 646 647 648 /** 649 * Specify a buffer object to receive transform feedback results. 650 * As above, but start at offset = 0. 651 * Called from the glBindBufferBase() and glTransformFeedbackBufferBase() 652 * functions. 653 */ 654 void 655 _mesa_bind_buffer_base_transform_feedback(struct gl_context *ctx, 656 struct gl_transform_feedback_object *obj, 657 GLuint index, 658 struct gl_buffer_object *bufObj, 659 bool dsa) 660 { 661 if (obj->Active) { 662 _mesa_error(ctx, GL_INVALID_OPERATION, 663 "%s(transform feedback active)", 664 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase"); 665 return; 666 } 667 668 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 669 _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%d out of bounds)", 670 dsa ? "glTransformFeedbackBufferBase" : "glBindBufferBase", 671 index); 672 return; 673 } 674 675 bind_buffer_range(ctx, obj, index, bufObj, 0, 0, dsa); 676 } 677 678 /** 679 * Wrapper around lookup_transform_feedback_object that throws 680 * GL_INVALID_OPERATION if id is not in the hash table. After calling 681 * _mesa_error, it returns NULL. 682 */ 683 static struct gl_transform_feedback_object * 684 lookup_transform_feedback_object_err(struct gl_context *ctx, 685 GLuint xfb, const char* func) 686 { 687 struct gl_transform_feedback_object *obj; 688 689 obj = _mesa_lookup_transform_feedback_object(ctx, xfb); 690 if (!obj) { 691 _mesa_error(ctx, GL_INVALID_OPERATION, 692 "%s(xfb=%u: non-generated object name)", func, xfb); 693 } 694 695 return obj; 696 } 697 698 /** 699 * Wrapper around _mesa_lookup_bufferobj that throws GL_INVALID_VALUE if id 700 * is not in the hash table. Specialised version for the 701 * transform-feedback-related functions. After calling _mesa_error, it 702 * returns NULL. 703 */ 704 static struct gl_buffer_object * 705 lookup_transform_feedback_bufferobj_err(struct gl_context *ctx, 706 GLuint buffer, const char* func) 707 { 708 struct gl_buffer_object *bufObj; 709 710 /* OpenGL 4.5 core profile, 13.2, pdf page 444: buffer must be zero or the 711 * name of an existing buffer object. 712 */ 713 if (buffer == 0) { 714 bufObj = ctx->Shared->NullBufferObj; 715 } else { 716 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 717 if (!bufObj) { 718 _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid buffer=%u)", func, 719 buffer); 720 } 721 } 722 723 return bufObj; 724 } 725 726 void GLAPIENTRY 727 _mesa_TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer) 728 { 729 GET_CURRENT_CONTEXT(ctx); 730 struct gl_transform_feedback_object *obj; 731 struct gl_buffer_object *bufObj; 732 733 obj = lookup_transform_feedback_object_err(ctx, xfb, 734 "glTransformFeedbackBufferBase"); 735 if (!obj) { 736 return; 737 } 738 739 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 740 "glTransformFeedbackBufferBase"); 741 if (!bufObj) { 742 return; 743 } 744 745 _mesa_bind_buffer_base_transform_feedback(ctx, obj, index, bufObj, true); 746 } 747 748 void GLAPIENTRY 749 _mesa_TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer, 750 GLintptr offset, GLsizeiptr size) 751 { 752 GET_CURRENT_CONTEXT(ctx); 753 struct gl_transform_feedback_object *obj; 754 struct gl_buffer_object *bufObj; 755 756 obj = lookup_transform_feedback_object_err(ctx, xfb, 757 "glTransformFeedbackBufferRange"); 758 if (!obj) { 759 return; 760 } 761 762 bufObj = lookup_transform_feedback_bufferobj_err(ctx, buffer, 763 "glTransformFeedbackBufferRange"); 764 if (!bufObj) { 765 return; 766 } 767 768 if (!_mesa_validate_buffer_range_xfb(ctx, obj, index, bufObj, offset, 769 size, true)) 770 return; 771 772 /* The per-attribute binding point */ 773 _mesa_set_transform_feedback_binding(ctx, obj, index, bufObj, offset, 774 size); 775 } 776 777 /** 778 * Specify a buffer object to receive transform feedback results, plus the 779 * offset in the buffer to start placing results. 780 * This function is part of GL_EXT_transform_feedback, but not GL3. 781 */ 782 static ALWAYS_INLINE void 783 bind_buffer_offset(struct gl_context *ctx, 784 struct gl_transform_feedback_object *obj, GLuint index, 785 GLuint buffer, GLintptr offset, bool no_error) 786 { 787 struct gl_buffer_object *bufObj; 788 789 if (buffer == 0) { 790 bufObj = ctx->Shared->NullBufferObj; 791 } else { 792 bufObj = _mesa_lookup_bufferobj(ctx, buffer); 793 if (!no_error && !bufObj) { 794 _mesa_error(ctx, GL_INVALID_OPERATION, 795 "glBindBufferOffsetEXT(invalid buffer=%u)", buffer); 796 return; 797 } 798 } 799 800 _mesa_bind_buffer_range_xfb(ctx, obj, index, bufObj, offset, 0); 801 } 802 803 804 void GLAPIENTRY 805 _mesa_BindBufferOffsetEXT_no_error(GLenum target, GLuint index, GLuint buffer, 806 GLintptr offset) 807 { 808 GET_CURRENT_CONTEXT(ctx); 809 bind_buffer_offset(ctx, ctx->TransformFeedback.CurrentObject, index, buffer, 810 offset, true); 811 } 812 813 814 void GLAPIENTRY 815 _mesa_BindBufferOffsetEXT(GLenum target, GLuint index, GLuint buffer, 816 GLintptr offset) 817 { 818 struct gl_transform_feedback_object *obj; 819 GET_CURRENT_CONTEXT(ctx); 820 821 if (target != GL_TRANSFORM_FEEDBACK_BUFFER) { 822 _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferOffsetEXT(target)"); 823 return; 824 } 825 826 obj = ctx->TransformFeedback.CurrentObject; 827 828 if (obj->Active) { 829 _mesa_error(ctx, GL_INVALID_OPERATION, 830 "glBindBufferOffsetEXT(transform feedback active)"); 831 return; 832 } 833 834 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 835 _mesa_error(ctx, GL_INVALID_VALUE, 836 "glBindBufferOffsetEXT(index=%d)", index); 837 return; 838 } 839 840 if (offset & 0x3) { 841 /* must be multiple of four */ 842 _mesa_error(ctx, GL_INVALID_VALUE, 843 "glBindBufferOffsetEXT(offset=%d)", (int) offset); 844 return; 845 } 846 847 bind_buffer_offset(ctx, obj, index, buffer, offset, false); 848 } 849 850 851 /** 852 * This function specifies the transform feedback outputs to be written 853 * to the feedback buffer(s), and in what order. 854 */ 855 static ALWAYS_INLINE void 856 transform_feedback_varyings(struct gl_context *ctx, 857 struct gl_shader_program *shProg, GLsizei count, 858 const GLchar *const *varyings, GLenum bufferMode) 859 { 860 GLint i; 861 862 /* free existing varyings, if any */ 863 for (i = 0; i < (GLint) shProg->TransformFeedback.NumVarying; i++) { 864 free(shProg->TransformFeedback.VaryingNames[i]); 865 } 866 free(shProg->TransformFeedback.VaryingNames); 867 868 /* allocate new memory for varying names */ 869 shProg->TransformFeedback.VaryingNames = 870 malloc(count * sizeof(GLchar *)); 871 872 if (!shProg->TransformFeedback.VaryingNames) { 873 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTransformFeedbackVaryings()"); 874 return; 875 } 876 877 /* Save the new names and the count */ 878 for (i = 0; i < count; i++) { 879 shProg->TransformFeedback.VaryingNames[i] = strdup(varyings[i]); 880 } 881 shProg->TransformFeedback.NumVarying = count; 882 883 shProg->TransformFeedback.BufferMode = bufferMode; 884 885 /* No need to invoke FLUSH_VERTICES or flag NewTransformFeedback since 886 * the varyings won't be used until shader link time. 887 */ 888 } 889 890 891 void GLAPIENTRY 892 _mesa_TransformFeedbackVaryings_no_error(GLuint program, GLsizei count, 893 const GLchar *const *varyings, 894 GLenum bufferMode) 895 { 896 GET_CURRENT_CONTEXT(ctx); 897 898 struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); 899 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 900 } 901 902 void GLAPIENTRY 903 _mesa_TransformFeedbackVaryings(GLuint program, GLsizei count, 904 const GLchar * const *varyings, 905 GLenum bufferMode) 906 { 907 struct gl_shader_program *shProg; 908 GLint i; 909 GET_CURRENT_CONTEXT(ctx); 910 911 /* From the ARB_transform_feedback2 specification: 912 * "The error INVALID_OPERATION is generated by TransformFeedbackVaryings 913 * if the current transform feedback object is active, even if paused." 914 */ 915 if (ctx->TransformFeedback.CurrentObject->Active) { 916 _mesa_error(ctx, GL_INVALID_OPERATION, 917 "glTransformFeedbackVaryings(current object is active)"); 918 return; 919 } 920 921 switch (bufferMode) { 922 case GL_INTERLEAVED_ATTRIBS: 923 break; 924 case GL_SEPARATE_ATTRIBS: 925 break; 926 default: 927 _mesa_error(ctx, GL_INVALID_ENUM, 928 "glTransformFeedbackVaryings(bufferMode)"); 929 return; 930 } 931 932 if (count < 0 || 933 (bufferMode == GL_SEPARATE_ATTRIBS && 934 (GLuint) count > ctx->Const.MaxTransformFeedbackBuffers)) { 935 _mesa_error(ctx, GL_INVALID_VALUE, 936 "glTransformFeedbackVaryings(count=%d)", count); 937 return; 938 } 939 940 shProg = _mesa_lookup_shader_program_err(ctx, program, 941 "glTransformFeedbackVaryings"); 942 if (!shProg) 943 return; 944 945 if (ctx->Extensions.ARB_transform_feedback3) { 946 if (bufferMode == GL_INTERLEAVED_ATTRIBS) { 947 unsigned buffers = 1; 948 949 for (i = 0; i < count; i++) { 950 if (strcmp(varyings[i], "gl_NextBuffer") == 0) 951 buffers++; 952 } 953 954 if (buffers > ctx->Const.MaxTransformFeedbackBuffers) { 955 _mesa_error(ctx, GL_INVALID_OPERATION, 956 "glTransformFeedbackVaryings(too many gl_NextBuffer " 957 "occurrences)"); 958 return; 959 } 960 } else { 961 for (i = 0; i < count; i++) { 962 if (strcmp(varyings[i], "gl_NextBuffer") == 0 || 963 strcmp(varyings[i], "gl_SkipComponents1") == 0 || 964 strcmp(varyings[i], "gl_SkipComponents2") == 0 || 965 strcmp(varyings[i], "gl_SkipComponents3") == 0 || 966 strcmp(varyings[i], "gl_SkipComponents4") == 0) { 967 _mesa_error(ctx, GL_INVALID_OPERATION, 968 "glTransformFeedbackVaryings(SEPARATE_ATTRIBS," 969 "varying=%s)", 970 varyings[i]); 971 return; 972 } 973 } 974 } 975 } 976 977 transform_feedback_varyings(ctx, shProg, count, varyings, bufferMode); 978 } 979 980 981 /** 982 * Get info about the transform feedback outputs which are to be written 983 * to the feedback buffer(s). 984 */ 985 void GLAPIENTRY 986 _mesa_GetTransformFeedbackVarying(GLuint program, GLuint index, 987 GLsizei bufSize, GLsizei *length, 988 GLsizei *size, GLenum *type, GLchar *name) 989 { 990 const struct gl_shader_program *shProg; 991 struct gl_program_resource *res; 992 GET_CURRENT_CONTEXT(ctx); 993 994 shProg = _mesa_lookup_shader_program_err(ctx, program, 995 "glGetTransformFeedbackVarying"); 996 if (!shProg) 997 return; 998 999 res = _mesa_program_resource_find_index((struct gl_shader_program *) shProg, 1000 GL_TRANSFORM_FEEDBACK_VARYING, 1001 index); 1002 if (!res) { 1003 _mesa_error(ctx, GL_INVALID_VALUE, 1004 "glGetTransformFeedbackVarying(index=%u)", index); 1005 return; 1006 } 1007 1008 /* return the varying's name and length */ 1009 _mesa_copy_string(name, bufSize, length, _mesa_program_resource_name(res)); 1010 1011 /* return the datatype and value's size (in datatype units) */ 1012 if (type) 1013 _mesa_program_resource_prop((struct gl_shader_program *) shProg, 1014 res, index, GL_TYPE, (GLint*) type, 1015 "glGetTransformFeedbackVarying"); 1016 if (size) 1017 _mesa_program_resource_prop((struct gl_shader_program *) shProg, 1018 res, index, GL_ARRAY_SIZE, (GLint*) size, 1019 "glGetTransformFeedbackVarying"); 1020 } 1021 1022 1023 1024 struct gl_transform_feedback_object * 1025 _mesa_lookup_transform_feedback_object(struct gl_context *ctx, GLuint name) 1026 { 1027 /* OpenGL 4.5 core profile, 13.2 pdf page 444: "xfb must be zero, indicating 1028 * the default transform feedback object, or the name of an existing 1029 * transform feedback object." 1030 */ 1031 if (name == 0) { 1032 return ctx->TransformFeedback.DefaultObject; 1033 } 1034 else 1035 return (struct gl_transform_feedback_object *) 1036 _mesa_HashLookupLocked(ctx->TransformFeedback.Objects, name); 1037 } 1038 1039 static void 1040 create_transform_feedbacks(struct gl_context *ctx, GLsizei n, GLuint *ids, 1041 bool dsa) 1042 { 1043 GLuint first; 1044 const char* func; 1045 1046 if (dsa) 1047 func = "glCreateTransformFeedbacks"; 1048 else 1049 func = "glGenTransformFeedbacks"; 1050 1051 if (n < 0) { 1052 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 1053 return; 1054 } 1055 1056 if (!ids) 1057 return; 1058 1059 /* we don't need contiguous IDs, but this might be faster */ 1060 first = _mesa_HashFindFreeKeyBlock(ctx->TransformFeedback.Objects, n); 1061 if (first) { 1062 GLsizei i; 1063 for (i = 0; i < n; i++) { 1064 struct gl_transform_feedback_object *obj 1065 = ctx->Driver.NewTransformFeedback(ctx, first + i); 1066 if (!obj) { 1067 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 1068 return; 1069 } 1070 ids[i] = first + i; 1071 _mesa_HashInsertLocked(ctx->TransformFeedback.Objects, first + i, 1072 obj); 1073 if (dsa) { 1074 /* this is normally done at bind time in the non-dsa case */ 1075 obj->EverBound = GL_TRUE; 1076 } 1077 } 1078 } 1079 else { 1080 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 1081 } 1082 } 1083 1084 /** 1085 * Create new transform feedback objects. Transform feedback objects 1086 * encapsulate the state related to transform feedback to allow quickly 1087 * switching state (and drawing the results, below). 1088 * Part of GL_ARB_transform_feedback2. 1089 */ 1090 void GLAPIENTRY 1091 _mesa_GenTransformFeedbacks(GLsizei n, GLuint *names) 1092 { 1093 GET_CURRENT_CONTEXT(ctx); 1094 1095 /* GenTransformFeedbacks should just reserve the object names that a 1096 * subsequent call to BindTransformFeedback should actively create. For 1097 * the sake of simplicity, we reserve the names and create the objects 1098 * straight away. 1099 */ 1100 1101 create_transform_feedbacks(ctx, n, names, false); 1102 } 1103 1104 /** 1105 * Create new transform feedback objects. Transform feedback objects 1106 * encapsulate the state related to transform feedback to allow quickly 1107 * switching state (and drawing the results, below). 1108 * Part of GL_ARB_direct_state_access. 1109 */ 1110 void GLAPIENTRY 1111 _mesa_CreateTransformFeedbacks(GLsizei n, GLuint *names) 1112 { 1113 GET_CURRENT_CONTEXT(ctx); 1114 1115 create_transform_feedbacks(ctx, n, names, true); 1116 } 1117 1118 1119 /** 1120 * Is the given ID a transform feedback object? 1121 * Part of GL_ARB_transform_feedback2. 1122 */ 1123 GLboolean GLAPIENTRY 1124 _mesa_IsTransformFeedback(GLuint name) 1125 { 1126 struct gl_transform_feedback_object *obj; 1127 GET_CURRENT_CONTEXT(ctx); 1128 1129 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 1130 1131 if (name == 0) 1132 return GL_FALSE; 1133 1134 obj = _mesa_lookup_transform_feedback_object(ctx, name); 1135 if (obj == NULL) 1136 return GL_FALSE; 1137 1138 return obj->EverBound; 1139 } 1140 1141 1142 /** 1143 * Bind the given transform feedback object. 1144 * Part of GL_ARB_transform_feedback2. 1145 */ 1146 static ALWAYS_INLINE void 1147 bind_transform_feedback(struct gl_context *ctx, GLuint name, bool no_error) 1148 { 1149 struct gl_transform_feedback_object *obj; 1150 1151 obj = _mesa_lookup_transform_feedback_object(ctx, name); 1152 if (!no_error && !obj) { 1153 _mesa_error(ctx, GL_INVALID_OPERATION, 1154 "glBindTransformFeedback(name=%u)", name); 1155 return; 1156 } 1157 1158 reference_transform_feedback_object(&ctx->TransformFeedback.CurrentObject, 1159 obj); 1160 } 1161 1162 1163 void GLAPIENTRY 1164 _mesa_BindTransformFeedback_no_error(GLenum target, GLuint name) 1165 { 1166 GET_CURRENT_CONTEXT(ctx); 1167 bind_transform_feedback(ctx, name, true); 1168 } 1169 1170 1171 void GLAPIENTRY 1172 _mesa_BindTransformFeedback(GLenum target, GLuint name) 1173 { 1174 GET_CURRENT_CONTEXT(ctx); 1175 1176 if (target != GL_TRANSFORM_FEEDBACK) { 1177 _mesa_error(ctx, GL_INVALID_ENUM, "glBindTransformFeedback(target)"); 1178 return; 1179 } 1180 1181 if (_mesa_is_xfb_active_and_unpaused(ctx)) { 1182 _mesa_error(ctx, GL_INVALID_OPERATION, 1183 "glBindTransformFeedback(transform is active, or not paused)"); 1184 return; 1185 } 1186 1187 bind_transform_feedback(ctx, name, false); 1188 } 1189 1190 1191 /** 1192 * Delete the given transform feedback objects. 1193 * Part of GL_ARB_transform_feedback2. 1194 */ 1195 void GLAPIENTRY 1196 _mesa_DeleteTransformFeedbacks(GLsizei n, const GLuint *names) 1197 { 1198 GLint i; 1199 GET_CURRENT_CONTEXT(ctx); 1200 1201 if (n < 0) { 1202 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTransformFeedbacks(n < 0)"); 1203 return; 1204 } 1205 1206 if (!names) 1207 return; 1208 1209 for (i = 0; i < n; i++) { 1210 if (names[i] > 0) { 1211 struct gl_transform_feedback_object *obj 1212 = _mesa_lookup_transform_feedback_object(ctx, names[i]); 1213 if (obj) { 1214 if (obj->Active) { 1215 _mesa_error(ctx, GL_INVALID_OPERATION, 1216 "glDeleteTransformFeedbacks(object %u is active)", 1217 names[i]); 1218 return; 1219 } 1220 _mesa_HashRemoveLocked(ctx->TransformFeedback.Objects, names[i]); 1221 /* unref, but object may not be deleted until later */ 1222 if (obj == ctx->TransformFeedback.CurrentObject) { 1223 reference_transform_feedback_object( 1224 &ctx->TransformFeedback.CurrentObject, 1225 ctx->TransformFeedback.DefaultObject); 1226 } 1227 reference_transform_feedback_object(&obj, NULL); 1228 } 1229 } 1230 } 1231 } 1232 1233 1234 /** 1235 * Pause transform feedback. 1236 * Part of GL_ARB_transform_feedback2. 1237 */ 1238 static void 1239 pause_transform_feedback(struct gl_context *ctx, 1240 struct gl_transform_feedback_object *obj) 1241 { 1242 FLUSH_VERTICES(ctx, 0); 1243 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 1244 1245 assert(ctx->Driver.PauseTransformFeedback); 1246 ctx->Driver.PauseTransformFeedback(ctx, obj); 1247 1248 obj->Paused = GL_TRUE; 1249 } 1250 1251 1252 void GLAPIENTRY 1253 _mesa_PauseTransformFeedback_no_error(void) 1254 { 1255 GET_CURRENT_CONTEXT(ctx); 1256 pause_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 1257 } 1258 1259 1260 void GLAPIENTRY 1261 _mesa_PauseTransformFeedback(void) 1262 { 1263 struct gl_transform_feedback_object *obj; 1264 GET_CURRENT_CONTEXT(ctx); 1265 1266 obj = ctx->TransformFeedback.CurrentObject; 1267 1268 if (!_mesa_is_xfb_active_and_unpaused(ctx)) { 1269 _mesa_error(ctx, GL_INVALID_OPERATION, 1270 "glPauseTransformFeedback(feedback not active or already paused)"); 1271 return; 1272 } 1273 1274 pause_transform_feedback(ctx, obj); 1275 } 1276 1277 1278 /** 1279 * Resume transform feedback. 1280 * Part of GL_ARB_transform_feedback2. 1281 */ 1282 static void 1283 resume_transform_feedback(struct gl_context *ctx, 1284 struct gl_transform_feedback_object *obj) 1285 { 1286 FLUSH_VERTICES(ctx, 0); 1287 ctx->NewDriverState |= ctx->DriverFlags.NewTransformFeedback; 1288 1289 obj->Paused = GL_FALSE; 1290 1291 assert(ctx->Driver.ResumeTransformFeedback); 1292 ctx->Driver.ResumeTransformFeedback(ctx, obj); 1293 } 1294 1295 1296 void GLAPIENTRY 1297 _mesa_ResumeTransformFeedback_no_error(void) 1298 { 1299 GET_CURRENT_CONTEXT(ctx); 1300 resume_transform_feedback(ctx, ctx->TransformFeedback.CurrentObject); 1301 } 1302 1303 1304 void GLAPIENTRY 1305 _mesa_ResumeTransformFeedback(void) 1306 { 1307 struct gl_transform_feedback_object *obj; 1308 GET_CURRENT_CONTEXT(ctx); 1309 1310 obj = ctx->TransformFeedback.CurrentObject; 1311 1312 if (!obj->Active || !obj->Paused) { 1313 _mesa_error(ctx, GL_INVALID_OPERATION, 1314 "glResumeTransformFeedback(feedback not active or not paused)"); 1315 return; 1316 } 1317 1318 /* From the ARB_transform_feedback2 specification: 1319 * "The error INVALID_OPERATION is generated by ResumeTransformFeedback if 1320 * the program object being used by the current transform feedback object 1321 * is not active." 1322 */ 1323 if (obj->program != get_xfb_source(ctx)) { 1324 _mesa_error(ctx, GL_INVALID_OPERATION, 1325 "glResumeTransformFeedback(wrong program bound)"); 1326 return; 1327 } 1328 1329 resume_transform_feedback(ctx, obj); 1330 } 1331 1332 extern void GLAPIENTRY 1333 _mesa_GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint *param) 1334 { 1335 struct gl_transform_feedback_object *obj; 1336 GET_CURRENT_CONTEXT(ctx); 1337 1338 obj = lookup_transform_feedback_object_err(ctx, xfb, 1339 "glGetTransformFeedbackiv"); 1340 if (!obj) { 1341 return; 1342 } 1343 1344 switch(pname) { 1345 case GL_TRANSFORM_FEEDBACK_PAUSED: 1346 *param = obj->Paused; 1347 break; 1348 case GL_TRANSFORM_FEEDBACK_ACTIVE: 1349 *param = obj->Active; 1350 break; 1351 default: 1352 _mesa_error(ctx, GL_INVALID_ENUM, 1353 "glGetTransformFeedbackiv(pname=%i)", pname); 1354 } 1355 } 1356 1357 extern void GLAPIENTRY 1358 _mesa_GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index, 1359 GLint *param) 1360 { 1361 struct gl_transform_feedback_object *obj; 1362 GET_CURRENT_CONTEXT(ctx); 1363 1364 obj = lookup_transform_feedback_object_err(ctx, xfb, 1365 "glGetTransformFeedbacki_v"); 1366 if (!obj) { 1367 return; 1368 } 1369 1370 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 1371 _mesa_error(ctx, GL_INVALID_VALUE, 1372 "glGetTransformFeedbacki_v(index=%i)", index); 1373 return; 1374 } 1375 1376 switch(pname) { 1377 case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: 1378 *param = obj->BufferNames[index]; 1379 break; 1380 default: 1381 _mesa_error(ctx, GL_INVALID_ENUM, 1382 "glGetTransformFeedbacki_v(pname=%i)", pname); 1383 } 1384 } 1385 1386 extern void GLAPIENTRY 1387 _mesa_GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index, 1388 GLint64 *param) 1389 { 1390 struct gl_transform_feedback_object *obj; 1391 GET_CURRENT_CONTEXT(ctx); 1392 1393 obj = lookup_transform_feedback_object_err(ctx, xfb, 1394 "glGetTransformFeedbacki64_v"); 1395 if (!obj) { 1396 return; 1397 } 1398 1399 if (index >= ctx->Const.MaxTransformFeedbackBuffers) { 1400 _mesa_error(ctx, GL_INVALID_VALUE, 1401 "glGetTransformFeedbacki64_v(index=%i)", index); 1402 return; 1403 } 1404 1405 /** 1406 * This follows the same general rules used for BindBufferBase: 1407 * 1408 * "To query the starting offset or size of the range of a buffer 1409 * object binding in an indexed array, call GetInteger64i_v with 1410 * target set to respectively the starting offset or binding size 1411 * name from table 6.5 for that array. Index must be in the range 1412 * zero to the number of bind points supported minus one. If the 1413 * starting offset or size was not specified when the buffer object 1414 * was bound (e.g. if it was bound with BindBufferBase), or if no 1415 * buffer object is bound to the target array at index, zero is 1416 * returned." 1417 */ 1418 if (obj->RequestedSize[index] == 0 && 1419 (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START || 1420 pname == GL_TRANSFORM_FEEDBACK_BUFFER_SIZE)) { 1421 *param = 0; 1422 return; 1423 } 1424 1425 compute_transform_feedback_buffer_sizes(obj); 1426 switch(pname) { 1427 case GL_TRANSFORM_FEEDBACK_BUFFER_START: 1428 assert(obj->RequestedSize[index] > 0); 1429 *param = obj->Offset[index]; 1430 break; 1431 case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: 1432 assert(obj->RequestedSize[index] > 0); 1433 *param = obj->Size[index]; 1434 break; 1435 default: 1436 _mesa_error(ctx, GL_INVALID_ENUM, 1437 "glGetTransformFeedbacki64_v(pname=%i)", pname); 1438 } 1439 } 1440