1 /* $Id: t_array_api.c,v 1.19 2001/09/14 17:00:42 brianp 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 if (_tnl_hard_begin( ctx
, mode
)) {
53 for (i
= start
; i
< count
; i
++) {
54 _tnl_array_element( ctx
, i
);
61 static void fallback_drawelements( GLcontext
*ctx
, GLenum mode
, GLsizei count
,
62 const GLuint
*indices
)
64 /* Simple version of the above code.
66 if (_tnl_hard_begin(ctx
, mode
)) {
68 for (i
= 0 ; i
< count
; i
++)
69 _tnl_array_element( ctx
, indices
[i
] );
75 static void _tnl_draw_range_elements( GLcontext
*ctx
, GLenum mode
,
76 GLuint start
, GLuint end
,
77 GLsizei count
, const GLuint
*indices
)
80 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
81 FLUSH_CURRENT( ctx
, 0 );
83 _tnl_vb_bind_arrays( ctx
, start
, end
);
85 tnl
->vb
.FirstPrimitive
= 0;
86 tnl
->vb
.Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
87 tnl
->vb
.PrimitiveLength
[0] = count
;
88 tnl
->vb
.Elts
= (GLuint
*)indices
;
90 if (ctx
->Array
.LockCount
)
91 tnl
->Driver
.RunPipeline( ctx
);
93 /* Note that arrays may have changed before/after execution.
95 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
96 tnl
->Driver
.RunPipeline( ctx
);
97 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
105 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
107 GET_CURRENT_CONTEXT(ctx
);
108 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
109 struct vertex_buffer
*VB
= &tnl
->vb
;
111 /* fprintf(stderr, "%s %d %d\n", __FUNCTION__, start, count); */
113 /* Check arguments, etc.
115 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
118 if (tnl
->pipeline
.build_state_changes
)
119 _tnl_validate_pipeline( ctx
);
121 if (ctx
->CompileFlag
) {
122 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
124 else if (count
< (GLint
) ctx
->Const
.MaxArrayLockSize
) {
126 /* Small primitives which can fit in a single vertex buffer:
128 FLUSH_CURRENT( ctx
, 0 );
130 if (ctx
->Array
.LockCount
)
132 if (start
< (GLint
) ctx
->Array
.LockFirst
)
133 start
= ctx
->Array
.LockFirst
;
134 if (start
+ count
> (GLint
) ctx
->Array
.LockCount
)
135 count
= ctx
->Array
.LockCount
- start
;
139 /* Locked drawarrays. Reuse any previously transformed data.
141 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
142 VB
->FirstPrimitive
= start
;
143 VB
->Primitive
[start
] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
144 VB
->PrimitiveLength
[start
] = count
;
145 tnl
->Driver
.RunPipeline( ctx
);
147 /* The arrays are small enough to fit in a single VB; just bind
148 * them and go. Any untransformed data will be copied on
151 * Invalidate any cached data dependent on these arrays.
153 _tnl_vb_bind_arrays( ctx
, start
, start
+ count
);
154 VB
->FirstPrimitive
= 0;
155 VB
->Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
156 VB
->PrimitiveLength
[0] = count
;
157 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
158 tnl
->Driver
.RunPipeline( ctx
);
159 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
163 int bufsz
= (ctx
->Const
.MaxArrayLockSize
- 4) & ~3;
165 int minimum
, modulo
, skip
;
167 /* Large primitives requiring decomposition to multiple vertex
189 case GL_TRIANGLE_STRIP
:
205 case GL_TRIANGLE_FAN
:
208 /* Primitives requiring a copied vertex (fan-like primitives)
209 * must use the slow path:
211 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
215 FLUSH_CURRENT( ctx
, 0 );
217 /* fprintf(stderr, "start %d count %d min %d modulo %d skip %d\n", */
218 /* start, count, minimum, modulo, skip); */
221 bufsz
-= bufsz
% modulo
;
225 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
227 nr
= MIN2( bufsz
, count
- j
);
229 /* fprintf(stderr, "%d..%d\n", j - minimum, j+nr); */
231 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
233 VB
->FirstPrimitive
= 0;
234 VB
->Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
235 VB
->PrimitiveLength
[0] = nr
+ minimum
;
236 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
237 tnl
->Driver
.RunPipeline( ctx
);
238 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
245 _tnl_DrawRangeElements(GLenum mode
,
246 GLuint start
, GLuint end
,
247 GLsizei count
, GLenum type
, const GLvoid
*indices
)
249 GET_CURRENT_CONTEXT(ctx
);
250 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
253 /* fprintf(stderr, "%s\n", __FUNCTION__); */
255 /* Check arguments, etc.
257 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
261 if (tnl
->pipeline
.build_state_changes
)
262 _tnl_validate_pipeline( ctx
);
264 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
265 count
, type
, indices
);
268 if (ctx
->CompileFlag
) {
269 /* Can't do anything when compiling:
271 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
273 else if (ctx
->Array
.LockCount
) {
274 /* Are the arrays already locked? If so we currently have to look
275 * at the whole locked range.
277 if (start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
278 _tnl_draw_range_elements( ctx
, mode
,
279 ctx
->Array
.LockFirst
,
280 ctx
->Array
.LockCount
,
283 /* The spec says referencing elements outside the locked
284 * range is undefined. I'm going to make it a noop this time
285 * round, maybe come up with something beter before 3.6.
287 * May be able to get away with just setting LockCount==0,
288 * though this raises the problems of dependent state. May
289 * have to call glUnlockArrays() directly?
291 * Or scan the list and replace bad indices?
294 "DrawRangeElements references "
295 "elements outside locked range.");
298 else if (end
+ 1 - start
< ctx
->Const
.MaxArrayLockSize
) {
299 /* The arrays aren't locked but we can still fit them inside a
300 * single vertexbuffer.
302 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
304 /* Range is too big to optimize:
306 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
313 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
314 const GLvoid
*indices
)
316 GET_CURRENT_CONTEXT(ctx
);
317 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
320 /* fprintf(stderr, "%s\n", __FUNCTION__); */
322 /* Check arguments, etc.
324 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
327 if (tnl
->pipeline
.build_state_changes
)
328 _tnl_validate_pipeline( ctx
);
330 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
331 count
, type
, indices
);
333 if (ctx
->CompileFlag
) {
334 /* Can't do anything when compiling:
336 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
338 else if (ctx
->Array
.LockCount
) {
339 _tnl_draw_range_elements( ctx
, mode
,
340 ctx
->Array
.LockFirst
,
341 ctx
->Array
.LockCount
,
345 /* Scan the index list and see if we can use the locked path anyway.
350 for (i
= 0 ; i
< count
; i
++)
351 if (ui_indices
[i
] > max_elt
)
352 max_elt
= ui_indices
[i
];
354 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
355 max_elt
< (GLuint
) count
) /* do we want to use it? */
356 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
358 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
363 void _tnl_array_init( GLcontext
*ctx
)
365 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
366 struct vertex_arrays
*tmp
= &tnl
->array_inputs
;
367 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->vtxfmt
);
370 vfmt
->DrawArrays
= _tnl_DrawArrays
;
371 vfmt
->DrawElements
= _tnl_DrawElements
;
372 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
374 /* Setup vector pointers that will be used to bind arrays to VB's.
376 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
377 _mesa_vector3f_init( &tmp
->Normal
, 0, 0 );
378 _mesa_vector1f_init( &tmp
->FogCoord
, 0, 0 );
379 _mesa_vector1ui_init( &tmp
->Index
, 0, 0 );
380 _mesa_vector1ub_init( &tmp
->EdgeFlag
, 0, 0 );
382 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
383 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
385 tnl
->tmp_primitive
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
386 tnl
->tmp_primitive_length
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
390 void _tnl_array_destroy( GLcontext
*ctx
)
392 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
393 if (tnl
->tmp_primitive_length
) FREE(tnl
->tmp_primitive_length
);
394 if (tnl
->tmp_primitive
) FREE(tnl
->tmp_primitive
);