1 /**************************************************************************
3 Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas.
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:
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
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.
26 **************************************************************************/
30 * Keith Whitwell <keith@tungstengraphics.com>
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
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,
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.
50 * The other case where fixup is required is when a vertex attribute
51 * is introduced in the middle of a primitive. Eg:
53 * TexCoord1f() Vertex2f()
54 * TexCoord1f() Color3f() Vertex2f()
57 * If the current value of Color isn't known at compile-time, this
58 * primitive will require fixup.
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.
65 * This could be improved to fallback only when a mix of EvalCoord and
66 * Vertex commands are issued within a single primitive.
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"
81 #include "vbo_context.h"
89 /* An interesting VBO number/name to help with debugging */
90 #define VBO_BUF_ID 12345
94 * NOTE: Old 'parity' issue is gone, but copying can still be
95 * wrong-footed on replay.
97 static GLuint
_save_copy_vertices( GLcontext
*ctx
,
98 const struct vbo_save_vertex_list
*node
,
99 const GLfloat
*src_buffer
)
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
;
118 for (i
= 0 ; i
< ovf
; i
++)
119 _mesa_memcpy( dst
+i
*sz
, src
+(nr
-ovf
+i
)*sz
, sz
*sizeof(GLfloat
) );
123 for (i
= 0 ; i
< ovf
; i
++)
124 _mesa_memcpy( dst
+i
*sz
, src
+(nr
-ovf
+i
)*sz
, sz
*sizeof(GLfloat
) );
128 for (i
= 0 ; i
< ovf
; i
++)
129 _mesa_memcpy( dst
+i
*sz
, src
+(nr
-ovf
+i
)*sz
, sz
*sizeof(GLfloat
) );
135 _mesa_memcpy( dst
, src
+(nr
-1)*sz
, sz
*sizeof(GLfloat
) );
139 case GL_TRIANGLE_FAN
:
144 _mesa_memcpy( dst
, src
+0, sz
*sizeof(GLfloat
) );
147 _mesa_memcpy( dst
, src
+0, sz
*sizeof(GLfloat
) );
148 _mesa_memcpy( dst
+sz
, src
+(nr
-1)*sz
, sz
*sizeof(GLfloat
) );
151 case GL_TRIANGLE_STRIP
:
154 case 0: ovf
= 0; break;
155 case 1: ovf
= 1; break;
156 default: ovf
= 2 + (nr
&1); break;
158 for (i
= 0 ; i
< ovf
; i
++)
159 _mesa_memcpy( dst
+i
*sz
, src
+(nr
-ovf
+i
)*sz
, sz
*sizeof(GLfloat
) );
168 static struct vbo_save_vertex_store
*alloc_vertex_store( GLcontext
*ctx
)
170 struct vbo_save_vertex_store
*vertex_store
= CALLOC_STRUCT(vbo_save_vertex_store
);
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
178 vertex_store
->bufferobj
= ctx
->Driver
.NewBufferObject(ctx
,
180 GL_ARRAY_BUFFER_ARB
);
182 ctx
->Driver
.BufferData( ctx
,
184 VBO_SAVE_BUFFER_SIZE
* sizeof(GLfloat
),
187 vertex_store
->bufferobj
);
189 vertex_store
->buffer
= NULL
;
190 vertex_store
->used
= 0;
191 vertex_store
->refcount
= 1;
196 static void free_vertex_store( GLcontext
*ctx
, struct vbo_save_vertex_store
*vertex_store
)
198 assert(!vertex_store
->buffer
);
200 if (vertex_store
->bufferobj
) {
201 _mesa_reference_buffer_object(ctx
, &vertex_store
->bufferobj
, NULL
);
204 FREE( vertex_store
);
207 static GLfloat
*map_vertex_store( GLcontext
*ctx
, struct vbo_save_vertex_store
*vertex_store
)
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
);
216 assert(vertex_store
->buffer
);
217 return vertex_store
->buffer
+ vertex_store
->used
;
220 static void unmap_vertex_store( GLcontext
*ctx
, struct vbo_save_vertex_store
*vertex_store
)
222 ctx
->Driver
.UnmapBuffer( ctx
, GL_ARRAY_BUFFER_ARB
, vertex_store
->bufferobj
);
223 vertex_store
->buffer
= NULL
;
227 static struct vbo_save_primitive_store
*alloc_prim_store( GLcontext
*ctx
)
229 struct vbo_save_primitive_store
*store
= CALLOC_STRUCT(vbo_save_primitive_store
);
236 static void _save_reset_counters( GLcontext
*ctx
)
238 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
240 save
->prim
= save
->prim_store
->buffer
+ save
->prim_store
->used
;
241 save
->buffer
= (save
->vertex_store
->buffer
+
242 save
->vertex_store
->used
);
244 assert(save
->buffer
== save
->buffer_ptr
);
246 if (save
->vertex_size
)
247 save
->max_vert
= ((VBO_SAVE_BUFFER_SIZE
- save
->vertex_store
->used
) /
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;
259 /* Insert the active immediate struct onto the display list currently
262 static void _save_compile_vertex_list( GLcontext
*ctx
)
264 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
265 struct vbo_save_vertex_list
*node
;
267 /* Allocate space for this structure in the display list currently
270 node
= (struct vbo_save_vertex_list
*)
271 _mesa_alloc_instruction(ctx
, save
->opcode_vertex_list
, sizeof(*node
));
276 /* Duplicate our template, increment refcounts to the storage structs:
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
;
289 node
->vertex_store
->refcount
++;
290 node
->prim_store
->refcount
++;
293 node
->current_size
= node
->vertex_size
- node
->attrsz
[0];
294 node
->current_data
= NULL
;
296 if (node
->current_size
) {
297 /* If the malloc fails, we just pull the data out of the VBO
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;
307 vertex_offset
= (node
->count
-1) * node
->vertex_size
* sizeof(GLfloat
);
309 memcpy( node
->current_data
,
310 buffer
+ node
->buffer_offset
+ vertex_offset
+ attr_offset
,
311 node
->current_size
* sizeof(GLfloat
) );
317 assert(node
->attrsz
[VBO_ATTRIB_POS
] != 0 ||
320 if (save
->dangling_attr_ref
)
321 ctx
->ListState
.CurrentList
->Flags
|= DLIST_DANGLING_REFS
;
323 save
->vertex_store
->used
+= save
->vertex_size
* node
->count
;
324 save
->prim_store
->used
+= node
->prim_count
;
327 /* Copy duplicated vertices
329 save
->copied
.nr
= _save_copy_vertices( ctx
, node
, save
->buffer
);
332 /* Deal with GL_COMPILE_AND_EXECUTE:
334 if (ctx
->ExecuteFlag
) {
335 struct _glapi_table
*dispatch
= GET_DISPATCH();
337 _glapi_set_dispatch(ctx
->Exec
);
339 vbo_loopback_vertex_list( ctx
,
340 (const GLfloat
*)((const char *)save
->vertex_store
->buffer
+
341 node
->buffer_offset
),
348 _glapi_set_dispatch(dispatch
);
352 /* Decide whether the storage structs are full, or can be used for
353 * the next vertex lists as well.
355 if (save
->vertex_store
->used
>
356 VBO_SAVE_BUFFER_SIZE
- 16 * (save
->vertex_size
+ 4)) {
360 unmap_vertex_store( ctx
, save
->vertex_store
);
362 /* Release old reference:
364 save
->vertex_store
->refcount
--;
365 assert(save
->vertex_store
->refcount
!= 0);
366 save
->vertex_store
= NULL
;
368 /* Allocate and map new store:
370 save
->vertex_store
= alloc_vertex_store( ctx
);
371 save
->buffer_ptr
= map_vertex_store( ctx
, save
->vertex_store
);
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
);
380 /* Reset our structures for the next run of vertices:
382 _save_reset_counters( ctx
);
386 /* TODO -- If no new vertices have been stored, don't bother saving
389 static void _save_wrap_buffers( GLcontext
*ctx
)
391 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
392 GLint i
= save
->prim_count
- 1;
396 assert(i
< (GLint
) save
->prim_max
);
399 /* Close off in-progress primitive.
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
;
406 /* store the copied vertices, and allocate a new list.
408 _save_compile_vertex_list( ctx
);
410 /* Restart interrupted primitive
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;
424 /* Called only when buffers are wrapped as the result of filling the
425 * vertex_store struct.
427 static void _save_wrap_filled_vertex( GLcontext
*ctx
)
429 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
430 GLfloat
*data
= save
->copied
.buffer
;
433 /* Emit a glEnd to close off the last vertex list.
435 _save_wrap_buffers( ctx
);
437 /* Copy stored stored vertices to start of new list.
439 assert(save
->max_vert
- save
->vert_count
> save
->copied
.nr
);
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
;
450 static void _save_copy_to_current( GLcontext
*ctx
)
452 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
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
],
466 static void _save_copy_from_current( GLcontext
*ctx
)
468 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
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];
485 /* Flush existing data, set new attrib size, replay copied vertices.
487 static void _save_upgrade_vertex( GLcontext
*ctx
,
491 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
496 /* Store the current run of vertices, and emit a GL_END. Emit a
497 * BEGIN in the new buffer.
499 if (save
->vert_count
)
500 _save_wrap_buffers( ctx
);
502 assert( save
->copied
.nr
== 0 );
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.
508 _save_copy_to_current( ctx
);
512 oldsz
= save
->attrsz
[attr
];
513 save
->attrsz
[attr
] = newsz
;
515 save
->vertex_size
+= newsz
- oldsz
;
516 save
->max_vert
= ((VBO_SAVE_BUFFER_SIZE
- save
->vertex_store
->used
) /
518 save
->vert_count
= 0;
520 /* Recalculate all the attrptr[] values:
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
];
528 save
->attrptr
[i
] = NULL
; /* will not be dereferenced. */
531 /* Copy from current to repopulate the vertex with correct values.
533 _save_copy_from_current( ctx
);
535 /* Replay stored vertices to translate them to new format here.
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.
543 GLfloat
*data
= save
->copied
.buffer
;
544 GLfloat
*dest
= save
->buffer
;
547 /* Need to note this and fix up at runtime (or loopback):
549 if (attr
!= VBO_ATTRIB_POS
&& save
->currentsz
[attr
][0] == 0) {
551 save
->dangling_attr_ref
= GL_TRUE
;
554 for (i
= 0 ; i
< save
->copied
.nr
; i
++) {
555 for (j
= 0 ; j
< VBO_ATTRIB_MAX
; j
++) {
556 if (save
->attrsz
[j
]) {
559 COPY_CLEAN_4V( dest
, oldsz
, data
);
564 COPY_SZ_4V( dest
, newsz
, save
->current
[attr
] );
569 GLint sz
= save
->attrsz
[j
];
570 COPY_SZ_4V( dest
, sz
, data
);
578 save
->buffer_ptr
= dest
;
579 save
->vert_count
+= save
->copied
.nr
;
583 static void save_fixup_vertex( GLcontext
*ctx
, GLuint attr
, GLuint sz
)
585 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
587 if (sz
> save
->attrsz
[attr
]) {
588 /* New size is larger. Need to flush existing vertices and get
589 * an enlarged vertex format.
591 _save_upgrade_vertex( ctx
, attr
, sz
);
593 else if (sz
< save
->active_sz
[attr
]) {
594 static GLfloat id
[4] = { 0, 0, 0, 1 };
597 /* New size is equal or smaller - just need to fill in some
600 for (i
= sz
; i
<= save
->attrsz
[attr
] ; i
++)
601 save
->attrptr
[attr
][i
-1] = id
[i
-1];
604 save
->active_sz
[attr
] = sz
;
607 static void _save_reset_vertex( GLcontext
*ctx
)
609 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
612 for (i
= 0 ; i
< VBO_ATTRIB_MAX
; i
++) {
614 save
->active_sz
[i
] = 0;
617 save
->vertex_size
= 0;
622 #define ERROR() _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ );
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.
631 #define ATTR( A, N, V0, V1, V2, V3 ) \
633 struct vbo_save_context *save = &vbo_context(ctx)->save; \
635 if (save->active_sz[A] != N) \
636 save_fixup_vertex(ctx, A, N); \
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; \
649 for (i = 0; i < save->vertex_size; i++) \
650 save->buffer_ptr[i] = save->vertex[i]; \
652 save->buffer_ptr += save->vertex_size; \
654 if (++save->vert_count >= save->max_vert) \
655 _save_wrap_filled_vertex( ctx ); \
659 #define TAG(x) _save_##x
661 #include "vbo_attrib_tmp.h"
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.
670 #define DO_FALLBACK(ctx) \
672 struct vbo_save_context *save = &vbo_context(ctx)->save; \
674 if (save->vert_count || save->prim_count) \
675 _save_compile_vertex_list( ctx ); \
677 _save_copy_to_current( ctx ); \
678 _save_reset_vertex( ctx ); \
679 _save_reset_counters( ctx ); \
680 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); \
681 ctx->Driver.SaveNeedFlush = 0; \
684 static void GLAPIENTRY
_save_EvalCoord1f( GLfloat u
)
686 GET_CURRENT_CONTEXT(ctx
);
688 ctx
->Save
->EvalCoord1f( u
);
691 static void GLAPIENTRY
_save_EvalCoord1fv( const GLfloat
*v
)
693 GET_CURRENT_CONTEXT(ctx
);
695 ctx
->Save
->EvalCoord1fv( v
);
698 static void GLAPIENTRY
_save_EvalCoord2f( GLfloat u
, GLfloat v
)
700 GET_CURRENT_CONTEXT(ctx
);
702 ctx
->Save
->EvalCoord2f( u
, v
);
705 static void GLAPIENTRY
_save_EvalCoord2fv( const GLfloat
*v
)
707 GET_CURRENT_CONTEXT(ctx
);
709 ctx
->Save
->EvalCoord2fv( v
);
712 static void GLAPIENTRY
_save_EvalPoint1( GLint i
)
714 GET_CURRENT_CONTEXT(ctx
);
716 ctx
->Save
->EvalPoint1( i
);
719 static void GLAPIENTRY
_save_EvalPoint2( GLint i
, GLint j
)
721 GET_CURRENT_CONTEXT(ctx
);
723 ctx
->Save
->EvalPoint2( i
, j
);
726 static void GLAPIENTRY
_save_CallList( GLuint l
)
728 GET_CURRENT_CONTEXT(ctx
);
730 ctx
->Save
->CallList( l
);
733 static void GLAPIENTRY
_save_CallLists( GLsizei n
, GLenum type
, const GLvoid
*v
)
735 GET_CURRENT_CONTEXT(ctx
);
737 ctx
->Save
->CallLists( n
, type
, v
);
743 /* This begin is hooked into ... Updating of
744 * ctx->Driver.CurrentSavePrimitive is already taken care of.
746 GLboolean
vbo_save_NotifyBegin( GLcontext
*ctx
, GLenum mode
)
748 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
750 GLuint i
= save
->prim_count
++;
752 assert(i
< save
->prim_max
);
753 save
->prim
[i
].mode
= mode
& ~VBO_SAVE_PRIM_WEAK
;
754 save
->prim
[i
].begin
= 1;
755 save
->prim
[i
].end
= 0;
756 save
->prim
[i
].weak
= (mode
& VBO_SAVE_PRIM_WEAK
) ? 1 : 0;
757 save
->prim
[i
].pad
= 0;
758 save
->prim
[i
].start
= save
->vert_count
;
759 save
->prim
[i
].count
= 0;
761 _mesa_install_save_vtxfmt( ctx
, &save
->vtxfmt
);
762 ctx
->Driver
.SaveNeedFlush
= 1;
768 static void GLAPIENTRY
_save_End( void )
770 GET_CURRENT_CONTEXT( ctx
);
771 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
772 GLint i
= save
->prim_count
- 1;
774 ctx
->Driver
.CurrentSavePrimitive
= PRIM_OUTSIDE_BEGIN_END
;
775 save
->prim
[i
].end
= 1;
776 save
->prim
[i
].count
= (save
->vert_count
-
777 save
->prim
[i
].start
);
779 if (i
== (GLint
) save
->prim_max
- 1) {
780 _save_compile_vertex_list( ctx
);
781 assert(save
->copied
.nr
== 0);
784 /* Swap out this vertex format while outside begin/end. Any color,
785 * etc. received between here and the next begin will be compiled
788 _mesa_install_save_vtxfmt( ctx
, &ctx
->ListState
.ListVtxfmt
);
792 /* These are all errors as this vtxfmt is only installed inside
795 static void GLAPIENTRY
_save_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
796 const GLvoid
*indices
)
798 GET_CURRENT_CONTEXT(ctx
);
799 (void) mode
; (void) count
; (void) type
; (void) indices
;
800 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glDrawElements" );
804 static void GLAPIENTRY
_save_DrawRangeElements(GLenum mode
,
805 GLuint start
, GLuint end
,
806 GLsizei count
, GLenum type
,
807 const GLvoid
*indices
)
809 GET_CURRENT_CONTEXT(ctx
);
810 (void) mode
; (void) start
; (void) end
; (void) count
; (void) type
; (void) indices
;
811 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glDrawRangeElements" );
814 static void GLAPIENTRY
_save_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
816 GET_CURRENT_CONTEXT(ctx
);
817 (void) mode
; (void) start
; (void) count
;
818 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glDrawArrays" );
821 static void GLAPIENTRY
_save_Rectf( GLfloat x1
, GLfloat y1
, GLfloat x2
, GLfloat y2
)
823 GET_CURRENT_CONTEXT(ctx
);
824 (void) x1
; (void) y1
; (void) x2
; (void) y2
;
825 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glRectf" );
828 static void GLAPIENTRY
_save_EvalMesh1( GLenum mode
, GLint i1
, GLint i2
)
830 GET_CURRENT_CONTEXT(ctx
);
831 (void) mode
; (void) i1
; (void) i2
;
832 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glEvalMesh1" );
835 static void GLAPIENTRY
_save_EvalMesh2( GLenum mode
, GLint i1
, GLint i2
,
838 GET_CURRENT_CONTEXT(ctx
);
839 (void) mode
; (void) i1
; (void) i2
; (void) j1
; (void) j2
;
840 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "glEvalMesh2" );
843 static void GLAPIENTRY
_save_Begin( GLenum mode
)
845 GET_CURRENT_CONTEXT( ctx
);
847 _mesa_compile_error( ctx
, GL_INVALID_OPERATION
, "Recursive glBegin" );
851 /* Unlike the functions above, these are to be hooked into the vtxfmt
852 * maintained in ctx->ListState, active when the list is known or
853 * suspected to be outside any begin/end primitive.
855 static void GLAPIENTRY
_save_OBE_Rectf( GLfloat x1
, GLfloat y1
, GLfloat x2
, GLfloat y2
)
857 GET_CURRENT_CONTEXT(ctx
);
858 vbo_save_NotifyBegin( ctx
, GL_QUADS
| VBO_SAVE_PRIM_WEAK
);
859 CALL_Vertex2f(GET_DISPATCH(), ( x1
, y1
));
860 CALL_Vertex2f(GET_DISPATCH(), ( x2
, y1
));
861 CALL_Vertex2f(GET_DISPATCH(), ( x2
, y2
));
862 CALL_Vertex2f(GET_DISPATCH(), ( x1
, y2
));
863 CALL_End(GET_DISPATCH(), ());
867 static void GLAPIENTRY
_save_OBE_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
869 GET_CURRENT_CONTEXT(ctx
);
872 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
877 vbo_save_NotifyBegin( ctx
, mode
| VBO_SAVE_PRIM_WEAK
);
879 for (i
= 0; i
< count
; i
++)
880 CALL_ArrayElement(GET_DISPATCH(), (start
+ i
));
881 CALL_End(GET_DISPATCH(), ());
883 _ae_unmap_vbos( ctx
);
886 /* Could do better by copying the arrays and element list intact and
887 * then emitting an indexed prim at runtime.
889 static void GLAPIENTRY
_save_OBE_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
890 const GLvoid
*indices
)
892 GET_CURRENT_CONTEXT(ctx
);
895 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
900 if (ctx
->Array
.ElementArrayBufferObj
->Name
)
901 indices
= ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Pointer
, indices
);
903 vbo_save_NotifyBegin( ctx
, mode
| VBO_SAVE_PRIM_WEAK
);
906 case GL_UNSIGNED_BYTE
:
907 for (i
= 0 ; i
< count
; i
++)
908 CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte
*)indices
)[i
] ));
910 case GL_UNSIGNED_SHORT
:
911 for (i
= 0 ; i
< count
; i
++)
912 CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort
*)indices
)[i
] ));
914 case GL_UNSIGNED_INT
:
915 for (i
= 0 ; i
< count
; i
++)
916 CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint
*)indices
)[i
] ));
919 _mesa_error( ctx
, GL_INVALID_ENUM
, "glDrawElements(type)" );
923 CALL_End(GET_DISPATCH(), ());
925 _ae_unmap_vbos( ctx
);
928 static void GLAPIENTRY
_save_OBE_DrawRangeElements(GLenum mode
,
929 GLuint start
, GLuint end
,
930 GLsizei count
, GLenum type
,
931 const GLvoid
*indices
)
933 GET_CURRENT_CONTEXT(ctx
);
934 if (_mesa_validate_DrawRangeElements( ctx
, mode
,
936 count
, type
, indices
))
937 _save_OBE_DrawElements( mode
, count
, type
, indices
);
944 static void _save_vtxfmt_init( GLcontext
*ctx
)
946 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
947 GLvertexformat
*vfmt
= &save
->vtxfmt
;
949 vfmt
->ArrayElement
= _ae_loopback_array_elt
; /* generic helper */
950 vfmt
->Begin
= _save_Begin
;
951 vfmt
->Color3f
= _save_Color3f
;
952 vfmt
->Color3fv
= _save_Color3fv
;
953 vfmt
->Color4f
= _save_Color4f
;
954 vfmt
->Color4fv
= _save_Color4fv
;
955 vfmt
->EdgeFlag
= _save_EdgeFlag
;
956 vfmt
->End
= _save_End
;
957 vfmt
->FogCoordfEXT
= _save_FogCoordfEXT
;
958 vfmt
->FogCoordfvEXT
= _save_FogCoordfvEXT
;
959 vfmt
->Indexf
= _save_Indexf
;
960 vfmt
->Indexfv
= _save_Indexfv
;
961 vfmt
->Materialfv
= _save_Materialfv
;
962 vfmt
->MultiTexCoord1fARB
= _save_MultiTexCoord1f
;
963 vfmt
->MultiTexCoord1fvARB
= _save_MultiTexCoord1fv
;
964 vfmt
->MultiTexCoord2fARB
= _save_MultiTexCoord2f
;
965 vfmt
->MultiTexCoord2fvARB
= _save_MultiTexCoord2fv
;
966 vfmt
->MultiTexCoord3fARB
= _save_MultiTexCoord3f
;
967 vfmt
->MultiTexCoord3fvARB
= _save_MultiTexCoord3fv
;
968 vfmt
->MultiTexCoord4fARB
= _save_MultiTexCoord4f
;
969 vfmt
->MultiTexCoord4fvARB
= _save_MultiTexCoord4fv
;
970 vfmt
->Normal3f
= _save_Normal3f
;
971 vfmt
->Normal3fv
= _save_Normal3fv
;
972 vfmt
->SecondaryColor3fEXT
= _save_SecondaryColor3fEXT
;
973 vfmt
->SecondaryColor3fvEXT
= _save_SecondaryColor3fvEXT
;
974 vfmt
->TexCoord1f
= _save_TexCoord1f
;
975 vfmt
->TexCoord1fv
= _save_TexCoord1fv
;
976 vfmt
->TexCoord2f
= _save_TexCoord2f
;
977 vfmt
->TexCoord2fv
= _save_TexCoord2fv
;
978 vfmt
->TexCoord3f
= _save_TexCoord3f
;
979 vfmt
->TexCoord3fv
= _save_TexCoord3fv
;
980 vfmt
->TexCoord4f
= _save_TexCoord4f
;
981 vfmt
->TexCoord4fv
= _save_TexCoord4fv
;
982 vfmt
->Vertex2f
= _save_Vertex2f
;
983 vfmt
->Vertex2fv
= _save_Vertex2fv
;
984 vfmt
->Vertex3f
= _save_Vertex3f
;
985 vfmt
->Vertex3fv
= _save_Vertex3fv
;
986 vfmt
->Vertex4f
= _save_Vertex4f
;
987 vfmt
->Vertex4fv
= _save_Vertex4fv
;
988 vfmt
->VertexAttrib1fARB
= _save_VertexAttrib1fARB
;
989 vfmt
->VertexAttrib1fvARB
= _save_VertexAttrib1fvARB
;
990 vfmt
->VertexAttrib2fARB
= _save_VertexAttrib2fARB
;
991 vfmt
->VertexAttrib2fvARB
= _save_VertexAttrib2fvARB
;
992 vfmt
->VertexAttrib3fARB
= _save_VertexAttrib3fARB
;
993 vfmt
->VertexAttrib3fvARB
= _save_VertexAttrib3fvARB
;
994 vfmt
->VertexAttrib4fARB
= _save_VertexAttrib4fARB
;
995 vfmt
->VertexAttrib4fvARB
= _save_VertexAttrib4fvARB
;
997 vfmt
->VertexAttrib1fNV
= _save_VertexAttrib1fNV
;
998 vfmt
->VertexAttrib1fvNV
= _save_VertexAttrib1fvNV
;
999 vfmt
->VertexAttrib2fNV
= _save_VertexAttrib2fNV
;
1000 vfmt
->VertexAttrib2fvNV
= _save_VertexAttrib2fvNV
;
1001 vfmt
->VertexAttrib3fNV
= _save_VertexAttrib3fNV
;
1002 vfmt
->VertexAttrib3fvNV
= _save_VertexAttrib3fvNV
;
1003 vfmt
->VertexAttrib4fNV
= _save_VertexAttrib4fNV
;
1004 vfmt
->VertexAttrib4fvNV
= _save_VertexAttrib4fvNV
;
1006 /* This will all require us to fallback to saving the list as opcodes:
1008 vfmt
->CallList
= _save_CallList
; /* inside begin/end */
1009 vfmt
->CallLists
= _save_CallLists
; /* inside begin/end */
1010 vfmt
->EvalCoord1f
= _save_EvalCoord1f
;
1011 vfmt
->EvalCoord1fv
= _save_EvalCoord1fv
;
1012 vfmt
->EvalCoord2f
= _save_EvalCoord2f
;
1013 vfmt
->EvalCoord2fv
= _save_EvalCoord2fv
;
1014 vfmt
->EvalPoint1
= _save_EvalPoint1
;
1015 vfmt
->EvalPoint2
= _save_EvalPoint2
;
1017 /* These are all errors as we at least know we are in some sort of
1020 vfmt
->EvalMesh1
= _save_EvalMesh1
;
1021 vfmt
->EvalMesh2
= _save_EvalMesh2
;
1022 vfmt
->Begin
= _save_Begin
;
1023 vfmt
->Rectf
= _save_Rectf
;
1024 vfmt
->DrawArrays
= _save_DrawArrays
;
1025 vfmt
->DrawElements
= _save_DrawElements
;
1026 vfmt
->DrawRangeElements
= _save_DrawRangeElements
;
1031 void vbo_save_SaveFlushVertices( GLcontext
*ctx
)
1033 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1035 /* Noop when we are actually active:
1037 if (ctx
->Driver
.CurrentSavePrimitive
== PRIM_INSIDE_UNKNOWN_PRIM
||
1038 ctx
->Driver
.CurrentSavePrimitive
<= GL_POLYGON
)
1041 if (save
->vert_count
||
1043 _save_compile_vertex_list( ctx
);
1045 _save_copy_to_current( ctx
);
1046 _save_reset_vertex( ctx
);
1047 _save_reset_counters( ctx
);
1048 ctx
->Driver
.SaveNeedFlush
= 0;
1051 void vbo_save_NewList( GLcontext
*ctx
, GLuint list
, GLenum mode
)
1053 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1055 (void) list
; (void) mode
;
1057 if (!save
->prim_store
)
1058 save
->prim_store
= alloc_prim_store( ctx
);
1060 if (!save
->vertex_store
)
1061 save
->vertex_store
= alloc_vertex_store( ctx
);
1063 save
->buffer_ptr
= map_vertex_store( ctx
, save
->vertex_store
);
1065 _save_reset_vertex( ctx
);
1066 _save_reset_counters( ctx
);
1067 ctx
->Driver
.SaveNeedFlush
= 0;
1070 void vbo_save_EndList( GLcontext
*ctx
)
1072 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1074 /* EndList called inside a (saved) Begin/End pair?
1076 if (ctx
->Driver
.CurrentSavePrimitive
!= PRIM_OUTSIDE_BEGIN_END
) {
1078 if (save
->prim_count
> 0) {
1079 GLint i
= save
->prim_count
- 1;
1080 ctx
->Driver
.CurrentSavePrimitive
= PRIM_OUTSIDE_BEGIN_END
;
1081 save
->prim
[i
].end
= 0;
1082 save
->prim
[i
].count
= (save
->vert_count
-
1083 save
->prim
[i
].start
);
1086 /* Make sure this vertex list gets replayed by the "loopback"
1089 save
->dangling_attr_ref
= 1;
1090 vbo_save_SaveFlushVertices( ctx
);
1092 /* Swap out this vertex format while outside begin/end. Any color,
1093 * etc. received between here and the next begin will be compiled
1096 _mesa_install_save_vtxfmt( ctx
, &ctx
->ListState
.ListVtxfmt
);
1099 unmap_vertex_store( ctx
, save
->vertex_store
);
1101 assert(save
->vertex_size
== 0);
1104 void vbo_save_BeginCallList( GLcontext
*ctx
, struct gl_display_list
*dlist
)
1106 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1107 save
->replay_flags
|= dlist
->Flags
;
1110 void vbo_save_EndCallList( GLcontext
*ctx
)
1112 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1114 if (ctx
->ListState
.CallDepth
== 1) {
1115 /* This is correct: want to keep only the VBO_SAVE_FALLBACK
1116 * flag, if it is set:
1118 save
->replay_flags
&= VBO_SAVE_FALLBACK
;
1123 static void vbo_destroy_vertex_list( GLcontext
*ctx
, void *data
)
1125 struct vbo_save_vertex_list
*node
= (struct vbo_save_vertex_list
*)data
;
1128 if ( --node
->vertex_store
->refcount
== 0 )
1129 free_vertex_store( ctx
, node
->vertex_store
);
1131 if ( --node
->prim_store
->refcount
== 0 )
1132 FREE( node
->prim_store
);
1136 static void vbo_print_vertex_list( GLcontext
*ctx
, void *data
)
1138 struct vbo_save_vertex_list
*node
= (struct vbo_save_vertex_list
*)data
;
1142 _mesa_debug(NULL
, "VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1147 for (i
= 0 ; i
< node
->prim_count
; i
++) {
1148 struct _mesa_prim
*prim
= &node
->prim
[i
];
1149 _mesa_debug(NULL
, " prim %d: %s%s %d..%d %s %s\n",
1151 _mesa_lookup_prim_by_nr(prim
->mode
),
1152 prim
->weak
? " (weak)" : "",
1154 prim
->start
+ prim
->count
,
1155 (prim
->begin
) ? "BEGIN" : "(wrap)",
1156 (prim
->end
) ? "END" : "(wrap)");
1161 static void _save_current_init( GLcontext
*ctx
)
1163 struct vbo_save_context
*save
= &vbo_context(ctx
)->save
;
1166 for (i
= VBO_ATTRIB_POS
; i
<= VBO_ATTRIB_GENERIC15
; i
++) {
1167 const GLuint j
= i
- VBO_ATTRIB_POS
;
1168 ASSERT(j
< VERT_ATTRIB_MAX
);
1169 save
->currentsz
[i
] = &ctx
->ListState
.ActiveAttribSize
[j
];
1170 save
->current
[i
] = ctx
->ListState
.CurrentAttrib
[j
];
1173 for (i
= VBO_ATTRIB_FIRST_MATERIAL
; i
<= VBO_ATTRIB_LAST_MATERIAL
; i
++) {
1174 const GLuint j
= i
- VBO_ATTRIB_FIRST_MATERIAL
;
1175 ASSERT(j
< MAT_ATTRIB_MAX
);
1176 save
->currentsz
[i
] = &ctx
->ListState
.ActiveMaterialSize
[j
];
1177 save
->current
[i
] = ctx
->ListState
.CurrentMaterial
[j
];
1182 * Initialize the display list compiler
1184 void vbo_save_api_init( struct vbo_save_context
*save
)
1186 GLcontext
*ctx
= save
->ctx
;
1189 save
->opcode_vertex_list
=
1190 _mesa_alloc_opcode( ctx
,
1191 sizeof(struct vbo_save_vertex_list
),
1192 vbo_save_playback_vertex_list
,
1193 vbo_destroy_vertex_list
,
1194 vbo_print_vertex_list
);
1196 ctx
->Driver
.NotifySaveBegin
= vbo_save_NotifyBegin
;
1198 _save_vtxfmt_init( ctx
);
1199 _save_current_init( ctx
);
1201 /* These will actually get set again when binding/drawing */
1202 for (i
= 0; i
< VBO_ATTRIB_MAX
; i
++)
1203 save
->inputs
[i
] = &save
->arrays
[i
];
1205 /* Hook our array functions into the outside-begin-end vtxfmt in
1208 ctx
->ListState
.ListVtxfmt
.Rectf
= _save_OBE_Rectf
;
1209 ctx
->ListState
.ListVtxfmt
.DrawArrays
= _save_OBE_DrawArrays
;
1210 ctx
->ListState
.ListVtxfmt
.DrawElements
= _save_OBE_DrawElements
;
1211 ctx
->ListState
.ListVtxfmt
.DrawRangeElements
= _save_OBE_DrawRangeElements
;
1212 _mesa_install_save_vtxfmt( ctx
, &ctx
->ListState
.ListVtxfmt
);