vbo_save_api.c revision c1a5c5c13d838309cf88664edf159e6086408f28
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_validate_DrawArrays(ctx, mode, start, count))
1236      return;
1237
1238   if (save->out_of_memory)
1239      return;
1240
1241   _ae_map_vbos(ctx);
1242
1243   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
1244                              | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1245
1246   for (i = 0; i < count; i++)
1247      CALL_ArrayElement(GET_DISPATCH(), (start + i));
1248   CALL_End(GET_DISPATCH(), ());
1249
1250   _ae_unmap_vbos(ctx);
1251}
1252
1253
1254/* Could do better by copying the arrays and element list intact and
1255 * then emitting an indexed prim at runtime.
1256 */
1257static void GLAPIENTRY
1258_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1259                       const GLvoid * indices)
1260{
1261   GET_CURRENT_CONTEXT(ctx);
1262   struct vbo_save_context *save = &vbo_context(ctx)->save;
1263   GLint i;
1264
1265   if (!_mesa_validate_DrawElements(ctx, mode, count, type, indices, 0))
1266      return;
1267
1268   if (save->out_of_memory)
1269      return;
1270
1271   _ae_map_vbos(ctx);
1272
1273   if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj))
1274      indices =
1275         ADD_POINTERS(ctx->Array.ArrayObj->ElementArrayBufferObj->Pointer, indices);
1276
1277   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
1278                              VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1279
1280   switch (type) {
1281   case GL_UNSIGNED_BYTE:
1282      for (i = 0; i < count; i++)
1283         CALL_ArrayElement(GET_DISPATCH(), (((GLubyte *) indices)[i]));
1284      break;
1285   case GL_UNSIGNED_SHORT:
1286      for (i = 0; i < count; i++)
1287         CALL_ArrayElement(GET_DISPATCH(), (((GLushort *) indices)[i]));
1288      break;
1289   case GL_UNSIGNED_INT:
1290      for (i = 0; i < count; i++)
1291         CALL_ArrayElement(GET_DISPATCH(), (((GLuint *) indices)[i]));
1292      break;
1293   default:
1294      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
1295      break;
1296   }
1297
1298   CALL_End(GET_DISPATCH(), ());
1299
1300   _ae_unmap_vbos(ctx);
1301}
1302
1303
1304static void GLAPIENTRY
1305_save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1306                            GLsizei count, GLenum type,
1307                            const GLvoid * indices)
1308{
1309   GET_CURRENT_CONTEXT(ctx);
1310   struct vbo_save_context *save = &vbo_context(ctx)->save;
1311
1312   if (!_mesa_validate_DrawRangeElements(ctx, mode,
1313                                         start, end, count, type, indices, 0))
1314      return;
1315
1316   if (save->out_of_memory)
1317      return;
1318
1319   _save_OBE_DrawElements(mode, count, type, indices);
1320}
1321
1322
1323static void GLAPIENTRY
1324_save_OBE_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
1325                            const GLvoid **indices, GLsizei primcount)
1326{
1327   GLsizei i;
1328
1329   for (i = 0; i < primcount; i++) {
1330      if (count[i] > 0) {
1331	 CALL_DrawElements(GET_DISPATCH(), (mode, count[i], type, indices[i]));
1332      }
1333   }
1334}
1335
1336
1337static void GLAPIENTRY
1338_save_OBE_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
1339                                      GLenum type,
1340                                      const GLvoid * const *indices,
1341                                      GLsizei primcount,
1342                                      const GLint *basevertex)
1343{
1344   GLsizei i;
1345
1346   for (i = 0; i < primcount; i++) {
1347      if (count[i] > 0) {
1348	 CALL_DrawElementsBaseVertex(GET_DISPATCH(), (mode, count[i], type,
1349						      indices[i],
1350						      basevertex[i]));
1351      }
1352   }
1353}
1354
1355
1356static void
1357_save_vtxfmt_init(struct gl_context *ctx)
1358{
1359   struct vbo_save_context *save = &vbo_context(ctx)->save;
1360   GLvertexformat *vfmt = &save->vtxfmt;
1361
1362   _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_);
1363
1364   vfmt->Begin = _save_Begin;
1365   vfmt->Color3f = _save_Color3f;
1366   vfmt->Color3fv = _save_Color3fv;
1367   vfmt->Color4f = _save_Color4f;
1368   vfmt->Color4fv = _save_Color4fv;
1369   vfmt->EdgeFlag = _save_EdgeFlag;
1370   vfmt->End = _save_End;
1371   vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV;
1372   vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1373   vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1374   vfmt->Indexf = _save_Indexf;
1375   vfmt->Indexfv = _save_Indexfv;
1376   vfmt->Materialfv = _save_Materialfv;
1377   vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1378   vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1379   vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1380   vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1381   vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1382   vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1383   vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1384   vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1385   vfmt->Normal3f = _save_Normal3f;
1386   vfmt->Normal3fv = _save_Normal3fv;
1387   vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1388   vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1389   vfmt->TexCoord1f = _save_TexCoord1f;
1390   vfmt->TexCoord1fv = _save_TexCoord1fv;
1391   vfmt->TexCoord2f = _save_TexCoord2f;
1392   vfmt->TexCoord2fv = _save_TexCoord2fv;
1393   vfmt->TexCoord3f = _save_TexCoord3f;
1394   vfmt->TexCoord3fv = _save_TexCoord3fv;
1395   vfmt->TexCoord4f = _save_TexCoord4f;
1396   vfmt->TexCoord4fv = _save_TexCoord4fv;
1397   vfmt->Vertex2f = _save_Vertex2f;
1398   vfmt->Vertex2fv = _save_Vertex2fv;
1399   vfmt->Vertex3f = _save_Vertex3f;
1400   vfmt->Vertex3fv = _save_Vertex3fv;
1401   vfmt->Vertex4f = _save_Vertex4f;
1402   vfmt->Vertex4fv = _save_Vertex4fv;
1403   vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1404   vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1405   vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1406   vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1407   vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1408   vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1409   vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1410   vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1411
1412   vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1413   vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1414   vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1415   vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1416   vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1417   vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1418   vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1419   vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1420
1421   /* integer-valued */
1422   vfmt->VertexAttribI1i = _save_VertexAttribI1i;
1423   vfmt->VertexAttribI2i = _save_VertexAttribI2i;
1424   vfmt->VertexAttribI3i = _save_VertexAttribI3i;
1425   vfmt->VertexAttribI4i = _save_VertexAttribI4i;
1426   vfmt->VertexAttribI2iv = _save_VertexAttribI2iv;
1427   vfmt->VertexAttribI3iv = _save_VertexAttribI3iv;
1428   vfmt->VertexAttribI4iv = _save_VertexAttribI4iv;
1429
1430   /* unsigned integer-valued */
1431   vfmt->VertexAttribI1ui = _save_VertexAttribI1ui;
1432   vfmt->VertexAttribI2ui = _save_VertexAttribI2ui;
1433   vfmt->VertexAttribI3ui = _save_VertexAttribI3ui;
1434   vfmt->VertexAttribI4ui = _save_VertexAttribI4ui;
1435   vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv;
1436   vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv;
1437   vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv;
1438
1439   vfmt->VertexP2ui = _save_VertexP2ui;
1440   vfmt->VertexP3ui = _save_VertexP3ui;
1441   vfmt->VertexP4ui = _save_VertexP4ui;
1442   vfmt->VertexP2uiv = _save_VertexP2uiv;
1443   vfmt->VertexP3uiv = _save_VertexP3uiv;
1444   vfmt->VertexP4uiv = _save_VertexP4uiv;
1445
1446   vfmt->TexCoordP1ui = _save_TexCoordP1ui;
1447   vfmt->TexCoordP2ui = _save_TexCoordP2ui;
1448   vfmt->TexCoordP3ui = _save_TexCoordP3ui;
1449   vfmt->TexCoordP4ui = _save_TexCoordP4ui;
1450   vfmt->TexCoordP1uiv = _save_TexCoordP1uiv;
1451   vfmt->TexCoordP2uiv = _save_TexCoordP2uiv;
1452   vfmt->TexCoordP3uiv = _save_TexCoordP3uiv;
1453   vfmt->TexCoordP4uiv = _save_TexCoordP4uiv;
1454
1455   vfmt->MultiTexCoordP1ui = _save_MultiTexCoordP1ui;
1456   vfmt->MultiTexCoordP2ui = _save_MultiTexCoordP2ui;
1457   vfmt->MultiTexCoordP3ui = _save_MultiTexCoordP3ui;
1458   vfmt->MultiTexCoordP4ui = _save_MultiTexCoordP4ui;
1459   vfmt->MultiTexCoordP1uiv = _save_MultiTexCoordP1uiv;
1460   vfmt->MultiTexCoordP2uiv = _save_MultiTexCoordP2uiv;
1461   vfmt->MultiTexCoordP3uiv = _save_MultiTexCoordP3uiv;
1462   vfmt->MultiTexCoordP4uiv = _save_MultiTexCoordP4uiv;
1463
1464   vfmt->NormalP3ui = _save_NormalP3ui;
1465   vfmt->NormalP3uiv = _save_NormalP3uiv;
1466
1467   vfmt->ColorP3ui = _save_ColorP3ui;
1468   vfmt->ColorP4ui = _save_ColorP4ui;
1469   vfmt->ColorP3uiv = _save_ColorP3uiv;
1470   vfmt->ColorP4uiv = _save_ColorP4uiv;
1471
1472   vfmt->SecondaryColorP3ui = _save_SecondaryColorP3ui;
1473   vfmt->SecondaryColorP3uiv = _save_SecondaryColorP3uiv;
1474
1475   vfmt->VertexAttribP1ui = _save_VertexAttribP1ui;
1476   vfmt->VertexAttribP2ui = _save_VertexAttribP2ui;
1477   vfmt->VertexAttribP3ui = _save_VertexAttribP3ui;
1478   vfmt->VertexAttribP4ui = _save_VertexAttribP4ui;
1479
1480   vfmt->VertexAttribP1uiv = _save_VertexAttribP1uiv;
1481   vfmt->VertexAttribP2uiv = _save_VertexAttribP2uiv;
1482   vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv;
1483   vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv;
1484
1485   /* This will all require us to fallback to saving the list as opcodes:
1486    */
1487   _MESA_INIT_DLIST_VTXFMT(vfmt, _save_);       /* inside begin/end */
1488
1489   _MESA_INIT_EVAL_VTXFMT(vfmt, _save_);
1490
1491   /* These calls all generate GL_INVALID_OPERATION since this vtxfmt is
1492    * only used when we're inside a glBegin/End pair.
1493    */
1494   vfmt->Begin = _save_Begin;
1495   vfmt->Rectf = _save_Rectf;
1496   vfmt->DrawArrays = _save_DrawArrays;
1497   vfmt->DrawElements = _save_DrawElements;
1498   vfmt->DrawRangeElements = _save_DrawRangeElements;
1499   vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex;
1500   vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex;
1501   vfmt->MultiDrawElementsEXT = _save_MultiDrawElements;
1502   vfmt->MultiDrawElementsBaseVertex = _save_MultiDrawElementsBaseVertex;
1503   vfmt->DrawTransformFeedback = _save_DrawTransformFeedback;
1504   vfmt->DrawTransformFeedbackStream = _save_DrawTransformFeedbackStream;
1505   vfmt->DrawTransformFeedbackInstanced = _save_DrawTransformFeedbackInstanced;
1506   vfmt->DrawTransformFeedbackStreamInstanced =
1507         _save_DrawTransformFeedbackStreamInstanced;
1508}
1509
1510
1511void
1512vbo_save_SaveFlushVertices(struct gl_context *ctx)
1513{
1514   struct vbo_save_context *save = &vbo_context(ctx)->save;
1515
1516   /* Noop when we are actually active:
1517    */
1518   if (ctx->Driver.CurrentSavePrimitive <= PRIM_MAX)
1519      return;
1520
1521   if (save->vert_count || save->prim_count)
1522      _save_compile_vertex_list(ctx);
1523
1524   _save_copy_to_current(ctx);
1525   _save_reset_vertex(ctx);
1526   _save_reset_counters(ctx);
1527   ctx->Driver.SaveNeedFlush = 0;
1528}
1529
1530
1531void
1532vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
1533{
1534   struct vbo_save_context *save = &vbo_context(ctx)->save;
1535
1536   (void) list;
1537   (void) mode;
1538
1539   if (!save->prim_store)
1540      save->prim_store = alloc_prim_store(ctx);
1541
1542   if (!save->vertex_store)
1543      save->vertex_store = alloc_vertex_store(ctx);
1544
1545   save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
1546
1547   _save_reset_vertex(ctx);
1548   _save_reset_counters(ctx);
1549   ctx->Driver.SaveNeedFlush = 0;
1550}
1551
1552
1553void
1554vbo_save_EndList(struct gl_context *ctx)
1555{
1556   struct vbo_save_context *save = &vbo_context(ctx)->save;
1557
1558   /* EndList called inside a (saved) Begin/End pair?
1559    */
1560   if (_mesa_inside_dlist_begin_end(ctx)) {
1561      if (save->prim_count > 0) {
1562         GLint i = save->prim_count - 1;
1563         ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1564         save->prim[i].end = 0;
1565         save->prim[i].count = (save->vert_count - save->prim[i].start);
1566      }
1567
1568      /* Make sure this vertex list gets replayed by the "loopback"
1569       * mechanism:
1570       */
1571      save->dangling_attr_ref = 1;
1572      vbo_save_SaveFlushVertices(ctx);
1573
1574      /* Swap out this vertex format while outside begin/end.  Any color,
1575       * etc. received between here and the next begin will be compiled
1576       * as opcodes.
1577       */
1578      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1579   }
1580
1581   vbo_save_unmap_vertex_store(ctx, save->vertex_store);
1582
1583   assert(save->vertex_size == 0);
1584}
1585
1586
1587void
1588vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
1589{
1590   struct vbo_save_context *save = &vbo_context(ctx)->save;
1591   save->replay_flags |= dlist->Flags;
1592}
1593
1594
1595void
1596vbo_save_EndCallList(struct gl_context *ctx)
1597{
1598   struct vbo_save_context *save = &vbo_context(ctx)->save;
1599
1600   if (ctx->ListState.CallDepth == 1) {
1601      /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1602       * flag, if it is set:
1603       */
1604      save->replay_flags &= VBO_SAVE_FALLBACK;
1605   }
1606}
1607
1608
1609static void
1610vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
1611{
1612   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1613   (void) ctx;
1614
1615   if (--node->vertex_store->refcount == 0)
1616      free_vertex_store(ctx, node->vertex_store);
1617
1618   if (--node->prim_store->refcount == 0)
1619      free(node->prim_store);
1620
1621   free(node->current_data);
1622   node->current_data = NULL;
1623}
1624
1625
1626static void
1627vbo_print_vertex_list(struct gl_context *ctx, void *data)
1628{
1629   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1630   GLuint i;
1631   (void) ctx;
1632
1633   printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1634          node->count, node->prim_count, node->vertex_size);
1635
1636   for (i = 0; i < node->prim_count; i++) {
1637      struct _mesa_prim *prim = &node->prim[i];
1638      printf("   prim %d: %s%s %d..%d %s %s\n",
1639             i,
1640             _mesa_lookup_prim_by_nr(prim->mode),
1641             prim->weak ? " (weak)" : "",
1642             prim->start,
1643             prim->start + prim->count,
1644             (prim->begin) ? "BEGIN" : "(wrap)",
1645             (prim->end) ? "END" : "(wrap)");
1646   }
1647}
1648
1649
1650/**
1651 * Called during context creation/init.
1652 */
1653static void
1654_save_current_init(struct gl_context *ctx)
1655{
1656   struct vbo_save_context *save = &vbo_context(ctx)->save;
1657   GLint i;
1658
1659   for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1660      const GLuint j = i - VBO_ATTRIB_POS;
1661      ASSERT(j < VERT_ATTRIB_MAX);
1662      save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1663      save->current[i] = ctx->ListState.CurrentAttrib[j];
1664   }
1665
1666   for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1667      const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1668      ASSERT(j < MAT_ATTRIB_MAX);
1669      save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1670      save->current[i] = ctx->ListState.CurrentMaterial[j];
1671   }
1672}
1673
1674
1675/**
1676 * Initialize the display list compiler.  Called during context creation.
1677 */
1678void
1679vbo_save_api_init(struct vbo_save_context *save)
1680{
1681   struct gl_context *ctx = save->ctx;
1682   GLuint i;
1683
1684   save->opcode_vertex_list =
1685      _mesa_dlist_alloc_opcode(ctx,
1686                               sizeof(struct vbo_save_vertex_list),
1687                               vbo_save_playback_vertex_list,
1688                               vbo_destroy_vertex_list,
1689                               vbo_print_vertex_list);
1690
1691   ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1692
1693   _save_vtxfmt_init(ctx);
1694   _save_current_init(ctx);
1695   _mesa_noop_vtxfmt_init(&save->vtxfmt_noop);
1696
1697   /* These will actually get set again when binding/drawing */
1698   for (i = 0; i < VBO_ATTRIB_MAX; i++)
1699      save->inputs[i] = &save->arrays[i];
1700
1701   /* Hook our array functions into the outside-begin-end vtxfmt in
1702    * ctx->ListState.
1703    */
1704   ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1705   ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1706   ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1707   ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1708   ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = _save_OBE_MultiDrawElements;
1709   ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex = _save_OBE_MultiDrawElementsBaseVertex;
1710}
1711