From 8fca2aa607bc6a778a4fa10ea739d4c105621843 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 8 Sep 2016 18:11:50 -0300 Subject: [PATCH 1/4] Call collectgarbage after love.lowmemory(), instead of before. --- src/scripts/boot.lua | 3 ++- src/scripts/boot.lua.h | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index 45baa5b44..ff42232f0 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -239,8 +239,9 @@ function love.createhandlers() if love.directorydropped then return love.directorydropped(dir) end end, lowmemory = function () + if love.lowmemory then love.lowmemory() end + collectgarbage() collectgarbage() - if love.lowmemory then return love.lowmemory() end end, }, { __index = function(self, name) diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 10527331a..45c5fe150 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -455,11 +455,13 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x77, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x77, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x77, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x28, + 0x29, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x28, 0x29, 0x0a, - 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x77, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, - 0x65, 0x2e, 0x6c, 0x6f, 0x77, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x09, 0x7d, 0x2c, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, From e8038784456672c68101d8c4dbac89ed916bdb3a Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 8 Sep 2016 18:42:56 -0300 Subject: [PATCH 2/4] =?UTF-8?q?love.event.quit("restart")=20now=20causes?= =?UTF-8?q?=20L=C3=96VE=20to=20restart=20the=20Lua=20state=20after=20shutt?= =?UTF-8?q?ing=20it=20down.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note that this isn't perfect – it won't kill running threads which aren't cleaned up by the Lua code when the main Lua state is shut down. --- src/love.cpp | 44 ++++++++++++++++++++++++++++-------------- src/scripts/boot.lua | 2 +- src/scripts/boot.lua.h | 5 +++-- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/love.cpp b/src/love.cpp index 40213b404..00e8f36c5 100644 --- a/src/love.cpp +++ b/src/love.cpp @@ -172,7 +172,13 @@ static int l_print_sdl_log(lua_State *L) } #endif -static int runlove(int argc, char **argv) +enum DoneAction +{ + DONE_QUIT, + DONE_RESTART, +}; + +static DoneAction runlove(int argc, char **argv, int &retval) { #ifdef LOVE_LEGENDARY_APP_ARGV_HACK int hack_argc = 0; @@ -186,7 +192,8 @@ static int runlove(int argc, char **argv) if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("LOVE %s (%s)\n", love_version(), love_codename()); - return 0; + retval = 0; + return DONE_QUIT; } // Create the virtual machine. @@ -247,7 +254,13 @@ static int runlove(int argc, char **argv) // Call the returned boot function. lua_call(L, 0, 1); - int retval = 0; + retval = 0; + DoneAction done = DONE_QUIT; + + // if love.boot() returns "restart", we'll start up again after closing this + // Lua state. + if (lua_type(L, -1) == LUA_TSTRING && strcmp(lua_tostring(L, -1), "restart") == 0) + done = DONE_RESTART; if (lua_isnumber(L, -1)) retval = (int) lua_tonumber(L, -1); @@ -262,13 +275,11 @@ static int runlove(int argc, char **argv) } #endif // LOVE_LEGENDARY_APP_ARGV_HACK - return retval; + return done; } int main(int argc, char **argv) { - int retval = 0; - if (strcmp(LOVE_VERSION_STRING, love_version()) != 0) { printf("Version mismatch detected!\nLOVE binary is version %s\n" @@ -276,15 +287,20 @@ int main(int argc, char **argv) return 1; } -#ifdef LOVE_IOS - // on iOS we should never programmatically exit the app, so we'll just - // "restart" when that is attempted. Games which use threads might cause - // some issues if the threads aren't cleaned up properly... - while (true) -#endif + int retval = 0; + DoneAction done = DONE_QUIT; + + do { - retval = runlove(argc, argv); - } + done = runlove(argc, argv, retval); + +#ifdef LOVE_IOS + // on iOS we should never programmatically exit the app, so we'll just + // "restart" when that is attempted. Games which use threads might cause + // some issues if the threads aren't cleaned up properly... + done = DONE_RESTART; +#endif + } while (done != DONE_QUIT); #ifdef LOVE_ANDROID SDL_Quit(); diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index ff42232f0..552827e1d 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -679,5 +679,5 @@ return function() local result, retval = xpcall(love.run, deferErrhand) if not result then return 1 end - return tonumber(retval) or 0 + return retval == nil and 0 or retval end diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 45c5fe150..90d13adf8 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -1234,8 +1234,9 @@ const unsigned char boot_lua[] = 0x72, 0x75, 0x6e, 0x2c, 0x20, 0x64, 0x65, 0x66, 0x65, 0x72, 0x45, 0x72, 0x72, 0x68, 0x61, 0x6e, 0x64, 0x29, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x20, 0x65, 0x6e, 0x64, 0x0a, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x72, - 0x65, 0x74, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x6f, 0x72, 0x20, 0x30, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x69, 0x6c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x30, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x74, 0x76, 0x61, + 0x6c, 0x0a, 0x65, 0x6e, 0x64, 0x0a, }; // [boot.lua] } // love From 0b1bfba493e5c34ce7cdb8ef4a574709df302800 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 8 Sep 2016 23:33:48 -0300 Subject: [PATCH 3/4] Use SDL_DetachThread instead of a conditional SDL_WaitThread when a Thread is destroyed. --- src/modules/thread/sdl/Thread.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/modules/thread/sdl/Thread.cpp b/src/modules/thread/sdl/Thread.cpp index 74b349988..b5bde2992 100644 --- a/src/modules/thread/sdl/Thread.cpp +++ b/src/modules/thread/sdl/Thread.cpp @@ -35,13 +35,9 @@ Thread::Thread(Threadable *t) Thread::~Thread() { - if (!running) // Clean up handle - wait(); - /* - if (running) - wait(); - FIXME: Needed for proper thread cleanup - */ + // Clean up handle + if (thread) + SDL_DetachThread(thread); } bool Thread::start() @@ -50,9 +46,9 @@ bool Thread::start() if (running) return false; if (thread) // Clean old handle up - SDL_WaitThread(thread, 0); + SDL_WaitThread(thread, nullptr); thread = SDL_CreateThread(thread_runner, t->getThreadName(), this); - running = (thread != 0); + running = (thread != nullptr); return running; } @@ -63,10 +59,10 @@ void Thread::wait() if (!thread) return; } - SDL_WaitThread(thread, 0); + SDL_WaitThread(thread, nullptr); Lock l(mutex); running = false; - thread = 0; + thread = nullptr; } bool Thread::isRunning() From a9c83897bb31bea0164848926bb5121c4a994c4c Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 11 Sep 2016 12:40:02 -0300 Subject: [PATCH 4/4] Shader:send improvements. Type and value checking is now completely based on the shader uniform's information, instead of on the arguments to Shader:send. This means Shader:send now converts numbers to integers or floats depending on the uniform's type, and matrices can now be passed in as a flat array (without requiring the 'dimension' field that shader:sendMatrix previously needed). As a result, Shader:sendTexture, Shader:sendMatrix, Shader:sendInt, and Shader:sendFloat are all deprecated in favour of Shader:send. Performance of Shader:send has also increased, especially when sending small amounts of values per call. --- src/modules/graphics/opengl/Shader.cpp | 138 ++++----- src/modules/graphics/opengl/Shader.h | 67 +---- src/modules/graphics/opengl/wrap_Shader.cpp | 317 ++++++++------------ 3 files changed, 199 insertions(+), 323 deletions(-) diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index bd288db65..16aff824a 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -192,13 +192,15 @@ void Shader::mapActiveUniforms() for (int i = 0; i < numuniforms; i++) { GLsizei namelen = 0; - Uniform u = {}; + GLenum gltype = 0; + UniformInfo u = {}; - glGetActiveUniform(program, (GLuint) i, bufsize, &namelen, &u.count, &u.type, cname); + glGetActiveUniform(program, (GLuint) i, bufsize, &namelen, &u.count, &gltype, cname); u.name = std::string(cname, (size_t) namelen); u.location = glGetUniformLocation(program, u.name.c_str()); - u.baseType = getUniformBaseType(u.type); + u.baseType = getUniformBaseType(gltype); + u.components = getUniformTypeSize(gltype); // Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to // do this themselves, but some Android devices (galaxy tab 3 and 4) @@ -409,20 +411,20 @@ void Shader::attach(bool temporary) gl.useProgram(program); current = this; // retain/release happens in Graphics::setShader. - } - if (!temporary) - { - // make sure all sent textures are properly bound to their respective texture units - // note: list potentially contains texture ids of deleted/invalid textures! - for (size_t i = 0; i < activeTexUnits.size(); ++i) + if (!temporary) { - if (activeTexUnits[i] > 0) - gl.bindTextureToUnit(activeTexUnits[i], i + 1, false); - } + // make sure all sent textures are properly bound to their respective texture units + // note: list potentially contains texture ids of deleted/invalid textures! + for (size_t i = 0; i < activeTexUnits.size(); ++i) + { + if (activeTexUnits[i] > 0) + gl.bindTextureToUnit(activeTexUnits[i], i + 1, false); + } - // We always want to use texture unit 0 for everyhing else. - gl.setTextureUnit(0); + // We always want to use texture unit 0 for everyhing else. + gl.setTextureUnit(0); + } } } @@ -442,130 +444,109 @@ void Shader::detach() current = nullptr; } -const Shader::Uniform &Shader::getUniform(const std::string &name) const +const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const { - std::map::const_iterator it = uniforms.find(name); + const auto it = uniforms.find(name); if (it == uniforms.end()) - throw love::Exception("Variable '%s' does not exist.\n" - "A common error is to define but not use the variable.", name.c_str()); + return nullptr; - return it->second; + return &(it->second); } -void Shader::checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const +void Shader::sendInts(const UniformInfo *info, const int *vec, int count) { - if (!program) - throw love::Exception("No active shader program."); + if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL) + return; - int realsize = getUniformTypeSize(u.type); - - if (size != realsize) - throw love::Exception("Value size of %d does not match variable size of %d.", size, realsize); - - if ((u.count == 1 && count > 1) || count < 0) - throw love::Exception("Invalid number of values (expected %d, got %d).", u.count, count); - - if (u.baseType == UNIFORM_SAMPLER && sendtype != u.baseType) - throw love::Exception("Cannot send a value of this type to an Image variable."); - - if ((sendtype == UNIFORM_FLOAT && u.baseType == UNIFORM_INT) || (sendtype == UNIFORM_INT && u.baseType == UNIFORM_FLOAT)) - throw love::Exception("Cannot convert between float and int."); -} - -void Shader::sendInt(const std::string &name, int size, const GLint *vec, int count) -{ TemporaryAttacher attacher(this); - const Uniform &u = getUniform(name); - checkSetUniformError(u, size, count, UNIFORM_INT); + int location = info->location; - switch (size) + switch (info->components) { case 4: - glUniform4iv(u.location, count, vec); + glUniform4iv(location, count, vec); break; case 3: - glUniform3iv(u.location, count, vec); + glUniform3iv(location, count, vec); break; case 2: - glUniform2iv(u.location, count, vec); + glUniform2iv(location, count, vec); break; case 1: default: - glUniform1iv(u.location, count, vec); + glUniform1iv(location, count, vec); break; } } -void Shader::sendFloat(const std::string &name, int size, const GLfloat *vec, int count) +void Shader::sendFloats(const UniformInfo *info, const float *vec, int count) { + if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL) + return; + TemporaryAttacher attacher(this); - const Uniform &u = getUniform(name); - checkSetUniformError(u, size, count, UNIFORM_FLOAT); + int location = info->location; - switch (size) + switch (info->components) { case 4: - glUniform4fv(u.location, count, vec); + glUniform4fv(location, count, vec); break; case 3: - glUniform3fv(u.location, count, vec); + glUniform3fv(location, count, vec); break; case 2: - glUniform2fv(u.location, count, vec); + glUniform2fv(location, count, vec); break; case 1: default: - glUniform1fv(u.location, count, vec); + glUniform1fv(location, count, vec); break; } } -void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int count) +void Shader::sendMatrices(const UniformInfo *info, const float *m, int count) { + if (info->baseType != UNIFORM_MATRIX) + return; + TemporaryAttacher attacher(this); - if (size < 2 || size > 4) - { - throw love::Exception("Invalid matrix size: %dx%d " - "(can only set 2x2, 3x3 or 4x4 matrices.)", size,size); - } + int location = info->location; - const Uniform &u = getUniform(name); - checkSetUniformError(u, size, count, UNIFORM_FLOAT); - - switch (size) + switch (info->components) { case 4: - glUniformMatrix4fv(u.location, count, GL_FALSE, m); + glUniformMatrix4fv(location, count, GL_FALSE, m); break; case 3: - glUniformMatrix3fv(u.location, count, GL_FALSE, m); + glUniformMatrix3fv(location, count, GL_FALSE, m); break; case 2: default: - glUniformMatrix2fv(u.location, count, GL_FALSE, m); + glUniformMatrix2fv(location, count, GL_FALSE, m); break; } } -void Shader::sendTexture(const std::string &name, Texture *texture) +void Shader::sendTexture(const UniformInfo *info, Texture *texture) { + if (info->baseType != UNIFORM_SAMPLER) + return; + GLuint gltex = *(GLuint *) texture->getHandle(); TemporaryAttacher attacher(this); - int texunit = getTextureUnit(name); - - const Uniform &u = getUniform(name); - checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER); + int texunit = getTextureUnit(info->name); // bind texture to assigned texture unit and send uniform to shader program gl.bindTextureToUnit(gltex, texunit, true); - glUniform1i(u.location, texunit); + glUniform1i(info->location, texunit); // increment global shader texture id counter for this texture unit, if we haven't already if (activeTexUnits[texunit-1] == 0) @@ -574,7 +555,7 @@ void Shader::sendTexture(const std::string &name, Texture *texture) // store texture id so it can be re-bound to the proper texture unit later activeTexUnits[texunit-1] = gltex; - retainObject(name, texture); + retainObject(info->name, texture); } void Shader::retainObject(const std::string &name, Object *object) @@ -632,9 +613,12 @@ Shader::UniformType Shader::getExternVariable(const std::string &name, int &comp return UNIFORM_UNKNOWN; } - components = getUniformTypeSize(it->second.type); + components = it->second.components; count = (int) it->second.count; + // Legacy support. This whole function is gone in 0.11 anyway. + if (it->second.baseType == UNIFORM_MATRIX) + return UNIFORM_FLOAT; return it->second.baseType; } @@ -894,6 +878,7 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const case GL_FLOAT_VEC2: case GL_FLOAT_VEC3: case GL_FLOAT_VEC4: + return UNIFORM_FLOAT; case GL_FLOAT_MAT2: case GL_FLOAT_MAT3: case GL_FLOAT_MAT4: @@ -903,7 +888,7 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const case GL_FLOAT_MAT3x4: case GL_FLOAT_MAT4x2: case GL_FLOAT_MAT4x3: - return UNIFORM_FLOAT; + return UNIFORM_MATRIX; case GL_BOOL: case GL_BOOL_VEC2: case GL_BOOL_VEC3: @@ -963,6 +948,7 @@ StringMap Shader::stageNames(Shader StringMap::Entry Shader::uniformTypeEntries[] = { {"float", Shader::UNIFORM_FLOAT}, + {"matrix", Shader::UNIFORM_MATRIX}, {"int", Shader::UNIFORM_INT}, {"bool", Shader::UNIFORM_BOOL}, {"image", Shader::UNIFORM_SAMPLER}, diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 8904343d2..1fad3973d 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -74,6 +74,7 @@ public: enum UniformType { UNIFORM_FLOAT, + UNIFORM_MATRIX, UNIFORM_INT, UNIFORM_BOOL, UNIFORM_SAMPLER, @@ -87,6 +88,15 @@ public: std::string pixel; }; + struct UniformInfo + { + int location; + int count; + int components; + UniformType baseType; + std::string name; + }; + // Pointer to currently active Shader. static Shader *current; @@ -128,44 +138,12 @@ public: **/ std::string getWarnings() const; - /** - * Send at least one integer or int-vector value to this Shader as a uniform. - * - * @param name The name of the uniform variable in the source code. - * @param size Number of elements in each vector to send. - * A value of 1 indicates a single-component vector (an int). - * @param vec Pointer to the integer or int-vector values. - * @param count Number of integer or int-vector values. - **/ - void sendInt(const std::string &name, int size, const GLint *vec, int count); + const UniformInfo *getUniformInfo(const std::string &name) const; - /** - * Send at least one float or vector value to this Shader as a uniform. - * - * @param name The name of the uniform variable in the source code. - * @param size Number of elements in each vector to send. - * A value of 1 indicates a single-component vector (a float). - * @param vec Pointer to the float or float-vector values. - * @param count Number of float or float-vector values. - **/ - void sendFloat(const std::string &name, int size, const GLfloat *vec, int count); - - /** - * Send at least one matrix to this Shader as a uniform. - * - * @param name The name of the uniform variable in the source code. - * @param size Number of rows/columns in the matrix. - * @param m Pointer to the first element of the first matrix. - * @param count Number of matrices to send. - **/ - void sendMatrix(const std::string &name, int size, const GLfloat *m, int count); - - /** - * Send a texture to this Shader as a uniform. - * - * @param name The name of the uniform variable in the source code. - **/ - void sendTexture(const std::string &name, Texture *texture); + void sendInts(const UniformInfo *info, const int *vec, int count); + void sendFloats(const UniformInfo *info, const float *vec, int count); + void sendMatrices(const UniformInfo *info, const float *m, int count); + void sendTexture(const UniformInfo *info, Texture *texture); /** * Gets the type, number of components, and number of array elements of @@ -221,24 +199,11 @@ public: private: - // Represents a single uniform/extern shader variable. - struct Uniform - { - GLint location; - GLint count; - GLenum type; - UniformType baseType; - std::string name; - }; - // Map active uniform names to their locations. void mapActiveUniforms(); - const Uniform &getUniform(const std::string &name) const; - int getUniformTypeSize(GLenum type) const; UniformType getUniformBaseType(GLenum type) const; - void checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const; GLuint compileCode(ShaderStage stage, const std::string &code); @@ -267,7 +232,7 @@ private: std::map attributes; // Uniform location buffer map - std::map uniforms; + std::map uniforms; // Texture unit pool for setting images std::map texUnitPool; // texUnitPool[name] = textureunit diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index 1039e012d..e13cc7926 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -23,7 +23,6 @@ #include "math/MathModule.h" #include -#include #include #include @@ -42,118 +41,51 @@ Shader *luax_checkshader(lua_State *L, int idx) int w_Shader_getWarnings(lua_State *L) { Shader *shader = luax_checkshader(L, 1); - lua_pushstring(L, shader->getWarnings().c_str()); + std::string warnings = shader->getWarnings(); + lua_pushstring(L, warnings.c_str()); return 1; } -template -static T *_getScalars(lua_State *L, Shader *shader, int count, size_t &dimension) +static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info) { - dimension = 1; - T *values = shader->getScratchBuffer(count); - - for (int i = 0; i < count; ++i) - { - if (lua_isnumber(L, 3 + i)) - values[i] = static_cast(lua_tonumber(L, 3 + i)); - else if (lua_isboolean(L, 3 + i)) - values[i] = static_cast(lua_toboolean(L, 3 + i)); - else - { - luax_typerror(L, 3 + i, "number or boolean"); - return 0; - } - } - - return values; + return std::min(std::max(lua_gettop(L) - startidx, 1), info->count); } template -static T *_getVectors(lua_State *L, Shader *shader, int count, size_t &dimension) +static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count) { - dimension = luax_objlen(L, 3); - T *values = shader->getScratchBuffer(count * dimension); + T *values = shader->getScratchBuffer(components * count); - for (int i = 0; i < count; ++i) + if (components == 1) { - if (!lua_istable(L, 3 + i)) + for (int i = 0; i < count; ++i) + values[i] = (T) luaL_checknumber(L, startidx + i); + } + else + { + for (int i = 0; i < count; i++) { - luax_typerror(L, 3 + i, "table"); - return 0; - } - if (luax_objlen(L, 3 + i) != dimension) - { - luaL_error(L, "Error in argument %d: Expected table size %d, got %d.", - 3+i, dimension, luax_objlen(L, 3+i)); - return 0; - } + luaL_checktype(L, startidx + i, LUA_TTABLE); - for (int k = 1; k <= (int) dimension; ++k) - { - lua_rawgeti(L, 3 + i, k); - if (lua_isnumber(L, -1)) - values[i * dimension + k - 1] = static_cast(lua_tonumber(L, -1)); - else if (lua_isboolean(L, -1)) - values[i * dimension + k - 1] = static_cast(lua_toboolean(L, -1)); - else + for (int k = 1; k <= components; k++) { - luax_typerror(L, -1, "number or boolean"); - return 0; + lua_rawgeti(L, startidx + i, k); + values[i * components + k - 1] = (T) luaL_checknumber(L, -1); } + + lua_pop(L, components); } - lua_pop(L, int(dimension)); } return values; } -int w_Shader_sendInt(lua_State *L) +int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors) { - Shader *shader = luax_checkshader(L, 1); - const char *name = luaL_checkstring(L, 2); - int count = lua_gettop(L) - 2; + int count = _getCount(L, startidx, info); + int components = info->components; - if (count < 1) - return luaL_error(L, "No variable to send."); - - int *values = 0; - size_t dimension = 1; - - if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) - values = _getScalars(L, shader, count, dimension); - else if (lua_istable(L, 3)) - values = _getVectors(L, shader, count, dimension); - else - return luax_typerror(L, 3, "number, boolean, or table"); - - if (!values) - return luaL_error(L, "Error in arguments."); - - luax_catchexcept(L, [&]() { shader->sendInt(name, (int) dimension, values, count); }); - return 0; -} - -static int w__Shader_sendFloat(lua_State *L, bool colors) -{ - Shader *shader = luax_checkshader(L, 1); - const char *name = luaL_checkstring(L, 2); - int count = lua_gettop(L) - 2; - - if (count < 1) - return luaL_error(L, "No variable to send."); - - float *values = nullptr; - size_t dimension = 1; - - if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) - values = _getScalars(L, shader, count, dimension); - else if (lua_istable(L, 3)) - values = _getVectors(L, shader, count, dimension); - else - return luax_typerror(L, 3, "number, boolean, or table"); - - if (!values) - return luaL_error(L, "Error in arguments."); + float *values = _getNumbers(L, startidx, shader, components, count); if (colors) { @@ -162,103 +94,94 @@ static int w__Shader_sendFloat(lua_State *L, bool colors) for (int i = 0; i < count; i++) { - for (int j = 0; j < (int) dimension; j++) + for (int j = 0; j < components; j++) { // the fourth component (alpha) is always already linear, if it exists. if (gammacorrect && j < 3) - values[i * dimension + j] = m.gammaToLinear(values[i * dimension + j] / 255.0f); + values[i * components + j] = m.gammaToLinear(values[i * components + j] / 255.0f); else - values[i * dimension + j] /= 255.0f; + values[i * components + j] /= 255.0f; } } } - luax_catchexcept(L, [&]() { shader->sendFloat(name, (int) dimension, values, count); }); + luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); return 0; } -int w_Shader_sendFloat(lua_State *L) +int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { - return w__Shader_sendFloat(L, false); + int count = _getCount(L, startidx, info); + int *values = _getNumbers(L, startidx, shader, info->components, count); + luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); }); + return 0; } -int w_Shader_sendColor(lua_State *L) +int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { - return w__Shader_sendFloat(L, true); -} + int count = _getCount(L, startidx, info); + int components = info->components; -int w_Shader_sendMatrix(lua_State *L) -{ - int count = lua_gettop(L) - 2; - Shader *shader = luax_checkshader(L, 1); - const char *name = luaL_checkstring(L, 2); + // We have to send booleans as ints or floats. + float *values = shader->getScratchBuffer(components * count); - if (!lua_istable(L, 3)) - return luax_typerror(L, 3, "matrix table"); - - int dimension = 0; - - lua_rawgeti(L, 3, 1); - if (lua_istable(L, -1)) - dimension = (int) luax_objlen(L, 3); - lua_pop(L, 1); - - if (dimension == 0) + if (components == 1) { - lua_getfield(L, 3, "dimension"); + for (int i = 0; i < count; i++) + { + luaL_checktype(L, startidx + i, LUA_TBOOLEAN); + values[i] = (float) lua_toboolean(L, startidx + i); + } + } + else + { + for (int i = 0; i < count; i++) + { + luaL_checktype(L, startidx + i, LUA_TTABLE); - if (!lua_isnoneornil(L, -1)) - dimension = (int) lua_tointeger(L, -1); - else - dimension = (int) sqrtf((float) luax_objlen(L, 3)); + for (int k = 1; k <= components; k++) + { + lua_rawgeti(L, startidx + i, k); + luaL_checktype(L, -1, LUA_TBOOLEAN); + values[i * components + k - 1] = (float) lua_toboolean(L, -1); + } - lua_pop(L, 1); + lua_pop(L, components); + } } - if (dimension < 2 || dimension > 4) - return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).", - dimension, dimension); + luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); + return 0; +} - float *values = shader->getScratchBuffer(dimension * dimension * count); +int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) +{ + int count = _getCount(L, startidx, info); + int dimension = info->components; + int elements = dimension * dimension; - for (int i = 0; i < count; ++i) + float *values = shader->getScratchBuffer(elements * count); + + for (int i = 0; i < count; i++) { - int other_dimension = 0; + luaL_checktype(L, startidx + i, LUA_TTABLE); - lua_rawgeti(L, 3+i, 1); + lua_rawgeti(L, startidx + i, 1); bool table_of_tables = lua_istable(L, -1); - - if (table_of_tables) - other_dimension = luax_objlen(L, -1); - lua_pop(L, 1); - if (!table_of_tables) - other_dimension = (int) sqrtf((float) luax_objlen(L, 3+i)); - - if (other_dimension != dimension) - { - // You unlock this door with the key of imagination. Beyond it is - // another dimension: a dimension of sound, a dimension of sight, - // a dimension of mind. You're moving into a land of both shadow - // and substance, of things and ideas. You've just crossed over - // into... the Twilight Zone. - return luaL_error(L, "Invalid matrix size at argument %d: Expected size %dx%d, got %dx%d.", - 3+i, dimension, dimension, other_dimension, other_dimension); - } - if (table_of_tables) { int n = 0; for (int j = 1; j <= dimension; j++) { - lua_rawgeti(L, 3+i, j); + lua_rawgeti(L, startidx + i, j); for (int k = 1; k <= dimension; k++) { lua_rawgeti(L, -k, k); - values[i * dimension * dimension + n] = (float) lua_tonumber(L, -1); + values[i * elements + n] = (float) luaL_checknumber(L, -1); n++; } @@ -267,67 +190,69 @@ int w_Shader_sendMatrix(lua_State *L) } else { - for (int k = 1; k <= dimension*dimension; k++) + for (int k = 1; k <= elements; k++) { - lua_rawgeti(L, 3+i, k); - values[i * dimension * dimension + k - 1] = (float) lua_tonumber(L, -1); + lua_rawgeti(L, startidx + i, k); + values[i * elements + (k - 1)] = (float) luaL_checknumber(L, -1); } - lua_pop(L, dimension*dimension); + lua_pop(L, elements); } } - luax_catchexcept(L, [&]() { shader->sendMatrix(name, dimension, values, count); }); + shader->sendMatrices(info, values, count); return 0; } -int w_Shader_sendTexture(lua_State *L) +int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { - Shader *shader = luax_checkshader(L, 1); - const char *name = luaL_checkstring(L, 2); - Texture *texture = luax_checktexture(L, 3); - - luax_catchexcept(L, [&](){ shader->sendTexture(name, texture); }); + // We don't support arrays of textures (yet). + Texture *texture = luax_checktexture(L, startidx); + luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); }); return 0; } int w_Shader_send(lua_State *L) { - int ttype = lua_type(L, 3); - Proxy *p = nullptr; + Shader *shader = luax_checkshader(L, 1); + const char *name = luaL_checkstring(L, 2); - switch (ttype) + const Shader::UniformInfo *info = shader->getUniformInfo(name); + if (info == nullptr) + return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name); + + int startidx = 3; + + switch (info->baseType) { - case LUA_TNUMBER: - case LUA_TBOOLEAN: - // Scalar float/boolean. - return w_Shader_sendFloat(L); - break; - case LUA_TUSERDATA: - // Texture (Image or Canvas). - p = (Proxy *) lua_touserdata(L, 3); - - if (typeFlags[p->type][GRAPHICS_TEXTURE_ID]) - return w_Shader_sendTexture(L); - - break; - case LUA_TTABLE: - // Vector or Matrix. - lua_rawgeti(L, 3, 1); - ttype = lua_type(L, -1); - lua_pop(L, 1); - - if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN) - return w_Shader_sendFloat(L); - else if (ttype == LUA_TTABLE) - return w_Shader_sendMatrix(L); - - break; + case Shader::UNIFORM_FLOAT: + return w_Shader_sendFloats(L, startidx, shader, info, false); + case Shader::UNIFORM_MATRIX: + return w_Shader_sendMatrices(L, startidx, shader, info); + case Shader::UNIFORM_INT: + return w_Shader_sendInts(L, startidx, shader, info); + case Shader::UNIFORM_BOOL: + return w_Shader_sendBooleans(L, startidx, shader, info); + case Shader::UNIFORM_SAMPLER: + return w_Shader_sendTexture(L, startidx, shader, info); default: - break; + return luaL_error(L, "Unknown variable type for shader uniform '%s", name); } +} - return luaL_argerror(L, 3, "number, boolean, table, image, or canvas expected"); +int w_Shader_sendColors(lua_State *L) +{ + Shader *shader = luax_checkshader(L, 1); + const char *name = luaL_checkstring(L, 2); + + const Shader::UniformInfo *info = shader->getUniformInfo(name); + if (info == nullptr) + return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name); + + if (info->baseType != Shader::UNIFORM_FLOAT || info->components < 3) + return luaL_error(L, "sendColor can only be used on vec3 or vec4 uniforms."); + + return w_Shader_sendFloats(L, 3, shader, info, true); } int w_Shader_getExternVariable(lua_State *L) @@ -365,13 +290,13 @@ int w_Shader_getExternVariable(lua_State *L) static const luaL_Reg w_Shader_functions[] = { { "getWarnings", w_Shader_getWarnings }, - { "sendInt", w_Shader_sendInt }, - { "sendBoolean", w_Shader_sendInt }, - { "sendFloat", w_Shader_sendFloat }, - { "sendColor", w_Shader_sendColor }, - { "sendMatrix", w_Shader_sendMatrix }, - { "sendTexture", w_Shader_sendTexture }, + { "sendInt", w_Shader_send }, + { "sendBoolean", w_Shader_send }, + { "sendFloat", w_Shader_send }, + { "sendMatrix", w_Shader_send }, + { "sendTexture", w_Shader_send }, { "send", w_Shader_send }, + { "sendColor", w_Shader_sendColors }, { "getExternVariable", w_Shader_getExternVariable }, { 0, 0 } };