/* * 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); }