blob: 17718824c5fd5557dbc4a8f68c65d62a41325373 [file] [log] [blame]
/*
* 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);
}