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