b0228b74c7f61b0485954dc8ca48ebef3f96c6c0
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_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
++)
60 static void fallback_drawelements( GLcontext
*ctx
, GLenum mode
, GLsizei count
,
61 const GLuint
*indices
)
63 if (_tnl_hard_begin(ctx
, mode
)) {
65 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
66 /* use indices in the buffer object */
67 ASSERT(ctx
->Array
.ElementArrayBufferObj
->Data
);
68 indices
= (const GLuint
*) ctx
->Array
.ElementArrayBufferObj
->Data
;
70 for (i
= 0 ; i
< count
; i
++) {
71 glArrayElement( indices
[i
] );
78 static void _tnl_draw_range_elements( GLcontext
*ctx
, GLenum mode
,
79 GLuint start
, GLuint end
,
80 GLsizei count
, GLuint
*indices
)
83 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
85 FLUSH_CURRENT( ctx
, 0 );
87 /* _mesa_debug(ctx, "%s\n", __FUNCTION__); */
88 if (tnl
->pipeline
.build_state_changes
)
89 _tnl_validate_pipeline( ctx
);
91 _tnl_vb_bind_arrays( ctx
, start
, end
);
93 tnl
->vb
.FirstPrimitive
= 0;
94 tnl
->vb
.Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
95 tnl
->vb
.PrimitiveLength
[0] = count
;
96 tnl
->vb
.Elts
= (GLuint
*)indices
;
98 for (i
= 0 ; i
< count
; i
++)
101 if (ctx
->Array
.LockCount
)
102 tnl
->Driver
.RunPipeline( ctx
);
104 /* Note that arrays may have changed before/after execution.
106 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
107 tnl
->Driver
.RunPipeline( ctx
);
108 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
111 for (i
= 0 ; i
< count
; i
++)
118 * Called via the GL API dispatcher.
121 _tnl_DrawArrays(GLenum mode
, GLint start
, GLsizei count
)
123 GET_CURRENT_CONTEXT(ctx
);
124 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
125 struct vertex_buffer
*VB
= &tnl
->vb
;
126 GLuint thresh
= (ctx
->Driver
.NeedFlush
& FLUSH_STORED_VERTICES
) ? 30 : 10;
128 if (MESA_VERBOSE
& VERBOSE_API
)
129 _mesa_debug(NULL
, "_tnl_DrawArrays %d %d\n", start
, count
);
131 /* Check arguments, etc.
133 if (!_mesa_validate_DrawArrays( ctx
, mode
, start
, count
))
136 if (tnl
->pipeline
.build_state_changes
)
137 _tnl_validate_pipeline( ctx
);
139 if (ctx
->CompileFlag
) {
140 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
142 else if (!ctx
->Array
.LockCount
&& (GLuint
) count
< thresh
) {
143 /* Small primitives: attempt to share a vb (at the expense of
144 * using the immediate interface).
146 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
148 else if (ctx
->Array
.LockCount
&&
149 count
< (GLint
) ctx
->Const
.MaxArrayLockSize
) {
151 /* Locked primitives which can fit in a single vertex buffer:
153 FLUSH_CURRENT( ctx
, 0 );
155 if (start
< (GLint
) ctx
->Array
.LockFirst
)
156 start
= ctx
->Array
.LockFirst
;
157 if (start
+ count
> (GLint
) ctx
->Array
.LockCount
)
158 count
= ctx
->Array
.LockCount
- start
;
160 /* Locked drawarrays. Reuse any previously transformed data.
162 _tnl_vb_bind_arrays( ctx
, ctx
->Array
.LockFirst
, ctx
->Array
.LockCount
);
163 VB
->FirstPrimitive
= start
;
164 VB
->Primitive
[start
] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
165 VB
->PrimitiveLength
[start
] = count
;
166 tnl
->Driver
.RunPipeline( ctx
);
169 int bufsz
= 256; /* Use a small buffer for cache goodness */
171 int minimum
, modulo
, skip
;
173 /* Large primitives requiring decomposition to multiple vertex
197 case GL_TRIANGLE_STRIP
:
213 case GL_TRIANGLE_FAN
:
216 /* Primitives requiring a copied vertex (fan-like primitives)
217 * must use the slow path if they cannot fit in a single
220 if (count
< (GLint
) ctx
->Const
.MaxArrayLockSize
) {
221 bufsz
= ctx
->Const
.MaxArrayLockSize
;
227 fallback_drawarrays( ctx
, mode
, start
, start
+ count
);
232 FLUSH_CURRENT( ctx
, 0 );
234 bufsz
-= bufsz
% modulo
;
238 for (j
= start
+ minimum
; j
< count
; j
+= nr
+ skip
) {
240 nr
= MIN2( bufsz
, count
- j
);
242 _tnl_vb_bind_arrays( ctx
, j
- minimum
, j
+ nr
);
244 VB
->FirstPrimitive
= 0;
245 VB
->Primitive
[0] = mode
| PRIM_BEGIN
| PRIM_END
| PRIM_LAST
;
246 VB
->PrimitiveLength
[0] = nr
+ minimum
;
247 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
248 tnl
->Driver
.RunPipeline( ctx
);
249 tnl
->pipeline
.run_input_changes
|= ctx
->Array
._Enabled
;
256 * Called via the GL API dispatcher.
259 _tnl_DrawRangeElements(GLenum mode
,
260 GLuint start
, GLuint end
,
261 GLsizei count
, GLenum type
, const GLvoid
*indices
)
263 GET_CURRENT_CONTEXT(ctx
);
266 if (MESA_VERBOSE
& VERBOSE_API
)
267 _mesa_debug(NULL
, "_tnl_DrawRangeElements %d %d %d\n", start
, end
, count
);
269 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
270 /* use indices in the buffer object */
271 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
273 "DrawRangeElements with empty vertex elements buffer!");
276 indices
= (GLvoid
*) ctx
->Array
.ElementArrayBufferObj
->Data
;
279 /* Check arguments, etc.
281 if (!_mesa_validate_DrawRangeElements( ctx
, mode
, start
, end
, count
,
285 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
286 count
, type
, indices
);
289 if (ctx
->CompileFlag
) {
290 /* Can't do anything when compiling:
292 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
294 else if (ctx
->Array
.LockCount
) {
295 /* Are the arrays already locked? If so we currently have to look
296 * at the whole locked range.
298 if (start
>= ctx
->Array
.LockFirst
&& end
<= ctx
->Array
.LockCount
)
299 _tnl_draw_range_elements( ctx
, mode
,
300 ctx
->Array
.LockFirst
,
301 ctx
->Array
.LockCount
,
304 /* The spec says referencing elements outside the locked
305 * range is undefined. I'm going to make it a noop this time
306 * round, maybe come up with something beter before 3.6.
308 * May be able to get away with just setting LockCount==0,
309 * though this raises the problems of dependent state. May
310 * have to call glUnlockArrays() directly?
312 * Or scan the list and replace bad indices?
315 "DrawRangeElements references "
316 "elements outside locked range.");
319 else if (end
+ 1 - start
< ctx
->Const
.MaxArrayLockSize
) {
320 /* The arrays aren't locked but we can still fit them inside a
321 * single vertexbuffer.
323 _tnl_draw_range_elements( ctx
, mode
, start
, end
+ 1, count
, ui_indices
);
325 /* Range is too big to optimize:
327 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
334 * Called via the GL API dispatcher.
337 _tnl_DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
338 const GLvoid
*indices
)
340 GET_CURRENT_CONTEXT(ctx
);
343 if (MESA_VERBOSE
& VERBOSE_API
)
344 _mesa_debug(NULL
, "_tnl_DrawElements %d\n", count
);
346 if (ctx
->Array
.ElementArrayBufferObj
->Name
) {
347 /* use indices in the buffer object */
348 if (!ctx
->Array
.ElementArrayBufferObj
->Data
) {
349 _mesa_warning(ctx
, "DrawElements with empty vertex elements buffer!");
352 indices
= (const GLvoid
*) ctx
->Array
.ElementArrayBufferObj
->Data
;
355 /* Check arguments, etc.
357 if (!_mesa_validate_DrawElements( ctx
, mode
, count
, type
, indices
))
360 ui_indices
= (GLuint
*)_ac_import_elements( ctx
, GL_UNSIGNED_INT
,
361 count
, type
, indices
);
363 if (ctx
->CompileFlag
) {
364 /* Can't do anything when compiling:
366 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
368 else if (ctx
->Array
.LockCount
) {
369 _tnl_draw_range_elements( ctx
, mode
,
370 ctx
->Array
.LockFirst
,
371 ctx
->Array
.LockCount
,
375 /* Scan the index list and see if we can use the locked path anyway.
380 for (i
= 0 ; i
< count
; i
++)
381 if (ui_indices
[i
] > max_elt
)
382 max_elt
= ui_indices
[i
];
384 if (max_elt
< ctx
->Const
.MaxArrayLockSize
&& /* can we use it? */
385 max_elt
< (GLuint
) count
) /* do we want to use it? */
386 _tnl_draw_range_elements( ctx
, mode
, 0, max_elt
+1, count
, ui_indices
);
388 fallback_drawelements( ctx
, mode
, count
, ui_indices
);
394 * Initialize context's vertex array fields. Called during T 'n L context
397 void _tnl_array_init( GLcontext
*ctx
)
399 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
400 struct vertex_arrays
*tmp
= &tnl
->array_inputs
;
401 GLvertexformat
*vfmt
= &(TNL_CONTEXT(ctx
)->vtxfmt
);
404 vfmt
->DrawArrays
= _tnl_DrawArrays
;
405 vfmt
->DrawElements
= _tnl_DrawElements
;
406 vfmt
->DrawRangeElements
= _tnl_DrawRangeElements
;
408 /* Setup vector pointers that will be used to bind arrays to VB's.
410 _mesa_vector4f_init( &tmp
->Obj
, 0, 0 );
411 _mesa_vector4f_init( &tmp
->Normal
, 0, 0 );
412 _mesa_vector4f_init( &tmp
->FogCoord
, 0, 0 );
413 _mesa_vector1ui_init( &tmp
->Index
, 0, 0 );
414 _mesa_vector1ub_init( &tmp
->EdgeFlag
, 0, 0 );
416 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++)
417 _mesa_vector4f_init( &tmp
->TexCoord
[i
], 0, 0);
419 tnl
->tmp_primitive
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
420 tnl
->tmp_primitive_length
= (GLuint
*)MALLOC(sizeof(GLuint
)*tnl
->vb
.Size
);
425 * Destroy the context's vertex array stuff.
426 * Called during T 'n L context destruction.
428 void _tnl_array_destroy( GLcontext
*ctx
)
430 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
431 if (tnl
->tmp_primitive_length
) FREE(tnl
->tmp_primitive_length
);
432 if (tnl
->tmp_primitive
) FREE(tnl
->tmp_primitive
);