818a15b6b8d4609ebcc8657402e3e14cee6025e6
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
;
108 /* XXX - indices may be read only
110 for (i = 0 ; i < count ; i++)
114 if (ctx
->Array
.LockCount
)
115 tnl
->Driver
.RunPipeline( ctx
);
117 /* The lower 16 bits represent the conventional arrays while the
118 * upper 16 bits represent the generic arrays. OR those bits
119 * together to indicate which vertex attribs are in effect.
121 GLuint enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
122 /* Note that arrays may have changed before/after execution.
124 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
125 tnl
->Driver
.RunPipeline( ctx
);
126 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
129 /* XXX - indices may be read only
131 for (i = 0 ; i < count ; i++)
139 * Called via the GL API dispatcher.
142 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
144 GET_CURRENT_CONTEXT(ctx
);
145 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
146 GLuint thresh
= (ctx
->Driver
.NeedFlush
& FLUSH_STORED_VERTICES
) ? 30 : 10;
147 GLuint enabledArrays
;
149 if (MESA_VERBOSE
& VERBOSE_API
)
150 _mesa_debug(NULL
, "_tnl_DrawArrays %d %d\n", start
, count
);
152 /* Check arguments, etc.
154 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
157 if (tnl
->pipeline
.build_state_changes
)
158 _tnl_validate_pipeline( ctx
);
160 assert(!ctx
->CompileFlag
);
162 if (!ctx
->Array
.LockCount
&& (GLuint
) count
< thresh
) {
163 /* Small primitives: attempt to share a vb (at the expense of
164 * using the immediate interface).
166 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
168 else if (ctx
->Array
.LockCount
&&
169 count
<= (GLint
) ctx
->Const
.MaxArrayLockSize
) {
171 struct tnl_prim prim
;
173 /* Locked primitives which can fit in a single vertex buffer:
175 FLUSH_CURRENT( ctx
, 0 );
177 if (start
< (GLint
) ctx
->Array
.LockFirst
)
178 start
= ctx
->Array
.LockFirst
;
179 if (start
+ count
> (GLint
) ctx
->Array
.LockCount
)
180 count
= ctx
->Array
.LockCount
- start
;
182 /* Locked drawarrays. Reuse any previously transformed data.
184 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
186 tnl
->vb
.Primitive
= &prim
;
187 tnl
->vb
.Primitive
[0].mode
= mode
| PRIM_BEGIN
| PRIM_END
;
188 tnl
->vb
.Primitive
[0].start
= start
;
189 tnl
->vb
.Primitive
[0].count
= count
;
190 tnl
->vb
.PrimitiveCount
= 1;
192 tnl
->Driver
.RunPipeline( ctx
);
195 int bufsz
= 256; /* Use a small buffer for cache goodness */
197 int minimum
, modulo
, skip
;
199 /* Large primitives requiring decomposition to multiple vertex
223 case GL_TRIANGLE_STRIP
:
239 case GL_TRIANGLE_FAN
:
242 /* Primitives requiring a copied vertex (fan-like primitives)
243 * must use the slow path if they cannot fit in a single
246 if (count
<= (GLint
) ctx
->Const
.MaxArrayLockSize
) {
247 bufsz
= ctx
->Const
.MaxArrayLockSize
;
253 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
258 FLUSH_CURRENT( ctx
, 0 );
260 bufsz
-= bufsz
% modulo
;
264 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
266 struct tnl_prim prim
;
268 nr
= MIN2( bufsz
, count
- j
);
270 /* XXX is the last parameter a count or index into the array??? */
271 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
273 tnl
->vb
.Primitive
= &prim
;
274 tnl
->vb
.Primitive
[0].mode
= mode
;
276 if (j
== start
+ minimum
)
277 tnl
->vb
.Primitive
[0].mode
|= PRIM_BEGIN
;
279 if (j
+ nr
+ skip
>= count
)
280 tnl
->vb
.Primitive
[0].mode
|= PRIM_END
;
282 tnl
->vb
.Primitive
[0].start
= 0;
283 tnl
->vb
.Primitive
[0].count
= nr
+ minimum
;
284 tnl
->vb
.PrimitiveCount
= 1;
286 /* The lower 16 bits represent the conventional arrays while the
287 * upper 16 bits represent the generic arrays. OR those bits
288 * together to indicate which vertex attribs are in effect.
290 enabledArrays
= ctx
->Array
._Enabled
| (ctx
->Array
._Enabled
>> 16);
291 /* Note that arrays may have changed before/after execution.
293 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
294 tnl
->Driver
.RunPipeline( ctx
);
295 tnl
->pipeline
.run_input_changes
|= enabledArrays
;
302 * Called via the GL API dispatcher.
305 _tnl_DrawRangeElements(GLenum mode
,
306 GLuint start
, GLuint end
,
307 GLsizei count
, GLenum type
, const GLvoid
*indices
)
309 GET_CURRENT_CONTEXT(ctx
);
312 if (MESA_VERBOSE
& VERBOSE_API
)
313 _mesa_debug(NULL
, "_tnl_DrawRangeElements %d %d %d\n", start
, end
, count
);
315 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
316 /* use indices in the buffer object */
317 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
319 "DrawRangeElements with empty vertex elements buffer!");
322 /* actual address is the sum of pointers */
323 indices
= (const GLvoid
*)
324 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
325 (const GLubyte
*) indices
);
328 /* Check arguments, etc.
330 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
334 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
335 count
, type
, indices
);
338 assert(!ctx
->CompileFlag
);
340 if (ctx
->Array
.LockCount
) {
341 /* Are the arrays already locked? If so we currently have to look
342 * at the whole locked range.
345 start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
346 _tnl_draw_range_elements( ctx
, mode
,
347 ctx
->Array
.LockFirst
,
348 ctx
->Array
.LockCount
,
351 /* The spec says referencing elements outside the locked
352 * range is undefined. I'm going to make it a noop this time
353 * round, maybe come up with something beter before 3.6.
355 * May be able to get away with just setting LockCount==0,
356 * though this raises the problems of dependent state. May
357 * have to call glUnlockArrays() directly?
359 * Or scan the list and replace bad indices?
362 "DrawRangeElements references "
363 "elements outside locked range.");
366 else if (start
== 0 &&
367 end
- start
+ 1 <= ctx
->Const
.MaxArrayLockSize
) {
368 /* The arrays aren't locked but we can still fit them inside a
369 * single vertexbuffer.
371 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
374 /* Range is too big to optimize:
376 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
383 * Called via the GL API dispatcher.
386 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
387 const GLvoid
*indices
)
389 GET_CURRENT_CONTEXT(ctx
);
392 if (MESA_VERBOSE
& VERBOSE_API
)
393 _mesa_debug(NULL
, "_tnl_DrawElements %d\n", count
);
395 /* Check arguments, etc. */
396 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
399 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
400 /* actual address is the sum of pointers */
401 indices
= (const GLvoid
*)
402 ADD_POINTERS(ctx
->Array
.ElementArrayBufferObj
->Data
,
403 (const GLubyte
*) indices
);
406 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
407 count
, type
, indices
);
409 assert(!ctx
->CompileFlag
);
411 if (ctx
->Array
.LockFirst
== 0 &&
412 ctx
->Array
.LockCount
) {
413 _tnl_draw_range_elements( ctx
, mode
,
414 ctx
->Array
.LockFirst
,
415 ctx
->Array
.LockCount
,
419 /* Scan the index list and see if we can use the locked path anyway.
424 for (i
= 0 ; i
< count
; i
++)
425 if (ui_indices
[i
] > max_elt
)
426 max_elt
= ui_indices
[i
];
428 /* XXX should this < really be <= ??? */
429 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
430 max_elt
< (GLuint
) count
) /* do we want to use it? */
431 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
433 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
439 * Initialize context's vertex array fields. Called during T 'n L context
442 void _tnl_array_init( GLcontext
*ctx
)
444 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
445 struct tnl_vertex_arrays
*tmp
= &tnl
->array_inputs
;
446 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->exec_vtxfmt
);
449 vfmt
->DrawArrays
= _tnl_DrawArrays
;
450 vfmt
->DrawElements
= _tnl_DrawElements
;
451 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
453 /* Setup vector pointers that will be used to bind arrays to VB's.
455 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
456 _mesa_vector4f_init( &tmp
->Normal
, 0, 0 );
457 _mesa_vector4f_init( &tmp
->FogCoord
, 0, 0 );
458 _mesa_vector4f_init( &tmp
->Index
, 0, 0 );
460 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
461 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
466 * Destroy the context's vertex array stuff.
467 * Called during T 'n L context destruction.
469 void _tnl_array_destroy( GLcontext
*ctx
)