From 6fb977b5bb6e56f2deb3c6c55aa6a5aff7f3b748 Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 13 Mar 2018 16:12:12 -0700 Subject: [PATCH] Improve Mesh VAO state diffing; Also permit empty tables for vertex formats. --- src/api.h | 2 +- src/api/data.c | 4 +- src/api/graphics.c | 10 +- src/api/types/mesh.c | 50 +++++++--- src/api/types/vertexData.c | 8 +- src/graphics/mesh.c | 183 +++++++++++++++++++------------------ src/graphics/mesh.h | 24 ++--- 7 files changed, 157 insertions(+), 124 deletions(-) diff --git a/src/api.h b/src/api.h index 7867c950..467fe450 100644 --- a/src/api.h +++ b/src/api.h @@ -98,7 +98,7 @@ extern map_int_t VerticalAligns; extern map_int_t WrapModes; // Shared helpers -void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format); +bool luax_checkvertexformat(lua_State* L, int index, VertexFormat* format); int luax_pushvertexformat(lua_State* L, VertexFormat* format); int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attribute); int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format); diff --git a/src/api/data.c b/src/api/data.c index ab823b04..751a6d15 100644 --- a/src/api/data.c +++ b/src/api/data.c @@ -99,8 +99,8 @@ int l_lovrDataNewVertexData(lua_State* L) { uint32_t count = luaL_checkinteger(L, 1); VertexFormat format; vertexFormatInit(&format); - luax_checkvertexformat(L, 2, &format); - VertexData* vertexData = lovrVertexDataCreate(count, format.count > 0 ? &format : NULL, true); + bool hasFormat = luax_checkvertexformat(L, 2, &format); + VertexData* vertexData = lovrVertexDataCreate(count, hasFormat ? &format : NULL, true); luax_pushtype(L, VertexData, vertexData); lovrRelease(vertexData); return 1; diff --git a/src/api/graphics.c b/src/api/graphics.c index d2722576..992a3f66 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -945,6 +945,7 @@ int l_lovrGraphicsNewMesh(lua_State* L) { int dataIndex = 0; int drawModeIndex = 2; VertexData* vertexData = NULL; + bool hasFormat = false; VertexFormat format; vertexFormatInit(&format); @@ -953,12 +954,12 @@ int l_lovrGraphicsNewMesh(lua_State* L) { } else if (lua_istable(L, 1)) { if (lua_isnumber(L, 2)) { drawModeIndex++; - luax_checkvertexformat(L, 1, &format); + hasFormat = luax_checkvertexformat(L, 1, &format); count = lua_tointeger(L, 2); dataIndex = 0; } else if (lua_istable(L, 2)) { drawModeIndex++; - luax_checkvertexformat(L, 1, &format); + hasFormat = luax_checkvertexformat(L, 1, &format); count = lua_objlen(L, 2); dataIndex = 2; } else { @@ -969,6 +970,7 @@ int l_lovrGraphicsNewMesh(lua_State* L) { vertexData = luax_checktype(L, 1, VertexData); format = vertexData->format; count = vertexData->count; + hasFormat = true; } else { luaL_argerror(L, 1, "table or number expected"); return 0; @@ -976,9 +978,9 @@ int l_lovrGraphicsNewMesh(lua_State* L) { if (!vertexData) { #ifdef EMSCRIPTEN - vertexData = lovrVertexDataCreate(count, format.count > 0 ? &format : NULL, true); + vertexData = lovrVertexDataCreate(count, hasFormat ? &format : NULL, true); #else - vertexData = lovrVertexDataCreate(count, format.count > 0 ? &format : NULL, false); + vertexData = lovrVertexDataCreate(count, hasFormat ? &format : NULL, false); #endif } diff --git a/src/api/types/mesh.c b/src/api/types/mesh.c index e2ddf976..b516b83a 100644 --- a/src/api/types/mesh.c +++ b/src/api/types/mesh.c @@ -1,5 +1,38 @@ #include "api.h" +int l_lovrMeshAttachAttributes(lua_State* L) { + Mesh* mesh = luax_checktype(L, 1, Mesh); + Mesh* other = luax_checktype(L, 2, Mesh); + int instanceDivisor = luaL_optinteger(L, 3, 0); + if (lua_isnoneornil(L, 4)) { + VertexFormat* format = lovrMeshGetVertexFormat(other); + for (int i = 0; i < format->count; i++) { + lovrMeshAttachAttribute(mesh, other, format->attributes[i].name, instanceDivisor); + } + } else if (lua_istable(L, 4)) { + int length = lua_objlen(L, 4); + for (int i = 0; i < length; i++) { + lua_rawgeti(L, -1, i + 1); + lovrMeshAttachAttribute(mesh, other, lua_tostring(L, -1), instanceDivisor); + lua_pop(L, 1); + } + } else { + int top = lua_gettop(L); + for (int i = 4; i <= top; i++) { + lovrMeshAttachAttribute(mesh, other, lua_tostring(L, i), instanceDivisor); + } + } + + return 0; +} + +int l_lovrMeshDetachAttribute(lua_State* L) { + Mesh* mesh = luax_checktype(L, 1, Mesh); + const char* name = luaL_checkstring(L, 2); + lovrMeshDetachAttribute(mesh, name); + return 0; +} + int l_lovrMeshDrawInstanced(lua_State* L) { Mesh* mesh = luax_checktype(L, 1, Mesh); int instances = luaL_checkinteger(L, 2); @@ -246,21 +279,9 @@ int l_lovrMeshSetMaterial(lua_State* L) { return 0; } -int l_lovrMeshAttachAttributes(lua_State* L) { - Mesh* attachTo = luax_checktype(L, 1, Mesh); - Mesh* attachThis = luax_checktype(L, 2, Mesh); - int instanceDivisor = luaL_optnumber(L, 3, 0); - // TODO: Check attribute name(s) in 4th argument and if present only attach those - - VertexFormat* format = &attachThis->vertexData->format; - for(int c = 0; c < format->count; c++) { - lovrMeshAttach(attachTo, attachThis, c, instanceDivisor); - } - - return 0; -} - const luaL_Reg lovrMesh[] = { + { "attachAttributes", l_lovrMeshAttachAttributes }, + { "detachAttribute", l_lovrMeshDetachAttribute }, { "drawInstanced", l_lovrMeshDrawInstanced }, { "draw", l_lovrMeshDraw }, { "getVertexFormat", l_lovrMeshGetVertexFormat }, @@ -280,6 +301,5 @@ const luaL_Reg lovrMesh[] = { { "setDrawRange", l_lovrMeshSetDrawRange }, { "getMaterial", l_lovrMeshGetMaterial }, { "setMaterial", l_lovrMeshSetMaterial }, - { "attachAttributes", l_lovrMeshAttachAttributes }, { NULL, NULL } }; diff --git a/src/api/types/vertexData.c b/src/api/types/vertexData.c index 03dc8970..812e2373 100644 --- a/src/api/types/vertexData.c +++ b/src/api/types/vertexData.c @@ -1,8 +1,8 @@ #include "api.h" -void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) { +bool luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) { if (!lua_istable(L, index)) { - return; + return false; } int length = lua_objlen(L, index); @@ -12,7 +12,7 @@ void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) { if (!lua_istable(L, -1) || lua_objlen(L, -1) != 3) { luaL_error(L, "Expected vertex format specified as tables containing name, data type, and size"); - return; + return false; } lua_rawgeti(L, -1, 1); @@ -25,6 +25,8 @@ void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) { vertexFormatAppend(format, name, *type, count); lua_pop(L, 4); } + + return true; } int luax_pushvertexformat(lua_State* L, VertexFormat* format) { diff --git a/src/graphics/mesh.c b/src/graphics/mesh.c index c4706589..b43b9937 100644 --- a/src/graphics/mesh.c +++ b/src/graphics/mesh.c @@ -4,60 +4,66 @@ #include #include -static void lovrMeshBindAttribute(Shader* shader, Mesh *mesh, VertexFormat *format, int i, bool enabled, int divisor) { - Attribute attribute = format->attributes[i]; - int location = lovrShaderGetAttributeId(shader, attribute.name); - - if (location >= 0) { - if (enabled) { - glEnableVertexAttribArray(location); - - GLenum glType; - switch (attribute.type) { - case ATTR_FLOAT: glType = GL_FLOAT; break; - case ATTR_BYTE: glType = GL_UNSIGNED_BYTE; break; - case ATTR_INT: glType = GL_UNSIGNED_INT; break; - } - - // Divisor lives in the VAO and the VAO is per-mesh, so the only reason we would need to set a zero divisor for an attribute is if a nonzero divisor attribute is attached then disabled, and a zero divisor attribute is enabled afterward in its place. Nonzero attachments length is used to determine there is a risk this has happened. - if (divisor || mesh->attachments.length) - glVertexAttribDivisor(location, divisor); - - if (attribute.type == ATTR_INT) { - glVertexAttribIPointer(location, attribute.count, glType, format->stride, (void*) attribute.offset); - } else { - glVertexAttribPointer(location, attribute.count, glType, GL_TRUE, format->stride, (void*) attribute.offset); - } - } else { - glDisableVertexAttribArray(location); - } - } -} - static void lovrMeshBindAttributes(Mesh* mesh) { + const char* key; + map_iter_t iter = map_iter(&mesh->attachments); Shader* shader = lovrGraphicsGetActiveShader(); - if (shader == mesh->lastShader && !mesh->attributesDirty) { - return; - } - lovrGraphicsBindVertexBuffer(mesh->vbo); + MeshAttachment layout[MAX_ATTACHMENTS]; + memset(layout, 0, MAX_ATTACHMENTS * sizeof(MeshAttachment)); - VertexFormat* format = &mesh->vertexData->format; - for (int i = 0; i < format->count; i++) { - lovrMeshBindAttribute(shader, mesh, format, i, mesh->enabledAttributes & (1 << i), 0); - } + while ((key = map_next(&mesh->attachments, &iter)) != NULL) { + int location = lovrShaderGetAttributeId(shader, key); - { - int i; MeshAttachment attachment; - vec_foreach(&mesh->attachments, attachment, i) { - lovrGraphicsBindVertexBuffer(attachment.mesh->vbo); - // TODO: Allow disabling of attached attributes? - lovrMeshBindAttribute(shader, attachment.mesh, &attachment.mesh->vertexData->format, attachment.attribute, true, attachment.instanceDivisor); + if (location >= 0) { + MeshAttachment* attachment = map_get(&mesh->attachments, key); + layout[location] = *attachment; } } - mesh->lastShader = shader; - mesh->attributesDirty = false; + for (int i = 0; i < MAX_ATTACHMENTS; i++) { + MeshAttachment previous = mesh->layout[i]; + MeshAttachment current = layout[i]; + + if (!memcmp(&previous, ¤t, sizeof(MeshAttachment))) { + continue; + } + + if (previous.enabled != current.enabled) { + if (current.enabled) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + mesh->layout[i] = current; + continue; + } + } + + if (previous.divisor != current.divisor) { + glVertexAttribDivisor(i, current.divisor); + } + + if (previous.mesh != current.mesh || previous.attributeIndex != current.attributeIndex) { + lovrGraphicsBindVertexBuffer(current.mesh->vbo); + VertexFormat* format = ¤t.mesh->vertexData->format; + Attribute attribute = format->attributes[current.attributeIndex]; + switch (attribute.type) { + case ATTR_FLOAT: + glVertexAttribPointer(i, attribute.count, GL_FLOAT, GL_TRUE, format->stride, (void*) attribute.offset); + break; + + case ATTR_BYTE: + glVertexAttribPointer(i, attribute.count, GL_UNSIGNED_BYTE, GL_TRUE, format->stride, (void*) attribute.offset); + break; + + case ATTR_INT: + glVertexAttribIPointer(i, attribute.count, GL_UNSIGNED_INT, format->stride, (void*) attribute.offset); + break; + } + } + + mesh->layout[i] = current; + } } Mesh* lovrMeshCreate(VertexData* vertexData, MeshDrawMode drawMode, MeshUsage usage) { @@ -69,8 +75,6 @@ Mesh* lovrMeshCreate(VertexData* vertexData, MeshDrawMode drawMode, MeshUsage us mesh->indices.raw = NULL; mesh->indexCount = 0; mesh->indexSize = count > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t); - mesh->enabledAttributes = ~0; - mesh->attributesDirty = true; mesh->isMapped = false; mesh->mapStart = 0; mesh->mapCount = 0; @@ -83,9 +87,7 @@ Mesh* lovrMeshCreate(VertexData* vertexData, MeshDrawMode drawMode, MeshUsage us mesh->vbo = 0; mesh->ibo = 0; mesh->material = NULL; - vec_init(&mesh->attachments); - mesh->lastShader = NULL; - mesh->isAnAttachment = false; + mesh->isAttachment = false; glGenBuffers(1, &mesh->vbo); glGenBuffers(1, &mesh->ibo); @@ -93,6 +95,14 @@ Mesh* lovrMeshCreate(VertexData* vertexData, MeshDrawMode drawMode, MeshUsage us glBufferData(GL_ARRAY_BUFFER, count * mesh->vertexData->format.stride, vertexData->blob.data, mesh->usage); glGenVertexArrays(1, &mesh->vao); + map_init(&mesh->attachments); + for (int i = 0; i < vertexData->format.count; i++) { + MeshAttachment attachment = { mesh, i, 0, true }; + map_set(&mesh->attachments, vertexData->format.attributes[i].name, attachment); + } + + memset(mesh->layout, 0, MAX_ATTACHMENTS * sizeof(MeshAttachment)); + return mesh; } @@ -103,19 +113,40 @@ void lovrMeshDestroy(void* ref) { glDeleteBuffers(1, &mesh->vbo); glDeleteBuffers(1, &mesh->ibo); glDeleteVertexArrays(1, &mesh->vao); - - { - int i; MeshAttachment attachment; - vec_foreach(&mesh->attachments, attachment, i) { - lovrRelease(attachment.mesh); + const char* key; + map_iter_t iter = map_iter(&mesh->attachments); + while ((key = map_next(&mesh->attachments, &iter)) != NULL) { + MeshAttachment* attachment = map_get(&mesh->attachments, key); + if (attachment->mesh != mesh) { + lovrRelease(attachment->mesh); } } - vec_deinit(&mesh->attachments); - + map_deinit(&mesh->attachments); free(mesh->indices.raw); free(mesh); } +void lovrMeshAttachAttribute(Mesh* mesh, Mesh* other, const char* name, int divisor) { + MeshAttachment* otherAttachment = map_get(&other->attachments, name); + lovrAssert(!mesh->isAttachment, "Attempted to attach to a mesh which is an attachment itself"); + lovrAssert(otherAttachment, "No attribute named '%s' exists", name); + lovrAssert(!map_get(&mesh->attachments, name), "Mesh already has an attribute named '%s'", name); + lovrAssert(divisor >= 0, "Divisor can't be negative"); + + MeshAttachment attachment = { other, otherAttachment->attributeIndex, divisor, true }; + map_set(&mesh->attachments, name, attachment); + other->isAttachment = true; + lovrRetain(other); +} + +void lovrMeshDetachAttribute(Mesh* mesh, const char* name) { + MeshAttachment* attachment = map_get(&mesh->attachments, name); + lovrAssert(attachment, "No attached attribute '%s' was found", name); + lovrAssert(attachment->mesh != mesh, "Attribute '%s' was not attached from another Mesh", name); + lovrRelease(attachment->mesh); + map_remove(&mesh->attachments, name); +} + void lovrMeshDraw(Mesh* mesh, mat4 transform, float* pose, int instances) { if (mesh->isMapped) { lovrMeshUnmap(mesh); @@ -187,28 +218,15 @@ void lovrMeshSetVertexMap(Mesh* mesh, void* data, size_t count) { } bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { - for (int i = 0; i < mesh->vertexData->format.count; i++) { - if (!strcmp(mesh->vertexData->format.attributes[i].name, name)) { - return mesh->enabledAttributes & (1 << i); - } - } - - return false; + MeshAttachment* attachment = map_get(&mesh->attachments, name); + lovrAssert(attachment, "Mesh does not have an attribute named '%s'", name); + return attachment->enabled; } void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) { - for (int i = 0; i < mesh->vertexData->format.count; i++) { - if (!strcmp(mesh->vertexData->format.attributes[i].name, name)) { - int mask = 1 << i; - if (enable && !(mesh->enabledAttributes & mask)) { - mesh->enabledAttributes |= mask; - mesh->attributesDirty = true; - } else if (!enable && (mesh->enabledAttributes & mask)) { - mesh->enabledAttributes &= ~(1 << i); - mesh->attributesDirty = true; - } - } - } + MeshAttachment* attachment = map_get(&mesh->attachments, name); + lovrAssert(attachment, "Mesh does not have an attribute named '%s'", name); + attachment->enabled = enable; } bool lovrMeshIsRangeEnabled(Mesh* mesh) { @@ -294,14 +312,3 @@ void lovrMeshUnmap(Mesh* mesh) { glUnmapBuffer(GL_ARRAY_BUFFER); #endif } - -void lovrMeshAttach(Mesh *attachTo, Mesh* attachThis, int attribute, int instanceDivisor) -{ - lovrAssert(!attachTo->isAnAttachment, "Attempted to attach to a mesh which is an attachment itself"); - lovrAssert(!attachThis->attachments.length, "Attempted to attach a mesh which has attachments itself"); - - MeshAttachment attachment = {attachThis, attribute, instanceDivisor}; - attachThis->isAnAttachment = true; - lovrRetain(attachThis); - vec_push(&attachTo->attachments, attachment); -} diff --git a/src/graphics/mesh.h b/src/graphics/mesh.h index 7be59235..37bfbdac 100644 --- a/src/graphics/mesh.h +++ b/src/graphics/mesh.h @@ -1,12 +1,14 @@ -#include "graphics/shader.h" #include "graphics/material.h" #include "data/vertexData.h" #include "math/math.h" #include "lib/glfw.h" +#include "lib/map/map.h" #include "util.h" #pragma once +#define MAX_ATTACHMENTS 16 + typedef enum { MESH_POINTS = GL_POINTS, MESH_LINES = GL_LINES, @@ -25,12 +27,13 @@ typedef enum { typedef struct Mesh Mesh; typedef struct { - Mesh *mesh; - int attribute; - int instanceDivisor; + Mesh* mesh; + int attributeIndex; + int divisor; + bool enabled; } MeshAttachment; -typedef vec_t(MeshAttachment) vec_meshattachment_t; +typedef map_t(MeshAttachment) map_attachment_t; struct Mesh { Ref ref; @@ -38,8 +41,6 @@ struct Mesh { IndexPointer indices; size_t indexCount; size_t indexSize; - int enabledAttributes; - bool attributesDirty; bool isMapped; int mapStart; size_t mapCount; @@ -52,13 +53,15 @@ struct Mesh { GLuint vbo; GLuint ibo; Material* material; - Shader* lastShader; - vec_meshattachment_t attachments; - bool isAnAttachment; + map_attachment_t attachments; + MeshAttachment layout[16]; + bool isAttachment; }; Mesh* lovrMeshCreate(VertexData* vertexData, MeshDrawMode drawMode, MeshUsage usage); void lovrMeshDestroy(void* ref); +void lovrMeshAttachAttribute(Mesh* mesh, Mesh* other, const char* name, int divisor); +void lovrMeshDetachAttribute(Mesh* mesh, const char* name); void lovrMeshDraw(Mesh* mesh, mat4 transform, float* pose, int instances); VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh); MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh); @@ -76,4 +79,3 @@ Material* lovrMeshGetMaterial(Mesh* mesh); void lovrMeshSetMaterial(Mesh* mesh, Material* material); VertexPointer lovrMeshMap(Mesh* mesh, int start, size_t count, bool read, bool write); void lovrMeshUnmap(Mesh* mesh); -void lovrMeshAttach(Mesh *attachTo, Mesh* attachThis, int attribute, int instanceDivisor);