arrayobj.c revision 848b8605
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 * For all the vertex binding points in the array object, unbind any pointers
79 * to any buffer objects (VBOs).
80 * This is done just prior to array object destruction.
81 */
82static void
83unbind_array_object_vbos(struct gl_context *ctx, struct gl_vertex_array_object *obj)
84{
85   GLuint i;
86
87   for (i = 0; i < Elements(obj->VertexBinding); i++)
88      _mesa_reference_buffer_object(ctx, &obj->VertexBinding[i].BufferObj, NULL);
89
90   for (i = 0; i < Elements(obj->_VertexAttrib); i++)
91      _mesa_reference_buffer_object(ctx, &obj->_VertexAttrib[i].BufferObj, NULL);
92}
93
94
95/**
96 * Allocate and initialize a new vertex array object.
97 *
98 * This function is intended to be called via
99 * \c dd_function_table::NewArrayObject.
100 */
101struct gl_vertex_array_object *
102_mesa_new_vao(struct gl_context *ctx, GLuint name)
103{
104   struct gl_vertex_array_object *obj = CALLOC_STRUCT(gl_vertex_array_object);
105   if (obj)
106      _mesa_initialize_vao(ctx, obj, name);
107   return obj;
108}
109
110
111/**
112 * Delete an array object.
113 *
114 * This function is intended to be called via
115 * \c dd_function_table::DeleteArrayObject.
116 */
117void
118_mesa_delete_vao(struct gl_context *ctx, struct gl_vertex_array_object *obj)
119{
120   unbind_array_object_vbos(ctx, obj);
121   _mesa_reference_buffer_object(ctx, &obj->IndexBufferObj, NULL);
122   mtx_destroy(&obj->Mutex);
123   free(obj->Label);
124   free(obj);
125}
126
127
128/**
129 * Set ptr to vao w/ reference counting.
130 * Note: this should only be called from the _mesa_reference_vao()
131 * inline function.
132 */
133void
134_mesa_reference_vao_(struct gl_context *ctx,
135                     struct gl_vertex_array_object **ptr,
136                     struct gl_vertex_array_object *vao)
137{
138   assert(*ptr != vao);
139
140   if (*ptr) {
141      /* Unreference the old array object */
142      GLboolean deleteFlag = GL_FALSE;
143      struct gl_vertex_array_object *oldObj = *ptr;
144
145      mtx_lock(&oldObj->Mutex);
146      ASSERT(oldObj->RefCount > 0);
147      oldObj->RefCount--;
148#if 0
149      printf("ArrayObj %p %d DECR to %d\n",
150             (void *) oldObj, oldObj->Name, oldObj->RefCount);
151#endif
152      deleteFlag = (oldObj->RefCount == 0);
153      mtx_unlock(&oldObj->Mutex);
154
155      if (deleteFlag) {
156	 ASSERT(ctx->Driver.DeleteArrayObject);
157         ctx->Driver.DeleteArrayObject(ctx, oldObj);
158      }
159
160      *ptr = NULL;
161   }
162   ASSERT(!*ptr);
163
164   if (vao) {
165      /* reference new array object */
166      mtx_lock(&vao->Mutex);
167      if (vao->RefCount == 0) {
168         /* this array's being deleted (look just above) */
169         /* Not sure this can every really happen.  Warn if it does. */
170         _mesa_problem(NULL, "referencing deleted array object");
171         *ptr = NULL;
172      }
173      else {
174         vao->RefCount++;
175#if 0
176         printf("ArrayObj %p %d INCR to %d\n",
177                (void *) vao, vao->Name, vao->RefCount);
178#endif
179         *ptr = vao;
180      }
181      mtx_unlock(&vao->Mutex);
182   }
183}
184
185
186
187static void
188init_array(struct gl_context *ctx,
189           struct gl_vertex_array_object *obj, GLuint index, GLint size, GLint type)
190{
191   struct gl_vertex_attrib_array *array = &obj->VertexAttrib[index];
192   struct gl_vertex_buffer_binding *binding = &obj->VertexBinding[index];
193
194   array->Size = size;
195   array->Type = type;
196   array->Format = GL_RGBA; /* only significant for GL_EXT_vertex_array_bgra */
197   array->Stride = 0;
198   array->Ptr = NULL;
199   array->RelativeOffset = 0;
200   array->Enabled = GL_FALSE;
201   array->Normalized = GL_FALSE;
202   array->Integer = GL_FALSE;
203   array->_ElementSize = size * _mesa_sizeof_type(type);
204   array->VertexBinding = index;
205
206   binding->Offset = 0;
207   binding->Stride = array->_ElementSize;
208   binding->BufferObj = NULL;
209   binding->_BoundArrays = BITFIELD64_BIT(index);
210
211   /* Vertex array buffers */
212   _mesa_reference_buffer_object(ctx, &binding->BufferObj,
213                                 ctx->Shared->NullBufferObj);
214}
215
216
217/**
218 * Initialize a gl_vertex_array_object's arrays.
219 */
220void
221_mesa_initialize_vao(struct gl_context *ctx,
222                     struct gl_vertex_array_object *obj,
223                     GLuint name)
224{
225   GLuint i;
226
227   obj->Name = name;
228
229   mtx_init(&obj->Mutex, mtx_plain);
230   obj->RefCount = 1;
231
232   /* Init the individual arrays */
233   for (i = 0; i < Elements(obj->_VertexAttrib); i++) {
234      switch (i) {
235      case VERT_ATTRIB_WEIGHT:
236         init_array(ctx, obj, VERT_ATTRIB_WEIGHT, 1, GL_FLOAT);
237         break;
238      case VERT_ATTRIB_NORMAL:
239         init_array(ctx, obj, VERT_ATTRIB_NORMAL, 3, GL_FLOAT);
240         break;
241      case VERT_ATTRIB_COLOR1:
242         init_array(ctx, obj, VERT_ATTRIB_COLOR1, 3, GL_FLOAT);
243         break;
244      case VERT_ATTRIB_FOG:
245         init_array(ctx, obj, VERT_ATTRIB_FOG, 1, GL_FLOAT);
246         break;
247      case VERT_ATTRIB_COLOR_INDEX:
248         init_array(ctx, obj, VERT_ATTRIB_COLOR_INDEX, 1, GL_FLOAT);
249         break;
250      case VERT_ATTRIB_EDGEFLAG:
251         init_array(ctx, obj, VERT_ATTRIB_EDGEFLAG, 1, GL_BOOL);
252         break;
253      case VERT_ATTRIB_POINT_SIZE:
254         init_array(ctx, obj, VERT_ATTRIB_POINT_SIZE, 1, GL_FLOAT);
255         break;
256      default:
257         init_array(ctx, obj, i, 4, GL_FLOAT);
258         break;
259      }
260   }
261
262   _mesa_reference_buffer_object(ctx, &obj->IndexBufferObj,
263                                 ctx->Shared->NullBufferObj);
264}
265
266
267/**
268 * Add the given array object to the array object pool.
269 */
270static void
271save_array_object( struct gl_context *ctx, struct gl_vertex_array_object *obj )
272{
273   if (obj->Name > 0) {
274      /* insert into hash table */
275      _mesa_HashInsert(ctx->Array.Objects, obj->Name, obj);
276   }
277}
278
279
280/**
281 * Remove the given array object from the array object pool.
282 * Do not deallocate the array object though.
283 */
284static void
285remove_array_object( struct gl_context *ctx, struct gl_vertex_array_object *obj )
286{
287   if (obj->Name > 0) {
288      /* remove from hash table */
289      _mesa_HashRemove(ctx->Array.Objects, obj->Name);
290   }
291}
292
293
294
295/**
296 * Helper for _mesa_update_vao_max_element().
297 * \return  min(vao->_VertexAttrib[*]._MaxElement).
298 */
299static GLuint
300compute_max_element(struct gl_vertex_array_object *vao, GLbitfield64 enabled)
301{
302   GLuint min = ~((GLuint)0);
303
304   while (enabled) {
305      struct gl_client_array *client_array;
306      GLint attrib = ffsll(enabled) - 1;
307      enabled ^= BITFIELD64_BIT(attrib);
308
309      client_array = &vao->_VertexAttrib[attrib];
310      assert(client_array->Enabled);
311      _mesa_update_array_max_element(client_array);
312      min = MIN2(min, client_array->_MaxElement);
313   }
314
315   return min;
316}
317
318
319/**
320 * Examine vertex arrays to update the gl_vertex_array_object::_MaxElement field.
321 */
322void
323_mesa_update_vao_max_element(struct gl_context *ctx,
324                                      struct gl_vertex_array_object *vao)
325{
326   GLbitfield64 enabled;
327
328   if (!ctx->VertexProgram._Current ||
329       ctx->VertexProgram._Current == ctx->VertexProgram._TnlProgram) {
330      enabled = _mesa_array_object_get_enabled_ff(vao);
331   } else {
332      enabled = _mesa_array_object_get_enabled_arb(vao);
333   }
334
335   /* _MaxElement is one past the last legal array element */
336   vao->_MaxElement = compute_max_element(vao, enabled);
337}
338
339
340/**
341 * Updates the derived gl_client_arrays when a gl_vertex_attrib_array
342 * or a gl_vertex_buffer_binding has changed.
343 */
344void
345_mesa_update_vao_client_arrays(struct gl_context *ctx,
346                               struct gl_vertex_array_object *vao)
347{
348   GLbitfield64 arrays = vao->NewArrays;
349
350   while (arrays) {
351      struct gl_client_array *client_array;
352      struct gl_vertex_attrib_array *attrib_array;
353      struct gl_vertex_buffer_binding *buffer_binding;
354
355      GLint attrib = ffsll(arrays) - 1;
356      arrays ^= BITFIELD64_BIT(attrib);
357
358      attrib_array = &vao->VertexAttrib[attrib];
359      buffer_binding = &vao->VertexBinding[attrib_array->VertexBinding];
360      client_array = &vao->_VertexAttrib[attrib];
361
362      _mesa_update_client_array(ctx, client_array, attrib_array,
363                                buffer_binding);
364   }
365}
366
367
368/**********************************************************************/
369/* API Functions                                                      */
370/**********************************************************************/
371
372
373/**
374 * Helper for _mesa_BindVertexArray() and _mesa_BindVertexArrayAPPLE().
375 * \param genRequired  specifies behavour when id was not generated with
376 *                     glGenVertexArrays().
377 */
378static void
379bind_vertex_array(struct gl_context *ctx, GLuint id, GLboolean genRequired)
380{
381   struct gl_vertex_array_object * const oldObj = ctx->Array.VAO;
382   struct gl_vertex_array_object *newObj = NULL;
383
384   ASSERT(oldObj != NULL);
385
386   if ( oldObj->Name == id )
387      return;   /* rebinding the same array object- no change */
388
389   /*
390    * Get pointer to new array object (newObj)
391    */
392   if (id == 0) {
393      /* The spec says there is no array object named 0, but we use
394       * one internally because it simplifies things.
395       */
396      newObj = ctx->Array.DefaultVAO;
397   }
398   else {
399      /* non-default array object */
400      newObj = _mesa_lookup_vao(ctx, id);
401      if (!newObj) {
402         if (genRequired) {
403            _mesa_error(ctx, GL_INVALID_OPERATION,
404                        "glBindVertexArray(non-gen name)");
405            return;
406         }
407
408         /* For APPLE version, generate a new array object now */
409	 newObj = (*ctx->Driver.NewArrayObject)(ctx, id);
410         if (!newObj) {
411            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindVertexArrayAPPLE");
412            return;
413         }
414
415         save_array_object(ctx, newObj);
416      }
417
418      if (!newObj->EverBound) {
419         /* The "Interactions with APPLE_vertex_array_object" section of the
420          * GL_ARB_vertex_array_object spec says:
421          *
422          *     "The first bind call, either BindVertexArray or
423          *     BindVertexArrayAPPLE, determines the semantic of the object."
424          */
425         newObj->ARBsemantics = genRequired;
426         newObj->EverBound = GL_TRUE;
427      }
428   }
429
430   if (ctx->Array.DrawMethod == DRAW_ARRAYS) {
431      /* The _DrawArrays pointer is pointing at the VAO being unbound and
432       * that VAO may be in the process of being deleted. If it's not going
433       * to be deleted, this will have no effect, because the pointer needs
434       * to be updated by the VBO module anyway.
435       *
436       * Before the VBO module can update the pointer, we have to set it
437       * to NULL for drivers not to set up arrays which are not bound,
438       * or to prevent a crash if the VAO being unbound is going to be
439       * deleted.
440       */
441      ctx->Array._DrawArrays = NULL;
442      ctx->Array.DrawMethod = DRAW_NONE;
443   }
444
445   ctx->NewState |= _NEW_ARRAY;
446   _mesa_reference_vao(ctx, &ctx->Array.VAO, newObj);
447
448   /* Pass BindVertexArray call to device driver */
449   if (ctx->Driver.BindArrayObject && newObj)
450      ctx->Driver.BindArrayObject(ctx, newObj);
451}
452
453
454/**
455 * ARB version of glBindVertexArray()
456 * This function behaves differently from glBindVertexArrayAPPLE() in
457 * that this function requires all ids to have been previously generated
458 * by glGenVertexArrays[APPLE]().
459 */
460void GLAPIENTRY
461_mesa_BindVertexArray( GLuint id )
462{
463   GET_CURRENT_CONTEXT(ctx);
464   bind_vertex_array(ctx, id, GL_TRUE);
465}
466
467
468/**
469 * Bind a new array.
470 *
471 * \todo
472 * The binding could be done more efficiently by comparing the non-NULL
473 * pointers in the old and new objects.  The only arrays that are "dirty" are
474 * the ones that are non-NULL in either object.
475 */
476void GLAPIENTRY
477_mesa_BindVertexArrayAPPLE( GLuint id )
478{
479   GET_CURRENT_CONTEXT(ctx);
480   bind_vertex_array(ctx, id, GL_FALSE);
481}
482
483
484/**
485 * Delete a set of array objects.
486 *
487 * \param n      Number of array objects to delete.
488 * \param ids    Array of \c n array object IDs.
489 */
490void GLAPIENTRY
491_mesa_DeleteVertexArrays(GLsizei n, const GLuint *ids)
492{
493   GET_CURRENT_CONTEXT(ctx);
494   GLsizei i;
495
496   if (n < 0) {
497      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteVertexArray(n)");
498      return;
499   }
500
501   for (i = 0; i < n; i++) {
502      struct gl_vertex_array_object *obj = _mesa_lookup_vao(ctx, ids[i]);
503
504      if ( obj != NULL ) {
505	 ASSERT( obj->Name == ids[i] );
506
507	 /* If the array object is currently bound, the spec says "the binding
508	  * for that object reverts to zero and the default vertex array
509	  * becomes current."
510	  */
511	 if ( obj == ctx->Array.VAO ) {
512	    _mesa_BindVertexArray(0);
513	 }
514
515	 /* The ID is immediately freed for re-use */
516	 remove_array_object(ctx, obj);
517
518         /* Unreference the array object.
519          * If refcount hits zero, the object will be deleted.
520          */
521         _mesa_reference_vao(ctx, &obj, NULL);
522      }
523   }
524}
525
526
527/**
528 * Generate a set of unique array object IDs and store them in \c arrays.
529 * Helper for _mesa_GenVertexArrays[APPLE]() functions below.
530 * \param n       Number of IDs to generate.
531 * \param arrays  Array of \c n locations to store the IDs.
532 * \param vboOnly Will arrays have to reside in VBOs?
533 */
534static void
535gen_vertex_arrays(struct gl_context *ctx, GLsizei n, GLuint *arrays)
536{
537   GLuint first;
538   GLint i;
539
540   if (n < 0) {
541      _mesa_error(ctx, GL_INVALID_VALUE, "glGenVertexArrays");
542      return;
543   }
544
545   if (!arrays) {
546      return;
547   }
548
549   first = _mesa_HashFindFreeKeyBlock(ctx->Array.Objects, n);
550
551   /* Allocate new, empty array objects and return identifiers */
552   for (i = 0; i < n; i++) {
553      struct gl_vertex_array_object *obj;
554      GLuint name = first + i;
555
556      obj = (*ctx->Driver.NewArrayObject)( ctx, name );
557      if (!obj) {
558         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenVertexArrays");
559         return;
560      }
561      save_array_object(ctx, obj);
562      arrays[i] = first + i;
563   }
564}
565
566
567/**
568 * ARB version of glGenVertexArrays()
569 * All arrays will be required to live in VBOs.
570 */
571void GLAPIENTRY
572_mesa_GenVertexArrays(GLsizei n, GLuint *arrays)
573{
574   GET_CURRENT_CONTEXT(ctx);
575   gen_vertex_arrays(ctx, n, arrays);
576}
577
578
579/**
580 * APPLE version of glGenVertexArraysAPPLE()
581 * Arrays may live in VBOs or ordinary memory.
582 */
583void GLAPIENTRY
584_mesa_GenVertexArraysAPPLE(GLsizei n, GLuint *arrays)
585{
586   GET_CURRENT_CONTEXT(ctx);
587   gen_vertex_arrays(ctx, n, arrays);
588}
589
590
591/**
592 * Determine if ID is the name of an array object.
593 *
594 * \param id  ID of the potential array object.
595 * \return  \c GL_TRUE if \c id is the name of a array object,
596 *          \c GL_FALSE otherwise.
597 */
598GLboolean GLAPIENTRY
599_mesa_IsVertexArray( GLuint id )
600{
601   struct gl_vertex_array_object * obj;
602   GET_CURRENT_CONTEXT(ctx);
603   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
604
605   if (id == 0)
606      return GL_FALSE;
607
608   obj = _mesa_lookup_vao(ctx, id);
609   if (obj == NULL)
610      return GL_FALSE;
611
612   return obj->EverBound;
613}
614