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