Improve Mesh VAO state diffing;

Also permit empty tables for vertex formats.
This commit is contained in:
bjorn 2018-03-13 16:12:12 -07:00
parent dd3644bafd
commit 6fb977b5bb
7 changed files with 157 additions and 124 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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
}

View File

@ -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 }
};

View File

@ -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) {

View File

@ -4,60 +4,66 @@
#include <stdlib.h>
#include <stdio.h>
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, &current, 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 = &current.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);
}

View File

@ -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);