arrayobj.c revision d4b0e0b717b698682700bf1cd9d448043a57701d
1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. 5 * (C) Copyright IBM Corporation 2006 6 * Copyright (C) 2009 VMware, Inc. All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included 16 * in all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 * OTHER DEALINGS IN THE SOFTWARE. 25 */ 26 27 28/** 29 * \file arrayobj.c 30 * 31 * Implementation of Vertex Array Objects (VAOs), from OpenGL 3.1+, 32 * the GL_ARB_vertex_array_object extension, or the older 33 * GL_APPLE_vertex_array_object extension. 34 * 35 * \todo 36 * The code in this file borrows a lot from bufferobj.c. There's a certain 37 * amount of cruft left over from that origin that may be unnecessary. 38 * 39 * \author Ian Romanick <idr@us.ibm.com> 40 * \author Brian Paul 41 */ 42 43 44#include "glheader.h" 45#include "hash.h" 46#include "image.h" 47#include "imports.h" 48#include "context.h" 49#include "bufferobj.h" 50#include "arrayobj.h" 51#include "macros.h" 52#include "mtypes.h" 53#include "varray.h" 54#include "main/dispatch.h" 55 56 57/** 58 * Look up the array object for the given ID. 59 * 60 * \returns 61 * Either a pointer to the array object with the specified ID or \c NULL for 62 * a non-existent ID. The spec defines ID 0 as being technically 63 * non-existent. 64 */ 65 66struct gl_vertex_array_object * 67_mesa_lookup_vao(struct gl_context *ctx, GLuint id) 68{ 69 if (id == 0) 70 return NULL; 71 else 72 return (struct gl_vertex_array_object *) 73 _mesa_HashLookup(ctx->Array.Objects, id); 74} 75 76 77/** 78 * Looks up the array object for the given ID. 79 * 80 * Unlike _mesa_lookup_vao, this function generates a GL_INVALID_OPERATION 81 * error if the array object does not exist. It also returns the default 82 * array object when ctx is a compatibility profile context and id is zero. 83 */ 84struct gl_vertex_array_object * 85_mesa_lookup_vao_err(struct gl_context *ctx, GLuint id, const char *caller) 86{ 87 /* The ARB_direct_state_access specification says: 88 * 89 * "<vaobj> is [compatibility profile: 90 * zero, indicating the default vertex array object, or] 91 * the name of the vertex array object." 92 */ 93 if (id == 0) { 94 if (ctx->API == API_OPENGL_CORE) { 95 _mesa_error(ctx, GL_INVALID_OPERATION, 96 "%s(zero is not valid vaobj name in a core profile " 97 "context)", caller); 98 return NULL; 99 } 100 101 return ctx->Array.DefaultVAO; 102 } else { 103 struct gl_vertex_array_object *vao; 104 105 if (ctx->Array.LastLookedUpVAO && 106 ctx->Array.LastLookedUpVAO->Name == id) { 107 vao = ctx->Array.LastLookedUpVAO; 108 } else { 109 vao = (struct gl_vertex_array_object *) 110 _mesa_HashLookup(ctx->Array.Objects, id); 111 112 /* The ARB_direct_state_access specification says: 113 * 114 * "An INVALID_OPERATION error is generated if <vaobj> is not 115 * [compatibility profile: zero or] the name of an existing 116 * vertex array object." 117 */ 118 if (!vao || !vao->EverBound) { 119 _mesa_error(ctx, GL_INVALID_OPERATION, 120 "%s(non-existent vaobj=%u)", caller, id); 121 return NULL; 122 } 123 124 _mesa_reference_vao(ctx, &ctx->Array.LastLookedUpVAO, vao); 125 } 126 127 return vao; 128 } 129} 130 131 132/** 133 * For all the vertex binding points in the array object, unbind any pointers 134 * to any buffer objects (VBOs). 135 * This is done just prior to array object destruction. 136 */ 137static void 138unbind_array_object_vbos(struct gl_context *ctx, struct gl_vertex_array_object *obj) 139{ 140 GLuint i; 141 142 for (i = 0; i < ARRAY_SIZE(obj->VertexBinding); i++) 143 _mesa_reference_buffer_object(ctx, &obj->VertexBinding[i].BufferObj, NULL); 144 145 for (i = 0; i < ARRAY_SIZE(obj->_VertexAttrib); i++) 146 _mesa_reference_buffer_object(ctx, &obj->_VertexAttrib[i].BufferObj, NULL); 147} 148 149 150/** 151 * Allocate and initialize a new vertex array object. 152 * 153 * This function is intended to be called via 154 * \c dd_function_table::NewArrayObject. 155 */ 156struct gl_vertex_array_object * 157_mesa_new_vao(struct gl_context *ctx, GLuint name) 158{ 159 struct gl_vertex_array_object *obj = CALLOC_STRUCT(gl_vertex_array_object); 160 if (obj) 161 _mesa_initialize_vao(ctx, obj, name); 162 return obj; 163} 164 165 166/** 167 * Delete an array object. 168 * 169 * This function is intended to be called via 170 * \c dd_function_table::DeleteArrayObject. 171 */ 172void 173_mesa_delete_vao(struct gl_context *ctx, struct gl_vertex_array_object *obj) 174{ 175 unbind_array_object_vbos(ctx, obj); 176 _mesa_reference_buffer_object(ctx, &obj->IndexBufferObj, NULL); 177 mtx_destroy(&obj->Mutex); 178 free(obj->Label); 179 free(obj); 180} 181 182 183/** 184 * Set ptr to vao w/ reference counting. 185 * Note: this should only be called from the _mesa_reference_vao() 186 * inline function. 187 */ 188void 189_mesa_reference_vao_(struct gl_context *ctx, 190 struct gl_vertex_array_object **ptr, 191 struct gl_vertex_array_object *vao) 192{ 193 assert(*ptr != vao); 194 195 if (*ptr) { 196 /* Unreference the old array object */ 197 GLboolean deleteFlag = GL_FALSE; 198 struct gl_vertex_array_object *oldObj = *ptr; 199 200 mtx_lock(&oldObj->Mutex); 201 assert(oldObj->RefCount > 0); 202 oldObj->RefCount--; 203 deleteFlag = (oldObj->RefCount == 0); 204 mtx_unlock(&oldObj->Mutex); 205 206 if (deleteFlag) { 207 assert(ctx->Driver.DeleteArrayObject); 208 ctx->Driver.DeleteArrayObject(ctx, oldObj); 209 } 210 211 *ptr = NULL; 212 } 213 assert(!*ptr); 214 215 if (vao) { 216 /* reference new array object */ 217 mtx_lock(&vao->Mutex); 218 if (vao->RefCount == 0) { 219 /* this array's being deleted (look just above) */ 220 /* Not sure this can every really happen. Warn if it does. */ 221 _mesa_problem(NULL, "referencing deleted array object"); 222 *ptr = NULL; 223 } 224 else { 225 vao->RefCount++; 226 *ptr = vao; 227 } 228 mtx_unlock(&vao->Mutex); 229 } 230} 231 232 233 234static void 235init_array(struct gl_context *ctx, 236 struct gl_vertex_array_object *obj, GLuint index, GLint size, GLint type) 237{ 238 struct gl_vertex_attrib_array *array = &obj->VertexAttrib[index]; 239 struct gl_vertex_buffer_binding *binding = &obj->VertexBinding[index]; 240 241 array->Size = size; 242 array->Type = type; 243 array->Format = GL_RGBA; /* only significant for GL_EXT_vertex_array_bgra */ 244 array->Stride = 0; 245 array->Ptr = NULL; 246 array->RelativeOffset = 0; 247 array->Enabled = GL_FALSE; 248 array->Normalized = GL_FALSE; 249 array->Integer = GL_FALSE; 250 array->Doubles = GL_FALSE; 251 array->_ElementSize = size * _mesa_sizeof_type(type); 252 array->VertexBinding = index; 253 254 binding->Offset = 0; 255 binding->Stride = array->_ElementSize; 256 binding->BufferObj = NULL; 257 binding->_BoundArrays = BITFIELD64_BIT(index); 258 259 /* Vertex array buffers */ 260 _mesa_reference_buffer_object(ctx, &binding->BufferObj, 261 ctx->Shared->NullBufferObj); 262} 263 264 265/** 266 * Initialize a gl_vertex_array_object's arrays. 267 */ 268void 269_mesa_initialize_vao(struct gl_context *ctx, 270 struct gl_vertex_array_object *obj, 271 GLuint name) 272{ 273 GLuint i; 274 275 obj->Name = name; 276 277 mtx_init(&obj->Mutex, mtx_plain); 278 obj->RefCount = 1; 279 280 /* Init the individual arrays */ 281 for (i = 0; i < ARRAY_SIZE(obj->VertexAttrib); i++) { 282 switch (i) { 283 case VERT_ATTRIB_WEIGHT: 284 init_array(ctx, obj, VERT_ATTRIB_WEIGHT, 1, GL_FLOAT); 285 break; 286 case VERT_ATTRIB_NORMAL: 287 init_array(ctx, obj, VERT_ATTRIB_NORMAL, 3, GL_FLOAT); 288 break; 289 case VERT_ATTRIB_COLOR1: 290 init_array(ctx, obj, VERT_ATTRIB_COLOR1, 3, GL_FLOAT); 291 break; 292 case VERT_ATTRIB_FOG: 293 init_array(ctx, obj, VERT_ATTRIB_FOG, 1, GL_FLOAT); 294 break; 295 case VERT_ATTRIB_COLOR_INDEX: 296 init_array(ctx, obj, VERT_ATTRIB_COLOR_INDEX, 1, GL_FLOAT); 297 break; 298 case VERT_ATTRIB_EDGEFLAG: 299 init_array(ctx, obj, VERT_ATTRIB_EDGEFLAG, 1, GL_BOOL); 300 break; 301 case VERT_ATTRIB_POINT_SIZE: 302 init_array(ctx, obj, VERT_ATTRIB_POINT_SIZE, 1, GL_FLOAT); 303 break; 304 default: 305 init_array(ctx, obj, i, 4, GL_FLOAT); 306 break; 307 } 308 } 309 310 _mesa_reference_buffer_object(ctx, &obj->IndexBufferObj, 311 ctx->Shared->NullBufferObj); 312} 313 314 315/** 316 * Add the given array object to the array object pool. 317 */ 318static void 319save_array_object( struct gl_context *ctx, struct gl_vertex_array_object *obj ) 320{ 321 if (obj->Name > 0) { 322 /* insert into hash table */ 323 _mesa_HashInsert(ctx->Array.Objects, obj->Name, obj); 324 } 325} 326 327 328/** 329 * Remove the given array object from the array object pool. 330 * Do not deallocate the array object though. 331 */ 332static void 333remove_array_object( struct gl_context *ctx, struct gl_vertex_array_object *obj ) 334{ 335 if (obj->Name > 0) { 336 /* remove from hash table */ 337 _mesa_HashRemove(ctx->Array.Objects, obj->Name); 338 } 339} 340 341 342/** 343 * Updates the derived gl_client_arrays when a gl_vertex_attrib_array 344 * or a gl_vertex_buffer_binding has changed. 345 */ 346void 347_mesa_update_vao_client_arrays(struct gl_context *ctx, 348 struct gl_vertex_array_object *vao) 349{ 350 GLbitfield64 arrays = vao->NewArrays; 351 352 while (arrays) { 353 struct gl_client_array *client_array; 354 struct gl_vertex_attrib_array *attrib_array; 355 struct gl_vertex_buffer_binding *buffer_binding; 356 357 GLint attrib = ffsll(arrays) - 1; 358 arrays ^= BITFIELD64_BIT(attrib); 359 360 attrib_array = &vao->VertexAttrib[attrib]; 361 buffer_binding = &vao->VertexBinding[attrib_array->VertexBinding]; 362 client_array = &vao->_VertexAttrib[attrib]; 363 364 _mesa_update_client_array(ctx, client_array, attrib_array, 365 buffer_binding); 366 } 367} 368 369 370/**********************************************************************/ 371/* API Functions */ 372/**********************************************************************/ 373 374 375/** 376 * Helper for _mesa_BindVertexArray() and _mesa_BindVertexArrayAPPLE(). 377 * \param genRequired specifies behavour when id was not generated with 378 * glGenVertexArrays(). 379 */ 380static void 381bind_vertex_array(struct gl_context *ctx, GLuint id, GLboolean genRequired) 382{ 383 struct gl_vertex_array_object * const oldObj = ctx->Array.VAO; 384 struct gl_vertex_array_object *newObj = NULL; 385 386 assert(oldObj != NULL); 387 388 if ( oldObj->Name == id ) 389 return; /* rebinding the same array object- no change */ 390 391 /* 392 * Get pointer to new array object (newObj) 393 */ 394 if (id == 0) { 395 /* The spec says there is no array object named 0, but we use 396 * one internally because it simplifies things. 397 */ 398 newObj = ctx->Array.DefaultVAO; 399 } 400 else { 401 /* non-default array object */ 402 newObj = _mesa_lookup_vao(ctx, id); 403 if (!newObj) { 404 if (genRequired) { 405 _mesa_error(ctx, GL_INVALID_OPERATION, 406 "glBindVertexArray(non-gen name)"); 407 return; 408 } 409 410 /* For APPLE version, generate a new array object now */ 411 newObj = (*ctx->Driver.NewArrayObject)(ctx, id); 412 if (!newObj) { 413 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindVertexArrayAPPLE"); 414 return; 415 } 416 417 save_array_object(ctx, newObj); 418 } 419 420 if (!newObj->EverBound) { 421 /* The "Interactions with APPLE_vertex_array_object" section of the 422 * GL_ARB_vertex_array_object spec says: 423 * 424 * "The first bind call, either BindVertexArray or 425 * BindVertexArrayAPPLE, determines the semantic of the object." 426 */ 427 newObj->ARBsemantics = genRequired; 428 newObj->EverBound = GL_TRUE; 429 } 430 } 431 432 if (ctx->Array.DrawMethod == DRAW_ARRAYS) { 433 /* The _DrawArrays pointer is pointing at the VAO being unbound and 434 * that VAO may be in the process of being deleted. If it's not going 435 * to be deleted, this will have no effect, because the pointer needs 436 * to be updated by the VBO module anyway. 437 * 438 * Before the VBO module can update the pointer, we have to set it 439 * to NULL for drivers not to set up arrays which are not bound, 440 * or to prevent a crash if the VAO being unbound is going to be 441 * deleted. 442 */ 443 ctx->Array._DrawArrays = NULL; 444 ctx->Array.DrawMethod = DRAW_NONE; 445 } 446 447 ctx->NewState |= _NEW_ARRAY; 448 _mesa_reference_vao(ctx, &ctx->Array.VAO, newObj); 449 450 /* Pass BindVertexArray call to device driver */ 451 if (ctx->Driver.BindArrayObject && newObj) 452 ctx->Driver.BindArrayObject(ctx, newObj); 453} 454 455 456/** 457 * ARB version of glBindVertexArray() 458 * This function behaves differently from glBindVertexArrayAPPLE() in 459 * that this function requires all ids to have been previously generated 460 * by glGenVertexArrays[APPLE](). 461 */ 462void GLAPIENTRY 463_mesa_BindVertexArray( GLuint id ) 464{ 465 GET_CURRENT_CONTEXT(ctx); 466 bind_vertex_array(ctx, id, GL_TRUE); 467} 468 469 470/** 471 * Bind a new array. 472 * 473 * \todo 474 * The binding could be done more efficiently by comparing the non-NULL 475 * pointers in the old and new objects. The only arrays that are "dirty" are 476 * the ones that are non-NULL in either object. 477 */ 478void GLAPIENTRY 479_mesa_BindVertexArrayAPPLE( GLuint id ) 480{ 481 GET_CURRENT_CONTEXT(ctx); 482 bind_vertex_array(ctx, id, GL_FALSE); 483} 484 485 486/** 487 * Delete a set of array objects. 488 * 489 * \param n Number of array objects to delete. 490 * \param ids Array of \c n array object IDs. 491 */ 492void GLAPIENTRY 493_mesa_DeleteVertexArrays(GLsizei n, const GLuint *ids) 494{ 495 GET_CURRENT_CONTEXT(ctx); 496 GLsizei i; 497 498 if (n < 0) { 499 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteVertexArray(n)"); 500 return; 501 } 502 503 for (i = 0; i < n; i++) { 504 struct gl_vertex_array_object *obj = _mesa_lookup_vao(ctx, ids[i]); 505 506 if ( obj != NULL ) { 507 assert( obj->Name == ids[i] ); 508 509 /* If the array object is currently bound, the spec says "the binding 510 * for that object reverts to zero and the default vertex array 511 * becomes current." 512 */ 513 if ( obj == ctx->Array.VAO ) { 514 _mesa_BindVertexArray(0); 515 } 516 517 /* The ID is immediately freed for re-use */ 518 remove_array_object(ctx, obj); 519 520 if (ctx->Array.LastLookedUpVAO == obj) 521 _mesa_reference_vao(ctx, &ctx->Array.LastLookedUpVAO, NULL); 522 523 /* Unreference the array object. 524 * If refcount hits zero, the object will be deleted. 525 */ 526 _mesa_reference_vao(ctx, &obj, NULL); 527 } 528 } 529} 530 531 532/** 533 * Generate a set of unique array object IDs and store them in \c arrays. 534 * Helper for _mesa_GenVertexArrays[APPLE]() and _mesa_CreateVertexArrays() 535 * below. 536 * 537 * \param n Number of IDs to generate. 538 * \param arrays Array of \c n locations to store the IDs. 539 * \param create Indicates that the objects should also be created. 540 * \param func The name of the GL entry point. 541 */ 542static void 543gen_vertex_arrays(struct gl_context *ctx, GLsizei n, GLuint *arrays, 544 bool create, const char *func) 545{ 546 GLuint first; 547 GLint i; 548 549 if (n < 0) { 550 _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", func); 551 return; 552 } 553 554 if (!arrays) { 555 return; 556 } 557 558 first = _mesa_HashFindFreeKeyBlock(ctx->Array.Objects, n); 559 560 /* For the sake of simplicity we create the array objects in both 561 * the Gen* and Create* cases. The only difference is the value of 562 * EverBound, which is set to true in the Create* case. 563 */ 564 for (i = 0; i < n; i++) { 565 struct gl_vertex_array_object *obj; 566 GLuint name = first + i; 567 568 obj = (*ctx->Driver.NewArrayObject)( ctx, name ); 569 if (!obj) { 570 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func); 571 return; 572 } 573 obj->EverBound = create; 574 save_array_object(ctx, obj); 575 arrays[i] = first + i; 576 } 577} 578 579 580/** 581 * ARB version of glGenVertexArrays() 582 * All arrays will be required to live in VBOs. 583 */ 584void GLAPIENTRY 585_mesa_GenVertexArrays(GLsizei n, GLuint *arrays) 586{ 587 GET_CURRENT_CONTEXT(ctx); 588 gen_vertex_arrays(ctx, n, arrays, false, "glGenVertexArrays"); 589} 590 591 592/** 593 * APPLE version of glGenVertexArraysAPPLE() 594 * Arrays may live in VBOs or ordinary memory. 595 */ 596void GLAPIENTRY 597_mesa_GenVertexArraysAPPLE(GLsizei n, GLuint *arrays) 598{ 599 GET_CURRENT_CONTEXT(ctx); 600 gen_vertex_arrays(ctx, n, arrays, false, "glGenVertexArraysAPPLE"); 601} 602 603 604/** 605 * ARB_direct_state_access 606 * Generates ID's and creates the array objects. 607 */ 608void GLAPIENTRY 609_mesa_CreateVertexArrays(GLsizei n, GLuint *arrays) 610{ 611 GET_CURRENT_CONTEXT(ctx); 612 gen_vertex_arrays(ctx, n, arrays, true, "glCreateVertexArrays"); 613} 614 615 616/** 617 * Determine if ID is the name of an array object. 618 * 619 * \param id ID of the potential array object. 620 * \return \c GL_TRUE if \c id is the name of a array object, 621 * \c GL_FALSE otherwise. 622 */ 623GLboolean GLAPIENTRY 624_mesa_IsVertexArray( GLuint id ) 625{ 626 struct gl_vertex_array_object * obj; 627 GET_CURRENT_CONTEXT(ctx); 628 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 629 630 if (id == 0) 631 return GL_FALSE; 632 633 obj = _mesa_lookup_vao(ctx, id); 634 if (obj == NULL) 635 return GL_FALSE; 636 637 return obj->EverBound; 638} 639 640 641/** 642 * Sets the element array buffer binding of a vertex array object. 643 * 644 * This is the ARB_direct_state_access equivalent of 645 * glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer). 646 */ 647void GLAPIENTRY 648_mesa_VertexArrayElementBuffer(GLuint vaobj, GLuint buffer) 649{ 650 GET_CURRENT_CONTEXT(ctx); 651 struct gl_vertex_array_object *vao; 652 struct gl_buffer_object *bufObj; 653 654 ASSERT_OUTSIDE_BEGIN_END(ctx); 655 656 /* The GL_ARB_direct_state_access specification says: 657 * 658 * "An INVALID_OPERATION error is generated by VertexArrayElementBuffer 659 * if <vaobj> is not [compatibility profile: zero or] the name of an 660 * existing vertex array object." 661 */ 662 vao =_mesa_lookup_vao_err(ctx, vaobj, "glVertexArrayElementBuffer"); 663 if (!vao) 664 return; 665 666 /* The GL_ARB_direct_state_access specification says: 667 * 668 * "An INVALID_OPERATION error is generated if <buffer> is not zero or 669 * the name of an existing buffer object." 670 */ 671 if (buffer != 0) 672 bufObj = _mesa_lookup_bufferobj_err(ctx, buffer, 673 "glVertexArrayElementBuffer"); 674 else 675 bufObj = ctx->Shared->NullBufferObj; 676 677 if (bufObj) 678 _mesa_reference_buffer_object(ctx, &vao->IndexBufferObj, bufObj); 679} 680 681 682void GLAPIENTRY 683_mesa_GetVertexArrayiv(GLuint vaobj, GLenum pname, GLint *param) 684{ 685 GET_CURRENT_CONTEXT(ctx); 686 struct gl_vertex_array_object *vao; 687 688 ASSERT_OUTSIDE_BEGIN_END(ctx); 689 690 /* The GL_ARB_direct_state_access specification says: 691 * 692 * "An INVALID_OPERATION error is generated if <vaobj> is not 693 * [compatibility profile: zero or] the name of an existing 694 * vertex array object." 695 */ 696 vao =_mesa_lookup_vao_err(ctx, vaobj, "glGetVertexArrayiv"); 697 if (!vao) 698 return; 699 700 /* The GL_ARB_direct_state_access specification says: 701 * 702 * "An INVALID_ENUM error is generated if <pname> is not 703 * ELEMENT_ARRAY_BUFFER_BINDING." 704 */ 705 if (pname != GL_ELEMENT_ARRAY_BUFFER_BINDING) { 706 _mesa_error(ctx, GL_INVALID_ENUM, 707 "glGetVertexArrayiv(pname != " 708 "GL_ELEMENT_ARRAY_BUFFER_BINDING)"); 709 return; 710 } 711 712 param[0] = vao->IndexBufferObj->Name; 713} 714