2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 * \brief Vertex array API functions (glDrawArrays, etc)
28 * \author Keith Whitwell
32 #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_save_api.h"
44 #include "t_context.h"
45 #include "t_pipeline.h"
47 static void fallback_drawarrays( GLcontext
*ctx
, GLenum mode
, GLint start
,
52 assert(!ctx
->CompileFlag
);
53 assert(ctx
->Driver
.CurrentExecPrimitive
== GL_POLYGON
+1);
56 for (i
= start
; i
< count
; i
++)
62 static void fallback_drawelements( GLcontext
*ctx
, GLenum mode
, GLsizei count
,
63 const GLuint
*indices
)
67 assert(!ctx
->CompileFlag
);
68 assert(ctx
->Driver
.CurrentExecPrimitive
== GL_POLYGON
+1);
70 /* Here, indices will already reflect the buffer object if active */
73 for (i
= 0 ; i
< count
; i
++) {
74 glArrayElement( indices
[i
] );
80 static void _tnl_draw_range_elements( GLcontext
*ctx
, GLenum mode
,
81 GLuint start
, GLuint end
,
82 GLsizei count
, GLuint
*indices
)
85 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
88 FLUSH_CURRENT( ctx
, 0 );
90 if (tnl
->pipeline
.build_state_changes
)
91 _tnl_validate_pipeline( ctx
);
93 /* XXX is "end" correct? Looking at the implementation of
94 * _tnl_vb_bind_arrays(), perhaps we should pass end-start.
96 _tnl_vb_bind_arrays( ctx
, start
, end
);
98 tnl
->vb
.Primitive
= &prim
;
99 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
100 tnl
->vb
.Primitive
[0].start
= 0;
101 tnl
->vb
.Primitive
[0].count
= count
;
102 tnl
->vb
.PrimitiveCount
= 1;
104 tnl
->vb
.Elts
= (GLuint
*)indices
;
107 for (i
= 0 ; i
< count
; i
++)
110 if (ctx
->Array
.LockCount
)
111 tnl
->Driver
.RunPipeline( ctx
);
113 /* The lower 16 bits represent the conventional arrays while the
114 * upper 16 bits represent the generic arrays. OR those bits
115 * together to indicate which vertex attribs are in effect.
117 GLuint enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
118 /* Note that arrays may have changed before/after execution.
120 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
121 tnl
->Driver
.RunPipeline( ctx
);
122 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
126 for (i
= 0 ; i
< count
; i
++)
133 * Called via the GL API dispatcher.
136 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
138 GET_CURRENT_CONTEXT(ctx
);
139 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
140 GLuint thresh
= (ctx
->Driver
.NeedFlush
& FLUSH_STORED_VERTICES
) ? 30 : 10;
141 GLuint enabledArrays
;
143 if (MESA_VERBOSE
& VERBOSE_API
)
144 _mesa_debug(NULL
, "_tnl_DrawArrays %d %d\n", start
, count
);
146 /* Check arguments, etc.
148 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
151 if (tnl
->pipeline
.build_state_changes
)
152 _tnl_validate_pipeline( ctx
);
154 assert(!ctx
->CompileFlag
);
156 if (!ctx
->Array
.LockCount
&& (GLuint
) count
< thresh
) {
157 /* Small primitives: attempt to share a vb (at the expense of
158 * using the immediate interface).
160 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
162 else if (ctx
->Array
.LockCount
&&
163 count
<= (GLint
) ctx
->Const
.MaxArrayLockSize
) {
165 struct tnl_prim prim
;
167 /* Locked primitives which can fit in a single vertex buffer:
169 FLUSH_CURRENT( ctx
, 0 );
171 if (start
< (GLint
) ctx
->Array
.LockFirst
)
172 start
= ctx
->Array
.LockFirst
;
173 if (start
+ count
> (GLint
) ctx
->Array
.LockCount
)
174 count
= ctx
->Array
.LockCount
- start
;
176 /* Locked drawarrays. Reuse any previously transformed data.
178 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
180 tnl
->vb
.Primitive
= &prim
;
181 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
182 tnl
->vb
.Primitive
[0].start
= start
;
183 tnl
->vb
.Primitive
[0].count
= count
;
184 tnl
->vb
.PrimitiveCount
= 1;
186 tnl
->Driver
.RunPipeline( ctx
);
189 int bufsz
= 256; /* Use a small buffer for cache goodness */
191 int minimum
, modulo
, skip
;
193 /* Large primitives requiring decomposition to multiple vertex
217 case GL_TRIANGLE_STRIP
:
233 case GL_TRIANGLE_FAN
:
236 /* Primitives requiring a copied vertex (fan-like primitives)
237 * must use the slow path if they cannot fit in a single
240 if (count
<= (GLint
) ctx
->Const
.MaxArrayLockSize
) {
241 bufsz
= ctx
->Const
.MaxArrayLockSize
;
247 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
252 FLUSH_CURRENT( ctx
, 0 );
254 bufsz
-= bufsz
% modulo
;
258 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
260 struct tnl_prim prim
;
262 nr
= MIN2( bufsz
, count
- j
);
264 /* XXX is the last parameter a count or index into the array??? */
265 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
267 tnl
->vb
.Primitive
= &prim
;
268 tnl
->vb
.Primitive
[0].mode
= mode
;
270 if (j
== start
+ minimum
)
271 tnl
->vb
.Primitive
[0].mode
|= PRIM_BEGIN
;
273 if (j
+ nr
+ skip
>= count
)
274 tnl
->vb
.Primitive
[0].mode
|= PRIM_END
;
276 tnl
->vb
.Primitive
[0].start
= 0;
277 tnl
->vb
.Primitive
[0].count
= nr
+ minimum
;
278 tnl
->vb
.PrimitiveCount
= 1;
280 /* The lower 16 bits represent the conventional arrays while the
281 * upper 16 bits represent the generic arrays. OR those bits
282 * together to indicate which vertex attribs are in effect.
284 enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
285 /* Note that arrays may have changed before/after execution.
287 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
288 tnl
->Driver
.RunPipeline( ctx
);
289 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
296 * Called via the GL API dispatcher.
299 _tnl_DrawRangeElements(GLenum mode
,
300 GLuint start
, GLuint end
,
301 GLsizei count
, GLenum type
, const GLvoid
*indices
)
303 GET_CURRENT_CONTEXT(ctx
);
306 if (MESA_VERBOSE
& VERBOSE_API
)
307 _mesa_debug(NULL
, "_tnl_DrawRangeElements %d %d %d\n", start
, end
, count
);
309 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
310 /* use indices in the buffer object */
311 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
313 "DrawRangeElements with empty vertex elements buffer!");
316 /* actual address is the sum of pointers */
317 indices
= (const GLvoid
*)
318 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
319 (const GLubyte
*) indices
);
322 /* Check arguments, etc.
324 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
328 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
329 count
, type
, indices
);
332 assert(!ctx
->CompileFlag
);
334 if (ctx
->Array
.LockCount
) {
335 /* Are the arrays already locked? If so we currently have to look
336 * at the whole locked range.
338 if (start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
339 _tnl_draw_range_elements( ctx
, mode
,
340 ctx
->Array
.LockFirst
,
341 ctx
->Array
.LockCount
,
344 /* The spec says referencing elements outside the locked
345 * range is undefined. I'm going to make it a noop this time
346 * round, maybe come up with something beter before 3.6.
348 * May be able to get away with just setting LockCount==0,
349 * though this raises the problems of dependent state. May
350 * have to call glUnlockArrays() directly?
352 * Or scan the list and replace bad indices?
355 "DrawRangeElements references "
356 "elements outside locked range.");
359 else if (end
- start
+ 1 <= ctx
->Const
.MaxArrayLockSize
) {
360 /* The arrays aren't locked but we can still fit them inside a
361 * single vertexbuffer.
363 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
366 /* Range is too big to optimize:
368 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
375 * Called via the GL API dispatcher.
378 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
379 const GLvoid
*indices
)
381 GET_CURRENT_CONTEXT(ctx
);
384 if (MESA_VERBOSE
& VERBOSE_API
)
385 _mesa_debug(NULL
, "_tnl_DrawElements %d\n", count
);
387 /* Check arguments, etc. */
388 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
391 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
392 /* actual address is the sum of pointers */
393 indices
= (const GLvoid
*)
394 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
395 (const GLubyte
*) indices
);
398 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
399 count
, type
, indices
);
401 assert(!ctx
->CompileFlag
);
403 if (ctx
->Array
.LockCount
) {
404 _tnl_draw_range_elements( ctx
, mode
,
405 ctx
->Array
.LockFirst
,
406 ctx
->Array
.LockCount
,
410 /* Scan the index list and see if we can use the locked path anyway.
415 for (i
= 0 ; i
< count
; i
++)
416 if (ui_indices
[i
] > max_elt
)
417 max_elt
= ui_indices
[i
];
419 /* XXX should this < really be <= ??? */
420 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
421 max_elt
< (GLuint
) count
) /* do we want to use it? */
422 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
424 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
430 * Initialize context's vertex array fields. Called during T 'n L context
433 void _tnl_array_init( GLcontext
*ctx
)
435 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
436 struct tnl_vertex_arrays
*tmp
= &tnl
->array_inputs
;
437 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->exec_vtxfmt
);
440 vfmt
->DrawArrays
= _tnl_DrawArrays
;
441 vfmt
->DrawElements
= _tnl_DrawElements
;
442 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
444 /* Setup vector pointers that will be used to bind arrays to VB's.
446 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
447 _mesa_vector4f_init( &tmp
->Normal
, 0, 0 );
448 _mesa_vector4f_init( &tmp
->FogCoord
, 0, 0 );
449 _mesa_vector4f_init( &tmp
->Index
, 0, 0 );
451 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
452 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
457 * Destroy the context's vertex array stuff.
458 * Called during T 'n L context destruction.
460 void _tnl_array_destroy( GLcontext
*ctx
)