arrayobj.c revision 496bf04905197dc46c2dffe281008bd7f5edf8a8
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.6
4 *
5 * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
6 * (C) Copyright IBM Corporation 2006
7 * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * BRIAN PAUL OR IBM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28
29/**
30 * \file arrayobj.c
31 * Functions for the GL_APPLE_vertex_array_object extension.
32 *
33 * \todo
34 * The code in this file borrows a lot from bufferobj.c.  There's a certain
35 * amount of cruft left over from that origin that may be unnecessary.
36 *
37 * \author Ian Romanick <idr@us.ibm.com>
38 * \author Brian Paul
39 */
40
41
42#include "glheader.h"
43#include "hash.h"
44#include "image.h"
45#include "imports.h"
46#include "context.h"
47#include "mfeatures.h"
48#if FEATURE_ARB_vertex_buffer_object
49#include "bufferobj.h"
50#endif
51#include "arrayobj.h"
52#include "macros.h"
53#include "mtypes.h"
54#include "varray.h"
55#include "main/dispatch.h"
56
57
58/**
59 * Look up the array object for the given ID.
60 *
61 * \returns
62 * Either a pointer to the array object with the specified ID or \c NULL for
63 * a non-existent ID.  The spec defines ID 0 as being technically
64 * non-existent.
65 */
66
67static INLINE struct gl_array_object *
68lookup_arrayobj(struct gl_context *ctx, GLuint id)
69{
70   if (id == 0)
71      return NULL;
72   else
73      return (struct gl_array_object *)
74         _mesa_HashLookup(ctx->Array.Objects, id);
75}
76
77
78/**
79 * For all the vertex arrays in the array object, unbind any pointers
80 * to any buffer objects (VBOs).
81 * This is done just prior to array object destruction.
82 */
83static void
84unbind_array_object_vbos(struct gl_context *ctx, struct gl_array_object *obj)
85{
86   GLuint i;
87
88   _mesa_reference_buffer_object(ctx, &obj->Vertex.BufferObj, NULL);
89   _mesa_reference_buffer_object(ctx, &obj->Weight.BufferObj, NULL);
90   _mesa_reference_buffer_object(ctx, &obj->Normal.BufferObj, NULL);
91   _mesa_reference_buffer_object(ctx, &obj->Color.BufferObj, NULL);
92   _mesa_reference_buffer_object(ctx, &obj->SecondaryColor.BufferObj, NULL);
93   _mesa_reference_buffer_object(ctx, &obj->FogCoord.BufferObj, NULL);
94   _mesa_reference_buffer_object(ctx, &obj->Index.BufferObj, NULL);
95   _mesa_reference_buffer_object(ctx, &obj->EdgeFlag.BufferObj, NULL);
96
97   for (i = 0; i < Elements(obj->TexCoord); i++)
98      _mesa_reference_buffer_object(ctx, &obj->TexCoord[i].BufferObj, NULL);
99
100   for (i = 0; i < Elements(obj->VertexAttrib); i++)
101      _mesa_reference_buffer_object(ctx, &obj->VertexAttrib[i].BufferObj,NULL);
102
103#if FEATURE_point_size_array
104   _mesa_reference_buffer_object(ctx, &obj->PointSize.BufferObj, NULL);
105#endif
106}
107
108
109/**
110 * Allocate and initialize a new vertex array object.
111 *
112 * This function is intended to be called via
113 * \c dd_function_table::NewArrayObject.
114 */
115struct gl_array_object *
116_mesa_new_array_object( struct gl_context *ctx, GLuint name )
117{
118   struct gl_array_object *obj = CALLOC_STRUCT(gl_array_object);
119   if (obj)
120      _mesa_initialize_array_object(ctx, obj, name);
121   return obj;
122}
123
124
125/**
126 * Delete an array object.
127 *
128 * This function is intended to be called via
129 * \c dd_function_table::DeleteArrayObject.
130 */
131void
132_mesa_delete_array_object( struct gl_context *ctx, struct gl_array_object *obj )
133{
134   (void) ctx;
135   unbind_array_object_vbos(ctx, obj);
136   _glthread_DESTROY_MUTEX(obj->Mutex);
137   free(obj);
138}
139
140
141/**
142 * Set ptr to arrayObj w/ reference counting.
143 */
144void
145_mesa_reference_array_object(struct gl_context *ctx,
146                             struct gl_array_object **ptr,
147                             struct gl_array_object *arrayObj)
148{
149   if (*ptr == arrayObj)
150      return;
151
152   if (*ptr) {
153      /* Unreference the old array object */
154      GLboolean deleteFlag = GL_FALSE;
155      struct gl_array_object *oldObj = *ptr;
156
157      _glthread_LOCK_MUTEX(oldObj->Mutex);
158      ASSERT(oldObj->RefCount > 0);
159      oldObj->RefCount--;
160#if 0
161      printf("ArrayObj %p %d DECR to %d\n",
162             (void *) oldObj, oldObj->Name, oldObj->RefCount);
163#endif
164      deleteFlag = (oldObj->RefCount == 0);
165      _glthread_UNLOCK_MUTEX(oldObj->Mutex);
166
167      if (deleteFlag) {
168	 ASSERT(ctx->Driver.DeleteArrayObject);
169         ctx->Driver.DeleteArrayObject(ctx, oldObj);
170      }
171
172      *ptr = NULL;
173   }
174   ASSERT(!*ptr);
175
176   if (arrayObj) {
177      /* reference new array object */
178      _glthread_LOCK_MUTEX(arrayObj->Mutex);
179      if (arrayObj->RefCount == 0) {
180         /* this array's being deleted (look just above) */
181         /* Not sure this can every really happen.  Warn if it does. */
182         _mesa_problem(NULL, "referencing deleted array object");
183         *ptr = NULL;
184      }
185      else {
186         arrayObj->RefCount++;
187#if 0
188         printf("ArrayObj %p %d INCR to %d\n",
189                (void *) arrayObj, arrayObj->Name, arrayObj->RefCount);
190#endif
191         *ptr = arrayObj;
192      }
193      _glthread_UNLOCK_MUTEX(arrayObj->Mutex);
194   }
195}
196
197
198
199static void
200init_array(struct gl_context *ctx,
201           struct gl_client_array *array, GLint size, GLint type)
202{
203   array->Size = size;
204   array->Type = type;
205   array->Format = GL_RGBA; /* only significant for GL_EXT_vertex_array_bgra */
206   array->Stride = 0;
207   array->StrideB = 0;
208   array->Ptr = NULL;
209   array->Enabled = GL_FALSE;
210   array->Normalized = GL_FALSE;
211   array->_ElementSize = size * _mesa_sizeof_type(type);
212#if FEATURE_ARB_vertex_buffer_object
213   /* Vertex array buffers */
214   _mesa_reference_buffer_object(ctx, &array->BufferObj,
215                                 ctx->Shared->NullBufferObj);
216#endif
217}
218
219
220/**
221 * Initialize a gl_array_object's arrays.
222 */
223void
224_mesa_initialize_array_object( struct gl_context *ctx,
225			       struct gl_array_object *obj,
226			       GLuint name )
227{
228   GLuint i;
229
230   obj->Name = name;
231
232   _glthread_INIT_MUTEX(obj->Mutex);
233   obj->RefCount = 1;
234
235   /* Init the individual arrays */
236   init_array(ctx, &obj->Vertex, 4, GL_FLOAT);
237   init_array(ctx, &obj->Weight, 1, GL_FLOAT);
238   init_array(ctx, &obj->Normal, 3, GL_FLOAT);
239   init_array(ctx, &obj->Color, 4, GL_FLOAT);
240   init_array(ctx, &obj->SecondaryColor, 3, GL_FLOAT);
241   init_array(ctx, &obj->FogCoord, 1, GL_FLOAT);
242   init_array(ctx, &obj->Index, 1, GL_FLOAT);
243   for (i = 0; i < Elements(obj->TexCoord); i++) {
244      init_array(ctx, &obj->TexCoord[i], 4, GL_FLOAT);
245   }
246   init_array(ctx, &obj->EdgeFlag, 1, GL_BOOL);
247   for (i = 0; i < Elements(obj->VertexAttrib); i++) {
248      init_array(ctx, &obj->VertexAttrib[i], 4, GL_FLOAT);
249   }
250
251#if FEATURE_point_size_array
252   init_array(ctx, &obj->PointSize, 1, GL_FLOAT);
253#endif
254}
255
256
257/**
258 * Add the given array object to the array object pool.
259 */
260static void
261save_array_object( struct gl_context *ctx, struct gl_array_object *obj )
262{
263   if (obj->Name > 0) {
264      /* insert into hash table */
265      _mesa_HashInsert(ctx->Array.Objects, obj->Name, obj);
266   }
267}
268
269
270/**
271 * Remove the given array object from the array object pool.
272 * Do not deallocate the array object though.
273 */
274static void
275remove_array_object( struct gl_context *ctx, struct gl_array_object *obj )
276{
277   if (obj->Name > 0) {
278      /* remove from hash table */
279      _mesa_HashRemove(ctx->Array.Objects, obj->Name);
280   }
281}
282
283
284
285/**
286 * Helper for update_arrays().
287 * \return  min(current min, array->_MaxElement).
288 */
289static GLuint
290update_min(GLuint min, struct gl_client_array *array)
291{
292   if (array->Enabled) {
293      _mesa_update_array_max_element(array);
294      return MIN2(min, array->_MaxElement);
295   }
296   else
297      return min;
298}
299
300
301/**
302 * Examine vertex arrays to update the gl_array_object::_MaxElement field.
303 */
304void
305_mesa_update_array_object_max_element(struct gl_context *ctx,
306                                      struct gl_array_object *arrayObj)
307{
308   GLuint i, min = ~0;
309
310   min = update_min(min, &arrayObj->Vertex);
311   min = update_min(min, &arrayObj->Weight);
312   min = update_min(min, &arrayObj->Normal);
313   min = update_min(min, &arrayObj->Color);
314   min = update_min(min, &arrayObj->SecondaryColor);
315   min = update_min(min, &arrayObj->FogCoord);
316   min = update_min(min, &arrayObj->Index);
317   min = update_min(min, &arrayObj->EdgeFlag);
318#if FEATURE_point_size_array
319   min = update_min(min, &arrayObj->PointSize);
320#endif
321   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++)
322      min = update_min(min, &arrayObj->TexCoord[i]);
323   for (i = 0; i < Elements(arrayObj->VertexAttrib); i++)
324      min = update_min(min, &arrayObj->VertexAttrib[i]);
325
326   /* _MaxElement is one past the last legal array element */
327   arrayObj->_MaxElement = min;
328}
329
330
331/**********************************************************************/
332/* API Functions                                                      */
333/**********************************************************************/
334
335
336/**
337 * Helper for _mesa_BindVertexArray() and _mesa_BindVertexArrayAPPLE().
338 * \param genRequired  specifies behavour when id was not generated with
339 *                     glGenVertexArrays().
340 */
341static void
342bind_vertex_array(struct gl_context *ctx, GLuint id, GLboolean genRequired)
343{
344   struct gl_array_object * const oldObj = ctx->Array.ArrayObj;
345   struct gl_array_object *newObj = NULL;
346   ASSERT_OUTSIDE_BEGIN_END(ctx);
347
348   ASSERT(oldObj != NULL);
349
350   if ( oldObj->Name == id )
351      return;   /* rebinding the same array object- no change */
352
353   /*
354    * Get pointer to new array object (newObj)
355    */
356   if (id == 0) {
357      /* The spec says there is no array object named 0, but we use
358       * one internally because it simplifies things.
359       */
360      newObj = ctx->Array.DefaultArrayObj;
361   }
362   else {
363      /* non-default array object */
364      newObj = lookup_arrayobj(ctx, id);
365      if (!newObj) {
366         if (genRequired) {
367            _mesa_error(ctx, GL_INVALID_OPERATION, "glBindVertexArray(id)");
368            return;
369         }
370
371         /* For APPLE version, generate a new array object now */
372	 newObj = (*ctx->Driver.NewArrayObject)(ctx, id);
373         if (!newObj) {
374            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindVertexArrayAPPLE");
375            return;
376         }
377         save_array_object(ctx, newObj);
378      }
379   }
380
381   ctx->NewState |= _NEW_ARRAY;
382   ctx->Array.NewState |= _NEW_ARRAY_ALL;
383   _mesa_reference_array_object(ctx, &ctx->Array.ArrayObj, newObj);
384
385   /* Pass BindVertexArray call to device driver */
386   if (ctx->Driver.BindArrayObject && newObj)
387      ctx->Driver.BindArrayObject(ctx, newObj);
388}
389
390
391/**
392 * ARB version of glBindVertexArray()
393 * This function behaves differently from glBindVertexArrayAPPLE() in
394 * that this function requires all ids to have been previously generated
395 * by glGenVertexArrays[APPLE]().
396 */
397void GLAPIENTRY
398_mesa_BindVertexArray( GLuint id )
399{
400   GET_CURRENT_CONTEXT(ctx);
401   bind_vertex_array(ctx, id, GL_TRUE);
402}
403
404
405/**
406 * Bind a new array.
407 *
408 * \todo
409 * The binding could be done more efficiently by comparing the non-NULL
410 * pointers in the old and new objects.  The only arrays that are "dirty" are
411 * the ones that are non-NULL in either object.
412 */
413void GLAPIENTRY
414_mesa_BindVertexArrayAPPLE( GLuint id )
415{
416   GET_CURRENT_CONTEXT(ctx);
417   bind_vertex_array(ctx, id, GL_FALSE);
418}
419
420
421/**
422 * Delete a set of array objects.
423 *
424 * \param n      Number of array objects to delete.
425 * \param ids    Array of \c n array object IDs.
426 */
427void GLAPIENTRY
428_mesa_DeleteVertexArraysAPPLE(GLsizei n, const GLuint *ids)
429{
430   GET_CURRENT_CONTEXT(ctx);
431   GLsizei i;
432   ASSERT_OUTSIDE_BEGIN_END(ctx);
433
434   if (n < 0) {
435      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteVertexArrayAPPLE(n)");
436      return;
437   }
438
439   for (i = 0; i < n; i++) {
440      struct gl_array_object *obj = lookup_arrayobj(ctx, ids[i]);
441
442      if ( obj != NULL ) {
443	 ASSERT( obj->Name == ids[i] );
444
445	 /* If the array object is currently bound, the spec says "the binding
446	  * for that object reverts to zero and the default vertex array
447	  * becomes current."
448	  */
449	 if ( obj == ctx->Array.ArrayObj ) {
450	    CALL_BindVertexArrayAPPLE( ctx->Exec, (0) );
451	 }
452
453	 /* The ID is immediately freed for re-use */
454	 remove_array_object(ctx, obj);
455
456         /* Unreference the array object.
457          * If refcount hits zero, the object will be deleted.
458          */
459         _mesa_reference_array_object(ctx, &obj, NULL);
460      }
461   }
462}
463
464
465/**
466 * Generate a set of unique array object IDs and store them in \c arrays.
467 * Helper for _mesa_GenVertexArrays[APPLE]() functions below.
468 * \param n       Number of IDs to generate.
469 * \param arrays  Array of \c n locations to store the IDs.
470 * \param vboOnly Will arrays have to reside in VBOs?
471 */
472static void
473gen_vertex_arrays(struct gl_context *ctx, GLsizei n, GLuint *arrays,
474                  GLboolean vboOnly)
475{
476   GLuint first;
477   GLint i;
478   ASSERT_OUTSIDE_BEGIN_END(ctx);
479
480   if (n < 0) {
481      _mesa_error(ctx, GL_INVALID_VALUE, "glGenVertexArraysAPPLE");
482      return;
483   }
484
485   if (!arrays) {
486      return;
487   }
488
489   first = _mesa_HashFindFreeKeyBlock(ctx->Array.Objects, n);
490
491   /* Allocate new, empty array objects and return identifiers */
492   for (i = 0; i < n; i++) {
493      struct gl_array_object *obj;
494      GLuint name = first + i;
495
496      obj = (*ctx->Driver.NewArrayObject)( ctx, name );
497      if (!obj) {
498         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenVertexArraysAPPLE");
499         return;
500      }
501      obj->VBOonly = vboOnly;
502      save_array_object(ctx, obj);
503      arrays[i] = first + i;
504   }
505}
506
507
508/**
509 * ARB version of glGenVertexArrays()
510 * All arrays will be required to live in VBOs.
511 */
512void GLAPIENTRY
513_mesa_GenVertexArrays(GLsizei n, GLuint *arrays)
514{
515   GET_CURRENT_CONTEXT(ctx);
516   gen_vertex_arrays(ctx, n, arrays, GL_TRUE);
517}
518
519
520/**
521 * APPLE version of glGenVertexArraysAPPLE()
522 * Arrays may live in VBOs or ordinary memory.
523 */
524void GLAPIENTRY
525_mesa_GenVertexArraysAPPLE(GLsizei n, GLuint *arrays)
526{
527   GET_CURRENT_CONTEXT(ctx);
528   gen_vertex_arrays(ctx, n, arrays, GL_FALSE);
529}
530
531
532/**
533 * Determine if ID is the name of an array object.
534 *
535 * \param id  ID of the potential array object.
536 * \return  \c GL_TRUE if \c id is the name of a array object,
537 *          \c GL_FALSE otherwise.
538 */
539GLboolean GLAPIENTRY
540_mesa_IsVertexArrayAPPLE( GLuint id )
541{
542   struct gl_array_object * obj;
543   GET_CURRENT_CONTEXT(ctx);
544   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
545
546   if (id == 0)
547      return GL_FALSE;
548
549   obj = lookup_arrayobj(ctx, id);
550
551   return (obj != NULL) ? GL_TRUE : GL_FALSE;
552}
553