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