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