Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / mesa / vbo / vbo_save_api.c
1 /**************************************************************************
2
3 Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas.
4
5 All Rights Reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 on the rights to use, copy, modify, merge, publish, distribute, sub
11 license, and/or sell copies of the Software, and to permit persons to whom
12 the Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice (including the next
15 paragraph) shall be included in all copies or substantial portions of the
16 Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **************************************************************************/
27
28 /*
29  * Authors:
30  *   Keith Whitwell <keith@tungstengraphics.com>
31  */
32
33
34
35 /* Display list compiler attempts to store lists of vertices with the
36  * same vertex layout.  Additionally it attempts to minimize the need
37  * for execute-time fixup of these vertex lists, allowing them to be
38  * cached on hardware.
39  *
40  * There are still some circumstances where this can be thwarted, for
41  * example by building a list that consists of one very long primitive
42  * (eg Begin(Triangles), 1000 vertices, End), and calling that list
43  * from inside a different begin/end object (Begin(Lines), CallList,
44  * End).  
45  *
46  * In that case the code will have to replay the list as individual
47  * commands through the Exec dispatch table, or fix up the copied
48  * vertices at execute-time.
49  *
50  * The other case where fixup is required is when a vertex attribute
51  * is introduced in the middle of a primitive.  Eg:
52  *  Begin(Lines)
53  *  TexCoord1f()           Vertex2f()
54  *  TexCoord1f() Color3f() Vertex2f()
55  *  End()
56  *
57  *  If the current value of Color isn't known at compile-time, this
58  *  primitive will require fixup.
59  *
60  *
61  * The list compiler currently doesn't attempt to compile lists
62  * containing EvalCoord or EvalPoint commands.  On encountering one of
63  * these, compilation falls back to opcodes.  
64  *
65  * This could be improved to fallback only when a mix of EvalCoord and
66  * Vertex commands are issued within a single primitive.
67  */
68
69
70 #include "main/glheader.h"
71 #include "main/bufferobj.h"
72 #include "main/context.h"
73 #include "main/dlist.h"
74 #include "main/enums.h"
75 #include "main/eval.h"
76 #include "main/macros.h"
77 #include "main/mfeatures.h"
78 #include "main/api_noop.h"
79 #include "main/api_validate.h"
80 #include "main/api_arrayelt.h"
81 #include "main/vtxfmt.h"
82 #include "main/dispatch.h"
83
84 #include "vbo_context.h"
85
86
87 #if FEATURE_dlist
88
89
90 #ifdef ERROR
91 #undef ERROR
92 #endif
93
94
95 /* An interesting VBO number/name to help with debugging */
96 #define VBO_BUF_ID  12345
97
98
99 /*
100  * NOTE: Old 'parity' issue is gone, but copying can still be
101  * wrong-footed on replay.
102  */
103 static GLuint
104 _save_copy_vertices(struct gl_context *ctx,
105                     const struct vbo_save_vertex_list *node,
106                     const GLfloat * src_buffer)
107 {
108    struct vbo_save_context *save = &vbo_context(ctx)->save;
109    const struct _mesa_prim *prim = &node->prim[node->prim_count - 1];
110    GLuint nr = prim->count;
111    GLuint sz = save->vertex_size;
112    const GLfloat *src = src_buffer + prim->start * sz;
113    GLfloat *dst = save->copied.buffer;
114    GLuint ovf, i;
115
116    if (prim->end)
117       return 0;
118
119    switch (prim->mode) {
120    case GL_POINTS:
121       return 0;
122    case GL_LINES:
123       ovf = nr & 1;
124       for (i = 0; i < ovf; i++)
125          memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
126                 sz * sizeof(GLfloat));
127       return i;
128    case GL_TRIANGLES:
129       ovf = nr % 3;
130       for (i = 0; i < ovf; i++)
131          memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
132                 sz * sizeof(GLfloat));
133       return i;
134    case GL_QUADS:
135       ovf = nr & 3;
136       for (i = 0; i < ovf; i++)
137          memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
138                 sz * sizeof(GLfloat));
139       return i;
140    case GL_LINE_STRIP:
141       if (nr == 0)
142          return 0;
143       else {
144          memcpy(dst, src + (nr - 1) * sz, sz * sizeof(GLfloat));
145          return 1;
146       }
147    case GL_LINE_LOOP:
148    case GL_TRIANGLE_FAN:
149    case GL_POLYGON:
150       if (nr == 0)
151          return 0;
152       else if (nr == 1) {
153          memcpy(dst, src + 0, sz * sizeof(GLfloat));
154          return 1;
155       }
156       else {
157          memcpy(dst, src + 0, sz * sizeof(GLfloat));
158          memcpy(dst + sz, src + (nr - 1) * sz, sz * sizeof(GLfloat));
159          return 2;
160       }
161    case GL_TRIANGLE_STRIP:
162    case GL_QUAD_STRIP:
163       switch (nr) {
164       case 0:
165          ovf = 0;
166          break;
167       case 1:
168          ovf = 1;
169          break;
170       default:
171          ovf = 2 + (nr & 1);
172          break;
173       }
174       for (i = 0; i < ovf; i++)
175          memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
176                 sz * sizeof(GLfloat));
177       return i;
178    default:
179       assert(0);
180       return 0;
181    }
182 }
183
184
185 static struct vbo_save_vertex_store *
186 alloc_vertex_store(struct gl_context *ctx)
187 {
188    struct vbo_save_vertex_store *vertex_store =
189       CALLOC_STRUCT(vbo_save_vertex_store);
190
191    /* obj->Name needs to be non-zero, but won't ever be examined more
192     * closely than that.  In particular these buffers won't be entered
193     * into the hash and can never be confused with ones visible to the
194     * user.  Perhaps there could be a special number for internal
195     * buffers:
196     */
197    vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx,
198                                                          VBO_BUF_ID,
199                                                          GL_ARRAY_BUFFER_ARB);
200
201    ctx->Driver.BufferData(ctx,
202                           GL_ARRAY_BUFFER_ARB,
203                           VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
204                           NULL, GL_STATIC_DRAW_ARB, vertex_store->bufferobj);
205
206    vertex_store->buffer = NULL;
207    vertex_store->used = 0;
208    vertex_store->refcount = 1;
209
210    return vertex_store;
211 }
212
213
214 static void
215 free_vertex_store(struct gl_context *ctx,
216                   struct vbo_save_vertex_store *vertex_store)
217 {
218    assert(!vertex_store->buffer);
219
220    if (vertex_store->bufferobj) {
221       _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
222    }
223
224    FREE(vertex_store);
225 }
226
227
228 static GLfloat *
229 map_vertex_store(struct gl_context *ctx,
230                  struct vbo_save_vertex_store *vertex_store)
231 {
232    assert(vertex_store->bufferobj);
233    assert(!vertex_store->buffer);
234    vertex_store->buffer =
235       (GLfloat *) ctx->Driver.MapBuffer(ctx,
236                                         GL_ARRAY_BUFFER_ARB,   /* not used */
237                                         GL_WRITE_ONLY,      /* not used */
238                                         vertex_store->
239                                         bufferobj);
240
241    assert(vertex_store->buffer);
242    return vertex_store->buffer + vertex_store->used;
243 }
244
245
246 static void
247 unmap_vertex_store(struct gl_context *ctx,
248                    struct vbo_save_vertex_store *vertex_store)
249 {
250    ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj);
251    vertex_store->buffer = NULL;
252 }
253
254
255 static struct vbo_save_primitive_store *
256 alloc_prim_store(struct gl_context *ctx)
257 {
258    struct vbo_save_primitive_store *store =
259       CALLOC_STRUCT(vbo_save_primitive_store);
260    (void) ctx;
261    store->used = 0;
262    store->refcount = 1;
263    return store;
264 }
265
266
267 static void
268 _save_reset_counters(struct gl_context *ctx)
269 {
270    struct vbo_save_context *save = &vbo_context(ctx)->save;
271
272    save->prim = save->prim_store->buffer + save->prim_store->used;
273    save->buffer = save->vertex_store->buffer + save->vertex_store->used;
274
275    assert(save->buffer == save->buffer_ptr);
276
277    if (save->vertex_size)
278       save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
279                         save->vertex_size);
280    else
281       save->max_vert = 0;
282
283    save->vert_count = 0;
284    save->prim_count = 0;
285    save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
286    save->dangling_attr_ref = 0;
287 }
288
289
290 /**
291  * Insert the active immediate struct onto the display list currently
292  * being built.
293  */
294 static void
295 _save_compile_vertex_list(struct gl_context *ctx)
296 {
297    struct vbo_save_context *save = &vbo_context(ctx)->save;
298    struct vbo_save_vertex_list *node;
299
300    /* Allocate space for this structure in the display list currently
301     * being compiled.
302     */
303    node = (struct vbo_save_vertex_list *)
304       _mesa_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node));
305
306    if (!node)
307       return;
308
309    /* Duplicate our template, increment refcounts to the storage structs:
310     */
311    memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
312    node->vertex_size = save->vertex_size;
313    node->buffer_offset =
314       (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat);
315    node->count = save->vert_count;
316    node->wrap_count = save->copied.nr;
317    node->dangling_attr_ref = save->dangling_attr_ref;
318    node->prim = save->prim;
319    node->prim_count = save->prim_count;
320    node->vertex_store = save->vertex_store;
321    node->prim_store = save->prim_store;
322
323    node->vertex_store->refcount++;
324    node->prim_store->refcount++;
325
326    if (node->prim[0].no_current_update) {
327       node->current_size = 0;
328       node->current_data = NULL;
329    }
330    else {
331       node->current_size = node->vertex_size - node->attrsz[0];
332       node->current_data = NULL;
333
334       if (node->current_size) {
335          /* If the malloc fails, we just pull the data out of the VBO
336           * later instead.
337           */
338          node->current_data = MALLOC(node->current_size * sizeof(GLfloat));
339          if (node->current_data) {
340             const char *buffer = (const char *) save->vertex_store->buffer;
341             unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
342             unsigned vertex_offset = 0;
343
344             if (node->count)
345                vertex_offset =
346                   (node->count - 1) * node->vertex_size * sizeof(GLfloat);
347
348             memcpy(node->current_data,
349                    buffer + node->buffer_offset + vertex_offset + attr_offset,
350                    node->current_size * sizeof(GLfloat));
351          }
352       }
353    }
354
355    assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0);
356
357    if (save->dangling_attr_ref)
358       ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
359
360    save->vertex_store->used += save->vertex_size * node->count;
361    save->prim_store->used += node->prim_count;
362
363    /* Copy duplicated vertices 
364     */
365    save->copied.nr = _save_copy_vertices(ctx, node, save->buffer);
366
367    /* Deal with GL_COMPILE_AND_EXECUTE:
368     */
369    if (ctx->ExecuteFlag) {
370       struct _glapi_table *dispatch = GET_DISPATCH();
371
372       _glapi_set_dispatch(ctx->Exec);
373
374       vbo_loopback_vertex_list(ctx,
375                                (const GLfloat *) ((const char *) save->
376                                                   vertex_store->buffer +
377                                                   node->buffer_offset),
378                                node->attrsz, node->prim, node->prim_count,
379                                node->wrap_count, node->vertex_size);
380
381       _glapi_set_dispatch(dispatch);
382    }
383
384    /* Decide whether the storage structs are full, or can be used for
385     * the next vertex lists as well.
386     */
387    if (save->vertex_store->used >
388        VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) {
389
390       /* Unmap old store:
391        */
392       unmap_vertex_store(ctx, save->vertex_store);
393
394       /* Release old reference:
395        */
396       save->vertex_store->refcount--;
397       assert(save->vertex_store->refcount != 0);
398       save->vertex_store = NULL;
399
400       /* Allocate and map new store:
401        */
402       save->vertex_store = alloc_vertex_store(ctx);
403       save->buffer_ptr = map_vertex_store(ctx, save->vertex_store);
404    }
405
406    if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
407       save->prim_store->refcount--;
408       assert(save->prim_store->refcount != 0);
409       save->prim_store = alloc_prim_store(ctx);
410    }
411
412    /* Reset our structures for the next run of vertices:
413     */
414    _save_reset_counters(ctx);
415 }
416
417
418 /**
419  * TODO -- If no new vertices have been stored, don't bother saving it.
420  */
421 static void
422 _save_wrap_buffers(struct gl_context *ctx)
423 {
424    struct vbo_save_context *save = &vbo_context(ctx)->save;
425    GLint i = save->prim_count - 1;
426    GLenum mode;
427    GLboolean weak;
428    GLboolean no_current_update;
429
430    assert(i < (GLint) save->prim_max);
431    assert(i >= 0);
432
433    /* Close off in-progress primitive.
434     */
435    save->prim[i].count = (save->vert_count - save->prim[i].start);
436    mode = save->prim[i].mode;
437    weak = save->prim[i].weak;
438    no_current_update = save->prim[i].no_current_update;
439
440    /* store the copied vertices, and allocate a new list.
441     */
442    _save_compile_vertex_list(ctx);
443
444    /* Restart interrupted primitive
445     */
446    save->prim[0].mode = mode;
447    save->prim[0].weak = weak;
448    save->prim[0].no_current_update = no_current_update;
449    save->prim[0].begin = 0;
450    save->prim[0].end = 0;
451    save->prim[0].pad = 0;
452    save->prim[0].start = 0;
453    save->prim[0].count = 0;
454    save->prim[0].num_instances = 1;
455    save->prim_count = 1;
456 }
457
458
459 /**
460  * Called only when buffers are wrapped as the result of filling the
461  * vertex_store struct.  
462  */
463 static void
464 _save_wrap_filled_vertex(struct gl_context *ctx)
465 {
466    struct vbo_save_context *save = &vbo_context(ctx)->save;
467    GLfloat *data = save->copied.buffer;
468    GLuint i;
469
470    /* Emit a glEnd to close off the last vertex list.
471     */
472    _save_wrap_buffers(ctx);
473
474    /* Copy stored stored vertices to start of new list.
475     */
476    assert(save->max_vert - save->vert_count > save->copied.nr);
477
478    for (i = 0; i < save->copied.nr; i++) {
479       memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat));
480       data += save->vertex_size;
481       save->buffer_ptr += save->vertex_size;
482       save->vert_count++;
483    }
484 }
485
486
487 static void
488 _save_copy_to_current(struct gl_context *ctx)
489 {
490    struct vbo_save_context *save = &vbo_context(ctx)->save;
491    GLuint i;
492
493    for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
494       if (save->attrsz[i]) {
495          save->currentsz[i][0] = save->attrsz[i];
496          COPY_CLEAN_4V(save->current[i], save->attrsz[i], save->attrptr[i]);
497       }
498    }
499 }
500
501
502 static void
503 _save_copy_from_current(struct gl_context *ctx)
504 {
505    struct vbo_save_context *save = &vbo_context(ctx)->save;
506    GLint i;
507
508    for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
509       switch (save->attrsz[i]) {
510       case 4:
511          save->attrptr[i][3] = save->current[i][3];
512       case 3:
513          save->attrptr[i][2] = save->current[i][2];
514       case 2:
515          save->attrptr[i][1] = save->current[i][1];
516       case 1:
517          save->attrptr[i][0] = save->current[i][0];
518       case 0:
519          break;
520       }
521    }
522 }
523
524
525 /* Flush existing data, set new attrib size, replay copied vertices.
526  */
527 static void
528 _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
529 {
530    struct vbo_save_context *save = &vbo_context(ctx)->save;
531    GLuint oldsz;
532    GLuint i;
533    GLfloat *tmp;
534
535    /* Store the current run of vertices, and emit a GL_END.  Emit a
536     * BEGIN in the new buffer.
537     */
538    if (save->vert_count)
539       _save_wrap_buffers(ctx);
540    else
541       assert(save->copied.nr == 0);
542
543    /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
544     * when the attribute already exists in the vertex and is having
545     * its size increased.  
546     */
547    _save_copy_to_current(ctx);
548
549    /* Fix up sizes:
550     */
551    oldsz = save->attrsz[attr];
552    save->attrsz[attr] = newsz;
553
554    save->vertex_size += newsz - oldsz;
555    save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
556                      save->vertex_size);
557    save->vert_count = 0;
558
559    /* Recalculate all the attrptr[] values:
560     */
561    for (i = 0, tmp = save->vertex; i < VBO_ATTRIB_MAX; i++) {
562       if (save->attrsz[i]) {
563          save->attrptr[i] = tmp;
564          tmp += save->attrsz[i];
565       }
566       else {
567          save->attrptr[i] = NULL;       /* will not be dereferenced. */
568       }
569    }
570
571    /* Copy from current to repopulate the vertex with correct values.
572     */
573    _save_copy_from_current(ctx);
574
575    /* Replay stored vertices to translate them to new format here.
576     *
577     * If there are copied vertices and the new (upgraded) attribute
578     * has not been defined before, this list is somewhat degenerate,
579     * and will need fixup at runtime.
580     */
581    if (save->copied.nr) {
582       GLfloat *data = save->copied.buffer;
583       GLfloat *dest = save->buffer;
584       GLuint j;
585
586       /* Need to note this and fix up at runtime (or loopback):
587        */
588       if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
589          assert(oldsz == 0);
590          save->dangling_attr_ref = GL_TRUE;
591       }
592
593       for (i = 0; i < save->copied.nr; i++) {
594          for (j = 0; j < VBO_ATTRIB_MAX; j++) {
595             if (save->attrsz[j]) {
596                if (j == attr) {
597                   if (oldsz) {
598                      COPY_CLEAN_4V(dest, oldsz, data);
599                      data += oldsz;
600                      dest += newsz;
601                   }
602                   else {
603                      COPY_SZ_4V(dest, newsz, save->current[attr]);
604                      dest += newsz;
605                   }
606                }
607                else {
608                   GLint sz = save->attrsz[j];
609                   COPY_SZ_4V(dest, sz, data);
610                   data += sz;
611                   dest += sz;
612                }
613             }
614          }
615       }
616
617       save->buffer_ptr = dest;
618       save->vert_count += save->copied.nr;
619    }
620 }
621
622
623 static void
624 save_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint sz)
625 {
626    struct vbo_save_context *save = &vbo_context(ctx)->save;
627
628    if (sz > save->attrsz[attr]) {
629       /* New size is larger.  Need to flush existing vertices and get
630        * an enlarged vertex format.
631        */
632       _save_upgrade_vertex(ctx, attr, sz);
633    }
634    else if (sz < save->active_sz[attr]) {
635       static GLfloat id[4] = { 0, 0, 0, 1 };
636       GLuint i;
637
638       /* New size is equal or smaller - just need to fill in some
639        * zeros.
640        */
641       for (i = sz; i <= save->attrsz[attr]; i++)
642          save->attrptr[attr][i - 1] = id[i - 1];
643    }
644
645    save->active_sz[attr] = sz;
646 }
647
648
649 static void
650 _save_reset_vertex(struct gl_context *ctx)
651 {
652    struct vbo_save_context *save = &vbo_context(ctx)->save;
653    GLuint i;
654
655    for (i = 0; i < VBO_ATTRIB_MAX; i++) {
656       save->attrsz[i] = 0;
657       save->active_sz[i] = 0;
658    }
659
660    save->vertex_size = 0;
661 }
662
663
664
665 #define ERROR(err)   _mesa_compile_error(ctx, err, __FUNCTION__);
666
667
668 /* Only one size for each attribute may be active at once.  Eg. if
669  * Color3f is installed/active, then Color4f may not be, even if the
670  * vertex actually contains 4 color coordinates.  This is because the
671  * 3f version won't otherwise set color[3] to 1.0 -- this is the job
672  * of the chooser function when switching between Color4f and Color3f.
673  */
674 #define ATTR(A, N, V0, V1, V2, V3)                              \
675 do {                                                            \
676    struct vbo_save_context *save = &vbo_context(ctx)->save;     \
677                                                                 \
678    if (save->active_sz[A] != N)                                 \
679       save_fixup_vertex(ctx, A, N);                             \
680                                                                 \
681    {                                                            \
682       GLfloat *dest = save->attrptr[A];                         \
683       if (N>0) dest[0] = V0;                                    \
684       if (N>1) dest[1] = V1;                                    \
685       if (N>2) dest[2] = V2;                                    \
686       if (N>3) dest[3] = V3;                                    \
687    }                                                            \
688                                                                 \
689    if ((A) == 0) {                                              \
690       GLuint i;                                                 \
691                                                                 \
692       for (i = 0; i < save->vertex_size; i++)                   \
693          save->buffer_ptr[i] = save->vertex[i];                 \
694                                                                 \
695       save->buffer_ptr += save->vertex_size;                    \
696                                                                 \
697       if (++save->vert_count >= save->max_vert)                 \
698          _save_wrap_filled_vertex(ctx);                         \
699    }                                                            \
700 } while (0)
701
702 #define TAG(x) _save_##x
703
704 #include "vbo_attrib_tmp.h"
705
706
707
708
709 /* Cope with EvalCoord/CallList called within a begin/end object:
710  *     -- Flush current buffer
711  *     -- Fallback to opcodes for the rest of the begin/end object.
712  */
713 static void
714 dlist_fallback(struct gl_context *ctx)
715 {
716    struct vbo_save_context *save = &vbo_context(ctx)->save;
717
718    if (save->vert_count || save->prim_count) {
719       if (save->prim_count > 0) {
720          /* Close off in-progress primitive. */
721          GLint i = save->prim_count - 1;
722          save->prim[i].count = save->vert_count - save->prim[i].start;
723       }
724
725       /* Need to replay this display list with loopback,
726        * unfortunately, otherwise this primitive won't be handled
727        * properly:
728        */
729       save->dangling_attr_ref = 1;
730
731       _save_compile_vertex_list(ctx);
732    }
733
734    _save_copy_to_current(ctx);
735    _save_reset_vertex(ctx);
736    _save_reset_counters(ctx);
737    _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
738    ctx->Driver.SaveNeedFlush = 0;
739 }
740
741
742 static void GLAPIENTRY
743 _save_EvalCoord1f(GLfloat u)
744 {
745    GET_CURRENT_CONTEXT(ctx);
746    dlist_fallback(ctx);
747    CALL_EvalCoord1f(ctx->Save, (u));
748 }
749
750 static void GLAPIENTRY
751 _save_EvalCoord1fv(const GLfloat * v)
752 {
753    GET_CURRENT_CONTEXT(ctx);
754    dlist_fallback(ctx);
755    CALL_EvalCoord1fv(ctx->Save, (v));
756 }
757
758 static void GLAPIENTRY
759 _save_EvalCoord2f(GLfloat u, GLfloat v)
760 {
761    GET_CURRENT_CONTEXT(ctx);
762    dlist_fallback(ctx);
763    CALL_EvalCoord2f(ctx->Save, (u, v));
764 }
765
766 static void GLAPIENTRY
767 _save_EvalCoord2fv(const GLfloat * v)
768 {
769    GET_CURRENT_CONTEXT(ctx);
770    dlist_fallback(ctx);
771    CALL_EvalCoord2fv(ctx->Save, (v));
772 }
773
774 static void GLAPIENTRY
775 _save_EvalPoint1(GLint i)
776 {
777    GET_CURRENT_CONTEXT(ctx);
778    dlist_fallback(ctx);
779    CALL_EvalPoint1(ctx->Save, (i));
780 }
781
782 static void GLAPIENTRY
783 _save_EvalPoint2(GLint i, GLint j)
784 {
785    GET_CURRENT_CONTEXT(ctx);
786    dlist_fallback(ctx);
787    CALL_EvalPoint2(ctx->Save, (i, j));
788 }
789
790 static void GLAPIENTRY
791 _save_CallList(GLuint l)
792 {
793    GET_CURRENT_CONTEXT(ctx);
794    dlist_fallback(ctx);
795    CALL_CallList(ctx->Save, (l));
796 }
797
798 static void GLAPIENTRY
799 _save_CallLists(GLsizei n, GLenum type, const GLvoid * v)
800 {
801    GET_CURRENT_CONTEXT(ctx);
802    dlist_fallback(ctx);
803    CALL_CallLists(ctx->Save, (n, type, v));
804 }
805
806
807
808 /* This begin is hooked into ...  Updating of
809  * ctx->Driver.CurrentSavePrimitive is already taken care of.
810  */
811 GLboolean
812 vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
813 {
814    struct vbo_save_context *save = &vbo_context(ctx)->save;
815
816    GLuint i = save->prim_count++;
817
818    assert(i < save->prim_max);
819    save->prim[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
820    save->prim[i].begin = 1;
821    save->prim[i].end = 0;
822    save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
823    save->prim[i].no_current_update =
824       (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
825    save->prim[i].pad = 0;
826    save->prim[i].start = save->vert_count;
827    save->prim[i].count = 0;
828    save->prim[i].num_instances = 1;
829
830    _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
831    ctx->Driver.SaveNeedFlush = 1;
832    return GL_TRUE;
833 }
834
835
836 static void GLAPIENTRY
837 _save_End(void)
838 {
839    GET_CURRENT_CONTEXT(ctx);
840    struct vbo_save_context *save = &vbo_context(ctx)->save;
841    GLint i = save->prim_count - 1;
842
843    ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
844    save->prim[i].end = 1;
845    save->prim[i].count = (save->vert_count - save->prim[i].start);
846
847    if (i == (GLint) save->prim_max - 1) {
848       _save_compile_vertex_list(ctx);
849       assert(save->copied.nr == 0);
850    }
851
852    /* Swap out this vertex format while outside begin/end.  Any color,
853     * etc. received between here and the next begin will be compiled
854     * as opcodes.
855     */
856    _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
857 }
858
859
860 /* These are all errors as this vtxfmt is only installed inside
861  * begin/end pairs.
862  */
863 static void GLAPIENTRY
864 _save_DrawElements(GLenum mode, GLsizei count, GLenum type,
865                    const GLvoid * indices)
866 {
867    GET_CURRENT_CONTEXT(ctx);
868    (void) mode;
869    (void) count;
870    (void) type;
871    (void) indices;
872    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements");
873 }
874
875
876 static void GLAPIENTRY
877 _save_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
878                         GLsizei count, GLenum type, const GLvoid * indices)
879 {
880    GET_CURRENT_CONTEXT(ctx);
881    (void) mode;
882    (void) start;
883    (void) end;
884    (void) count;
885    (void) type;
886    (void) indices;
887    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawRangeElements");
888 }
889
890
891 static void GLAPIENTRY
892 _save_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
893                              const GLvoid * indices, GLint basevertex)
894 {
895    GET_CURRENT_CONTEXT(ctx);
896    (void) mode;
897    (void) count;
898    (void) type;
899    (void) indices;
900    (void) basevertex;
901    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements");
902 }
903
904
905 static void GLAPIENTRY
906 _save_DrawRangeElementsBaseVertex(GLenum mode,
907                                   GLuint start,
908                                   GLuint end,
909                                   GLsizei count,
910                                   GLenum type,
911                                   const GLvoid * indices, GLint basevertex)
912 {
913    GET_CURRENT_CONTEXT(ctx);
914    (void) mode;
915    (void) start;
916    (void) end;
917    (void) count;
918    (void) type;
919    (void) indices;
920    (void) basevertex;
921    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawRangeElements");
922 }
923
924
925 static void GLAPIENTRY
926 _save_DrawArrays(GLenum mode, GLint start, GLsizei count)
927 {
928    GET_CURRENT_CONTEXT(ctx);
929    (void) mode;
930    (void) start;
931    (void) count;
932    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawArrays");
933 }
934
935
936 static void GLAPIENTRY
937 _save_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
938 {
939    GET_CURRENT_CONTEXT(ctx);
940    (void) x1;
941    (void) y1;
942    (void) x2;
943    (void) y2;
944    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glRectf");
945 }
946
947
948 static void GLAPIENTRY
949 _save_EvalMesh1(GLenum mode, GLint i1, GLint i2)
950 {
951    GET_CURRENT_CONTEXT(ctx);
952    (void) mode;
953    (void) i1;
954    (void) i2;
955    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glEvalMesh1");
956 }
957
958
959 static void GLAPIENTRY
960 _save_EvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)
961 {
962    GET_CURRENT_CONTEXT(ctx);
963    (void) mode;
964    (void) i1;
965    (void) i2;
966    (void) j1;
967    (void) j2;
968    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glEvalMesh2");
969 }
970
971
972 static void GLAPIENTRY
973 _save_Begin(GLenum mode)
974 {
975    GET_CURRENT_CONTEXT(ctx);
976    (void) mode;
977    _mesa_compile_error(ctx, GL_INVALID_OPERATION, "Recursive glBegin");
978 }
979
980
981 static void GLAPIENTRY
982 _save_PrimitiveRestartNV(void)
983 {
984    GLenum curPrim;
985    GET_CURRENT_CONTEXT(ctx);
986
987    curPrim = ctx->Driver.CurrentSavePrimitive;
988
989    _save_End();
990    _save_Begin(curPrim);
991 }
992
993
994 /* Unlike the functions above, these are to be hooked into the vtxfmt
995  * maintained in ctx->ListState, active when the list is known or
996  * suspected to be outside any begin/end primitive.
997  */
998 static void GLAPIENTRY
999 _save_OBE_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
1000 {
1001    GET_CURRENT_CONTEXT(ctx);
1002    vbo_save_NotifyBegin(ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK);
1003    CALL_Vertex2f(GET_DISPATCH(), (x1, y1));
1004    CALL_Vertex2f(GET_DISPATCH(), (x2, y1));
1005    CALL_Vertex2f(GET_DISPATCH(), (x2, y2));
1006    CALL_Vertex2f(GET_DISPATCH(), (x1, y2));
1007    CALL_End(GET_DISPATCH(), ());
1008 }
1009
1010
1011 static void GLAPIENTRY
1012 _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
1013 {
1014    GET_CURRENT_CONTEXT(ctx);
1015    GLint i;
1016
1017    if (!_mesa_validate_DrawArrays(ctx, mode, start, count))
1018       return;
1019
1020    _ae_map_vbos(ctx);
1021
1022    vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
1023                               | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1024
1025    for (i = 0; i < count; i++)
1026       CALL_ArrayElement(GET_DISPATCH(), (start + i));
1027    CALL_End(GET_DISPATCH(), ());
1028
1029    _ae_unmap_vbos(ctx);
1030 }
1031
1032
1033 /* Could do better by copying the arrays and element list intact and
1034  * then emitting an indexed prim at runtime.
1035  */
1036 static void GLAPIENTRY
1037 _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1038                        const GLvoid * indices)
1039 {
1040    GET_CURRENT_CONTEXT(ctx);
1041    GLint i;
1042
1043    if (!_mesa_validate_DrawElements(ctx, mode, count, type, indices, 0))
1044       return;
1045
1046    _ae_map_vbos(ctx);
1047
1048    if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj))
1049       indices =
1050          ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices);
1051
1052    vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
1053                               VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1054
1055    switch (type) {
1056    case GL_UNSIGNED_BYTE:
1057       for (i = 0; i < count; i++)
1058          CALL_ArrayElement(GET_DISPATCH(), (((GLubyte *) indices)[i]));
1059       break;
1060    case GL_UNSIGNED_SHORT:
1061       for (i = 0; i < count; i++)
1062          CALL_ArrayElement(GET_DISPATCH(), (((GLushort *) indices)[i]));
1063       break;
1064    case GL_UNSIGNED_INT:
1065       for (i = 0; i < count; i++)
1066          CALL_ArrayElement(GET_DISPATCH(), (((GLuint *) indices)[i]));
1067       break;
1068    default:
1069       _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
1070       break;
1071    }
1072
1073    CALL_End(GET_DISPATCH(), ());
1074
1075    _ae_unmap_vbos(ctx);
1076 }
1077
1078
1079 static void GLAPIENTRY
1080 _save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1081                             GLsizei count, GLenum type,
1082                             const GLvoid * indices)
1083 {
1084    GET_CURRENT_CONTEXT(ctx);
1085    if (_mesa_validate_DrawRangeElements(ctx, mode,
1086                                         start, end, count, type, indices, 0)) {
1087       _save_OBE_DrawElements(mode, count, type, indices);
1088    }
1089 }
1090
1091
1092 static void
1093 _save_vtxfmt_init(struct gl_context *ctx)
1094 {
1095    struct vbo_save_context *save = &vbo_context(ctx)->save;
1096    GLvertexformat *vfmt = &save->vtxfmt;
1097
1098    _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_);
1099
1100    vfmt->Begin = _save_Begin;
1101    vfmt->Color3f = _save_Color3f;
1102    vfmt->Color3fv = _save_Color3fv;
1103    vfmt->Color4f = _save_Color4f;
1104    vfmt->Color4fv = _save_Color4fv;
1105    vfmt->EdgeFlag = _save_EdgeFlag;
1106    vfmt->End = _save_End;
1107    vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV;
1108    vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1109    vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1110    vfmt->Indexf = _save_Indexf;
1111    vfmt->Indexfv = _save_Indexfv;
1112    vfmt->Materialfv = _save_Materialfv;
1113    vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1114    vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1115    vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1116    vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1117    vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1118    vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1119    vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1120    vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1121    vfmt->Normal3f = _save_Normal3f;
1122    vfmt->Normal3fv = _save_Normal3fv;
1123    vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1124    vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1125    vfmt->TexCoord1f = _save_TexCoord1f;
1126    vfmt->TexCoord1fv = _save_TexCoord1fv;
1127    vfmt->TexCoord2f = _save_TexCoord2f;
1128    vfmt->TexCoord2fv = _save_TexCoord2fv;
1129    vfmt->TexCoord3f = _save_TexCoord3f;
1130    vfmt->TexCoord3fv = _save_TexCoord3fv;
1131    vfmt->TexCoord4f = _save_TexCoord4f;
1132    vfmt->TexCoord4fv = _save_TexCoord4fv;
1133    vfmt->Vertex2f = _save_Vertex2f;
1134    vfmt->Vertex2fv = _save_Vertex2fv;
1135    vfmt->Vertex3f = _save_Vertex3f;
1136    vfmt->Vertex3fv = _save_Vertex3fv;
1137    vfmt->Vertex4f = _save_Vertex4f;
1138    vfmt->Vertex4fv = _save_Vertex4fv;
1139    vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1140    vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1141    vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1142    vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1143    vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1144    vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1145    vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1146    vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1147
1148    vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1149    vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1150    vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1151    vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1152    vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1153    vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1154    vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1155    vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1156
1157    /* integer-valued */
1158    vfmt->VertexAttribI1i = _save_VertexAttribI1i;
1159    vfmt->VertexAttribI2i = _save_VertexAttribI2i;
1160    vfmt->VertexAttribI3i = _save_VertexAttribI3i;
1161    vfmt->VertexAttribI4i = _save_VertexAttribI4i;
1162    vfmt->VertexAttribI2iv = _save_VertexAttribI2iv;
1163    vfmt->VertexAttribI3iv = _save_VertexAttribI3iv;
1164    vfmt->VertexAttribI4iv = _save_VertexAttribI4iv;
1165
1166    /* unsigned integer-valued */
1167    vfmt->VertexAttribI1ui = _save_VertexAttribI1ui;
1168    vfmt->VertexAttribI2ui = _save_VertexAttribI2ui;
1169    vfmt->VertexAttribI3ui = _save_VertexAttribI3ui;
1170    vfmt->VertexAttribI4ui = _save_VertexAttribI4ui;
1171    vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv;
1172    vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv;
1173    vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv;
1174
1175    /* This will all require us to fallback to saving the list as opcodes:
1176     */
1177    _MESA_INIT_DLIST_VTXFMT(vfmt, _save_);       /* inside begin/end */
1178
1179    _MESA_INIT_EVAL_VTXFMT(vfmt, _save_);
1180
1181    /* These are all errors as we at least know we are in some sort of
1182     * begin/end pair:
1183     */
1184    vfmt->Begin = _save_Begin;
1185    vfmt->Rectf = _save_Rectf;
1186    vfmt->DrawArrays = _save_DrawArrays;
1187    vfmt->DrawElements = _save_DrawElements;
1188    vfmt->DrawRangeElements = _save_DrawRangeElements;
1189    vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex;
1190    vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex;
1191    /* Loops back into vfmt->DrawElements */
1192    vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements;
1193    vfmt->MultiDrawElementsBaseVertex = _mesa_noop_MultiDrawElementsBaseVertex;
1194 }
1195
1196
1197 void
1198 vbo_save_SaveFlushVertices(struct gl_context *ctx)
1199 {
1200    struct vbo_save_context *save = &vbo_context(ctx)->save;
1201
1202    /* Noop when we are actually active:
1203     */
1204    if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
1205        ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
1206       return;
1207
1208    if (save->vert_count || save->prim_count)
1209       _save_compile_vertex_list(ctx);
1210
1211    _save_copy_to_current(ctx);
1212    _save_reset_vertex(ctx);
1213    _save_reset_counters(ctx);
1214    ctx->Driver.SaveNeedFlush = 0;
1215 }
1216
1217
1218 void
1219 vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
1220 {
1221    struct vbo_save_context *save = &vbo_context(ctx)->save;
1222
1223    (void) list;
1224    (void) mode;
1225
1226    if (!save->prim_store)
1227       save->prim_store = alloc_prim_store(ctx);
1228
1229    if (!save->vertex_store)
1230       save->vertex_store = alloc_vertex_store(ctx);
1231
1232    save->buffer_ptr = map_vertex_store(ctx, save->vertex_store);
1233
1234    _save_reset_vertex(ctx);
1235    _save_reset_counters(ctx);
1236    ctx->Driver.SaveNeedFlush = 0;
1237 }
1238
1239
1240 void
1241 vbo_save_EndList(struct gl_context *ctx)
1242 {
1243    struct vbo_save_context *save = &vbo_context(ctx)->save;
1244
1245    /* EndList called inside a (saved) Begin/End pair?
1246     */
1247    if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) {
1248
1249       if (save->prim_count > 0) {
1250          GLint i = save->prim_count - 1;
1251          ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1252          save->prim[i].end = 0;
1253          save->prim[i].count = (save->vert_count - save->prim[i].start);
1254       }
1255
1256       /* Make sure this vertex list gets replayed by the "loopback"
1257        * mechanism:
1258        */
1259       save->dangling_attr_ref = 1;
1260       vbo_save_SaveFlushVertices(ctx);
1261
1262       /* Swap out this vertex format while outside begin/end.  Any color,
1263        * etc. received between here and the next begin will be compiled
1264        * as opcodes.
1265        */
1266       _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1267    }
1268
1269    unmap_vertex_store(ctx, save->vertex_store);
1270
1271    assert(save->vertex_size == 0);
1272 }
1273
1274
1275 void
1276 vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
1277 {
1278    struct vbo_save_context *save = &vbo_context(ctx)->save;
1279    save->replay_flags |= dlist->Flags;
1280 }
1281
1282
1283 void
1284 vbo_save_EndCallList(struct gl_context *ctx)
1285 {
1286    struct vbo_save_context *save = &vbo_context(ctx)->save;
1287
1288    if (ctx->ListState.CallDepth == 1) {
1289       /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1290        * flag, if it is set:
1291        */
1292       save->replay_flags &= VBO_SAVE_FALLBACK;
1293    }
1294 }
1295
1296
1297 static void
1298 vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
1299 {
1300    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1301    (void) ctx;
1302
1303    if (--node->vertex_store->refcount == 0)
1304       free_vertex_store(ctx, node->vertex_store);
1305
1306    if (--node->prim_store->refcount == 0)
1307       FREE(node->prim_store);
1308
1309    if (node->current_data) {
1310       FREE(node->current_data);
1311       node->current_data = NULL;
1312    }
1313 }
1314
1315
1316 static void
1317 vbo_print_vertex_list(struct gl_context *ctx, void *data)
1318 {
1319    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1320    GLuint i;
1321    (void) ctx;
1322
1323    printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1324           node->count, node->prim_count, node->vertex_size);
1325
1326    for (i = 0; i < node->prim_count; i++) {
1327       struct _mesa_prim *prim = &node->prim[i];
1328       _mesa_debug(NULL, "   prim %d: %s%s %d..%d %s %s\n",
1329                   i,
1330                   _mesa_lookup_prim_by_nr(prim->mode),
1331                   prim->weak ? " (weak)" : "",
1332                   prim->start,
1333                   prim->start + prim->count,
1334                   (prim->begin) ? "BEGIN" : "(wrap)",
1335                   (prim->end) ? "END" : "(wrap)");
1336    }
1337 }
1338
1339
1340 static void
1341 _save_current_init(struct gl_context *ctx)
1342 {
1343    struct vbo_save_context *save = &vbo_context(ctx)->save;
1344    GLint i;
1345
1346    for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1347       const GLuint j = i - VBO_ATTRIB_POS;
1348       ASSERT(j < VERT_ATTRIB_MAX);
1349       save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1350       save->current[i] = ctx->ListState.CurrentAttrib[j];
1351    }
1352
1353    for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1354       const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1355       ASSERT(j < MAT_ATTRIB_MAX);
1356       save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1357       save->current[i] = ctx->ListState.CurrentMaterial[j];
1358    }
1359 }
1360
1361
1362 /**
1363  * Initialize the display list compiler
1364  */
1365 void
1366 vbo_save_api_init(struct vbo_save_context *save)
1367 {
1368    struct gl_context *ctx = save->ctx;
1369    GLuint i;
1370
1371    save->opcode_vertex_list =
1372       _mesa_dlist_alloc_opcode(ctx,
1373                                sizeof(struct vbo_save_vertex_list),
1374                                vbo_save_playback_vertex_list,
1375                                vbo_destroy_vertex_list,
1376                                vbo_print_vertex_list);
1377
1378    ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1379
1380    _save_vtxfmt_init(ctx);
1381    _save_current_init(ctx);
1382
1383    /* These will actually get set again when binding/drawing */
1384    for (i = 0; i < VBO_ATTRIB_MAX; i++)
1385       save->inputs[i] = &save->arrays[i];
1386
1387    /* Hook our array functions into the outside-begin-end vtxfmt in 
1388     * ctx->ListState.
1389     */
1390    ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1391    ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1392    ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1393    ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1394    /* loops back into _save_OBE_DrawElements */
1395    ctx->ListState.ListVtxfmt.MultiDrawElementsEXT =
1396       _mesa_noop_MultiDrawElements;
1397    ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex =
1398       _mesa_noop_MultiDrawElementsBaseVertex;
1399    _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1400 }
1401
1402
1403 #endif /* FEATURE_dlist */