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