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