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