2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2003 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 _tnl_vb_bind_arrays( ctx
, start
, end
);
95 tnl
->vb
.Primitive
= &prim
;
96 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
97 tnl
->vb
.Primitive
[0].start
= 0;
98 tnl
->vb
.Primitive
[0].count
= count
;
99 tnl
->vb
.PrimitiveCount
= 1;
101 tnl
->vb
.Elts
= (GLuint
*)indices
;
104 for (i
= 0 ; i
< count
; i
++)
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
;
118 tnl
->Driver
.RunPipeline( ctx
);
119 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
123 for (i
= 0 ; i
< count
; i
++)
130 * Called via the GL API dispatcher.
133 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
135 GET_CURRENT_CONTEXT(ctx
);
136 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
137 GLuint thresh
= (ctx
->Driver
.NeedFlush
& FLUSH_STORED_VERTICES
) ? 30 : 10;
138 GLuint enabledArrays
;
140 if (MESA_VERBOSE
& VERBOSE_API
)
141 _mesa_debug(NULL
, "_tnl_DrawArrays %d %d\n", start
, count
);
143 /* Check arguments, etc.
145 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
148 if (tnl
->pipeline
.build_state_changes
)
149 _tnl_validate_pipeline( ctx
);
151 assert(!ctx
->CompileFlag
);
153 if (!ctx
->Array
.LockCount
&& (GLuint
) count
< thresh
) {
154 /* Small primitives: attempt to share a vb (at the expense of
155 * using the immediate interface).
157 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
159 else if (ctx
->Array
.LockCount
&&
160 count
< (GLint
) ctx
->Const
.MaxArrayLockSize
) {
162 struct tnl_prim prim
;
164 /* Locked primitives which can fit in a single vertex buffer:
166 FLUSH_CURRENT( ctx
, 0 );
168 if (start
< (GLint
) ctx
->Array
.LockFirst
)
169 start
= ctx
->Array
.LockFirst
;
170 if (start
+ count
> (GLint
) ctx
->Array
.LockCount
)
171 count
= ctx
->Array
.LockCount
- start
;
173 /* Locked drawarrays. Reuse any previously transformed data.
175 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
177 tnl
->vb
.Primitive
= &prim
;
178 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
179 tnl
->vb
.Primitive
[0].start
= start
;
180 tnl
->vb
.Primitive
[0].count
= count
;
181 tnl
->vb
.PrimitiveCount
= 1;
183 tnl
->Driver
.RunPipeline( ctx
);
186 int bufsz
= 256; /* Use a small buffer for cache goodness */
188 int minimum
, modulo
, skip
;
190 /* Large primitives requiring decomposition to multiple vertex
214 case GL_TRIANGLE_STRIP
:
230 case GL_TRIANGLE_FAN
:
233 /* Primitives requiring a copied vertex (fan-like primitives)
234 * must use the slow path if they cannot fit in a single
237 if (count
< (GLint
) ctx
->Const
.MaxArrayLockSize
) {
238 bufsz
= ctx
->Const
.MaxArrayLockSize
;
244 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
249 FLUSH_CURRENT( ctx
, 0 );
251 bufsz
-= bufsz
% modulo
;
255 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
257 struct tnl_prim prim
;
259 nr
= MIN2( bufsz
, count
- j
);
261 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
263 tnl
->vb
.Primitive
= &prim
;
264 tnl
->vb
.Primitive
[0].mode
= mode
;
266 if (j
== start
+ minimum
)
267 tnl
->vb
.Primitive
[0].mode
|= PRIM_BEGIN
;
269 if (j
+ nr
+ skip
>= count
)
270 tnl
->vb
.Primitive
[0].mode
|= PRIM_END
;
272 tnl
->vb
.Primitive
[0].start
= 0;
273 tnl
->vb
.Primitive
[0].count
= nr
+ minimum
;
274 tnl
->vb
.PrimitiveCount
= 1;
276 /* The lower 16 bits represent the conventional arrays while the
277 * upper 16 bits represent the generic arrays. OR those bits
278 * together to indicate which vertex attribs are in effect.
280 enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
281 /* Note that arrays may have changed before/after execution.
283 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
284 tnl
->Driver
.RunPipeline( ctx
);
285 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
292 * Called via the GL API dispatcher.
295 _tnl_DrawRangeElements(GLenum mode
,
296 GLuint start
, GLuint end
,
297 GLsizei count
, GLenum type
, const GLvoid
*indices
)
299 GET_CURRENT_CONTEXT(ctx
);
302 if (MESA_VERBOSE
& VERBOSE_API
)
303 _mesa_debug(NULL
, "_tnl_DrawRangeElements %d %d %d\n", start
, end
, count
);
305 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
306 /* use indices in the buffer object */
307 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
309 "DrawRangeElements with empty vertex elements buffer!");
312 /* actual address is the sum of pointers */
313 indices
= (const GLvoid
*)
314 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
315 (const GLubyte
*) indices
);
318 /* Check arguments, etc.
320 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
324 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
325 count
, type
, indices
);
328 assert(!ctx
->CompileFlag
);
330 if (ctx
->Array
.LockCount
) {
331 /* Are the arrays already locked? If so we currently have to look
332 * at the whole locked range.
334 if (start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
335 _tnl_draw_range_elements( ctx
, mode
,
336 ctx
->Array
.LockFirst
,
337 ctx
->Array
.LockCount
,
340 /* The spec says referencing elements outside the locked
341 * range is undefined. I'm going to make it a noop this time
342 * round, maybe come up with something beter before 3.6.
344 * May be able to get away with just setting LockCount==0,
345 * though this raises the problems of dependent state. May
346 * have to call glUnlockArrays() directly?
348 * Or scan the list and replace bad indices?
351 "DrawRangeElements references "
352 "elements outside locked range.");
355 else if (end
+ 1 - start
< ctx
->Const
.MaxArrayLockSize
) {
356 /* The arrays aren't locked but we can still fit them inside a
357 * single vertexbuffer.
359 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
361 /* Range is too big to optimize:
363 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
370 * Called via the GL API dispatcher.
373 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
374 const GLvoid
*indices
)
376 GET_CURRENT_CONTEXT(ctx
);
379 if (MESA_VERBOSE
& VERBOSE_API
)
380 _mesa_debug(NULL
, "_tnl_DrawElements %d\n", count
);
382 /* Check arguments, etc. */
383 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
386 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
387 /* actual address is the sum of pointers */
388 indices
= (const GLvoid
*)
389 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
390 (const GLubyte
*) indices
);
393 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
394 count
, type
, indices
);
396 assert(!ctx
->CompileFlag
);
398 if (ctx
->Array
.LockCount
) {
399 _tnl_draw_range_elements( ctx
, mode
,
400 ctx
->Array
.LockFirst
,
401 ctx
->Array
.LockCount
,
405 /* Scan the index list and see if we can use the locked path anyway.
410 for (i
= 0 ; i
< count
; i
++)
411 if (ui_indices
[i
] > max_elt
)
412 max_elt
= ui_indices
[i
];
414 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
415 max_elt
< (GLuint
) count
) /* do we want to use it? */
416 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
418 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
424 * Initialize context's vertex array fields. Called during T 'n L context
427 void _tnl_array_init( GLcontext
*ctx
)
429 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
430 struct tnl_vertex_arrays
*tmp
= &tnl
->array_inputs
;
431 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->exec_vtxfmt
);
434 vfmt
->DrawArrays
= _tnl_DrawArrays
;
435 vfmt
->DrawElements
= _tnl_DrawElements
;
436 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
438 /* Setup vector pointers that will be used to bind arrays to VB's.
440 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
441 _mesa_vector4f_init( &tmp
->Normal
, 0, 0 );
442 _mesa_vector4f_init( &tmp
->FogCoord
, 0, 0 );
443 _mesa_vector4f_init( &tmp
->Index
, 0, 0 );
445 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
446 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
451 * Destroy the context's vertex array stuff.
452 * Called during T 'n L context destruction.
454 void _tnl_array_destroy( GLcontext
*ctx
)