1 /* $Id: t_imm_dlist.c,v 1.21 2001/06/28 17:34:14 keithw Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 * Keith Whitwell <keithw@valinux.com>
39 #include "t_context.h"
40 #include "t_imm_api.h"
41 #include "t_imm_elt.h"
42 #include "t_imm_alloc.h"
43 #include "t_imm_dlist.h"
44 #include "t_imm_debug.h"
45 #include "t_imm_exec.h"
46 #include "t_imm_fixup.h"
47 #include "t_pipeline.h"
54 GLuint SavedBeginState
;
61 GLuint MaterialOrMask
;
62 GLuint MaterialAndMask
;
65 static void execute_compiled_cassette( GLcontext
*ctx
, void *data
);
66 static void loopback_compiled_cassette( GLcontext
*ctx
, struct immediate
*IM
);
69 static void build_normal_lengths( struct immediate
*IM
)
73 GLfloat (*data
)[3] = IM
->Normal
+ IM
->Start
;
74 GLfloat
*dest
= IM
->NormalLengthPtr
;
75 GLuint
*flags
= IM
->Flag
+ IM
->Start
;
76 GLuint count
= IM
->Count
- IM
->Start
;
79 dest
= IM
->NormalLengthPtr
= ALIGN_MALLOC( IMM_SIZE
*sizeof(GLfloat
), 32 );
84 len
= (GLfloat
) LEN_3FV( data
[0] );
85 if (len
> 0.0) len
= 1.0/len
;
87 for (i
= 0 ; i
< count
; ) {
89 if (flags
[++i
] & VERT_NORM
) {
90 len
= (GLfloat
) LEN_3FV( data
[i
] );
91 if (len
> 0.0) len
= 1.0/len
;
96 static void fixup_normal_lengths( struct immediate
*IM
)
100 GLfloat (*data
)[3] = IM
->Normal
;
101 GLfloat
*dest
= IM
->NormalLengthPtr
;
102 GLuint
*flags
= IM
->Flag
;
104 for (i
= IM
->CopyStart
; i
<= IM
->Start
; i
++) {
105 len
= (GLfloat
) LEN_3FV( data
[i
] );
106 if (len
> 0.0) len
= 1.0/len
;
110 while (!(flags
[i
] & (VERT_NORM
|VERT_END_VB
))) {
118 /* Insert the active immediate struct onto the display list currently
122 _tnl_compile_cassette( GLcontext
*ctx
, struct immediate
*IM
)
124 struct immediate
*im
= TNL_CURRENT_IM(ctx
);
125 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
126 TNLvertexcassette
*node
;
127 GLuint new_beginstate
;
130 ASSERT (IM
->FlushElt
== FLUSH_ELT_LAZY
);
131 _tnl_translate_array_elts( ctx
, IM
, IM
->Start
, IM
->Count
);
134 _tnl_compute_orflag( IM
, IM
->Start
);
136 /* Need to clear this flag, or fixup gets confused. (The
137 * array-elements have been translated away by now, so it's ok to
140 IM
->OrFlag
&= ~VERT_ELT
;
141 IM
->AndFlag
&= ~VERT_ELT
;
143 _tnl_fixup_input( ctx
, IM
);
145 node
= (TNLvertexcassette
*)
146 _mesa_alloc_instruction(ctx
,
147 tnl
->opcode_vertex_cassette
,
148 sizeof(TNLvertexcassette
));
152 node
->IM
= im
; im
->ref_count
++;
153 node
->Start
= im
->Start
;
154 node
->Count
= im
->Count
;
155 node
->BeginState
= im
->BeginState
;
156 node
->SavedBeginState
= im
->SavedBeginState
;
157 node
->OrFlag
= im
->OrFlag
;
158 node
->TexSize
= im
->TexSize
;
159 node
->AndFlag
= im
->AndFlag
;
160 node
->LastData
= im
->LastData
;
161 node
->LastPrimitive
= im
->LastPrimitive
;
162 node
->LastMaterial
= im
->LastMaterial
;
163 node
->MaterialOrMask
= im
->MaterialOrMask
;
164 node
->MaterialAndMask
= im
->MaterialAndMask
;
166 if (tnl
->CalcDListNormalLengths
) {
167 build_normal_lengths( im
);
170 if (ctx
->ExecuteFlag
) {
171 execute_compiled_cassette( ctx
, (void *)node
);
174 /* Discard any errors raised in the last cassette.
176 new_beginstate
= node
->BeginState
& (VERT_BEGIN_0
|VERT_BEGIN_1
);
178 /* Decide whether this immediate struct is full, or can be used for
179 * the next batch of vertices as well.
181 if (im
->Count
> IMM_MAXDATA
- 16) {
184 struct immediate
*new_im
= _tnl_alloc_immediate(ctx
);
186 im
->ref_count
--; /* remove CURRENT_IM reference */
187 ASSERT(im
->ref_count
> 0); /* it is compiled into a display list */
188 SET_IMMEDIATE( ctx
, new_im
);
189 _tnl_reset_compile_input( ctx
, IMM_MAX_COPIED_VERTS
,
190 new_beginstate
, node
->SavedBeginState
);
192 /* Still some room in the current immediate.
194 _tnl_reset_compile_input( ctx
, im
->Count
+1+IMM_MAX_COPIED_VERTS
,
195 new_beginstate
, node
->SavedBeginState
);
200 static void fixup_compiled_primitives( GLcontext
*ctx
, struct immediate
*IM
)
202 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
204 /* Can potentially overwrite primitive details - need to save the
207 tnl
->DlistPrimitive
= IM
->Primitive
[IM
->Start
];
208 tnl
->DlistPrimitiveLength
= IM
->PrimitiveLength
[IM
->Start
];
209 tnl
->DlistLastPrimitive
= IM
->LastPrimitive
;
211 /* The first primitive may be different from what was recorded in
212 * the immediate struct. Consider an immediate that starts with a
213 * glBegin, compiled in a display list, which is called from within
214 * an existing Begin/End object.
216 if (ctx
->Driver
.CurrentExecPrimitive
== GL_POLYGON
+1) {
219 if (IM
->BeginState
& VERT_ERROR_1
)
220 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glBegin/glEnd");
222 for (i
= IM
->Start
; i
<= IM
->Count
; i
+= IM
->PrimitiveLength
[i
])
223 if (IM
->Flag
[i
] & (VERT_BEGIN
|VERT_END_VB
))
226 /* Would like to just ignore vertices upto this point. Can't
227 * set copystart because it might skip materials?
229 ASSERT(IM
->Start
== IM
->CopyStart
);
230 if (i
> IM
->CopyStart
) {
231 IM
->Primitive
[IM
->CopyStart
] = GL_POLYGON
+1;
232 IM
->PrimitiveLength
[IM
->CopyStart
] = i
- IM
->CopyStart
;
233 if (IM
->Flag
[i
] & VERT_END_VB
) {
234 IM
->Primitive
[IM
->CopyStart
] |= PRIM_LAST
;
235 IM
->LastPrimitive
= IM
->CopyStart
;
241 if (IM
->BeginState
& VERT_ERROR_0
)
242 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glBegin/glEnd");
244 if (IM
->CopyStart
== IM
->Start
&&
245 IM
->Flag
[IM
->Start
] & (VERT_END
|VERT_END_VB
))
250 IM
->Primitive
[IM
->CopyStart
] = ctx
->Driver
.CurrentExecPrimitive
;
252 IM
->Primitive
[IM
->CopyStart
] |= PRIM_PARITY
;
254 /* one of these should be true, else we'll be in an infinite loop
256 ASSERT(IM
->PrimitiveLength
[IM
->Start
] > 0 ||
257 IM
->Flag
[IM
->Start
] & (VERT_END
|VERT_END_VB
));
259 for (i
= IM
->Start
; i
<= IM
->Count
; i
+= IM
->PrimitiveLength
[i
])
260 if (IM
->Flag
[i
] & (VERT_END
|VERT_END_VB
)) {
261 IM
->PrimitiveLength
[IM
->CopyStart
] = i
- IM
->CopyStart
;
262 if (IM
->Flag
[i
] & VERT_END_VB
) {
263 IM
->Primitive
[IM
->CopyStart
] |= PRIM_LAST
;
264 IM
->LastPrimitive
= IM
->CopyStart
;
266 if (IM
->Flag
[i
] & VERT_END
) {
267 IM
->Primitive
[IM
->CopyStart
] |= PRIM_END
;
275 /* Undo any changes potentially made to the immediate in the range
276 * IM->Start..IM->Count above.
278 static void restore_compiled_primitives( GLcontext
*ctx
, struct immediate
*IM
)
280 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
281 IM
->Primitive
[IM
->Start
] = tnl
->DlistPrimitive
;
282 IM
->PrimitiveLength
[IM
->Start
] = tnl
->DlistPrimitiveLength
;
288 execute_compiled_cassette( GLcontext
*ctx
, void *data
)
290 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
291 TNLvertexcassette
*node
= (TNLvertexcassette
*)data
;
292 struct immediate
*IM
= node
->IM
;
294 /* fprintf(stderr, "%s\n", __FUNCTION__); */
296 IM
->Start
= node
->Start
;
297 IM
->CopyStart
= node
->Start
;
298 IM
->Count
= node
->Count
;
299 IM
->BeginState
= node
->BeginState
;
300 IM
->SavedBeginState
= node
->SavedBeginState
;
301 IM
->OrFlag
= node
->OrFlag
;
302 IM
->TexSize
= node
->TexSize
;
303 IM
->AndFlag
= node
->AndFlag
;
304 IM
->LastData
= node
->LastData
;
305 IM
->LastPrimitive
= node
->LastPrimitive
;
306 IM
->LastMaterial
= node
->LastMaterial
;
307 IM
->MaterialOrMask
= node
->MaterialOrMask
;
308 IM
->MaterialAndMask
= node
->MaterialAndMask
;
310 if ((MESA_VERBOSE
& VERBOSE_DISPLAY_LIST
) &&
311 (MESA_VERBOSE
& VERBOSE_IMMEDIATE
))
312 _tnl_print_cassette( IM
);
314 if (MESA_VERBOSE
& VERBOSE_DISPLAY_LIST
) {
315 fprintf(stderr
, "Run cassette %d, rows %d..%d, beginstate %x ",
317 IM
->Start
, IM
->Count
, IM
->BeginState
);
318 _tnl_print_vert_flags("orflag", IM
->OrFlag
);
322 /* Need to respect 'HardBeginEnd' even if the commands are looped
323 * back to a driver tnl module.
325 if (IM
->SavedBeginState
) {
326 if (ctx
->Driver
.CurrentExecPrimitive
== GL_POLYGON
+1)
327 tnl
->ReplayHardBeginEnd
= 1;
328 if (!tnl
->ReplayHardBeginEnd
) {
329 /* This is a user error. Whatever operation (like glRectf)
330 * decomposed to this hard begin/end pair is now being run
331 * inside a begin/end object -- illegally. Reject it and
334 _mesa_error(ctx
, GL_INVALID_OPERATION
, "hard replay");
339 if (tnl
->LoopbackDListCassettes
) {
340 fixup_compiled_primitives( ctx
, IM
);
341 loopback_compiled_cassette( ctx
, IM
);
342 restore_compiled_primitives( ctx
, IM
);
344 else if (IM
->Count
== IM
->Start
) {
345 _tnl_copy_to_current( ctx
, IM
, IM
->OrFlag
);
349 _mesa_update_state(ctx
);
351 if (tnl
->pipeline
.build_state_changes
)
352 _tnl_validate_pipeline( ctx
);
354 _tnl_fixup_compiled_cassette( ctx
, IM
);
355 fixup_compiled_primitives( ctx
, IM
);
357 if (IM
->Primitive
[IM
->LastPrimitive
] & PRIM_END
)
358 ctx
->Driver
.CurrentExecPrimitive
= GL_POLYGON
+1;
360 ctx
->Driver
.CurrentExecPrimitive
=
361 IM
->Primitive
[IM
->LastPrimitive
] & PRIM_MODE_MASK
;
363 _tnl_get_exec_copy_verts( ctx
, IM
);
365 if (IM
->NormalLengthPtr
)
366 fixup_normal_lengths( IM
);
368 _tnl_run_cassette( ctx
, IM
);
370 restore_compiled_primitives( ctx
, IM
);
373 if (ctx
->Driver
.CurrentExecPrimitive
== GL_POLYGON
+1)
374 tnl
->ReplayHardBeginEnd
= 0;
378 destroy_compiled_cassette( GLcontext
*ctx
, void *data
)
380 TNLvertexcassette
*node
= (TNLvertexcassette
*)data
;
382 if ( --node
->IM
->ref_count
== 0 )
383 _tnl_free_immediate( node
->IM
);
388 print_compiled_cassette( GLcontext
*ctx
, void *data
)
390 TNLvertexcassette
*node
= (TNLvertexcassette
*)data
;
391 struct immediate
*IM
= node
->IM
;
393 fprintf(stderr
, "TNL-VERTEX-CASSETTE, id %u, rows %u..%u\n",
394 node
->IM
->id
, node
->Start
, node
->Count
);
396 IM
->Start
= node
->Start
;
397 IM
->Count
= node
->Count
;
398 IM
->BeginState
= node
->BeginState
;
399 IM
->OrFlag
= node
->OrFlag
;
400 IM
->TexSize
= node
->TexSize
;
401 IM
->AndFlag
= node
->AndFlag
;
402 IM
->LastData
= node
->LastData
;
403 IM
->LastPrimitive
= node
->LastPrimitive
;
404 IM
->LastMaterial
= node
->LastMaterial
;
405 IM
->MaterialOrMask
= node
->MaterialOrMask
;
406 IM
->MaterialAndMask
= node
->MaterialAndMask
;
408 _tnl_print_cassette( node
->IM
);
412 _tnl_BeginCallList( GLcontext
*ctx
, GLuint list
)
416 FLUSH_CURRENT(ctx
, 0);
420 /* Called at the tail of a CallList. Nothing to do.
423 _tnl_EndCallList( GLcontext
*ctx
)
430 _tnl_EndList( GLcontext
*ctx
)
432 struct immediate
*IM
= TNL_CURRENT_IM(ctx
);
437 /* outside begin/end, even in COMPILE_AND_EXEC,
438 * so no vertices to copy, right?
440 ASSERT(TNL_CONTEXT(ctx
)->ExecCopyCount
== 0);
442 /* If this one isn't free, get a clean one. (Otherwise we'll be
443 * using one that's already half full).
445 if (IM
->ref_count
!= 0)
446 IM
= _tnl_alloc_immediate( ctx
);
448 ASSERT(IM
->ref_count
== 0);
450 SET_IMMEDIATE( ctx
, IM
);
453 _tnl_reset_exec_input( ctx
, IMM_MAX_COPIED_VERTS
, 0, 0 );
458 _tnl_NewList( GLcontext
*ctx
, GLuint list
, GLenum mode
)
460 struct immediate
*IM
= TNL_CURRENT_IM(ctx
);
462 /* Use the installed immediate struct. No vertices in the current
463 * immediate, no copied vertices in the system.
465 ASSERT(TNL_CURRENT_IM(ctx
));
466 ASSERT(TNL_CURRENT_IM(ctx
)->Start
== IMM_MAX_COPIED_VERTS
);
467 ASSERT(TNL_CURRENT_IM(ctx
)->Start
== TNL_CURRENT_IM(ctx
)->Count
);
468 ASSERT(TNL_CONTEXT(ctx
)->ExecCopyCount
== 0);
470 /* Set current Begin/End state to unknown:
472 IM
->BeginState
= VERT_BEGIN_0
;
473 ctx
->Driver
.CurrentSavePrimitive
= PRIM_UNKNOWN
;
478 _tnl_dlist_init( GLcontext
*ctx
)
480 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
482 tnl
->opcode_vertex_cassette
=
483 _mesa_alloc_opcode( ctx
,
484 sizeof(TNLvertexcassette
),
485 execute_compiled_cassette
,
486 destroy_compiled_cassette
,
487 print_compiled_cassette
);
491 static void emit_material( struct gl_material
*src
, GLuint bitmask
)
493 if (bitmask
& FRONT_EMISSION_BIT
)
494 glMaterialfv( GL_FRONT
, GL_EMISSION
, src
[0].Emission
);
496 if (bitmask
& BACK_EMISSION_BIT
)
497 glMaterialfv( GL_BACK
, GL_EMISSION
, src
[1].Emission
);
499 if (bitmask
& FRONT_AMBIENT_BIT
)
500 glMaterialfv( GL_FRONT
, GL_AMBIENT
, src
[0].Ambient
);
502 if (bitmask
& BACK_AMBIENT_BIT
)
503 glMaterialfv( GL_BACK
, GL_AMBIENT
, src
[1].Ambient
);
505 if (bitmask
& FRONT_DIFFUSE_BIT
)
506 glMaterialfv( GL_FRONT
, GL_DIFFUSE
, src
[0].Diffuse
);
508 if (bitmask
& BACK_DIFFUSE_BIT
)
509 glMaterialfv( GL_BACK
, GL_DIFFUSE
, src
[1].Diffuse
);
511 if (bitmask
& FRONT_SPECULAR_BIT
)
512 glMaterialfv( GL_FRONT
, GL_SPECULAR
, src
[0].Specular
);
514 if (bitmask
& BACK_SPECULAR_BIT
)
515 glMaterialfv( GL_BACK
, GL_SPECULAR
, src
[1].Specular
);
517 if (bitmask
& FRONT_SHININESS_BIT
)
518 glMaterialfv( GL_FRONT
, GL_SHININESS
, &src
[0].Shininess
);
520 if (bitmask
& BACK_SHININESS_BIT
)
521 glMaterialfv( GL_BACK
, GL_SHININESS
, &src
[1].Shininess
);
523 if (bitmask
& FRONT_INDEXES_BIT
) {
525 ind
[0] = src
[0].AmbientIndex
;
526 ind
[1] = src
[0].DiffuseIndex
;
527 ind
[2] = src
[0].SpecularIndex
;
528 glMaterialfv( GL_FRONT
, GL_COLOR_INDEXES
, ind
);
531 if (bitmask
& BACK_INDEXES_BIT
) {
533 ind
[0] = src
[1].AmbientIndex
;
534 ind
[1] = src
[1].DiffuseIndex
;
535 ind
[2] = src
[1].SpecularIndex
;
536 glMaterialfv( GL_BACK
, GL_COLOR_INDEXES
, ind
);
541 /* Low-performance helper function to allow driver-supplied tnl
542 * modules to process tnl display lists. This is primarily supplied
543 * to avoid fallbacks if CallList is invoked inside a Begin/End pair.
544 * For higher performance, drivers should fallback to tnl (if outside
545 * begin/end), or (for tnl hardware) implement their own display list
548 static void loopback_compiled_cassette( GLcontext
*ctx
, struct immediate
*IM
)
551 GLuint
*flags
= IM
->Flag
;
552 GLuint orflag
= IM
->OrFlag
;
554 void (*vertex
)( const GLfloat
* );
555 void (*texcoordfv
[MAX_TEXTURE_UNITS
])( GLuint
, const GLfloat
* );
557 GLuint p
, length
, prim
= 0;
559 if (orflag
& VERT_OBJ_234
)
560 vertex
= glVertex4fv
;
562 vertex
= glVertex3fv
;
564 if (orflag
& VERT_TEX_ANY
) {
565 for (j
= 0 ; j
< ctx
->Const
.MaxTextureUnits
; j
++) {
566 if (orflag
& VERT_TEX(j
)) {
568 if ((IM
->TexSize
& TEX_SIZE_4(j
)) == TEX_SIZE_4(j
))
569 texcoordfv
[j
] = glMultiTexCoord4fvARB
;
570 else if (IM
->TexSize
& TEX_SIZE_3(j
))
571 texcoordfv
[j
] = glMultiTexCoord3fvARB
;
573 texcoordfv
[j
] = glMultiTexCoord2fvARB
;
578 for (p
= IM
->Start
; !(prim
& PRIM_LAST
) ; p
+= length
)
580 prim
= IM
->Primitive
[p
];
581 length
= IM
->PrimitiveLength
[p
];
582 ASSERT(length
|| (prim
& PRIM_LAST
));
583 ASSERT((prim
& PRIM_MODE_MASK
) <= GL_POLYGON
+1);
585 if (prim
& PRIM_BEGIN
) {
586 /* fprintf(stderr, "begin %s\n", _mesa_prim_name[prim&PRIM_MODE_MASK]); */
587 glBegin(prim
& PRIM_MODE_MASK
);
590 for ( i
= p
; i
<= p
+length
; i
++) {
591 if (flags
[i
] & VERT_TEX_ANY
) {
593 for (k
= 0 ; k
< maxtex
; k
++) {
594 if (flags
[i
] & VERT_TEX(k
)) {
595 texcoordfv
[k
]( GL_TEXTURE0_ARB
+ k
, IM
->TexCoord
[k
][i
] );
600 if (flags
[i
] & VERT_NORM
) {
601 /* fprintf(stderr, "normal %d: %f %f %f\n", i, */
602 /* IM->Normal[i][0], IM->Normal[i][1], IM->Normal[i][2]); */
603 glNormal3fv(IM
->Normal
[i
]);
606 if (flags
[i
] & VERT_RGBA
) {
607 /* fprintf(stderr, "color %d: %f %f %f\n", i, */
608 /* IM->Color[i][0], IM->Color[i][1], IM->Color[i][2]); */
609 glColor4fv( IM
->Color
[i
] );
612 if (flags
[i
] & VERT_SPEC_RGB
)
613 glSecondaryColor3fvEXT( IM
->SecondaryColor
[i
] );
615 if (flags
[i
] & VERT_FOG_COORD
)
616 glFogCoordfEXT( IM
->FogCoord
[i
] );
618 if (flags
[i
] & VERT_INDEX
)
619 glIndexi( IM
->Index
[i
] );
621 if (flags
[i
] & VERT_EDGE
)
622 glEdgeFlag( IM
->EdgeFlag
[i
] );
624 if (flags
[i
] & VERT_MATERIAL
)
625 emit_material( IM
->Material
[i
], IM
->MaterialMask
[i
] );
627 if (flags
[i
]&VERT_OBJ_234
) {
628 /* fprintf(stderr, "vertex %d: %f %f %f\n", i, */
629 /* IM->Obj[i][0], IM->Obj[i][1], IM->Obj[i][2]); */
630 vertex( IM
->Obj
[i
] );
632 else if (flags
[i
] & VERT_EVAL_C1
)
633 glEvalCoord1f(IM
->Obj
[i
][0]);
634 else if (flags
[i
] & VERT_EVAL_P1
)
635 glEvalPoint1(IM
->Obj
[i
][0]);
636 else if (flags
[i
] & VERT_EVAL_C2
)
637 glEvalCoord2f( IM
->Obj
[i
][0], IM
->Obj
[i
][1]);
638 else if (flags
[i
] & VERT_EVAL_P2
)
639 glEvalPoint2( IM
->Obj
[i
][0], IM
->Obj
[i
][1]);
642 if (prim
& PRIM_END
) {
643 /* fprintf(stderr, "end\n"); */