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