vbo_save_api.c revision 1a93e7690dc90211164082d6a2d26d93da8127ef
1/**************************************************************************
2
3Copyright 2002-2008 VMware, Inc.
4
5All Rights Reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a
8copy of this software and associated documentation files (the "Software"),
9to deal in the Software without restriction, including without limitation
10on the rights to use, copy, modify, merge, publish, distribute, sub
11license, and/or sell copies of the Software, and to permit persons to whom
12the Software is furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice (including the next
15paragraph) shall be included in all copies or substantial portions of the
16Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21VMWARE AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26**************************************************************************/
27
28/*
29 * Authors:
30 *   Keith Whitwell <keithw@vmware.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/api_validate.h"
78#include "main/api_arrayelt.h"
79#include "main/vtxfmt.h"
80#include "main/dispatch.h"
81
82#include "vbo_context.h"
83#include "vbo_noop.h"
84
85
86#ifdef ERROR
87#undef ERROR
88#endif
89
90
91/* An interesting VBO number/name to help with debugging */
92#define VBO_BUF_ID  12345
93
94
95/*
96 * NOTE: Old 'parity' issue is gone, but copying can still be
97 * wrong-footed on replay.
98 */
99static GLuint
100_save_copy_vertices(struct gl_context *ctx,
101                    const struct vbo_save_vertex_list *node,
102                    const fi_type * src_buffer)
103{
104   struct vbo_save_context *save = &vbo_context(ctx)->save;
105   const struct _mesa_prim *prim = &node->prim[node->prim_count - 1];
106   GLuint nr = prim->count;
107   GLuint sz = save->vertex_size;
108   const fi_type *src = src_buffer + prim->start * sz;
109   fi_type *dst = save->copied.buffer;
110   GLuint ovf, i;
111
112   if (prim->end)
113      return 0;
114
115   switch (prim->mode) {
116   case GL_POINTS:
117      return 0;
118   case GL_LINES:
119      ovf = nr & 1;
120      for (i = 0; i < ovf; i++)
121         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
122                sz * sizeof(GLfloat));
123      return i;
124   case GL_TRIANGLES:
125      ovf = nr % 3;
126      for (i = 0; i < ovf; i++)
127         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
128                sz * sizeof(GLfloat));
129      return i;
130   case GL_QUADS:
131      ovf = nr & 3;
132      for (i = 0; i < ovf; i++)
133         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
134                sz * sizeof(GLfloat));
135      return i;
136   case GL_LINE_STRIP:
137      if (nr == 0)
138         return 0;
139      else {
140         memcpy(dst, src + (nr - 1) * sz, sz * sizeof(GLfloat));
141         return 1;
142      }
143   case GL_LINE_LOOP:
144   case GL_TRIANGLE_FAN:
145   case GL_POLYGON:
146      if (nr == 0)
147         return 0;
148      else if (nr == 1) {
149         memcpy(dst, src + 0, sz * sizeof(GLfloat));
150         return 1;
151      }
152      else {
153         memcpy(dst, src + 0, sz * sizeof(GLfloat));
154         memcpy(dst + sz, src + (nr - 1) * sz, sz * sizeof(GLfloat));
155         return 2;
156      }
157   case GL_TRIANGLE_STRIP:
158   case GL_QUAD_STRIP:
159      switch (nr) {
160      case 0:
161         ovf = 0;
162         break;
163      case 1:
164         ovf = 1;
165         break;
166      default:
167         ovf = 2 + (nr & 1);
168         break;
169      }
170      for (i = 0; i < ovf; i++)
171         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
172                sz * sizeof(GLfloat));
173      return i;
174   default:
175      assert(0);
176      return 0;
177   }
178}
179
180
181static struct vbo_save_vertex_store *
182alloc_vertex_store(struct gl_context *ctx)
183{
184   struct vbo_save_context *save = &vbo_context(ctx)->save;
185   struct vbo_save_vertex_store *vertex_store =
186      CALLOC_STRUCT(vbo_save_vertex_store);
187
188   /* obj->Name needs to be non-zero, but won't ever be examined more
189    * closely than that.  In particular these buffers won't be entered
190    * into the hash and can never be confused with ones visible to the
191    * user.  Perhaps there could be a special number for internal
192    * buffers:
193    */
194   vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx, VBO_BUF_ID);
195   if (vertex_store->bufferobj) {
196      save->out_of_memory =
197         !ctx->Driver.BufferData(ctx,
198                                 GL_ARRAY_BUFFER_ARB,
199                                 VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
200                                 NULL, GL_STATIC_DRAW_ARB,
201                                 GL_MAP_WRITE_BIT |
202                                 GL_DYNAMIC_STORAGE_BIT,
203                                 vertex_store->bufferobj);
204   }
205   else {
206      save->out_of_memory = GL_TRUE;
207   }
208
209   if (save->out_of_memory) {
210      _mesa_error(ctx, GL_OUT_OF_MEMORY, "internal VBO allocation");
211      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
212   }
213
214   vertex_store->buffer = NULL;
215   vertex_store->used = 0;
216   vertex_store->refcount = 1;
217
218   return vertex_store;
219}
220
221
222static void
223free_vertex_store(struct gl_context *ctx,
224                  struct vbo_save_vertex_store *vertex_store)
225{
226   assert(!vertex_store->buffer);
227
228   if (vertex_store->bufferobj) {
229      _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
230   }
231
232   free(vertex_store);
233}
234
235
236fi_type *
237vbo_save_map_vertex_store(struct gl_context *ctx,
238                          struct vbo_save_vertex_store *vertex_store)
239{
240   const GLbitfield access = (GL_MAP_WRITE_BIT |
241                              GL_MAP_INVALIDATE_RANGE_BIT |
242                              GL_MAP_UNSYNCHRONIZED_BIT |
243                              GL_MAP_FLUSH_EXPLICIT_BIT);
244
245   assert(vertex_store->bufferobj);
246   assert(!vertex_store->buffer);  /* the buffer should not be mapped */
247
248   if (vertex_store->bufferobj->Size > 0) {
249      /* Map the remaining free space in the VBO */
250      GLintptr offset = vertex_store->used * sizeof(GLfloat);
251      GLsizeiptr size = vertex_store->bufferobj->Size - offset;
252      fi_type *range = (fi_type *)
253         ctx->Driver.MapBufferRange(ctx, offset, size, access,
254                                    vertex_store->bufferobj,
255                                    MAP_INTERNAL);
256      if (range) {
257         /* compute address of start of whole buffer (needed elsewhere) */
258         vertex_store->buffer = range - vertex_store->used;
259         assert(vertex_store->buffer);
260         return range;
261      }
262      else {
263         vertex_store->buffer = NULL;
264         return NULL;
265      }
266   }
267   else {
268      /* probably ran out of memory for buffers */
269      return NULL;
270   }
271}
272
273
274void
275vbo_save_unmap_vertex_store(struct gl_context *ctx,
276                            struct vbo_save_vertex_store *vertex_store)
277{
278   if (vertex_store->bufferobj->Size > 0) {
279      GLintptr offset = 0;
280      GLsizeiptr length = vertex_store->used * sizeof(GLfloat)
281         - vertex_store->bufferobj->Mappings[MAP_INTERNAL].Offset;
282
283      /* Explicitly flush the region we wrote to */
284      ctx->Driver.FlushMappedBufferRange(ctx, offset, length,
285                                         vertex_store->bufferobj,
286                                         MAP_INTERNAL);
287
288      ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj, MAP_INTERNAL);
289   }
290   vertex_store->buffer = NULL;
291}
292
293
294static struct vbo_save_primitive_store *
295alloc_prim_store(struct gl_context *ctx)
296{
297   struct vbo_save_primitive_store *store =
298      CALLOC_STRUCT(vbo_save_primitive_store);
299   (void) ctx;
300   store->used = 0;
301   store->refcount = 1;
302   return store;
303}
304
305
306static void
307_save_reset_counters(struct gl_context *ctx)
308{
309   struct vbo_save_context *save = &vbo_context(ctx)->save;
310
311   save->prim = save->prim_store->buffer + save->prim_store->used;
312   save->buffer = save->vertex_store->buffer + save->vertex_store->used;
313
314   assert(save->buffer == save->buffer_ptr);
315
316   if (save->vertex_size)
317      save->max_vert = (VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
318                        save->vertex_size;
319   else
320      save->max_vert = 0;
321
322   save->vert_count = 0;
323   save->prim_count = 0;
324   save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
325   save->dangling_attr_ref = GL_FALSE;
326}
327
328/**
329 * For a list of prims, try merging prims that can just be extensions of the
330 * previous prim.
331 */
332static void
333merge_prims(struct gl_context *ctx,
334            struct _mesa_prim *prim_list,
335            GLuint *prim_count)
336{
337   GLuint i;
338   struct _mesa_prim *prev_prim = prim_list;
339
340   for (i = 1; i < *prim_count; i++) {
341      struct _mesa_prim *this_prim = prim_list + i;
342
343      vbo_try_prim_conversion(this_prim);
344
345      if (vbo_can_merge_prims(prev_prim, this_prim)) {
346         /* We've found a prim that just extend the previous one.  Tack it
347          * onto the previous one, and let this primitive struct get dropped.
348          */
349         vbo_merge_prims(prev_prim, this_prim);
350         continue;
351      }
352
353      /* If any previous primitives have been dropped, then we need to copy
354       * this later one into the next available slot.
355       */
356      prev_prim++;
357      if (prev_prim != this_prim)
358         *prev_prim = *this_prim;
359   }
360
361   *prim_count = prev_prim - prim_list + 1;
362}
363
364/**
365 * Insert the active immediate struct onto the display list currently
366 * being built.
367 */
368static void
369_save_compile_vertex_list(struct gl_context *ctx)
370{
371   struct vbo_save_context *save = &vbo_context(ctx)->save;
372   struct vbo_save_vertex_list *node;
373
374   /* Allocate space for this structure in the display list currently
375    * being compiled.
376    */
377   node = (struct vbo_save_vertex_list *)
378      _mesa_dlist_alloc_aligned(ctx, save->opcode_vertex_list, sizeof(*node));
379
380   if (!node)
381      return;
382
383   /* Make sure the pointer is aligned to the size of a pointer */
384   assert((GLintptr) node % sizeof(void *) == 0);
385
386   /* Duplicate our template, increment refcounts to the storage structs:
387    */
388   memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
389   memcpy(node->attrtype, save->attrtype, sizeof(node->attrtype));
390   node->vertex_size = save->vertex_size;
391   node->buffer_offset =
392      (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat);
393   node->count = save->vert_count;
394   node->wrap_count = save->copied.nr;
395   node->dangling_attr_ref = save->dangling_attr_ref;
396   node->prim = save->prim;
397   node->prim_count = save->prim_count;
398   node->vertex_store = save->vertex_store;
399   node->prim_store = save->prim_store;
400
401   node->vertex_store->refcount++;
402   node->prim_store->refcount++;
403
404   if (node->prim[0].no_current_update) {
405      node->current_size = 0;
406      node->current_data = NULL;
407   }
408   else {
409      node->current_size = node->vertex_size - node->attrsz[0];
410      node->current_data = NULL;
411
412      if (node->current_size) {
413         /* If the malloc fails, we just pull the data out of the VBO
414          * later instead.
415          */
416         node->current_data = malloc(node->current_size * sizeof(GLfloat));
417         if (node->current_data) {
418            const char *buffer = (const char *) save->vertex_store->buffer;
419            unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
420            unsigned vertex_offset = 0;
421
422            if (node->count)
423               vertex_offset =
424                  (node->count - 1) * node->vertex_size * sizeof(GLfloat);
425
426            memcpy(node->current_data,
427                   buffer + node->buffer_offset + vertex_offset + attr_offset,
428                   node->current_size * sizeof(GLfloat));
429         }
430      }
431   }
432
433   assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0);
434
435   if (save->dangling_attr_ref)
436      ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
437
438   save->vertex_store->used += save->vertex_size * node->count;
439   save->prim_store->used += node->prim_count;
440
441   /* Copy duplicated vertices
442    */
443   save->copied.nr = _save_copy_vertices(ctx, node, save->buffer);
444
445   merge_prims(ctx, node->prim, &node->prim_count);
446
447   /* Deal with GL_COMPILE_AND_EXECUTE:
448    */
449   if (ctx->ExecuteFlag) {
450      struct _glapi_table *dispatch = GET_DISPATCH();
451
452      _glapi_set_dispatch(ctx->Exec);
453
454      vbo_loopback_vertex_list(ctx,
455                               (const GLfloat *) ((const char *) save->
456                                                  vertex_store->buffer +
457                                                  node->buffer_offset),
458                               node->attrsz, node->prim, node->prim_count,
459                               node->wrap_count, node->vertex_size);
460
461      _glapi_set_dispatch(dispatch);
462   }
463
464   /* Decide whether the storage structs are full, or can be used for
465    * the next vertex lists as well.
466    */
467   if (save->vertex_store->used >
468       VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) {
469
470      /* Unmap old store:
471       */
472      vbo_save_unmap_vertex_store(ctx, save->vertex_store);
473
474      /* Release old reference:
475       */
476      save->vertex_store->refcount--;
477      assert(save->vertex_store->refcount != 0);
478      save->vertex_store = NULL;
479
480      /* Allocate and map new store:
481       */
482      save->vertex_store = alloc_vertex_store(ctx);
483      save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
484      save->out_of_memory = save->buffer_ptr == NULL;
485   }
486
487   if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
488      save->prim_store->refcount--;
489      assert(save->prim_store->refcount != 0);
490      save->prim_store = alloc_prim_store(ctx);
491   }
492
493   /* Reset our structures for the next run of vertices:
494    */
495   _save_reset_counters(ctx);
496}
497
498
499/**
500 * This is called when we fill a vertex buffer before we hit a glEnd().
501 * We
502 * TODO -- If no new vertices have been stored, don't bother saving it.
503 */
504static void
505_save_wrap_buffers(struct gl_context *ctx)
506{
507   struct vbo_save_context *save = &vbo_context(ctx)->save;
508   GLint i = save->prim_count - 1;
509   GLenum mode;
510   GLboolean weak;
511   GLboolean no_current_update;
512
513   assert(i < (GLint) save->prim_max);
514   assert(i >= 0);
515
516   /* Close off in-progress primitive.
517    */
518   save->prim[i].count = (save->vert_count - save->prim[i].start);
519   mode = save->prim[i].mode;
520   weak = save->prim[i].weak;
521   no_current_update = save->prim[i].no_current_update;
522
523   /* store the copied vertices, and allocate a new list.
524    */
525   _save_compile_vertex_list(ctx);
526
527   /* Restart interrupted primitive
528    */
529   save->prim[0].mode = mode;
530   save->prim[0].weak = weak;
531   save->prim[0].no_current_update = no_current_update;
532   save->prim[0].begin = 0;
533   save->prim[0].end = 0;
534   save->prim[0].pad = 0;
535   save->prim[0].start = 0;
536   save->prim[0].count = 0;
537   save->prim[0].num_instances = 1;
538   save->prim[0].base_instance = 0;
539   save->prim[0].is_indirect = 0;
540   save->prim_count = 1;
541}
542
543
544/**
545 * Called only when buffers are wrapped as the result of filling the
546 * vertex_store struct.
547 */
548static void
549_save_wrap_filled_vertex(struct gl_context *ctx)
550{
551   struct vbo_save_context *save = &vbo_context(ctx)->save;
552   fi_type *data = save->copied.buffer;
553   GLuint i;
554
555   /* Emit a glEnd to close off the last vertex list.
556    */
557   _save_wrap_buffers(ctx);
558
559   /* Copy stored stored vertices to start of new list.
560    */
561   assert(save->max_vert - save->vert_count > save->copied.nr);
562
563   for (i = 0; i < save->copied.nr; i++) {
564      memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat));
565      data += save->vertex_size;
566      save->buffer_ptr += save->vertex_size;
567      save->vert_count++;
568   }
569}
570
571
572static void
573_save_copy_to_current(struct gl_context *ctx)
574{
575   struct vbo_save_context *save = &vbo_context(ctx)->save;
576   GLuint i;
577
578   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
579      if (save->attrsz[i]) {
580         save->currentsz[i][0] = save->attrsz[i];
581         COPY_CLEAN_4V_TYPE_AS_UNION(save->current[i], save->attrsz[i],
582                                     save->attrptr[i], save->attrtype[i]);
583      }
584   }
585}
586
587
588static void
589_save_copy_from_current(struct gl_context *ctx)
590{
591   struct vbo_save_context *save = &vbo_context(ctx)->save;
592   GLint i;
593
594   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
595      switch (save->attrsz[i]) {
596      case 4:
597         save->attrptr[i][3] = save->current[i][3];
598      case 3:
599         save->attrptr[i][2] = save->current[i][2];
600      case 2:
601         save->attrptr[i][1] = save->current[i][1];
602      case 1:
603         save->attrptr[i][0] = save->current[i][0];
604      case 0:
605         break;
606      }
607   }
608}
609
610
611/**
612 * Called when we increase the size of a vertex attribute.  For example,
613 * if we've seen one or more glTexCoord2f() calls and now we get a
614 * glTexCoord3f() call.
615 * Flush existing data, set new attrib size, replay copied vertices.
616 */
617static void
618_save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
619{
620   struct vbo_save_context *save = &vbo_context(ctx)->save;
621   GLuint oldsz;
622   GLuint i;
623   fi_type *tmp;
624
625   /* Store the current run of vertices, and emit a GL_END.  Emit a
626    * BEGIN in the new buffer.
627    */
628   if (save->vert_count)
629      _save_wrap_buffers(ctx);
630   else
631      assert(save->copied.nr == 0);
632
633   /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
634    * when the attribute already exists in the vertex and is having
635    * its size increased.
636    */
637   _save_copy_to_current(ctx);
638
639   /* Fix up sizes:
640    */
641   oldsz = save->attrsz[attr];
642   save->attrsz[attr] = newsz;
643
644   save->vertex_size += newsz - oldsz;
645   save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
646                     save->vertex_size);
647   save->vert_count = 0;
648
649   /* Recalculate all the attrptr[] values:
650    */
651   for (i = 0, tmp = save->vertex; i < VBO_ATTRIB_MAX; i++) {
652      if (save->attrsz[i]) {
653         save->attrptr[i] = tmp;
654         tmp += save->attrsz[i];
655      }
656      else {
657         save->attrptr[i] = NULL;       /* will not be dereferenced. */
658      }
659   }
660
661   /* Copy from current to repopulate the vertex with correct values.
662    */
663   _save_copy_from_current(ctx);
664
665   /* Replay stored vertices to translate them to new format here.
666    *
667    * If there are copied vertices and the new (upgraded) attribute
668    * has not been defined before, this list is somewhat degenerate,
669    * and will need fixup at runtime.
670    */
671   if (save->copied.nr) {
672      const fi_type *data = save->copied.buffer;
673      fi_type *dest = save->buffer;
674      GLuint j;
675
676      /* Need to note this and fix up at runtime (or loopback):
677       */
678      if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
679         assert(oldsz == 0);
680         save->dangling_attr_ref = GL_TRUE;
681      }
682
683      for (i = 0; i < save->copied.nr; i++) {
684         for (j = 0; j < VBO_ATTRIB_MAX; j++) {
685            if (save->attrsz[j]) {
686               if (j == attr) {
687                  if (oldsz) {
688                     COPY_CLEAN_4V_TYPE_AS_UNION(dest, oldsz, data,
689                                                 save->attrtype[j]);
690                     data += oldsz;
691                     dest += newsz;
692                  }
693                  else {
694                     COPY_SZ_4V(dest, newsz, save->current[attr]);
695                     dest += newsz;
696                  }
697               }
698               else {
699                  GLint sz = save->attrsz[j];
700                  COPY_SZ_4V(dest, sz, data);
701                  data += sz;
702                  dest += sz;
703               }
704            }
705         }
706      }
707
708      save->buffer_ptr = dest;
709      save->vert_count += save->copied.nr;
710   }
711}
712
713
714/**
715 * This is called when the size of a vertex attribute changes.
716 * For example, after seeing one or more glTexCoord2f() calls we
717 * get a glTexCoord4f() or glTexCoord1f() call.
718 */
719static void
720save_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint sz)
721{
722   struct vbo_save_context *save = &vbo_context(ctx)->save;
723
724   if (sz > save->attrsz[attr]) {
725      /* New size is larger.  Need to flush existing vertices and get
726       * an enlarged vertex format.
727       */
728      _save_upgrade_vertex(ctx, attr, sz);
729   }
730   else if (sz < save->active_sz[attr]) {
731      GLuint i;
732      const fi_type *id = vbo_get_default_vals_as_union(save->attrtype[attr]);
733
734      /* New size is equal or smaller - just need to fill in some
735       * zeros.
736       */
737      for (i = sz; i <= save->attrsz[attr]; i++)
738         save->attrptr[attr][i - 1] = id[i - 1];
739   }
740
741   save->active_sz[attr] = sz;
742}
743
744
745/**
746 * Reset the current size of all vertex attributes to the default
747 * value of 0.  This signals that we haven't yet seen any per-vertex
748 * commands such as glNormal3f() or glTexCoord2f().
749 */
750static void
751_save_reset_vertex(struct gl_context *ctx)
752{
753   struct vbo_save_context *save = &vbo_context(ctx)->save;
754   GLuint i;
755
756   for (i = 0; i < VBO_ATTRIB_MAX; i++) {
757      save->attrsz[i] = 0;
758      save->active_sz[i] = 0;
759   }
760
761   save->vertex_size = 0;
762}
763
764
765
766#define ERROR(err)   _mesa_compile_error(ctx, err, __FUNCTION__);
767
768
769/* Only one size for each attribute may be active at once.  Eg. if
770 * Color3f is installed/active, then Color4f may not be, even if the
771 * vertex actually contains 4 color coordinates.  This is because the
772 * 3f version won't otherwise set color[3] to 1.0 -- this is the job
773 * of the chooser function when switching between Color4f and Color3f.
774 */
775#define ATTR_UNION(A, N, T, V0, V1, V2, V3)			\
776do {								\
777   struct vbo_save_context *save = &vbo_context(ctx)->save;	\
778								\
779   if (save->active_sz[A] != N)					\
780      save_fixup_vertex(ctx, A, N);				\
781								\
782   {								\
783      fi_type *dest = save->attrptr[A];		\
784      if (N>0) dest[0] = V0;					\
785      if (N>1) dest[1] = V1;					\
786      if (N>2) dest[2] = V2;					\
787      if (N>3) dest[3] = V3;					\
788      save->attrtype[A] = T;					\
789   }								\
790								\
791   if ((A) == 0) {						\
792      GLuint i;							\
793								\
794      for (i = 0; i < save->vertex_size; i++)			\
795	 save->buffer_ptr[i] = save->vertex[i];			\
796								\
797      save->buffer_ptr += save->vertex_size;			\
798								\
799      if (++save->vert_count >= save->max_vert)			\
800	 _save_wrap_filled_vertex(ctx);				\
801   }								\
802} while (0)
803
804#define TAG(x) _save_##x
805
806#include "vbo_attrib_tmp.h"
807
808
809
810#define MAT( ATTR, N, face, params )			\
811do {							\
812   if (face != GL_BACK)					\
813      MAT_ATTR( ATTR, N, params ); /* front */		\
814   if (face != GL_FRONT)				\
815      MAT_ATTR( ATTR + 1, N, params ); /* back */	\
816} while (0)
817
818
819/**
820 * Save a glMaterial call found between glBegin/End.
821 * glMaterial calls outside Begin/End are handled in dlist.c.
822 */
823static void GLAPIENTRY
824_save_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
825{
826   GET_CURRENT_CONTEXT(ctx);
827
828   if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) {
829      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(face)");
830      return;
831   }
832
833   switch (pname) {
834   case GL_EMISSION:
835      MAT(VBO_ATTRIB_MAT_FRONT_EMISSION, 4, face, params);
836      break;
837   case GL_AMBIENT:
838      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
839      break;
840   case GL_DIFFUSE:
841      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
842      break;
843   case GL_SPECULAR:
844      MAT(VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params);
845      break;
846   case GL_SHININESS:
847      if (*params < 0 || *params > ctx->Const.MaxShininess) {
848         _mesa_compile_error(ctx, GL_INVALID_VALUE, "glMaterial(shininess)");
849      }
850      else {
851         MAT(VBO_ATTRIB_MAT_FRONT_SHININESS, 1, face, params);
852      }
853      break;
854   case GL_COLOR_INDEXES:
855      MAT(VBO_ATTRIB_MAT_FRONT_INDEXES, 3, face, params);
856      break;
857   case GL_AMBIENT_AND_DIFFUSE:
858      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
859      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
860      break;
861   default:
862      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(pname)");
863      return;
864   }
865}
866
867
868/* Cope with EvalCoord/CallList called within a begin/end object:
869 *     -- Flush current buffer
870 *     -- Fallback to opcodes for the rest of the begin/end object.
871 */
872static void
873dlist_fallback(struct gl_context *ctx)
874{
875   struct vbo_save_context *save = &vbo_context(ctx)->save;
876
877   if (save->vert_count || save->prim_count) {
878      if (save->prim_count > 0) {
879         /* Close off in-progress primitive. */
880         GLint i = save->prim_count - 1;
881         save->prim[i].count = save->vert_count - save->prim[i].start;
882      }
883
884      /* Need to replay this display list with loopback,
885       * unfortunately, otherwise this primitive won't be handled
886       * properly:
887       */
888      save->dangling_attr_ref = GL_TRUE;
889
890      _save_compile_vertex_list(ctx);
891   }
892
893   _save_copy_to_current(ctx);
894   _save_reset_vertex(ctx);
895   _save_reset_counters(ctx);
896   if (save->out_of_memory) {
897      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
898   }
899   else {
900      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
901   }
902   ctx->Driver.SaveNeedFlush = GL_FALSE;
903}
904
905
906static void GLAPIENTRY
907_save_EvalCoord1f(GLfloat u)
908{
909   GET_CURRENT_CONTEXT(ctx);
910   dlist_fallback(ctx);
911   CALL_EvalCoord1f(ctx->Save, (u));
912}
913
914static void GLAPIENTRY
915_save_EvalCoord1fv(const GLfloat * v)
916{
917   GET_CURRENT_CONTEXT(ctx);
918   dlist_fallback(ctx);
919   CALL_EvalCoord1fv(ctx->Save, (v));
920}
921
922static void GLAPIENTRY
923_save_EvalCoord2f(GLfloat u, GLfloat v)
924{
925   GET_CURRENT_CONTEXT(ctx);
926   dlist_fallback(ctx);
927   CALL_EvalCoord2f(ctx->Save, (u, v));
928}
929
930static void GLAPIENTRY
931_save_EvalCoord2fv(const GLfloat * v)
932{
933   GET_CURRENT_CONTEXT(ctx);
934   dlist_fallback(ctx);
935   CALL_EvalCoord2fv(ctx->Save, (v));
936}
937
938static void GLAPIENTRY
939_save_EvalPoint1(GLint i)
940{
941   GET_CURRENT_CONTEXT(ctx);
942   dlist_fallback(ctx);
943   CALL_EvalPoint1(ctx->Save, (i));
944}
945
946static void GLAPIENTRY
947_save_EvalPoint2(GLint i, GLint j)
948{
949   GET_CURRENT_CONTEXT(ctx);
950   dlist_fallback(ctx);
951   CALL_EvalPoint2(ctx->Save, (i, j));
952}
953
954static void GLAPIENTRY
955_save_CallList(GLuint l)
956{
957   GET_CURRENT_CONTEXT(ctx);
958   dlist_fallback(ctx);
959   CALL_CallList(ctx->Save, (l));
960}
961
962static void GLAPIENTRY
963_save_CallLists(GLsizei n, GLenum type, const GLvoid * v)
964{
965   GET_CURRENT_CONTEXT(ctx);
966   dlist_fallback(ctx);
967   CALL_CallLists(ctx->Save, (n, type, v));
968}
969
970
971
972/**
973 * Called via ctx->Driver.NotifySaveBegin() when a glBegin is getting
974 * compiled into a display list.
975 * Updating of ctx->Driver.CurrentSavePrimitive is already taken care of.
976 */
977GLboolean
978vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
979{
980   struct vbo_save_context *save = &vbo_context(ctx)->save;
981   const GLuint i = save->prim_count++;
982
983   assert(i < save->prim_max);
984   save->prim[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
985   save->prim[i].begin = 1;
986   save->prim[i].end = 0;
987   save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
988   save->prim[i].no_current_update =
989      (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
990   save->prim[i].pad = 0;
991   save->prim[i].start = save->vert_count;
992   save->prim[i].count = 0;
993   save->prim[i].num_instances = 1;
994   save->prim[i].base_instance = 0;
995   save->prim[i].is_indirect = 0;
996
997   if (save->out_of_memory) {
998      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
999   }
1000   else {
1001      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
1002   }
1003
1004   /* We need to call SaveFlushVertices() if there's state change */
1005   ctx->Driver.SaveNeedFlush = GL_TRUE;
1006
1007   /* GL_TRUE means we've handled this glBegin here; don't compile a BEGIN
1008    * opcode into the display list.
1009    */
1010   return GL_TRUE;
1011}
1012
1013
1014static void GLAPIENTRY
1015_save_End(void)
1016{
1017   GET_CURRENT_CONTEXT(ctx);
1018   struct vbo_save_context *save = &vbo_context(ctx)->save;
1019   const GLint i = save->prim_count - 1;
1020
1021   ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1022   save->prim[i].end = 1;
1023   save->prim[i].count = (save->vert_count - save->prim[i].start);
1024
1025   if (i == (GLint) save->prim_max - 1) {
1026      _save_compile_vertex_list(ctx);
1027      assert(save->copied.nr == 0);
1028   }
1029
1030   /* Swap out this vertex format while outside begin/end.  Any color,
1031    * etc. received between here and the next begin will be compiled
1032    * as opcodes.
1033    */
1034   if (save->out_of_memory) {
1035      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
1036   }
1037   else {
1038      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1039   }
1040}
1041
1042
1043static void GLAPIENTRY
1044_save_Begin(GLenum mode)
1045{
1046   GET_CURRENT_CONTEXT(ctx);
1047   (void) mode;
1048   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "Recursive glBegin");
1049}
1050
1051
1052static void GLAPIENTRY
1053_save_PrimitiveRestartNV(void)
1054{
1055   GLenum curPrim;
1056   GET_CURRENT_CONTEXT(ctx);
1057
1058   curPrim = ctx->Driver.CurrentSavePrimitive;
1059
1060   _save_End();
1061   _save_Begin(curPrim);
1062}
1063
1064
1065/* Unlike the functions above, these are to be hooked into the vtxfmt
1066 * maintained in ctx->ListState, active when the list is known or
1067 * suspected to be outside any begin/end primitive.
1068 * Note: OBE = Outside Begin/End
1069 */
1070static void GLAPIENTRY
1071_save_OBE_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
1072{
1073   GET_CURRENT_CONTEXT(ctx);
1074   vbo_save_NotifyBegin(ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK);
1075   CALL_Vertex2f(GET_DISPATCH(), (x1, y1));
1076   CALL_Vertex2f(GET_DISPATCH(), (x2, y1));
1077   CALL_Vertex2f(GET_DISPATCH(), (x2, y2));
1078   CALL_Vertex2f(GET_DISPATCH(), (x1, y2));
1079   CALL_End(GET_DISPATCH(), ());
1080}
1081
1082
1083static void GLAPIENTRY
1084_save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
1085{
1086   GET_CURRENT_CONTEXT(ctx);
1087   struct vbo_save_context *save = &vbo_context(ctx)->save;
1088   GLint i;
1089
1090   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1091      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)");
1092      return;
1093   }
1094   if (count < 0) {
1095      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count<0)");
1096      return;
1097   }
1098
1099   if (save->out_of_memory)
1100      return;
1101
1102   _ae_map_vbos(ctx);
1103
1104   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
1105                              | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1106
1107   for (i = 0; i < count; i++)
1108      CALL_ArrayElement(GET_DISPATCH(), (start + i));
1109   CALL_End(GET_DISPATCH(), ());
1110
1111   _ae_unmap_vbos(ctx);
1112}
1113
1114
1115/* Could do better by copying the arrays and element list intact and
1116 * then emitting an indexed prim at runtime.
1117 */
1118static void GLAPIENTRY
1119_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1120                       const GLvoid * indices)
1121{
1122   GET_CURRENT_CONTEXT(ctx);
1123   struct vbo_save_context *save = &vbo_context(ctx)->save;
1124   struct gl_buffer_object *indexbuf = ctx->Array.VAO->IndexBufferObj;
1125   GLint i;
1126
1127   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1128      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawElements(mode)");
1129      return;
1130   }
1131   if (count < 0) {
1132      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawElements(count<0)");
1133      return;
1134   }
1135   if (type != GL_UNSIGNED_BYTE &&
1136       type != GL_UNSIGNED_SHORT &&
1137       type != GL_UNSIGNED_INT) {
1138      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawElements(count<0)");
1139      return;
1140   }
1141
1142   if (save->out_of_memory)
1143      return;
1144
1145   _ae_map_vbos(ctx);
1146
1147   if (_mesa_is_bufferobj(indexbuf))
1148      indices =
1149         ADD_POINTERS(indexbuf->Mappings[MAP_INTERNAL].Pointer, indices);
1150
1151   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
1152                              VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1153
1154   switch (type) {
1155   case GL_UNSIGNED_BYTE:
1156      for (i = 0; i < count; i++)
1157         CALL_ArrayElement(GET_DISPATCH(), (((GLubyte *) indices)[i]));
1158      break;
1159   case GL_UNSIGNED_SHORT:
1160      for (i = 0; i < count; i++)
1161         CALL_ArrayElement(GET_DISPATCH(), (((GLushort *) indices)[i]));
1162      break;
1163   case GL_UNSIGNED_INT:
1164      for (i = 0; i < count; i++)
1165         CALL_ArrayElement(GET_DISPATCH(), (((GLuint *) indices)[i]));
1166      break;
1167   default:
1168      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
1169      break;
1170   }
1171
1172   CALL_End(GET_DISPATCH(), ());
1173
1174   _ae_unmap_vbos(ctx);
1175}
1176
1177
1178static void GLAPIENTRY
1179_save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1180                            GLsizei count, GLenum type,
1181                            const GLvoid * indices)
1182{
1183   GET_CURRENT_CONTEXT(ctx);
1184   struct vbo_save_context *save = &vbo_context(ctx)->save;
1185
1186   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1187      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(mode)");
1188      return;
1189   }
1190   if (count < 0) {
1191      _mesa_compile_error(ctx, GL_INVALID_VALUE,
1192                          "glDrawRangeElements(count<0)");
1193      return;
1194   }
1195   if (type != GL_UNSIGNED_BYTE &&
1196       type != GL_UNSIGNED_SHORT &&
1197       type != GL_UNSIGNED_INT) {
1198      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)");
1199      return;
1200   }
1201   if (end < start) {
1202      _mesa_compile_error(ctx, GL_INVALID_VALUE,
1203                          "glDrawRangeElements(end < start)");
1204      return;
1205   }
1206
1207   if (save->out_of_memory)
1208      return;
1209
1210   _save_OBE_DrawElements(mode, count, type, indices);
1211}
1212
1213
1214static void GLAPIENTRY
1215_save_OBE_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
1216                            const GLvoid * const *indices, GLsizei primcount)
1217{
1218   GLsizei i;
1219
1220   for (i = 0; i < primcount; i++) {
1221      if (count[i] > 0) {
1222	 CALL_DrawElements(GET_DISPATCH(), (mode, count[i], type, indices[i]));
1223      }
1224   }
1225}
1226
1227
1228static void GLAPIENTRY
1229_save_OBE_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
1230                                      GLenum type,
1231                                      const GLvoid * const *indices,
1232                                      GLsizei primcount,
1233                                      const GLint *basevertex)
1234{
1235   GLsizei i;
1236
1237   for (i = 0; i < primcount; i++) {
1238      if (count[i] > 0) {
1239	 CALL_DrawElementsBaseVertex(GET_DISPATCH(), (mode, count[i], type,
1240						      indices[i],
1241						      basevertex[i]));
1242      }
1243   }
1244}
1245
1246
1247static void
1248_save_vtxfmt_init(struct gl_context *ctx)
1249{
1250   struct vbo_save_context *save = &vbo_context(ctx)->save;
1251   GLvertexformat *vfmt = &save->vtxfmt;
1252
1253   vfmt->ArrayElement = _ae_ArrayElement;
1254
1255   vfmt->Color3f = _save_Color3f;
1256   vfmt->Color3fv = _save_Color3fv;
1257   vfmt->Color4f = _save_Color4f;
1258   vfmt->Color4fv = _save_Color4fv;
1259   vfmt->EdgeFlag = _save_EdgeFlag;
1260   vfmt->End = _save_End;
1261   vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV;
1262   vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1263   vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1264   vfmt->Indexf = _save_Indexf;
1265   vfmt->Indexfv = _save_Indexfv;
1266   vfmt->Materialfv = _save_Materialfv;
1267   vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1268   vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1269   vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1270   vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1271   vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1272   vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1273   vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1274   vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1275   vfmt->Normal3f = _save_Normal3f;
1276   vfmt->Normal3fv = _save_Normal3fv;
1277   vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1278   vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1279   vfmt->TexCoord1f = _save_TexCoord1f;
1280   vfmt->TexCoord1fv = _save_TexCoord1fv;
1281   vfmt->TexCoord2f = _save_TexCoord2f;
1282   vfmt->TexCoord2fv = _save_TexCoord2fv;
1283   vfmt->TexCoord3f = _save_TexCoord3f;
1284   vfmt->TexCoord3fv = _save_TexCoord3fv;
1285   vfmt->TexCoord4f = _save_TexCoord4f;
1286   vfmt->TexCoord4fv = _save_TexCoord4fv;
1287   vfmt->Vertex2f = _save_Vertex2f;
1288   vfmt->Vertex2fv = _save_Vertex2fv;
1289   vfmt->Vertex3f = _save_Vertex3f;
1290   vfmt->Vertex3fv = _save_Vertex3fv;
1291   vfmt->Vertex4f = _save_Vertex4f;
1292   vfmt->Vertex4fv = _save_Vertex4fv;
1293   vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1294   vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1295   vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1296   vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1297   vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1298   vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1299   vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1300   vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1301
1302   vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1303   vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1304   vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1305   vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1306   vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1307   vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1308   vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1309   vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1310
1311   /* integer-valued */
1312   vfmt->VertexAttribI1i = _save_VertexAttribI1i;
1313   vfmt->VertexAttribI2i = _save_VertexAttribI2i;
1314   vfmt->VertexAttribI3i = _save_VertexAttribI3i;
1315   vfmt->VertexAttribI4i = _save_VertexAttribI4i;
1316   vfmt->VertexAttribI2iv = _save_VertexAttribI2iv;
1317   vfmt->VertexAttribI3iv = _save_VertexAttribI3iv;
1318   vfmt->VertexAttribI4iv = _save_VertexAttribI4iv;
1319
1320   /* unsigned integer-valued */
1321   vfmt->VertexAttribI1ui = _save_VertexAttribI1ui;
1322   vfmt->VertexAttribI2ui = _save_VertexAttribI2ui;
1323   vfmt->VertexAttribI3ui = _save_VertexAttribI3ui;
1324   vfmt->VertexAttribI4ui = _save_VertexAttribI4ui;
1325   vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv;
1326   vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv;
1327   vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv;
1328
1329   vfmt->VertexP2ui = _save_VertexP2ui;
1330   vfmt->VertexP3ui = _save_VertexP3ui;
1331   vfmt->VertexP4ui = _save_VertexP4ui;
1332   vfmt->VertexP2uiv = _save_VertexP2uiv;
1333   vfmt->VertexP3uiv = _save_VertexP3uiv;
1334   vfmt->VertexP4uiv = _save_VertexP4uiv;
1335
1336   vfmt->TexCoordP1ui = _save_TexCoordP1ui;
1337   vfmt->TexCoordP2ui = _save_TexCoordP2ui;
1338   vfmt->TexCoordP3ui = _save_TexCoordP3ui;
1339   vfmt->TexCoordP4ui = _save_TexCoordP4ui;
1340   vfmt->TexCoordP1uiv = _save_TexCoordP1uiv;
1341   vfmt->TexCoordP2uiv = _save_TexCoordP2uiv;
1342   vfmt->TexCoordP3uiv = _save_TexCoordP3uiv;
1343   vfmt->TexCoordP4uiv = _save_TexCoordP4uiv;
1344
1345   vfmt->MultiTexCoordP1ui = _save_MultiTexCoordP1ui;
1346   vfmt->MultiTexCoordP2ui = _save_MultiTexCoordP2ui;
1347   vfmt->MultiTexCoordP3ui = _save_MultiTexCoordP3ui;
1348   vfmt->MultiTexCoordP4ui = _save_MultiTexCoordP4ui;
1349   vfmt->MultiTexCoordP1uiv = _save_MultiTexCoordP1uiv;
1350   vfmt->MultiTexCoordP2uiv = _save_MultiTexCoordP2uiv;
1351   vfmt->MultiTexCoordP3uiv = _save_MultiTexCoordP3uiv;
1352   vfmt->MultiTexCoordP4uiv = _save_MultiTexCoordP4uiv;
1353
1354   vfmt->NormalP3ui = _save_NormalP3ui;
1355   vfmt->NormalP3uiv = _save_NormalP3uiv;
1356
1357   vfmt->ColorP3ui = _save_ColorP3ui;
1358   vfmt->ColorP4ui = _save_ColorP4ui;
1359   vfmt->ColorP3uiv = _save_ColorP3uiv;
1360   vfmt->ColorP4uiv = _save_ColorP4uiv;
1361
1362   vfmt->SecondaryColorP3ui = _save_SecondaryColorP3ui;
1363   vfmt->SecondaryColorP3uiv = _save_SecondaryColorP3uiv;
1364
1365   vfmt->VertexAttribP1ui = _save_VertexAttribP1ui;
1366   vfmt->VertexAttribP2ui = _save_VertexAttribP2ui;
1367   vfmt->VertexAttribP3ui = _save_VertexAttribP3ui;
1368   vfmt->VertexAttribP4ui = _save_VertexAttribP4ui;
1369
1370   vfmt->VertexAttribP1uiv = _save_VertexAttribP1uiv;
1371   vfmt->VertexAttribP2uiv = _save_VertexAttribP2uiv;
1372   vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv;
1373   vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv;
1374
1375   /* This will all require us to fallback to saving the list as opcodes:
1376    */
1377   vfmt->CallList = _save_CallList;
1378   vfmt->CallLists = _save_CallLists;
1379
1380   vfmt->EvalCoord1f = _save_EvalCoord1f;
1381   vfmt->EvalCoord1fv = _save_EvalCoord1fv;
1382   vfmt->EvalCoord2f = _save_EvalCoord2f;
1383   vfmt->EvalCoord2fv = _save_EvalCoord2fv;
1384   vfmt->EvalPoint1 = _save_EvalPoint1;
1385   vfmt->EvalPoint2 = _save_EvalPoint2;
1386
1387   /* These calls all generate GL_INVALID_OPERATION since this vtxfmt is
1388    * only used when we're inside a glBegin/End pair.
1389    */
1390   vfmt->Begin = _save_Begin;
1391}
1392
1393
1394/**
1395 * Initialize the dispatch table with the VBO functions for display
1396 * list compilation.
1397 */
1398void
1399vbo_initialize_save_dispatch(const struct gl_context *ctx,
1400                             struct _glapi_table *exec)
1401{
1402   SET_DrawArrays(exec, _save_OBE_DrawArrays);
1403   SET_DrawElements(exec, _save_OBE_DrawElements);
1404   SET_DrawRangeElements(exec, _save_OBE_DrawRangeElements);
1405   SET_MultiDrawElementsEXT(exec, _save_OBE_MultiDrawElements);
1406   SET_MultiDrawElementsBaseVertex(exec, _save_OBE_MultiDrawElementsBaseVertex);
1407   SET_Rectf(exec, _save_OBE_Rectf);
1408   /* Note: other glDraw functins aren't compiled into display lists */
1409}
1410
1411
1412
1413void
1414vbo_save_SaveFlushVertices(struct gl_context *ctx)
1415{
1416   struct vbo_save_context *save = &vbo_context(ctx)->save;
1417
1418   /* Noop when we are actually active:
1419    */
1420   if (ctx->Driver.CurrentSavePrimitive <= PRIM_MAX)
1421      return;
1422
1423   if (save->vert_count || save->prim_count)
1424      _save_compile_vertex_list(ctx);
1425
1426   _save_copy_to_current(ctx);
1427   _save_reset_vertex(ctx);
1428   _save_reset_counters(ctx);
1429   ctx->Driver.SaveNeedFlush = GL_FALSE;
1430}
1431
1432
1433void
1434vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
1435{
1436   struct vbo_save_context *save = &vbo_context(ctx)->save;
1437
1438   (void) list;
1439   (void) mode;
1440
1441   if (!save->prim_store)
1442      save->prim_store = alloc_prim_store(ctx);
1443
1444   if (!save->vertex_store)
1445      save->vertex_store = alloc_vertex_store(ctx);
1446
1447   save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
1448
1449   _save_reset_vertex(ctx);
1450   _save_reset_counters(ctx);
1451   ctx->Driver.SaveNeedFlush = GL_FALSE;
1452}
1453
1454
1455void
1456vbo_save_EndList(struct gl_context *ctx)
1457{
1458   struct vbo_save_context *save = &vbo_context(ctx)->save;
1459
1460   /* EndList called inside a (saved) Begin/End pair?
1461    */
1462   if (_mesa_inside_dlist_begin_end(ctx)) {
1463      if (save->prim_count > 0) {
1464         GLint i = save->prim_count - 1;
1465         ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1466         save->prim[i].end = 0;
1467         save->prim[i].count = save->vert_count - save->prim[i].start;
1468      }
1469
1470      /* Make sure this vertex list gets replayed by the "loopback"
1471       * mechanism:
1472       */
1473      save->dangling_attr_ref = GL_TRUE;
1474      vbo_save_SaveFlushVertices(ctx);
1475
1476      /* Swap out this vertex format while outside begin/end.  Any color,
1477       * etc. received between here and the next begin will be compiled
1478       * as opcodes.
1479       */
1480      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1481   }
1482
1483   vbo_save_unmap_vertex_store(ctx, save->vertex_store);
1484
1485   assert(save->vertex_size == 0);
1486}
1487
1488
1489void
1490vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
1491{
1492   struct vbo_save_context *save = &vbo_context(ctx)->save;
1493   save->replay_flags |= dlist->Flags;
1494}
1495
1496
1497void
1498vbo_save_EndCallList(struct gl_context *ctx)
1499{
1500   struct vbo_save_context *save = &vbo_context(ctx)->save;
1501
1502   if (ctx->ListState.CallDepth == 1) {
1503      /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1504       * flag, if it is set:
1505       */
1506      save->replay_flags &= VBO_SAVE_FALLBACK;
1507   }
1508}
1509
1510
1511static void
1512vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
1513{
1514   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1515   (void) ctx;
1516
1517   if (--node->vertex_store->refcount == 0)
1518      free_vertex_store(ctx, node->vertex_store);
1519
1520   if (--node->prim_store->refcount == 0)
1521      free(node->prim_store);
1522
1523   free(node->current_data);
1524   node->current_data = NULL;
1525}
1526
1527
1528static void
1529vbo_print_vertex_list(struct gl_context *ctx, void *data, FILE *f)
1530{
1531   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1532   GLuint i;
1533   struct gl_buffer_object *buffer = node->vertex_store ?
1534      node->vertex_store->bufferobj : NULL;
1535   (void) ctx;
1536
1537   fprintf(f, "VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize "
1538           "buffer %p\n",
1539           node->count, node->prim_count, node->vertex_size,
1540           buffer);
1541
1542   for (i = 0; i < node->prim_count; i++) {
1543      struct _mesa_prim *prim = &node->prim[i];
1544      fprintf(f, "   prim %d: %s%s %d..%d %s %s\n",
1545             i,
1546             _mesa_lookup_prim_by_nr(prim->mode),
1547             prim->weak ? " (weak)" : "",
1548             prim->start,
1549             prim->start + prim->count,
1550             (prim->begin) ? "BEGIN" : "(wrap)",
1551             (prim->end) ? "END" : "(wrap)");
1552   }
1553}
1554
1555
1556/**
1557 * Called during context creation/init.
1558 */
1559static void
1560_save_current_init(struct gl_context *ctx)
1561{
1562   struct vbo_save_context *save = &vbo_context(ctx)->save;
1563   GLint i;
1564
1565   for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1566      const GLuint j = i - VBO_ATTRIB_POS;
1567      assert(j < VERT_ATTRIB_MAX);
1568      save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1569      save->current[i] = (fi_type *) ctx->ListState.CurrentAttrib[j];
1570   }
1571
1572   for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1573      const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1574      assert(j < MAT_ATTRIB_MAX);
1575      save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1576      save->current[i] = (fi_type *) ctx->ListState.CurrentMaterial[j];
1577   }
1578}
1579
1580
1581/**
1582 * Initialize the display list compiler.  Called during context creation.
1583 */
1584void
1585vbo_save_api_init(struct vbo_save_context *save)
1586{
1587   struct gl_context *ctx = save->ctx;
1588   GLuint i;
1589
1590   save->opcode_vertex_list =
1591      _mesa_dlist_alloc_opcode(ctx,
1592                               sizeof(struct vbo_save_vertex_list),
1593                               vbo_save_playback_vertex_list,
1594                               vbo_destroy_vertex_list,
1595                               vbo_print_vertex_list);
1596
1597   ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1598
1599   _save_vtxfmt_init(ctx);
1600   _save_current_init(ctx);
1601   _mesa_noop_vtxfmt_init(&save->vtxfmt_noop);
1602
1603   /* These will actually get set again when binding/drawing */
1604   for (i = 0; i < VBO_ATTRIB_MAX; i++)
1605      save->inputs[i] = &save->arrays[i];
1606}
1607