Correct test for attenuation.
[mesa.git] / src / mesa / tnl / t_array_api.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.1
4 *
5 * Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
6 *
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:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
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.
23 */
24
25 /**
26 * \file t_array_api.c
27 * \brief Vertex array API functions (glDrawArrays, etc)
28 * \author Keith Whitwell
29 */
30
31 #include "glheader.h"
32 #include "api_validate.h"
33 #include "context.h"
34 #include "imports.h"
35 #include "macros.h"
36 #include "mtypes.h"
37 #include "state.h"
38
39 #include "array_cache/acache.h"
40
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"
46
47 static void fallback_drawarrays( GLcontext *ctx, GLenum mode, GLint start,
48 GLsizei count )
49 {
50 GLint i;
51
52 assert(!ctx->CompileFlag);
53 assert(ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1);
54
55 GL_CALL(Begin)(mode);
56 for (i = 0; i < count; i++)
57 GL_CALL(ArrayElement)( start + i );
58 GL_CALL(End)();
59 }
60
61
62 static void fallback_drawelements( GLcontext *ctx, GLenum mode, GLsizei count,
63 const GLuint *indices)
64 {
65 GLint i;
66
67 assert(!ctx->CompileFlag);
68 assert(ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1);
69
70 /* Here, indices will already reflect the buffer object if active */
71
72 GL_CALL(Begin)(mode);
73 for (i = 0 ; i < count ; i++) {
74 GL_CALL(ArrayElement)( indices[i] );
75 }
76 GL_CALL(End)();
77 }
78
79
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
83 * memory.
84 */
85 static void _tnl_draw_range_elements( GLcontext *ctx, GLenum mode,
86 GLuint max_index,
87 GLsizei index_count, GLuint *indices )
88
89 {
90 TNLcontext *tnl = TNL_CONTEXT(ctx);
91 struct tnl_prim prim;
92 FLUSH_CURRENT( ctx, 0 );
93
94 _tnl_vb_bind_arrays( ctx, 0, max_index );
95
96 tnl->vb.Primitive = &prim;
97 tnl->vb.Primitive[0].mode = mode | PRIM_BEGIN | PRIM_END;
98 tnl->vb.Primitive[0].start = 0;
99 tnl->vb.Primitive[0].count = index_count;
100 tnl->vb.PrimitiveCount = 1;
101
102 tnl->vb.Elts = (GLuint *)indices;
103
104 tnl->Driver.RunPipeline( ctx );
105 }
106
107
108
109 /**
110 * Called via the GL API dispatcher.
111 */
112 void GLAPIENTRY
113 _tnl_DrawArrays(GLenum mode, GLint start, GLsizei count)
114 {
115 GET_CURRENT_CONTEXT(ctx);
116 TNLcontext *tnl = TNL_CONTEXT(ctx);
117 GLuint thresh = (ctx->Driver.NeedFlush & FLUSH_STORED_VERTICES) ? 30 : 10;
118
119 if (MESA_VERBOSE & VERBOSE_API)
120 _mesa_debug(NULL, "_tnl_DrawArrays %d %d\n", start, count);
121
122 /* Check arguments, etc.
123 */
124 if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
125 return;
126
127 assert(!ctx->CompileFlag);
128
129 if (!ctx->Array.LockCount && (GLuint) count < thresh) {
130 /* Small primitives: attempt to share a vb (at the expense of
131 * using the immediate interface).
132 */
133 fallback_drawarrays( ctx, mode, start, count );
134 }
135 else if (start >= (GLint) ctx->Array.LockFirst &&
136 start + count <= (GLint)(ctx->Array.LockFirst + ctx->Array.LockCount)) {
137
138 struct tnl_prim prim;
139
140 /* Locked primitives which can fit in a single vertex buffer:
141 */
142 FLUSH_CURRENT( ctx, 0 );
143
144 /* Locked drawarrays. Reuse any previously transformed data.
145 */
146 _tnl_vb_bind_arrays( ctx, ctx->Array.LockFirst,
147 ctx->Array.LockFirst + ctx->Array.LockCount );
148
149 tnl->vb.Primitive = &prim;
150 tnl->vb.Primitive[0].mode = mode | PRIM_BEGIN | PRIM_END;
151 tnl->vb.Primitive[0].start = start;
152 tnl->vb.Primitive[0].count = count;
153 tnl->vb.PrimitiveCount = 1;
154
155 tnl->Driver.RunPipeline( ctx );
156 }
157 else {
158 int bufsz = 256; /* Use a small buffer for cache goodness */
159 int j, nr;
160 int minimum, modulo, skip;
161
162 /* Large primitives requiring decomposition to multiple vertex
163 * buffers:
164 */
165 switch (mode) {
166 case GL_POINTS:
167 minimum = 0;
168 modulo = 1;
169 skip = 0;
170 break;
171 case GL_LINES:
172 minimum = 1;
173 modulo = 2;
174 skip = 1;
175 break;
176 case GL_LINE_STRIP:
177 minimum = 1;
178 modulo = 1;
179 skip = 0;
180 break;
181 case GL_TRIANGLES:
182 minimum = 2;
183 modulo = 3;
184 skip = 2;
185 break;
186 case GL_TRIANGLE_STRIP:
187 minimum = 2;
188 modulo = 1;
189 skip = 0;
190 break;
191 case GL_QUADS:
192 minimum = 3;
193 modulo = 4;
194 skip = 3;
195 break;
196 case GL_QUAD_STRIP:
197 minimum = 3;
198 modulo = 2;
199 skip = 0;
200 break;
201 case GL_LINE_LOOP:
202 case GL_TRIANGLE_FAN:
203 case GL_POLYGON:
204 default:
205 /* Primitives requiring a copied vertex (fan-like primitives)
206 * must use the slow path if they cannot fit in a single
207 * vertex buffer.
208 */
209 if (count <= (GLint) ctx->Const.MaxArrayLockSize) {
210 bufsz = ctx->Const.MaxArrayLockSize;
211 minimum = 0;
212 modulo = 1;
213 skip = 0;
214 }
215 else {
216 fallback_drawarrays( ctx, mode, start, count );
217 return;
218 }
219 }
220
221 FLUSH_CURRENT( ctx, 0 );
222
223 bufsz -= bufsz % modulo;
224 bufsz -= minimum;
225 count += start;
226
227 for (j = start + minimum ; j < count ; j += nr + skip ) {
228
229 struct tnl_prim prim;
230
231 nr = MIN2( bufsz, count - j );
232
233 /* XXX is the last parameter a count or index into the array??? */
234 _tnl_vb_bind_arrays( ctx, j - minimum, j + nr );
235
236 tnl->vb.Primitive = &prim;
237 tnl->vb.Primitive[0].mode = mode;
238
239 if (j == start + minimum)
240 tnl->vb.Primitive[0].mode |= PRIM_BEGIN;
241
242 if (j + nr + skip >= count)
243 tnl->vb.Primitive[0].mode |= PRIM_END;
244
245 tnl->vb.Primitive[0].start = 0;
246 tnl->vb.Primitive[0].count = nr + minimum;
247 tnl->vb.PrimitiveCount = 1;
248
249 tnl->Driver.RunPipeline( ctx );
250 }
251 }
252 }
253
254
255 /**
256 * Called via the GL API dispatcher.
257 */
258 void GLAPIENTRY
259 _tnl_DrawRangeElements(GLenum mode,
260 GLuint start, GLuint end,
261 GLsizei count, GLenum type, const GLvoid *indices)
262 {
263 GET_CURRENT_CONTEXT(ctx);
264 GLuint *ui_indices;
265
266 if (MESA_VERBOSE & VERBOSE_API)
267 _mesa_debug(NULL, "_tnl_DrawRangeElements %d %d %d\n", start, end, count);
268
269 if (ctx->Array.ElementArrayBufferObj->Name) {
270 /* use indices in the buffer object */
271 if (!ctx->Array.ElementArrayBufferObj->Data) {
272 _mesa_warning(ctx,
273 "DrawRangeElements with empty vertex elements buffer!");
274 return;
275 }
276 /* actual address is the sum of pointers */
277 indices = (const GLvoid *)
278 ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data,
279 (const GLubyte *) indices);
280 }
281
282 /* Check arguments, etc.
283 */
284 if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count,
285 type, indices ))
286 return;
287
288 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
289 count, type, indices );
290
291
292 assert(!ctx->CompileFlag);
293
294 if (ctx->Array.LockCount) {
295 /* Are the arrays already locked? If so we currently have to look
296 * at the whole locked range.
297 */
298
299 if (start == 0 && ctx->Array.LockFirst == 0 &&
300 end < (ctx->Array.LockFirst + ctx->Array.LockCount))
301 _tnl_draw_range_elements( ctx, mode,
302 ctx->Array.LockCount,
303 count, ui_indices );
304 else {
305 fallback_drawelements( ctx, mode, count, ui_indices );
306 }
307 }
308 else if (start == 0 && end < ctx->Const.MaxArrayLockSize) {
309 /* The arrays aren't locked but we can still fit them inside a
310 * single vertexbuffer.
311 */
312 _tnl_draw_range_elements( ctx, mode, end + 1, count, ui_indices );
313 }
314 else {
315 /* Range is too big to optimize:
316 */
317 fallback_drawelements( ctx, mode, count, ui_indices );
318 }
319 }
320
321
322
323 /**
324 * Called via the GL API dispatcher.
325 */
326 void GLAPIENTRY
327 _tnl_DrawElements(GLenum mode, GLsizei count, GLenum type,
328 const GLvoid *indices)
329 {
330 GET_CURRENT_CONTEXT(ctx);
331 GLuint *ui_indices;
332
333 if (MESA_VERBOSE & VERBOSE_API)
334 _mesa_debug(NULL, "_tnl_DrawElements %d\n", count);
335
336 /* Check arguments, etc. */
337 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
338 return;
339
340 if (ctx->Array.ElementArrayBufferObj->Name) {
341 /* actual address is the sum of pointers */
342 indices = (const GLvoid *)
343 ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data,
344 (const GLubyte *) indices);
345 }
346
347 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
348 count, type, indices );
349
350 assert(!ctx->CompileFlag);
351
352 if (ctx->Array.LockCount) {
353 if (ctx->Array.LockFirst == 0)
354 _tnl_draw_range_elements( ctx, mode,
355 ctx->Array.LockCount,
356 count, ui_indices );
357 else
358 fallback_drawelements( ctx, mode, count, ui_indices );
359 }
360 else {
361 /* Scan the index list and see if we can use the locked path anyway.
362 */
363 GLuint max_elt = 0;
364 GLint i;
365
366 for (i = 0 ; i < count ; i++)
367 if (ui_indices[i] > max_elt)
368 max_elt = ui_indices[i];
369
370 if (max_elt < ctx->Const.MaxArrayLockSize && /* can we use it? */
371 max_elt < (GLuint) count) /* do we want to use it? */
372 _tnl_draw_range_elements( ctx, mode, max_elt+1, count, ui_indices );
373 else
374 fallback_drawelements( ctx, mode, count, ui_indices );
375 }
376 }
377
378
379 /**
380 * Initialize context's vertex array fields. Called during T 'n L context
381 * creation.
382 */
383 void _tnl_array_init( GLcontext *ctx )
384 {
385 TNLcontext *tnl = TNL_CONTEXT(ctx);
386 struct tnl_vertex_arrays *tmp = &tnl->array_inputs;
387 GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt);
388 GLuint i;
389
390 vfmt->DrawArrays = _tnl_DrawArrays;
391 vfmt->DrawElements = _tnl_DrawElements;
392 vfmt->DrawRangeElements = _tnl_DrawRangeElements;
393
394 /* Setup vector pointers that will be used to bind arrays to VB's.
395 */
396 _mesa_vector4f_init( &tmp->Obj, 0, NULL);
397 _mesa_vector4f_init( &tmp->Normal, 0, NULL);
398 _mesa_vector4f_init( &tmp->FogCoord, 0, NULL);
399 _mesa_vector4f_init( &tmp->Index, 0, NULL);
400
401 for (i = 0; i < ctx->Const.MaxTextureUnits; i++)
402 _mesa_vector4f_init( &tmp->TexCoord[i], 0, NULL);
403 }
404
405
406 /**
407 * Destroy the context's vertex array stuff.
408 * Called during T 'n L context destruction.
409 */
410 void _tnl_array_destroy( GLcontext *ctx )
411 {
412 (void) ctx;
413 }