vbo_save_api.c revision b3d2ec9942303d1d03e28a25b030eb060415abfb
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_noop.h"
78#include "main/api_validate.h"
79#include "main/api_arrayelt.h"
80#include "main/vtxfmt.h"
81#include "main/dispatch.h"
82
83#include "vbo_context.h"
84
85
86#if FEATURE_dlist
87
88
89#ifdef ERROR
90#undef ERROR
91#endif
92
93
94/* An interesting VBO number/name to help with debugging */
95#define VBO_BUF_ID  12345
96
97
98/*
99 * NOTE: Old 'parity' issue is gone, but copying can still be
100 * wrong-footed on replay.
101 */
102static GLuint _save_copy_vertices( struct gl_context *ctx,
103				   const struct vbo_save_vertex_list *node,
104				   const GLfloat *src_buffer)
105{
106   struct vbo_save_context *save = &vbo_context( ctx )->save;
107   const struct _mesa_prim *prim = &node->prim[node->prim_count-1];
108   GLuint nr = prim->count;
109   GLuint sz = save->vertex_size;
110   const GLfloat *src = src_buffer + prim->start * sz;
111   GLfloat *dst = save->copied.buffer;
112   GLuint ovf, i;
113
114   if (prim->end)
115      return 0;
116
117   switch( prim->mode )
118   {
119   case GL_POINTS:
120      return 0;
121   case GL_LINES:
122      ovf = nr&1;
123      for (i = 0 ; i < ovf ; i++)
124	 memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
125      return i;
126   case GL_TRIANGLES:
127      ovf = nr%3;
128      for (i = 0 ; i < ovf ; i++)
129	 memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
130      return i;
131   case GL_QUADS:
132      ovf = nr&3;
133      for (i = 0 ; i < ovf ; i++)
134	 memcpy( dst+i*sz, src+(nr-ovf+i)*sz, 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      } else {
152	 memcpy( dst, src+0, sz*sizeof(GLfloat) );
153	 memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) );
154	 return 2;
155      }
156   case GL_TRIANGLE_STRIP:
157   case GL_QUAD_STRIP:
158      switch (nr) {
159      case 0: ovf = 0; break;
160      case 1: ovf = 1; break;
161      default: ovf = 2 + (nr&1); break;
162      }
163      for (i = 0 ; i < ovf ; i++)
164	 memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
165      return i;
166   default:
167      assert(0);
168      return 0;
169   }
170}
171
172
173static struct vbo_save_vertex_store *alloc_vertex_store( struct gl_context *ctx )
174{
175   struct vbo_save_vertex_store *vertex_store = CALLOC_STRUCT(vbo_save_vertex_store);
176
177   /* obj->Name needs to be non-zero, but won't ever be examined more
178    * closely than that.  In particular these buffers won't be entered
179    * into the hash and can never be confused with ones visible to the
180    * user.  Perhaps there could be a special number for internal
181    * buffers:
182    */
183   vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx,
184                                                         VBO_BUF_ID,
185                                                         GL_ARRAY_BUFFER_ARB);
186
187   ctx->Driver.BufferData( ctx,
188			   GL_ARRAY_BUFFER_ARB,
189			   VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
190			   NULL,
191			   GL_STATIC_DRAW_ARB,
192			   vertex_store->bufferobj);
193
194   vertex_store->buffer = NULL;
195   vertex_store->used = 0;
196   vertex_store->refcount = 1;
197
198   return vertex_store;
199}
200
201static void free_vertex_store( struct gl_context *ctx, struct vbo_save_vertex_store *vertex_store )
202{
203   assert(!vertex_store->buffer);
204
205   if (vertex_store->bufferobj) {
206      _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
207   }
208
209   FREE( vertex_store );
210}
211
212static GLfloat *map_vertex_store( struct gl_context *ctx, struct vbo_save_vertex_store *vertex_store )
213{
214   assert(vertex_store->bufferobj);
215   assert(!vertex_store->buffer);
216   vertex_store->buffer = (GLfloat *)ctx->Driver.MapBuffer(ctx,
217							   GL_ARRAY_BUFFER_ARB,	/* not used */
218							   GL_WRITE_ONLY, /* not used */
219							   vertex_store->bufferobj);
220
221   assert(vertex_store->buffer);
222   return vertex_store->buffer + vertex_store->used;
223}
224
225static void unmap_vertex_store( struct gl_context *ctx, struct vbo_save_vertex_store *vertex_store )
226{
227   ctx->Driver.UnmapBuffer( ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj );
228   vertex_store->buffer = NULL;
229}
230
231
232static struct vbo_save_primitive_store *alloc_prim_store( struct gl_context *ctx )
233{
234   struct vbo_save_primitive_store *store = CALLOC_STRUCT(vbo_save_primitive_store);
235   (void) ctx;
236   store->used = 0;
237   store->refcount = 1;
238   return store;
239}
240
241static void _save_reset_counters( struct gl_context *ctx )
242{
243   struct vbo_save_context *save = &vbo_context(ctx)->save;
244
245   save->prim = save->prim_store->buffer + save->prim_store->used;
246   save->buffer = (save->vertex_store->buffer +
247		   save->vertex_store->used);
248
249   assert(save->buffer == save->buffer_ptr);
250
251   if (save->vertex_size)
252      save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
253			 save->vertex_size);
254   else
255      save->max_vert = 0;
256
257   save->vert_count = 0;
258   save->prim_count = 0;
259   save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
260   save->dangling_attr_ref = 0;
261}
262
263
264/* Insert the active immediate struct onto the display list currently
265 * being built.
266 */
267static void _save_compile_vertex_list( struct gl_context *ctx )
268{
269   struct vbo_save_context *save = &vbo_context(ctx)->save;
270   struct vbo_save_vertex_list *node;
271
272   /* Allocate space for this structure in the display list currently
273    * being compiled.
274    */
275   node = (struct vbo_save_vertex_list *)
276      _mesa_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node));
277
278   if (!node)
279      return;
280
281   /* Duplicate our template, increment refcounts to the storage structs:
282    */
283   memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
284   node->vertex_size = save->vertex_size;
285   node->buffer_offset = (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat);
286   node->count = save->vert_count;
287   node->wrap_count = save->copied.nr;
288   node->dangling_attr_ref = save->dangling_attr_ref;
289   node->prim = save->prim;
290   node->prim_count = save->prim_count;
291   node->vertex_store = save->vertex_store;
292   node->prim_store = save->prim_store;
293
294   node->vertex_store->refcount++;
295   node->prim_store->refcount++;
296
297   if (node->prim[0].no_current_update) {
298      node->current_size = 0;
299      node->current_data = NULL;
300   } else {
301      node->current_size = node->vertex_size - node->attrsz[0];
302      node->current_data = NULL;
303
304      if (node->current_size) {
305         /* If the malloc fails, we just pull the data out of the VBO
306          * later instead.
307          */
308         node->current_data = MALLOC( node->current_size * sizeof(GLfloat) );
309         if (node->current_data) {
310            const char *buffer = (const char *)save->vertex_store->buffer;
311            unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
312            unsigned vertex_offset = 0;
313
314            if (node->count)
315               vertex_offset = (node->count-1) * node->vertex_size * sizeof(GLfloat);
316
317            memcpy( node->current_data,
318                    buffer + node->buffer_offset + vertex_offset + attr_offset,
319                    node->current_size * sizeof(GLfloat) );
320         }
321      }
322   }
323
324
325
326   assert(node->attrsz[VBO_ATTRIB_POS] != 0 ||
327	  node->count == 0);
328
329   if (save->dangling_attr_ref)
330      ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
331
332   save->vertex_store->used += save->vertex_size * node->count;
333   save->prim_store->used += node->prim_count;
334
335
336   /* Copy duplicated vertices
337    */
338   save->copied.nr = _save_copy_vertices( ctx, node, save->buffer );
339
340
341   /* Deal with GL_COMPILE_AND_EXECUTE:
342    */
343   if (ctx->ExecuteFlag) {
344      struct _glapi_table *dispatch = GET_DISPATCH();
345
346      _glapi_set_dispatch(ctx->Exec);
347
348      vbo_loopback_vertex_list( ctx,
349				(const GLfloat *)((const char *)save->vertex_store->buffer +
350						  node->buffer_offset),
351				node->attrsz,
352				node->prim,
353				node->prim_count,
354				node->wrap_count,
355				node->vertex_size);
356
357      _glapi_set_dispatch(dispatch);
358   }
359
360
361   /* Decide whether the storage structs are full, or can be used for
362    * the next vertex lists as well.
363    */
364   if (save->vertex_store->used >
365       VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) {
366
367      /* Unmap old store:
368       */
369      unmap_vertex_store( ctx, save->vertex_store );
370
371      /* Release old reference:
372       */
373      save->vertex_store->refcount--;
374      assert(save->vertex_store->refcount != 0);
375      save->vertex_store = NULL;
376
377      /* Allocate and map new store:
378       */
379      save->vertex_store = alloc_vertex_store( ctx );
380      save->buffer_ptr = map_vertex_store( ctx, save->vertex_store );
381   }
382
383   if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
384      save->prim_store->refcount--;
385      assert(save->prim_store->refcount != 0);
386      save->prim_store = alloc_prim_store( ctx );
387   }
388
389   /* Reset our structures for the next run of vertices:
390    */
391   _save_reset_counters( ctx );
392}
393
394
395/* TODO -- If no new vertices have been stored, don't bother saving
396 * it.
397 */
398static void _save_wrap_buffers( struct gl_context *ctx )
399{
400   struct vbo_save_context *save = &vbo_context(ctx)->save;
401   GLint i = save->prim_count - 1;
402   GLenum mode;
403   GLboolean weak;
404   GLboolean no_current_update;
405
406   assert(i < (GLint) save->prim_max);
407   assert(i >= 0);
408
409   /* Close off in-progress primitive.
410    */
411   save->prim[i].count = (save->vert_count -
412			  save->prim[i].start);
413   mode = save->prim[i].mode;
414   weak = save->prim[i].weak;
415   no_current_update = save->prim[i].no_current_update;
416
417   /* store the copied vertices, and allocate a new list.
418    */
419   _save_compile_vertex_list( ctx );
420
421   /* Restart interrupted primitive
422    */
423   save->prim[0].mode = mode;
424   save->prim[0].weak = weak;
425   save->prim[0].no_current_update = no_current_update;
426   save->prim[0].begin = 0;
427   save->prim[0].end = 0;
428   save->prim[0].pad = 0;
429   save->prim[0].start = 0;
430   save->prim[0].count = 0;
431   save->prim[0].num_instances = 1;
432   save->prim_count = 1;
433}
434
435
436
437/* Called only when buffers are wrapped as the result of filling the
438 * vertex_store struct.
439 */
440static void _save_wrap_filled_vertex( struct gl_context *ctx )
441{
442   struct vbo_save_context *save = &vbo_context(ctx)->save;
443   GLfloat *data = save->copied.buffer;
444   GLuint i;
445
446   /* Emit a glEnd to close off the last vertex list.
447    */
448   _save_wrap_buffers( ctx );
449
450    /* Copy stored stored vertices to start of new list.
451    */
452   assert(save->max_vert - save->vert_count > save->copied.nr);
453
454   for (i = 0 ; i < save->copied.nr ; i++) {
455      memcpy( save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat));
456      data += save->vertex_size;
457      save->buffer_ptr += save->vertex_size;
458      save->vert_count++;
459   }
460}
461
462
463static void _save_copy_to_current( struct gl_context *ctx )
464{
465   struct vbo_save_context *save = &vbo_context(ctx)->save;
466   GLuint i;
467
468   for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
469      if (save->attrsz[i]) {
470	 save->currentsz[i][0] = save->attrsz[i];
471	 COPY_CLEAN_4V(save->current[i],
472		       save->attrsz[i],
473		       save->attrptr[i]);
474      }
475   }
476}
477
478
479static void _save_copy_from_current( struct gl_context *ctx )
480{
481   struct vbo_save_context *save = &vbo_context(ctx)->save;
482   GLint i;
483
484   for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
485      switch (save->attrsz[i]) {
486      case 4: save->attrptr[i][3] = save->current[i][3];
487      case 3: save->attrptr[i][2] = save->current[i][2];
488      case 2: save->attrptr[i][1] = save->current[i][1];
489      case 1: save->attrptr[i][0] = save->current[i][0];
490      case 0: break;
491      }
492   }
493}
494
495
496
497
498/* Flush existing data, set new attrib size, replay copied vertices.
499 */
500static void _save_upgrade_vertex( struct gl_context *ctx,
501				 GLuint attr,
502				 GLuint newsz )
503{
504   struct vbo_save_context *save = &vbo_context(ctx)->save;
505   GLuint oldsz;
506   GLuint i;
507   GLfloat *tmp;
508
509   /* Store the current run of vertices, and emit a GL_END.  Emit a
510    * BEGIN in the new buffer.
511    */
512   if (save->vert_count)
513      _save_wrap_buffers( ctx );
514   else
515      assert( save->copied.nr == 0 );
516
517   /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
518    * when the attribute already exists in the vertex and is having
519    * its size increased.
520    */
521   _save_copy_to_current( ctx );
522
523   /* Fix up sizes:
524    */
525   oldsz = save->attrsz[attr];
526   save->attrsz[attr] = newsz;
527
528   save->vertex_size += newsz - oldsz;
529   save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
530		      save->vertex_size);
531   save->vert_count = 0;
532
533   /* Recalculate all the attrptr[] values:
534    */
535   for (i = 0, tmp = save->vertex ; i < VBO_ATTRIB_MAX ; i++) {
536      if (save->attrsz[i]) {
537	 save->attrptr[i] = tmp;
538	 tmp += save->attrsz[i];
539      }
540      else
541	 save->attrptr[i] = NULL; /* will not be dereferenced. */
542   }
543
544   /* Copy from current to repopulate the vertex with correct values.
545    */
546   _save_copy_from_current( ctx );
547
548   /* Replay stored vertices to translate them to new format here.
549    *
550    * If there are copied vertices and the new (upgraded) attribute
551    * has not been defined before, this list is somewhat degenerate,
552    * and will need fixup at runtime.
553    */
554   if (save->copied.nr)
555   {
556      GLfloat *data = save->copied.buffer;
557      GLfloat *dest = save->buffer;
558      GLuint j;
559
560      /* Need to note this and fix up at runtime (or loopback):
561       */
562      if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
563	 assert(oldsz == 0);
564	 save->dangling_attr_ref = GL_TRUE;
565      }
566
567      for (i = 0 ; i < save->copied.nr ; i++) {
568	 for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) {
569	    if (save->attrsz[j]) {
570	       if (j == attr) {
571		  if (oldsz) {
572		     COPY_CLEAN_4V( dest, oldsz, data );
573		     data += oldsz;
574		     dest += newsz;
575		  }
576		  else {
577		     COPY_SZ_4V( dest, newsz, save->current[attr] );
578		     dest += newsz;
579		  }
580	       }
581	       else {
582		  GLint sz = save->attrsz[j];
583		  COPY_SZ_4V( dest, sz, data );
584		  data += sz;
585		  dest += sz;
586	       }
587	    }
588	 }
589      }
590
591      save->buffer_ptr = dest;
592      save->vert_count += save->copied.nr;
593   }
594}
595
596static void save_fixup_vertex( struct gl_context *ctx, GLuint attr, GLuint sz )
597{
598   struct vbo_save_context *save = &vbo_context(ctx)->save;
599
600   if (sz > save->attrsz[attr]) {
601      /* New size is larger.  Need to flush existing vertices and get
602       * an enlarged vertex format.
603       */
604      _save_upgrade_vertex( ctx, attr, sz );
605   }
606   else if (sz < save->active_sz[attr]) {
607      static GLfloat id[4] = { 0, 0, 0, 1 };
608      GLuint i;
609
610      /* New size is equal or smaller - just need to fill in some
611       * zeros.
612       */
613      for (i = sz ; i <= save->attrsz[attr] ; i++)
614	 save->attrptr[attr][i-1] = id[i-1];
615   }
616
617   save->active_sz[attr] = sz;
618}
619
620static void _save_reset_vertex( struct gl_context *ctx )
621{
622   struct vbo_save_context *save = &vbo_context(ctx)->save;
623   GLuint i;
624
625   for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
626      save->attrsz[i] = 0;
627      save->active_sz[i] = 0;
628   }
629
630   save->vertex_size = 0;
631}
632
633
634
635#define ERROR()   _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ );
636
637
638/* Only one size for each attribute may be active at once.  Eg. if
639 * Color3f is installed/active, then Color4f may not be, even if the
640 * vertex actually contains 4 color coordinates.  This is because the
641 * 3f version won't otherwise set color[3] to 1.0 -- this is the job
642 * of the chooser function when switching between Color4f and Color3f.
643 */
644#define ATTR( A, N, V0, V1, V2, V3 )				\
645do {								\
646   struct vbo_save_context *save = &vbo_context(ctx)->save;	\
647								\
648   if (save->active_sz[A] != N)				\
649      save_fixup_vertex(ctx, A, N);				\
650								\
651   {								\
652      GLfloat *dest = save->attrptr[A];			\
653      if (N>0) dest[0] = V0;					\
654      if (N>1) dest[1] = V1;					\
655      if (N>2) dest[2] = V2;					\
656      if (N>3) dest[3] = V3;					\
657   }								\
658								\
659   if ((A) == 0) {						\
660      GLuint i;							\
661								\
662      for (i = 0; i < save->vertex_size; i++)			\
663	 save->buffer_ptr[i] = save->vertex[i];			\
664								\
665      save->buffer_ptr += save->vertex_size;				\
666								\
667      if (++save->vert_count >= save->max_vert)			\
668	 _save_wrap_filled_vertex( ctx );			\
669   }								\
670} while (0)
671
672#define TAG(x) _save_##x
673
674#include "vbo_attrib_tmp.h"
675
676
677
678
679/* Cope with EvalCoord/CallList called within a begin/end object:
680 *     -- Flush current buffer
681 *     -- Fallback to opcodes for the rest of the begin/end object.
682 */
683static void DO_FALLBACK( struct gl_context *ctx )
684{
685   struct vbo_save_context *save = &vbo_context(ctx)->save;
686
687   if (save->vert_count || save->prim_count) {
688      GLint i = save->prim_count - 1;
689
690      /* Close off in-progress primitive.
691       */
692      save->prim[i].count = (save->vert_count -
693                             save->prim[i].start);
694
695      /* Need to replay this display list with loopback,
696       * unfortunately, otherwise this primitive won't be handled
697       * properly:
698       */
699      save->dangling_attr_ref = 1;
700
701      _save_compile_vertex_list( ctx );
702   }
703
704   _save_copy_to_current( ctx );
705   _save_reset_vertex( ctx );
706   _save_reset_counters( ctx );
707   _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
708   ctx->Driver.SaveNeedFlush = 0;
709}
710
711static void GLAPIENTRY _save_EvalCoord1f( GLfloat u )
712{
713   GET_CURRENT_CONTEXT(ctx);
714   DO_FALLBACK(ctx);
715   CALL_EvalCoord1f(ctx->Save, (u));
716}
717
718static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v )
719{
720   GET_CURRENT_CONTEXT(ctx);
721   DO_FALLBACK(ctx);
722   CALL_EvalCoord1fv(ctx->Save, (v));
723}
724
725static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v )
726{
727   GET_CURRENT_CONTEXT(ctx);
728   DO_FALLBACK(ctx);
729   CALL_EvalCoord2f(ctx->Save, (u, v));
730}
731
732static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v )
733{
734   GET_CURRENT_CONTEXT(ctx);
735   DO_FALLBACK(ctx);
736   CALL_EvalCoord2fv(ctx->Save, (v));
737}
738
739static void GLAPIENTRY _save_EvalPoint1( GLint i )
740{
741   GET_CURRENT_CONTEXT(ctx);
742   DO_FALLBACK(ctx);
743   CALL_EvalPoint1(ctx->Save, (i));
744}
745
746static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j )
747{
748   GET_CURRENT_CONTEXT(ctx);
749   DO_FALLBACK(ctx);
750   CALL_EvalPoint2(ctx->Save, (i, j));
751}
752
753static void GLAPIENTRY _save_CallList( GLuint l )
754{
755   GET_CURRENT_CONTEXT(ctx);
756   DO_FALLBACK(ctx);
757   CALL_CallList(ctx->Save, (l));
758}
759
760static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v )
761{
762   GET_CURRENT_CONTEXT(ctx);
763   DO_FALLBACK(ctx);
764   CALL_CallLists(ctx->Save, (n, type, v));
765}
766
767
768
769
770/* This begin is hooked into ...  Updating of
771 * ctx->Driver.CurrentSavePrimitive is already taken care of.
772 */
773GLboolean vbo_save_NotifyBegin( struct gl_context *ctx, GLenum mode )
774{
775   struct vbo_save_context *save = &vbo_context(ctx)->save;
776
777   GLuint i = save->prim_count++;
778
779   assert(i < save->prim_max);
780   save->prim[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
781   save->prim[i].begin = 1;
782   save->prim[i].end = 0;
783   save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
784   save->prim[i].no_current_update = (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
785   save->prim[i].pad = 0;
786   save->prim[i].start = save->vert_count;
787   save->prim[i].count = 0;
788   save->prim[i].num_instances = 1;
789
790   _mesa_install_save_vtxfmt( ctx, &save->vtxfmt );
791   ctx->Driver.SaveNeedFlush = 1;
792   return GL_TRUE;
793}
794
795
796
797static void GLAPIENTRY _save_End( void )
798{
799   GET_CURRENT_CONTEXT( ctx );
800   struct vbo_save_context *save = &vbo_context(ctx)->save;
801   GLint i = save->prim_count - 1;
802
803   ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
804   save->prim[i].end = 1;
805   save->prim[i].count = (save->vert_count -
806			  save->prim[i].start);
807
808   if (i == (GLint) save->prim_max - 1) {
809      _save_compile_vertex_list( ctx );
810      assert(save->copied.nr == 0);
811   }
812
813   /* Swap out this vertex format while outside begin/end.  Any color,
814    * etc. received between here and the next begin will be compiled
815    * as opcodes.
816    */
817   _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
818}
819
820
821/* These are all errors as this vtxfmt is only installed inside
822 * begin/end pairs.
823 */
824static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type,
825			       const GLvoid *indices)
826{
827   GET_CURRENT_CONTEXT(ctx);
828   (void) mode; (void) count; (void) type; (void) indices;
829   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" );
830}
831
832
833static void GLAPIENTRY _save_DrawRangeElements(GLenum mode,
834				    GLuint start, GLuint end,
835				    GLsizei count, GLenum type,
836				    const GLvoid *indices)
837{
838   GET_CURRENT_CONTEXT(ctx);
839   (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices;
840   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" );
841}
842
843static void GLAPIENTRY _save_DrawElementsBaseVertex(GLenum mode,
844						    GLsizei count,
845						    GLenum type,
846						    const GLvoid *indices,
847						    GLint basevertex)
848{
849   GET_CURRENT_CONTEXT(ctx);
850   (void) mode; (void) count; (void) type; (void) indices; (void)basevertex;
851
852   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" );
853}
854
855static void GLAPIENTRY _save_DrawRangeElementsBaseVertex(GLenum mode,
856							 GLuint start,
857							 GLuint end,
858							 GLsizei count,
859							 GLenum type,
860							 const GLvoid *indices,
861							 GLint basevertex)
862{
863   GET_CURRENT_CONTEXT(ctx);
864   (void) mode; (void) start; (void) end; (void) count; (void) type;
865   (void) indices; (void)basevertex;
866
867   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" );
868}
869
870static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count)
871{
872   GET_CURRENT_CONTEXT(ctx);
873   (void) mode; (void) start; (void) count;
874   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" );
875}
876
877static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
878{
879   GET_CURRENT_CONTEXT(ctx);
880   (void) x1; (void) y1; (void) x2; (void) y2;
881   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" );
882}
883
884static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 )
885{
886   GET_CURRENT_CONTEXT(ctx);
887   (void) mode; (void) i1; (void) i2;
888   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" );
889}
890
891static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2,
892				  GLint j1, GLint j2 )
893{
894   GET_CURRENT_CONTEXT(ctx);
895   (void) mode; (void) i1; (void) i2; (void) j1; (void) j2;
896   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" );
897}
898
899static void GLAPIENTRY _save_Begin( GLenum mode )
900{
901   GET_CURRENT_CONTEXT( ctx );
902   (void) mode;
903   _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" );
904}
905
906
907static void GLAPIENTRY _save_PrimitiveRestartNV( void )
908{
909   GLenum curPrim;
910   GET_CURRENT_CONTEXT( ctx );
911
912   curPrim = ctx->Driver.CurrentSavePrimitive;
913
914   _save_End();
915   _save_Begin(curPrim);
916}
917
918
919/* Unlike the functions above, these are to be hooked into the vtxfmt
920 * maintained in ctx->ListState, active when the list is known or
921 * suspected to be outside any begin/end primitive.
922 */
923static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
924{
925   GET_CURRENT_CONTEXT(ctx);
926   vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK );
927   CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 ));
928   CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 ));
929   CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 ));
930   CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 ));
931   CALL_End(GET_DISPATCH(), ());
932}
933
934
935static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
936{
937   GET_CURRENT_CONTEXT(ctx);
938   GLint i;
939
940   if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
941      return;
942
943   _ae_map_vbos( ctx );
944
945   vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK | VBO_SAVE_PRIM_NO_CURRENT_UPDATE);
946
947   for (i = 0; i < count; i++)
948       CALL_ArrayElement(GET_DISPATCH(), (start + i));
949   CALL_End(GET_DISPATCH(), ());
950
951   _ae_unmap_vbos( ctx );
952}
953
954/* Could do better by copying the arrays and element list intact and
955 * then emitting an indexed prim at runtime.
956 */
957static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
958				   const GLvoid *indices)
959{
960   GET_CURRENT_CONTEXT(ctx);
961   GLint i;
962
963   if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices, 0 ))
964      return;
965
966   _ae_map_vbos( ctx );
967
968   if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj))
969      indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices);
970
971   vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK | VBO_SAVE_PRIM_NO_CURRENT_UPDATE );
972
973   switch (type) {
974   case GL_UNSIGNED_BYTE:
975      for (i = 0 ; i < count ; i++)
976	  CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] ));
977      break;
978   case GL_UNSIGNED_SHORT:
979      for (i = 0 ; i < count ; i++)
980	  CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] ));
981      break;
982   case GL_UNSIGNED_INT:
983      for (i = 0 ; i < count ; i++)
984	  CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] ));
985      break;
986   default:
987      _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
988      break;
989   }
990
991   CALL_End(GET_DISPATCH(), ());
992
993   _ae_unmap_vbos( ctx );
994}
995
996static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode,
997					GLuint start, GLuint end,
998					GLsizei count, GLenum type,
999					const GLvoid *indices)
1000{
1001   GET_CURRENT_CONTEXT(ctx);
1002   if (_mesa_validate_DrawRangeElements( ctx, mode,
1003					 start, end,
1004					 count, type, indices, 0 ))
1005      _save_OBE_DrawElements( mode, count, type, indices );
1006}
1007
1008
1009
1010
1011
1012static void _save_vtxfmt_init( struct gl_context *ctx )
1013{
1014   struct vbo_save_context *save = &vbo_context(ctx)->save;
1015   GLvertexformat *vfmt = &save->vtxfmt;
1016
1017   _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_);
1018
1019   vfmt->Begin = _save_Begin;
1020   vfmt->Color3f = _save_Color3f;
1021   vfmt->Color3fv = _save_Color3fv;
1022   vfmt->Color4f = _save_Color4f;
1023   vfmt->Color4fv = _save_Color4fv;
1024   vfmt->EdgeFlag = _save_EdgeFlag;
1025   vfmt->End = _save_End;
1026   vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV;
1027   vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1028   vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1029   vfmt->Indexf = _save_Indexf;
1030   vfmt->Indexfv = _save_Indexfv;
1031   vfmt->Materialfv = _save_Materialfv;
1032   vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1033   vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1034   vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1035   vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1036   vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1037   vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1038   vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1039   vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1040   vfmt->Normal3f = _save_Normal3f;
1041   vfmt->Normal3fv = _save_Normal3fv;
1042   vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1043   vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1044   vfmt->TexCoord1f = _save_TexCoord1f;
1045   vfmt->TexCoord1fv = _save_TexCoord1fv;
1046   vfmt->TexCoord2f = _save_TexCoord2f;
1047   vfmt->TexCoord2fv = _save_TexCoord2fv;
1048   vfmt->TexCoord3f = _save_TexCoord3f;
1049   vfmt->TexCoord3fv = _save_TexCoord3fv;
1050   vfmt->TexCoord4f = _save_TexCoord4f;
1051   vfmt->TexCoord4fv = _save_TexCoord4fv;
1052   vfmt->Vertex2f = _save_Vertex2f;
1053   vfmt->Vertex2fv = _save_Vertex2fv;
1054   vfmt->Vertex3f = _save_Vertex3f;
1055   vfmt->Vertex3fv = _save_Vertex3fv;
1056   vfmt->Vertex4f = _save_Vertex4f;
1057   vfmt->Vertex4fv = _save_Vertex4fv;
1058   vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1059   vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1060   vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1061   vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1062   vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1063   vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1064   vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1065   vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1066
1067   vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1068   vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1069   vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1070   vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1071   vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1072   vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1073   vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1074   vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1075
1076   /* integer-valued */
1077   vfmt->VertexAttribI1i = _save_VertexAttribI1i;
1078   vfmt->VertexAttribI2i = _save_VertexAttribI2i;
1079   vfmt->VertexAttribI3i = _save_VertexAttribI3i;
1080   vfmt->VertexAttribI4i = _save_VertexAttribI4i;
1081   vfmt->VertexAttribI2iv = _save_VertexAttribI2iv;
1082   vfmt->VertexAttribI3iv = _save_VertexAttribI3iv;
1083   vfmt->VertexAttribI4iv = _save_VertexAttribI4iv;
1084
1085   /* unsigned integer-valued */
1086   vfmt->VertexAttribI1ui = _save_VertexAttribI1ui;
1087   vfmt->VertexAttribI2ui = _save_VertexAttribI2ui;
1088   vfmt->VertexAttribI3ui = _save_VertexAttribI3ui;
1089   vfmt->VertexAttribI4ui = _save_VertexAttribI4ui;
1090   vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv;
1091   vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv;
1092   vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv;
1093
1094   /* This will all require us to fallback to saving the list as opcodes:
1095    */
1096   _MESA_INIT_DLIST_VTXFMT(vfmt, _save_); /* inside begin/end */
1097
1098   _MESA_INIT_EVAL_VTXFMT(vfmt, _save_);
1099
1100   /* These are all errors as we at least know we are in some sort of
1101    * begin/end pair:
1102    */
1103   vfmt->Begin = _save_Begin;
1104   vfmt->Rectf = _save_Rectf;
1105   vfmt->DrawArrays = _save_DrawArrays;
1106   vfmt->DrawElements = _save_DrawElements;
1107   vfmt->DrawRangeElements = _save_DrawRangeElements;
1108   vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex;
1109   vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex;
1110   /* Loops back into vfmt->DrawElements */
1111   vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements;
1112   vfmt->MultiDrawElementsBaseVertex = _mesa_noop_MultiDrawElementsBaseVertex;
1113}
1114
1115
1116void vbo_save_SaveFlushVertices( struct gl_context *ctx )
1117{
1118   struct vbo_save_context *save = &vbo_context(ctx)->save;
1119
1120   /* Noop when we are actually active:
1121    */
1122   if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
1123       ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
1124      return;
1125
1126   if (save->vert_count ||
1127       save->prim_count)
1128      _save_compile_vertex_list( ctx );
1129
1130   _save_copy_to_current( ctx );
1131   _save_reset_vertex( ctx );
1132   _save_reset_counters( ctx );
1133   ctx->Driver.SaveNeedFlush = 0;
1134}
1135
1136void vbo_save_NewList( struct gl_context *ctx, GLuint list, GLenum mode )
1137{
1138   struct vbo_save_context *save = &vbo_context(ctx)->save;
1139
1140   (void) list; (void) mode;
1141
1142   if (!save->prim_store)
1143      save->prim_store = alloc_prim_store( ctx );
1144
1145   if (!save->vertex_store)
1146      save->vertex_store = alloc_vertex_store( ctx );
1147
1148   save->buffer_ptr = map_vertex_store( ctx, save->vertex_store );
1149
1150   _save_reset_vertex( ctx );
1151   _save_reset_counters( ctx );
1152   ctx->Driver.SaveNeedFlush = 0;
1153}
1154
1155void vbo_save_EndList( struct gl_context *ctx )
1156{
1157   struct vbo_save_context *save = &vbo_context(ctx)->save;
1158
1159   /* EndList called inside a (saved) Begin/End pair?
1160    */
1161   if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) {
1162
1163      if (save->prim_count > 0) {
1164         GLint i = save->prim_count - 1;
1165         ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1166         save->prim[i].end = 0;
1167         save->prim[i].count = (save->vert_count -
1168                                save->prim[i].start);
1169      }
1170
1171      /* Make sure this vertex list gets replayed by the "loopback"
1172       * mechanism:
1173       */
1174      save->dangling_attr_ref = 1;
1175      vbo_save_SaveFlushVertices( ctx );
1176
1177      /* Swap out this vertex format while outside begin/end.  Any color,
1178       * etc. received between here and the next begin will be compiled
1179       * as opcodes.
1180       */
1181      _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1182   }
1183
1184   unmap_vertex_store( ctx, save->vertex_store );
1185
1186   assert(save->vertex_size == 0);
1187}
1188
1189void vbo_save_BeginCallList( struct gl_context *ctx, struct gl_display_list *dlist )
1190{
1191   struct vbo_save_context *save = &vbo_context(ctx)->save;
1192   save->replay_flags |= dlist->Flags;
1193}
1194
1195void vbo_save_EndCallList( struct gl_context *ctx )
1196{
1197   struct vbo_save_context *save = &vbo_context(ctx)->save;
1198
1199   if (ctx->ListState.CallDepth == 1) {
1200      /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1201       * flag, if it is set:
1202       */
1203      save->replay_flags &= VBO_SAVE_FALLBACK;
1204   }
1205}
1206
1207
1208static void vbo_destroy_vertex_list( struct gl_context *ctx, void *data )
1209{
1210   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
1211   (void) ctx;
1212
1213   if ( --node->vertex_store->refcount == 0 )
1214      free_vertex_store( ctx, node->vertex_store );
1215
1216   if ( --node->prim_store->refcount == 0 )
1217      FREE( node->prim_store );
1218
1219   if (node->current_data) {
1220      FREE(node->current_data);
1221      node->current_data = NULL;
1222   }
1223}
1224
1225
1226static void vbo_print_vertex_list( struct gl_context *ctx, void *data )
1227{
1228   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
1229   GLuint i;
1230   (void) ctx;
1231
1232   printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1233	  node->count,
1234	  node->prim_count,
1235	  node->vertex_size);
1236
1237   for (i = 0 ; i < node->prim_count ; i++) {
1238      struct _mesa_prim *prim = &node->prim[i];
1239      _mesa_debug(NULL, "   prim %d: %s%s %d..%d %s %s\n",
1240		  i,
1241		  _mesa_lookup_prim_by_nr(prim->mode),
1242		  prim->weak ? " (weak)" : "",
1243		  prim->start,
1244		  prim->start + prim->count,
1245		  (prim->begin) ? "BEGIN" : "(wrap)",
1246		  (prim->end) ? "END" : "(wrap)");
1247   }
1248}
1249
1250
1251static void _save_current_init( struct gl_context *ctx )
1252{
1253   struct vbo_save_context *save = &vbo_context(ctx)->save;
1254   GLint i;
1255
1256   for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1257      const GLuint j = i - VBO_ATTRIB_POS;
1258      ASSERT(j < VERT_ATTRIB_MAX);
1259      save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1260      save->current[i] = ctx->ListState.CurrentAttrib[j];
1261   }
1262
1263   for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1264      const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1265      ASSERT(j < MAT_ATTRIB_MAX);
1266      save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1267      save->current[i] = ctx->ListState.CurrentMaterial[j];
1268   }
1269}
1270
1271/**
1272 * Initialize the display list compiler
1273 */
1274void vbo_save_api_init( struct vbo_save_context *save )
1275{
1276   struct gl_context *ctx = save->ctx;
1277   GLuint i;
1278
1279   save->opcode_vertex_list =
1280      _mesa_dlist_alloc_opcode( ctx,
1281                                sizeof(struct vbo_save_vertex_list),
1282                                vbo_save_playback_vertex_list,
1283                                vbo_destroy_vertex_list,
1284                                vbo_print_vertex_list );
1285
1286   ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1287
1288   _save_vtxfmt_init( ctx );
1289   _save_current_init( ctx );
1290
1291   /* These will actually get set again when binding/drawing */
1292   for (i = 0; i < VBO_ATTRIB_MAX; i++)
1293      save->inputs[i] = &save->arrays[i];
1294
1295   /* Hook our array functions into the outside-begin-end vtxfmt in
1296    * ctx->ListState.
1297    */
1298   ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1299   ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1300   ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1301   ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1302   /* loops back into _save_OBE_DrawElements */
1303   ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = _mesa_noop_MultiDrawElements;
1304   ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex = _mesa_noop_MultiDrawElementsBaseVertex;
1305   _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1306}
1307
1308
1309#endif /* FEATURE_dlist */
1310