st/mesa: simplify the signature of get_client_array
[mesa.git] / src / mesa / state_tracker / st_atom_array.c
1
2 /**************************************************************************
3 *
4 * Copyright 2007 VMware, Inc.
5 * Copyright 2012 Marek Olšák <maraeo@gmail.com>
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
23 * IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30 /*
31 * This converts the VBO's vertex attribute/array information into
32 * Gallium vertex state and binds it.
33 *
34 * Authors:
35 * Keith Whitwell <keithw@vmware.com>
36 * Marek Olšák <maraeo@gmail.com>
37 */
38
39 #include "st_context.h"
40 #include "st_atom.h"
41 #include "st_cb_bufferobjects.h"
42 #include "st_draw.h"
43 #include "st_program.h"
44
45 #include "cso_cache/cso_context.h"
46 #include "util/u_math.h"
47 #include "main/bufferobj.h"
48 #include "main/glformats.h"
49
50 /* vertex_formats[gltype - GL_BYTE][integer*2 + normalized][size - 1] */
51 static const uint16_t vertex_formats[][4][4] = {
52 { /* GL_BYTE */
53 {
54 PIPE_FORMAT_R8_SSCALED,
55 PIPE_FORMAT_R8G8_SSCALED,
56 PIPE_FORMAT_R8G8B8_SSCALED,
57 PIPE_FORMAT_R8G8B8A8_SSCALED
58 },
59 {
60 PIPE_FORMAT_R8_SNORM,
61 PIPE_FORMAT_R8G8_SNORM,
62 PIPE_FORMAT_R8G8B8_SNORM,
63 PIPE_FORMAT_R8G8B8A8_SNORM
64 },
65 {
66 PIPE_FORMAT_R8_SINT,
67 PIPE_FORMAT_R8G8_SINT,
68 PIPE_FORMAT_R8G8B8_SINT,
69 PIPE_FORMAT_R8G8B8A8_SINT
70 },
71 },
72 { /* GL_UNSIGNED_BYTE */
73 {
74 PIPE_FORMAT_R8_USCALED,
75 PIPE_FORMAT_R8G8_USCALED,
76 PIPE_FORMAT_R8G8B8_USCALED,
77 PIPE_FORMAT_R8G8B8A8_USCALED
78 },
79 {
80 PIPE_FORMAT_R8_UNORM,
81 PIPE_FORMAT_R8G8_UNORM,
82 PIPE_FORMAT_R8G8B8_UNORM,
83 PIPE_FORMAT_R8G8B8A8_UNORM
84 },
85 {
86 PIPE_FORMAT_R8_UINT,
87 PIPE_FORMAT_R8G8_UINT,
88 PIPE_FORMAT_R8G8B8_UINT,
89 PIPE_FORMAT_R8G8B8A8_UINT
90 },
91 },
92 { /* GL_SHORT */
93 {
94 PIPE_FORMAT_R16_SSCALED,
95 PIPE_FORMAT_R16G16_SSCALED,
96 PIPE_FORMAT_R16G16B16_SSCALED,
97 PIPE_FORMAT_R16G16B16A16_SSCALED
98 },
99 {
100 PIPE_FORMAT_R16_SNORM,
101 PIPE_FORMAT_R16G16_SNORM,
102 PIPE_FORMAT_R16G16B16_SNORM,
103 PIPE_FORMAT_R16G16B16A16_SNORM
104 },
105 {
106 PIPE_FORMAT_R16_SINT,
107 PIPE_FORMAT_R16G16_SINT,
108 PIPE_FORMAT_R16G16B16_SINT,
109 PIPE_FORMAT_R16G16B16A16_SINT
110 },
111 },
112 { /* GL_UNSIGNED_SHORT */
113 {
114 PIPE_FORMAT_R16_USCALED,
115 PIPE_FORMAT_R16G16_USCALED,
116 PIPE_FORMAT_R16G16B16_USCALED,
117 PIPE_FORMAT_R16G16B16A16_USCALED
118 },
119 {
120 PIPE_FORMAT_R16_UNORM,
121 PIPE_FORMAT_R16G16_UNORM,
122 PIPE_FORMAT_R16G16B16_UNORM,
123 PIPE_FORMAT_R16G16B16A16_UNORM
124 },
125 {
126 PIPE_FORMAT_R16_UINT,
127 PIPE_FORMAT_R16G16_UINT,
128 PIPE_FORMAT_R16G16B16_UINT,
129 PIPE_FORMAT_R16G16B16A16_UINT
130 },
131 },
132 { /* GL_INT */
133 {
134 PIPE_FORMAT_R32_SSCALED,
135 PIPE_FORMAT_R32G32_SSCALED,
136 PIPE_FORMAT_R32G32B32_SSCALED,
137 PIPE_FORMAT_R32G32B32A32_SSCALED
138 },
139 {
140 PIPE_FORMAT_R32_SNORM,
141 PIPE_FORMAT_R32G32_SNORM,
142 PIPE_FORMAT_R32G32B32_SNORM,
143 PIPE_FORMAT_R32G32B32A32_SNORM
144 },
145 {
146 PIPE_FORMAT_R32_SINT,
147 PIPE_FORMAT_R32G32_SINT,
148 PIPE_FORMAT_R32G32B32_SINT,
149 PIPE_FORMAT_R32G32B32A32_SINT
150 },
151 },
152 { /* GL_UNSIGNED_INT */
153 {
154 PIPE_FORMAT_R32_USCALED,
155 PIPE_FORMAT_R32G32_USCALED,
156 PIPE_FORMAT_R32G32B32_USCALED,
157 PIPE_FORMAT_R32G32B32A32_USCALED
158 },
159 {
160 PIPE_FORMAT_R32_UNORM,
161 PIPE_FORMAT_R32G32_UNORM,
162 PIPE_FORMAT_R32G32B32_UNORM,
163 PIPE_FORMAT_R32G32B32A32_UNORM
164 },
165 {
166 PIPE_FORMAT_R32_UINT,
167 PIPE_FORMAT_R32G32_UINT,
168 PIPE_FORMAT_R32G32B32_UINT,
169 PIPE_FORMAT_R32G32B32A32_UINT
170 },
171 },
172 { /* GL_FLOAT */
173 {
174 PIPE_FORMAT_R32_FLOAT,
175 PIPE_FORMAT_R32G32_FLOAT,
176 PIPE_FORMAT_R32G32B32_FLOAT,
177 PIPE_FORMAT_R32G32B32A32_FLOAT
178 },
179 {
180 PIPE_FORMAT_R32_FLOAT,
181 PIPE_FORMAT_R32G32_FLOAT,
182 PIPE_FORMAT_R32G32B32_FLOAT,
183 PIPE_FORMAT_R32G32B32A32_FLOAT
184 },
185 },
186 {{0}}, /* GL_2_BYTES */
187 {{0}}, /* GL_3_BYTES */
188 {{0}}, /* GL_4_BYTES */
189 { /* GL_DOUBLE */
190 {
191 PIPE_FORMAT_R64_FLOAT,
192 PIPE_FORMAT_R64G64_FLOAT,
193 PIPE_FORMAT_R64G64B64_FLOAT,
194 PIPE_FORMAT_R64G64B64A64_FLOAT
195 },
196 {
197 PIPE_FORMAT_R64_FLOAT,
198 PIPE_FORMAT_R64G64_FLOAT,
199 PIPE_FORMAT_R64G64B64_FLOAT,
200 PIPE_FORMAT_R64G64B64A64_FLOAT
201 },
202 },
203 { /* GL_HALF_FLOAT */
204 {
205 PIPE_FORMAT_R16_FLOAT,
206 PIPE_FORMAT_R16G16_FLOAT,
207 PIPE_FORMAT_R16G16B16_FLOAT,
208 PIPE_FORMAT_R16G16B16A16_FLOAT
209 },
210 {
211 PIPE_FORMAT_R16_FLOAT,
212 PIPE_FORMAT_R16G16_FLOAT,
213 PIPE_FORMAT_R16G16B16_FLOAT,
214 PIPE_FORMAT_R16G16B16A16_FLOAT
215 },
216 },
217 { /* GL_FIXED */
218 {
219 PIPE_FORMAT_R32_FIXED,
220 PIPE_FORMAT_R32G32_FIXED,
221 PIPE_FORMAT_R32G32B32_FIXED,
222 PIPE_FORMAT_R32G32B32A32_FIXED
223 },
224 {
225 PIPE_FORMAT_R32_FIXED,
226 PIPE_FORMAT_R32G32_FIXED,
227 PIPE_FORMAT_R32G32B32_FIXED,
228 PIPE_FORMAT_R32G32B32A32_FIXED
229 },
230 },
231 };
232
233
234 /**
235 * Return a PIPE_FORMAT_x for the given GL datatype and size.
236 */
237 enum pipe_format
238 st_pipe_vertex_format(GLenum type, GLuint size, GLenum format,
239 GLboolean normalized, GLboolean integer)
240 {
241 unsigned index;
242
243 assert(size >= 1 && size <= 4);
244 assert(format == GL_RGBA || format == GL_BGRA);
245
246 switch (type) {
247 case GL_HALF_FLOAT_OES:
248 type = GL_HALF_FLOAT;
249 break;
250
251 case GL_INT_2_10_10_10_REV:
252 assert(size == 4 && !integer);
253
254 if (format == GL_BGRA) {
255 if (normalized)
256 return PIPE_FORMAT_B10G10R10A2_SNORM;
257 else
258 return PIPE_FORMAT_B10G10R10A2_SSCALED;
259 } else {
260 if (normalized)
261 return PIPE_FORMAT_R10G10B10A2_SNORM;
262 else
263 return PIPE_FORMAT_R10G10B10A2_SSCALED;
264 }
265 break;
266
267 case GL_UNSIGNED_INT_2_10_10_10_REV:
268 assert(size == 4 && !integer);
269
270 if (format == GL_BGRA) {
271 if (normalized)
272 return PIPE_FORMAT_B10G10R10A2_UNORM;
273 else
274 return PIPE_FORMAT_B10G10R10A2_USCALED;
275 } else {
276 if (normalized)
277 return PIPE_FORMAT_R10G10B10A2_UNORM;
278 else
279 return PIPE_FORMAT_R10G10B10A2_USCALED;
280 }
281 break;
282
283 case GL_UNSIGNED_INT_10F_11F_11F_REV:
284 assert(size == 3 && !integer && format == GL_RGBA);
285 return PIPE_FORMAT_R11G11B10_FLOAT;
286
287 case GL_UNSIGNED_BYTE:
288 if (format == GL_BGRA) {
289 /* this is an odd-ball case */
290 assert(normalized);
291 return PIPE_FORMAT_B8G8R8A8_UNORM;
292 }
293 break;
294 }
295
296 index = integer*2 + normalized;
297 assert(index <= 2);
298 assert(type >= GL_BYTE && type <= GL_FIXED);
299 return vertex_formats[type - GL_BYTE][index][size-1];
300 }
301
302 static const struct gl_vertex_array *
303 get_client_array(const struct gl_vertex_array **arrays,
304 unsigned mesaAttr)
305 {
306 /* st_program uses 0xffffffff to denote a double placeholder attribute */
307 if (mesaAttr == ST_DOUBLE_ATTRIB_PLACEHOLDER)
308 return NULL;
309 return arrays[mesaAttr];
310 }
311
312 /**
313 * Examine the active arrays to determine if we have interleaved
314 * vertex arrays all living in one VBO, or all living in user space.
315 */
316 static GLboolean
317 is_interleaved_arrays(const struct st_vertex_program *vp,
318 const struct gl_vertex_array **arrays,
319 unsigned num_inputs)
320 {
321 GLuint attr;
322 const struct gl_buffer_object *firstBufObj = NULL;
323 GLint firstStride = -1;
324 const GLubyte *firstPtr = NULL;
325 GLboolean userSpaceBuffer = GL_FALSE;
326
327 for (attr = 0; attr < num_inputs; attr++) {
328 const struct gl_vertex_array *array;
329 const struct gl_buffer_object *bufObj;
330 GLsizei stride;
331
332 array = get_client_array(arrays, vp->index_to_input[attr]);
333 if (!array)
334 continue;
335
336 stride = array->StrideB; /* in bytes */
337 bufObj = array->BufferObj;
338 if (attr == 0) {
339 /* save info about the first array */
340 firstStride = stride;
341 firstPtr = array->Ptr;
342 firstBufObj = bufObj;
343 userSpaceBuffer = !bufObj || !bufObj->Name;
344 }
345 else {
346 /* check if other arrays interleave with the first, in same buffer */
347 if (stride != firstStride)
348 return GL_FALSE; /* strides don't match */
349
350 if (bufObj != firstBufObj)
351 return GL_FALSE; /* arrays in different VBOs */
352
353 if (llabs(array->Ptr - firstPtr) > firstStride)
354 return GL_FALSE; /* arrays start too far apart */
355
356 if ((!_mesa_is_bufferobj(bufObj)) != userSpaceBuffer)
357 return GL_FALSE; /* mix of VBO and user-space arrays */
358 }
359 }
360
361 return GL_TRUE;
362 }
363
364 static void init_velement(struct pipe_vertex_element *velement,
365 int src_offset, int format,
366 int instance_divisor, int vbo_index)
367 {
368 velement->src_offset = src_offset;
369 velement->src_format = format;
370 velement->instance_divisor = instance_divisor;
371 velement->vertex_buffer_index = vbo_index;
372 assert(velement->src_format);
373 }
374
375 static void init_velement_lowered(const struct st_vertex_program *vp,
376 struct pipe_vertex_element *velements,
377 int src_offset, int format,
378 int instance_divisor, int vbo_index,
379 int nr_components, GLboolean doubles,
380 GLuint *attr_idx)
381 {
382 int idx = *attr_idx;
383 if (doubles) {
384 int lower_format;
385
386 if (nr_components < 2)
387 lower_format = PIPE_FORMAT_R32G32_UINT;
388 else
389 lower_format = PIPE_FORMAT_R32G32B32A32_UINT;
390
391 init_velement(&velements[idx], src_offset,
392 lower_format, instance_divisor, vbo_index);
393 idx++;
394
395 if (idx < vp->num_inputs &&
396 vp->index_to_input[idx] == ST_DOUBLE_ATTRIB_PLACEHOLDER) {
397 if (nr_components >= 3) {
398 if (nr_components == 3)
399 lower_format = PIPE_FORMAT_R32G32_UINT;
400 else
401 lower_format = PIPE_FORMAT_R32G32B32A32_UINT;
402
403 init_velement(&velements[idx], src_offset + 4 * sizeof(float),
404 lower_format, instance_divisor, vbo_index);
405 } else {
406 /* The values here are undefined. Fill in some conservative
407 * dummy values.
408 */
409 init_velement(&velements[idx], src_offset, PIPE_FORMAT_R32G32_UINT,
410 instance_divisor, vbo_index);
411 }
412
413 idx++;
414 }
415 } else {
416 init_velement(&velements[idx], src_offset,
417 format, instance_divisor, vbo_index);
418 idx++;
419 }
420 *attr_idx = idx;
421 }
422
423 static void
424 set_vertex_attribs(struct st_context *st,
425 struct pipe_vertex_buffer *vbuffers,
426 unsigned num_vbuffers,
427 struct pipe_vertex_element *velements,
428 unsigned num_velements)
429 {
430 struct cso_context *cso = st->cso_context;
431
432 cso_set_vertex_buffers(cso, 0, num_vbuffers, vbuffers);
433 if (st->last_num_vbuffers > num_vbuffers) {
434 /* Unbind remaining buffers, if any. */
435 cso_set_vertex_buffers(cso, num_vbuffers,
436 st->last_num_vbuffers - num_vbuffers, NULL);
437 }
438 st->last_num_vbuffers = num_vbuffers;
439 cso_set_vertex_elements(cso, num_velements, velements);
440 }
441
442 /**
443 * Set up for drawing interleaved arrays that all live in one VBO
444 * or all live in user space.
445 * \param vbuffer returns vertex buffer info
446 * \param velements returns vertex element info
447 */
448 static void
449 setup_interleaved_attribs(struct st_context *st,
450 const struct st_vertex_program *vp,
451 const struct gl_vertex_array **arrays,
452 unsigned num_inputs)
453 {
454 struct pipe_vertex_buffer vbuffer;
455 struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS] = {{0}};
456 GLuint attr;
457 const GLubyte *low_addr = NULL;
458 GLboolean usingVBO; /* all arrays in a VBO? */
459 struct gl_buffer_object *bufobj;
460 GLsizei stride;
461
462 /* Find the lowest address of the arrays we're drawing,
463 * Init bufobj and stride.
464 */
465 if (num_inputs) {
466 const struct gl_vertex_array *array;
467
468 array = get_client_array(arrays, vp->index_to_input[0]);
469 assert(array);
470
471 /* Since we're doing interleaved arrays, we know there'll be at most
472 * one buffer object and the stride will be the same for all arrays.
473 * Grab them now.
474 */
475 bufobj = array->BufferObj;
476 stride = array->StrideB;
477
478 low_addr = arrays[vp->index_to_input[0]]->Ptr;
479
480 for (attr = 1; attr < num_inputs; attr++) {
481 const GLubyte *start;
482 array = get_client_array(arrays, vp->index_to_input[attr]);
483 if (!array)
484 continue;
485 start = array->Ptr;
486 low_addr = MIN2(low_addr, start);
487 }
488 }
489 else {
490 /* not sure we'll ever have zero inputs, but play it safe */
491 bufobj = NULL;
492 stride = 0;
493 low_addr = 0;
494 }
495
496 /* are the arrays in user space? */
497 usingVBO = _mesa_is_bufferobj(bufobj);
498
499 for (attr = 0; attr < num_inputs;) {
500 const struct gl_vertex_array *array;
501 unsigned src_offset;
502 unsigned src_format;
503
504 array = get_client_array(arrays, vp->index_to_input[attr]);
505 assert(array);
506
507 src_offset = (unsigned) (array->Ptr - low_addr);
508 assert(array->_ElementSize ==
509 _mesa_bytes_per_vertex_attrib(array->Size, array->Type));
510
511 src_format = st_pipe_vertex_format(array->Type,
512 array->Size,
513 array->Format,
514 array->Normalized,
515 array->Integer);
516
517 init_velement_lowered(vp, velements, src_offset, src_format,
518 array->InstanceDivisor, 0,
519 array->Size, array->Doubles, &attr);
520 }
521
522 /*
523 * Return the vbuffer info and setup user-space attrib info, if needed.
524 */
525 if (num_inputs == 0) {
526 /* just defensive coding here */
527 vbuffer.buffer.resource = NULL;
528 vbuffer.is_user_buffer = false;
529 vbuffer.buffer_offset = 0;
530 vbuffer.stride = 0;
531 }
532 else if (usingVBO) {
533 /* all interleaved arrays in a VBO */
534 struct st_buffer_object *stobj = st_buffer_object(bufobj);
535
536 if (!stobj || !stobj->buffer) {
537 st->vertex_array_out_of_memory = true;
538 return; /* out-of-memory error probably */
539 }
540
541 vbuffer.buffer.resource = stobj->buffer;
542 vbuffer.is_user_buffer = false;
543 vbuffer.buffer_offset = pointer_to_offset(low_addr);
544 vbuffer.stride = stride;
545 }
546 else {
547 /* all interleaved arrays in user memory */
548 vbuffer.buffer.user = low_addr;
549 vbuffer.is_user_buffer = !!low_addr; /* if NULL, then unbind */
550 vbuffer.buffer_offset = 0;
551 vbuffer.stride = stride;
552 }
553
554 set_vertex_attribs(st, &vbuffer, num_inputs ? 1 : 0,
555 velements, num_inputs);
556 }
557
558 /**
559 * Set up a separate pipe_vertex_buffer and pipe_vertex_element for each
560 * vertex attribute.
561 * \param vbuffer returns vertex buffer info
562 * \param velements returns vertex element info
563 */
564 static void
565 setup_non_interleaved_attribs(struct st_context *st,
566 const struct st_vertex_program *vp,
567 const struct gl_vertex_array **arrays,
568 unsigned num_inputs)
569 {
570 struct gl_context *ctx = st->ctx;
571 struct pipe_vertex_buffer vbuffer[PIPE_MAX_ATTRIBS];
572 struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS] = {{0}};
573 unsigned num_vbuffers = 0;
574 GLuint attr;
575
576 for (attr = 0; attr < num_inputs;) {
577 const unsigned mesaAttr = vp->index_to_input[attr];
578 const struct gl_vertex_array *array;
579 struct gl_buffer_object *bufobj;
580 GLsizei stride;
581 unsigned src_format;
582 unsigned bufidx;
583
584 array = get_client_array(arrays, mesaAttr);
585 assert(array);
586
587 bufidx = num_vbuffers++;
588
589 stride = array->StrideB;
590 bufobj = array->BufferObj;
591 assert(array->_ElementSize ==
592 _mesa_bytes_per_vertex_attrib(array->Size, array->Type));
593
594 if (_mesa_is_bufferobj(bufobj)) {
595 /* Attribute data is in a VBO.
596 * Recall that for VBOs, the gl_vertex_array->Ptr field is
597 * really an offset from the start of the VBO, not a pointer.
598 */
599 struct st_buffer_object *stobj = st_buffer_object(bufobj);
600
601 if (!stobj || !stobj->buffer) {
602 st->vertex_array_out_of_memory = true;
603 return; /* out-of-memory error probably */
604 }
605
606 vbuffer[bufidx].buffer.resource = stobj->buffer;
607 vbuffer[bufidx].is_user_buffer = false;
608 vbuffer[bufidx].buffer_offset = pointer_to_offset(array->Ptr);
609 }
610 else {
611 /* wrap user data */
612 void *ptr;
613
614 if (array->Ptr) {
615 ptr = (void *) array->Ptr;
616 }
617 else {
618 /* no array, use ctx->Current.Attrib[] value */
619 ptr = (void *) ctx->Current.Attrib[mesaAttr];
620 stride = 0;
621 }
622
623 assert(ptr);
624
625 vbuffer[bufidx].buffer.user = ptr;
626 vbuffer[bufidx].is_user_buffer = !!ptr; /* if NULL, then unbind */
627 vbuffer[bufidx].buffer_offset = 0;
628 }
629
630 /* common-case setup */
631 vbuffer[bufidx].stride = stride; /* in bytes */
632
633 src_format = st_pipe_vertex_format(array->Type,
634 array->Size,
635 array->Format,
636 array->Normalized,
637 array->Integer);
638
639 init_velement_lowered(vp, velements, 0, src_format,
640 array->InstanceDivisor, bufidx,
641 array->Size, array->Doubles, &attr);
642 }
643
644 set_vertex_attribs(st, vbuffer, num_vbuffers, velements, num_inputs);
645 }
646
647 void st_update_array(struct st_context *st)
648 {
649 struct gl_context *ctx = st->ctx;
650 const struct gl_vertex_array **arrays = ctx->Array._DrawArrays;
651 const struct st_vertex_program *vp;
652 unsigned num_inputs;
653
654 st->vertex_array_out_of_memory = FALSE;
655
656 /* No drawing has been done yet, so do nothing. */
657 if (!arrays)
658 return;
659
660 /* vertex program validation must be done before this */
661 vp = st->vp;
662 num_inputs = st->vp_variant->num_inputs;
663
664 if (is_interleaved_arrays(vp, arrays, num_inputs))
665 setup_interleaved_attribs(st, vp, arrays, num_inputs);
666 else
667 setup_non_interleaved_attribs(st, vp, arrays, num_inputs);
668 }