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