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