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