1 /* $Id: t_array_api.c,v 1.6 2001/02/15 01:33:52 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>
31 #include "api_validate.h"
39 #include "array_cache/acache.h"
41 #include "t_array_api.h"
42 #include "t_array_import.h"
43 #include "t_imm_api.h"
44 #include "t_imm_exec.h"
45 #include "t_context.h"
46 #include "t_pipeline.h"
48 static void fallback_drawarrays( GLcontext
*ctx
, GLenum mode
, GLint start
,
51 /* Need to produce immediate structs, either for compiling or
52 * because the array range is too large to process in a single
53 * VB. In GL_EXECUTE mode, this introduces two redundant
54 * operations: producing the flag array and computing the orflag
58 if (_tnl_hard_begin( ctx
, mode
)) {
60 for (j
= 0 ; j
< count
; ) {
61 struct immediate
*IM
= TNL_CURRENT_IM(ctx
);
62 GLuint nr
= MIN2( IMM_MAXDATA
- IM
->Start
, count
- j
);
63 GLuint sf
= IM
->Flag
[IM
->Start
];
65 _tnl_fill_immediate_drawarrays( ctx
, IM
, j
, j
+nr
);
67 if (j
== 0) IM
->Flag
[IM
->Start
] |= sf
;
69 IM
->Count
= IM
->Start
+ nr
;
75 _tnl_flush_immediate( IM
);
79 /* Simple alternative to above code.
81 if (_tnl_hard_begin( ctx
, mode
))
84 for (i
=start
;i
<count
;i
++) {
85 _tnl_array_element( ctx
, i
);
93 static void _tnl_draw_elements( GLcontext
*ctx
, GLenum mode
, GLsizei count
,
94 const GLuint
*indices
)
97 /* Optimized code that fakes the effect of calling
98 * _tnl_array_element for each index in the list.
100 if (_tnl_hard_begin( ctx
, mode
)) {
102 for (j
= 0 ; j
< count
; ) {
103 struct immediate
*IM
= TNL_CURRENT_IM(ctx
);
104 GLuint start
= IM
->Start
;
105 GLuint nr
= MIN2( IMM_MAXDATA
- start
, count
- j
) + start
;
106 GLuint sf
= IM
->Flag
[start
];
109 for (i
= start
; i
< nr
; i
++) {
110 IM
->Elt
[i
] = (GLuint
) *indices
++;
111 IM
->Flag
[i
] = VERT_ELT
;
114 if (j
== 0) IM
->Flag
[start
] |= sf
;
122 _tnl_flush_immediate( IM
);
126 /* Simple version of the above code.
128 if (_tnl_hard_begin(ctx
, mode
)) {
130 for (i
= 0 ; i
< count
; i
++)
131 _tnl_array_element( ctx
, indices
[i
] );
138 static void _tnl_draw_range_elements( GLcontext
*ctx
, GLenum mode
,
139 GLuint start
, GLuint end
,
140 GLsizei count
, const GLuint
*indices
)
143 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
144 FLUSH_CURRENT( ctx
, 0 );
146 _tnl_vb_bind_arrays( ctx
, start
, end
);
148 tnl
->vb
.FirstPrimitive
= 0;
149 tnl
->vb
.Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
150 tnl
->vb
.PrimitiveLength
[0] = count
;
151 tnl
->vb
.Elts
= (GLuint
*)indices
;
153 if (ctx
->Array
.LockCount
)
154 _tnl_run_pipeline( ctx
);
156 /* Note that arrays may have changed before/after execution.
158 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
159 _tnl_run_pipeline( ctx
);
160 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
168 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
170 GET_CURRENT_CONTEXT(ctx
);
171 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
172 struct vertex_buffer
*VB
= &tnl
->vb
;
174 /* Check arguments, etc.
176 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
179 if (tnl
->pipeline
.build_state_changes
)
180 _tnl_validate_pipeline( ctx
);
182 if (!ctx
->CompileFlag
&& count
- start
< ctx
->Const
.MaxArrayLockSize
) {
183 FLUSH_CURRENT( ctx
, 0 );
185 if (ctx
->Array
.LockCount
)
187 if (start
< ctx
->Array
.LockFirst
) start
= ctx
->Array
.LockFirst
;
188 if (count
> ctx
->Array
.LockCount
) count
= ctx
->Array
.LockCount
;
189 if (start
>= count
) return;
191 /* Locked drawarrays. Reuse any previously transformed data.
193 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
194 VB
->FirstPrimitive
= start
;
195 VB
->Primitive
[start
] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
196 VB
->PrimitiveLength
[start
] = count
- start
;
197 _tnl_run_pipeline( ctx
);
199 /* The arrays are small enough to fit in a single VB; just bind
200 * them and go. Any untransformed data will be copied on
203 * Invalidate any locked data dependent on these arrays.
205 _tnl_vb_bind_arrays( ctx
, start
, count
);
206 VB
->FirstPrimitive
= 0;
207 VB
->Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
208 VB
->PrimitiveLength
[0] = count
- start
;
209 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
210 _tnl_run_pipeline( ctx
);
211 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
214 else if (!ctx
->CompileFlag
&& mode
== GL_TRIANGLE_STRIP
) {
215 int bufsz
= (ctx
->Const
.MaxArrayLockSize
- 2) & ~1;
218 FLUSH_CURRENT( ctx
, 0 );
220 /* TODO: other non-fan primitives.
222 for (j
= start
; j
< count
- 2; j
+= nr
- 2 ) {
223 nr
= MIN2( bufsz
, count
- j
);
224 _tnl_vb_bind_arrays( ctx
, j
, j
+ nr
);
225 VB
->FirstPrimitive
= 0;
226 VB
->Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
227 VB
->PrimitiveLength
[0] = nr
;
228 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
229 _tnl_run_pipeline( ctx
);
230 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
233 fallback_drawarrays( ctx
, mode
, start
, count
);
239 _tnl_DrawRangeElements(GLenum mode
,
240 GLuint start
, GLuint end
,
241 GLsizei count
, GLenum type
, const GLvoid
*indices
)
243 GET_CURRENT_CONTEXT(ctx
);
244 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
247 /* Check arguments, etc.
249 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
253 if (tnl
->pipeline
.build_state_changes
)
254 _tnl_validate_pipeline( ctx
);
256 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
257 count
, type
, indices
);
260 if (ctx
->Array
.LockCount
) {
261 /* Are the arrays already locked? If so we currently have to look
262 * at the whole locked range.
264 if (start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
265 _tnl_draw_range_elements( ctx
, mode
,
266 ctx
->Array
.LockFirst
,
267 ctx
->Array
.LockCount
,
270 /* The spec says referencing elements outside the locked
271 * range is undefined. I'm going to make it a noop this time
272 * round, maybe come up with something beter before 3.6.
274 * May be able to get away with just setting LockCount==0,
275 * though this raises the problems of dependent state. May
276 * have to call glUnlockArrays() directly?
278 * Or scan the list and replace bad indices?
281 "DrawRangeElements references "
282 "elements outside locked range.");
285 else if (end
+ 1 - start
< ctx
->Const
.MaxArrayLockSize
) {
286 /* The arrays aren't locked but we can still fit them inside a
287 * single vertexbuffer.
289 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
291 /* Range is too big to optimize:
293 _tnl_draw_elements( ctx
, mode
, count
, ui_indices
);
300 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
301 const GLvoid
*indices
)
303 GET_CURRENT_CONTEXT(ctx
);
304 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
307 /* Check arguments, etc.
309 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
312 if (tnl
->pipeline
.build_state_changes
)
313 _tnl_validate_pipeline( ctx
);
315 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
316 count
, type
, indices
);
318 if (ctx
->Array
.LockCount
) {
319 _tnl_draw_range_elements( ctx
, mode
,
320 ctx
->Array
.LockFirst
,
321 ctx
->Array
.LockCount
,
325 /* Scan the index list and see if we can use the locked path anyway.
330 for (i
= 0 ; i
< count
; i
++)
331 if (ui_indices
[i
] > max_elt
) max_elt
= ui_indices
[i
];
333 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
334 max_elt
< count
) /* do we want to use it? */
335 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
337 _tnl_draw_elements( ctx
, mode
, count
, ui_indices
);
342 void _tnl_array_init( GLcontext
*ctx
)
344 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
345 struct vertex_arrays
*tmp
= &tnl
->array_inputs
;
346 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->vtxfmt
);
349 vfmt
->DrawArrays
= _tnl_DrawArrays
;
350 vfmt
->DrawElements
= _tnl_DrawElements
;
351 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
353 /* Setup vector pointers that will be used to bind arrays to VB's.
355 gl_vector4f_init( &tmp
->Obj
, 0, 0 );
356 gl_vector3f_init( &tmp
->Normal
, 0, 0 );
357 #if CHAN_TYPE == GL_UNSIGNED_BYTE
358 gl_vector4ub_init( &tmp
->Color
, 0, 0 );
359 gl_vector4ub_init( &tmp
->SecondaryColor
, 0, 0 );
360 #elif CHAN_TYPE == GL_UNSIGNED_SHORT
361 gl_vector4us_init( &tmp
->Color
, 0, 0 );
362 gl_vector4us_init( &tmp
->SecondaryColor
, 0, 0 );
363 #elif CHAN_TYPE == GL_FLOAT
364 gl_vector4f_init( &tmp
->Color
, 0, 0 );
365 gl_vector4f_init( &tmp
->SecondaryColor
, 0, 0 );
367 gl_vector1f_init( &tmp
->FogCoord
, 0, 0 );
368 gl_vector1ui_init( &tmp
->Index
, 0, 0 );
369 gl_vector1ub_init( &tmp
->EdgeFlag
, 0, 0 );
371 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
372 gl_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
374 tnl
->tmp_primitive
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
375 tnl
->tmp_primitive_length
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
379 void _tnl_array_destroy( GLcontext
*ctx
)
381 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
382 if (tnl
->tmp_primitive_length
) FREE(tnl
->tmp_primitive_length
);
383 if (tnl
->tmp_primitive
) FREE(tnl
->tmp_primitive
);