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