Merge branch 'mesa_7_5_branch' into mesa_7_6_branch
[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_DrawArrays(GLenum mode, GLint start, GLsizei count)
830 {
831 GET_CURRENT_CONTEXT(ctx);
832 (void) mode; (void) start; (void) count;
833 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" );
834 }
835
836 static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
837 {
838 GET_CURRENT_CONTEXT(ctx);
839 (void) x1; (void) y1; (void) x2; (void) y2;
840 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" );
841 }
842
843 static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 )
844 {
845 GET_CURRENT_CONTEXT(ctx);
846 (void) mode; (void) i1; (void) i2;
847 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" );
848 }
849
850 static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2,
851 GLint j1, GLint j2 )
852 {
853 GET_CURRENT_CONTEXT(ctx);
854 (void) mode; (void) i1; (void) i2; (void) j1; (void) j2;
855 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" );
856 }
857
858 static void GLAPIENTRY _save_Begin( GLenum mode )
859 {
860 GET_CURRENT_CONTEXT( ctx );
861 (void) mode;
862 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" );
863 }
864
865
866 /* Unlike the functions above, these are to be hooked into the vtxfmt
867 * maintained in ctx->ListState, active when the list is known or
868 * suspected to be outside any begin/end primitive.
869 */
870 static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
871 {
872 GET_CURRENT_CONTEXT(ctx);
873 vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK );
874 CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 ));
875 CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 ));
876 CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 ));
877 CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 ));
878 CALL_End(GET_DISPATCH(), ());
879 }
880
881
882 static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
883 {
884 GET_CURRENT_CONTEXT(ctx);
885 GLint i;
886
887 if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
888 return;
889
890 _ae_map_vbos( ctx );
891
892 vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK );
893
894 for (i = 0; i < count; i++)
895 CALL_ArrayElement(GET_DISPATCH(), (start + i));
896 CALL_End(GET_DISPATCH(), ());
897
898 _ae_unmap_vbos( ctx );
899 }
900
901 /* Could do better by copying the arrays and element list intact and
902 * then emitting an indexed prim at runtime.
903 */
904 static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
905 const GLvoid *indices)
906 {
907 GET_CURRENT_CONTEXT(ctx);
908 GLint i;
909
910 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
911 return;
912
913 _ae_map_vbos( ctx );
914
915 if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj))
916 indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices);
917
918 vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK );
919
920 switch (type) {
921 case GL_UNSIGNED_BYTE:
922 for (i = 0 ; i < count ; i++)
923 CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] ));
924 break;
925 case GL_UNSIGNED_SHORT:
926 for (i = 0 ; i < count ; i++)
927 CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] ));
928 break;
929 case GL_UNSIGNED_INT:
930 for (i = 0 ; i < count ; i++)
931 CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] ));
932 break;
933 default:
934 _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
935 break;
936 }
937
938 CALL_End(GET_DISPATCH(), ());
939
940 _ae_unmap_vbos( ctx );
941 }
942
943 static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode,
944 GLuint start, GLuint end,
945 GLsizei count, GLenum type,
946 const GLvoid *indices)
947 {
948 GET_CURRENT_CONTEXT(ctx);
949 if (_mesa_validate_DrawRangeElements( ctx, mode,
950 start, end,
951 count, type, indices ))
952 _save_OBE_DrawElements( mode, count, type, indices );
953 }
954
955
956
957
958
959 static void _save_vtxfmt_init( GLcontext *ctx )
960 {
961 struct vbo_save_context *save = &vbo_context(ctx)->save;
962 GLvertexformat *vfmt = &save->vtxfmt;
963
964 vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */
965 vfmt->Begin = _save_Begin;
966 vfmt->Color3f = _save_Color3f;
967 vfmt->Color3fv = _save_Color3fv;
968 vfmt->Color4f = _save_Color4f;
969 vfmt->Color4fv = _save_Color4fv;
970 vfmt->EdgeFlag = _save_EdgeFlag;
971 vfmt->End = _save_End;
972 vfmt->FogCoordfEXT = _save_FogCoordfEXT;
973 vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
974 vfmt->Indexf = _save_Indexf;
975 vfmt->Indexfv = _save_Indexfv;
976 vfmt->Materialfv = _save_Materialfv;
977 vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
978 vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
979 vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
980 vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
981 vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
982 vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
983 vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
984 vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
985 vfmt->Normal3f = _save_Normal3f;
986 vfmt->Normal3fv = _save_Normal3fv;
987 vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
988 vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
989 vfmt->TexCoord1f = _save_TexCoord1f;
990 vfmt->TexCoord1fv = _save_TexCoord1fv;
991 vfmt->TexCoord2f = _save_TexCoord2f;
992 vfmt->TexCoord2fv = _save_TexCoord2fv;
993 vfmt->TexCoord3f = _save_TexCoord3f;
994 vfmt->TexCoord3fv = _save_TexCoord3fv;
995 vfmt->TexCoord4f = _save_TexCoord4f;
996 vfmt->TexCoord4fv = _save_TexCoord4fv;
997 vfmt->Vertex2f = _save_Vertex2f;
998 vfmt->Vertex2fv = _save_Vertex2fv;
999 vfmt->Vertex3f = _save_Vertex3f;
1000 vfmt->Vertex3fv = _save_Vertex3fv;
1001 vfmt->Vertex4f = _save_Vertex4f;
1002 vfmt->Vertex4fv = _save_Vertex4fv;
1003 vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1004 vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1005 vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1006 vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1007 vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1008 vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1009 vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1010 vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1011
1012 vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1013 vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1014 vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1015 vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1016 vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1017 vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1018 vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1019 vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1020
1021 /* This will all require us to fallback to saving the list as opcodes:
1022 */
1023 vfmt->CallList = _save_CallList; /* inside begin/end */
1024 vfmt->CallLists = _save_CallLists; /* inside begin/end */
1025 vfmt->EvalCoord1f = _save_EvalCoord1f;
1026 vfmt->EvalCoord1fv = _save_EvalCoord1fv;
1027 vfmt->EvalCoord2f = _save_EvalCoord2f;
1028 vfmt->EvalCoord2fv = _save_EvalCoord2fv;
1029 vfmt->EvalPoint1 = _save_EvalPoint1;
1030 vfmt->EvalPoint2 = _save_EvalPoint2;
1031
1032 /* These are all errors as we at least know we are in some sort of
1033 * begin/end pair:
1034 */
1035 vfmt->EvalMesh1 = _save_EvalMesh1;
1036 vfmt->EvalMesh2 = _save_EvalMesh2;
1037 vfmt->Begin = _save_Begin;
1038 vfmt->Rectf = _save_Rectf;
1039 vfmt->DrawArrays = _save_DrawArrays;
1040 vfmt->DrawElements = _save_DrawElements;
1041 vfmt->DrawRangeElements = _save_DrawRangeElements;
1042 /* Loops back into vfmt->DrawElements */
1043 vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements;
1044
1045 }
1046
1047
1048 void vbo_save_SaveFlushVertices( GLcontext *ctx )
1049 {
1050 struct vbo_save_context *save = &vbo_context(ctx)->save;
1051
1052 /* Noop when we are actually active:
1053 */
1054 if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
1055 ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
1056 return;
1057
1058 if (save->vert_count ||
1059 save->prim_count)
1060 _save_compile_vertex_list( ctx );
1061
1062 _save_copy_to_current( ctx );
1063 _save_reset_vertex( ctx );
1064 _save_reset_counters( ctx );
1065 ctx->Driver.SaveNeedFlush = 0;
1066 }
1067
1068 void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode )
1069 {
1070 struct vbo_save_context *save = &vbo_context(ctx)->save;
1071
1072 (void) list; (void) mode;
1073
1074 if (!save->prim_store)
1075 save->prim_store = alloc_prim_store( ctx );
1076
1077 if (!save->vertex_store)
1078 save->vertex_store = alloc_vertex_store( ctx );
1079
1080 save->buffer_ptr = map_vertex_store( ctx, save->vertex_store );
1081
1082 _save_reset_vertex( ctx );
1083 _save_reset_counters( ctx );
1084 ctx->Driver.SaveNeedFlush = 0;
1085 }
1086
1087 void vbo_save_EndList( GLcontext *ctx )
1088 {
1089 struct vbo_save_context *save = &vbo_context(ctx)->save;
1090
1091 /* EndList called inside a (saved) Begin/End pair?
1092 */
1093 if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) {
1094
1095 if (save->prim_count > 0) {
1096 GLint i = save->prim_count - 1;
1097 ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1098 save->prim[i].end = 0;
1099 save->prim[i].count = (save->vert_count -
1100 save->prim[i].start);
1101 }
1102
1103 /* Make sure this vertex list gets replayed by the "loopback"
1104 * mechanism:
1105 */
1106 save->dangling_attr_ref = 1;
1107 vbo_save_SaveFlushVertices( ctx );
1108
1109 /* Swap out this vertex format while outside begin/end. Any color,
1110 * etc. received between here and the next begin will be compiled
1111 * as opcodes.
1112 */
1113 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1114 }
1115
1116 unmap_vertex_store( ctx, save->vertex_store );
1117
1118 assert(save->vertex_size == 0);
1119 }
1120
1121 void vbo_save_BeginCallList( GLcontext *ctx, struct gl_display_list *dlist )
1122 {
1123 struct vbo_save_context *save = &vbo_context(ctx)->save;
1124 save->replay_flags |= dlist->Flags;
1125 }
1126
1127 void vbo_save_EndCallList( GLcontext *ctx )
1128 {
1129 struct vbo_save_context *save = &vbo_context(ctx)->save;
1130
1131 if (ctx->ListState.CallDepth == 1) {
1132 /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1133 * flag, if it is set:
1134 */
1135 save->replay_flags &= VBO_SAVE_FALLBACK;
1136 }
1137 }
1138
1139
1140 static void vbo_destroy_vertex_list( GLcontext *ctx, void *data )
1141 {
1142 struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
1143 (void) ctx;
1144
1145 if ( --node->vertex_store->refcount == 0 )
1146 free_vertex_store( ctx, node->vertex_store );
1147
1148 if ( --node->prim_store->refcount == 0 )
1149 FREE( node->prim_store );
1150
1151 if (node->current_data) {
1152 FREE(node->current_data);
1153 node->current_data = NULL;
1154 }
1155 }
1156
1157
1158 static void vbo_print_vertex_list( GLcontext *ctx, void *data )
1159 {
1160 struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data;
1161 GLuint i;
1162 (void) ctx;
1163
1164 _mesa_printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1165 node->count,
1166 node->prim_count,
1167 node->vertex_size);
1168
1169 for (i = 0 ; i < node->prim_count ; i++) {
1170 struct _mesa_prim *prim = &node->prim[i];
1171 _mesa_debug(NULL, " prim %d: %s%s %d..%d %s %s\n",
1172 i,
1173 _mesa_lookup_prim_by_nr(prim->mode),
1174 prim->weak ? " (weak)" : "",
1175 prim->start,
1176 prim->start + prim->count,
1177 (prim->begin) ? "BEGIN" : "(wrap)",
1178 (prim->end) ? "END" : "(wrap)");
1179 }
1180 }
1181
1182
1183 static void _save_current_init( GLcontext *ctx )
1184 {
1185 struct vbo_save_context *save = &vbo_context(ctx)->save;
1186 GLint i;
1187
1188 for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
1189 const GLuint j = i - VBO_ATTRIB_POS;
1190 ASSERT(j < VERT_ATTRIB_MAX);
1191 save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
1192 save->current[i] = ctx->ListState.CurrentAttrib[j];
1193 }
1194
1195 for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
1196 const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
1197 ASSERT(j < MAT_ATTRIB_MAX);
1198 save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1199 save->current[i] = ctx->ListState.CurrentMaterial[j];
1200 }
1201 }
1202
1203 /**
1204 * Initialize the display list compiler
1205 */
1206 void vbo_save_api_init( struct vbo_save_context *save )
1207 {
1208 GLcontext *ctx = save->ctx;
1209 GLuint i;
1210
1211 save->opcode_vertex_list =
1212 _mesa_alloc_opcode( ctx,
1213 sizeof(struct vbo_save_vertex_list),
1214 vbo_save_playback_vertex_list,
1215 vbo_destroy_vertex_list,
1216 vbo_print_vertex_list );
1217
1218 ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
1219
1220 _save_vtxfmt_init( ctx );
1221 _save_current_init( ctx );
1222
1223 /* These will actually get set again when binding/drawing */
1224 for (i = 0; i < VBO_ATTRIB_MAX; i++)
1225 save->inputs[i] = &save->arrays[i];
1226
1227 /* Hook our array functions into the outside-begin-end vtxfmt in
1228 * ctx->ListState.
1229 */
1230 ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1231 ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1232 ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1233 ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1234 /* loops back into _save_OBE_DrawElements */
1235 ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = _mesa_noop_MultiDrawElements;
1236 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1237 }
1238