mesa: Replace _NEW_ARRAY_* bits with VERT_BIT_*
[mesa.git] / src / mesa / main / arrayobj.c
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
67 static inline struct gl_array_object *
68 lookup_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 */
83 static void
84 unbind_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 */
115 struct 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 */
131 void
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 _mesa_reference_buffer_object(ctx, &obj->ElementArrayBufferObj, NULL);
137 _glthread_DESTROY_MUTEX(obj->Mutex);
138 free(obj);
139 }
140
141
142 /**
143 * Set ptr to arrayObj w/ reference counting.
144 */
145 void
146 _mesa_reference_array_object(struct gl_context *ctx,
147 struct gl_array_object **ptr,
148 struct gl_array_object *arrayObj)
149 {
150 if (*ptr == arrayObj)
151 return;
152
153 if (*ptr) {
154 /* Unreference the old array object */
155 GLboolean deleteFlag = GL_FALSE;
156 struct gl_array_object *oldObj = *ptr;
157
158 _glthread_LOCK_MUTEX(oldObj->Mutex);
159 ASSERT(oldObj->RefCount > 0);
160 oldObj->RefCount--;
161 #if 0
162 printf("ArrayObj %p %d DECR to %d\n",
163 (void *) oldObj, oldObj->Name, oldObj->RefCount);
164 #endif
165 deleteFlag = (oldObj->RefCount == 0);
166 _glthread_UNLOCK_MUTEX(oldObj->Mutex);
167
168 if (deleteFlag) {
169 ASSERT(ctx->Driver.DeleteArrayObject);
170 ctx->Driver.DeleteArrayObject(ctx, oldObj);
171 }
172
173 *ptr = NULL;
174 }
175 ASSERT(!*ptr);
176
177 if (arrayObj) {
178 /* reference new array object */
179 _glthread_LOCK_MUTEX(arrayObj->Mutex);
180 if (arrayObj->RefCount == 0) {
181 /* this array's being deleted (look just above) */
182 /* Not sure this can every really happen. Warn if it does. */
183 _mesa_problem(NULL, "referencing deleted array object");
184 *ptr = NULL;
185 }
186 else {
187 arrayObj->RefCount++;
188 #if 0
189 printf("ArrayObj %p %d INCR to %d\n",
190 (void *) arrayObj, arrayObj->Name, arrayObj->RefCount);
191 #endif
192 *ptr = arrayObj;
193 }
194 _glthread_UNLOCK_MUTEX(arrayObj->Mutex);
195 }
196 }
197
198
199
200 static void
201 init_array(struct gl_context *ctx,
202 struct gl_client_array *array, GLint size, GLint type)
203 {
204 array->Size = size;
205 array->Type = type;
206 array->Format = GL_RGBA; /* only significant for GL_EXT_vertex_array_bgra */
207 array->Stride = 0;
208 array->StrideB = 0;
209 array->Ptr = NULL;
210 array->Enabled = GL_FALSE;
211 array->Normalized = GL_FALSE;
212 array->Integer = GL_FALSE;
213 array->_ElementSize = size * _mesa_sizeof_type(type);
214 #if FEATURE_ARB_vertex_buffer_object
215 /* Vertex array buffers */
216 _mesa_reference_buffer_object(ctx, &array->BufferObj,
217 ctx->Shared->NullBufferObj);
218 #endif
219 }
220
221
222 /**
223 * Initialize a gl_array_object's arrays.
224 */
225 void
226 _mesa_initialize_array_object( struct gl_context *ctx,
227 struct gl_array_object *obj,
228 GLuint name )
229 {
230 GLuint i;
231
232 obj->Name = name;
233
234 _glthread_INIT_MUTEX(obj->Mutex);
235 obj->RefCount = 1;
236
237 /* Init the individual arrays */
238 init_array(ctx, &obj->Vertex, 4, GL_FLOAT);
239 init_array(ctx, &obj->Weight, 1, GL_FLOAT);
240 init_array(ctx, &obj->Normal, 3, GL_FLOAT);
241 init_array(ctx, &obj->Color, 4, GL_FLOAT);
242 init_array(ctx, &obj->SecondaryColor, 3, GL_FLOAT);
243 init_array(ctx, &obj->FogCoord, 1, GL_FLOAT);
244 init_array(ctx, &obj->Index, 1, GL_FLOAT);
245 for (i = 0; i < Elements(obj->TexCoord); i++) {
246 init_array(ctx, &obj->TexCoord[i], 4, GL_FLOAT);
247 }
248 init_array(ctx, &obj->EdgeFlag, 1, GL_BOOL);
249 for (i = 0; i < Elements(obj->VertexAttrib); i++) {
250 init_array(ctx, &obj->VertexAttrib[i], 4, GL_FLOAT);
251 }
252
253 #if FEATURE_point_size_array
254 init_array(ctx, &obj->PointSize, 1, GL_FLOAT);
255 #endif
256
257 _mesa_reference_buffer_object(ctx, &obj->ElementArrayBufferObj,
258 ctx->Shared->NullBufferObj);
259 }
260
261
262 /**
263 * Add the given array object to the array object pool.
264 */
265 static void
266 save_array_object( struct gl_context *ctx, struct gl_array_object *obj )
267 {
268 if (obj->Name > 0) {
269 /* insert into hash table */
270 _mesa_HashInsert(ctx->Array.Objects, obj->Name, obj);
271 }
272 }
273
274
275 /**
276 * Remove the given array object from the array object pool.
277 * Do not deallocate the array object though.
278 */
279 static void
280 remove_array_object( struct gl_context *ctx, struct gl_array_object *obj )
281 {
282 if (obj->Name > 0) {
283 /* remove from hash table */
284 _mesa_HashRemove(ctx->Array.Objects, obj->Name);
285 }
286 }
287
288
289
290 /**
291 * Helper for update_arrays().
292 * \return min(current min, array->_MaxElement).
293 */
294 static GLuint
295 update_min(GLuint min, struct gl_client_array *array)
296 {
297 if (array->Enabled) {
298 _mesa_update_array_max_element(array);
299 return MIN2(min, array->_MaxElement);
300 }
301 else
302 return min;
303 }
304
305
306 /**
307 * Examine vertex arrays to update the gl_array_object::_MaxElement field.
308 */
309 void
310 _mesa_update_array_object_max_element(struct gl_context *ctx,
311 struct gl_array_object *arrayObj)
312 {
313 GLuint i, min = ~0;
314
315 min = update_min(min, &arrayObj->Vertex);
316 min = update_min(min, &arrayObj->Weight);
317 min = update_min(min, &arrayObj->Normal);
318 min = update_min(min, &arrayObj->Color);
319 min = update_min(min, &arrayObj->SecondaryColor);
320 min = update_min(min, &arrayObj->FogCoord);
321 min = update_min(min, &arrayObj->Index);
322 min = update_min(min, &arrayObj->EdgeFlag);
323 #if FEATURE_point_size_array
324 min = update_min(min, &arrayObj->PointSize);
325 #endif
326 for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++)
327 min = update_min(min, &arrayObj->TexCoord[i]);
328 for (i = 0; i < Elements(arrayObj->VertexAttrib); i++)
329 min = update_min(min, &arrayObj->VertexAttrib[i]);
330
331 /* _MaxElement is one past the last legal array element */
332 arrayObj->_MaxElement = min;
333 }
334
335
336 /**********************************************************************/
337 /* API Functions */
338 /**********************************************************************/
339
340
341 /**
342 * Helper for _mesa_BindVertexArray() and _mesa_BindVertexArrayAPPLE().
343 * \param genRequired specifies behavour when id was not generated with
344 * glGenVertexArrays().
345 */
346 static void
347 bind_vertex_array(struct gl_context *ctx, GLuint id, GLboolean genRequired)
348 {
349 struct gl_array_object * const oldObj = ctx->Array.ArrayObj;
350 struct gl_array_object *newObj = NULL;
351 ASSERT_OUTSIDE_BEGIN_END(ctx);
352
353 ASSERT(oldObj != NULL);
354
355 if ( oldObj->Name == id )
356 return; /* rebinding the same array object- no change */
357
358 /*
359 * Get pointer to new array object (newObj)
360 */
361 if (id == 0) {
362 /* The spec says there is no array object named 0, but we use
363 * one internally because it simplifies things.
364 */
365 newObj = ctx->Array.DefaultArrayObj;
366 }
367 else {
368 /* non-default array object */
369 newObj = lookup_arrayobj(ctx, id);
370 if (!newObj) {
371 if (genRequired) {
372 _mesa_error(ctx, GL_INVALID_OPERATION, "glBindVertexArray(id)");
373 return;
374 }
375
376 /* For APPLE version, generate a new array object now */
377 newObj = (*ctx->Driver.NewArrayObject)(ctx, id);
378 if (!newObj) {
379 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindVertexArrayAPPLE");
380 return;
381 }
382 save_array_object(ctx, newObj);
383 }
384 }
385
386 ctx->NewState |= _NEW_ARRAY;
387 ctx->Array.NewState |= VERT_BIT_ALL;
388 _mesa_reference_array_object(ctx, &ctx->Array.ArrayObj, newObj);
389
390 /* Pass BindVertexArray call to device driver */
391 if (ctx->Driver.BindArrayObject && newObj)
392 ctx->Driver.BindArrayObject(ctx, newObj);
393 }
394
395
396 /**
397 * ARB version of glBindVertexArray()
398 * This function behaves differently from glBindVertexArrayAPPLE() in
399 * that this function requires all ids to have been previously generated
400 * by glGenVertexArrays[APPLE]().
401 */
402 void GLAPIENTRY
403 _mesa_BindVertexArray( GLuint id )
404 {
405 GET_CURRENT_CONTEXT(ctx);
406 bind_vertex_array(ctx, id, GL_TRUE);
407 }
408
409
410 /**
411 * Bind a new array.
412 *
413 * \todo
414 * The binding could be done more efficiently by comparing the non-NULL
415 * pointers in the old and new objects. The only arrays that are "dirty" are
416 * the ones that are non-NULL in either object.
417 */
418 void GLAPIENTRY
419 _mesa_BindVertexArrayAPPLE( GLuint id )
420 {
421 GET_CURRENT_CONTEXT(ctx);
422 bind_vertex_array(ctx, id, GL_FALSE);
423 }
424
425
426 /**
427 * Delete a set of array objects.
428 *
429 * \param n Number of array objects to delete.
430 * \param ids Array of \c n array object IDs.
431 */
432 void GLAPIENTRY
433 _mesa_DeleteVertexArraysAPPLE(GLsizei n, const GLuint *ids)
434 {
435 GET_CURRENT_CONTEXT(ctx);
436 GLsizei i;
437 ASSERT_OUTSIDE_BEGIN_END(ctx);
438
439 if (n < 0) {
440 _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteVertexArrayAPPLE(n)");
441 return;
442 }
443
444 for (i = 0; i < n; i++) {
445 struct gl_array_object *obj = lookup_arrayobj(ctx, ids[i]);
446
447 if ( obj != NULL ) {
448 ASSERT( obj->Name == ids[i] );
449
450 /* If the array object is currently bound, the spec says "the binding
451 * for that object reverts to zero and the default vertex array
452 * becomes current."
453 */
454 if ( obj == ctx->Array.ArrayObj ) {
455 CALL_BindVertexArrayAPPLE( ctx->Exec, (0) );
456 }
457
458 /* The ID is immediately freed for re-use */
459 remove_array_object(ctx, obj);
460
461 /* Unreference the array object.
462 * If refcount hits zero, the object will be deleted.
463 */
464 _mesa_reference_array_object(ctx, &obj, NULL);
465 }
466 }
467 }
468
469
470 /**
471 * Generate a set of unique array object IDs and store them in \c arrays.
472 * Helper for _mesa_GenVertexArrays[APPLE]() functions below.
473 * \param n Number of IDs to generate.
474 * \param arrays Array of \c n locations to store the IDs.
475 * \param vboOnly Will arrays have to reside in VBOs?
476 */
477 static void
478 gen_vertex_arrays(struct gl_context *ctx, GLsizei n, GLuint *arrays,
479 GLboolean vboOnly)
480 {
481 GLuint first;
482 GLint i;
483 ASSERT_OUTSIDE_BEGIN_END(ctx);
484
485 if (n < 0) {
486 _mesa_error(ctx, GL_INVALID_VALUE, "glGenVertexArraysAPPLE");
487 return;
488 }
489
490 if (!arrays) {
491 return;
492 }
493
494 first = _mesa_HashFindFreeKeyBlock(ctx->Array.Objects, n);
495
496 /* Allocate new, empty array objects and return identifiers */
497 for (i = 0; i < n; i++) {
498 struct gl_array_object *obj;
499 GLuint name = first + i;
500
501 obj = (*ctx->Driver.NewArrayObject)( ctx, name );
502 if (!obj) {
503 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenVertexArraysAPPLE");
504 return;
505 }
506 obj->VBOonly = vboOnly;
507 save_array_object(ctx, obj);
508 arrays[i] = first + i;
509 }
510 }
511
512
513 /**
514 * ARB version of glGenVertexArrays()
515 * All arrays will be required to live in VBOs.
516 */
517 void GLAPIENTRY
518 _mesa_GenVertexArrays(GLsizei n, GLuint *arrays)
519 {
520 GET_CURRENT_CONTEXT(ctx);
521 gen_vertex_arrays(ctx, n, arrays, GL_TRUE);
522 }
523
524
525 /**
526 * APPLE version of glGenVertexArraysAPPLE()
527 * Arrays may live in VBOs or ordinary memory.
528 */
529 void GLAPIENTRY
530 _mesa_GenVertexArraysAPPLE(GLsizei n, GLuint *arrays)
531 {
532 GET_CURRENT_CONTEXT(ctx);
533 gen_vertex_arrays(ctx, n, arrays, GL_FALSE);
534 }
535
536
537 /**
538 * Determine if ID is the name of an array object.
539 *
540 * \param id ID of the potential array object.
541 * \return \c GL_TRUE if \c id is the name of a array object,
542 * \c GL_FALSE otherwise.
543 */
544 GLboolean GLAPIENTRY
545 _mesa_IsVertexArrayAPPLE( GLuint id )
546 {
547 struct gl_array_object * obj;
548 GET_CURRENT_CONTEXT(ctx);
549 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
550
551 if (id == 0)
552 return GL_FALSE;
553
554 obj = lookup_arrayobj(ctx, id);
555
556 return (obj != NULL) ? GL_TRUE : GL_FALSE;
557 }