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