Change < to <= when testing counts against ctx->Const.MaxArrayLockSize,
[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 if (start)
107 for (i = 0 ; i < count ; i++)
108 indices[i] -= start;
109
110 if (ctx->Array.LockCount)
111 tnl->Driver.RunPipeline( ctx );
112 else {
113 /* The lower 16 bits represent the conventional arrays while the
114 * upper 16 bits represent the generic arrays. OR those bits
115 * together to indicate which vertex attribs are in effect.
116 */
117 GLuint enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16);
118 /* Note that arrays may have changed before/after execution.
119 */
120 tnl->pipeline.run_input_changes |= enabledArrays;
121 tnl->Driver.RunPipeline( ctx );
122 tnl->pipeline.run_input_changes |= enabledArrays;
123 }
124
125 if (start)
126 for (i = 0 ; i < count ; i++)
127 indices[i] += start;
128 }
129
130
131
132 /**
133 * Called via the GL API dispatcher.
134 */
135 void GLAPIENTRY
136 _tnl_DrawArrays(GLenum mode, GLint start, GLsizei count)
137 {
138 GET_CURRENT_CONTEXT(ctx);
139 TNLcontext *tnl = TNL_CONTEXT(ctx);
140 GLuint thresh = (ctx->Driver.NeedFlush & FLUSH_STORED_VERTICES) ? 30 : 10;
141 GLuint enabledArrays;
142
143 if (MESA_VERBOSE & VERBOSE_API)
144 _mesa_debug(NULL, "_tnl_DrawArrays %d %d\n", start, count);
145
146 /* Check arguments, etc.
147 */
148 if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
149 return;
150
151 if (tnl->pipeline.build_state_changes)
152 _tnl_validate_pipeline( ctx );
153
154 assert(!ctx->CompileFlag);
155
156 if (!ctx->Array.LockCount && (GLuint) count < thresh) {
157 /* Small primitives: attempt to share a vb (at the expense of
158 * using the immediate interface).
159 */
160 fallback_drawarrays( ctx, mode, start, start + count );
161 }
162 else if (ctx->Array.LockCount &&
163 count <= (GLint) ctx->Const.MaxArrayLockSize) {
164
165 struct tnl_prim prim;
166
167 /* Locked primitives which can fit in a single vertex buffer:
168 */
169 FLUSH_CURRENT( ctx, 0 );
170
171 if (start < (GLint) ctx->Array.LockFirst)
172 start = ctx->Array.LockFirst;
173 if (start + count > (GLint) ctx->Array.LockCount)
174 count = ctx->Array.LockCount - start;
175
176 /* Locked drawarrays. Reuse any previously transformed data.
177 */
178 _tnl_vb_bind_arrays( ctx, ctx->Array.LockFirst, ctx->Array.LockCount );
179
180 tnl->vb.Primitive = &prim;
181 tnl->vb.Primitive[0].mode = mode | PRIM_BEGIN | PRIM_END;
182 tnl->vb.Primitive[0].start = start;
183 tnl->vb.Primitive[0].count = count;
184 tnl->vb.PrimitiveCount = 1;
185
186 tnl->Driver.RunPipeline( ctx );
187 }
188 else {
189 int bufsz = 256; /* Use a small buffer for cache goodness */
190 int j, nr;
191 int minimum, modulo, skip;
192
193 /* Large primitives requiring decomposition to multiple vertex
194 * buffers:
195 */
196 switch (mode) {
197 case GL_POINTS:
198 minimum = 0;
199 modulo = 1;
200 skip = 0;
201 break;
202 case GL_LINES:
203 minimum = 1;
204 modulo = 2;
205 skip = 1;
206 break;
207 case GL_LINE_STRIP:
208 minimum = 1;
209 modulo = 1;
210 skip = 0;
211 break;
212 case GL_TRIANGLES:
213 minimum = 2;
214 modulo = 3;
215 skip = 2;
216 break;
217 case GL_TRIANGLE_STRIP:
218 minimum = 2;
219 modulo = 1;
220 skip = 0;
221 break;
222 case GL_QUADS:
223 minimum = 3;
224 modulo = 4;
225 skip = 3;
226 break;
227 case GL_QUAD_STRIP:
228 minimum = 3;
229 modulo = 2;
230 skip = 0;
231 break;
232 case GL_LINE_LOOP:
233 case GL_TRIANGLE_FAN:
234 case GL_POLYGON:
235 default:
236 /* Primitives requiring a copied vertex (fan-like primitives)
237 * must use the slow path if they cannot fit in a single
238 * vertex buffer.
239 */
240 if (count <= (GLint) ctx->Const.MaxArrayLockSize) {
241 bufsz = ctx->Const.MaxArrayLockSize;
242 minimum = 0;
243 modulo = 1;
244 skip = 0;
245 }
246 else {
247 fallback_drawarrays( ctx, mode, start, start + count );
248 return;
249 }
250 }
251
252 FLUSH_CURRENT( ctx, 0 );
253
254 bufsz -= bufsz % modulo;
255 bufsz -= minimum;
256 count += start;
257
258 for (j = start + minimum ; j < count ; j += nr + skip ) {
259
260 struct tnl_prim prim;
261
262 nr = MIN2( bufsz, count - j );
263
264 /* XXX is the last parameter a count or index into the array??? */
265 _tnl_vb_bind_arrays( ctx, j - minimum, j + nr );
266
267 tnl->vb.Primitive = &prim;
268 tnl->vb.Primitive[0].mode = mode;
269
270 if (j == start + minimum)
271 tnl->vb.Primitive[0].mode |= PRIM_BEGIN;
272
273 if (j + nr + skip >= count)
274 tnl->vb.Primitive[0].mode |= PRIM_END;
275
276 tnl->vb.Primitive[0].start = 0;
277 tnl->vb.Primitive[0].count = nr + minimum;
278 tnl->vb.PrimitiveCount = 1;
279
280 /* The lower 16 bits represent the conventional arrays while the
281 * upper 16 bits represent the generic arrays. OR those bits
282 * together to indicate which vertex attribs are in effect.
283 */
284 enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16);
285 /* Note that arrays may have changed before/after execution.
286 */
287 tnl->pipeline.run_input_changes |= enabledArrays;
288 tnl->Driver.RunPipeline( ctx );
289 tnl->pipeline.run_input_changes |= enabledArrays;
290 }
291 }
292 }
293
294
295 /**
296 * Called via the GL API dispatcher.
297 */
298 void GLAPIENTRY
299 _tnl_DrawRangeElements(GLenum mode,
300 GLuint start, GLuint end,
301 GLsizei count, GLenum type, const GLvoid *indices)
302 {
303 GET_CURRENT_CONTEXT(ctx);
304 GLuint *ui_indices;
305
306 if (MESA_VERBOSE & VERBOSE_API)
307 _mesa_debug(NULL, "_tnl_DrawRangeElements %d %d %d\n", start, end, count);
308
309 if (ctx->Array.ElementArrayBufferObj->Name) {
310 /* use indices in the buffer object */
311 if (!ctx->Array.ElementArrayBufferObj->Data) {
312 _mesa_warning(ctx,
313 "DrawRangeElements with empty vertex elements buffer!");
314 return;
315 }
316 /* actual address is the sum of pointers */
317 indices = (const GLvoid *)
318 ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data,
319 (const GLubyte *) indices);
320 }
321
322 /* Check arguments, etc.
323 */
324 if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count,
325 type, indices ))
326 return;
327
328 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
329 count, type, indices );
330
331
332 assert(!ctx->CompileFlag);
333
334 if (ctx->Array.LockCount) {
335 /* Are the arrays already locked? If so we currently have to look
336 * at the whole locked range.
337 */
338 if (start >= ctx->Array.LockFirst && end <= ctx->Array.LockCount)
339 _tnl_draw_range_elements( ctx, mode,
340 ctx->Array.LockFirst,
341 ctx->Array.LockCount,
342 count, ui_indices );
343 else {
344 /* The spec says referencing elements outside the locked
345 * range is undefined. I'm going to make it a noop this time
346 * round, maybe come up with something beter before 3.6.
347 *
348 * May be able to get away with just setting LockCount==0,
349 * though this raises the problems of dependent state. May
350 * have to call glUnlockArrays() directly?
351 *
352 * Or scan the list and replace bad indices?
353 */
354 _mesa_problem( ctx,
355 "DrawRangeElements references "
356 "elements outside locked range.");
357 }
358 }
359 else if (end - start + 1 <= ctx->Const.MaxArrayLockSize) {
360 /* The arrays aren't locked but we can still fit them inside a
361 * single vertexbuffer.
362 */
363 _tnl_draw_range_elements( ctx, mode, start, end + 1, count, ui_indices );
364 }
365 else {
366 /* Range is too big to optimize:
367 */
368 fallback_drawelements( ctx, mode, count, ui_indices );
369 }
370 }
371
372
373
374 /**
375 * Called via the GL API dispatcher.
376 */
377 void GLAPIENTRY
378 _tnl_DrawElements(GLenum mode, GLsizei count, GLenum type,
379 const GLvoid *indices)
380 {
381 GET_CURRENT_CONTEXT(ctx);
382 GLuint *ui_indices;
383
384 if (MESA_VERBOSE & VERBOSE_API)
385 _mesa_debug(NULL, "_tnl_DrawElements %d\n", count);
386
387 /* Check arguments, etc. */
388 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
389 return;
390
391 if (ctx->Array.ElementArrayBufferObj->Name) {
392 /* actual address is the sum of pointers */
393 indices = (const GLvoid *)
394 ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data,
395 (const GLubyte *) indices);
396 }
397
398 ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT,
399 count, type, indices );
400
401 assert(!ctx->CompileFlag);
402
403 if (ctx->Array.LockCount) {
404 _tnl_draw_range_elements( ctx, mode,
405 ctx->Array.LockFirst,
406 ctx->Array.LockCount,
407 count, ui_indices );
408 }
409 else {
410 /* Scan the index list and see if we can use the locked path anyway.
411 */
412 GLuint max_elt = 0;
413 GLint i;
414
415 for (i = 0 ; i < count ; i++)
416 if (ui_indices[i] > max_elt)
417 max_elt = ui_indices[i];
418
419 /* XXX should this < really be <= ??? */
420 if (max_elt < ctx->Const.MaxArrayLockSize && /* can we use it? */
421 max_elt < (GLuint) count) /* do we want to use it? */
422 _tnl_draw_range_elements( ctx, mode, 0, max_elt+1, count, ui_indices );
423 else
424 fallback_drawelements( ctx, mode, count, ui_indices );
425 }
426 }
427
428
429 /**
430 * Initialize context's vertex array fields. Called during T 'n L context
431 * creation.
432 */
433 void _tnl_array_init( GLcontext *ctx )
434 {
435 TNLcontext *tnl = TNL_CONTEXT(ctx);
436 struct tnl_vertex_arrays *tmp = &tnl->array_inputs;
437 GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt);
438 GLuint i;
439
440 vfmt->DrawArrays = _tnl_DrawArrays;
441 vfmt->DrawElements = _tnl_DrawElements;
442 vfmt->DrawRangeElements = _tnl_DrawRangeElements;
443
444 /* Setup vector pointers that will be used to bind arrays to VB's.
445 */
446 _mesa_vector4f_init( &tmp->Obj, 0, 0 );
447 _mesa_vector4f_init( &tmp->Normal, 0, 0 );
448 _mesa_vector4f_init( &tmp->FogCoord, 0, 0 );
449 _mesa_vector4f_init( &tmp->Index, 0, 0 );
450
451 for (i = 0; i < ctx->Const.MaxTextureUnits; i++)
452 _mesa_vector4f_init( &tmp->TexCoord[i], 0, 0);
453 }
454
455
456 /**
457 * Destroy the context's vertex array stuff.
458 * Called during T 'n L context destruction.
459 */
460 void _tnl_array_destroy( GLcontext *ctx )
461 {
462 }