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