1/*
2 * Copyright © 2020 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24/* This implements vertex array state tracking for glthread. It's separate
25 * from the rest of Mesa. Only minimum functionality is implemented here
26 * to serve glthread.
27 */
28
29#include "main/glthread.h"
30#include "main/glformats.h"
31#include "main/mtypes.h"
32#include "main/hash.h"
33#include "main/dispatch.h"
34#include "main/varray.h"
35
36
37void
38_mesa_glthread_reset_vao(struct glthread_vao *vao)
39{
40   static unsigned default_elem_size[VERT_ATTRIB_MAX] = {
41      [VERT_ATTRIB_NORMAL] = 12,
42      [VERT_ATTRIB_COLOR1] = 12,
43      [VERT_ATTRIB_FOG] = 4,
44      [VERT_ATTRIB_COLOR_INDEX] = 4,
45      [VERT_ATTRIB_EDGEFLAG] = 1,
46      [VERT_ATTRIB_POINT_SIZE] = 4,
47   };
48
49   vao->CurrentElementBufferName = 0;
50   vao->UserEnabled = 0;
51   vao->Enabled = 0;
52   vao->BufferEnabled = 0;
53   vao->UserPointerMask = 0;
54   vao->NonZeroDivisorMask = 0;
55
56   for (unsigned i = 0; i < ARRAY_SIZE(vao->Attrib); i++) {
57      unsigned elem_size = default_elem_size[i];
58      if (!elem_size)
59         elem_size = 16;
60
61      vao->Attrib[i].ElementSize = elem_size;
62      vao->Attrib[i].RelativeOffset = 0;
63      vao->Attrib[i].BufferIndex = i;
64      vao->Attrib[i].Stride = elem_size;
65      vao->Attrib[i].Divisor = 0;
66      vao->Attrib[i].EnabledAttribCount = 0;
67      vao->Attrib[i].Pointer = NULL;
68   }
69}
70
71static struct glthread_vao *
72lookup_vao(struct gl_context *ctx, GLuint id)
73{
74   struct glthread_state *glthread = &ctx->GLThread;
75   struct glthread_vao *vao;
76
77   assert(id != 0);
78
79   if (glthread->LastLookedUpVAO &&
80       glthread->LastLookedUpVAO->Name == id) {
81      vao = glthread->LastLookedUpVAO;
82   } else {
83      vao = _mesa_HashLookupLocked(glthread->VAOs, id);
84      if (!vao)
85         return NULL;
86
87      glthread->LastLookedUpVAO = vao;
88   }
89
90   return vao;
91}
92
93void
94_mesa_glthread_BindVertexArray(struct gl_context *ctx, GLuint id)
95{
96   struct glthread_state *glthread = &ctx->GLThread;
97
98   if (id == 0) {
99      glthread->CurrentVAO = &glthread->DefaultVAO;
100   } else {
101      struct glthread_vao *vao = lookup_vao(ctx, id);
102
103      if (vao)
104         glthread->CurrentVAO = vao;
105   }
106}
107
108void
109_mesa_glthread_DeleteVertexArrays(struct gl_context *ctx,
110                                  GLsizei n, const GLuint *ids)
111{
112   struct glthread_state *glthread = &ctx->GLThread;
113
114   if (!ids)
115      return;
116
117   for (int i = 0; i < n; i++) {
118      /* IDs equal to 0 should be silently ignored. */
119      if (!ids[i])
120         continue;
121
122      struct glthread_vao *vao = lookup_vao(ctx, ids[i]);
123      if (!vao)
124         continue;
125
126      /* If the array object is currently bound, the spec says "the binding
127       * for that object reverts to zero and the default vertex array
128       * becomes current."
129       */
130      if (glthread->CurrentVAO == vao)
131         glthread->CurrentVAO = &glthread->DefaultVAO;
132
133      if (glthread->LastLookedUpVAO == vao)
134         glthread->LastLookedUpVAO = NULL;
135
136      /* The ID is immediately freed for re-use */
137      _mesa_HashRemoveLocked(glthread->VAOs, vao->Name);
138      free(vao);
139   }
140}
141
142void
143_mesa_glthread_GenVertexArrays(struct gl_context *ctx,
144                               GLsizei n, GLuint *arrays)
145{
146   struct glthread_state *glthread = &ctx->GLThread;
147
148   if (!arrays)
149      return;
150
151   /* The IDs have been generated at this point. Create VAOs for glthread. */
152   for (int i = 0; i < n; i++) {
153      GLuint id = arrays[i];
154      struct glthread_vao *vao;
155
156      vao = calloc(1, sizeof(*vao));
157      if (!vao)
158         continue; /* Is that all we can do? */
159
160      vao->Name = id;
161      _mesa_glthread_reset_vao(vao);
162      _mesa_HashInsertLocked(glthread->VAOs, id, vao, true);
163   }
164}
165
166/* If vaobj is NULL, use the currently-bound VAO. */
167static inline struct glthread_vao *
168get_vao(struct gl_context *ctx, const GLuint *vaobj)
169{
170   if (vaobj)
171      return lookup_vao(ctx, *vaobj);
172
173   return ctx->GLThread.CurrentVAO;
174}
175
176static void
177update_primitive_restart(struct gl_context *ctx)
178{
179   struct glthread_state *glthread = &ctx->GLThread;
180
181   glthread->_PrimitiveRestart = glthread->PrimitiveRestart ||
182                                 glthread->PrimitiveRestartFixedIndex;
183   glthread->_RestartIndex[0] =
184      _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex,
185                                   glthread->RestartIndex, 1);
186   glthread->_RestartIndex[1] =
187      _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex,
188                                   glthread->RestartIndex, 2);
189   glthread->_RestartIndex[3] =
190      _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex,
191                                   glthread->RestartIndex, 4);
192}
193
194void
195_mesa_glthread_set_prim_restart(struct gl_context *ctx, GLenum cap, bool value)
196{
197   switch (cap) {
198   case GL_PRIMITIVE_RESTART:
199      ctx->GLThread.PrimitiveRestart = value;
200      break;
201   case GL_PRIMITIVE_RESTART_FIXED_INDEX:
202      ctx->GLThread.PrimitiveRestartFixedIndex = value;
203      break;
204   }
205
206   update_primitive_restart(ctx);
207}
208
209void
210_mesa_glthread_PrimitiveRestartIndex(struct gl_context *ctx, GLuint index)
211{
212   ctx->GLThread.RestartIndex = index;
213   update_primitive_restart(ctx);
214}
215
216static inline void
217enable_buffer(struct glthread_vao *vao, unsigned binding_index)
218{
219   int attrib_count = ++vao->Attrib[binding_index].EnabledAttribCount;
220
221   if (attrib_count == 1)
222      vao->BufferEnabled |= 1 << binding_index;
223   else if (attrib_count == 2)
224      vao->BufferInterleaved |= 1 << binding_index;
225}
226
227static inline void
228disable_buffer(struct glthread_vao *vao, unsigned binding_index)
229{
230   int attrib_count = --vao->Attrib[binding_index].EnabledAttribCount;
231
232   if (attrib_count == 0)
233      vao->BufferEnabled &= ~(1 << binding_index);
234   else if (attrib_count == 1)
235      vao->BufferInterleaved &= ~(1 << binding_index);
236   else
237      assert(attrib_count >= 0);
238}
239
240void
241_mesa_glthread_ClientState(struct gl_context *ctx, GLuint *vaobj,
242                           gl_vert_attrib attrib, bool enable)
243{
244   /* The primitive restart client state uses a special value. */
245   if (attrib == VERT_ATTRIB_PRIMITIVE_RESTART_NV) {
246      ctx->GLThread.PrimitiveRestart = enable;
247      update_primitive_restart(ctx);
248      return;
249   }
250
251   if (attrib >= VERT_ATTRIB_MAX)
252      return;
253
254   struct glthread_vao *vao = get_vao(ctx, vaobj);
255   if (!vao)
256      return;
257
258   const unsigned attrib_bit = 1u << attrib;
259
260   if (enable && !(vao->UserEnabled & attrib_bit)) {
261      vao->UserEnabled |= attrib_bit;
262
263      /* The generic0 attribute supersedes the position attribute. We need to
264       * update BufferBindingEnabled accordingly.
265       */
266      if (attrib == VERT_ATTRIB_POS) {
267         if (!(vao->UserEnabled & VERT_BIT_GENERIC0))
268            enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
269      } else {
270         enable_buffer(vao, vao->Attrib[attrib].BufferIndex);
271
272         if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS)
273            disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
274      }
275   } else if (!enable && (vao->UserEnabled & attrib_bit)) {
276      vao->UserEnabled &= ~attrib_bit;
277
278      /* The generic0 attribute supersedes the position attribute. We need to
279       * update BufferBindingEnabled accordingly.
280       */
281      if (attrib == VERT_ATTRIB_POS) {
282         if (!(vao->UserEnabled & VERT_BIT_GENERIC0))
283            disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
284      } else {
285         disable_buffer(vao, vao->Attrib[attrib].BufferIndex);
286
287         if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS)
288            enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex);
289      }
290   }
291
292   /* The generic0 attribute supersedes the position attribute. */
293   vao->Enabled = vao->UserEnabled;
294   if (vao->Enabled & VERT_BIT_GENERIC0)
295      vao->Enabled &= ~VERT_BIT_POS;
296}
297
298static void
299set_attrib_binding(struct glthread_state *glthread, struct glthread_vao *vao,
300                   gl_vert_attrib attrib, unsigned new_binding_index)
301{
302   unsigned old_binding_index = vao->Attrib[attrib].BufferIndex;
303
304   if (old_binding_index != new_binding_index) {
305      vao->Attrib[attrib].BufferIndex = new_binding_index;
306
307      if (vao->Enabled & (1u << attrib)) {
308         /* Update BufferBindingEnabled. */
309         enable_buffer(vao, new_binding_index);
310         disable_buffer(vao, old_binding_index);
311      }
312   }
313}
314
315void _mesa_glthread_AttribDivisor(struct gl_context *ctx, const GLuint *vaobj,
316                                  gl_vert_attrib attrib, GLuint divisor)
317{
318   if (attrib >= VERT_ATTRIB_MAX)
319      return;
320
321   struct glthread_vao *vao = get_vao(ctx, vaobj);
322   if (!vao)
323      return;
324
325   vao->Attrib[attrib].Divisor = divisor;
326
327   set_attrib_binding(&ctx->GLThread, vao, attrib, attrib);
328
329   if (divisor)
330      vao->NonZeroDivisorMask |= 1u << attrib;
331   else
332      vao->NonZeroDivisorMask &= ~(1u << attrib);
333}
334
335static unsigned
336element_size(GLint size, GLenum type)
337{
338   if (size == GL_BGRA)
339      size = 4;
340
341   return _mesa_bytes_per_vertex_attrib(size, type);
342}
343
344static void
345attrib_pointer(struct glthread_state *glthread, struct glthread_vao *vao,
346               GLuint buffer, gl_vert_attrib attrib,
347               GLint size, GLenum type, GLsizei stride,
348               const void *pointer)
349{
350   if (attrib >= VERT_ATTRIB_MAX)
351      return;
352
353   unsigned elem_size = element_size(size, type);
354
355   vao->Attrib[attrib].ElementSize = elem_size;
356   vao->Attrib[attrib].Stride = stride ? stride : elem_size;
357   vao->Attrib[attrib].Pointer = pointer;
358   vao->Attrib[attrib].RelativeOffset = 0;
359
360   set_attrib_binding(glthread, vao, attrib, attrib);
361
362   if (buffer != 0)
363      vao->UserPointerMask &= ~(1u << attrib);
364   else
365      vao->UserPointerMask |= 1u << attrib;
366}
367
368void
369_mesa_glthread_AttribPointer(struct gl_context *ctx, gl_vert_attrib attrib,
370                             GLint size, GLenum type, GLsizei stride,
371                             const void *pointer)
372{
373   struct glthread_state *glthread = &ctx->GLThread;
374
375   attrib_pointer(glthread, glthread->CurrentVAO,
376                  glthread->CurrentArrayBufferName,
377                  attrib, size, type, stride, pointer);
378}
379
380void
381_mesa_glthread_DSAAttribPointer(struct gl_context *ctx, GLuint vaobj,
382                                GLuint buffer, gl_vert_attrib attrib,
383                                GLint size, GLenum type, GLsizei stride,
384                                GLintptr offset)
385{
386   struct glthread_state *glthread = &ctx->GLThread;
387   struct glthread_vao *vao;
388
389   vao = lookup_vao(ctx, vaobj);
390   if (!vao)
391      return;
392
393   attrib_pointer(glthread, vao, buffer, attrib, size, type, stride,
394                  (const void*)offset);
395}
396
397static void
398attrib_format(struct glthread_state *glthread, struct glthread_vao *vao,
399              GLuint attribindex, GLint size, GLenum type,
400              GLuint relativeoffset)
401{
402   if (attribindex >= VERT_ATTRIB_GENERIC_MAX)
403      return;
404
405   unsigned elem_size = element_size(size, type);
406
407   unsigned i = VERT_ATTRIB_GENERIC(attribindex);
408   vao->Attrib[i].ElementSize = elem_size;
409   vao->Attrib[i].RelativeOffset = relativeoffset;
410}
411
412void
413_mesa_glthread_AttribFormat(struct gl_context *ctx, GLuint attribindex,
414                            GLint size, GLenum type, GLuint relativeoffset)
415{
416   struct glthread_state *glthread = &ctx->GLThread;
417
418   attrib_format(glthread, glthread->CurrentVAO, attribindex, size, type,
419                 relativeoffset);
420}
421
422void
423_mesa_glthread_DSAAttribFormat(struct gl_context *ctx, GLuint vaobj,
424                               GLuint attribindex, GLint size, GLenum type,
425                               GLuint relativeoffset)
426{
427   struct glthread_state *glthread = &ctx->GLThread;
428   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
429
430   if (vao)
431      attrib_format(glthread, vao, attribindex, size, type, relativeoffset);
432}
433
434static void
435bind_vertex_buffer(struct glthread_state *glthread, struct glthread_vao *vao,
436                   GLuint bindingindex, GLuint buffer, GLintptr offset,
437                   GLsizei stride)
438{
439   if (bindingindex >= VERT_ATTRIB_GENERIC_MAX)
440      return;
441
442   unsigned i = VERT_ATTRIB_GENERIC(bindingindex);
443   vao->Attrib[i].Pointer = (const void*)offset;
444   vao->Attrib[i].Stride = stride;
445
446   if (buffer != 0)
447      vao->UserPointerMask &= ~(1u << i);
448   else
449      vao->UserPointerMask |= 1u << i;
450}
451
452void
453_mesa_glthread_VertexBuffer(struct gl_context *ctx, GLuint bindingindex,
454                            GLuint buffer, GLintptr offset, GLsizei stride)
455{
456   struct glthread_state *glthread = &ctx->GLThread;
457
458   bind_vertex_buffer(glthread, glthread->CurrentVAO, bindingindex, buffer,
459                      offset, stride);
460}
461
462void
463_mesa_glthread_DSAVertexBuffer(struct gl_context *ctx, GLuint vaobj,
464                               GLuint bindingindex, GLuint buffer,
465                               GLintptr offset, GLsizei stride)
466{
467   struct glthread_state *glthread = &ctx->GLThread;
468   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
469
470   if (vao)
471      bind_vertex_buffer(glthread, vao, bindingindex, buffer, offset, stride);
472}
473
474void
475_mesa_glthread_DSAVertexBuffers(struct gl_context *ctx, GLuint vaobj,
476                                GLuint first, GLsizei count,
477                                const GLuint *buffers,
478                                const GLintptr *offsets,
479                                const GLsizei *strides)
480{
481   struct glthread_state *glthread = &ctx->GLThread;
482   struct glthread_vao *vao;
483
484   vao = lookup_vao(ctx, vaobj);
485   if (!vao)
486      return;
487
488   for (unsigned i = 0; i < count; i++) {
489      bind_vertex_buffer(glthread, vao, first + i, buffers[i], offsets[i],
490                         strides[i]);
491   }
492}
493
494static void
495binding_divisor(struct glthread_state *glthread, struct glthread_vao *vao,
496                GLuint bindingindex, GLuint divisor)
497{
498   if (bindingindex >= VERT_ATTRIB_GENERIC_MAX)
499      return;
500
501   unsigned i = VERT_ATTRIB_GENERIC(bindingindex);
502   vao->Attrib[i].Divisor = divisor;
503
504   if (divisor)
505      vao->NonZeroDivisorMask |= 1u << i;
506   else
507      vao->NonZeroDivisorMask &= ~(1u << i);
508}
509
510void
511_mesa_glthread_BindingDivisor(struct gl_context *ctx, GLuint bindingindex,
512                              GLuint divisor)
513{
514   struct glthread_state *glthread = &ctx->GLThread;
515
516   binding_divisor(glthread, glthread->CurrentVAO, bindingindex, divisor);
517}
518
519void
520_mesa_glthread_DSABindingDivisor(struct gl_context *ctx, GLuint vaobj,
521                                 GLuint bindingindex, GLuint divisor)
522{
523   struct glthread_state *glthread = &ctx->GLThread;
524   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
525
526   if (vao)
527      binding_divisor(glthread, vao, bindingindex, divisor);
528}
529
530void
531_mesa_glthread_AttribBinding(struct gl_context *ctx, GLuint attribindex,
532                             GLuint bindingindex)
533{
534   struct glthread_state *glthread = &ctx->GLThread;
535
536   if (attribindex >= VERT_ATTRIB_GENERIC_MAX ||
537       bindingindex >= VERT_ATTRIB_GENERIC_MAX)
538      return;
539
540   set_attrib_binding(glthread, glthread->CurrentVAO,
541                      VERT_ATTRIB_GENERIC(attribindex),
542                      VERT_ATTRIB_GENERIC(bindingindex));
543}
544
545void
546_mesa_glthread_DSAAttribBinding(struct gl_context *ctx, GLuint vaobj,
547                                GLuint attribindex, GLuint bindingindex)
548{
549   struct glthread_state *glthread = &ctx->GLThread;
550
551   if (attribindex >= VERT_ATTRIB_GENERIC_MAX ||
552       bindingindex >= VERT_ATTRIB_GENERIC_MAX)
553      return;
554
555   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
556   if (vao) {
557      set_attrib_binding(glthread, vao,
558                         VERT_ATTRIB_GENERIC(attribindex),
559                         VERT_ATTRIB_GENERIC(bindingindex));
560   }
561}
562
563void
564_mesa_glthread_DSAElementBuffer(struct gl_context *ctx, GLuint vaobj,
565                                GLuint buffer)
566{
567   struct glthread_vao *vao = lookup_vao(ctx, vaobj);
568
569   if (vao)
570      vao->CurrentElementBufferName = buffer;
571}
572
573void
574_mesa_glthread_PushClientAttrib(struct gl_context *ctx, GLbitfield mask,
575                                bool set_default)
576{
577   struct glthread_state *glthread = &ctx->GLThread;
578
579   if (glthread->ClientAttribStackTop >= MAX_CLIENT_ATTRIB_STACK_DEPTH)
580      return;
581
582   struct glthread_client_attrib *top =
583      &glthread->ClientAttribStack[glthread->ClientAttribStackTop];
584
585   if (mask & GL_CLIENT_VERTEX_ARRAY_BIT) {
586      top->VAO = *glthread->CurrentVAO;
587      top->CurrentArrayBufferName = glthread->CurrentArrayBufferName;
588      top->ClientActiveTexture = glthread->ClientActiveTexture;
589      top->RestartIndex = glthread->RestartIndex;
590      top->PrimitiveRestart = glthread->PrimitiveRestart;
591      top->PrimitiveRestartFixedIndex = glthread->PrimitiveRestartFixedIndex;
592      top->Valid = true;
593   } else {
594      top->Valid = false;
595   }
596
597   glthread->ClientAttribStackTop++;
598
599   if (set_default)
600      _mesa_glthread_ClientAttribDefault(ctx, mask);
601}
602
603void
604_mesa_glthread_PopClientAttrib(struct gl_context *ctx)
605{
606   struct glthread_state *glthread = &ctx->GLThread;
607
608   if (glthread->ClientAttribStackTop == 0)
609      return;
610
611   glthread->ClientAttribStackTop--;
612
613   struct glthread_client_attrib *top =
614      &glthread->ClientAttribStack[glthread->ClientAttribStackTop];
615
616   if (!top->Valid)
617      return;
618
619   /* Popping a delete VAO is an error. */
620   struct glthread_vao *vao = NULL;
621   if (top->VAO.Name) {
622      vao = lookup_vao(ctx, top->VAO.Name);
623      if (!vao)
624         return;
625   }
626
627   /* Restore states. */
628   glthread->CurrentArrayBufferName = top->CurrentArrayBufferName;
629   glthread->ClientActiveTexture = top->ClientActiveTexture;
630   glthread->RestartIndex = top->RestartIndex;
631   glthread->PrimitiveRestart = top->PrimitiveRestart;
632   glthread->PrimitiveRestartFixedIndex = top->PrimitiveRestartFixedIndex;
633
634   if (!vao)
635      vao = &glthread->DefaultVAO;
636
637   assert(top->VAO.Name == vao->Name);
638   *vao = top->VAO; /* Copy all fields. */
639   glthread->CurrentVAO = vao;
640}
641
642void
643_mesa_glthread_ClientAttribDefault(struct gl_context *ctx, GLbitfield mask)
644{
645   struct glthread_state *glthread = &ctx->GLThread;
646
647   if (!(mask & GL_CLIENT_VERTEX_ARRAY_BIT))
648      return;
649
650   glthread->CurrentArrayBufferName = 0;
651   glthread->ClientActiveTexture = 0;
652   glthread->RestartIndex = 0;
653   glthread->PrimitiveRestart = false;
654   glthread->PrimitiveRestartFixedIndex = false;
655   glthread->CurrentVAO = &glthread->DefaultVAO;
656   _mesa_glthread_reset_vao(glthread->CurrentVAO);
657}
658
659void
660_mesa_glthread_InterleavedArrays(struct gl_context *ctx, GLenum format,
661                                 GLsizei stride, const GLvoid *pointer)
662{
663   struct gl_interleaved_layout layout;
664   unsigned tex = VERT_ATTRIB_TEX(ctx->GLThread.ClientActiveTexture);
665
666   if (stride < 0 || !_mesa_get_interleaved_layout(format, &layout))
667      return;
668
669   if (!stride)
670      stride = layout.defstride;
671
672   _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_EDGEFLAG, false);
673   _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR_INDEX, false);
674   /* XXX also disable secondary color and generic arrays? */
675
676   /* Texcoords */
677   if (layout.tflag) {
678      _mesa_glthread_ClientState(ctx, NULL, tex, true);
679      _mesa_glthread_AttribPointer(ctx, tex, layout.tcomps, GL_FLOAT, stride,
680                                   (GLubyte *) pointer + layout.toffset);
681   } else {
682      _mesa_glthread_ClientState(ctx, NULL, tex, false);
683   }
684
685   /* Color */
686   if (layout.cflag) {
687      _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR0, true);
688      _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_COLOR0, layout.ccomps,
689                                   layout.ctype, stride,
690                                   (GLubyte *) pointer + layout.coffset);
691   } else {
692      _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR0, false);
693   }
694
695   /* Normals */
696   if (layout.nflag) {
697      _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_NORMAL, true);
698      _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_NORMAL, 3, GL_FLOAT,
699                                   stride, (GLubyte *) pointer + layout.noffset);
700   } else {
701      _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_NORMAL, false);
702   }
703
704   /* Vertices */
705   _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_POS, true);
706   _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_POS, layout.vcomps, GL_FLOAT,
707                                stride, (GLubyte *) pointer + layout.voffset);
708}
709