vbo_save_api.c revision 7ec681f3
1/**************************************************************************
2
3Copyright 2002-2008 VMware, Inc.
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
21VMWARE 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 <keithw@vmware.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 * The compilation process works as follows. All vertex attributes
69 * except position are copied to vbo_save_context::attrptr (see ATTR_UNION).
70 * 'attrptr' are pointers to vbo_save_context::vertex ordered according to the enabled
71 * attributes (se upgrade_vertex).
72 * When the position attribute is received, all the attributes are then
73 * copied to the vertex_store (see the end of ATTR_UNION).
74 * The vertex_store is simply an extensible float array.
75 * When the vertex list needs to be compiled (see compile_vertex_list),
76 * several transformations are performed:
77 *   - some primitives are merged together (eg: two consecutive GL_TRIANGLES
78 * with 3 vertices can be merged in a single GL_TRIANGLES with 6 vertices).
79 *   - an index buffer is built.
80 *   - identical vertices are detected and only one is kept.
81 * At the end of this transformation, the index buffer and the vertex buffer
82 * are uploaded in vRAM in the same buffer object.
83 * This buffer object is shared between multiple display list to allow
84 * draw calls merging later.
85 *
86 * The layout of this buffer for two display lists is:
87 *    V0A0|V0A1|V1A0|V1A1|P0I0|P0I1|V0A0V0A1V0A2|V1A1V1A1V1A2|...
88 *                                 ` new list starts
89 *        - VxAy: vertex x, attributes y
90 *        - PxIy: draw x, index y
91 *
92 * To allow draw call merging, display list must use the same VAO, including
93 * the same Offset in the buffer object. To achieve this, the start values of
94 * the primitive are shifted and the indices adjusted (see offset_diff and
95 * start_offset in compile_vertex_list).
96 *
97 * Display list using the loopback code (see vbo_save_playback_vertex_list_loopback),
98 * can't be drawn with an index buffer so this transformation is disabled
99 * in this case.
100 */
101
102
103#include "main/glheader.h"
104#include "main/arrayobj.h"
105#include "main/bufferobj.h"
106#include "main/context.h"
107#include "main/dlist.h"
108#include "main/enums.h"
109#include "main/eval.h"
110#include "main/macros.h"
111#include "main/draw_validate.h"
112#include "main/api_arrayelt.h"
113#include "main/vtxfmt.h"
114#include "main/dispatch.h"
115#include "main/state.h"
116#include "main/varray.h"
117#include "util/bitscan.h"
118#include "util/u_memory.h"
119#include "util/hash_table.h"
120#include "util/u_prim.h"
121
122#include "gallium/include/pipe/p_state.h"
123
124#include "vbo_noop.h"
125#include "vbo_private.h"
126
127
128#ifdef ERROR
129#undef ERROR
130#endif
131
132/* An interesting VBO number/name to help with debugging */
133#define VBO_BUF_ID  12345
134
135static void GLAPIENTRY
136_save_Materialfv(GLenum face, GLenum pname, const GLfloat *params);
137
138static void GLAPIENTRY
139_save_EvalCoord1f(GLfloat u);
140
141static void GLAPIENTRY
142_save_EvalCoord2f(GLfloat u, GLfloat v);
143
144static void
145handle_out_of_memory(struct gl_context *ctx)
146{
147   struct vbo_save_context *save = &vbo_context(ctx)->save;
148   _mesa_noop_vtxfmt_init(ctx, &save->vtxfmt);
149   save->out_of_memory = true;
150}
151
152/*
153 * NOTE: Old 'parity' issue is gone, but copying can still be
154 * wrong-footed on replay.
155 */
156static GLuint
157copy_vertices(struct gl_context *ctx,
158              const struct vbo_save_vertex_list *node,
159              const fi_type * src_buffer)
160{
161   struct vbo_save_context *save = &vbo_context(ctx)->save;
162   struct _mesa_prim *prim = &node->cold->prims[node->cold->prim_count - 1];
163   GLuint sz = save->vertex_size;
164
165   if (prim->end || !prim->count || !sz)
166      return 0;
167
168   const fi_type *src = src_buffer + prim->start * sz;
169   assert(save->copied.buffer == NULL);
170   save->copied.buffer = malloc(sizeof(fi_type) * sz * prim->count);
171
172   unsigned r = vbo_copy_vertices(ctx, prim->mode, prim->start, &prim->count,
173                                  prim->begin, sz, true, save->copied.buffer, src);
174   if (!r) {
175      free(save->copied.buffer);
176      save->copied.buffer = NULL;
177   }
178   return r;
179}
180
181
182static struct vbo_save_primitive_store *
183realloc_prim_store(struct vbo_save_primitive_store *store, int prim_count)
184{
185   if (store == NULL)
186      store = CALLOC_STRUCT(vbo_save_primitive_store);
187
188   uint32_t old_size = store->size;
189   store->size = prim_count;
190   assert (old_size < store->size);
191   store->prims = realloc(store->prims, store->size * sizeof(struct _mesa_prim));
192   memset(&store->prims[old_size], 0, (store->size - old_size) * sizeof(struct _mesa_prim));
193
194   return store;
195}
196
197
198static void
199reset_counters(struct gl_context *ctx)
200{
201   struct vbo_save_context *save = &vbo_context(ctx)->save;
202
203   save->vertex_store->used = 0;
204   save->prim_store->used = 0;
205   save->dangling_attr_ref = GL_FALSE;
206}
207
208/**
209 * For a list of prims, try merging prims that can just be extensions of the
210 * previous prim.
211 */
212static void
213merge_prims(struct gl_context *ctx, struct _mesa_prim *prim_list,
214            GLuint *prim_count)
215{
216   GLuint i;
217   struct _mesa_prim *prev_prim = prim_list;
218
219   for (i = 1; i < *prim_count; i++) {
220      struct _mesa_prim *this_prim = prim_list + i;
221
222      vbo_try_prim_conversion(&this_prim->mode, &this_prim->count);
223
224      if (vbo_merge_draws(ctx, true,
225                          prev_prim->mode, this_prim->mode,
226                          prev_prim->start, this_prim->start,
227                          &prev_prim->count, this_prim->count,
228                          prev_prim->basevertex, this_prim->basevertex,
229                          &prev_prim->end,
230                          this_prim->begin, this_prim->end)) {
231         /* We've found a prim that just extend the previous one.  Tack it
232          * onto the previous one, and let this primitive struct get dropped.
233          */
234         continue;
235      }
236
237      /* If any previous primitives have been dropped, then we need to copy
238       * this later one into the next available slot.
239       */
240      prev_prim++;
241      if (prev_prim != this_prim)
242         *prev_prim = *this_prim;
243   }
244
245   *prim_count = prev_prim - prim_list + 1;
246}
247
248
249/**
250 * Convert GL_LINE_LOOP primitive into GL_LINE_STRIP so that drivers
251 * don't have to worry about handling the _mesa_prim::begin/end flags.
252 * See https://bugs.freedesktop.org/show_bug.cgi?id=81174
253 */
254static void
255convert_line_loop_to_strip(struct vbo_save_context *save,
256                           struct vbo_save_vertex_list *node)
257{
258   struct _mesa_prim *prim = &node->cold->prims[node->cold->prim_count - 1];
259
260   assert(prim->mode == GL_LINE_LOOP);
261
262   if (prim->end) {
263      /* Copy the 0th vertex to end of the buffer and extend the
264       * vertex count by one to finish the line loop.
265       */
266      const GLuint sz = save->vertex_size;
267      /* 0th vertex: */
268      const fi_type *src = save->vertex_store->buffer_in_ram + prim->start * sz;
269      /* end of buffer: */
270      fi_type *dst = save->vertex_store->buffer_in_ram + (prim->start + prim->count) * sz;
271
272      memcpy(dst, src, sz * sizeof(float));
273
274      prim->count++;
275      node->cold->vertex_count++;
276      save->vertex_store->used += sz;
277   }
278
279   if (!prim->begin) {
280      /* Drawing the second or later section of a long line loop.
281       * Skip the 0th vertex.
282       */
283      prim->start++;
284      prim->count--;
285   }
286
287   prim->mode = GL_LINE_STRIP;
288}
289
290
291/* Compare the present vao if it has the same setup. */
292static bool
293compare_vao(gl_vertex_processing_mode mode,
294            const struct gl_vertex_array_object *vao,
295            const struct gl_buffer_object *bo, GLintptr buffer_offset,
296            GLuint stride, GLbitfield64 vao_enabled,
297            const GLubyte size[VBO_ATTRIB_MAX],
298            const GLenum16 type[VBO_ATTRIB_MAX],
299            const GLuint offset[VBO_ATTRIB_MAX])
300{
301   if (!vao)
302      return false;
303
304   /* If the enabled arrays are not the same we are not equal. */
305   if (vao_enabled != vao->Enabled)
306      return false;
307
308   /* Check the buffer binding at 0 */
309   if (vao->BufferBinding[0].BufferObj != bo)
310      return false;
311   /* BufferBinding[0].Offset != buffer_offset is checked per attribute */
312   if (vao->BufferBinding[0].Stride != stride)
313      return false;
314   assert(vao->BufferBinding[0].InstanceDivisor == 0);
315
316   /* Retrieve the mapping from VBO_ATTRIB to VERT_ATTRIB space */
317   const GLubyte *const vao_to_vbo_map = _vbo_attribute_alias_map[mode];
318
319   /* Now check the enabled arrays */
320   GLbitfield mask = vao_enabled;
321   while (mask) {
322      const int attr = u_bit_scan(&mask);
323      const unsigned char vbo_attr = vao_to_vbo_map[attr];
324      const GLenum16 tp = type[vbo_attr];
325      const GLintptr off = offset[vbo_attr] + buffer_offset;
326      const struct gl_array_attributes *attrib = &vao->VertexAttrib[attr];
327      if (attrib->RelativeOffset + vao->BufferBinding[0].Offset != off)
328         return false;
329      if (attrib->Format.Type != tp)
330         return false;
331      if (attrib->Format.Size != size[vbo_attr])
332         return false;
333      assert(attrib->Format.Format == GL_RGBA);
334      assert(attrib->Format.Normalized == GL_FALSE);
335      assert(attrib->Format.Integer == vbo_attrtype_to_integer_flag(tp));
336      assert(attrib->Format.Doubles == vbo_attrtype_to_double_flag(tp));
337      assert(attrib->BufferBindingIndex == 0);
338   }
339
340   return true;
341}
342
343
344/* Create or reuse the vao for the vertex processing mode. */
345static void
346update_vao(struct gl_context *ctx,
347           gl_vertex_processing_mode mode,
348           struct gl_vertex_array_object **vao,
349           struct gl_buffer_object *bo, GLintptr buffer_offset,
350           GLuint stride, GLbitfield64 vbo_enabled,
351           const GLubyte size[VBO_ATTRIB_MAX],
352           const GLenum16 type[VBO_ATTRIB_MAX],
353           const GLuint offset[VBO_ATTRIB_MAX])
354{
355   /* Compute the bitmasks of vao_enabled arrays */
356   GLbitfield vao_enabled = _vbo_get_vao_enabled_from_vbo(mode, vbo_enabled);
357
358   /*
359    * Check if we can possibly reuse the exisiting one.
360    * In the long term we should reset them when something changes.
361    */
362   if (compare_vao(mode, *vao, bo, buffer_offset, stride,
363                   vao_enabled, size, type, offset))
364      return;
365
366   /* The initial refcount is 1 */
367   _mesa_reference_vao(ctx, vao, NULL);
368   *vao = _mesa_new_vao(ctx, ~((GLuint)0));
369
370   /*
371    * assert(stride <= ctx->Const.MaxVertexAttribStride);
372    * MaxVertexAttribStride is not set for drivers that does not
373    * expose GL 44 or GLES 31.
374    */
375
376   /* Bind the buffer object at binding point 0 */
377   _mesa_bind_vertex_buffer(ctx, *vao, 0, bo, buffer_offset, stride, false,
378                            false);
379
380   /* Retrieve the mapping from VBO_ATTRIB to VERT_ATTRIB space
381    * Note that the position/generic0 aliasing is done in the VAO.
382    */
383   const GLubyte *const vao_to_vbo_map = _vbo_attribute_alias_map[mode];
384   /* Now set the enable arrays */
385   GLbitfield mask = vao_enabled;
386   while (mask) {
387      const int vao_attr = u_bit_scan(&mask);
388      const GLubyte vbo_attr = vao_to_vbo_map[vao_attr];
389      assert(offset[vbo_attr] <= ctx->Const.MaxVertexAttribRelativeOffset);
390
391      _vbo_set_attrib_format(ctx, *vao, vao_attr, buffer_offset,
392                             size[vbo_attr], type[vbo_attr], offset[vbo_attr]);
393      _mesa_vertex_attrib_binding(ctx, *vao, vao_attr, 0);
394   }
395   _mesa_enable_vertex_array_attribs(ctx, *vao, vao_enabled);
396   assert(vao_enabled == (*vao)->Enabled);
397   assert((vao_enabled & ~(*vao)->VertexAttribBufferMask) == 0);
398
399   /* Finalize and freeze the VAO */
400   _mesa_set_vao_immutable(ctx, *vao);
401}
402
403static void wrap_filled_vertex(struct gl_context *ctx);
404
405/* Grow the vertex storage to accomodate for vertex_count new vertices */
406static void
407grow_vertex_storage(struct gl_context *ctx, int vertex_count)
408{
409   struct vbo_save_context *save = &vbo_context(ctx)->save;
410   assert (save->vertex_store);
411
412   int new_size = (save->vertex_store->used +
413                   vertex_count * save->vertex_size) * sizeof(GLfloat);
414
415   /* Limit how much memory we allocate. */
416   if (save->prim_store->used > 0 &&
417       vertex_count > 0 &&
418       new_size > VBO_SAVE_BUFFER_SIZE) {
419      wrap_filled_vertex(ctx);
420      new_size = VBO_SAVE_BUFFER_SIZE;
421   }
422
423   if (new_size > save->vertex_store->buffer_in_ram_size) {
424      save->vertex_store->buffer_in_ram_size = new_size;
425      save->vertex_store->buffer_in_ram = realloc(save->vertex_store->buffer_in_ram,
426                                                  save->vertex_store->buffer_in_ram_size);
427      if (save->vertex_store->buffer_in_ram == NULL)
428         handle_out_of_memory(ctx);
429   }
430
431}
432
433struct vertex_key {
434   unsigned vertex_size;
435   fi_type *vertex_attributes;
436};
437
438static uint32_t _hash_vertex_key(const void *key)
439{
440   struct vertex_key *k = (struct vertex_key*)key;
441   unsigned sz = k->vertex_size;
442   assert(sz);
443   return _mesa_hash_data(k->vertex_attributes, sz * sizeof(float));
444}
445
446static bool _compare_vertex_key(const void *key1, const void *key2)
447{
448   struct vertex_key *k1 = (struct vertex_key*)key1;
449   struct vertex_key *k2 = (struct vertex_key*)key2;
450   /* All the compared vertices are going to be drawn with the same VAO,
451    * so we can compare the attributes. */
452   assert (k1->vertex_size == k2->vertex_size);
453   return memcmp(k1->vertex_attributes,
454                 k2->vertex_attributes,
455                 k1->vertex_size * sizeof(float)) == 0;
456}
457
458static void _free_entry(struct hash_entry *entry)
459{
460   free((void*)entry->key);
461}
462
463/* Add vertex to the vertex buffer and return its index. If this vertex is a duplicate
464 * of an existing vertex, return the original index instead.
465 */
466static uint32_t
467add_vertex(struct vbo_save_context *save, struct hash_table *hash_to_index,
468           uint32_t index, fi_type *new_buffer, uint32_t *max_index)
469{
470   /* If vertex deduplication is disabled return the original index. */
471   if (!hash_to_index)
472      return index;
473
474   fi_type *vert = save->vertex_store->buffer_in_ram + save->vertex_size * index;
475
476   struct vertex_key *key = malloc(sizeof(struct vertex_key));
477   key->vertex_size = save->vertex_size;
478   key->vertex_attributes = vert;
479
480   struct hash_entry *entry = _mesa_hash_table_search(hash_to_index, key);
481   if (entry) {
482      free(key);
483      /* We found an existing vertex with the same hash, return its index. */
484      return (uintptr_t) entry->data;
485   } else {
486      /* This is a new vertex. Determine a new index and copy its attributes to the vertex
487       * buffer. Note that 'new_buffer' is created at each list compilation so we write vertices
488       * starting at index 0.
489       */
490      uint32_t n = _mesa_hash_table_num_entries(hash_to_index);
491      *max_index = MAX2(n, *max_index);
492
493      memcpy(&new_buffer[save->vertex_size * n],
494             vert,
495             save->vertex_size * sizeof(fi_type));
496
497      _mesa_hash_table_insert(hash_to_index, key, (void*)(uintptr_t)(n));
498
499      /* The index buffer is shared between list compilations, so add the base index to get
500       * the final index.
501       */
502      return n;
503   }
504}
505
506
507static uint32_t
508get_vertex_count(struct vbo_save_context *save)
509{
510   if (!save->vertex_size)
511      return 0;
512   return save->vertex_store->used / save->vertex_size;
513}
514
515
516/**
517 * Insert the active immediate struct onto the display list currently
518 * being built.
519 */
520static void
521compile_vertex_list(struct gl_context *ctx)
522{
523   struct vbo_save_context *save = &vbo_context(ctx)->save;
524   struct vbo_save_vertex_list *node;
525
526   /* Allocate space for this structure in the display list currently
527    * being compiled.
528    */
529   node = (struct vbo_save_vertex_list *)
530      _mesa_dlist_alloc_vertex_list(ctx, !save->dangling_attr_ref && !save->no_current_update);
531
532   if (!node)
533      return;
534
535   memset(node, 0, sizeof(struct vbo_save_vertex_list));
536   node->cold = calloc(1, sizeof(*node->cold));
537
538   /* Make sure the pointer is aligned to the size of a pointer */
539   assert((GLintptr) node % sizeof(void *) == 0);
540
541   const GLsizei stride = save->vertex_size*sizeof(GLfloat);
542
543   node->cold->vertex_count = get_vertex_count(save);
544   node->cold->wrap_count = save->copied.nr;
545   node->cold->prims = malloc(sizeof(struct _mesa_prim) * save->prim_store->used);
546   memcpy(node->cold->prims, save->prim_store->prims, sizeof(struct _mesa_prim) * save->prim_store->used);
547   node->cold->ib.obj = NULL;
548   node->cold->prim_count = save->prim_store->used;
549
550   if (save->no_current_update) {
551      node->cold->current_data = NULL;
552   }
553   else {
554      GLuint current_size = save->vertex_size - save->attrsz[0];
555      node->cold->current_data = NULL;
556
557      if (current_size) {
558         node->cold->current_data = malloc(current_size * sizeof(GLfloat));
559         if (node->cold->current_data) {
560            const char *buffer = (const char *)save->vertex_store->buffer_in_ram;
561            unsigned attr_offset = save->attrsz[0] * sizeof(GLfloat);
562            unsigned vertex_offset = 0;
563
564            if (node->cold->vertex_count)
565               vertex_offset = (node->cold->vertex_count - 1) * stride;
566
567            memcpy(node->cold->current_data, buffer + vertex_offset + attr_offset,
568                   current_size * sizeof(GLfloat));
569         } else {
570            _mesa_error(ctx, GL_OUT_OF_MEMORY, "Current value allocation");
571            handle_out_of_memory(ctx);
572         }
573      }
574   }
575
576   assert(save->attrsz[VBO_ATTRIB_POS] != 0 || node->cold->vertex_count == 0);
577
578   if (save->dangling_attr_ref)
579      ctx->ListState.Current.UseLoopback = true;
580
581   /* Copy duplicated vertices
582    */
583   save->copied.nr = copy_vertices(ctx, node, save->vertex_store->buffer_in_ram);
584
585   if (node->cold->prims[node->cold->prim_count - 1].mode == GL_LINE_LOOP) {
586      convert_line_loop_to_strip(save, node);
587   }
588
589   merge_prims(ctx, node->cold->prims, &node->cold->prim_count);
590
591   GLintptr buffer_offset = 0;
592   GLuint start_offset = 0;
593
594   /* Create an index buffer. */
595   node->cold->min_index = node->cold->max_index = 0;
596   if (node->cold->vertex_count == 0 || node->cold->prim_count == 0)
597      goto end;
598
599   /* We won't modify node->prims, so use a const alias to avoid unintended
600    * writes to it. */
601   const struct _mesa_prim *original_prims = node->cold->prims;
602
603   int end = original_prims[node->cold->prim_count - 1].start +
604             original_prims[node->cold->prim_count - 1].count;
605   int total_vert_count = end - original_prims[0].start;
606
607   node->cold->min_index = node->cold->prims[0].start;
608   node->cold->max_index = end - 1;
609
610   int max_index_count = total_vert_count * 2;
611   uint32_t* indices = (uint32_t*) malloc(max_index_count * sizeof(uint32_t));
612   struct _mesa_prim *merged_prims = NULL;
613
614   int idx = 0;
615   struct hash_table *vertex_to_index = NULL;
616   fi_type *temp_vertices_buffer = NULL;
617
618   /* The loopback replay code doesn't use the index buffer, so we can't
619    * dedup vertices in this case.
620    */
621   if (!ctx->ListState.Current.UseLoopback) {
622      vertex_to_index = _mesa_hash_table_create(NULL, _hash_vertex_key, _compare_vertex_key);
623      temp_vertices_buffer = malloc(save->vertex_store->buffer_in_ram_size);
624   }
625
626   uint32_t max_index = 0;
627
628   int last_valid_prim = -1;
629   /* Construct indices array. */
630   for (unsigned i = 0; i < node->cold->prim_count; i++) {
631      assert(original_prims[i].basevertex == 0);
632      GLubyte mode = original_prims[i].mode;
633
634      int vertex_count = original_prims[i].count;
635      if (!vertex_count) {
636         continue;
637      }
638
639      /* Increase indices storage if the original estimation was too small. */
640      if (idx + 3 * vertex_count > max_index_count) {
641         max_index_count = max_index_count + 3 * vertex_count;
642         indices = (uint32_t*) realloc(indices, max_index_count * sizeof(uint32_t));
643      }
644
645      /* Line strips may get converted to lines */
646      if (mode == GL_LINE_STRIP)
647         mode = GL_LINES;
648
649      /* If 2 consecutive prims use the same mode => merge them. */
650      bool merge_prims = last_valid_prim >= 0 &&
651                         mode == merged_prims[last_valid_prim].mode &&
652                         mode != GL_LINE_LOOP && mode != GL_TRIANGLE_FAN &&
653                         mode != GL_QUAD_STRIP && mode != GL_POLYGON &&
654                         mode != GL_PATCHES;
655
656      /* To be able to merge consecutive triangle strips we need to insert
657       * a degenerate triangle.
658       */
659      if (merge_prims &&
660          mode == GL_TRIANGLE_STRIP) {
661         /* Insert a degenerate triangle */
662         assert(merged_prims[last_valid_prim].mode == GL_TRIANGLE_STRIP);
663         unsigned tri_count = merged_prims[last_valid_prim].count - 2;
664
665         indices[idx] = indices[idx - 1];
666         indices[idx + 1] = add_vertex(save, vertex_to_index, original_prims[i].start,
667                                       temp_vertices_buffer, &max_index);
668         idx += 2;
669         merged_prims[last_valid_prim].count += 2;
670
671         if (tri_count % 2) {
672            /* Add another index to preserve winding order */
673            indices[idx++] = add_vertex(save, vertex_to_index, original_prims[i].start,
674                                        temp_vertices_buffer, &max_index);
675            merged_prims[last_valid_prim].count++;
676         }
677      }
678
679      int start = idx;
680
681      /* Convert line strips to lines if it'll allow if the previous
682       * prim mode is GL_LINES (so merge_prims is true) or if the next
683       * primitive mode is GL_LINES or GL_LINE_LOOP.
684       */
685      if (original_prims[i].mode == GL_LINE_STRIP &&
686          (merge_prims ||
687           (i < node->cold->prim_count - 1 &&
688            (original_prims[i + 1].mode == GL_LINE_STRIP ||
689             original_prims[i + 1].mode == GL_LINES)))) {
690         for (unsigned j = 0; j < vertex_count; j++) {
691            indices[idx++] = add_vertex(save, vertex_to_index, original_prims[i].start + j,
692                                        temp_vertices_buffer, &max_index);
693            /* Repeat all but the first/last indices. */
694            if (j && j != vertex_count - 1) {
695               indices[idx++] = add_vertex(save, vertex_to_index, original_prims[i].start + j,
696                                           temp_vertices_buffer, &max_index);
697            }
698         }
699      } else {
700         /* We didn't convert to LINES, so restore the original mode */
701         mode = original_prims[i].mode;
702
703         for (unsigned j = 0; j < vertex_count; j++) {
704            indices[idx++] = add_vertex(save, vertex_to_index, original_prims[i].start + j,
705                                        temp_vertices_buffer, &max_index);
706         }
707      }
708
709      /* Duplicate the last vertex for incomplete primitives */
710      unsigned min_vert = u_prim_vertex_count(mode)->min;
711      for (unsigned j = vertex_count; j < min_vert; j++) {
712         indices[idx++] = add_vertex(save, vertex_to_index,
713                                     original_prims[i].start + vertex_count - 1,
714                                     temp_vertices_buffer, &max_index);
715      }
716
717      if (merge_prims) {
718         /* Update vertex count. */
719         merged_prims[last_valid_prim].count += idx - start;
720      } else {
721         /* Keep this primitive */
722         last_valid_prim += 1;
723         assert(last_valid_prim <= i);
724         merged_prims = realloc(merged_prims, (1 + last_valid_prim) * sizeof(struct _mesa_prim));
725         merged_prims[last_valid_prim] = original_prims[i];
726         merged_prims[last_valid_prim].start = start;
727         merged_prims[last_valid_prim].count = idx - start;
728      }
729      merged_prims[last_valid_prim].mode = mode;
730   }
731
732   assert(idx > 0 && idx <= max_index_count);
733
734   unsigned merged_prim_count = last_valid_prim + 1;
735   node->cold->ib.ptr = NULL;
736   node->cold->ib.count = idx;
737   node->cold->ib.index_size_shift = (GL_UNSIGNED_INT - GL_UNSIGNED_BYTE) >> 1;
738
739   /* How many bytes do we need to store the indices and the vertices */
740   total_vert_count = vertex_to_index ? (max_index + 1) : idx;
741   unsigned total_bytes_needed = idx * sizeof(uint32_t) +
742                                 total_vert_count * save->vertex_size * sizeof(fi_type);
743
744   const GLintptr old_offset = save->VAO[0] ?
745      save->VAO[0]->BufferBinding[0].Offset + save->VAO[0]->VertexAttrib[VERT_ATTRIB_POS].RelativeOffset : 0;
746   if (old_offset != save->current_bo_bytes_used && stride > 0) {
747      GLintptr offset_diff = save->current_bo_bytes_used - old_offset;
748      while (offset_diff > 0 &&
749             save->current_bo_bytes_used < save->current_bo->Size &&
750             offset_diff % stride != 0) {
751         save->current_bo_bytes_used++;
752         offset_diff = save->current_bo_bytes_used - old_offset;
753      }
754   }
755   buffer_offset = save->current_bo_bytes_used;
756
757   /* Can we reuse the previous bo or should we allocate a new one? */
758   int available_bytes = save->current_bo ? save->current_bo->Size - save->current_bo_bytes_used : 0;
759   if (total_bytes_needed > available_bytes) {
760      if (save->current_bo)
761         _mesa_reference_buffer_object(ctx, &save->current_bo, NULL);
762      save->current_bo = ctx->Driver.NewBufferObject(ctx, VBO_BUF_ID + 1);
763      bool success = ctx->Driver.BufferData(ctx,
764                                            GL_ELEMENT_ARRAY_BUFFER_ARB,
765                                            MAX2(total_bytes_needed, VBO_SAVE_BUFFER_SIZE),
766                                            NULL,
767                                            GL_STATIC_DRAW_ARB, GL_MAP_WRITE_BIT,
768                                            save->current_bo);
769      if (!success) {
770         _mesa_reference_buffer_object(ctx, &save->current_bo, NULL);
771         _mesa_error(ctx, GL_OUT_OF_MEMORY, "IB allocation");
772         handle_out_of_memory(ctx);
773      } else {
774         save->current_bo_bytes_used = 0;
775         available_bytes = save->current_bo->Size;
776      }
777      buffer_offset = 0;
778   } else {
779      assert(old_offset <= buffer_offset);
780      const GLintptr offset_diff = buffer_offset - old_offset;
781      if (offset_diff > 0 && stride > 0 && offset_diff % stride == 0) {
782         /* The vertex size is an exact multiple of the buffer offset.
783          * This means that we can use zero-based vertex attribute pointers
784          * and specify the start of the primitive with the _mesa_prim::start
785          * field.  This results in issuing several draw calls with identical
786          * vertex attribute information.  This can result in fewer state
787          * changes in drivers.  In particular, the Gallium CSO module will
788          * filter out redundant vertex buffer changes.
789          */
790         /* We cannot immediately update the primitives as some methods below
791          * still need the uncorrected start vertices
792          */
793         start_offset = offset_diff/stride;
794         assert(old_offset == buffer_offset - offset_diff);
795         buffer_offset = old_offset;
796      }
797
798      /* Correct the primitive starts, we can only do this here as copy_vertices
799       * and convert_line_loop_to_strip above consume the uncorrected starts.
800       * On the other hand the _vbo_loopback_vertex_list call below needs the
801       * primitives to be corrected already.
802       */
803      for (unsigned i = 0; i < node->cold->prim_count; i++) {
804         node->cold->prims[i].start += start_offset;
805      }
806      /* start_offset shifts vertices (so v[0] becomes v[start_offset]), so we have
807       * to apply this transformation to all indices and max_index.
808       */
809      for (unsigned i = 0; i < idx; i++)
810         indices[i] += start_offset;
811      max_index += start_offset;
812   }
813
814   _mesa_reference_buffer_object(ctx, &node->cold->ib.obj, save->current_bo);
815
816   /* Upload the vertices first (see buffer_offset) */
817   ctx->Driver.BufferSubData(ctx,
818                             save->current_bo_bytes_used,
819                             total_vert_count * save->vertex_size * sizeof(fi_type),
820                             vertex_to_index ? temp_vertices_buffer : save->vertex_store->buffer_in_ram,
821                             node->cold->ib.obj);
822   save->current_bo_bytes_used += total_vert_count * save->vertex_size * sizeof(fi_type);
823
824  if (vertex_to_index) {
825      _mesa_hash_table_destroy(vertex_to_index, _free_entry);
826      free(temp_vertices_buffer);
827   }
828
829   /* Since we append the indices to an existing buffer, we need to adjust the start value of each
830    * primitive (not the indices themselves). */
831   if (!ctx->ListState.Current.UseLoopback) {
832      save->current_bo_bytes_used += align(save->current_bo_bytes_used, 4) - save->current_bo_bytes_used;
833      int indices_offset = save->current_bo_bytes_used / 4;
834      for (int i = 0; i < merged_prim_count; i++) {
835         merged_prims[i].start += indices_offset;
836      }
837   }
838
839   /* Then upload the indices. */
840   if (node->cold->ib.obj) {
841      ctx->Driver.BufferSubData(ctx,
842                                save->current_bo_bytes_used,
843                                idx * sizeof(uint32_t),
844                                indices,
845                                node->cold->ib.obj);
846      save->current_bo_bytes_used += idx * sizeof(uint32_t);
847   } else {
848      node->cold->vertex_count = 0;
849      node->cold->prim_count = 0;
850   }
851
852   /* Prepare for DrawGallium */
853   memset(&node->merged.info, 0, sizeof(struct pipe_draw_info));
854   /* The other info fields will be updated in vbo_save_playback_vertex_list */
855   node->merged.info.index_size = 4;
856   node->merged.info.instance_count = 1;
857   node->merged.info.index.gl_bo = node->cold->ib.obj;
858   if (merged_prim_count == 1) {
859      node->merged.info.mode = merged_prims[0].mode;
860      node->merged.start_count.start = merged_prims[0].start;
861      node->merged.start_count.count = merged_prims[0].count;
862      node->merged.start_count.index_bias = 0;
863      node->merged.mode = NULL;
864   } else {
865      node->merged.mode = malloc(merged_prim_count * sizeof(unsigned char));
866      node->merged.start_counts = malloc(merged_prim_count * sizeof(struct pipe_draw_start_count_bias));
867      for (unsigned i = 0; i < merged_prim_count; i++) {
868         node->merged.start_counts[i].start = merged_prims[i].start;
869         node->merged.start_counts[i].count = merged_prims[i].count;
870         node->merged.start_counts[i].index_bias = 0;
871         node->merged.mode[i] = merged_prims[i].mode;
872      }
873   }
874   node->merged.num_draws = merged_prim_count;
875   if (node->merged.num_draws > 1) {
876      bool same_mode = true;
877      for (unsigned i = 1; i < node->merged.num_draws && same_mode; i++) {
878         same_mode = node->merged.mode[i] == node->merged.mode[0];
879      }
880      if (same_mode) {
881         /* All primitives use the same mode, so we can simplify a bit */
882         node->merged.info.mode = node->merged.mode[0];
883         free(node->merged.mode);
884         node->merged.mode = NULL;
885      }
886   }
887
888   free(indices);
889   free(merged_prims);
890
891end:
892
893   if (!save->current_bo) {
894      save->current_bo = ctx->Driver.NewBufferObject(ctx, VBO_BUF_ID + 1);
895      bool success = ctx->Driver.BufferData(ctx,
896                                            GL_ELEMENT_ARRAY_BUFFER_ARB,
897                                            VBO_SAVE_BUFFER_SIZE,
898                                            NULL,
899                                            GL_STATIC_DRAW_ARB, GL_MAP_WRITE_BIT,
900                                            save->current_bo);
901      if (!success)
902         handle_out_of_memory(ctx);
903   }
904
905   GLuint offsets[VBO_ATTRIB_MAX];
906   for (unsigned i = 0, offset = 0; i < VBO_ATTRIB_MAX; ++i) {
907      offsets[i] = offset;
908      offset += save->attrsz[i] * sizeof(GLfloat);
909   }
910   /* Create a pair of VAOs for the possible VERTEX_PROCESSING_MODEs
911    * Note that this may reuse the previous one of possible.
912    */
913   for (gl_vertex_processing_mode vpm = VP_MODE_FF; vpm < VP_MODE_MAX; ++vpm) {
914      /* create or reuse the vao */
915      update_vao(ctx, vpm, &save->VAO[vpm],
916                 save->current_bo, buffer_offset, stride,
917                 save->enabled, save->attrsz, save->attrtype, offsets);
918      /* Reference the vao in the dlist */
919      node->VAO[vpm] = NULL;
920      _mesa_reference_vao(ctx, &node->VAO[vpm], save->VAO[vpm]);
921   }
922
923   /* Prepare for DrawGalliumVertexState */
924   if (node->merged.num_draws && ctx->Driver.DrawGalliumVertexState) {
925      for (unsigned i = 0; i < VP_MODE_MAX; i++) {
926         uint32_t enabled_attribs = _vbo_get_vao_filter(i) &
927                                    node->VAO[i]->_EnabledWithMapMode;
928
929         node->merged.gallium.state[i] =
930            ctx->Driver.CreateGalliumVertexState(ctx, node->VAO[i],
931                                                 node->cold->ib.obj,
932                                                 enabled_attribs);
933         node->merged.gallium.private_refcount[i] = 0;
934         node->merged.gallium.enabled_attribs[i] = enabled_attribs;
935      }
936
937      node->merged.gallium.ctx = ctx;
938      node->merged.gallium.info.mode = node->merged.info.mode;
939      node->merged.gallium.info.take_vertex_state_ownership = false;
940      assert(node->merged.info.index_size == 4);
941   }
942
943   /* Deal with GL_COMPILE_AND_EXECUTE:
944    */
945   if (ctx->ExecuteFlag) {
946      struct _glapi_table *dispatch = GET_DISPATCH();
947
948      _glapi_set_dispatch(ctx->Exec);
949
950      /* _vbo_loopback_vertex_list doesn't use the index buffer, so we have to
951       * use buffer_in_ram (which contains all vertices) instead of current_bo
952       * (which contains deduplicated vertices *when* UseLoopback is false).
953       *
954       * The problem is that the VAO offset is based on current_bo's layout,
955       * so we have to use a temp value.
956       */
957      struct gl_vertex_array_object *vao = node->VAO[VP_MODE_SHADER];
958      GLintptr original = vao->BufferBinding[0].Offset;
959      /* 'start_offset' has been added to all primitives 'start', so undo it here. */
960      vao->BufferBinding[0].Offset = -(GLintptr)(start_offset * stride);
961      _vbo_loopback_vertex_list(ctx, node, save->vertex_store->buffer_in_ram);
962      vao->BufferBinding[0].Offset = original;
963
964      _glapi_set_dispatch(dispatch);
965   }
966
967   /* Reset our structures for the next run of vertices:
968    */
969   reset_counters(ctx);
970}
971
972
973/**
974 * This is called when we fill a vertex buffer before we hit a glEnd().
975 * We
976 * TODO -- If no new vertices have been stored, don't bother saving it.
977 */
978static void
979wrap_buffers(struct gl_context *ctx)
980{
981   struct vbo_save_context *save = &vbo_context(ctx)->save;
982   GLint i = save->prim_store->used - 1;
983   GLenum mode;
984
985   assert(i < (GLint) save->prim_store->size);
986   assert(i >= 0);
987
988   /* Close off in-progress primitive.
989    */
990   save->prim_store->prims[i].count = (get_vertex_count(save) - save->prim_store->prims[i].start);
991   mode = save->prim_store->prims[i].mode;
992
993   /* store the copied vertices, and allocate a new list.
994    */
995   compile_vertex_list(ctx);
996
997   /* Restart interrupted primitive
998    */
999   save->prim_store->prims[0].mode = mode;
1000   save->prim_store->prims[0].begin = 0;
1001   save->prim_store->prims[0].end = 0;
1002   save->prim_store->prims[0].start = 0;
1003   save->prim_store->prims[0].count = 0;
1004   save->prim_store->used = 1;
1005}
1006
1007
1008/**
1009 * Called only when buffers are wrapped as the result of filling the
1010 * vertex_store struct.
1011 */
1012static void
1013wrap_filled_vertex(struct gl_context *ctx)
1014{
1015   struct vbo_save_context *save = &vbo_context(ctx)->save;
1016   unsigned numComponents;
1017
1018   /* Emit a glEnd to close off the last vertex list.
1019    */
1020   wrap_buffers(ctx);
1021
1022   assert(save->vertex_store->used == 0 && save->vertex_store->used == 0);
1023
1024   /* Copy stored stored vertices to start of new list.
1025    */
1026   numComponents = save->copied.nr * save->vertex_size;
1027
1028   fi_type *buffer_ptr = save->vertex_store->buffer_in_ram;
1029   if (numComponents) {
1030      assert(save->copied.buffer);
1031      memcpy(buffer_ptr,
1032             save->copied.buffer,
1033             numComponents * sizeof(fi_type));
1034      free(save->copied.buffer);
1035      save->copied.buffer = NULL;
1036   }
1037   save->vertex_store->used = numComponents;
1038}
1039
1040
1041static void
1042copy_to_current(struct gl_context *ctx)
1043{
1044   struct vbo_save_context *save = &vbo_context(ctx)->save;
1045   GLbitfield64 enabled = save->enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
1046
1047   while (enabled) {
1048      const int i = u_bit_scan64(&enabled);
1049      assert(save->attrsz[i]);
1050
1051      if (save->attrtype[i] == GL_DOUBLE ||
1052          save->attrtype[i] == GL_UNSIGNED_INT64_ARB)
1053         memcpy(save->current[i], save->attrptr[i], save->attrsz[i] * sizeof(GLfloat));
1054      else
1055         COPY_CLEAN_4V_TYPE_AS_UNION(save->current[i], save->attrsz[i],
1056                                     save->attrptr[i], save->attrtype[i]);
1057   }
1058}
1059
1060
1061static void
1062copy_from_current(struct gl_context *ctx)
1063{
1064   struct vbo_save_context *save = &vbo_context(ctx)->save;
1065   GLbitfield64 enabled = save->enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
1066
1067   while (enabled) {
1068      const int i = u_bit_scan64(&enabled);
1069
1070      switch (save->attrsz[i]) {
1071      case 4:
1072         save->attrptr[i][3] = save->current[i][3];
1073         FALLTHROUGH;
1074      case 3:
1075         save->attrptr[i][2] = save->current[i][2];
1076         FALLTHROUGH;
1077      case 2:
1078         save->attrptr[i][1] = save->current[i][1];
1079         FALLTHROUGH;
1080      case 1:
1081         save->attrptr[i][0] = save->current[i][0];
1082         break;
1083      case 0:
1084         unreachable("Unexpected vertex attribute size");
1085      }
1086   }
1087}
1088
1089
1090/**
1091 * Called when we increase the size of a vertex attribute.  For example,
1092 * if we've seen one or more glTexCoord2f() calls and now we get a
1093 * glTexCoord3f() call.
1094 * Flush existing data, set new attrib size, replay copied vertices.
1095 */
1096static void
1097upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
1098{
1099   struct vbo_save_context *save = &vbo_context(ctx)->save;
1100   GLuint oldsz;
1101   GLuint i;
1102   fi_type *tmp;
1103
1104   /* Store the current run of vertices, and emit a GL_END.  Emit a
1105    * BEGIN in the new buffer.
1106    */
1107   if (save->vertex_store->used)
1108      wrap_buffers(ctx);
1109   else
1110      assert(save->copied.nr == 0);
1111
1112   /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
1113    * when the attribute already exists in the vertex and is having
1114    * its size increased.
1115    */
1116   copy_to_current(ctx);
1117
1118   /* Fix up sizes:
1119    */
1120   oldsz = save->attrsz[attr];
1121   save->attrsz[attr] = newsz;
1122   save->enabled |= BITFIELD64_BIT(attr);
1123
1124   save->vertex_size += newsz - oldsz;
1125
1126   /* Recalculate all the attrptr[] values:
1127    */
1128   tmp = save->vertex;
1129   for (i = 0; i < VBO_ATTRIB_MAX; i++) {
1130      if (save->attrsz[i]) {
1131         save->attrptr[i] = tmp;
1132         tmp += save->attrsz[i];
1133      }
1134      else {
1135         save->attrptr[i] = NULL;       /* will not be dereferenced. */
1136      }
1137   }
1138
1139   /* Copy from current to repopulate the vertex with correct values.
1140    */
1141   copy_from_current(ctx);
1142
1143   /* Replay stored vertices to translate them to new format here.
1144    *
1145    * If there are copied vertices and the new (upgraded) attribute
1146    * has not been defined before, this list is somewhat degenerate,
1147    * and will need fixup at runtime.
1148    */
1149   if (save->copied.nr) {
1150      assert(save->copied.buffer);
1151      const fi_type *data = save->copied.buffer;
1152      grow_vertex_storage(ctx, save->copied.nr);
1153      fi_type *dest = save->vertex_store->buffer_in_ram;
1154
1155      /* Need to note this and fix up at runtime (or loopback):
1156       */
1157      if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) {
1158         assert(oldsz == 0);
1159         save->dangling_attr_ref = GL_TRUE;
1160      }
1161
1162      for (i = 0; i < save->copied.nr; i++) {
1163         GLbitfield64 enabled = save->enabled;
1164         while (enabled) {
1165            const int j = u_bit_scan64(&enabled);
1166            assert(save->attrsz[j]);
1167            if (j == attr) {
1168               int k;
1169               const fi_type *src = oldsz ? data : save->current[attr];
1170               int copy = oldsz ? oldsz : newsz;
1171               for (k = 0; k < copy; k++)
1172                  dest[k] = src[k];
1173               for (; k < newsz; k++) {
1174                  switch (save->attrtype[j]) {
1175                     case GL_FLOAT:
1176                        dest[k] = FLOAT_AS_UNION(k == 3);
1177                        break;
1178                     case GL_INT:
1179                        dest[k] = INT_AS_UNION(k == 3);
1180                        break;
1181                     case GL_UNSIGNED_INT:
1182                        dest[k] = UINT_AS_UNION(k == 3);
1183                        break;
1184                     default:
1185                        dest[k] = FLOAT_AS_UNION(k == 3);
1186                        assert(!"Unexpected type in upgrade_vertex");
1187                        break;
1188                  }
1189               }
1190               dest += newsz;
1191               data += oldsz;
1192            } else {
1193               GLint sz = save->attrsz[j];
1194               for (int k = 0; k < sz; k++)
1195                  dest[k] = data[k];
1196               data += sz;
1197               dest += sz;
1198            }
1199         }
1200      }
1201
1202      save->vertex_store->used += save->vertex_size * save->copied.nr;
1203      free(save->copied.buffer);
1204      save->copied.buffer = NULL;
1205   }
1206}
1207
1208
1209/**
1210 * This is called when the size of a vertex attribute changes.
1211 * For example, after seeing one or more glTexCoord2f() calls we
1212 * get a glTexCoord4f() or glTexCoord1f() call.
1213 */
1214static void
1215fixup_vertex(struct gl_context *ctx, GLuint attr,
1216             GLuint sz, GLenum newType)
1217{
1218   struct vbo_save_context *save = &vbo_context(ctx)->save;
1219
1220   if (sz > save->attrsz[attr] ||
1221       newType != save->attrtype[attr]) {
1222      /* New size is larger.  Need to flush existing vertices and get
1223       * an enlarged vertex format.
1224       */
1225      upgrade_vertex(ctx, attr, sz);
1226   }
1227   else if (sz < save->active_sz[attr]) {
1228      GLuint i;
1229      const fi_type *id = vbo_get_default_vals_as_union(save->attrtype[attr]);
1230
1231      /* New size is equal or smaller - just need to fill in some
1232       * zeros.
1233       */
1234      for (i = sz; i <= save->attrsz[attr]; i++)
1235         save->attrptr[attr][i - 1] = id[i - 1];
1236   }
1237
1238   save->active_sz[attr] = sz;
1239
1240   grow_vertex_storage(ctx, 1);
1241}
1242
1243
1244/**
1245 * Reset the current size of all vertex attributes to the default
1246 * value of 0.  This signals that we haven't yet seen any per-vertex
1247 * commands such as glNormal3f() or glTexCoord2f().
1248 */
1249static void
1250reset_vertex(struct gl_context *ctx)
1251{
1252   struct vbo_save_context *save = &vbo_context(ctx)->save;
1253
1254   while (save->enabled) {
1255      const int i = u_bit_scan64(&save->enabled);
1256      assert(save->attrsz[i]);
1257      save->attrsz[i] = 0;
1258      save->active_sz[i] = 0;
1259   }
1260
1261   save->vertex_size = 0;
1262}
1263
1264
1265/**
1266 * If index=0, does glVertexAttrib*() alias glVertex() to emit a vertex?
1267 * It depends on a few things, including whether we're inside or outside
1268 * of glBegin/glEnd.
1269 */
1270static inline bool
1271is_vertex_position(const struct gl_context *ctx, GLuint index)
1272{
1273   return (index == 0 &&
1274           _mesa_attr_zero_aliases_vertex(ctx) &&
1275           _mesa_inside_dlist_begin_end(ctx));
1276}
1277
1278
1279
1280#define ERROR(err)   _mesa_compile_error(ctx, err, __func__);
1281
1282
1283/* Only one size for each attribute may be active at once.  Eg. if
1284 * Color3f is installed/active, then Color4f may not be, even if the
1285 * vertex actually contains 4 color coordinates.  This is because the
1286 * 3f version won't otherwise set color[3] to 1.0 -- this is the job
1287 * of the chooser function when switching between Color4f and Color3f.
1288 */
1289#define ATTR_UNION(A, N, T, C, V0, V1, V2, V3)                  \
1290do {                                                            \
1291   struct vbo_save_context *save = &vbo_context(ctx)->save;     \
1292   int sz = (sizeof(C) / sizeof(GLfloat));                      \
1293                                                                \
1294   if (save->active_sz[A] != N)                                 \
1295      fixup_vertex(ctx, A, N * sz, T);                          \
1296                                                                \
1297   {                                                            \
1298      C *dest = (C *)save->attrptr[A];                          \
1299      if (N>0) dest[0] = V0;                                    \
1300      if (N>1) dest[1] = V1;                                    \
1301      if (N>2) dest[2] = V2;                                    \
1302      if (N>3) dest[3] = V3;                                    \
1303      save->attrtype[A] = T;                                    \
1304   }                                                            \
1305                                                                \
1306   if ((A) == VBO_ATTRIB_POS) {                                 \
1307      fi_type *buffer_ptr = save->vertex_store->buffer_in_ram + \
1308                            save->vertex_store->used;           \
1309                                                                \
1310      for (int i = 0; i < save->vertex_size; i++)               \
1311        buffer_ptr[i] = save->vertex[i];                        \
1312                                                                \
1313      save->vertex_store->used += save->vertex_size;            \
1314      unsigned used_next = (save->vertex_store->used +          \
1315                            save->vertex_size) * sizeof(float); \
1316      if (used_next > save->vertex_store->buffer_in_ram_size) { \
1317         grow_vertex_storage(ctx, get_vertex_count(save));      \
1318         assert(used_next <=                                    \
1319                save->vertex_store->buffer_in_ram_size);        \
1320      }                                                         \
1321   }                                                            \
1322} while (0)
1323
1324#define TAG(x) _save_##x
1325
1326#include "vbo_attrib_tmp.h"
1327
1328
1329#define MAT( ATTR, N, face, params )                            \
1330do {                                                            \
1331   if (face != GL_BACK)                                         \
1332      MAT_ATTR( ATTR, N, params ); /* front */                  \
1333   if (face != GL_FRONT)                                        \
1334      MAT_ATTR( ATTR + 1, N, params ); /* back */               \
1335} while (0)
1336
1337
1338/**
1339 * Save a glMaterial call found between glBegin/End.
1340 * glMaterial calls outside Begin/End are handled in dlist.c.
1341 */
1342static void GLAPIENTRY
1343_save_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
1344{
1345   GET_CURRENT_CONTEXT(ctx);
1346
1347   if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) {
1348      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(face)");
1349      return;
1350   }
1351
1352   switch (pname) {
1353   case GL_EMISSION:
1354      MAT(VBO_ATTRIB_MAT_FRONT_EMISSION, 4, face, params);
1355      break;
1356   case GL_AMBIENT:
1357      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
1358      break;
1359   case GL_DIFFUSE:
1360      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
1361      break;
1362   case GL_SPECULAR:
1363      MAT(VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params);
1364      break;
1365   case GL_SHININESS:
1366      if (*params < 0 || *params > ctx->Const.MaxShininess) {
1367         _mesa_compile_error(ctx, GL_INVALID_VALUE, "glMaterial(shininess)");
1368      }
1369      else {
1370         MAT(VBO_ATTRIB_MAT_FRONT_SHININESS, 1, face, params);
1371      }
1372      break;
1373   case GL_COLOR_INDEXES:
1374      MAT(VBO_ATTRIB_MAT_FRONT_INDEXES, 3, face, params);
1375      break;
1376   case GL_AMBIENT_AND_DIFFUSE:
1377      MAT(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params);
1378      MAT(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params);
1379      break;
1380   default:
1381      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMaterial(pname)");
1382      return;
1383   }
1384}
1385
1386
1387/* Cope with EvalCoord/CallList called within a begin/end object:
1388 *     -- Flush current buffer
1389 *     -- Fallback to opcodes for the rest of the begin/end object.
1390 */
1391static void
1392dlist_fallback(struct gl_context *ctx)
1393{
1394   struct vbo_save_context *save = &vbo_context(ctx)->save;
1395
1396   if (save->vertex_store->used || save->prim_store->used) {
1397      if (save->prim_store->used > 0 && save->vertex_store->used > 0) {
1398         assert(save->vertex_size);
1399         /* Close off in-progress primitive. */
1400         GLint i = save->prim_store->used - 1;
1401         save->prim_store->prims[i].count =
1402            get_vertex_count(save) -
1403            save->prim_store->prims[i].start;
1404      }
1405
1406      /* Need to replay this display list with loopback,
1407       * unfortunately, otherwise this primitive won't be handled
1408       * properly:
1409       */
1410      save->dangling_attr_ref = GL_TRUE;
1411
1412      compile_vertex_list(ctx);
1413   }
1414
1415   copy_to_current(ctx);
1416   reset_vertex(ctx);
1417   if (save->out_of_memory) {
1418      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
1419   }
1420   else {
1421      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1422   }
1423   ctx->Driver.SaveNeedFlush = GL_FALSE;
1424}
1425
1426
1427static void GLAPIENTRY
1428_save_EvalCoord1f(GLfloat u)
1429{
1430   GET_CURRENT_CONTEXT(ctx);
1431   dlist_fallback(ctx);
1432   CALL_EvalCoord1f(ctx->Save, (u));
1433}
1434
1435static void GLAPIENTRY
1436_save_EvalCoord1fv(const GLfloat * v)
1437{
1438   GET_CURRENT_CONTEXT(ctx);
1439   dlist_fallback(ctx);
1440   CALL_EvalCoord1fv(ctx->Save, (v));
1441}
1442
1443static void GLAPIENTRY
1444_save_EvalCoord2f(GLfloat u, GLfloat v)
1445{
1446   GET_CURRENT_CONTEXT(ctx);
1447   dlist_fallback(ctx);
1448   CALL_EvalCoord2f(ctx->Save, (u, v));
1449}
1450
1451static void GLAPIENTRY
1452_save_EvalCoord2fv(const GLfloat * v)
1453{
1454   GET_CURRENT_CONTEXT(ctx);
1455   dlist_fallback(ctx);
1456   CALL_EvalCoord2fv(ctx->Save, (v));
1457}
1458
1459static void GLAPIENTRY
1460_save_EvalPoint1(GLint i)
1461{
1462   GET_CURRENT_CONTEXT(ctx);
1463   dlist_fallback(ctx);
1464   CALL_EvalPoint1(ctx->Save, (i));
1465}
1466
1467static void GLAPIENTRY
1468_save_EvalPoint2(GLint i, GLint j)
1469{
1470   GET_CURRENT_CONTEXT(ctx);
1471   dlist_fallback(ctx);
1472   CALL_EvalPoint2(ctx->Save, (i, j));
1473}
1474
1475static void GLAPIENTRY
1476_save_CallList(GLuint l)
1477{
1478   GET_CURRENT_CONTEXT(ctx);
1479   dlist_fallback(ctx);
1480   CALL_CallList(ctx->Save, (l));
1481}
1482
1483static void GLAPIENTRY
1484_save_CallLists(GLsizei n, GLenum type, const GLvoid * v)
1485{
1486   GET_CURRENT_CONTEXT(ctx);
1487   dlist_fallback(ctx);
1488   CALL_CallLists(ctx->Save, (n, type, v));
1489}
1490
1491
1492
1493/**
1494 * Called when a glBegin is getting compiled into a display list.
1495 * Updating of ctx->Driver.CurrentSavePrimitive is already taken care of.
1496 */
1497void
1498vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode,
1499                     bool no_current_update)
1500{
1501   struct vbo_save_context *save = &vbo_context(ctx)->save;
1502   const GLuint i = save->prim_store->used++;
1503
1504   ctx->Driver.CurrentSavePrimitive = mode;
1505
1506   if (!save->prim_store || i >= save->prim_store->size) {
1507      save->prim_store = realloc_prim_store(save->prim_store, i * 2);
1508   }
1509   save->prim_store->prims[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
1510   save->prim_store->prims[i].begin = 1;
1511   save->prim_store->prims[i].end = 0;
1512   save->prim_store->prims[i].start = get_vertex_count(save);
1513   save->prim_store->prims[i].count = 0;
1514
1515   save->no_current_update = no_current_update;
1516
1517   _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
1518
1519   /* We need to call vbo_save_SaveFlushVertices() if there's state change */
1520   ctx->Driver.SaveNeedFlush = GL_TRUE;
1521}
1522
1523
1524static void GLAPIENTRY
1525_save_End(void)
1526{
1527   GET_CURRENT_CONTEXT(ctx);
1528   struct vbo_save_context *save = &vbo_context(ctx)->save;
1529   const GLint i = save->prim_store->used - 1;
1530
1531   ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1532   save->prim_store->prims[i].end = 1;
1533   save->prim_store->prims[i].count = (get_vertex_count(save) - save->prim_store->prims[i].start);
1534
1535   /* Swap out this vertex format while outside begin/end.  Any color,
1536    * etc. received between here and the next begin will be compiled
1537    * as opcodes.
1538    */
1539   if (save->out_of_memory) {
1540      _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
1541   }
1542   else {
1543      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
1544   }
1545}
1546
1547
1548static void GLAPIENTRY
1549_save_Begin(GLenum mode)
1550{
1551   GET_CURRENT_CONTEXT(ctx);
1552   (void) mode;
1553   _mesa_compile_error(ctx, GL_INVALID_OPERATION, "Recursive glBegin");
1554}
1555
1556
1557static void GLAPIENTRY
1558_save_PrimitiveRestartNV(void)
1559{
1560   GET_CURRENT_CONTEXT(ctx);
1561   struct vbo_save_context *save = &vbo_context(ctx)->save;
1562
1563   if (save->prim_store->used == 0) {
1564      /* We're not inside a glBegin/End pair, so calling glPrimitiverRestartNV
1565       * is an error.
1566       */
1567      _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1568                          "glPrimitiveRestartNV called outside glBegin/End");
1569   } else {
1570      /* get current primitive mode */
1571      GLenum curPrim = save->prim_store->prims[save->prim_store->used - 1].mode;
1572      bool no_current_update = save->no_current_update;
1573
1574      /* restart primitive */
1575      CALL_End(ctx->CurrentServerDispatch, ());
1576      vbo_save_NotifyBegin(ctx, curPrim, no_current_update);
1577   }
1578}
1579
1580
1581/* Unlike the functions above, these are to be hooked into the vtxfmt
1582 * maintained in ctx->ListState, active when the list is known or
1583 * suspected to be outside any begin/end primitive.
1584 * Note: OBE = Outside Begin/End
1585 */
1586static void GLAPIENTRY
1587_save_OBE_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
1588{
1589   GET_CURRENT_CONTEXT(ctx);
1590   struct _glapi_table *dispatch = ctx->CurrentServerDispatch;
1591
1592   vbo_save_NotifyBegin(ctx, GL_QUADS, false);
1593   CALL_Vertex2f(dispatch, (x1, y1));
1594   CALL_Vertex2f(dispatch, (x2, y1));
1595   CALL_Vertex2f(dispatch, (x2, y2));
1596   CALL_Vertex2f(dispatch, (x1, y2));
1597   CALL_End(dispatch, ());
1598}
1599
1600
1601static void GLAPIENTRY
1602_save_OBE_Rectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2)
1603{
1604   _save_OBE_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2);
1605}
1606
1607static void GLAPIENTRY
1608_save_OBE_Rectdv(const GLdouble *v1, const GLdouble *v2)
1609{
1610   _save_OBE_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]);
1611}
1612
1613static void GLAPIENTRY
1614_save_OBE_Rectfv(const GLfloat *v1, const GLfloat *v2)
1615{
1616   _save_OBE_Rectf(v1[0], v1[1], v2[0], v2[1]);
1617}
1618
1619static void GLAPIENTRY
1620_save_OBE_Recti(GLint x1, GLint y1, GLint x2, GLint y2)
1621{
1622   _save_OBE_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2);
1623}
1624
1625static void GLAPIENTRY
1626_save_OBE_Rectiv(const GLint *v1, const GLint *v2)
1627{
1628   _save_OBE_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]);
1629}
1630
1631static void GLAPIENTRY
1632_save_OBE_Rects(GLshort x1, GLshort y1, GLshort x2, GLshort y2)
1633{
1634   _save_OBE_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2);
1635}
1636
1637static void GLAPIENTRY
1638_save_OBE_Rectsv(const GLshort *v1, const GLshort *v2)
1639{
1640   _save_OBE_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]);
1641}
1642
1643static void GLAPIENTRY
1644_save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
1645{
1646   GET_CURRENT_CONTEXT(ctx);
1647   struct gl_vertex_array_object *vao = ctx->Array.VAO;
1648   struct vbo_save_context *save = &vbo_context(ctx)->save;
1649   GLint i;
1650
1651   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1652      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)");
1653      return;
1654   }
1655   if (count < 0) {
1656      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count<0)");
1657      return;
1658   }
1659
1660   if (save->out_of_memory)
1661      return;
1662
1663   grow_vertex_storage(ctx, count);
1664
1665   /* Make sure to process any VBO binding changes */
1666   _mesa_update_state(ctx);
1667
1668   _mesa_vao_map_arrays(ctx, vao, GL_MAP_READ_BIT);
1669
1670   vbo_save_NotifyBegin(ctx, mode, true);
1671
1672   for (i = 0; i < count; i++)
1673      _mesa_array_element(ctx, start + i);
1674   CALL_End(ctx->CurrentServerDispatch, ());
1675
1676   _mesa_vao_unmap_arrays(ctx, vao);
1677}
1678
1679
1680static void GLAPIENTRY
1681_save_OBE_MultiDrawArrays(GLenum mode, const GLint *first,
1682                          const GLsizei *count, GLsizei primcount)
1683{
1684   GET_CURRENT_CONTEXT(ctx);
1685   GLint i;
1686
1687   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1688      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMultiDrawArrays(mode)");
1689      return;
1690   }
1691
1692   if (primcount < 0) {
1693      _mesa_compile_error(ctx, GL_INVALID_VALUE,
1694                          "glMultiDrawArrays(primcount<0)");
1695      return;
1696   }
1697
1698   unsigned vertcount = 0;
1699   for (i = 0; i < primcount; i++) {
1700      if (count[i] < 0) {
1701         _mesa_compile_error(ctx, GL_INVALID_VALUE,
1702                             "glMultiDrawArrays(count[i]<0)");
1703         return;
1704      }
1705      vertcount += count[i];
1706   }
1707
1708   grow_vertex_storage(ctx, vertcount);
1709
1710   for (i = 0; i < primcount; i++) {
1711      if (count[i] > 0) {
1712         _save_OBE_DrawArrays(mode, first[i], count[i]);
1713      }
1714   }
1715}
1716
1717
1718static void
1719array_element(struct gl_context *ctx,
1720              GLint basevertex, GLuint elt, unsigned index_size_shift)
1721{
1722   /* Section 10.3.5 Primitive Restart:
1723    * [...]
1724    *    When one of the *BaseVertex drawing commands specified in section 10.5
1725    * is used, the primitive restart comparison occurs before the basevertex
1726    * offset is added to the array index.
1727    */
1728   /* If PrimitiveRestart is enabled and the index is the RestartIndex
1729    * then we call PrimitiveRestartNV and return.
1730    */
1731   if (ctx->Array._PrimitiveRestart[index_size_shift] &&
1732       elt == ctx->Array._RestartIndex[index_size_shift]) {
1733      CALL_PrimitiveRestartNV(ctx->CurrentServerDispatch, ());
1734      return;
1735   }
1736
1737   _mesa_array_element(ctx, basevertex + elt);
1738}
1739
1740
1741/* Could do better by copying the arrays and element list intact and
1742 * then emitting an indexed prim at runtime.
1743 */
1744static void GLAPIENTRY
1745_save_OBE_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
1746                                 const GLvoid * indices, GLint basevertex)
1747{
1748   GET_CURRENT_CONTEXT(ctx);
1749   struct vbo_save_context *save = &vbo_context(ctx)->save;
1750   struct gl_vertex_array_object *vao = ctx->Array.VAO;
1751   struct gl_buffer_object *indexbuf = vao->IndexBufferObj;
1752   GLint i;
1753
1754   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1755      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawElements(mode)");
1756      return;
1757   }
1758   if (count < 0) {
1759      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawElements(count<0)");
1760      return;
1761   }
1762   if (type != GL_UNSIGNED_BYTE &&
1763       type != GL_UNSIGNED_SHORT &&
1764       type != GL_UNSIGNED_INT) {
1765      _mesa_compile_error(ctx, GL_INVALID_VALUE, "glDrawElements(count<0)");
1766      return;
1767   }
1768
1769   if (save->out_of_memory)
1770      return;
1771
1772   grow_vertex_storage(ctx, count);
1773
1774   /* Make sure to process any VBO binding changes */
1775   _mesa_update_state(ctx);
1776
1777   _mesa_vao_map(ctx, vao, GL_MAP_READ_BIT);
1778
1779   if (indexbuf)
1780      indices =
1781         ADD_POINTERS(indexbuf->Mappings[MAP_INTERNAL].Pointer, indices);
1782
1783   vbo_save_NotifyBegin(ctx, mode, true);
1784
1785   switch (type) {
1786   case GL_UNSIGNED_BYTE:
1787      for (i = 0; i < count; i++)
1788         array_element(ctx, basevertex, ((GLubyte *) indices)[i], 0);
1789      break;
1790   case GL_UNSIGNED_SHORT:
1791      for (i = 0; i < count; i++)
1792         array_element(ctx, basevertex, ((GLushort *) indices)[i], 1);
1793      break;
1794   case GL_UNSIGNED_INT:
1795      for (i = 0; i < count; i++)
1796         array_element(ctx, basevertex, ((GLuint *) indices)[i], 2);
1797      break;
1798   default:
1799      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
1800      break;
1801   }
1802
1803   CALL_End(ctx->CurrentServerDispatch, ());
1804
1805   _mesa_vao_unmap(ctx, vao);
1806}
1807
1808static void GLAPIENTRY
1809_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1810                       const GLvoid * indices)
1811{
1812   _save_OBE_DrawElementsBaseVertex(mode, count, type, indices, 0);
1813}
1814
1815
1816static void GLAPIENTRY
1817_save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
1818                            GLsizei count, GLenum type,
1819                            const GLvoid * indices)
1820{
1821   GET_CURRENT_CONTEXT(ctx);
1822   struct vbo_save_context *save = &vbo_context(ctx)->save;
1823
1824   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
1825      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(mode)");
1826      return;
1827   }
1828   if (count < 0) {
1829      _mesa_compile_error(ctx, GL_INVALID_VALUE,
1830                          "glDrawRangeElements(count<0)");
1831      return;
1832   }
1833   if (type != GL_UNSIGNED_BYTE &&
1834       type != GL_UNSIGNED_SHORT &&
1835       type != GL_UNSIGNED_INT) {
1836      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)");
1837      return;
1838   }
1839   if (end < start) {
1840      _mesa_compile_error(ctx, GL_INVALID_VALUE,
1841                          "glDrawRangeElements(end < start)");
1842      return;
1843   }
1844
1845   if (save->out_of_memory)
1846      return;
1847
1848   _save_OBE_DrawElements(mode, count, type, indices);
1849}
1850
1851
1852static void GLAPIENTRY
1853_save_OBE_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
1854                            const GLvoid * const *indices, GLsizei primcount)
1855{
1856   GET_CURRENT_CONTEXT(ctx);
1857   struct _glapi_table *dispatch = ctx->CurrentServerDispatch;
1858   GLsizei i;
1859
1860   int vertcount = 0;
1861   for (i = 0; i < primcount; i++) {
1862      vertcount += count[i];
1863   }
1864   grow_vertex_storage(ctx, vertcount);
1865
1866   for (i = 0; i < primcount; i++) {
1867      if (count[i] > 0) {
1868         CALL_DrawElements(dispatch, (mode, count[i], type, indices[i]));
1869      }
1870   }
1871}
1872
1873
1874static void GLAPIENTRY
1875_save_OBE_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count,
1876                                      GLenum type,
1877                                      const GLvoid * const *indices,
1878                                      GLsizei primcount,
1879                                      const GLint *basevertex)
1880{
1881   GET_CURRENT_CONTEXT(ctx);
1882   struct _glapi_table *dispatch = ctx->CurrentServerDispatch;
1883   GLsizei i;
1884
1885   int vertcount = 0;
1886   for (i = 0; i < primcount; i++) {
1887      vertcount += count[i];
1888   }
1889   grow_vertex_storage(ctx, vertcount);
1890
1891   for (i = 0; i < primcount; i++) {
1892      if (count[i] > 0) {
1893         CALL_DrawElementsBaseVertex(dispatch, (mode, count[i], type,
1894                                     indices[i],
1895                                     basevertex[i]));
1896      }
1897   }
1898}
1899
1900
1901static void
1902vtxfmt_init(struct gl_context *ctx)
1903{
1904   struct vbo_save_context *save = &vbo_context(ctx)->save;
1905   GLvertexformat *vfmt = &save->vtxfmt;
1906
1907#define NAME_AE(x) _ae_##x
1908#define NAME_CALLLIST(x) _save_##x
1909#define NAME(x) _save_##x
1910#define NAME_ES(x) _save_##x##ARB
1911
1912#include "vbo_init_tmp.h"
1913}
1914
1915
1916/**
1917 * Initialize the dispatch table with the VBO functions for display
1918 * list compilation.
1919 */
1920void
1921vbo_initialize_save_dispatch(const struct gl_context *ctx,
1922                             struct _glapi_table *exec)
1923{
1924   SET_DrawArrays(exec, _save_OBE_DrawArrays);
1925   SET_MultiDrawArrays(exec, _save_OBE_MultiDrawArrays);
1926   SET_DrawElements(exec, _save_OBE_DrawElements);
1927   SET_DrawElementsBaseVertex(exec, _save_OBE_DrawElementsBaseVertex);
1928   SET_DrawRangeElements(exec, _save_OBE_DrawRangeElements);
1929   SET_MultiDrawElementsEXT(exec, _save_OBE_MultiDrawElements);
1930   SET_MultiDrawElementsBaseVertex(exec, _save_OBE_MultiDrawElementsBaseVertex);
1931   SET_Rectf(exec, _save_OBE_Rectf);
1932   SET_Rectd(exec, _save_OBE_Rectd);
1933   SET_Rectdv(exec, _save_OBE_Rectdv);
1934   SET_Rectfv(exec, _save_OBE_Rectfv);
1935   SET_Recti(exec, _save_OBE_Recti);
1936   SET_Rectiv(exec, _save_OBE_Rectiv);
1937   SET_Rects(exec, _save_OBE_Rects);
1938   SET_Rectsv(exec, _save_OBE_Rectsv);
1939
1940   /* Note: other glDraw functins aren't compiled into display lists */
1941}
1942
1943
1944
1945void
1946vbo_save_SaveFlushVertices(struct gl_context *ctx)
1947{
1948   struct vbo_save_context *save = &vbo_context(ctx)->save;
1949
1950   /* Noop when we are actually active:
1951    */
1952   if (ctx->Driver.CurrentSavePrimitive <= PRIM_MAX)
1953      return;
1954
1955   if (save->vertex_store->used || save->prim_store->used)
1956      compile_vertex_list(ctx);
1957
1958   copy_to_current(ctx);
1959   reset_vertex(ctx);
1960   ctx->Driver.SaveNeedFlush = GL_FALSE;
1961}
1962
1963
1964/**
1965 * Called from glNewList when we're starting to compile a display list.
1966 */
1967void
1968vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
1969{
1970   struct vbo_save_context *save = &vbo_context(ctx)->save;
1971
1972   (void) list;
1973   (void) mode;
1974
1975   if (!save->prim_store)
1976      save->prim_store = realloc_prim_store(NULL, 8);
1977
1978   if (!save->vertex_store)
1979      save->vertex_store = CALLOC_STRUCT(vbo_save_vertex_store);
1980
1981   reset_vertex(ctx);
1982   ctx->Driver.SaveNeedFlush = GL_FALSE;
1983}
1984
1985
1986/**
1987 * Called from glEndList when we're finished compiling a display list.
1988 */
1989void
1990vbo_save_EndList(struct gl_context *ctx)
1991{
1992   struct vbo_save_context *save = &vbo_context(ctx)->save;
1993
1994   /* EndList called inside a (saved) Begin/End pair?
1995    */
1996   if (_mesa_inside_dlist_begin_end(ctx)) {
1997      if (save->prim_store->used > 0) {
1998         GLint i = save->prim_store->used - 1;
1999         ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
2000         save->prim_store->prims[i].end = 0;
2001         save->prim_store->prims[i].count = get_vertex_count(save) - save->prim_store->prims[i].start;
2002      }
2003
2004      /* Make sure this vertex list gets replayed by the "loopback"
2005       * mechanism:
2006       */
2007      save->dangling_attr_ref = GL_TRUE;
2008      vbo_save_SaveFlushVertices(ctx);
2009
2010      /* Swap out this vertex format while outside begin/end.  Any color,
2011       * etc. received between here and the next begin will be compiled
2012       * as opcodes.
2013       */
2014      _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt);
2015   }
2016
2017   assert(save->vertex_size == 0);
2018}
2019
2020/**
2021 * Called during context creation/init.
2022 */
2023static void
2024current_init(struct gl_context *ctx)
2025{
2026   struct vbo_save_context *save = &vbo_context(ctx)->save;
2027   GLint i;
2028
2029   for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_EDGEFLAG; i++) {
2030      const GLuint j = i - VBO_ATTRIB_POS;
2031      assert(j < VERT_ATTRIB_MAX);
2032      save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
2033      save->current[i] = (fi_type *) ctx->ListState.CurrentAttrib[j];
2034   }
2035
2036   for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
2037      const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
2038      assert(j < MAT_ATTRIB_MAX);
2039      save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
2040      save->current[i] = (fi_type *) ctx->ListState.CurrentMaterial[j];
2041   }
2042}
2043
2044
2045/**
2046 * Initialize the display list compiler.  Called during context creation.
2047 */
2048void
2049vbo_save_api_init(struct vbo_save_context *save)
2050{
2051   struct gl_context *ctx = gl_context_from_vbo_save(save);
2052
2053   vtxfmt_init(ctx);
2054   current_init(ctx);
2055}
2056