| /* |
| * Copyright © 2020 Advanced Micro Devices, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| /* This implements vertex array state tracking for glthread. It's separate |
| * from the rest of Mesa. Only minimum functionality is implemented here |
| * to serve glthread. |
| */ |
| |
| #include "main/glthread.h" |
| #include "main/glformats.h" |
| #include "main/mtypes.h" |
| #include "main/hash.h" |
| #include "main/dispatch.h" |
| #include "main/varray.h" |
| |
| |
| void |
| _mesa_glthread_reset_vao(struct glthread_vao *vao) |
| { |
| static unsigned default_elem_size[VERT_ATTRIB_MAX] = { |
| [VERT_ATTRIB_NORMAL] = 12, |
| [VERT_ATTRIB_COLOR1] = 12, |
| [VERT_ATTRIB_FOG] = 4, |
| [VERT_ATTRIB_COLOR_INDEX] = 4, |
| [VERT_ATTRIB_EDGEFLAG] = 1, |
| [VERT_ATTRIB_POINT_SIZE] = 4, |
| }; |
| |
| vao->CurrentElementBufferName = 0; |
| vao->UserEnabled = 0; |
| vao->Enabled = 0; |
| vao->BufferEnabled = 0; |
| vao->UserPointerMask = 0; |
| vao->NonZeroDivisorMask = 0; |
| |
| for (unsigned i = 0; i < ARRAY_SIZE(vao->Attrib); i++) { |
| unsigned elem_size = default_elem_size[i]; |
| if (!elem_size) |
| elem_size = 16; |
| |
| vao->Attrib[i].ElementSize = elem_size; |
| vao->Attrib[i].RelativeOffset = 0; |
| vao->Attrib[i].BufferIndex = i; |
| vao->Attrib[i].Stride = elem_size; |
| vao->Attrib[i].Divisor = 0; |
| vao->Attrib[i].EnabledAttribCount = 0; |
| vao->Attrib[i].Pointer = NULL; |
| } |
| } |
| |
| static struct glthread_vao * |
| lookup_vao(struct gl_context *ctx, GLuint id) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao; |
| |
| assert(id != 0); |
| |
| if (glthread->LastLookedUpVAO && |
| glthread->LastLookedUpVAO->Name == id) { |
| vao = glthread->LastLookedUpVAO; |
| } else { |
| vao = _mesa_HashLookupLocked(glthread->VAOs, id); |
| if (!vao) |
| return NULL; |
| |
| glthread->LastLookedUpVAO = vao; |
| } |
| |
| return vao; |
| } |
| |
| void |
| _mesa_glthread_BindVertexArray(struct gl_context *ctx, GLuint id) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (id == 0) { |
| glthread->CurrentVAO = &glthread->DefaultVAO; |
| } else { |
| struct glthread_vao *vao = lookup_vao(ctx, id); |
| |
| if (vao) |
| glthread->CurrentVAO = vao; |
| } |
| } |
| |
| void |
| _mesa_glthread_DeleteVertexArrays(struct gl_context *ctx, |
| GLsizei n, const GLuint *ids) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (!ids) |
| return; |
| |
| for (int i = 0; i < n; i++) { |
| /* IDs equal to 0 should be silently ignored. */ |
| if (!ids[i]) |
| continue; |
| |
| struct glthread_vao *vao = lookup_vao(ctx, ids[i]); |
| if (!vao) |
| continue; |
| |
| /* If the array object is currently bound, the spec says "the binding |
| * for that object reverts to zero and the default vertex array |
| * becomes current." |
| */ |
| if (glthread->CurrentVAO == vao) |
| glthread->CurrentVAO = &glthread->DefaultVAO; |
| |
| if (glthread->LastLookedUpVAO == vao) |
| glthread->LastLookedUpVAO = NULL; |
| |
| /* The ID is immediately freed for re-use */ |
| _mesa_HashRemoveLocked(glthread->VAOs, vao->Name); |
| free(vao); |
| } |
| } |
| |
| void |
| _mesa_glthread_GenVertexArrays(struct gl_context *ctx, |
| GLsizei n, GLuint *arrays) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (!arrays) |
| return; |
| |
| /* The IDs have been generated at this point. Create VAOs for glthread. */ |
| for (int i = 0; i < n; i++) { |
| GLuint id = arrays[i]; |
| struct glthread_vao *vao; |
| |
| vao = calloc(1, sizeof(*vao)); |
| if (!vao) |
| continue; /* Is that all we can do? */ |
| |
| vao->Name = id; |
| _mesa_glthread_reset_vao(vao); |
| _mesa_HashInsertLocked(glthread->VAOs, id, vao, true); |
| } |
| } |
| |
| /* If vaobj is NULL, use the currently-bound VAO. */ |
| static inline struct glthread_vao * |
| get_vao(struct gl_context *ctx, const GLuint *vaobj) |
| { |
| if (vaobj) |
| return lookup_vao(ctx, *vaobj); |
| |
| return ctx->GLThread.CurrentVAO; |
| } |
| |
| static void |
| update_primitive_restart(struct gl_context *ctx) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| glthread->_PrimitiveRestart = glthread->PrimitiveRestart || |
| glthread->PrimitiveRestartFixedIndex; |
| glthread->_RestartIndex[0] = |
| _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex, |
| glthread->RestartIndex, 1); |
| glthread->_RestartIndex[1] = |
| _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex, |
| glthread->RestartIndex, 2); |
| glthread->_RestartIndex[3] = |
| _mesa_get_prim_restart_index(glthread->PrimitiveRestartFixedIndex, |
| glthread->RestartIndex, 4); |
| } |
| |
| void |
| _mesa_glthread_set_prim_restart(struct gl_context *ctx, GLenum cap, bool value) |
| { |
| switch (cap) { |
| case GL_PRIMITIVE_RESTART: |
| ctx->GLThread.PrimitiveRestart = value; |
| break; |
| case GL_PRIMITIVE_RESTART_FIXED_INDEX: |
| ctx->GLThread.PrimitiveRestartFixedIndex = value; |
| break; |
| } |
| |
| update_primitive_restart(ctx); |
| } |
| |
| void |
| _mesa_glthread_PrimitiveRestartIndex(struct gl_context *ctx, GLuint index) |
| { |
| ctx->GLThread.RestartIndex = index; |
| update_primitive_restart(ctx); |
| } |
| |
| static inline void |
| enable_buffer(struct glthread_vao *vao, unsigned binding_index) |
| { |
| int attrib_count = ++vao->Attrib[binding_index].EnabledAttribCount; |
| |
| if (attrib_count == 1) |
| vao->BufferEnabled |= 1 << binding_index; |
| else if (attrib_count == 2) |
| vao->BufferInterleaved |= 1 << binding_index; |
| } |
| |
| static inline void |
| disable_buffer(struct glthread_vao *vao, unsigned binding_index) |
| { |
| int attrib_count = --vao->Attrib[binding_index].EnabledAttribCount; |
| |
| if (attrib_count == 0) |
| vao->BufferEnabled &= ~(1 << binding_index); |
| else if (attrib_count == 1) |
| vao->BufferInterleaved &= ~(1 << binding_index); |
| else |
| assert(attrib_count >= 0); |
| } |
| |
| void |
| _mesa_glthread_ClientState(struct gl_context *ctx, GLuint *vaobj, |
| gl_vert_attrib attrib, bool enable) |
| { |
| /* The primitive restart client state uses a special value. */ |
| if (attrib == VERT_ATTRIB_PRIMITIVE_RESTART_NV) { |
| ctx->GLThread.PrimitiveRestart = enable; |
| update_primitive_restart(ctx); |
| return; |
| } |
| |
| if (attrib >= VERT_ATTRIB_MAX) |
| return; |
| |
| struct glthread_vao *vao = get_vao(ctx, vaobj); |
| if (!vao) |
| return; |
| |
| const unsigned attrib_bit = 1u << attrib; |
| |
| if (enable && !(vao->UserEnabled & attrib_bit)) { |
| vao->UserEnabled |= attrib_bit; |
| |
| /* The generic0 attribute supersedes the position attribute. We need to |
| * update BufferBindingEnabled accordingly. |
| */ |
| if (attrib == VERT_ATTRIB_POS) { |
| if (!(vao->UserEnabled & VERT_BIT_GENERIC0)) |
| enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex); |
| } else { |
| enable_buffer(vao, vao->Attrib[attrib].BufferIndex); |
| |
| if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS) |
| disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex); |
| } |
| } else if (!enable && (vao->UserEnabled & attrib_bit)) { |
| vao->UserEnabled &= ~attrib_bit; |
| |
| /* The generic0 attribute supersedes the position attribute. We need to |
| * update BufferBindingEnabled accordingly. |
| */ |
| if (attrib == VERT_ATTRIB_POS) { |
| if (!(vao->UserEnabled & VERT_BIT_GENERIC0)) |
| disable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex); |
| } else { |
| disable_buffer(vao, vao->Attrib[attrib].BufferIndex); |
| |
| if (attrib == VERT_ATTRIB_GENERIC0 && vao->UserEnabled & VERT_BIT_POS) |
| enable_buffer(vao, vao->Attrib[VERT_ATTRIB_POS].BufferIndex); |
| } |
| } |
| |
| /* The generic0 attribute supersedes the position attribute. */ |
| vao->Enabled = vao->UserEnabled; |
| if (vao->Enabled & VERT_BIT_GENERIC0) |
| vao->Enabled &= ~VERT_BIT_POS; |
| } |
| |
| static void |
| set_attrib_binding(struct glthread_state *glthread, struct glthread_vao *vao, |
| gl_vert_attrib attrib, unsigned new_binding_index) |
| { |
| unsigned old_binding_index = vao->Attrib[attrib].BufferIndex; |
| |
| if (old_binding_index != new_binding_index) { |
| vao->Attrib[attrib].BufferIndex = new_binding_index; |
| |
| if (vao->Enabled & (1u << attrib)) { |
| /* Update BufferBindingEnabled. */ |
| enable_buffer(vao, new_binding_index); |
| disable_buffer(vao, old_binding_index); |
| } |
| } |
| } |
| |
| void _mesa_glthread_AttribDivisor(struct gl_context *ctx, const GLuint *vaobj, |
| gl_vert_attrib attrib, GLuint divisor) |
| { |
| if (attrib >= VERT_ATTRIB_MAX) |
| return; |
| |
| struct glthread_vao *vao = get_vao(ctx, vaobj); |
| if (!vao) |
| return; |
| |
| vao->Attrib[attrib].Divisor = divisor; |
| |
| set_attrib_binding(&ctx->GLThread, vao, attrib, attrib); |
| |
| if (divisor) |
| vao->NonZeroDivisorMask |= 1u << attrib; |
| else |
| vao->NonZeroDivisorMask &= ~(1u << attrib); |
| } |
| |
| static void |
| attrib_pointer(struct glthread_state *glthread, struct glthread_vao *vao, |
| GLuint buffer, gl_vert_attrib attrib, |
| GLint size, GLenum type, GLsizei stride, |
| const void *pointer) |
| { |
| if (attrib >= VERT_ATTRIB_MAX) |
| return; |
| |
| unsigned elem_size = _mesa_bytes_per_vertex_attrib(size, type); |
| |
| vao->Attrib[attrib].ElementSize = elem_size; |
| vao->Attrib[attrib].Stride = stride ? stride : elem_size; |
| vao->Attrib[attrib].Pointer = pointer; |
| vao->Attrib[attrib].RelativeOffset = 0; |
| |
| set_attrib_binding(glthread, vao, attrib, attrib); |
| |
| if (buffer != 0) |
| vao->UserPointerMask &= ~(1u << attrib); |
| else |
| vao->UserPointerMask |= 1u << attrib; |
| } |
| |
| void |
| _mesa_glthread_AttribPointer(struct gl_context *ctx, gl_vert_attrib attrib, |
| GLint size, GLenum type, GLsizei stride, |
| const void *pointer) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| attrib_pointer(glthread, glthread->CurrentVAO, |
| glthread->CurrentArrayBufferName, |
| attrib, size, type, stride, pointer); |
| } |
| |
| void |
| _mesa_glthread_DSAAttribPointer(struct gl_context *ctx, GLuint vaobj, |
| GLuint buffer, gl_vert_attrib attrib, |
| GLint size, GLenum type, GLsizei stride, |
| GLintptr offset) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao; |
| |
| vao = lookup_vao(ctx, vaobj); |
| if (!vao) |
| return; |
| |
| attrib_pointer(glthread, vao, buffer, attrib, size, type, stride, |
| (const void*)offset); |
| } |
| |
| static void |
| attrib_format(struct glthread_state *glthread, struct glthread_vao *vao, |
| GLuint attribindex, GLint size, GLenum type, |
| GLuint relativeoffset) |
| { |
| if (attribindex >= VERT_ATTRIB_GENERIC_MAX) |
| return; |
| |
| unsigned elem_size = _mesa_bytes_per_vertex_attrib(size, type); |
| |
| unsigned i = VERT_ATTRIB_GENERIC(attribindex); |
| vao->Attrib[i].ElementSize = elem_size; |
| vao->Attrib[i].RelativeOffset = relativeoffset; |
| } |
| |
| void |
| _mesa_glthread_AttribFormat(struct gl_context *ctx, GLuint attribindex, |
| GLint size, GLenum type, GLuint relativeoffset) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| attrib_format(glthread, glthread->CurrentVAO, attribindex, size, type, |
| relativeoffset); |
| } |
| |
| void |
| _mesa_glthread_DSAAttribFormat(struct gl_context *ctx, GLuint vaobj, |
| GLuint attribindex, GLint size, GLenum type, |
| GLuint relativeoffset) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao = lookup_vao(ctx, vaobj); |
| |
| if (vao) |
| attrib_format(glthread, vao, attribindex, size, type, relativeoffset); |
| } |
| |
| static void |
| bind_vertex_buffer(struct glthread_state *glthread, struct glthread_vao *vao, |
| GLuint bindingindex, GLuint buffer, GLintptr offset, |
| GLsizei stride) |
| { |
| if (bindingindex >= VERT_ATTRIB_GENERIC_MAX) |
| return; |
| |
| unsigned i = VERT_ATTRIB_GENERIC(bindingindex); |
| vao->Attrib[i].Pointer = (const void*)offset; |
| vao->Attrib[i].Stride = stride; |
| |
| if (buffer != 0) |
| vao->UserPointerMask &= ~(1u << i); |
| else |
| vao->UserPointerMask |= 1u << i; |
| } |
| |
| void |
| _mesa_glthread_VertexBuffer(struct gl_context *ctx, GLuint bindingindex, |
| GLuint buffer, GLintptr offset, GLsizei stride) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| bind_vertex_buffer(glthread, glthread->CurrentVAO, bindingindex, buffer, |
| offset, stride); |
| } |
| |
| void |
| _mesa_glthread_DSAVertexBuffer(struct gl_context *ctx, GLuint vaobj, |
| GLuint bindingindex, GLuint buffer, |
| GLintptr offset, GLsizei stride) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao = lookup_vao(ctx, vaobj); |
| |
| if (vao) |
| bind_vertex_buffer(glthread, vao, bindingindex, buffer, offset, stride); |
| } |
| |
| void |
| _mesa_glthread_DSAVertexBuffers(struct gl_context *ctx, GLuint vaobj, |
| GLuint first, GLsizei count, |
| const GLuint *buffers, |
| const GLintptr *offsets, |
| const GLsizei *strides) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao; |
| |
| vao = lookup_vao(ctx, vaobj); |
| if (!vao) |
| return; |
| |
| for (unsigned i = 0; i < count; i++) { |
| bind_vertex_buffer(glthread, vao, first + i, buffers[i], offsets[i], |
| strides[i]); |
| } |
| } |
| |
| static void |
| binding_divisor(struct glthread_state *glthread, struct glthread_vao *vao, |
| GLuint bindingindex, GLuint divisor) |
| { |
| if (bindingindex >= VERT_ATTRIB_GENERIC_MAX) |
| return; |
| |
| unsigned i = VERT_ATTRIB_GENERIC(bindingindex); |
| vao->Attrib[i].Divisor = divisor; |
| |
| if (divisor) |
| vao->NonZeroDivisorMask |= 1u << i; |
| else |
| vao->NonZeroDivisorMask &= ~(1u << i); |
| } |
| |
| void |
| _mesa_glthread_BindingDivisor(struct gl_context *ctx, GLuint bindingindex, |
| GLuint divisor) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| binding_divisor(glthread, glthread->CurrentVAO, bindingindex, divisor); |
| } |
| |
| void |
| _mesa_glthread_DSABindingDivisor(struct gl_context *ctx, GLuint vaobj, |
| GLuint bindingindex, GLuint divisor) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| struct glthread_vao *vao = lookup_vao(ctx, vaobj); |
| |
| if (vao) |
| binding_divisor(glthread, vao, bindingindex, divisor); |
| } |
| |
| void |
| _mesa_glthread_AttribBinding(struct gl_context *ctx, GLuint attribindex, |
| GLuint bindingindex) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (attribindex >= VERT_ATTRIB_GENERIC_MAX || |
| bindingindex >= VERT_ATTRIB_GENERIC_MAX) |
| return; |
| |
| set_attrib_binding(glthread, glthread->CurrentVAO, |
| VERT_ATTRIB_GENERIC(attribindex), |
| VERT_ATTRIB_GENERIC(bindingindex)); |
| } |
| |
| void |
| _mesa_glthread_DSAAttribBinding(struct gl_context *ctx, GLuint vaobj, |
| GLuint attribindex, GLuint bindingindex) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (attribindex >= VERT_ATTRIB_GENERIC_MAX || |
| bindingindex >= VERT_ATTRIB_GENERIC_MAX) |
| return; |
| |
| struct glthread_vao *vao = lookup_vao(ctx, vaobj); |
| if (vao) { |
| set_attrib_binding(glthread, vao, |
| VERT_ATTRIB_GENERIC(attribindex), |
| VERT_ATTRIB_GENERIC(bindingindex)); |
| } |
| } |
| |
| void |
| _mesa_glthread_DSAElementBuffer(struct gl_context *ctx, GLuint vaobj, |
| GLuint buffer) |
| { |
| struct glthread_vao *vao = lookup_vao(ctx, vaobj); |
| |
| if (vao) |
| vao->CurrentElementBufferName = buffer; |
| } |
| |
| void |
| _mesa_glthread_PushClientAttrib(struct gl_context *ctx, GLbitfield mask, |
| bool set_default) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (glthread->ClientAttribStackTop >= MAX_CLIENT_ATTRIB_STACK_DEPTH) |
| return; |
| |
| struct glthread_client_attrib *top = |
| &glthread->ClientAttribStack[glthread->ClientAttribStackTop]; |
| |
| if (mask & GL_CLIENT_VERTEX_ARRAY_BIT) { |
| top->VAO = *glthread->CurrentVAO; |
| top->CurrentArrayBufferName = glthread->CurrentArrayBufferName; |
| top->ClientActiveTexture = glthread->ClientActiveTexture; |
| top->RestartIndex = glthread->RestartIndex; |
| top->PrimitiveRestart = glthread->PrimitiveRestart; |
| top->PrimitiveRestartFixedIndex = glthread->PrimitiveRestartFixedIndex; |
| top->Valid = true; |
| } else { |
| top->Valid = false; |
| } |
| |
| glthread->ClientAttribStackTop++; |
| |
| if (set_default) |
| _mesa_glthread_ClientAttribDefault(ctx, mask); |
| } |
| |
| void |
| _mesa_glthread_PopClientAttrib(struct gl_context *ctx) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (glthread->ClientAttribStackTop == 0) |
| return; |
| |
| glthread->ClientAttribStackTop--; |
| |
| struct glthread_client_attrib *top = |
| &glthread->ClientAttribStack[glthread->ClientAttribStackTop]; |
| |
| if (!top->Valid) |
| return; |
| |
| /* Popping a delete VAO is an error. */ |
| struct glthread_vao *vao = NULL; |
| if (top->VAO.Name) { |
| vao = lookup_vao(ctx, top->VAO.Name); |
| if (!vao) |
| return; |
| } |
| |
| /* Restore states. */ |
| glthread->CurrentArrayBufferName = top->CurrentArrayBufferName; |
| glthread->ClientActiveTexture = top->ClientActiveTexture; |
| glthread->RestartIndex = top->RestartIndex; |
| glthread->PrimitiveRestart = top->PrimitiveRestart; |
| glthread->PrimitiveRestartFixedIndex = top->PrimitiveRestartFixedIndex; |
| |
| if (!vao) |
| vao = &glthread->DefaultVAO; |
| |
| assert(top->VAO.Name == vao->Name); |
| *vao = top->VAO; /* Copy all fields. */ |
| glthread->CurrentVAO = vao; |
| } |
| |
| void |
| _mesa_glthread_ClientAttribDefault(struct gl_context *ctx, GLbitfield mask) |
| { |
| struct glthread_state *glthread = &ctx->GLThread; |
| |
| if (!(mask & GL_CLIENT_VERTEX_ARRAY_BIT)) |
| return; |
| |
| glthread->CurrentArrayBufferName = 0; |
| glthread->ClientActiveTexture = 0; |
| glthread->RestartIndex = 0; |
| glthread->PrimitiveRestart = false; |
| glthread->PrimitiveRestartFixedIndex = false; |
| glthread->CurrentVAO = &glthread->DefaultVAO; |
| _mesa_glthread_reset_vao(glthread->CurrentVAO); |
| } |
| |
| void |
| _mesa_glthread_InterleavedArrays(struct gl_context *ctx, GLenum format, |
| GLsizei stride, const GLvoid *pointer) |
| { |
| struct gl_interleaved_layout layout; |
| unsigned tex = VERT_ATTRIB_TEX(ctx->GLThread.ClientActiveTexture); |
| |
| if (stride < 0 || !_mesa_get_interleaved_layout(format, &layout)) |
| return; |
| |
| if (!stride) |
| stride = layout.defstride; |
| |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_EDGEFLAG, false); |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR_INDEX, false); |
| /* XXX also disable secondary color and generic arrays? */ |
| |
| /* Texcoords */ |
| if (layout.tflag) { |
| _mesa_glthread_ClientState(ctx, NULL, tex, true); |
| _mesa_glthread_AttribPointer(ctx, tex, layout.tcomps, GL_FLOAT, stride, |
| (GLubyte *) pointer + layout.toffset); |
| } else { |
| _mesa_glthread_ClientState(ctx, NULL, tex, false); |
| } |
| |
| /* Color */ |
| if (layout.cflag) { |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR0, true); |
| _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_COLOR0, layout.ccomps, |
| layout.ctype, stride, |
| (GLubyte *) pointer + layout.coffset); |
| } else { |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_COLOR0, false); |
| } |
| |
| /* Normals */ |
| if (layout.nflag) { |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_NORMAL, true); |
| _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_NORMAL, 3, GL_FLOAT, |
| stride, (GLubyte *) pointer + layout.noffset); |
| } else { |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_NORMAL, false); |
| } |
| |
| /* Vertices */ |
| _mesa_glthread_ClientState(ctx, NULL, VERT_ATTRIB_POS, true); |
| _mesa_glthread_AttribPointer(ctx, VERT_ATTRIB_POS, layout.vcomps, GL_FLOAT, |
| stride, (GLubyte *) pointer + layout.voffset); |
| } |