vbo_save_api.c revision b76f6d9557ff27140e18cf8aa2b57db8876d5d4d
11e57a46299244793beb27e74be171d1540606999oliviermartin/**************************************************************************
21e57a46299244793beb27e74be171d1540606999oliviermartin
3c26aa6f1d4d2612bccf10557a8a6b160f6d68b3foliviermartinCopyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas.
41e57a46299244793beb27e74be171d1540606999oliviermartin
51e57a46299244793beb27e74be171d1540606999oliviermartinAll Rights Reserved.
61e57a46299244793beb27e74be171d1540606999oliviermartin
71e57a46299244793beb27e74be171d1540606999oliviermartinPermission is hereby granted, free of charge, to any person obtaining a
81e57a46299244793beb27e74be171d1540606999oliviermartincopy of this software and associated documentation files (the "Software"),
91e57a46299244793beb27e74be171d1540606999oliviermartinto deal in the Software without restriction, including without limitation
101e57a46299244793beb27e74be171d1540606999oliviermartinon the rights to use, copy, modify, merge, publish, distribute, sub
111e57a46299244793beb27e74be171d1540606999oliviermartinlicense, and/or sell copies of the Software, and to permit persons to whom
121e57a46299244793beb27e74be171d1540606999oliviermartinthe Software is furnished to do so, subject to the following conditions:
131e57a46299244793beb27e74be171d1540606999oliviermartin
141e57a46299244793beb27e74be171d1540606999oliviermartinThe above copyright notice and this permission notice (including the next
151e57a46299244793beb27e74be171d1540606999oliviermartinparagraph) shall be included in all copies or substantial portions of the
161e57a46299244793beb27e74be171d1540606999oliviermartinSoftware.
171e57a46299244793beb27e74be171d1540606999oliviermartin
181e57a46299244793beb27e74be171d1540606999oliviermartinTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
191e57a46299244793beb27e74be171d1540606999oliviermartinIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
201e57a46299244793beb27e74be171d1540606999oliviermartinFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
211e57a46299244793beb27e74be171d1540606999oliviermartinTUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
221e57a46299244793beb27e74be171d1540606999oliviermartinDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
231e57a46299244793beb27e74be171d1540606999oliviermartinOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
241e57a46299244793beb27e74be171d1540606999oliviermartinUSE OR OTHER DEALINGS IN THE SOFTWARE.
251e57a46299244793beb27e74be171d1540606999oliviermartin
261e57a46299244793beb27e74be171d1540606999oliviermartin**************************************************************************/
271e57a46299244793beb27e74be171d1540606999oliviermartin
281e57a46299244793beb27e74be171d1540606999oliviermartin/*
291e57a46299244793beb27e74be171d1540606999oliviermartin * Authors:
301e57a46299244793beb27e74be171d1540606999oliviermartin *   Keith Whitwell <keith@tungstengraphics.com>
311e57a46299244793beb27e74be171d1540606999oliviermartin */
321e57a46299244793beb27e74be171d1540606999oliviermartin
331e57a46299244793beb27e74be171d1540606999oliviermartin
341e57a46299244793beb27e74be171d1540606999oliviermartin
351e57a46299244793beb27e74be171d1540606999oliviermartin/* Display list compiler attempts to store lists of vertices with the
361e57a46299244793beb27e74be171d1540606999oliviermartin * same vertex layout.  Additionally it attempts to minimize the need
371e57a46299244793beb27e74be171d1540606999oliviermartin * for execute-time fixup of these vertex lists, allowing them to be
381e57a46299244793beb27e74be171d1540606999oliviermartin * cached on hardware.
391e57a46299244793beb27e74be171d1540606999oliviermartin *
401e57a46299244793beb27e74be171d1540606999oliviermartin * There are still some circumstances where this can be thwarted, for
411e57a46299244793beb27e74be171d1540606999oliviermartin * example by building a list that consists of one very long primitive
421e57a46299244793beb27e74be171d1540606999oliviermartin * (eg Begin(Triangles), 1000 vertices, End), and calling that list
431e57a46299244793beb27e74be171d1540606999oliviermartin * from inside a different begin/end object (Begin(Lines), CallList,
441e57a46299244793beb27e74be171d1540606999oliviermartin * End).
451e57a46299244793beb27e74be171d1540606999oliviermartin *
461e57a46299244793beb27e74be171d1540606999oliviermartin * In that case the code will have to replay the list as individual
47c26aa6f1d4d2612bccf10557a8a6b160f6d68b3foliviermartin * commands through the Exec dispatch table, or fix up the copied
481e57a46299244793beb27e74be171d1540606999oliviermartin * vertices at execute-time.
491e57a46299244793beb27e74be171d1540606999oliviermartin *
501e57a46299244793beb27e74be171d1540606999oliviermartin * The other case where fixup is required is when a vertex attribute
511e57a46299244793beb27e74be171d1540606999oliviermartin * is introduced in the middle of a primitive.  Eg:
52c0b2e4775dcb06d9016f17f5d22e6276d9b58397oliviermartin *  Begin(Lines)
531e57a46299244793beb27e74be171d1540606999oliviermartin *  TexCoord1f()           Vertex2f()
541e57a46299244793beb27e74be171d1540606999oliviermartin *  TexCoord1f() Color3f() Vertex2f()
551e57a46299244793beb27e74be171d1540606999oliviermartin *  End()
561e57a46299244793beb27e74be171d1540606999oliviermartin *
571e57a46299244793beb27e74be171d1540606999oliviermartin *  If the current value of Color isn't known at compile-time, this
581e57a46299244793beb27e74be171d1540606999oliviermartin *  primitive will require fixup.
591e57a46299244793beb27e74be171d1540606999oliviermartin *
601e57a46299244793beb27e74be171d1540606999oliviermartin *
611e57a46299244793beb27e74be171d1540606999oliviermartin * The list compiler currently doesn't attempt to compile lists
621e57a46299244793beb27e74be171d1540606999oliviermartin * containing EvalCoord or EvalPoint commands.  On encountering one of
631e57a46299244793beb27e74be171d1540606999oliviermartin * these, compilation falls back to opcodes.
641e57a46299244793beb27e74be171d1540606999oliviermartin *
651e57a46299244793beb27e74be171d1540606999oliviermartin * This could be improved to fallback only when a mix of EvalCoord and
661e57a46299244793beb27e74be171d1540606999oliviermartin * Vertex commands are issued within a single primitive.
671e57a46299244793beb27e74be171d1540606999oliviermartin */
681e57a46299244793beb27e74be171d1540606999oliviermartin
691e57a46299244793beb27e74be171d1540606999oliviermartin
701e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/glheader.h"
711e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/bufferobj.h"
721e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/context.h"
731e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/dlist.h"
741e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/enums.h"
751e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/eval.h"
761e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/macros.h"
771e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/api_validate.h"
781e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/api_arrayelt.h"
791e57a46299244793beb27e74be171d1540606999oliviermartin#include "main/vtxfmt.h"
80#include "main/dispatch.h"
81
82#include "vbo_context.h"
83#include "vbo_noop.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
100_save_copy_vertices(struct gl_context *ctx,
101                    const struct vbo_save_vertex_list *node,
102                    const GLfloat * src_buffer)
103{
104   struct vbo_save_context *save = &vbo_context(ctx)->save;
105   const struct _mesa_prim *prim = &node->prim[node->prim_count - 1];
106   GLuint nr = prim->count;
107   GLuint sz = save->vertex_size;
108   const GLfloat *src = src_buffer + prim->start * sz;
109   GLfloat *dst = save->copied.buffer;
110   GLuint ovf, i;
111
112   if (prim->end)
113      return 0;
114
115   switch (prim->mode) {
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,
122                sz * sizeof(GLfloat));
123      return i;
124   case GL_TRIANGLES:
125      ovf = nr % 3;
126      for (i = 0; i < ovf; i++)
127         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
128                sz * sizeof(GLfloat));
129      return i;
130   case GL_QUADS:
131      ovf = nr & 3;
132      for (i = 0; i < ovf; i++)
133         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
134                sz * sizeof(GLfloat));
135      return i;
136   case GL_LINE_STRIP:
137      if (nr == 0)
138         return 0;
139      else {
140         memcpy(dst, src + (nr - 1) * sz, sz * sizeof(GLfloat));
141         return 1;
142      }
143   case GL_LINE_LOOP:
144   case GL_TRIANGLE_FAN:
145   case GL_POLYGON:
146      if (nr == 0)
147         return 0;
148      else if (nr == 1) {
149         memcpy(dst, src + 0, sz * sizeof(GLfloat));
150         return 1;
151      }
152      else {
153         memcpy(dst, src + 0, sz * sizeof(GLfloat));
154         memcpy(dst + sz, src + (nr - 1) * sz, sz * sizeof(GLfloat));
155         return 2;
156      }
157   case GL_TRIANGLE_STRIP:
158   case GL_QUAD_STRIP:
159      switch (nr) {
160      case 0:
161         ovf = 0;
162         break;
163      case 1:
164         ovf = 1;
165         break;
166      default:
167         ovf = 2 + (nr & 1);
168         break;
169      }
170      for (i = 0; i < ovf; i++)
171         memcpy(dst + i * sz, src + (nr - ovf + i) * sz,
172                sz * sizeof(GLfloat));
173      return i;
174   default:
175      assert(0);
176      return 0;
177   }
178}
179
180
181static struct vbo_save_vertex_store *
182alloc_vertex_store(struct gl_context *ctx)
183{
184   struct vbo_save_context *save = &vbo_context(ctx)->save;
185   struct vbo_save_vertex_store *vertex_store =
186      CALLOC_STRUCT(vbo_save_vertex_store);
187
188   /* obj->Name needs to be non-zero, but won't ever be examined more
189    * closely than that.  In particular these buffers won't be entered
190    * into the hash and can never be confused with ones visible to the
191    * user.  Perhaps there could be a special number for internal
192    * buffers:
193    */
194   vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx,
195                                                         VBO_BUF_ID,
196                                                         GL_ARRAY_BUFFER_ARB);
197   if (vertex_store->bufferobj) {
198      save->out_of_memory =
199         !ctx->Driver.BufferData(ctx,
200                                 GL_ARRAY_BUFFER_ARB,
201                                 VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
202                                 NULL, GL_STATIC_DRAW_ARB,
203                                 vertex_store->bufferobj);
204   }
205   else {
206      save->out_of_memory = GL_TRUE;
207   }
208
209   if (save->out_of_memory) {
210      _mesa_error(ctx, GL_OUT_OF_MEMORY, "internal VBO allocation");
211      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
212   }
213
214   vertex_store->buffer = NULL;
215   vertex_store->used = 0;
216   vertex_store->refcount = 1;
217
218   return vertex_store;
219}
220
221
222static void
223free_vertex_store(struct gl_context *ctx,
224                  struct vbo_save_vertex_store *vertex_store)
225{
226   assert(!vertex_store->buffer);
227
228   if (vertex_store->bufferobj) {
229      _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
230   }
231
232   free(vertex_store);
233}
234
235
236GLfloat *
237vbo_save_map_vertex_store(struct gl_context *ctx,
238                          struct vbo_save_vertex_store *vertex_store)
239{
240   assert(vertex_store->bufferobj);
241   assert(!vertex_store->buffer);
242   if (vertex_store->bufferobj->Size > 0) {
243      vertex_store->buffer =
244         (GLfloat *) ctx->Driver.MapBufferRange(ctx, 0,
245                                                vertex_store->bufferobj->Size,
246                                                GL_MAP_WRITE_BIT,  /* not used */
247                                                vertex_store->bufferobj);
248      assert(vertex_store->buffer);
249      return vertex_store->buffer + vertex_store->used;
250   }
251   else {
252      /* probably ran out of memory for buffers */
253      return NULL;
254   }
255}
256
257
258void
259vbo_save_unmap_vertex_store(struct gl_context *ctx,
260                            struct vbo_save_vertex_store *vertex_store)
261{
262   if (vertex_store->bufferobj->Size > 0) {
263      ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj);
264   }
265   vertex_store->buffer = NULL;
266}
267
268
269static struct vbo_save_primitive_store *
270alloc_prim_store(struct gl_context *ctx)
271{
272   struct vbo_save_primitive_store *store =
273      CALLOC_STRUCT(vbo_save_primitive_store);
274   (void) ctx;
275   store->used = 0;
276   store->refcount = 1;
277   return store;
278}
279
280
281static void
282_save_reset_counters(struct gl_context *ctx)
283{
284   struct vbo_save_context *save = &vbo_context(ctx)->save;
285
286   save->prim = save->prim_store->buffer + save->prim_store->used;
287   save->buffer = save->vertex_store->buffer + save->vertex_store->used;
288
289   assert(save->buffer == save->buffer_ptr);
290
291   if (save->vertex_size)
292      save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
293                        save->vertex_size);
294   else
295      save->max_vert = 0;
296
297   save->vert_count = 0;
298   save->prim_count = 0;
299   save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
300   save->dangling_attr_ref = 0;
301}
302
303/**
304 * For a list of prims, try merging prims that can just be extensions of the
305 * previous prim.
306 */
307static void
308vbo_merge_prims(struct gl_context *ctx,
309                struct _mesa_prim *prim_list,
310                GLuint *prim_count)
311{
312   GLuint i;
313   struct _mesa_prim *prev_prim = prim_list;
314
315   for (i = 1; i < *prim_count; i++) {
316      struct _mesa_prim *this_prim = prim_list + i;
317
318      if (this_prim->mode == prev_prim->mode &&
319          this_prim->mode == GL_QUADS &&
320          this_prim->count % 4 == 0 &&
321          prev_prim->count % 4 == 0 &&
322          this_prim->start == prev_prim->start + prev_prim->count &&
323          this_prim->basevertex == prev_prim->basevertex &&
324          this_prim->num_instances == prev_prim->num_instances &&
325          this_prim->base_instance == prev_prim->base_instance) {
326         /* We've found a prim that just extend the previous one.  Tack it
327          * onto the previous one, and let this primitive struct get dropped.
328          */
329         prev_prim->count += this_prim->count;
330         prev_prim->end = this_prim->end;
331         continue;
332      }
333
334      /* If any previous primitives have been dropped, then we need to copy
335       * this later one into the next available slot.
336       */
337      prev_prim++;
338      if (prev_prim != this_prim)
339         *prev_prim = *this_prim;
340   }
341
342   *prim_count = prev_prim - prim_list + 1;
343}
344
345/**
346 * Insert the active immediate struct onto the display list currently
347 * being built.
348 */
349static void
350_save_compile_vertex_list(struct gl_context *ctx)
351{
352   struct vbo_save_context *save = &vbo_context(ctx)->save;
353   struct vbo_save_vertex_list *node;
354
355   /* Allocate space for this structure in the display list currently
356    * being compiled.
357    */
358   node = (struct vbo_save_vertex_list *)
359      _mesa_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node));
360
361   if (!node)
362      return;
363
364   /* Duplicate our template, increment refcounts to the storage structs:
365    */
366   memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
367   memcpy(node->attrtype, save->attrtype, sizeof(node->attrtype));
368   node->vertex_size = save->vertex_size;
369   node->buffer_offset =
370      (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat);
371   node->count = save->vert_count;
372   node->wrap_count = save->copied.nr;
373   node->dangling_attr_ref = save->dangling_attr_ref;
374   node->prim = save->prim;
375   node->prim_count = save->prim_count;
376   node->vertex_store = save->vertex_store;
377   node->prim_store = save->prim_store;
378
379   node->vertex_store->refcount++;
380   node->prim_store->refcount++;
381
382   if (node->prim[0].no_current_update) {
383      node->current_size = 0;
384      node->current_data = NULL;
385   }
386   else {
387      node->current_size = node->vertex_size - node->attrsz[0];
388      node->current_data = NULL;
389
390      if (node->current_size) {
391         /* If the malloc fails, we just pull the data out of the VBO
392          * later instead.
393          */
394         node->current_data = malloc(node->current_size * sizeof(GLfloat));
395         if (node->current_data) {
396            const char *buffer = (const char *) save->vertex_store->buffer;
397            unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
398            unsigned vertex_offset = 0;
399
400            if (node->count)
401               vertex_offset =
402                  (node->count - 1) * node->vertex_size * sizeof(GLfloat);
403
404            memcpy(node->current_data,
405                   buffer + node->buffer_offset + vertex_offset + attr_offset,
406                   node->current_size * sizeof(GLfloat));
407         }
408      }
409   }
410
411   assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0);
412
413   if (save->dangling_attr_ref)
414      ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
415
416   save->vertex_store->used += save->vertex_size * node->count;
417   save->prim_store->used += node->prim_count;
418
419   /* Copy duplicated vertices
420    */
421   save->copied.nr = _save_copy_vertices(ctx, node, save->buffer);
422
423   vbo_merge_prims(ctx, node->prim, &node->prim_count);
424
425   /* Deal with GL_COMPILE_AND_EXECUTE:
426    */
427   if (ctx->ExecuteFlag) {
428      struct _glapi_table *dispatch = GET_DISPATCH();
429
430      _glapi_set_dispatch(ctx->Exec);
431
432      vbo_loopback_vertex_list(ctx,
433                               (const GLfloat *) ((const char *) save->
434                                                  vertex_store->buffer +
435                                                  node->buffer_offset),
436                               node->attrsz, node->prim, node->prim_count,
437                               node->wrap_count, node->vertex_size);
438
439      _glapi_set_dispatch(dispatch);
440   }
441
442   /* Decide whether the storage structs are full, or can be used for
443    * the next vertex lists as well.
444    */
445   if (save->vertex_store->used >
446       VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) {
447
448      /* Unmap old store:
449       */
450      vbo_save_unmap_vertex_store(ctx, save->vertex_store);
451
452      /* Release old reference:
453       */
454      save->vertex_store->refcount--;
455      assert(save->vertex_store->refcount != 0);
456      save->vertex_store = NULL;
457
458      /* Allocate and map new store:
459       */
460      save->vertex_store = alloc_vertex_store(ctx);
461      save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
462      save->out_of_memory = save->buffer_ptr == NULL;
463   }
464
465   if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
466      save->prim_store->refcount--;
467      assert(save->prim_store->refcount != 0);
468      save->prim_store = alloc_prim_store(ctx);
469   }
470
471   /* Reset our structures for the next run of vertices:
472    */
473   _save_reset_counters(ctx);
474}
475
476
477/**
478 * TODO -- If no new vertices have been stored, don't bother saving it.
479 */
480static void
481_save_wrap_buffers(struct gl_context *ctx)
482{
483   struct vbo_save_context *save = &vbo_context(ctx)->save;
484   GLint i = save->prim_count - 1;
485   GLenum mode;
486   GLboolean weak;
487   GLboolean no_current_update;
488
489   assert(i < (GLint) save->prim_max);
490   assert(i >= 0);
491
492   /* Close off in-progress primitive.
493    */
494   save->prim[i].count = (save->vert_count - save->prim[i].start);
495   mode = save->prim[i].mode;
496   weak = save->prim[i].weak;
497   no_current_update = save->prim[i].no_current_update;
498
499   /* store the copied vertices, and allocate a new list.
500    */
501   _save_compile_vertex_list(ctx);
502
503   /* Restart interrupted primitive
504    */
505   save->prim[0].mode = mode;
506   save->prim[0].weak = weak;
507   save->prim[0].no_current_update = no_current_update;
508   save->prim[0].begin = 0;
509   save->prim[0].end = 0;
510   save->prim[0].pad = 0;
511   save->prim[0].start = 0;
512   save->prim[0].count = 0;
513   save->prim[0].num_instances = 1;
514   save->prim[0].base_instance = 0;
515   save->prim_count = 1;
516}
517
518
519/**
520 * Called only when buffers are wrapped as the result of filling the
521 * vertex_store struct.
522 */
523static void
524_save_wrap_filled_vertex(struct gl_context *ctx)
525{
526   struct vbo_save_context *save = &vbo_context(ctx)->save;
527   GLfloat *data = save->copied.buffer;
528   GLuint i;
529
530   /* Emit a glEnd to close off the last vertex list.
531    */
532   _save_wrap_buffers(ctx);
533
534   /* Copy stored stored vertices to start of new list.
535    */
536   assert(save->max_vert - save->vert_count > save->copied.nr);
537
538   for (i = 0; i < save->copied.nr; i++) {
539      memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat));
540      data += save->vertex_size;
541      save->buffer_ptr += save->vertex_size;
542      save->vert_count++;
543   }
544}
545
546
547static void
548_save_copy_to_current(struct gl_context *ctx)
549{
550   struct vbo_save_context *save = &vbo_context(ctx)->save;
551   GLuint i;
552
553   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
554      if (save->attrsz[i]) {
555         save->currentsz[i][0] = save->attrsz[i];
556         COPY_CLEAN_4V_TYPE_AS_FLOAT(save->current[i], save->attrsz[i],
557                                     save->attrptr[i], save->attrtype[i]);
558      }
559   }
560}
561
562
563static void
564_save_copy_from_current(struct gl_context *ctx)
565{
566   struct vbo_save_context *save = &vbo_context(ctx)->save;
567   GLint i;
568
569   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
570      switch (save->attrsz[i]) {
571      case 4:
572         save->attrptr[i][3] = save->current[i][3];
573      case 3:
574         save->attrptr[i][2] = save->current[i][2];
575      case 2:
576         save->attrptr[i][1] = save->current[i][1];
577      case 1:
578         save->attrptr[i][0] = save->current[i][0];
579      case 0:
580         break;
581      }
582   }
583}
584
585
586/* Flush existing data, set new attrib size, replay copied vertices.
587 */
588static void
589_save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
590{
591   struct vbo_save_context *save = &vbo_context(ctx)->save;
592   GLuint oldsz;
593   GLuint i;
594   GLfloat *tmp;
595
596   /* Store the current run of vertices, and emit a GL_END.  Emit a
597    * BEGIN in the new buffer.
598    */
599   if (save->vert_count)
600      _save_wrap_buffers(ctx);
601   else
602      assert(save->copied.nr == 0);
603
604   /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
605    * when the attribute already exists in the vertex and is having
606    * its size increased.
607    */
608   _save_copy_to_current(ctx);
609
610   /* Fix up sizes:
611    */
612   oldsz = save->attrsz[attr];
613   save->attrsz[attr] = newsz;
614
615   save->vertex_size += newsz - oldsz;
616   save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
617                     save->vertex_size);
618   save->vert_count = 0;
619
620   /* Recalculate all the attrptr[] values:
621    */
622   for (i = 0, tmp = save->vertex; i < VBO_ATTRIB_MAX; i++) {
623      if (save->attrsz[i]) {
624         save->attrptr[i] = tmp;
625         tmp += save->attrsz[i];
626      }
627      else {
628         save->attrptr[i] = NULL;       /* will not be dereferenced. */
629      }
630   }
631
632   /* Copy from current to repopulate the vertex with correct values.
633    */
634   _save_copy_from_current(ctx);
635
636   /* Replay stored vertices to translate them to new format here.
637    *
638    * If there are copied vertices and the new (upgraded) attribute
639    * has not been defined before, this list is somewhat degenerate,
640    * and will need fixup at runtime.
641    */
642   if (save->copied.nr) {
643      GLfloat *data = save->copied.buffer;
644      GLfloat *dest = save->buffer;
645      GLuint j;
646
647      /* Need to note this and fix up at runtime (or loopback):
648       */
649      if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
650         assert(oldsz == 0);
651         save->dangling_attr_ref = GL_TRUE;
652      }
653
654      for (i = 0; i < save->copied.nr; i++) {
655         for (j = 0; j < VBO_ATTRIB_MAX; j++) {
656            if (save->attrsz[j]) {
657               if (j == attr) {
658                  if (oldsz) {
659                     COPY_CLEAN_4V_TYPE_AS_FLOAT(dest, oldsz, data,
660                                                 save->attrtype[j]);
661                     data += oldsz;
662                     dest += newsz;
663                  }
664                  else {
665                     COPY_SZ_4V(dest, newsz, save->current[attr]);
666                     dest += newsz;
667                  }
668               }
669               else {
670                  GLint sz = save->attrsz[j];
671                  COPY_SZ_4V(dest, sz, data);
672                  data += sz;
673                  dest += sz;
674               }
675            }
676         }
677      }
678
679      save->buffer_ptr = dest;
680      save->vert_count += save->copied.nr;
681   }
682}
683
684
685static void
686save_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint sz)
687{
688   struct vbo_save_context *save = &vbo_context(ctx)->save;
689
690   if (sz > save->attrsz[attr]) {
691      /* New size is larger.  Need to flush existing vertices and get
692       * an enlarged vertex format.
693       */
694      _save_upgrade_vertex(ctx, attr, sz);
695   }
696   else if (sz < save->active_sz[attr]) {
697      GLuint i;
698      const GLfloat *id = vbo_get_default_vals_as_float(save->attrtype[attr]);
699
700      /* New size is equal or smaller - just need to fill in some
701       * zeros.
702       */
703      for (i = sz; i <= save->attrsz[attr]; i++)
704         save->attrptr[attr][i - 1] = id[i - 1];
705   }
706
707   save->active_sz[attr] = sz;
708}
709
710
711static void
712_save_reset_vertex(struct gl_context *ctx)
713{
714   struct vbo_save_context *save = &vbo_context(ctx)->save;
715   GLuint i;
716
717   for (i = 0; i < VBO_ATTRIB_MAX; i++) {
718      save->attrsz[i] = 0;
719      save->active_sz[i] = 0;
720   }
721
722   save->vertex_size = 0;
723}
724
725
726
727#define ERROR(err)   _mesa_compile_error(ctx, err, __FUNCTION__);
728
729
730/* Only one size for each attribute may be active at once.  Eg. if
731 * Color3f is installed/active, then Color4f may not be, even if the
732 * vertex actually contains 4 color coordinates.  This is because the
733 * 3f version won't otherwise set color[3] to 1.0 -- this is the job
734 * of the chooser function when switching between Color4f and Color3f.
735 */
736#define ATTR(A, N, T, V0, V1, V2, V3)				\
737do {								\
738   struct vbo_save_context *save = &vbo_context(ctx)->save;	\
739								\
740   if (save->active_sz[A] != N)					\
741      save_fixup_vertex(ctx, A, N);				\
742								\
743   {								\
744      GLfloat *dest = save->attrptr[A];				\
745      if (N>0) dest[0] = V0;					\
746      if (N>1) dest[1] = V1;					\
747      if (N>2) dest[2] = V2;					\
748      if (N>3) dest[3] = V3;					\
749      save->attrtype[A] = T;                                    \
750   }								\
751								\
752   if ((A) == 0) {						\
753      GLuint i;							\
754								\
755      for (i = 0; i < save->vertex_size; i++)			\
756	 save->buffer_ptr[i] = save->vertex[i];			\
757								\
758      save->buffer_ptr += save->vertex_size;			\
759								\
760      if (++save->vert_count >= save->max_vert)			\
761	 _save_wrap_filled_vertex(ctx);				\
762   }								\
763} while (0)
764
765#define TAG(x) _save_##x
766
767#include "vbo_attrib_tmp.h"
768
769
770
771#define MAT( ATTR, N, face, params )			\
772do {							\
773   if (face != GL_BACK)					\
774      MAT_ATTR( ATTR, N, params ); /* front */		\
775   if (face != GL_FRONT)				\
776      MAT_ATTR( ATTR + 1, N, params ); /* back */	\
777} while (0)
778
779
780/**
781 * Save a glMaterial call found between glBegin/End.
782 * glMaterial calls outside Begin/End are handled in dlist.c.
783 */
784static void GLAPIENTRY
785_save_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
786{
787   GET_CURRENT_CONTEXT(ctx);
788
789   if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) {
790      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(face)");
791      return;
792   }
793
794   switch (pname) {
795   case GL_EMISSION:
796      MAT(VBO_ATTRIB_MAT_FRONT_EMISSION, 4, face, params);
797      break;
798   case GL_AMBIENT:
799      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
800      break;
801   case GL_DIFFUSE:
802      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
803      break;
804   case GL_SPECULAR:
805      MAT(VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params);
806      break;
807   case GL_SHININESS:
808      if (*params < 0 || *params > ctx->Const.MaxShininess) {
809         _mesa_compile_error(ctx, GL_INVALID_VALUE, "glMaterial(shininess)");
810      }
811      else {
812         MAT(VBO_ATTRIB_MAT_FRONT_SHININESS, 1, face, params);
813      }
814      break;
815   case GL_COLOR_INDEXES:
816      MAT(VBO_ATTRIB_MAT_FRONT_INDEXES, 3, face, params);
817      break;
818   case GL_AMBIENT_AND_DIFFUSE:
819      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
820      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
821      break;
822   default:
823      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(pname)");
824      return;
825   }
826}
827
828
829/* Cope with EvalCoord/CallList called within a begin/end object:
830 *     -- Flush current buffer
831 *     -- Fallback to opcodes for the rest of the begin/end object.
832 */
833static void
834dlist_fallback(struct gl_context *ctx)
835{
836   struct vbo_save_context *save = &vbo_context(ctx)->save;
837
838   if (save->vert_count || save->prim_count) {
839      if (save->prim_count > 0) {
840         /* Close off in-progress primitive. */
841         GLint i = save->prim_count - 1;
842         save->prim[i].count = save->vert_count - save->prim[i].start;
843      }
844
845      /* Need to replay this display list with loopback,
846       * unfortunately, otherwise this primitive won't be handled
847       * properly:
848       */
849      save->dangling_attr_ref = 1;
850
851      _save_compile_vertex_list(ctx);
852   }
853
854   _save_copy_to_current(ctx);
855   _save_reset_vertex(ctx);
856   _save_reset_counters(ctx);
857   if (save->out_of_memory) {
858      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
859   }
860   else {
861      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
862   }
863   ctx->Driver.SaveNeedFlush = 0;
864}
865
866
867static void GLAPIENTRY
868_save_EvalCoord1f(GLfloat u)
869{
870   GET_CURRENT_CONTEXT(ctx);
871   dlist_fallback(ctx);
872   CALL_EvalCoord1f(ctx->Save, (u));
873}
874
875static void GLAPIENTRY
876_save_EvalCoord1fv(const GLfloat * v)
877{
878   GET_CURRENT_CONTEXT(ctx);
879   dlist_fallback(ctx);
880   CALL_EvalCoord1fv(ctx->Save, (v));
881}
882
883static void GLAPIENTRY
884_save_EvalCoord2f(GLfloat u, GLfloat v)
885{
886   GET_CURRENT_CONTEXT(ctx);
887   dlist_fallback(ctx);
888   CALL_EvalCoord2f(ctx->Save, (u, v));
889}
890
891static void GLAPIENTRY
892_save_EvalCoord2fv(const GLfloat * v)
893{
894   GET_CURRENT_CONTEXT(ctx);
895   dlist_fallback(ctx);
896   CALL_EvalCoord2fv(ctx->Save, (v));
897}
898
899static void GLAPIENTRY
900_save_EvalPoint1(GLint i)
901{
902   GET_CURRENT_CONTEXT(ctx);
903   dlist_fallback(ctx);
904   CALL_EvalPoint1(ctx->Save, (i));
905}
906
907static void GLAPIENTRY
908_save_EvalPoint2(GLint i, GLint j)
909{
910   GET_CURRENT_CONTEXT(ctx);
911   dlist_fallback(ctx);
912   CALL_EvalPoint2(ctx->Save, (i, j));
913}
914
915static void GLAPIENTRY
916_save_CallList(GLuint l)
917{
918   GET_CURRENT_CONTEXT(ctx);
919   dlist_fallback(ctx);
920   CALL_CallList(ctx->Save, (l));
921}
922
923static void GLAPIENTRY
924_save_CallLists(GLsizei n, GLenum type, const GLvoid * v)
925{
926   GET_CURRENT_CONTEXT(ctx);
927   dlist_fallback(ctx);
928   CALL_CallLists(ctx->Save, (n, type, v));
929}
930
931
932
933/* This begin is hooked into ...  Updating of
934 * ctx->Driver.CurrentSavePrimitive is already taken care of.
935 */
936GLboolean
937vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
938{
939   struct vbo_save_context *save = &vbo_context(ctx)->save;
940
941   GLuint i = save->prim_count++;
942
943   assert(i < save->prim_max);
944   save->prim[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
945   save->prim[i].begin = 1;
946   save->prim[i].end = 0;
947   save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
948   save->prim[i].no_current_update =
949      (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
950   save->prim[i].pad = 0;
951   save->prim[i].start = save->vert_count;
952   save->prim[i].count = 0;
953   save->prim[i].num_instances = 1;
954   save->prim[i].base_instance = 0;
955
956   if (save->out_of_memory) {
957      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
958   }
959   else {
960      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
961   }
962   ctx->Driver.SaveNeedFlush = 1;
963   return GL_TRUE;
964}
965
966
967static void GLAPIENTRY
968_save_End(void)
969{
970   GET_CURRENT_CONTEXT(ctx);
971   struct vbo_save_context *save = &vbo_context(ctx)->save;
972   GLint i = save->prim_count - 1;
973
974   ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
975   save->prim[i].end = 1;
976   save->prim[i].count = (save->vert_count - save->prim[i].start);
977
978   if (i == (GLint) save->prim_max - 1) {
979      _save_compile_vertex_list(ctx);
980      assert(save->copied.nr == 0);
981   }
982
983   /* Swap out this vertex format while outside begin/end.  Any color,
984    * etc. received between here and the next begin will be compiled
985    * as opcodes.
986    */
987   if (save->out_of_memory) {
988      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
989   }
990   else {
991      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
992   }
993}
994
995
996/* These are all errors as this vtxfmt is only installed inside
997 * begin/end pairs.
998 */
999static void GLAPIENTRY
1000_save_DrawElements(GLenum mode, GLsizei count, GLenum type,
1001                   const GLvoid * indices)
1002{
1003   GET_CURRENT_CONTEXT(ctx);
1004   (void) mode;
1005   (void) count;
1006   (void) type;
1007   (void) indices;
1008   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements");
1009}
1010
1011
1012static void GLAPIENTRY
1013_save_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1014                        GLsizei count, GLenum type, const GLvoid * indices)
1015{
1016   GET_CURRENT_CONTEXT(ctx);
1017   (void) mode;
1018   (void) start;
1019   (void) end;
1020   (void) count;
1021   (void) type;
1022   (void) indices;
1023   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawRangeElements");
1024}
1025
1026
1027static void GLAPIENTRY
1028_save_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
1029                             const GLvoid * indices, GLint basevertex)
1030{
1031   GET_CURRENT_CONTEXT(ctx);
1032   (void) mode;
1033   (void) count;
1034   (void) type;
1035   (void) indices;
1036   (void) basevertex;
1037   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements");
1038}
1039
1040
1041static void GLAPIENTRY
1042_save_DrawRangeElementsBaseVertex(GLenum mode,
1043                                  GLuint start,
1044                                  GLuint end,
1045                                  GLsizei count,
1046                                  GLenum type,
1047                                  const GLvoid * indices, GLint basevertex)
1048{
1049   GET_CURRENT_CONTEXT(ctx);
1050   (void) mode;
1051   (void) start;
1052   (void) end;
1053   (void) count;
1054   (void) type;
1055   (void) indices;
1056   (void) basevertex;
1057   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawRangeElements");
1058}
1059
1060
1061static void GLAPIENTRY
1062_save_DrawArrays(GLenum mode, GLint start, GLsizei count)
1063{
1064   GET_CURRENT_CONTEXT(ctx);
1065   (void) mode;
1066   (void) start;
1067   (void) count;
1068   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawArrays");
1069}
1070
1071
1072static void GLAPIENTRY
1073_save_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
1074                        const GLvoid **indices, GLsizei primcount)
1075{
1076   GET_CURRENT_CONTEXT(ctx);
1077   (void) mode;
1078   (void) count;
1079   (void) type;
1080   (void) indices;
1081   (void) primcount;
1082   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glMultiDrawElements");
1083}
1084
1085
1086static void GLAPIENTRY
1087_save_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
1088                                  GLenum type, const GLvoid * const *indices,
1089                                  GLsizei primcount, const GLint *basevertex)
1090{
1091   GET_CURRENT_CONTEXT(ctx);
1092   (void) mode;
1093   (void) count;
1094   (void) type;
1095   (void) indices;
1096   (void) primcount;
1097   (void) basevertex;
1098   _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1099                       "glMultiDrawElementsBaseVertex");
1100}
1101
1102
1103static void GLAPIENTRY
1104_save_DrawTransformFeedback(GLenum mode, GLuint name)
1105{
1106   GET_CURRENT_CONTEXT(ctx);
1107   (void) mode;
1108   (void) name;
1109   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawTransformFeedback");
1110}
1111
1112
1113static void GLAPIENTRY
1114_save_DrawTransformFeedbackStream(GLenum mode, GLuint name, GLuint stream)
1115{
1116   GET_CURRENT_CONTEXT(ctx);
1117   (void) mode;
1118   (void) name;
1119   (void) stream;
1120   _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1121                       "glDrawTransformFeedbackStream");
1122}
1123
1124
1125static void GLAPIENTRY
1126_save_DrawTransformFeedbackInstanced(GLenum mode, GLuint name,
1127                                     GLsizei primcount)
1128{
1129   GET_CURRENT_CONTEXT(ctx);
1130   (void) mode;
1131   (void) name;
1132   (void) primcount;
1133   _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1134                       "glDrawTransformFeedbackInstanced");
1135}
1136
1137
1138static void GLAPIENTRY
1139_save_DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint name,
1140                                           GLuint stream, GLsizei primcount)
1141{
1142   GET_CURRENT_CONTEXT(ctx);
1143   (void) mode;
1144   (void) name;
1145   (void) stream;
1146   (void) primcount;
1147   _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1148                       "glDrawTransformFeedbackStreamInstanced");
1149}
1150
1151
1152static void GLAPIENTRY
1153_save_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
1154{
1155   GET_CURRENT_CONTEXT(ctx);
1156   (void) x1;
1157   (void) y1;
1158   (void) x2;
1159   (void) y2;
1160   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glRectf");
1161}
1162
1163
1164static void GLAPIENTRY
1165_save_EvalMesh1(GLenum mode, GLint i1, GLint i2)
1166{
1167   GET_CURRENT_CONTEXT(ctx);
1168   (void) mode;
1169   (void) i1;
1170   (void) i2;
1171   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glEvalMesh1");
1172}
1173
1174
1175static void GLAPIENTRY
1176_save_EvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)
1177{
1178   GET_CURRENT_CONTEXT(ctx);
1179   (void) mode;
1180   (void) i1;
1181   (void) i2;
1182   (void) j1;
1183   (void) j2;
1184   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glEvalMesh2");
1185}
1186
1187
1188static void GLAPIENTRY
1189_save_Begin(GLenum mode)
1190{
1191   GET_CURRENT_CONTEXT(ctx);
1192   (void) mode;
1193   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "Recursive glBegin");
1194}
1195
1196
1197static void GLAPIENTRY
1198_save_PrimitiveRestartNV(void)
1199{
1200   GLenum curPrim;
1201   GET_CURRENT_CONTEXT(ctx);
1202
1203   curPrim = ctx->Driver.CurrentSavePrimitive;
1204
1205   _save_End();
1206   _save_Begin(curPrim);
1207}
1208
1209
1210/* Unlike the functions above, these are to be hooked into the vtxfmt
1211 * maintained in ctx->ListState, active when the list is known or
1212 * suspected to be outside any begin/end primitive.
1213 * Note: OBE = Outside Begin/End
1214 */
1215static void GLAPIENTRY
1216_save_OBE_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
1217{
1218   GET_CURRENT_CONTEXT(ctx);
1219   vbo_save_NotifyBegin(ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK);
1220   CALL_Vertex2f(GET_DISPATCH(), (x1, y1));
1221   CALL_Vertex2f(GET_DISPATCH(), (x2, y1));
1222   CALL_Vertex2f(GET_DISPATCH(), (x2, y2));
1223   CALL_Vertex2f(GET_DISPATCH(), (x1, y2));
1224   CALL_End(GET_DISPATCH(), ());
1225}
1226
1227
1228static void GLAPIENTRY
1229_save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
1230{
1231   GET_CURRENT_CONTEXT(ctx);
1232   struct vbo_save_context *save = &vbo_context(ctx)->save;
1233   GLint i;
1234
1235   if (!_mesa_validate_DrawArrays(ctx, mode, start, count))
1236      return;
1237
1238   if (save->out_of_memory)
1239      return;
1240
1241   _ae_map_vbos(ctx);
1242
1243   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
1244                              | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1245
1246   for (i = 0; i < count; i++)
1247      CALL_ArrayElement(GET_DISPATCH(), (start + i));
1248   CALL_End(GET_DISPATCH(), ());
1249
1250   _ae_unmap_vbos(ctx);
1251}
1252
1253
1254/* Could do better by copying the arrays and element list intact and
1255 * then emitting an indexed prim at runtime.
1256 */
1257static void GLAPIENTRY
1258_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1259                       const GLvoid * indices)
1260{
1261   GET_CURRENT_CONTEXT(ctx);
1262   struct vbo_save_context *save = &vbo_context(ctx)->save;
1263   GLint i;
1264
1265   if (!_mesa_validate_DrawElements(ctx, mode, count, type, indices, 0))
1266      return;
1267
1268   if (save->out_of_memory)
1269      return;
1270
1271   _ae_map_vbos(ctx);
1272
1273   if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj))
1274      indices =
1275         ADD_POINTERS(ctx->Array.ArrayObj->ElementArrayBufferObj->Pointer, indices);
1276
1277   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
1278                              VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
1279
1280   switch (type) {
1281   case GL_UNSIGNED_BYTE:
1282      for (i = 0; i < count; i++)
1283         CALL_ArrayElement(GET_DISPATCH(), (((GLubyte *) indices)[i]));
1284      break;
1285   case GL_UNSIGNED_SHORT:
1286      for (i = 0; i < count; i++)
1287         CALL_ArrayElement(GET_DISPATCH(), (((GLushort *) indices)[i]));
1288      break;
1289   case GL_UNSIGNED_INT:
1290      for (i = 0; i < count; i++)
1291         CALL_ArrayElement(GET_DISPATCH(), (((GLuint *) indices)[i]));
1292      break;
1293   default:
1294      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
1295      break;
1296   }
1297
1298   CALL_End(GET_DISPATCH(), ());
1299
1300   _ae_unmap_vbos(ctx);
1301}
1302
1303
1304static void GLAPIENTRY
1305_save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1306                            GLsizei count, GLenum type,
1307                            const GLvoid * indices)
1308{
1309   GET_CURRENT_CONTEXT(ctx);
1310   struct vbo_save_context *save = &vbo_context(ctx)->save;
1311
1312   if (!_mesa_validate_DrawRangeElements(ctx, mode,
1313                                         start, end, count, type, indices, 0))
1314      return;
1315
1316   if (save->out_of_memory)
1317      return;
1318
1319   _save_OBE_DrawElements(mode, count, type, indices);
1320}
1321
1322
1323static void GLAPIENTRY
1324_save_OBE_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
1325                            const GLvoid **indices, GLsizei primcount)
1326{
1327   GLsizei i;
1328
1329   for (i = 0; i < primcount; i++) {
1330      if (count[i] > 0) {
1331	 CALL_DrawElements(GET_DISPATCH(), (mode, count[i], type, indices[i]));
1332      }
1333   }
1334}
1335
1336
1337static void GLAPIENTRY
1338_save_OBE_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
1339                                      GLenum type,
1340                                      const GLvoid * const *indices,
1341                                      GLsizei primcount,
1342                                      const GLint *basevertex)
1343{
1344   GLsizei i;
1345
1346   for (i = 0; i < primcount; i++) {
1347      if (count[i] > 0) {
1348	 CALL_DrawElementsBaseVertex(GET_DISPATCH(), (mode, count[i], type,
1349						      indices[i],
1350						      basevertex[i]));
1351      }
1352   }
1353}
1354
1355
1356static void
1357_save_vtxfmt_init(struct gl_context *ctx)
1358{
1359   struct vbo_save_context *save = &vbo_context(ctx)->save;
1360   GLvertexformat *vfmt = &save->vtxfmt;
1361
1362   _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_);
1363
1364   vfmt->Begin = _save_Begin;
1365   vfmt->Color3f = _save_Color3f;
1366   vfmt->Color3fv = _save_Color3fv;
1367   vfmt->Color4f = _save_Color4f;
1368   vfmt->Color4fv = _save_Color4fv;
1369   vfmt->EdgeFlag = _save_EdgeFlag;
1370   vfmt->End = _save_End;
1371   vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV;
1372   vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1373   vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1374   vfmt->Indexf = _save_Indexf;
1375   vfmt->Indexfv = _save_Indexfv;
1376   vfmt->Materialfv = _save_Materialfv;
1377   vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1378   vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1379   vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1380   vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1381   vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1382   vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1383   vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1384   vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1385   vfmt->Normal3f = _save_Normal3f;
1386   vfmt->Normal3fv = _save_Normal3fv;
1387   vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1388   vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1389   vfmt->TexCoord1f = _save_TexCoord1f;
1390   vfmt->TexCoord1fv = _save_TexCoord1fv;
1391   vfmt->TexCoord2f = _save_TexCoord2f;
1392   vfmt->TexCoord2fv = _save_TexCoord2fv;
1393   vfmt->TexCoord3f = _save_TexCoord3f;
1394   vfmt->TexCoord3fv = _save_TexCoord3fv;
1395   vfmt->TexCoord4f = _save_TexCoord4f;
1396   vfmt->TexCoord4fv = _save_TexCoord4fv;
1397   vfmt->Vertex2f = _save_Vertex2f;
1398   vfmt->Vertex2fv = _save_Vertex2fv;
1399   vfmt->Vertex3f = _save_Vertex3f;
1400   vfmt->Vertex3fv = _save_Vertex3fv;
1401   vfmt->Vertex4f = _save_Vertex4f;
1402   vfmt->Vertex4fv = _save_Vertex4fv;
1403   vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1404   vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1405   vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1406   vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1407   vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1408   vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1409   vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1410   vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1411
1412   vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1413   vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1414   vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1415   vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1416   vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1417   vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1418   vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1419   vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1420
1421   /* integer-valued */
1422   vfmt->VertexAttribI1i = _save_VertexAttribI1i;
1423   vfmt->VertexAttribI2i = _save_VertexAttribI2i;
1424   vfmt->VertexAttribI3i = _save_VertexAttribI3i;
1425   vfmt->VertexAttribI4i = _save_VertexAttribI4i;
1426   vfmt->VertexAttribI2iv = _save_VertexAttribI2iv;
1427   vfmt->VertexAttribI3iv = _save_VertexAttribI3iv;
1428   vfmt->VertexAttribI4iv = _save_VertexAttribI4iv;
1429
1430   /* unsigned integer-valued */
1431   vfmt->VertexAttribI1ui = _save_VertexAttribI1ui;
1432   vfmt->VertexAttribI2ui = _save_VertexAttribI2ui;
1433   vfmt->VertexAttribI3ui = _save_VertexAttribI3ui;
1434   vfmt->VertexAttribI4ui = _save_VertexAttribI4ui;
1435   vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv;
1436   vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv;
1437   vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv;
1438
1439   vfmt->VertexP2ui = _save_VertexP2ui;
1440   vfmt->VertexP3ui = _save_VertexP3ui;
1441   vfmt->VertexP4ui = _save_VertexP4ui;
1442   vfmt->VertexP2uiv = _save_VertexP2uiv;
1443   vfmt->VertexP3uiv = _save_VertexP3uiv;
1444   vfmt->VertexP4uiv = _save_VertexP4uiv;
1445
1446   vfmt->TexCoordP1ui = _save_TexCoordP1ui;
1447   vfmt->TexCoordP2ui = _save_TexCoordP2ui;
1448   vfmt->TexCoordP3ui = _save_TexCoordP3ui;
1449   vfmt->TexCoordP4ui = _save_TexCoordP4ui;
1450   vfmt->TexCoordP1uiv = _save_TexCoordP1uiv;
1451   vfmt->TexCoordP2uiv = _save_TexCoordP2uiv;
1452   vfmt->TexCoordP3uiv = _save_TexCoordP3uiv;
1453   vfmt->TexCoordP4uiv = _save_TexCoordP4uiv;
1454
1455   vfmt->MultiTexCoordP1ui = _save_MultiTexCoordP1ui;
1456   vfmt->MultiTexCoordP2ui = _save_MultiTexCoordP2ui;
1457   vfmt->MultiTexCoordP3ui = _save_MultiTexCoordP3ui;
1458   vfmt->MultiTexCoordP4ui = _save_MultiTexCoordP4ui;
1459   vfmt->MultiTexCoordP1uiv = _save_MultiTexCoordP1uiv;
1460   vfmt->MultiTexCoordP2uiv = _save_MultiTexCoordP2uiv;
1461   vfmt->MultiTexCoordP3uiv = _save_MultiTexCoordP3uiv;
1462   vfmt->MultiTexCoordP4uiv = _save_MultiTexCoordP4uiv;
1463
1464   vfmt->NormalP3ui = _save_NormalP3ui;
1465   vfmt->NormalP3uiv = _save_NormalP3uiv;
1466
1467   vfmt->ColorP3ui = _save_ColorP3ui;
1468   vfmt->ColorP4ui = _save_ColorP4ui;
1469   vfmt->ColorP3uiv = _save_ColorP3uiv;
1470   vfmt->ColorP4uiv = _save_ColorP4uiv;
1471
1472   vfmt->SecondaryColorP3ui = _save_SecondaryColorP3ui;
1473   vfmt->SecondaryColorP3uiv = _save_SecondaryColorP3uiv;
1474
1475   vfmt->VertexAttribP1ui = _save_VertexAttribP1ui;
1476   vfmt->VertexAttribP2ui = _save_VertexAttribP2ui;
1477   vfmt->VertexAttribP3ui = _save_VertexAttribP3ui;
1478   vfmt->VertexAttribP4ui = _save_VertexAttribP4ui;
1479
1480   vfmt->VertexAttribP1uiv = _save_VertexAttribP1uiv;
1481   vfmt->VertexAttribP2uiv = _save_VertexAttribP2uiv;
1482   vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv;
1483   vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv;
1484
1485   /* This will all require us to fallback to saving the list as opcodes:
1486    */
1487   _MESA_INIT_DLIST_VTXFMT(vfmt, _save_);       /* inside begin/end */
1488
1489   _MESA_INIT_EVAL_VTXFMT(vfmt, _save_);
1490
1491   /* These calls all generate GL_INVALID_OPERATION since this vtxfmt is
1492    * only used when we're inside a glBegin/End pair.
1493    */
1494   vfmt->Begin = _save_Begin;
1495   vfmt->Rectf = _save_Rectf;
1496   vfmt->DrawArrays = _save_DrawArrays;
1497   vfmt->DrawElements = _save_DrawElements;
1498   vfmt->DrawRangeElements = _save_DrawRangeElements;
1499   vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex;
1500   vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex;
1501   vfmt->MultiDrawElementsEXT = _save_MultiDrawElements;
1502   vfmt->MultiDrawElementsBaseVertex = _save_MultiDrawElementsBaseVertex;
1503   vfmt->DrawTransformFeedback = _save_DrawTransformFeedback;
1504   vfmt->DrawTransformFeedbackStream = _save_DrawTransformFeedbackStream;
1505   vfmt->DrawTransformFeedbackInstanced = _save_DrawTransformFeedbackInstanced;
1506   vfmt->DrawTransformFeedbackStreamInstanced =
1507         _save_DrawTransformFeedbackStreamInstanced;
1508}
1509
1510
1511void
1512vbo_save_SaveFlushVertices(struct gl_context *ctx)
1513{
1514   struct vbo_save_context *save = &vbo_context(ctx)->save;
1515
1516   /* Noop when we are actually active:
1517    */
1518   if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
1519       ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
1520      return;
1521
1522   if (save->vert_count || save->prim_count)
1523      _save_compile_vertex_list(ctx);
1524
1525   _save_copy_to_current(ctx);
1526   _save_reset_vertex(ctx);
1527   _save_reset_counters(ctx);
1528   ctx->Driver.SaveNeedFlush = 0;
1529}
1530
1531
1532void
1533vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
1534{
1535   struct vbo_save_context *save = &vbo_context(ctx)->save;
1536
1537   (void) list;
1538   (void) mode;
1539
1540   if (!save->prim_store)
1541      save->prim_store = alloc_prim_store(ctx);
1542
1543   if (!save->vertex_store)
1544      save->vertex_store = alloc_vertex_store(ctx);
1545
1546   save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
1547
1548   _save_reset_vertex(ctx);
1549   _save_reset_counters(ctx);
1550   ctx->Driver.SaveNeedFlush = 0;
1551}
1552
1553
1554void
1555vbo_save_EndList(struct gl_context *ctx)
1556{
1557   struct vbo_save_context *save = &vbo_context(ctx)->save;
1558
1559   /* EndList called inside a (saved) Begin/End pair?
1560    */
1561   if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) {
1562
1563      if (save->prim_count > 0) {
1564         GLint i = save->prim_count - 1;
1565         ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1566         save->prim[i].end = 0;
1567         save->prim[i].count = (save->vert_count - save->prim[i].start);
1568      }
1569
1570      /* Make sure this vertex list gets replayed by the "loopback"
1571       * mechanism:
1572       */
1573      save->dangling_attr_ref = 1;
1574      vbo_save_SaveFlushVertices(ctx);
1575
1576      /* Swap out this vertex format while outside begin/end.  Any color,
1577       * etc. received between here and the next begin will be compiled
1578       * as opcodes.
1579       */
1580      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1581   }
1582
1583   vbo_save_unmap_vertex_store(ctx, save->vertex_store);
1584
1585   assert(save->vertex_size == 0);
1586}
1587
1588
1589void
1590vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
1591{
1592   struct vbo_save_context *save = &vbo_context(ctx)->save;
1593   save->replay_flags |= dlist->Flags;
1594}
1595
1596
1597void
1598vbo_save_EndCallList(struct gl_context *ctx)
1599{
1600   struct vbo_save_context *save = &vbo_context(ctx)->save;
1601
1602   if (ctx->ListState.CallDepth == 1) {
1603      /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1604       * flag, if it is set:
1605       */
1606      save->replay_flags &= VBO_SAVE_FALLBACK;
1607   }
1608}
1609
1610
1611static void
1612vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
1613{
1614   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1615   (void) ctx;
1616
1617   if (--node->vertex_store->refcount == 0)
1618      free_vertex_store(ctx, node->vertex_store);
1619
1620   if (--node->prim_store->refcount == 0)
1621      free(node->prim_store);
1622
1623   free(node->current_data);
1624   node->current_data = NULL;
1625}
1626
1627
1628static void
1629vbo_print_vertex_list(struct gl_context *ctx, void *data)
1630{
1631   struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
1632   GLuint i;
1633   (void) ctx;
1634
1635   printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1636          node->count, node->prim_count, node->vertex_size);
1637
1638   for (i = 0; i < node->prim_count; i++) {
1639      struct _mesa_prim *prim = &node->prim[i];
1640      printf("   prim %d: %s%s %d..%d %s %s\n",
1641             i,
1642             _mesa_lookup_prim_by_nr(prim->mode),
1643             prim->weak ? " (weak)" : "",
1644             prim->start,
1645             prim->start + prim->count,
1646             (prim->begin) ? "BEGIN" : "(wrap)",
1647             (prim->end) ? "END" : "(wrap)");
1648   }
1649}
1650
1651
1652/**
1653 * Called during context creation/init.
1654 */
1655static void
1656_save_current_init(struct gl_context *ctx)
1657{
1658   struct vbo_save_context *save = &vbo_context(ctx)->save;
1659   GLint i;
1660
1661   for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1662      const GLuint j = i - VBO_ATTRIB_POS;
1663      ASSERT(j < VERT_ATTRIB_MAX);
1664      save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1665      save->current[i] = ctx->ListState.CurrentAttrib[j];
1666   }
1667
1668   for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1669      const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1670      ASSERT(j < MAT_ATTRIB_MAX);
1671      save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1672      save->current[i] = ctx->ListState.CurrentMaterial[j];
1673   }
1674}
1675
1676
1677/**
1678 * Initialize the display list compiler.  Called during context creation.
1679 */
1680void
1681vbo_save_api_init(struct vbo_save_context *save)
1682{
1683   struct gl_context *ctx = save->ctx;
1684   GLuint i;
1685
1686   save->opcode_vertex_list =
1687      _mesa_dlist_alloc_opcode(ctx,
1688                               sizeof(struct vbo_save_vertex_list),
1689                               vbo_save_playback_vertex_list,
1690                               vbo_destroy_vertex_list,
1691                               vbo_print_vertex_list);
1692
1693   ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1694
1695   _save_vtxfmt_init(ctx);
1696   _save_current_init(ctx);
1697   _mesa_noop_vtxfmt_init(&save->vtxfmt_noop);
1698
1699   /* These will actually get set again when binding/drawing */
1700   for (i = 0; i < VBO_ATTRIB_MAX; i++)
1701      save->inputs[i] = &save->arrays[i];
1702
1703   /* Hook our array functions into the outside-begin-end vtxfmt in
1704    * ctx->ListState.
1705    */
1706   ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1707   ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1708   ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1709   ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1710   ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = _save_OBE_MultiDrawElements;
1711   ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex = _save_OBE_MultiDrawElementsBaseVertex;
1712}
1713