Home | History | Annotate | Download | only in state_tracker
      1 
      2 /**************************************************************************
      3  *
      4  * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
      5  * Copyright 2012 Marek Olk <maraeo (at) gmail.com>
      6  * 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
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sub license, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice (including the
     17  * next paragraph) shall be included in all copies or substantial portions
     18  * of the Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     23  * IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
     24  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     25  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     26  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     27  *
     28  **************************************************************************/
     29 
     30 /*
     31  * This converts the VBO's vertex attribute/array information into
     32  * Gallium vertex state and binds it.
     33  *
     34  * Authors:
     35  *   Keith Whitwell <keith (at) tungstengraphics.com>
     36  *   Marek Olk <maraeo (at) gmail.com>
     37  */
     38 
     39 #include "st_context.h"
     40 #include "st_atom.h"
     41 #include "st_cb_bufferobjects.h"
     42 #include "st_draw.h"
     43 #include "st_program.h"
     44 
     45 #include "cso_cache/cso_context.h"
     46 #include "util/u_math.h"
     47 
     48 #include "main/bufferobj.h"
     49 #include "main/glformats.h"
     50 
     51 
     52 static GLuint double_types[4] = {
     53    PIPE_FORMAT_R64_FLOAT,
     54    PIPE_FORMAT_R64G64_FLOAT,
     55    PIPE_FORMAT_R64G64B64_FLOAT,
     56    PIPE_FORMAT_R64G64B64A64_FLOAT
     57 };
     58 
     59 static GLuint float_types[4] = {
     60    PIPE_FORMAT_R32_FLOAT,
     61    PIPE_FORMAT_R32G32_FLOAT,
     62    PIPE_FORMAT_R32G32B32_FLOAT,
     63    PIPE_FORMAT_R32G32B32A32_FLOAT
     64 };
     65 
     66 static GLuint half_float_types[4] = {
     67    PIPE_FORMAT_R16_FLOAT,
     68    PIPE_FORMAT_R16G16_FLOAT,
     69    PIPE_FORMAT_R16G16B16_FLOAT,
     70    PIPE_FORMAT_R16G16B16A16_FLOAT
     71 };
     72 
     73 static GLuint uint_types_norm[4] = {
     74    PIPE_FORMAT_R32_UNORM,
     75    PIPE_FORMAT_R32G32_UNORM,
     76    PIPE_FORMAT_R32G32B32_UNORM,
     77    PIPE_FORMAT_R32G32B32A32_UNORM
     78 };
     79 
     80 static GLuint uint_types_scale[4] = {
     81    PIPE_FORMAT_R32_USCALED,
     82    PIPE_FORMAT_R32G32_USCALED,
     83    PIPE_FORMAT_R32G32B32_USCALED,
     84    PIPE_FORMAT_R32G32B32A32_USCALED
     85 };
     86 
     87 static GLuint uint_types_int[4] = {
     88    PIPE_FORMAT_R32_UINT,
     89    PIPE_FORMAT_R32G32_UINT,
     90    PIPE_FORMAT_R32G32B32_UINT,
     91    PIPE_FORMAT_R32G32B32A32_UINT
     92 };
     93 
     94 static GLuint int_types_norm[4] = {
     95    PIPE_FORMAT_R32_SNORM,
     96    PIPE_FORMAT_R32G32_SNORM,
     97    PIPE_FORMAT_R32G32B32_SNORM,
     98    PIPE_FORMAT_R32G32B32A32_SNORM
     99 };
    100 
    101 static GLuint int_types_scale[4] = {
    102    PIPE_FORMAT_R32_SSCALED,
    103    PIPE_FORMAT_R32G32_SSCALED,
    104    PIPE_FORMAT_R32G32B32_SSCALED,
    105    PIPE_FORMAT_R32G32B32A32_SSCALED
    106 };
    107 
    108 static GLuint int_types_int[4] = {
    109    PIPE_FORMAT_R32_SINT,
    110    PIPE_FORMAT_R32G32_SINT,
    111    PIPE_FORMAT_R32G32B32_SINT,
    112    PIPE_FORMAT_R32G32B32A32_SINT
    113 };
    114 
    115 static GLuint ushort_types_norm[4] = {
    116    PIPE_FORMAT_R16_UNORM,
    117    PIPE_FORMAT_R16G16_UNORM,
    118    PIPE_FORMAT_R16G16B16_UNORM,
    119    PIPE_FORMAT_R16G16B16A16_UNORM
    120 };
    121 
    122 static GLuint ushort_types_scale[4] = {
    123    PIPE_FORMAT_R16_USCALED,
    124    PIPE_FORMAT_R16G16_USCALED,
    125    PIPE_FORMAT_R16G16B16_USCALED,
    126    PIPE_FORMAT_R16G16B16A16_USCALED
    127 };
    128 
    129 static GLuint ushort_types_int[4] = {
    130    PIPE_FORMAT_R16_UINT,
    131    PIPE_FORMAT_R16G16_UINT,
    132    PIPE_FORMAT_R16G16B16_UINT,
    133    PIPE_FORMAT_R16G16B16A16_UINT
    134 };
    135 
    136 static GLuint short_types_norm[4] = {
    137    PIPE_FORMAT_R16_SNORM,
    138    PIPE_FORMAT_R16G16_SNORM,
    139    PIPE_FORMAT_R16G16B16_SNORM,
    140    PIPE_FORMAT_R16G16B16A16_SNORM
    141 };
    142 
    143 static GLuint short_types_scale[4] = {
    144    PIPE_FORMAT_R16_SSCALED,
    145    PIPE_FORMAT_R16G16_SSCALED,
    146    PIPE_FORMAT_R16G16B16_SSCALED,
    147    PIPE_FORMAT_R16G16B16A16_SSCALED
    148 };
    149 
    150 static GLuint short_types_int[4] = {
    151    PIPE_FORMAT_R16_SINT,
    152    PIPE_FORMAT_R16G16_SINT,
    153    PIPE_FORMAT_R16G16B16_SINT,
    154    PIPE_FORMAT_R16G16B16A16_SINT
    155 };
    156 
    157 static GLuint ubyte_types_norm[4] = {
    158    PIPE_FORMAT_R8_UNORM,
    159    PIPE_FORMAT_R8G8_UNORM,
    160    PIPE_FORMAT_R8G8B8_UNORM,
    161    PIPE_FORMAT_R8G8B8A8_UNORM
    162 };
    163 
    164 static GLuint ubyte_types_scale[4] = {
    165    PIPE_FORMAT_R8_USCALED,
    166    PIPE_FORMAT_R8G8_USCALED,
    167    PIPE_FORMAT_R8G8B8_USCALED,
    168    PIPE_FORMAT_R8G8B8A8_USCALED
    169 };
    170 
    171 static GLuint ubyte_types_int[4] = {
    172    PIPE_FORMAT_R8_UINT,
    173    PIPE_FORMAT_R8G8_UINT,
    174    PIPE_FORMAT_R8G8B8_UINT,
    175    PIPE_FORMAT_R8G8B8A8_UINT
    176 };
    177 
    178 static GLuint byte_types_norm[4] = {
    179    PIPE_FORMAT_R8_SNORM,
    180    PIPE_FORMAT_R8G8_SNORM,
    181    PIPE_FORMAT_R8G8B8_SNORM,
    182    PIPE_FORMAT_R8G8B8A8_SNORM
    183 };
    184 
    185 static GLuint byte_types_scale[4] = {
    186    PIPE_FORMAT_R8_SSCALED,
    187    PIPE_FORMAT_R8G8_SSCALED,
    188    PIPE_FORMAT_R8G8B8_SSCALED,
    189    PIPE_FORMAT_R8G8B8A8_SSCALED
    190 };
    191 
    192 static GLuint byte_types_int[4] = {
    193    PIPE_FORMAT_R8_SINT,
    194    PIPE_FORMAT_R8G8_SINT,
    195    PIPE_FORMAT_R8G8B8_SINT,
    196    PIPE_FORMAT_R8G8B8A8_SINT
    197 };
    198 
    199 static GLuint fixed_types[4] = {
    200    PIPE_FORMAT_R32_FIXED,
    201    PIPE_FORMAT_R32G32_FIXED,
    202    PIPE_FORMAT_R32G32B32_FIXED,
    203    PIPE_FORMAT_R32G32B32A32_FIXED
    204 };
    205 
    206 
    207 /**
    208  * Return a PIPE_FORMAT_x for the given GL datatype and size.
    209  */
    210 enum pipe_format
    211 st_pipe_vertex_format(GLenum type, GLuint size, GLenum format,
    212                       GLboolean normalized, GLboolean integer)
    213 {
    214    assert((type >= GL_BYTE && type <= GL_DOUBLE) ||
    215           type == GL_FIXED || type == GL_HALF_FLOAT ||
    216           type == GL_INT_2_10_10_10_REV ||
    217           type == GL_UNSIGNED_INT_2_10_10_10_REV);
    218    assert(size >= 1);
    219    assert(size <= 4);
    220    assert(format == GL_RGBA || format == GL_BGRA);
    221 
    222    if (type == GL_INT_2_10_10_10_REV ||
    223        type == GL_UNSIGNED_INT_2_10_10_10_REV) {
    224       assert(size == 4);
    225       assert(!integer);
    226 
    227       if (format == GL_BGRA) {
    228          if (type == GL_INT_2_10_10_10_REV) {
    229             if (normalized)
    230                return PIPE_FORMAT_B10G10R10A2_SNORM;
    231             else
    232                return PIPE_FORMAT_B10G10R10A2_SSCALED;
    233          } else {
    234             if (normalized)
    235                return PIPE_FORMAT_B10G10R10A2_UNORM;
    236             else
    237                return PIPE_FORMAT_B10G10R10A2_USCALED;
    238          }
    239       } else {
    240          if (type == GL_INT_2_10_10_10_REV) {
    241             if (normalized)
    242                return PIPE_FORMAT_R10G10B10A2_SNORM;
    243             else
    244                return PIPE_FORMAT_R10G10B10A2_SSCALED;
    245          } else {
    246             if (normalized)
    247                return PIPE_FORMAT_R10G10B10A2_UNORM;
    248             else
    249                return PIPE_FORMAT_R10G10B10A2_USCALED;
    250          }
    251       }
    252    }
    253 
    254    if (format == GL_BGRA) {
    255       /* this is an odd-ball case */
    256       assert(type == GL_UNSIGNED_BYTE);
    257       assert(normalized);
    258       return PIPE_FORMAT_B8G8R8A8_UNORM;
    259    }
    260 
    261    if (integer) {
    262       switch (type) {
    263       case GL_INT: return int_types_int[size-1];
    264       case GL_SHORT: return short_types_int[size-1];
    265       case GL_BYTE: return byte_types_int[size-1];
    266       case GL_UNSIGNED_INT: return uint_types_int[size-1];
    267       case GL_UNSIGNED_SHORT: return ushort_types_int[size-1];
    268       case GL_UNSIGNED_BYTE: return ubyte_types_int[size-1];
    269       default: assert(0); return 0;
    270       }
    271    }
    272    else if (normalized) {
    273       switch (type) {
    274       case GL_DOUBLE: return double_types[size-1];
    275       case GL_FLOAT: return float_types[size-1];
    276       case GL_HALF_FLOAT: return half_float_types[size-1];
    277       case GL_INT: return int_types_norm[size-1];
    278       case GL_SHORT: return short_types_norm[size-1];
    279       case GL_BYTE: return byte_types_norm[size-1];
    280       case GL_UNSIGNED_INT: return uint_types_norm[size-1];
    281       case GL_UNSIGNED_SHORT: return ushort_types_norm[size-1];
    282       case GL_UNSIGNED_BYTE: return ubyte_types_norm[size-1];
    283       case GL_FIXED: return fixed_types[size-1];
    284       default: assert(0); return 0;
    285       }
    286    }
    287    else {
    288       switch (type) {
    289       case GL_DOUBLE: return double_types[size-1];
    290       case GL_FLOAT: return float_types[size-1];
    291       case GL_HALF_FLOAT: return half_float_types[size-1];
    292       case GL_INT: return int_types_scale[size-1];
    293       case GL_SHORT: return short_types_scale[size-1];
    294       case GL_BYTE: return byte_types_scale[size-1];
    295       case GL_UNSIGNED_INT: return uint_types_scale[size-1];
    296       case GL_UNSIGNED_SHORT: return ushort_types_scale[size-1];
    297       case GL_UNSIGNED_BYTE: return ubyte_types_scale[size-1];
    298       case GL_FIXED: return fixed_types[size-1];
    299       default: assert(0); return 0;
    300       }
    301    }
    302    return PIPE_FORMAT_NONE; /* silence compiler warning */
    303 }
    304 
    305 /**
    306  * Examine the active arrays to determine if we have interleaved
    307  * vertex arrays all living in one VBO, or all living in user space.
    308  */
    309 static GLboolean
    310 is_interleaved_arrays(const struct st_vertex_program *vp,
    311                       const struct st_vp_variant *vpv,
    312                       const struct gl_client_array **arrays)
    313 {
    314    GLuint attr;
    315    const struct gl_buffer_object *firstBufObj = NULL;
    316    GLint firstStride = -1;
    317    const GLubyte *firstPtr = NULL;
    318    GLboolean userSpaceBuffer = GL_FALSE;
    319 
    320    for (attr = 0; attr < vpv->num_inputs; attr++) {
    321       const GLuint mesaAttr = vp->index_to_input[attr];
    322       const struct gl_client_array *array = arrays[mesaAttr];
    323       const struct gl_buffer_object *bufObj = array->BufferObj;
    324       const GLsizei stride = array->StrideB; /* in bytes */
    325 
    326       if (attr == 0) {
    327          /* save info about the first array */
    328          firstStride = stride;
    329          firstPtr = array->Ptr;
    330          firstBufObj = bufObj;
    331          userSpaceBuffer = !bufObj || !bufObj->Name;
    332       }
    333       else {
    334          /* check if other arrays interleave with the first, in same buffer */
    335          if (stride != firstStride)
    336             return GL_FALSE; /* strides don't match */
    337 
    338          if (bufObj != firstBufObj)
    339             return GL_FALSE; /* arrays in different VBOs */
    340 
    341          if (abs(array->Ptr - firstPtr) > firstStride)
    342             return GL_FALSE; /* arrays start too far apart */
    343 
    344          if ((!_mesa_is_bufferobj(bufObj)) != userSpaceBuffer)
    345             return GL_FALSE; /* mix of VBO and user-space arrays */
    346       }
    347    }
    348 
    349    return GL_TRUE;
    350 }
    351 
    352 /**
    353  * Set up for drawing interleaved arrays that all live in one VBO
    354  * or all live in user space.
    355  * \param vbuffer  returns vertex buffer info
    356  * \param velements  returns vertex element info
    357  */
    358 static boolean
    359 setup_interleaved_attribs(const struct st_vertex_program *vp,
    360                           const struct st_vp_variant *vpv,
    361                           const struct gl_client_array **arrays,
    362                           struct pipe_vertex_buffer *vbuffer,
    363                           struct pipe_vertex_element velements[])
    364 {
    365    GLuint attr;
    366    const GLubyte *low_addr = NULL;
    367    GLboolean usingVBO;      /* all arrays in a VBO? */
    368    struct gl_buffer_object *bufobj;
    369    GLsizei stride;
    370 
    371    /* Find the lowest address of the arrays we're drawing,
    372     * Init bufobj and stride.
    373     */
    374    if (vpv->num_inputs) {
    375       const GLuint mesaAttr0 = vp->index_to_input[0];
    376       const struct gl_client_array *array = arrays[mesaAttr0];
    377 
    378       /* Since we're doing interleaved arrays, we know there'll be at most
    379        * one buffer object and the stride will be the same for all arrays.
    380        * Grab them now.
    381        */
    382       bufobj = array->BufferObj;
    383       stride = array->StrideB;
    384 
    385       low_addr = arrays[vp->index_to_input[0]]->Ptr;
    386 
    387       for (attr = 1; attr < vpv->num_inputs; attr++) {
    388          const GLubyte *start = arrays[vp->index_to_input[attr]]->Ptr;
    389          low_addr = MIN2(low_addr, start);
    390       }
    391    }
    392    else {
    393       /* not sure we'll ever have zero inputs, but play it safe */
    394       bufobj = NULL;
    395       stride = 0;
    396       low_addr = 0;
    397    }
    398 
    399    /* are the arrays in user space? */
    400    usingVBO = _mesa_is_bufferobj(bufobj);
    401 
    402    for (attr = 0; attr < vpv->num_inputs; attr++) {
    403       const GLuint mesaAttr = vp->index_to_input[attr];
    404       const struct gl_client_array *array = arrays[mesaAttr];
    405       unsigned src_offset = (unsigned) (array->Ptr - low_addr);
    406       GLuint element_size = array->_ElementSize;
    407 
    408       assert(element_size == array->Size * _mesa_sizeof_type(array->Type));
    409 
    410       velements[attr].src_offset = src_offset;
    411       velements[attr].instance_divisor = array->InstanceDivisor;
    412       velements[attr].vertex_buffer_index = 0;
    413       velements[attr].src_format = st_pipe_vertex_format(array->Type,
    414                                                          array->Size,
    415                                                          array->Format,
    416                                                          array->Normalized,
    417                                                          array->Integer);
    418       assert(velements[attr].src_format);
    419    }
    420 
    421    /*
    422     * Return the vbuffer info and setup user-space attrib info, if needed.
    423     */
    424    if (vpv->num_inputs == 0) {
    425       /* just defensive coding here */
    426       vbuffer->buffer = NULL;
    427       vbuffer->user_buffer = NULL;
    428       vbuffer->buffer_offset = 0;
    429       vbuffer->stride = 0;
    430    }
    431    else if (usingVBO) {
    432       /* all interleaved arrays in a VBO */
    433       struct st_buffer_object *stobj = st_buffer_object(bufobj);
    434 
    435       if (!stobj || !stobj->buffer) {
    436          return FALSE; /* out-of-memory error probably */
    437       }
    438 
    439       vbuffer->buffer = stobj->buffer;
    440       vbuffer->user_buffer = NULL;
    441       vbuffer->buffer_offset = pointer_to_offset(low_addr);
    442       vbuffer->stride = stride;
    443    }
    444    else {
    445       /* all interleaved arrays in user memory */
    446       vbuffer->buffer = NULL;
    447       vbuffer->user_buffer = low_addr;
    448       vbuffer->buffer_offset = 0;
    449       vbuffer->stride = stride;
    450    }
    451    return TRUE;
    452 }
    453 
    454 /**
    455  * Set up a separate pipe_vertex_buffer and pipe_vertex_element for each
    456  * vertex attribute.
    457  * \param vbuffer  returns vertex buffer info
    458  * \param velements  returns vertex element info
    459  */
    460 static boolean
    461 setup_non_interleaved_attribs(struct st_context *st,
    462                               const struct st_vertex_program *vp,
    463                               const struct st_vp_variant *vpv,
    464                               const struct gl_client_array **arrays,
    465                               struct pipe_vertex_buffer vbuffer[],
    466                               struct pipe_vertex_element velements[])
    467 {
    468    struct gl_context *ctx = st->ctx;
    469    GLuint attr;
    470 
    471    for (attr = 0; attr < vpv->num_inputs; attr++) {
    472       const GLuint mesaAttr = vp->index_to_input[attr];
    473       const struct gl_client_array *array = arrays[mesaAttr];
    474       struct gl_buffer_object *bufobj = array->BufferObj;
    475       GLsizei stride = array->StrideB;
    476 
    477       assert(array->_ElementSize == array->Size * _mesa_sizeof_type(array->Type));
    478 
    479       if (_mesa_is_bufferobj(bufobj)) {
    480          /* Attribute data is in a VBO.
    481           * Recall that for VBOs, the gl_client_array->Ptr field is
    482           * really an offset from the start of the VBO, not a pointer.
    483           */
    484          struct st_buffer_object *stobj = st_buffer_object(bufobj);
    485 
    486          if (!stobj || !stobj->buffer) {
    487             return FALSE; /* out-of-memory error probably */
    488          }
    489 
    490          vbuffer[attr].buffer = stobj->buffer;
    491          vbuffer[attr].user_buffer = NULL;
    492          vbuffer[attr].buffer_offset = pointer_to_offset(array->Ptr);
    493       }
    494       else {
    495          /* wrap user data */
    496          void *ptr;
    497 
    498          if (array->Ptr) {
    499             ptr = (void *) array->Ptr;
    500          }
    501          else {
    502             /* no array, use ctx->Current.Attrib[] value */
    503             ptr = (void *) ctx->Current.Attrib[mesaAttr];
    504             stride = 0;
    505          }
    506 
    507          assert(ptr);
    508 
    509          vbuffer[attr].buffer = NULL;
    510          vbuffer[attr].user_buffer = ptr;
    511          vbuffer[attr].buffer_offset = 0;
    512       }
    513 
    514       /* common-case setup */
    515       vbuffer[attr].stride = stride; /* in bytes */
    516 
    517       velements[attr].src_offset = 0;
    518       velements[attr].instance_divisor = array->InstanceDivisor;
    519       velements[attr].vertex_buffer_index = attr;
    520       velements[attr].src_format = st_pipe_vertex_format(array->Type,
    521                                                          array->Size,
    522                                                          array->Format,
    523                                                          array->Normalized,
    524                                                          array->Integer);
    525       assert(velements[attr].src_format);
    526    }
    527    return TRUE;
    528 }
    529 
    530 static void update_array(struct st_context *st)
    531 {
    532    struct gl_context *ctx = st->ctx;
    533    const struct gl_client_array **arrays = ctx->Array._DrawArrays;
    534    const struct st_vertex_program *vp;
    535    const struct st_vp_variant *vpv;
    536    struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS];
    537    struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS];
    538    unsigned num_vbuffers, num_velements;
    539 
    540    st->vertex_array_out_of_memory = FALSE;
    541 
    542    /* No drawing has been done yet, so do nothing. */
    543    if (!arrays)
    544       return;
    545 
    546    /* vertex program validation must be done before this */
    547    vp = st->vp;
    548    vpv = st->vp_variant;
    549 
    550    memset(velements, 0, sizeof(struct pipe_vertex_element) * vpv->num_inputs);
    551 
    552    /*
    553     * Setup the vbuffer[] and velements[] arrays.
    554     */
    555    if (is_interleaved_arrays(vp, vpv, arrays)) {
    556       if (!setup_interleaved_attribs(vp, vpv, arrays, vbuffer, velements)) {
    557          st->vertex_array_out_of_memory = TRUE;
    558          return;
    559       }
    560 
    561       num_vbuffers = 1;
    562       num_velements = vpv->num_inputs;
    563       if (num_velements == 0)
    564          num_vbuffers = 0;
    565    }
    566    else {
    567       if (!setup_non_interleaved_attribs(st, vp, vpv, arrays, vbuffer,
    568                                          velements)) {
    569          st->vertex_array_out_of_memory = TRUE;
    570          return;
    571       }
    572 
    573       num_vbuffers = vpv->num_inputs;
    574       num_velements = vpv->num_inputs;
    575    }
    576 
    577    cso_set_vertex_buffers(st->cso_context, num_vbuffers, vbuffer);
    578    cso_set_vertex_elements(st->cso_context, num_velements, velements);
    579 }
    580 
    581 
    582 const struct st_tracked_state st_update_array = {
    583    "st_update_array",					/* name */
    584    {							/* dirty */
    585       (_NEW_PROGRAM | _NEW_BUFFER_OBJECT),		/* mesa */
    586       ST_NEW_VERTEX_ARRAYS | ST_NEW_VERTEX_PROGRAM,     /* st */
    587    },
    588    update_array						/* update */
    589 };
    590