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