ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

vbo_save_api.c
Go to the documentation of this file.
00001 /**************************************************************************
00002 
00003 Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas.
00004 
00005 All Rights Reserved.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 on the rights to use, copy, modify, merge, publish, distribute, sub
00011 license, and/or sell copies of the Software, and to permit persons to whom
00012 the Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice (including the next
00015 paragraph) shall be included in all copies or substantial portions of the
00016 Software.
00017 
00018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
00021 TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
00022 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
00023 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
00024 USE OR OTHER DEALINGS IN THE SOFTWARE.
00025 
00026 **************************************************************************/
00027 
00028 /*
00029  * Authors:
00030  *   Keith Whitwell <keith@tungstengraphics.com>
00031  */
00032 
00033 
00034 
00035 /* Display list compiler attempts to store lists of vertices with the
00036  * same vertex layout.  Additionally it attempts to minimize the need
00037  * for execute-time fixup of these vertex lists, allowing them to be
00038  * cached on hardware.
00039  *
00040  * There are still some circumstances where this can be thwarted, for
00041  * example by building a list that consists of one very long primitive
00042  * (eg Begin(Triangles), 1000 vertices, End), and calling that list
00043  * from inside a different begin/end object (Begin(Lines), CallList,
00044  * End).  
00045  *
00046  * In that case the code will have to replay the list as individual
00047  * commands through the Exec dispatch table, or fix up the copied
00048  * vertices at execute-time.
00049  *
00050  * The other case where fixup is required is when a vertex attribute
00051  * is introduced in the middle of a primitive.  Eg:
00052  *  Begin(Lines)
00053  *  TexCoord1f()           Vertex2f()
00054  *  TexCoord1f() Color3f() Vertex2f()
00055  *  End()
00056  *
00057  *  If the current value of Color isn't known at compile-time, this
00058  *  primitive will require fixup.
00059  *
00060  *
00061  * The list compiler currently doesn't attempt to compile lists
00062  * containing EvalCoord or EvalPoint commands.  On encountering one of
00063  * these, compilation falls back to opcodes.  
00064  *
00065  * This could be improved to fallback only when a mix of EvalCoord and
00066  * Vertex commands are issued within a single primitive.
00067  */
00068 
00069 
00070 #include "main/glheader.h"
00071 #include "main/bufferobj.h"
00072 #include "main/context.h"
00073 #include "main/dlist.h"
00074 #include "main/enums.h"
00075 #include "main/macros.h"
00076 #include "main/api_validate.h"
00077 #include "main/api_arrayelt.h"
00078 #include "main/vtxfmt.h"
00079 #include "glapi/dispatch.h"
00080 
00081 #include "vbo_context.h"
00082 
00083 
00084 #ifdef ERROR
00085 #undef ERROR
00086 #endif
00087 
00088 
00089 /* An interesting VBO number/name to help with debugging */
00090 #define VBO_BUF_ID  12345
00091 
00092 
00093 /*
00094  * NOTE: Old 'parity' issue is gone, but copying can still be
00095  * wrong-footed on replay.
00096  */
00097 static GLuint _save_copy_vertices( GLcontext *ctx, 
00098                    const struct vbo_save_vertex_list *node,
00099                    const GLfloat *src_buffer)
00100 {
00101    struct vbo_save_context *save = &vbo_context( ctx )->save;
00102    const struct _mesa_prim *prim = &node->prim[node->prim_count-1];
00103    GLuint nr = prim->count;
00104    GLuint sz = save->vertex_size;
00105    const GLfloat *src = src_buffer + prim->start * sz;
00106    GLfloat *dst = save->copied.buffer;
00107    GLuint ovf, i;
00108 
00109    if (prim->end)
00110       return 0;
00111      
00112    switch( prim->mode )
00113    {
00114    case GL_POINTS:
00115       return 0;
00116    case GL_LINES:
00117       ovf = nr&1;
00118       for (i = 0 ; i < ovf ; i++)
00119      _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
00120       return i;
00121    case GL_TRIANGLES:
00122       ovf = nr%3;
00123       for (i = 0 ; i < ovf ; i++)
00124      _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
00125       return i;
00126    case GL_QUADS:
00127       ovf = nr&3;
00128       for (i = 0 ; i < ovf ; i++)
00129      _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
00130       return i;
00131    case GL_LINE_STRIP:
00132       if (nr == 0) 
00133      return 0;
00134       else {
00135      _mesa_memcpy( dst, src+(nr-1)*sz, sz*sizeof(GLfloat) );
00136      return 1;
00137       }
00138    case GL_LINE_LOOP:
00139    case GL_TRIANGLE_FAN:
00140    case GL_POLYGON:
00141       if (nr == 0) 
00142      return 0;
00143       else if (nr == 1) {
00144      _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) );
00145      return 1;
00146       } else {
00147      _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) );
00148      _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) );
00149      return 2;
00150       }
00151    case GL_TRIANGLE_STRIP:
00152    case GL_QUAD_STRIP:
00153       switch (nr) {
00154       case 0: ovf = 0; break;
00155       case 1: ovf = 1; break;
00156       default: ovf = 2 + (nr&1); break;
00157       }
00158       for (i = 0 ; i < ovf ; i++)
00159      _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
00160       return i;
00161    default:
00162       assert(0);
00163       return 0;
00164    }
00165 }
00166 
00167 
00168 static struct vbo_save_vertex_store *alloc_vertex_store( GLcontext *ctx )
00169 {
00170    struct vbo_save_vertex_store *vertex_store = CALLOC_STRUCT(vbo_save_vertex_store);
00171 
00172    /* obj->Name needs to be non-zero, but won't ever be examined more
00173     * closely than that.  In particular these buffers won't be entered
00174     * into the hash and can never be confused with ones visible to the
00175     * user.  Perhaps there could be a special number for internal
00176     * buffers:
00177     */
00178    vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx,
00179                                                          VBO_BUF_ID,
00180                                                          GL_ARRAY_BUFFER_ARB);
00181 
00182    ctx->Driver.BufferData( ctx, 
00183                GL_ARRAY_BUFFER_ARB, 
00184                VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
00185                NULL,
00186                GL_STATIC_DRAW_ARB,
00187                vertex_store->bufferobj);
00188 
00189    vertex_store->buffer = NULL;
00190    vertex_store->used = 0;
00191    vertex_store->refcount = 1;
00192 
00193    return vertex_store;
00194 }
00195 
00196 static void free_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store )
00197 {
00198    assert(!vertex_store->buffer);
00199 
00200    if (vertex_store->bufferobj) {
00201       _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
00202    }
00203 
00204    FREE( vertex_store );
00205 }
00206 
00207 static GLfloat *map_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store )
00208 {
00209    assert(vertex_store->bufferobj);
00210    assert(!vertex_store->buffer);
00211    vertex_store->buffer = (GLfloat *)ctx->Driver.MapBuffer(ctx, 
00212                                GL_ARRAY_BUFFER_ARB, /* not used */
00213                                GL_WRITE_ONLY, /* not used */
00214                                vertex_store->bufferobj); 
00215 
00216    assert(vertex_store->buffer);
00217    return vertex_store->buffer + vertex_store->used;
00218 }
00219 
00220 static void unmap_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store )
00221 {
00222    ctx->Driver.UnmapBuffer( ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj );
00223    vertex_store->buffer = NULL;
00224 }
00225 
00226 
00227 static struct vbo_save_primitive_store *alloc_prim_store( GLcontext *ctx )
00228 {
00229    struct vbo_save_primitive_store *store = CALLOC_STRUCT(vbo_save_primitive_store);
00230    (void) ctx;
00231    store->used = 0;
00232    store->refcount = 1;
00233    return store;
00234 }
00235 
00236 static void _save_reset_counters( GLcontext *ctx )
00237 {
00238    struct vbo_save_context *save = &vbo_context(ctx)->save;
00239 
00240    save->prim = save->prim_store->buffer + save->prim_store->used;
00241    save->buffer = (save->vertex_store->buffer + 
00242            save->vertex_store->used);
00243 
00244    assert(save->buffer == save->vbptr);
00245 
00246    if (save->vertex_size)
00247       save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / 
00248              save->vertex_size);
00249    else
00250       save->max_vert = 0;
00251 
00252    save->vert_count = 0;
00253    save->prim_count = 0;
00254    save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
00255    save->dangling_attr_ref = 0;
00256 }
00257 
00258 
00259 /* Insert the active immediate struct onto the display list currently
00260  * being built.
00261  */
00262 static void _save_compile_vertex_list( GLcontext *ctx )
00263 {
00264    struct vbo_save_context *save = &vbo_context(ctx)->save;
00265    struct vbo_save_vertex_list *node;
00266 
00267    /* Allocate space for this structure in the display list currently
00268     * being compiled.
00269     */
00270    node = (struct vbo_save_vertex_list *)
00271       _mesa_alloc_instruction(ctx, save->opcode_vertex_list, sizeof(*node));
00272 
00273    if (!node)
00274       return;
00275 
00276    /* Duplicate our template, increment refcounts to the storage structs:
00277     */
00278    _mesa_memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz)); 
00279    node->vertex_size = save->vertex_size;
00280    node->buffer_offset = (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat); 
00281    node->count = save->vert_count;
00282    node->wrap_count = save->copied.nr;
00283    node->dangling_attr_ref = save->dangling_attr_ref;
00284    node->prim = save->prim;
00285    node->prim_count = save->prim_count;
00286    node->vertex_store = save->vertex_store;
00287    node->prim_store = save->prim_store;
00288 
00289    node->vertex_store->refcount++;
00290    node->prim_store->refcount++;
00291 
00292    assert(node->attrsz[VBO_ATTRIB_POS] != 0 ||
00293       node->count == 0);
00294 
00295    if (save->dangling_attr_ref)
00296       ctx->ListState.CurrentList->flags |= MESA_DLIST_DANGLING_REFS;
00297 
00298    save->vertex_store->used += save->vertex_size * node->count;
00299    save->prim_store->used += node->prim_count;
00300 
00301 
00302    /* Copy duplicated vertices 
00303     */
00304    save->copied.nr = _save_copy_vertices( ctx, node, save->buffer );
00305 
00306 
00307    /* Deal with GL_COMPILE_AND_EXECUTE:
00308     */
00309    if (ctx->ExecuteFlag) {
00310       struct _glapi_table *dispatch = GET_DISPATCH();
00311 
00312       _glapi_set_dispatch(ctx->Exec);
00313 
00314       vbo_loopback_vertex_list( ctx,
00315                 (const GLfloat *)((const char *)save->vertex_store->buffer + 
00316                           node->buffer_offset),
00317                 node->attrsz,
00318                 node->prim,
00319                 node->prim_count,
00320                 node->wrap_count,
00321                 node->vertex_size);
00322 
00323       _glapi_set_dispatch(dispatch);
00324    }
00325 
00326 
00327    /* Decide whether the storage structs are full, or can be used for
00328     * the next vertex lists as well.
00329     */
00330    if (save->vertex_store->used > 
00331        VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) {
00332 
00333       /* Unmap old store:
00334        */
00335       unmap_vertex_store( ctx, save->vertex_store );
00336 
00337       /* Release old reference:
00338        */
00339       save->vertex_store->refcount--; 
00340       assert(save->vertex_store->refcount != 0);
00341       save->vertex_store = NULL;
00342 
00343       /* Allocate and map new store:
00344        */
00345       save->vertex_store = alloc_vertex_store( ctx );
00346       save->vbptr = map_vertex_store( ctx, save->vertex_store );
00347    } 
00348 
00349    if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
00350       save->prim_store->refcount--; 
00351       assert(save->prim_store->refcount != 0);
00352       save->prim_store = alloc_prim_store( ctx );
00353    } 
00354 
00355    /* Reset our structures for the next run of vertices:
00356     */
00357    _save_reset_counters( ctx );
00358 }
00359 
00360 
00361 /* TODO -- If no new vertices have been stored, don't bother saving
00362  * it.
00363  */
00364 static void _save_wrap_buffers( GLcontext *ctx )
00365 {
00366    struct vbo_save_context *save = &vbo_context(ctx)->save;
00367    GLint i = save->prim_count - 1;
00368    GLenum mode;
00369    GLboolean weak;
00370 
00371    assert(i < (GLint) save->prim_max);
00372    assert(i >= 0);
00373 
00374    /* Close off in-progress primitive.
00375     */
00376    save->prim[i].count = (save->vert_count - 
00377               save->prim[i].start);
00378    mode = save->prim[i].mode;
00379    weak = save->prim[i].weak;
00380    
00381    /* store the copied vertices, and allocate a new list.
00382     */
00383    _save_compile_vertex_list( ctx );
00384 
00385    /* Restart interrupted primitive
00386     */
00387    save->prim[0].mode = mode;
00388    save->prim[0].weak = weak;
00389    save->prim[0].begin = 0;
00390    save->prim[0].end = 0;
00391    save->prim[0].pad = 0;
00392    save->prim[0].start = 0;
00393    save->prim[0].count = 0;
00394    save->prim_count = 1;
00395 }
00396 
00397 
00398 
00399 /* Called only when buffers are wrapped as the result of filling the
00400  * vertex_store struct.  
00401  */
00402 static void _save_wrap_filled_vertex( GLcontext *ctx )
00403 {
00404    struct vbo_save_context *save = &vbo_context(ctx)->save;
00405    GLfloat *data = save->copied.buffer;
00406    GLuint i;
00407 
00408    /* Emit a glEnd to close off the last vertex list.
00409     */
00410    _save_wrap_buffers( ctx );
00411    
00412     /* Copy stored stored vertices to start of new list.
00413     */
00414    assert(save->max_vert - save->vert_count > save->copied.nr);
00415 
00416    for (i = 0 ; i < save->copied.nr ; i++) {
00417       _mesa_memcpy( save->vbptr, data, save->vertex_size * sizeof(GLfloat));
00418       data += save->vertex_size;
00419       save->vbptr += save->vertex_size;
00420       save->vert_count++;
00421    }
00422 }
00423 
00424 
00425 static void _save_copy_to_current( GLcontext *ctx )
00426 {
00427    struct vbo_save_context *save = &vbo_context(ctx)->save; 
00428    GLuint i;
00429 
00430    for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
00431       if (save->attrsz[i]) {
00432      save->currentsz[i][0] = save->attrsz[i];
00433      COPY_CLEAN_4V(save->current[i], 
00434                save->attrsz[i], 
00435                save->attrptr[i]);
00436       }
00437    }
00438 }
00439 
00440 
00441 static void _save_copy_from_current( GLcontext *ctx )
00442 {
00443    struct vbo_save_context *save = &vbo_context(ctx)->save; 
00444    GLint i;
00445 
00446    for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
00447       switch (save->attrsz[i]) {
00448       case 4: save->attrptr[i][3] = save->current[i][3];
00449       case 3: save->attrptr[i][2] = save->current[i][2];
00450       case 2: save->attrptr[i][1] = save->current[i][1];
00451       case 1: save->attrptr[i][0] = save->current[i][0];
00452       case 0: break;
00453       }
00454    }
00455 }
00456 
00457 
00458 
00459 
00460 /* Flush existing data, set new attrib size, replay copied vertices.
00461  */ 
00462 static void _save_upgrade_vertex( GLcontext *ctx, 
00463                  GLuint attr,
00464                  GLuint newsz )
00465 {
00466    struct vbo_save_context *save = &vbo_context(ctx)->save;
00467    GLuint oldsz;
00468    GLuint i;
00469    GLfloat *tmp;
00470 
00471    /* Store the current run of vertices, and emit a GL_END.  Emit a
00472     * BEGIN in the new buffer.
00473     */
00474    if (save->vert_count) 
00475       _save_wrap_buffers( ctx );
00476    else
00477       assert( save->copied.nr == 0 );
00478 
00479    /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
00480     * when the attribute already exists in the vertex and is having
00481     * its size increased.  
00482     */
00483    _save_copy_to_current( ctx );
00484 
00485    /* Fix up sizes:
00486     */
00487    oldsz = save->attrsz[attr];
00488    save->attrsz[attr] = newsz;
00489 
00490    save->vertex_size += newsz - oldsz;
00491    save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / 
00492               save->vertex_size);
00493    save->vert_count = 0;
00494 
00495    /* Recalculate all the attrptr[] values:
00496     */
00497    for (i = 0, tmp = save->vertex ; i < VBO_ATTRIB_MAX ; i++) {
00498       if (save->attrsz[i]) {
00499      save->attrptr[i] = tmp;
00500      tmp += save->attrsz[i];
00501       }
00502       else 
00503      save->attrptr[i] = NULL; /* will not be dereferenced. */
00504    }
00505 
00506    /* Copy from current to repopulate the vertex with correct values.
00507     */
00508    _save_copy_from_current( ctx );
00509 
00510    /* Replay stored vertices to translate them to new format here.
00511     *
00512     * If there are copied vertices and the new (upgraded) attribute
00513     * has not been defined before, this list is somewhat degenerate,
00514     * and will need fixup at runtime.
00515     */
00516    if (save->copied.nr)
00517    {
00518       GLfloat *data = save->copied.buffer;
00519       GLfloat *dest = save->buffer;
00520       GLuint j;
00521 
00522       /* Need to note this and fix up at runtime (or loopback):
00523        */
00524       if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
00525      assert(oldsz == 0);
00526      save->dangling_attr_ref = GL_TRUE;
00527       }
00528 
00529       for (i = 0 ; i < save->copied.nr ; i++) {
00530      for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) {
00531         if (save->attrsz[j]) {
00532            if (j == attr) {
00533           if (oldsz) {
00534              COPY_CLEAN_4V( dest, oldsz, data );
00535              data += oldsz;
00536              dest += newsz;
00537           }
00538           else {
00539              COPY_SZ_4V( dest, newsz, save->current[attr] );
00540              dest += newsz;
00541           }
00542            }
00543            else {
00544           GLint sz = save->attrsz[j];
00545           COPY_SZ_4V( dest, sz, data );
00546           data += sz;
00547           dest += sz;
00548            }
00549         }
00550      }
00551       }
00552 
00553       save->vbptr = dest;
00554       save->vert_count += save->copied.nr;
00555    }
00556 }
00557 
00558 static void save_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz )
00559 {
00560    struct vbo_save_context *save = &vbo_context(ctx)->save; 
00561 
00562    if (sz > save->attrsz[attr]) {
00563       /* New size is larger.  Need to flush existing vertices and get
00564        * an enlarged vertex format.
00565        */
00566       _save_upgrade_vertex( ctx, attr, sz );
00567    }
00568    else if (sz < save->active_sz[attr]) {
00569       static GLfloat id[4] = { 0, 0, 0, 1 };
00570       GLuint i;
00571 
00572       /* New size is equal or smaller - just need to fill in some
00573        * zeros.
00574        */
00575       for (i = sz ; i <= save->attrsz[attr] ; i++)
00576      save->attrptr[attr][i-1] = id[i-1];
00577    }
00578 
00579    save->active_sz[attr] = sz;
00580 }
00581 
00582 static void _save_reset_vertex( GLcontext *ctx )
00583 {
00584    struct vbo_save_context *save = &vbo_context(ctx)->save;
00585    GLuint i;
00586 
00587    for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
00588       save->attrsz[i] = 0;
00589       save->active_sz[i] = 0;
00590    }
00591       
00592    save->vertex_size = 0;
00593 }
00594 
00595 
00596 
00597 #define ERROR()   _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ );
00598 
00599 
00600 /* Only one size for each attribute may be active at once.  Eg. if
00601  * Color3f is installed/active, then Color4f may not be, even if the
00602  * vertex actually contains 4 color coordinates.  This is because the
00603  * 3f version won't otherwise set color[3] to 1.0 -- this is the job
00604  * of the chooser function when switching between Color4f and Color3f.
00605  */
00606 #define ATTR( A, N, V0, V1, V2, V3 )                \
00607 do {                                \
00608    struct vbo_save_context *save = &vbo_context(ctx)->save; \
00609                                 \
00610    if (save->active_sz[A] != N)             \
00611       save_fixup_vertex(ctx, A, N);             \
00612                                 \
00613    {                                \
00614       GLfloat *dest = save->attrptr[A];         \
00615       if (N>0) dest[0] = V0;                    \
00616       if (N>1) dest[1] = V1;                    \
00617       if (N>2) dest[2] = V2;                    \
00618       if (N>3) dest[3] = V3;                    \
00619    }                                \
00620                                 \
00621    if ((A) == 0) {                      \
00622       GLuint i;                         \
00623                                 \
00624       for (i = 0; i < save->vertex_size; i++)           \
00625      save->vbptr[i] = save->vertex[i];          \
00626                                 \
00627       save->vbptr += save->vertex_size;             \
00628                                 \
00629       if (++save->vert_count >= save->max_vert)         \
00630      _save_wrap_filled_vertex( ctx );           \
00631    }                                \
00632 } while (0)
00633 
00634 #define TAG(x) _save_##x
00635 
00636 #include "vbo_attrib_tmp.h"
00637 
00638 
00639 
00640 
00641 /* Cope with EvalCoord/CallList called within a begin/end object:
00642  *     -- Flush current buffer
00643  *     -- Fallback to opcodes for the rest of the begin/end object.
00644  */
00645 #define DO_FALLBACK(ctx)                            \
00646 do {                                    \
00647    struct vbo_save_context *save = &vbo_context(ctx)->save;                 \
00648                                     \
00649    if (save->vert_count || save->prim_count)                        \
00650       _save_compile_vertex_list( ctx );                 \
00651                                     \
00652    _save_copy_to_current( ctx );                    \
00653    _save_reset_vertex( ctx );                       \
00654    _save_reset_counters( ctx );  \
00655    _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );    \
00656    ctx->Driver.SaveNeedFlush = 0;                   \
00657 } while (0)
00658 
00659 static void GLAPIENTRY _save_EvalCoord1f( GLfloat u )
00660 {
00661    GET_CURRENT_CONTEXT(ctx);
00662    DO_FALLBACK(ctx);
00663    ctx->Save->EvalCoord1f( u );
00664 }
00665 
00666 static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v )
00667 {
00668    GET_CURRENT_CONTEXT(ctx);
00669    DO_FALLBACK(ctx);
00670    ctx->Save->EvalCoord1fv( v );
00671 }
00672 
00673 static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v )
00674 {
00675    GET_CURRENT_CONTEXT(ctx);
00676    DO_FALLBACK(ctx);
00677    ctx->Save->EvalCoord2f( u, v );
00678 }
00679 
00680 static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v )
00681 {
00682    GET_CURRENT_CONTEXT(ctx);
00683    DO_FALLBACK(ctx);
00684    ctx->Save->EvalCoord2fv( v );
00685 }
00686 
00687 static void GLAPIENTRY _save_EvalPoint1( GLint i )
00688 {
00689    GET_CURRENT_CONTEXT(ctx);
00690    DO_FALLBACK(ctx);
00691    ctx->Save->EvalPoint1( i );
00692 }
00693 
00694 static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j )
00695 {
00696    GET_CURRENT_CONTEXT(ctx);
00697    DO_FALLBACK(ctx);
00698    ctx->Save->EvalPoint2( i, j );
00699 }
00700 
00701 static void GLAPIENTRY _save_CallList( GLuint l )
00702 {
00703    GET_CURRENT_CONTEXT(ctx);
00704    DO_FALLBACK(ctx);
00705    ctx->Save->CallList( l );
00706 }
00707 
00708 static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v )
00709 {
00710    GET_CURRENT_CONTEXT(ctx);
00711    DO_FALLBACK(ctx);
00712    ctx->Save->CallLists( n, type, v );
00713 }
00714 
00715 
00716 
00717 
00718 /* This begin is hooked into ...  Updating of
00719  * ctx->Driver.CurrentSavePrimitive is already taken care of.
00720  */
00721 GLboolean vbo_save_NotifyBegin( GLcontext *ctx, GLenum mode )
00722 {
00723    struct vbo_save_context *save = &vbo_context(ctx)->save; 
00724 
00725    GLuint i = save->prim_count++;
00726 
00727    assert(i < save->prim_max);
00728    save->prim[i].mode = mode & ~VBO_SAVE_PRIM_WEAK;
00729    save->prim[i].begin = 1;
00730    save->prim[i].end = 0;
00731    save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
00732    save->prim[i].pad = 0;
00733    save->prim[i].start = save->vert_count;
00734    save->prim[i].count = 0;   
00735 
00736    _mesa_install_save_vtxfmt( ctx, &save->vtxfmt );      
00737    ctx->Driver.SaveNeedFlush = 1;
00738    return GL_TRUE;
00739 }
00740 
00741 
00742 
00743 static void GLAPIENTRY _save_End( void )
00744 {
00745    GET_CURRENT_CONTEXT( ctx ); 
00746    struct vbo_save_context *save = &vbo_context(ctx)->save; 
00747    GLint i = save->prim_count - 1;
00748 
00749    ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
00750    save->prim[i].end = 1;
00751    save->prim[i].count = (save->vert_count - 
00752               save->prim[i].start);
00753 
00754    if (i == (GLint) save->prim_max - 1) {
00755       _save_compile_vertex_list( ctx );
00756       assert(save->copied.nr == 0);
00757    }
00758 
00759    /* Swap out this vertex format while outside begin/end.  Any color,
00760     * etc. received between here and the next begin will be compiled
00761     * as opcodes.
00762     */   
00763    _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
00764 }
00765 
00766 
00767 /* These are all errors as this vtxfmt is only installed inside
00768  * begin/end pairs.
00769  */
00770 static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type,
00771                    const GLvoid *indices)
00772 {
00773    GET_CURRENT_CONTEXT(ctx);
00774    (void) mode; (void) count; (void) type; (void) indices;
00775    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" );
00776 }
00777 
00778 
00779 static void GLAPIENTRY _save_DrawRangeElements(GLenum mode,
00780                     GLuint start, GLuint end,
00781                     GLsizei count, GLenum type,
00782                     const GLvoid *indices)
00783 {
00784    GET_CURRENT_CONTEXT(ctx);
00785    (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices;
00786    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" );
00787 }
00788 
00789 static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count)
00790 {
00791    GET_CURRENT_CONTEXT(ctx);
00792    (void) mode; (void) start; (void) count;
00793    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" );
00794 }
00795 
00796 static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
00797 {
00798    GET_CURRENT_CONTEXT(ctx);
00799    (void) x1; (void) y1; (void) x2; (void) y2;
00800    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" );
00801 }
00802 
00803 static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 )
00804 {
00805    GET_CURRENT_CONTEXT(ctx);
00806    (void) mode; (void) i1; (void) i2;
00807    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" );
00808 }
00809 
00810 static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2,
00811                   GLint j1, GLint j2 )
00812 {
00813    GET_CURRENT_CONTEXT(ctx);
00814    (void) mode; (void) i1; (void) i2; (void) j1; (void) j2;
00815    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" );
00816 }
00817 
00818 static void GLAPIENTRY _save_Begin( GLenum mode )
00819 {
00820    GET_CURRENT_CONTEXT( ctx );
00821    (void) mode;
00822    _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" );
00823 }
00824 
00825 
00826 /* Unlike the functions above, these are to be hooked into the vtxfmt
00827  * maintained in ctx->ListState, active when the list is known or
00828  * suspected to be outside any begin/end primitive.
00829  */
00830 static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
00831 {
00832    GET_CURRENT_CONTEXT(ctx);
00833    vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK );
00834    CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 ));
00835    CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 ));
00836    CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 ));
00837    CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 ));
00838    CALL_End(GET_DISPATCH(), ());
00839 }
00840 
00841 
00842 static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
00843 {
00844    GET_CURRENT_CONTEXT(ctx);
00845    GLint i;
00846 
00847    if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
00848       return;
00849 
00850    _ae_map_vbos( ctx );
00851 
00852    vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK );
00853 
00854    for (i = 0; i < count; i++)
00855        CALL_ArrayElement(GET_DISPATCH(), (start + i));
00856    CALL_End(GET_DISPATCH(), ());
00857 
00858    _ae_unmap_vbos( ctx );
00859 }
00860 
00861 /* Could do better by copying the arrays and element list intact and
00862  * then emitting an indexed prim at runtime.
00863  */
00864 static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
00865                    const GLvoid *indices)
00866 {
00867    GET_CURRENT_CONTEXT(ctx);
00868    GLint i;
00869 
00870    if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
00871       return;
00872 
00873    _ae_map_vbos( ctx );
00874 
00875    if (ctx->Array.ElementArrayBufferObj->Name)
00876       indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices);
00877 
00878    vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK );
00879 
00880    switch (type) {
00881    case GL_UNSIGNED_BYTE:
00882       for (i = 0 ; i < count ; i++)
00883       CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] ));
00884       break;
00885    case GL_UNSIGNED_SHORT:
00886       for (i = 0 ; i < count ; i++)
00887       CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] ));
00888       break;
00889    case GL_UNSIGNED_INT:
00890       for (i = 0 ; i < count ; i++)
00891       CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] ));
00892       break;
00893    default:
00894       _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
00895       break;
00896    }
00897 
00898    CALL_End(GET_DISPATCH(), ());
00899 
00900    _ae_unmap_vbos( ctx );
00901 }
00902 
00903 static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode,
00904                     GLuint start, GLuint end,
00905                     GLsizei count, GLenum type,
00906                     const GLvoid *indices)
00907 {
00908    GET_CURRENT_CONTEXT(ctx);
00909    if (_mesa_validate_DrawRangeElements( ctx, mode,
00910                      start, end,
00911                      count, type, indices ))
00912       _save_OBE_DrawElements( mode, count, type, indices );
00913 }
00914 
00915 
00916 
00917 
00918 
00919 static void _save_vtxfmt_init( GLcontext *ctx )
00920 {
00921    struct vbo_save_context *save = &vbo_context(ctx)->save;
00922    GLvertexformat *vfmt = &save->vtxfmt;
00923 
00924    vfmt->ArrayElement = _ae_loopback_array_elt;         /* generic helper */
00925    vfmt->Begin = _save_Begin;
00926    vfmt->Color3f = _save_Color3f;
00927    vfmt->Color3fv = _save_Color3fv;
00928    vfmt->Color4f = _save_Color4f;
00929    vfmt->Color4fv = _save_Color4fv;
00930    vfmt->EdgeFlag = _save_EdgeFlag;
00931    vfmt->End = _save_End;
00932    vfmt->FogCoordfEXT = _save_FogCoordfEXT;
00933    vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
00934    vfmt->Indexf = _save_Indexf;
00935    vfmt->Indexfv = _save_Indexfv;
00936    vfmt->Materialfv = _save_Materialfv;
00937    vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
00938    vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
00939    vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
00940    vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
00941    vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
00942    vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
00943    vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
00944    vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
00945    vfmt->Normal3f = _save_Normal3f;
00946    vfmt->Normal3fv = _save_Normal3fv;
00947    vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
00948    vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
00949    vfmt->TexCoord1f = _save_TexCoord1f;
00950    vfmt->TexCoord1fv = _save_TexCoord1fv;
00951    vfmt->TexCoord2f = _save_TexCoord2f;
00952    vfmt->TexCoord2fv = _save_TexCoord2fv;
00953    vfmt->TexCoord3f = _save_TexCoord3f;
00954    vfmt->TexCoord3fv = _save_TexCoord3fv;
00955    vfmt->TexCoord4f = _save_TexCoord4f;
00956    vfmt->TexCoord4fv = _save_TexCoord4fv;
00957    vfmt->Vertex2f = _save_Vertex2f;
00958    vfmt->Vertex2fv = _save_Vertex2fv;
00959    vfmt->Vertex3f = _save_Vertex3f;
00960    vfmt->Vertex3fv = _save_Vertex3fv;
00961    vfmt->Vertex4f = _save_Vertex4f;
00962    vfmt->Vertex4fv = _save_Vertex4fv;
00963    vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
00964    vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
00965    vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
00966    vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
00967    vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
00968    vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
00969    vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
00970    vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
00971 
00972    vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
00973    vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
00974    vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
00975    vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
00976    vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
00977    vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
00978    vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
00979    vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
00980    
00981    /* This will all require us to fallback to saving the list as opcodes:
00982     */ 
00983    vfmt->CallList = _save_CallList; /* inside begin/end */
00984    vfmt->CallLists = _save_CallLists; /* inside begin/end */
00985    vfmt->EvalCoord1f = _save_EvalCoord1f;
00986    vfmt->EvalCoord1fv = _save_EvalCoord1fv;
00987    vfmt->EvalCoord2f = _save_EvalCoord2f;
00988    vfmt->EvalCoord2fv = _save_EvalCoord2fv;
00989    vfmt->EvalPoint1 = _save_EvalPoint1;
00990    vfmt->EvalPoint2 = _save_EvalPoint2;
00991 
00992    /* These are all errors as we at least know we are in some sort of
00993     * begin/end pair:
00994     */
00995    vfmt->EvalMesh1 = _save_EvalMesh1;   
00996    vfmt->EvalMesh2 = _save_EvalMesh2;
00997    vfmt->Begin = _save_Begin;
00998    vfmt->Rectf = _save_Rectf;
00999    vfmt->DrawArrays = _save_DrawArrays;
01000    vfmt->DrawElements = _save_DrawElements;
01001    vfmt->DrawRangeElements = _save_DrawRangeElements;
01002 
01003 }
01004 
01005 
01006 void vbo_save_SaveFlushVertices( GLcontext *ctx )
01007 {
01008    struct vbo_save_context *save = &vbo_context(ctx)->save;
01009 
01010    /* Noop when we are actually active:
01011     */
01012    if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
01013        ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
01014       return;
01015 
01016    if (save->vert_count ||
01017        save->prim_count) 
01018       _save_compile_vertex_list( ctx );
01019    
01020    _save_copy_to_current( ctx );
01021    _save_reset_vertex( ctx );
01022    _save_reset_counters( ctx );  
01023    ctx->Driver.SaveNeedFlush = 0;
01024 }
01025 
01026 void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode )
01027 {
01028    struct vbo_save_context *save = &vbo_context(ctx)->save;
01029 
01030    (void) list; (void) mode;
01031 
01032    if (!save->prim_store)
01033       save->prim_store = alloc_prim_store( ctx );
01034 
01035    if (!save->vertex_store) 
01036       save->vertex_store = alloc_vertex_store( ctx );
01037       
01038    save->vbptr = map_vertex_store( ctx, save->vertex_store );
01039    
01040    _save_reset_vertex( ctx );
01041    _save_reset_counters( ctx );  
01042    ctx->Driver.SaveNeedFlush = 0;
01043 }
01044 
01045 void vbo_save_EndList( GLcontext *ctx )
01046 {
01047    struct vbo_save_context *save = &vbo_context(ctx)->save;
01048 
01049    /* EndList called inside a (saved) Begin/End pair?
01050     */
01051    if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) {
01052 
01053       if (save->prim_count > 0) {
01054          GLint i = save->prim_count - 1;
01055          ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
01056          save->prim[i].end = 0;
01057          save->prim[i].count = (save->vert_count - 
01058                                 save->prim[i].start);
01059       }
01060 
01061       /* Make sure this vertex list gets replayed by the "loopback"
01062        * mechanism:
01063        */
01064       save->dangling_attr_ref = 1;
01065       vbo_save_SaveFlushVertices( ctx );
01066 
01067       /* Swap out this vertex format while outside begin/end.  Any color,
01068        * etc. received between here and the next begin will be compiled
01069        * as opcodes.
01070        */   
01071       _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
01072    }
01073 
01074    unmap_vertex_store( ctx, save->vertex_store );
01075 
01076    assert(save->vertex_size == 0);
01077 }
01078  
01079 void vbo_save_BeginCallList( GLcontext *ctx, struct mesa_display_list *dlist )
01080 {
01081    struct vbo_save_context *save = &vbo_context(ctx)->save;
01082    save->replay_flags |= dlist->flags;
01083 }
01084 
01085 void vbo_save_EndCallList( GLcontext *ctx )
01086 {
01087    struct vbo_save_context *save = &vbo_context(ctx)->save;
01088    
01089    if (ctx->ListState.CallDepth == 1) {
01090       /* This is correct: want to keep only the VBO_SAVE_FALLBACK
01091        * flag, if it is set:
01092        */
01093       save->replay_flags &= VBO_SAVE_FALLBACK;
01094    }
01095 }
01096 
01097 
01098 static void vbo_destroy_vertex_list( GLcontext *ctx, void *data )
01099 {
01100    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
01101    (void) ctx;
01102 
01103    if ( --node->vertex_store->refcount == 0 ) 
01104       free_vertex_store( ctx, node->vertex_store );
01105 
01106    if ( --node->prim_store->refcount == 0 )
01107       FREE( node->prim_store );
01108 }
01109 
01110 
01111 static void vbo_print_vertex_list( GLcontext *ctx, void *data )
01112 {
01113    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
01114    GLuint i;
01115    (void) ctx;
01116 
01117    _mesa_debug(NULL, "VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
01118                node->count,
01119            node->prim_count,
01120            node->vertex_size);
01121 
01122    for (i = 0 ; i < node->prim_count ; i++) {
01123       struct _mesa_prim *prim = &node->prim[i];
01124       _mesa_debug(NULL, "   prim %d: %s%s %d..%d %s %s\n",
01125           i, 
01126           _mesa_lookup_enum_by_nr(prim->mode),
01127           prim->weak ? " (weak)" : "",
01128           prim->start, 
01129           prim->start + prim->count,
01130           (prim->begin) ? "BEGIN" : "(wrap)",
01131           (prim->end) ? "END" : "(wrap)");
01132    }
01133 }
01134 
01135 
01136 static void _save_current_init( GLcontext *ctx ) 
01137 {
01138    struct vbo_save_context *save = &vbo_context(ctx)->save;
01139    GLint i;
01140 
01141    for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
01142       const GLuint j = i - VBO_ATTRIB_POS;
01143       ASSERT(j < VERT_ATTRIB_MAX);
01144       save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
01145       save->current[i] = ctx->ListState.CurrentAttrib[j];
01146    }
01147 
01148    for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
01149       const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
01150       ASSERT(j < MAT_ATTRIB_MAX);
01151       save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
01152       save->current[i] = ctx->ListState.CurrentMaterial[j];
01153    }
01154 }
01155 
01159 void vbo_save_api_init( struct vbo_save_context *save )
01160 {
01161    GLcontext *ctx = save->ctx;
01162    GLuint i;
01163 
01164    save->opcode_vertex_list =
01165       _mesa_alloc_opcode( ctx,
01166               sizeof(struct vbo_save_vertex_list),
01167               vbo_save_playback_vertex_list,
01168               vbo_destroy_vertex_list,
01169               vbo_print_vertex_list );
01170 
01171    ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
01172 
01173    _save_vtxfmt_init( ctx );
01174    _save_current_init( ctx );
01175 
01176    /* These will actually get set again when binding/drawing */
01177    for (i = 0; i < VBO_ATTRIB_MAX; i++)
01178       save->inputs[i] = &save->arrays[i];
01179 
01180    /* Hook our array functions into the outside-begin-end vtxfmt in 
01181     * ctx->ListState.
01182     */
01183    ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
01184    ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
01185    ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
01186    ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
01187    _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
01188 }
01189 

Generated on Sun May 27 2012 04:20:48 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.