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