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