/* * Mesa 3-D graphics library * Version: 7.2 * * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. * (C) Copyright IBM Corporation 2006 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL OR IBM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * \file arrayobj.c * Functions for the GL_APPLE_vertex_array_object extension. * * \todo * The code in this file borrows a lot from bufferobj.c. There's a certain * amount of cruft left over from that origin that may be unnecessary. * * \author Ian Romanick * \author Brian Paul */ #include "glheader.h" #include "hash.h" #include "imports.h" #include "context.h" #if FEATURE_ARB_vertex_buffer_object #include "bufferobj.h" #endif #include "arrayobj.h" #include "glapi/dispatch.h" /** * Look up the array object for the given ID. * * \returns * Either a pointer to the array object with the specified ID or \c NULL for * a non-existent ID. The spec defines ID 0 as being technically * non-existent. */ static INLINE struct gl_array_object * lookup_arrayobj(GLcontext *ctx, GLuint id) { return (id == 0) ? NULL : (struct gl_array_object *) _mesa_HashLookup(ctx->Shared->ArrayObjects, id); } /** * Allocate and initialize a new vertex array object. * * This function is intended to be called via * \c dd_function_table::NewArrayObject. */ struct gl_array_object * _mesa_new_array_object( GLcontext *ctx, GLuint name ) { struct gl_array_object *obj = MALLOC_STRUCT(gl_array_object); if (obj) _mesa_initialize_array_object(ctx, obj, name); return obj; } /** * Delete an array object. * * This function is intended to be called via * \c dd_function_table::DeleteArrayObject. */ void _mesa_delete_array_object( GLcontext *ctx, struct gl_array_object *obj ) { (void) ctx; _mesa_free(obj); } void _mesa_initialize_array_object( GLcontext *ctx, struct gl_array_object *obj, GLuint name ) { GLuint i; obj->Name = name; /* Vertex arrays */ obj->Vertex.Size = 4; obj->Vertex.Type = GL_FLOAT; obj->Vertex.Stride = 0; obj->Vertex.StrideB = 0; obj->Vertex.Ptr = NULL; obj->Vertex.Enabled = GL_FALSE; obj->Normal.Type = GL_FLOAT; obj->Normal.Stride = 0; obj->Normal.StrideB = 0; obj->Normal.Ptr = NULL; obj->Normal.Enabled = GL_FALSE; obj->Color.Size = 4; obj->Color.Type = GL_FLOAT; obj->Color.Stride = 0; obj->Color.StrideB = 0; obj->Color.Ptr = NULL; obj->Color.Enabled = GL_FALSE; obj->SecondaryColor.Size = 4; obj->SecondaryColor.Type = GL_FLOAT; obj->SecondaryColor.Stride = 0; obj->SecondaryColor.StrideB = 0; obj->SecondaryColor.Ptr = NULL; obj->SecondaryColor.Enabled = GL_FALSE; obj->FogCoord.Size = 1; obj->FogCoord.Type = GL_FLOAT; obj->FogCoord.Stride = 0; obj->FogCoord.StrideB = 0; obj->FogCoord.Ptr = NULL; obj->FogCoord.Enabled = GL_FALSE; obj->Index.Type = GL_FLOAT; obj->Index.Stride = 0; obj->Index.StrideB = 0; obj->Index.Ptr = NULL; obj->Index.Enabled = GL_FALSE; for (i = 0; i < MAX_TEXTURE_UNITS; i++) { obj->TexCoord[i].Size = 4; obj->TexCoord[i].Type = GL_FLOAT; obj->TexCoord[i].Stride = 0; obj->TexCoord[i].StrideB = 0; obj->TexCoord[i].Ptr = NULL; obj->TexCoord[i].Enabled = GL_FALSE; } obj->EdgeFlag.Stride = 0; obj->EdgeFlag.StrideB = 0; obj->EdgeFlag.Ptr = NULL; obj->EdgeFlag.Enabled = GL_FALSE; for (i = 0; i < VERT_ATTRIB_MAX; i++) { obj->VertexAttrib[i].Size = 4; obj->VertexAttrib[i].Type = GL_FLOAT; obj->VertexAttrib[i].Stride = 0; obj->VertexAttrib[i].StrideB = 0; obj->VertexAttrib[i].Ptr = NULL; obj->VertexAttrib[i].Enabled = GL_FALSE; obj->VertexAttrib[i].Normalized = GL_FALSE; } #if FEATURE_ARB_vertex_buffer_object /* Vertex array buffers */ obj->Vertex.BufferObj = ctx->Array.NullBufferObj; obj->Normal.BufferObj = ctx->Array.NullBufferObj; obj->Color.BufferObj = ctx->Array.NullBufferObj; obj->SecondaryColor.BufferObj = ctx->Array.NullBufferObj; obj->FogCoord.BufferObj = ctx->Array.NullBufferObj; obj->Index.BufferObj = ctx->Array.NullBufferObj; for (i = 0; i < MAX_TEXTURE_UNITS; i++) { obj->TexCoord[i].BufferObj = ctx->Array.NullBufferObj; } obj->EdgeFlag.BufferObj = ctx->Array.NullBufferObj; for (i = 0; i < VERT_ATTRIB_MAX; i++) { obj->VertexAttrib[i].BufferObj = ctx->Array.NullBufferObj; } #endif } /** * Add the given array object to the array object pool. */ void _mesa_save_array_object( GLcontext *ctx, struct gl_array_object *obj ) { if (obj->Name > 0) { /* insert into hash table */ _mesa_HashInsert(ctx->Shared->ArrayObjects, obj->Name, obj); } } /** * Remove the given array object from the array object pool. * Do not deallocate the array object though. */ void _mesa_remove_array_object( GLcontext *ctx, struct gl_array_object *obj ) { if (obj->Name > 0) { /* remove from hash table */ _mesa_HashRemove(ctx->Shared->ArrayObjects, obj->Name); } } static void unbind_buffer_object( GLcontext *ctx, struct gl_buffer_object *bufObj ) { if (bufObj != ctx->Array.NullBufferObj) { _mesa_reference_buffer_object(ctx, &bufObj, NULL); } } /**********************************************************************/ /* API Functions */ /**********************************************************************/ /** * Bind a new array. * * \todo * The binding could be done more efficiently by comparing the non-NULL * pointers in the old and new objects. The only arrays that are "dirty" are * the ones that are non-NULL in either object. */ void GLAPIENTRY _mesa_BindVertexArrayAPPLE( GLuint id ) { GET_CURRENT_CONTEXT(ctx); struct gl_array_object * const oldObj = ctx->Array.ArrayObj; struct gl_array_object *newObj = NULL; ASSERT_OUTSIDE_BEGIN_END(ctx); ASSERT(oldObj != NULL); if ( oldObj->Name == id ) return; /* rebinding the same array object- no change */ /* * Get pointer to new array object (newBufObj) */ if (id == 0) { /* The spec says there is no array object named 0, but we use * one internally because it simplifies things. */ newObj = ctx->Array.DefaultArrayObj; } else { /* non-default array object */ newObj = lookup_arrayobj(ctx, id); if (!newObj) { /* If this is a new array object id, allocate an array object now. */ newObj = (*ctx->Driver.NewArrayObject)(ctx, id); if (!newObj) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindVertexArrayAPPLE"); return; } _mesa_save_array_object(ctx, newObj); } } ctx->NewState |= _NEW_ARRAY; ctx->Array.NewState |= _NEW_ARRAY_ALL; ctx->Array.ArrayObj = newObj; /* Pass BindVertexArray call to device driver */ if (ctx->Driver.BindArrayObject && newObj) (*ctx->Driver.BindArrayObject)( ctx, newObj ); } /** * Delete a set of array objects. * * \param n Number of array objects to delete. * \param ids Array of \c n array object IDs. */ void GLAPIENTRY _mesa_DeleteVertexArraysAPPLE(GLsizei n, const GLuint *ids) { GET_CURRENT_CONTEXT(ctx); GLsizei i; ASSERT_OUTSIDE_BEGIN_END(ctx); if (n < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteVertexArrayAPPLE(n)"); return; } _glthread_LOCK_MUTEX(ctx->Shared->Mutex); for (i = 0; i < n; i++) { struct gl_array_object *obj = lookup_arrayobj(ctx, ids[i]); if ( obj != NULL ) { ASSERT( obj->Name == ids[i] ); /* If the array object is currently bound, the spec says "the binding * for that object reverts to zero and the default vertex array * becomes current." */ if ( obj == ctx->Array.ArrayObj ) { CALL_BindVertexArrayAPPLE( ctx->Exec, (0) ); } #if FEATURE_ARB_vertex_buffer_object /* Unbind any buffer objects that might be bound to arrays in * this array object. */ unbind_buffer_object( ctx, obj->Vertex.BufferObj ); unbind_buffer_object( ctx, obj->Normal.BufferObj ); unbind_buffer_object( ctx, obj->Color.BufferObj ); unbind_buffer_object( ctx, obj->SecondaryColor.BufferObj ); unbind_buffer_object( ctx, obj->FogCoord.BufferObj ); unbind_buffer_object( ctx, obj->Index.BufferObj ); for (i = 0; i < MAX_TEXTURE_UNITS; i++) { unbind_buffer_object( ctx, obj->TexCoord[i].BufferObj ); } unbind_buffer_object( ctx, obj->EdgeFlag.BufferObj ); for (i = 0; i < VERT_ATTRIB_MAX; i++) { unbind_buffer_object( ctx, obj->VertexAttrib[i].BufferObj ); } #endif /* The ID is immediately freed for re-use */ _mesa_remove_array_object(ctx, obj); ctx->Driver.DeleteArrayObject(ctx, obj); } } _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); } /** * Generate a set of unique array object IDs and store them in \c arrays. * * \param n Number of IDs to generate. * \param arrays Array of \c n locations to store the IDs. */ void GLAPIENTRY _mesa_GenVertexArraysAPPLE(GLsizei n, GLuint *arrays) { GET_CURRENT_CONTEXT(ctx); GLuint first; GLint i; ASSERT_OUTSIDE_BEGIN_END(ctx); if (n < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glGenVertexArraysAPPLE"); return; } if (!arrays) { return; } /* * This must be atomic (generation and allocation of array object IDs) */ _glthread_LOCK_MUTEX(ctx->Shared->Mutex); first = _mesa_HashFindFreeKeyBlock(ctx->Shared->ArrayObjects, n); /* Allocate new, empty array objects and return identifiers */ for (i = 0; i < n; i++) { struct gl_array_object *obj; GLuint name = first + i; obj = (*ctx->Driver.NewArrayObject)( ctx, name ); if (!obj) { _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenVertexArraysAPPLE"); return; } _mesa_save_array_object(ctx, obj); arrays[i] = first + i; } _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); } /** * Determine if ID is the name of an array object. * * \param id ID of the potential array object. * \return \c GL_TRUE if \c id is the name of a array object, * \c GL_FALSE otherwise. */ GLboolean GLAPIENTRY _mesa_IsVertexArrayAPPLE( GLuint id ) { struct gl_array_object * obj; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); if (id == 0) return GL_FALSE; _glthread_LOCK_MUTEX(ctx->Shared->Mutex); obj = lookup_arrayobj(ctx, id); _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); return (obj != NULL) ? GL_TRUE : GL_FALSE; }