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