diff --git a/src/api.h b/src/api.h index dcd1dff3..fb0a2296 100644 --- a/src/api.h +++ b/src/api.h @@ -56,6 +56,7 @@ extern const luaL_Reg lovrModelData[]; extern const luaL_Reg lovrRandomGenerator[]; extern const luaL_Reg lovrRasterizer[]; extern const luaL_Reg lovrShader[]; +extern const luaL_Reg lovrShaderBlock[]; extern const luaL_Reg lovrShape[]; extern const luaL_Reg lovrSliderJoint[]; extern const luaL_Reg lovrSoundData[]; @@ -114,3 +115,4 @@ Blob* luax_readblob(lua_State* L, int index, const char* debug); Seed luax_checkrandomseed(lua_State* L, int index); void luax_checkvariant(lua_State* L, int index, Variant* variant); int luax_pushvariant(lua_State* L, Variant* variant); +int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug); diff --git a/src/api/graphics.c b/src/api/graphics.c index 9db0510d..3bdeca18 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -241,6 +241,7 @@ int l_lovrGraphicsInit(lua_State* L) { luax_registertype(L, "Mesh", lovrMesh); luax_registertype(L, "Model", lovrModel); luax_registertype(L, "Shader", lovrShader); + luax_registertype(L, "ShaderBlock", lovrShaderBlock); luax_registertype(L, "Texture", lovrTexture); luax_extendtype(L, "Texture", "Canvas", lovrTexture, lovrCanvas); lovrGraphicsInit(); @@ -885,9 +886,9 @@ int l_lovrGraphicsNewShaderBlock(lua_State* L) { lua_getfield(L, -1, "type"); const char* type = lua_tolstring(L, -1, &length); uniform.components = 1; - if (strcmp(type, "float")) { + if (!strcmp(type, "float")) { uniform.type = UNIFORM_FLOAT; - } else if (strcmp(type, "int")) { + } else if (!strcmp(type, "int")) { uniform.type = UNIFORM_INT; } else { uniform.components = type[length - 1] - '0'; diff --git a/src/api/types/shader.c b/src/api/types/shader.c index 448be90a..eec6fa14 100644 --- a/src/api/types/shader.c +++ b/src/api/types/shader.c @@ -9,6 +9,132 @@ struct TempData { static struct TempData tempData; +int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug) { + Blob* blob = luax_totype(L, index, Blob); + int components = uniform->components; + int count = uniform->count; + + if (uniform->type == UNIFORM_MATRIX) { + components *= components; + } + + // TODO samplers/textures + + if (blob) { + size_t elements = count * components; + const char* s = elements == 1 ? "" : "s"; + size_t capacity; + + switch (uniform->type) { + case UNIFORM_FLOAT: + case UNIFORM_MATRIX: + capacity = blob->size / sizeof(float); + lovrAssert(capacity >= elements, "Blob can only hold %d float%s, at least %d needed", capacity, s, elements); + memcpy(dest, blob->data, elements * sizeof(float)); + break; + + case UNIFORM_INT: + capacity = blob->size / sizeof(int); + lovrAssert(capacity >= elements, "Blob can only hold %d int%s, at least %d needed", capacity, s, elements); + memcpy(dest, blob->data, elements * sizeof(int)); + break; + + default: lovrThrow(""); // TODO + } + + return 0; + } + + if (components == 1) { + bool isTable = lua_istable(L, index); + if (isTable) { + int length = lua_objlen(L, index); + lovrAssert(length == count, ""); // TODO + for (int i = 0; i < length; i++) { + lua_rawgeti(L, index, i + 1); + switch (uniform->type) { + case UNIFORM_FLOAT: *((float*) dest + i) = luaL_checknumber(L, -1); break; + case UNIFORM_INT: *((int*) dest + i) = luaL_checkinteger(L, -1); break; + default: lovrThrow(""); // TODO + } + lua_pop(L, 1); + } + } else { + for (int i = 0; i < count; i++) { + switch (uniform->type) { + case UNIFORM_FLOAT: *((float*) dest + i) = luaL_checknumber(L, index + i); break; + case UNIFORM_INT: *((int*) dest + i) = luaL_checkinteger(L, index + i); break; + default: lovrThrow(""); // TODO + } + } + } + } else { + luaL_checktype(L, index, LUA_TTABLE); + lua_rawgeti(L, index, 1); + bool wrappedTable = lua_istable(L, -1) || luax_totype(L, -1, Transform); + lua_pop(L, 1); + + if (wrappedTable) { + int length = lua_objlen(L, index); + lovrAssert(length == count, ""); // TODO + for (int i = 0; i < length; i++) { + lua_rawgeti(L, index, i + 1); + + if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, -1)) { + Transform* transform = luax_checktype(L, -1, Transform); + memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float)); + continue; + } + + for (int j = 0; j < components; j++) { + lua_rawgeti(L, -1, j + 1); + switch (uniform->type) { + case UNIFORM_FLOAT: + case UNIFORM_MATRIX: + *((float*) dest + i * components + j) = luaL_checknumber(L, -1); + + case UNIFORM_INT: + *((int*) dest + i * components + j) = luaL_checkinteger(L, -1); + break; + + default: + lovrThrow(""); // TODO + } + lua_pop(L, 1); + } + lua_pop(L, 1); + } + } else { + for (int i = 0; i < count; i++) { + if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, index + i)) { + Transform* transform = luax_checktype(L, index + i, Transform); + memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float)); + continue; + } + + luaL_checktype(L, index + i, LUA_TTABLE); + for (int j = 0; j < components; j++) { + lua_rawgeti(L, index + i, j + 1); + switch (uniform->type) { + case UNIFORM_FLOAT: + case UNIFORM_MATRIX: + *((float*) dest + i * components + j) = luaL_checknumber(L, -1); + break; + + case UNIFORM_INT: + *((float*) dest + i * components + j) = luaL_checknumber(L, -1); + break; + + default: lovrThrow(""); // TODO + } + } + } + } + } + + return 0; +} + int l_lovrShaderHasUniform(lua_State* L) { Shader* shader = luax_checktype(L, 1, Shader); const char* name = luaL_checkstring(L, 2); @@ -19,146 +145,15 @@ int l_lovrShaderHasUniform(lua_State* L) { int l_lovrShaderSend(lua_State* L) { Shader* shader = luax_checktype(L, 1, Shader); const char* name = luaL_checkstring(L, 2); - lua_settop(L, 3); + const Uniform* uniform = lovrShaderGetUniform(shader, name); + lovrAssert(uniform, "Unknown shader variable '%s'", name); - int count; - int components; - int size; - UniformType type; - bool present = lovrShaderGetUniform(shader, name, &count, &components, &size, &type); - - if (!present) { - return luaL_error(L, "Unknown shader variable '%s'", name); - } - - if (tempData.size < size) { - tempData.size = size; + if (tempData.size < uniform->size) { + tempData.size = uniform->size; tempData.data = realloc(tempData.data, tempData.size); } - int* ints = (int*) tempData.data; - float* floats = (float*) tempData.data; - Texture** textures = (Texture**) tempData.data; - int n = 1; - - if (components > 1 && lua_istable(L, 3)) { - lua_rawgeti(L, 3, 1); - if (!lua_istable(L, -1)) { - lua_newtable(L); - lua_pushvalue(L, 3); - lua_rawseti(L, -2, 1); - } else { - lua_pop(L, 1); - } - - n = lua_objlen(L, -1); - if (n != count) { - return luaL_error(L, "Expected %d element%s for array '%s', got %d", count, count == 1 ? "" : "s", name, n); - } - } - - Blob* blob = luax_totype(L, 3, Blob); - - switch (type) { - case UNIFORM_FLOAT: - if (blob) { - n = count; - floats = (float*) blob->data; - size_t count = n * components; - size_t capacity = blob->size / sizeof(float); - const char* s = capacity == 1 ? "" : "s"; - lovrAssert(capacity >= count, "Blob can only hold %d float%s, at least %d needed", capacity, s, count); - } else if (components == 1) { - floats[0] = luaL_checknumber(L, 3); - } else { - luaL_checktype(L, 3, LUA_TTABLE); - for (int i = 0; i < n; i++) { - lua_rawgeti(L, -1, i + 1); - if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) { - return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, name, lua_objlen(L, -1)); - } - for (int j = 0; j < components; j++) { - lua_rawgeti(L, -1, j + 1); - floats[i * components + j] = luaL_checknumber(L, -1); - lua_pop(L, 1); - } - lua_pop(L, 1); - } - } - lovrShaderSetFloat(shader, name, floats, n * components); - break; - - case UNIFORM_INT: - if (blob) { - n = count; - ints = (int*) blob->data; - size_t count = n * components; - size_t capacity = blob->size / sizeof(int); - const char* s = capacity == 1 ? "" : "s"; - lovrAssert(capacity >= count, "Blob can only hold %d int%s, at least %d needed", capacity, s, count); - } else if (components == 1) { - ints[0] = luaL_checkinteger(L, 3); - } else { - luaL_checktype(L, 3, LUA_TTABLE); - for (int i = 0; i < n; i++) { - lua_rawgeti(L, -1, i + 1); - if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) { - return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, name, lua_objlen(L, -1)); - } - for (int j = 0; j < components; j++) { - lua_rawgeti(L, -1, j + 1); - ints[i * components + j] = luaL_checkinteger(L, -1); - lua_pop(L, 1); - } - lua_pop(L, 1); - } - } - lovrShaderSetInt(shader, name, ints, n * components); - break; - - case UNIFORM_MATRIX: - if (blob) { - n = count; - floats = (float*) blob->data; - size_t count = n * components * components; - size_t capacity = blob->size / sizeof(float); - const char* s = capacity == 1 ? "x" : "ces"; - lovrAssert(capacity >= count, "Blob can only hold %d matri%s, at least %d needed", capacity, s, count); - } else if (components == 4 && lua_isuserdata(L, 3)) { - Transform* transform = luax_checktype(L, 3, Transform); - memcpy(floats, transform->matrix, 16 * sizeof(float)); - } else { - luaL_checktype(L, 3, LUA_TTABLE); - for (int i = 0; i < n; i++) { - lua_rawgeti(L, -1, i + 1); - for (int j = 0; j < components * components; j++) { - lua_rawgeti(L, -1, j + 1); - floats[i * components * components + j] = luaL_checknumber(L, -1); - lua_pop(L, 1); - } - lua_pop(L, 1); - } - } - lovrShaderSetMatrix(shader, name, floats, n * components * components); - break; - - case UNIFORM_SAMPLER: - if (components == 1) { - textures[0] = luax_checktype(L, 3, Texture); - } else { - luaL_checktype(L, 3, LUA_TTABLE); - for (int i = 0; i < n; i++) { - lua_rawgeti(L, -1, i + 1); - textures[i] = luax_checktype(L, -1, Texture); - lua_pop(L, 1); - } - } - lovrShaderSetTexture(shader, name, textures, n); - break; - - default: break; - } - + luax_checkuniform(L, 3, uniform, tempData.data, name); return 0; } diff --git a/src/api/types/shaderBlock.c b/src/api/types/shaderBlock.c new file mode 100644 index 00000000..83422048 --- /dev/null +++ b/src/api/types/shaderBlock.c @@ -0,0 +1,27 @@ +#include "api.h" +#include "graphics/shader.h" +#include "math/transform.h" + +int l_lovrShaderBlockGetSize(lua_State* L) { + ShaderBlock* block = luax_checktype(L, 1, ShaderBlock); + lua_pushinteger(L, lovrShaderBlockGetSize(block)); + return 1; +} + +int l_lovrShaderBlockSend(lua_State* L) { + ShaderBlock* block = luax_checktype(L, 1, ShaderBlock); + const char* name = luaL_checkstring(L, 2); + const Uniform* uniform = lovrShaderBlockGetUniform(block, name); + lovrAssert(uniform, "Unknown uniform for ShaderBlock '%s'", name); + uint8_t* data = ((uint8_t*) lovrShaderBlockMap(block)) + uniform->offset; + + luax_checkuniform(L, 3, uniform, data, name); + + return 0; +} + +const luaL_Reg lovrShaderBlock[] = { + { "getSize", l_lovrShaderBlockGetSize }, + { "send", l_lovrShaderBlockSend }, + { NULL, NULL } +}; diff --git a/src/graphics/opengl.c b/src/graphics/opengl.c index d9014a33..aa9ca887 100644 --- a/src/graphics/opengl.c +++ b/src/graphics/opengl.c @@ -67,7 +67,11 @@ static struct { struct ShaderBlock { Ref ref; vec_uniform_t uniforms; + map_int_t uniformMap; uint32_t buffer; + size_t size; + void* data; + bool mapped; }; typedef struct { @@ -1442,7 +1446,12 @@ void lovrShaderBind(Shader* shader) { UniformBlock* block; vec_foreach_ptr(&shader->blocks, block, i) { - lovrGpuBindUniformBuffer(block->source ? block->source->buffer : 0, block->binding); + if (block->source) { + lovrShaderBlockUnmap(block->source); + lovrGpuBindUniformBuffer(block->source->buffer, block->binding); + } else { + lovrGpuBindUniformBuffer(0, block->binding); + } } } @@ -1455,19 +1464,13 @@ bool lovrShaderHasUniform(Shader* shader, const char* name) { return map_get(&shader->uniformMap, name) != NULL; } -bool lovrShaderGetUniform(Shader* shader, const char* name, int* count, int* components, int* size, UniformType* type) { +const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) { int* index = map_get(&shader->uniformMap, name); if (!index) { return false; } - Uniform* uniform = &shader->uniforms.data[*index]; - - *count = uniform->count; - *components = uniform->components; - *size = uniform->size; - *type = uniform->type; - return true; + return &shader->uniforms.data[*index]; } static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, int size, const char* debug) { @@ -1529,32 +1532,70 @@ ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms) { vec_init(&block->uniforms); vec_extend(&block->uniforms, uniforms); + map_init(&block->uniformMap); int i; Uniform* uniform; size_t size = 0; vec_foreach_ptr(&block->uniforms, uniform, i) { + + // Calculate size and offset uniform->offset = size; if (uniform->type == UNIFORM_MATRIX) { size += uniform->components * uniform->components * 4; } else { size += uniform->count == 1 ? 4 : (uniform->count * 16); } + + // Write index to uniform lookup + map_set(&block->uniformMap, uniform->name, i); } glGenBuffers(1, &block->buffer); lovrGpuBindUniformBuffer(block->buffer, 0); glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_STATIC_DRAW); + block->size = size; + block->data = calloc(1, size); + return block; } void lovrShaderBlockDestroy(void* ref) { - UniformBlock* block = ref; + ShaderBlock* block = ref; vec_deinit(&block->uniforms); + map_deinit(&block->uniformMap); + free(block->data); free(block); } +size_t lovrShaderBlockGetSize(ShaderBlock* block) { + return block->size; +} + +const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) { + int* index = map_get(&block->uniformMap, name); + if (!index) return NULL; + + return &block->uniforms.data[*index]; +} + +void* lovrShaderBlockMap(ShaderBlock* block) { + block->mapped = true; + return block->data; +} + +void lovrShaderBlockUnmap(ShaderBlock* block) { + if (!block->mapped) { + return; + } + + lovrGpuBindUniformBuffer(block->buffer, 0); + glBufferData(GL_UNIFORM_BUFFER, block->size, NULL, GL_STATIC_DRAW); + glBufferSubData(GL_UNIFORM_BUFFER, 0, block->size, block->data); + block->mapped = false; +} + // Mesh Mesh* lovrMeshCreate(uint32_t count, VertexFormat format, MeshDrawMode drawMode, MeshUsage usage) { diff --git a/src/graphics/shader.h b/src/graphics/shader.h index 03b9e427..fb8735df 100644 --- a/src/graphics/shader.h +++ b/src/graphics/shader.h @@ -54,7 +54,7 @@ void lovrShaderDestroy(void* ref); void lovrShaderBind(Shader* shader); int lovrShaderGetAttributeId(Shader* shader, const char* name); bool lovrShaderHasUniform(Shader* shader, const char* name); -bool lovrShaderGetUniform(Shader* shader, const char* name, int* count, int* components, int* size, UniformType* type); +const Uniform* lovrShaderGetUniform(Shader* shader, const char* name); void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count); void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count); void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count); @@ -64,3 +64,7 @@ void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block); ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms); void lovrShaderBlockDestroy(void* ref); +size_t lovrShaderBlockGetSize(ShaderBlock* block); +const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name); +void* lovrShaderBlockMap(ShaderBlock* block); +void lovrShaderBlockUnmap(ShaderBlock* block);