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 /* Note this function no longer takes a 'start' value, the range is
81 * assumed to start at zero. The old trick of subtracting 'start'
82 * from each index won't work if the indices are not in writeable
85 static void _tnl_draw_range_elements( GLcontext
*ctx
, GLenum mode
,
87 GLsizei index_count
, GLuint
*indices
)
90 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
92 FLUSH_CURRENT( ctx
, 0 );
94 if (tnl
->pipeline
.build_state_changes
)
95 _tnl_validate_pipeline( ctx
);
97 _tnl_vb_bind_arrays( ctx
, 0, max_index
);
99 tnl
->vb
.Primitive
= &prim
;
100 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
101 tnl
->vb
.Primitive
[0].start
= 0;
102 tnl
->vb
.Primitive
[0].count
= index_count
;
103 tnl
->vb
.PrimitiveCount
= 1;
105 tnl
->vb
.Elts
= (GLuint
*)indices
;
107 if (ctx
->Array
.LockCount
)
108 tnl
->Driver
.RunPipeline( ctx
);
110 /* The lower 16 bits represent the conventional arrays while the
111 * upper 16 bits represent the generic arrays. OR those bits
112 * together to indicate which vertex attribs are in effect.
114 GLuint enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
115 /* Note that arrays may have changed before/after execution.
117 tnl
->pipeline
.run_input_changes
|= enabledArrays
& 0xffff;
118 tnl
->Driver
.RunPipeline( ctx
);
119 tnl
->pipeline
.run_input_changes
|= enabledArrays
& 0xffff;
126 * Called via the GL API dispatcher.
129 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
131 GET_CURRENT_CONTEXT(ctx
);
132 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
133 GLuint thresh
= (ctx
->Driver
.NeedFlush
& FLUSH_STORED_VERTICES
) ? 30 : 10;
134 GLuint enabledArrays
;
136 if (MESA_VERBOSE
& VERBOSE_API
)
137 _mesa_debug(NULL
, "_tnl_DrawArrays %d %d\n", start
, count
);
139 /* Check arguments, etc.
141 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
144 if (tnl
->pipeline
.build_state_changes
)
145 _tnl_validate_pipeline( ctx
);
147 assert(!ctx
->CompileFlag
);
149 if (!ctx
->Array
.LockCount
&& (GLuint
) count
< thresh
) {
150 /* Small primitives: attempt to share a vb (at the expense of
151 * using the immediate interface).
153 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
155 else if (start
>= (GLint
) ctx
->Array
.LockFirst
&&
156 start
+ count
<= (GLint
)(ctx
->Array
.LockFirst
+ ctx
->Array
.LockCount
)) {
158 struct tnl_prim prim
;
160 /* Locked primitives which can fit in a single vertex buffer:
162 FLUSH_CURRENT( ctx
, 0 );
164 /* Locked drawarrays. Reuse any previously transformed data.
166 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
,
167 ctx
->Array
.LockFirst
+ ctx
->Array
.LockCount
);
169 tnl
->vb
.Primitive
= &prim
;
170 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
171 tnl
->vb
.Primitive
[0].start
= start
;
172 tnl
->vb
.Primitive
[0].count
= count
;
173 tnl
->vb
.PrimitiveCount
= 1;
175 tnl
->Driver
.RunPipeline( ctx
);
178 int bufsz
= 256; /* Use a small buffer for cache goodness */
180 int minimum
, modulo
, skip
;
182 /* Large primitives requiring decomposition to multiple vertex
206 case GL_TRIANGLE_STRIP
:
222 case GL_TRIANGLE_FAN
:
225 /* Primitives requiring a copied vertex (fan-like primitives)
226 * must use the slow path if they cannot fit in a single
229 if (count
<= (GLint
) ctx
->Const
.MaxArrayLockSize
) {
230 bufsz
= ctx
->Const
.MaxArrayLockSize
;
236 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
241 FLUSH_CURRENT( ctx
, 0 );
243 bufsz
-= bufsz
% modulo
;
247 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
249 struct tnl_prim prim
;
251 nr
= MIN2( bufsz
, count
- j
);
253 /* XXX is the last parameter a count or index into the array??? */
254 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
256 tnl
->vb
.Primitive
= &prim
;
257 tnl
->vb
.Primitive
[0].mode
= mode
;
259 if (j
== start
+ minimum
)
260 tnl
->vb
.Primitive
[0].mode
|= PRIM_BEGIN
;
262 if (j
+ nr
+ skip
>= count
)
263 tnl
->vb
.Primitive
[0].mode
|= PRIM_END
;
265 tnl
->vb
.Primitive
[0].start
= 0;
266 tnl
->vb
.Primitive
[0].count
= nr
+ minimum
;
267 tnl
->vb
.PrimitiveCount
= 1;
269 /* The lower 16 bits represent the conventional arrays while the
270 * upper 16 bits represent the generic arrays. OR those bits
271 * together to indicate which vertex attribs are in effect.
273 enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
274 /* Note that arrays may have changed before/after execution.
276 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
277 tnl
->Driver
.RunPipeline( ctx
);
278 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
285 * Called via the GL API dispatcher.
288 _tnl_DrawRangeElements(GLenum mode
,
289 GLuint start
, GLuint end
,
290 GLsizei count
, GLenum type
, const GLvoid
*indices
)
292 GET_CURRENT_CONTEXT(ctx
);
295 if (MESA_VERBOSE
& VERBOSE_API
)
296 _mesa_debug(NULL
, "_tnl_DrawRangeElements %d %d %d\n", start
, end
, count
);
298 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
299 /* use indices in the buffer object */
300 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
302 "DrawRangeElements with empty vertex elements buffer!");
305 /* actual address is the sum of pointers */
306 indices
= (const GLvoid
*)
307 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
308 (const GLubyte
*) indices
);
311 /* Check arguments, etc.
313 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
317 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
318 count
, type
, indices
);
321 assert(!ctx
->CompileFlag
);
323 if (ctx
->Array
.LockCount
) {
324 /* Are the arrays already locked? If so we currently have to look
325 * at the whole locked range.
328 if (start
== 0 && ctx
->Array
.LockFirst
== 0 &&
329 end
< (ctx
->Array
.LockFirst
+ ctx
->Array
.LockCount
))
330 _tnl_draw_range_elements( ctx
, mode
,
331 ctx
->Array
.LockCount
,
334 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
337 else if (start
== 0 && end
< ctx
->Const
.MaxArrayLockSize
) {
338 /* The arrays aren't locked but we can still fit them inside a
339 * single vertexbuffer.
341 _tnl_draw_range_elements( ctx
, mode
, end
+ 1, count
, ui_indices
);
344 /* Range is too big to optimize:
346 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
353 * Called via the GL API dispatcher.
356 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
357 const GLvoid
*indices
)
359 GET_CURRENT_CONTEXT(ctx
);
362 if (MESA_VERBOSE
& VERBOSE_API
)
363 _mesa_debug(NULL
, "_tnl_DrawElements %d\n", count
);
365 /* Check arguments, etc. */
366 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
369 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
370 /* actual address is the sum of pointers */
371 indices
= (const GLvoid
*)
372 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
373 (const GLubyte
*) indices
);
376 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
377 count
, type
, indices
);
379 assert(!ctx
->CompileFlag
);
381 if (ctx
->Array
.LockCount
) {
382 if (ctx
->Array
.LockFirst
== 0)
383 _tnl_draw_range_elements( ctx
, mode
,
384 ctx
->Array
.LockCount
,
387 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
390 /* Scan the index list and see if we can use the locked path anyway.
395 for (i
= 0 ; i
< count
; i
++)
396 if (ui_indices
[i
] > max_elt
)
397 max_elt
= ui_indices
[i
];
399 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
400 max_elt
< (GLuint
) count
) /* do we want to use it? */
401 _tnl_draw_range_elements( ctx
, mode
, max_elt
+1, count
, ui_indices
);
403 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
409 * Initialize context's vertex array fields. Called during T 'n L context
412 void _tnl_array_init( GLcontext
*ctx
)
414 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
415 struct tnl_vertex_arrays
*tmp
= &tnl
->array_inputs
;
416 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->exec_vtxfmt
);
419 vfmt
->DrawArrays
= _tnl_DrawArrays
;
420 vfmt
->DrawElements
= _tnl_DrawElements
;
421 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
423 /* Setup vector pointers that will be used to bind arrays to VB's.
425 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
426 _mesa_vector4f_init( &tmp
->Normal
, 0, 0 );
427 _mesa_vector4f_init( &tmp
->FogCoord
, 0, 0 );
428 _mesa_vector4f_init( &tmp
->Index
, 0, 0 );
430 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
431 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
436 * Destroy the context's vertex array stuff.
437 * Called during T 'n L context destruction.
439 void _tnl_array_destroy( GLcontext
*ctx
)