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