Don't call _tnl_draw_range_elements() unless start == 0.
[mesa.git] / src / mesa / tnl / t_array_api.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.0
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 glBegin(mode);
56 for (i = start; i < count; i++)
57 glArrayElement( i );
58 glEnd();
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 glBegin(mode);
73 for (i = 0 ; i < count ; i++) {
74 glArrayElement( indices[i] );
75 }
76 glEnd();
77 }
78
79
80 static void _tnl_draw_range_elements( GLcontext *ctx, GLenum mode,
81 GLuint start, GLuint end,
82 GLsizei count, GLuint *indices )
83
84 {
85 TNLcontext *tnl = TNL_CONTEXT(ctx);
86 struct tnl_prim prim;
87 int i;
88 FLUSH_CURRENT( ctx, 0 );
89
90 if (tnl->pipeline.build_state_changes)
91 _tnl_validate_pipeline( ctx );
92
93 /* XXX is "end" correct? Looking at the implementation of
94 * _tnl_vb_bind_arrays(), perhaps we should pass end-start.
95 */
96 _tnl_vb_bind_arrays( ctx, start, end );
97
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;
103
104 tnl->vb.Elts = (GLuint *)indices;
105
106 assert (start == 0);
107
108 /* XXX - indices may be read only
109 if (start)
110 for (i = 0 ; i < count ; i++)
111 indices[i] -= start;
112 */
113
114 if (ctx->Array.LockCount)
115 tnl->Driver.RunPipeline( ctx );
116 else {
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.
120 */
121 GLuint enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16);
122 /* Note that arrays may have changed before/after execution.
123 */
124 tnl->pipeline.run_input_changes |= enabledArrays;
125 tnl->Driver.RunPipeline( ctx );
126 tnl->pipeline.run_input_changes |= enabledArrays;
127 }
128
129 /* XXX - indices may be read only
130 if (start)
131 for (i = 0 ; i < count ; i++)
132 indices[i] += start;
133 */
134 }
135
136
137
138 /**
139 * Called via the GL API dispatcher.
140 */
141 void GLAPIENTRY
142 _tnl_DrawArrays(GLenum mode, GLint start, GLsizei count)
143 {
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;
148
149 if (MESA_VERBOSE & VERBOSE_API)
150 _mesa_debug(NULL, "_tnl_DrawArrays %d %d\n", start, count);
151
152 /* Check arguments, etc.
153 */
154 if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
155 return;
156
157 if (tnl->pipeline.build_state_changes)
158 _tnl_validate_pipeline( ctx );
159
160 assert(!ctx->CompileFlag);
161
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).
165 */
166 fallback_drawarrays( ctx, mode, start, start + count );
167 }
168 else if (ctx->Array.LockCount &&
169 count <= (GLint) ctx->Const.MaxArrayLockSize) {
170
171 struct tnl_prim prim;
172
173 /* Locked primitives which can fit in a single vertex buffer:
174 */
175 FLUSH_CURRENT( ctx, 0 );
176
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;
181
182 /* Locked drawarrays. Reuse any previously transformed data.
183 */
184 _tnl_vb_bind_arrays( ctx, ctx->Array.LockFirst, ctx->Array.LockCount );
185
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;
191
192 tnl->Driver.RunPipeline( ctx );
193 }
194 else {
195 int bufsz = 256; /* Use a small buffer for cache goodness */
196 int j, nr;
197 int minimum, modulo, skip;
198
199 /* Large primitives requiring decomposition to multiple vertex
200 * buffers:
201 */
202 switch (mode) {
203 case GL_POINTS:
204 minimum = 0;
205 modulo = 1;
206 skip = 0;
207 break;
208 case GL_LINES:
209 minimum = 1;
210 modulo = 2;
211 skip = 1;
212 break;
213 case GL_LINE_STRIP:
214 minimum = 1;
215 modulo = 1;
216 skip = 0;
217 break;
218 case GL_TRIANGLES:
219 minimum = 2;
220 modulo = 3;
221 skip = 2;
222 break;
223 case GL_TRIANGLE_STRIP:
224 minimum = 2;
225 modulo = 1;
226 skip = 0;
227 break;
228 case GL_QUADS:
229 minimum = 3;
230 modulo = 4;
231 skip = 3;
232 break;
233 case GL_QUAD_STRIP:
234 minimum = 3;
235 modulo = 2;
236 skip = 0;
237 break;
238 case GL_LINE_LOOP:
239 case GL_TRIANGLE_FAN:
240 case GL_POLYGON:
241 default:
242 /* Primitives requiring a copied vertex (fan-like primitives)
243 * must use the slow path if they cannot fit in a single
244 * vertex buffer.
245 */
246 if (count <= (GLint) ctx->Const.MaxArrayLockSize) {
247 bufsz = ctx->Const.MaxArrayLockSize;
248 minimum = 0;
249 modulo = 1;
250 skip = 0;
251 }
252 else {
253 fallback_drawarrays( ctx, mode, start, start + count );
254 return;
255 }
256 }
257
258 FLUSH_CURRENT( ctx, 0 );
259
260 bufsz -= bufsz % modulo;
261 bufsz -= minimum;
262 count += start;
263
264 for (j = start + minimum ; j < count ; j += nr + skip ) {
265
266 struct tnl_prim prim;
267
268 nr = MIN2( bufsz, count - j );
269
270 /* XXX is the last parameter a count or index into the array??? */
271 _tnl_vb_bind_arrays( ctx, j - minimum, j + nr );
272
273 tnl->vb.Primitive = &prim;
274 tnl->vb.Primitive[0].mode = mode;
275
276 if (j == start + minimum)
277 tnl->vb.Primitive[0].mode |= PRIM_BEGIN;
278
279 if (j + nr + skip >= count)
280 tnl->vb.Primitive[0].mode |= PRIM_END;
281
282 tnl->vb.Primitive[0].start = 0;
283 tnl->vb.Primitive[0].count = nr + minimum;
284 tnl->vb.PrimitiveCount = 1;
285
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.
289 */
290 enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16);
291 /* Note that arrays may have changed before/after execution.
292 */
293 tnl->pipeline.run_input_changes |= enabledArrays;
294 tnl->Driver.RunPipeline( ctx );
295 tnl->pipeline.run_input_changes |= enabledArrays;
296 }
297 }
298 }
299
300
301 /**
302 * Called via the GL API dispatcher.
303 */
304 void GLAPIENTRY
305 _tnl_DrawRangeElements(GLenum mode,
306 GLuint start, GLuint end,
307 GLsizei count, GLenum type, const GLvoid *indices)
308 {
309 GET_CURRENT_CONTEXT(ctx);
310 GLuint *ui_indices;
311
312 if (MESA_VERBOSE & VERBOSE_API)
313 _mesa_debug(NULL, "_tnl_DrawRangeElements %d %d %d\n", start, end, count);
314
315 if (ctx->Array.ElementArrayBufferObj->Name) {
316 /* use indices in the buffer object */
317 if (!ctx->Array.ElementArrayBufferObj->Data) {
318 _mesa_warning(ctx,
319 "DrawRangeElements with empty vertex elements buffer!");
320 return;
321 }
322 /* actual address is the sum of pointers */
323 indices = (const GLvoid *)
324 ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data,
325 (const GLubyte *) indices);
326 }
327
328 /* Check arguments, etc.
329 */
330 if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count,
331 type, indices ))
332 return;
333
334 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
335 count, type, indices );
336
337
338 assert(!ctx->CompileFlag);
339
340 if (ctx->Array.LockCount) {
341 /* Are the arrays already locked? If so we currently have to look
342 * at the whole locked range.
343 */
344 if (start == 0 &&
345 start >= ctx->Array.LockFirst && end <= ctx->Array.LockCount)
346 _tnl_draw_range_elements( ctx, mode,
347 ctx->Array.LockFirst,
348 ctx->Array.LockCount,
349 count, ui_indices );
350 else {
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.
354 *
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?
358 *
359 * Or scan the list and replace bad indices?
360 */
361 _mesa_problem( ctx,
362 "DrawRangeElements references "
363 "elements outside locked range.");
364 }
365 }
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.
370 */
371 _tnl_draw_range_elements( ctx, mode, start, end + 1, count, ui_indices );
372 }
373 else {
374 /* Range is too big to optimize:
375 */
376 fallback_drawelements( ctx, mode, count, ui_indices );
377 }
378 }
379
380
381
382 /**
383 * Called via the GL API dispatcher.
384 */
385 void GLAPIENTRY
386 _tnl_DrawElements(GLenum mode, GLsizei count, GLenum type,
387 const GLvoid *indices)
388 {
389 GET_CURRENT_CONTEXT(ctx);
390 GLuint *ui_indices;
391
392 if (MESA_VERBOSE & VERBOSE_API)
393 _mesa_debug(NULL, "_tnl_DrawElements %d\n", count);
394
395 /* Check arguments, etc. */
396 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
397 return;
398
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);
404 }
405
406 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
407 count, type, indices );
408
409 assert(!ctx->CompileFlag);
410
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,
416 count, ui_indices );
417 }
418 else {
419 /* Scan the index list and see if we can use the locked path anyway.
420 */
421 GLuint max_elt = 0;
422 GLint i;
423
424 for (i = 0 ; i < count ; i++)
425 if (ui_indices[i] > max_elt)
426 max_elt = ui_indices[i];
427
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 );
432 else
433 fallback_drawelements( ctx, mode, count, ui_indices );
434 }
435 }
436
437
438 /**
439 * Initialize context's vertex array fields. Called during T 'n L context
440 * creation.
441 */
442 void _tnl_array_init( GLcontext *ctx )
443 {
444 TNLcontext *tnl = TNL_CONTEXT(ctx);
445 struct tnl_vertex_arrays *tmp = &tnl->array_inputs;
446 GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt);
447 GLuint i;
448
449 vfmt->DrawArrays = _tnl_DrawArrays;
450 vfmt->DrawElements = _tnl_DrawElements;
451 vfmt->DrawRangeElements = _tnl_DrawRangeElements;
452
453 /* Setup vector pointers that will be used to bind arrays to VB's.
454 */
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 );
459
460 for (i = 0; i < ctx->Const.MaxTextureUnits; i++)
461 _mesa_vector4f_init( &tmp->TexCoord[i], 0, 0);
462 }
463
464
465 /**
466 * Destroy the context's vertex array stuff.
467 * Called during T 'n L context destruction.
468 */
469 void _tnl_array_destroy( GLcontext *ctx )
470 {
471 }