diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index ffafa4182..f5cc027ca 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -151,6 +151,9 @@ Canvas::~Canvas() bool Canvas::loadVolatile() { + if (texture != 0) + return true; + OpenGL::TempDebugGroup debuggroup("Canvas load"); fbo = texture = 0; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 73b6fae81..0d0e8c7d3 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -281,6 +281,9 @@ void Image::loadFromImageData() bool Image::loadVolatile() { + if (texture != 0) + return true; + OpenGL::TempDebugGroup debuggroup("Image load"); if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB)) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index c740c35e6..f0905de10 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -106,8 +106,8 @@ public: enum FramebufferTarget { - FRAMEBUFFER_READ = (1 << 1), - FRAMEBUFFER_DRAW = (1 << 2), + FRAMEBUFFER_READ = (1 << 0), + FRAMEBUFFER_DRAW = (1 << 1), FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW), }; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 0900a94f9..6ad34bcea 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -41,11 +41,12 @@ namespace // reattaches the originally active program when destroyed struct TemporaryAttacher { - TemporaryAttacher(Shader *shader) + TemporaryAttacher(Shader *shader, bool attachNow) : curShader(shader) , prevShader(Shader::current) { - curShader->attach(true); + if (attachNow) + attach(); } ~TemporaryAttacher() @@ -56,6 +57,11 @@ namespace curShader->detach(); } + void attach() + { + curShader->attach(true); + } + Shader *curShader; Shader *prevShader; }; @@ -69,8 +75,6 @@ Shader *Shader::defaultVideoShader = nullptr; Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2]; Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2]; -std::vector Shader::textureCounters; - Shader::Shader(const ShaderSource &source) : shaderSource(source) , program(0) @@ -84,10 +88,6 @@ Shader::Shader(const ShaderSource &source) if (source.vertex.empty() && source.pixel.empty()) throw love::Exception("Cannot create shader: no source code!"); - // initialize global texture id counters if needed - if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1) - textureCounters.resize(gl.getMaxTextureUnits() - 1, 0); - // load shader source and create program object loadVolatile(); } @@ -97,12 +97,25 @@ Shader::~Shader() if (current == this) detach(); - for (const auto &retainable : boundRetainables) - retainable.second->release(); - - boundRetainables.clear(); - unloadVolatile(); + + for (const auto &p : uniforms) + { + // Allocated with malloc(). + if (p.second.data != nullptr) + free(p.second.data); + + if (p.second.baseType == UNIFORM_SAMPLER) + { + for (int i = 0; i < p.second.count; i++) + { + if (p.second.textures[i] != nullptr) + p.second.textures[i]->release(); + } + + delete[] p.second.textures; + } + } } GLuint Shader::compileCode(ShaderStage stage, const std::string &code) @@ -176,8 +189,6 @@ void Shader::mapActiveUniforms() for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) builtinUniforms[i] = -1; - uniforms.clear(); - GLint activeprogram = 0; glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram); @@ -189,6 +200,9 @@ void Shader::mapActiveUniforms() GLchar cname[256]; const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar)); + std::map olduniforms = uniforms; + uniforms.clear(); + for (int i = 0; i < numuniforms; i++) { GLsizei namelen = 0; @@ -206,12 +220,6 @@ void Shader::mapActiveUniforms() else u.components = getUniformTypeComponents(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) - // don't seem to do it... - if (u.baseType == UNIFORM_SAMPLER) - glUniform1i(u.location, 0); - // glGetActiveUniform appends "[0]" to the end of array uniform names... if (u.name.length() > 3) { @@ -225,8 +233,135 @@ void Shader::mapActiveUniforms() if (builtinNames.find(u.name.c_str(), builtin)) builtinUniforms[int(builtin)] = u.location; - if (u.location != -1) - uniforms[u.name] = u; + if (u.location == -1) + continue; + + // Make sure previously set uniform data is preserved, and shader- + // initialized values are retrieved. + auto oldu = olduniforms.find(u.name); + if (oldu != olduniforms.end()) + { + u.data = oldu->second.data; + u.textures = oldu->second.textures; + + updateUniform(&u, u.count, true); + + if (u.baseType == UNIFORM_SAMPLER) + { + // Make sure all stored textures have their Volatiles loaded + // before the sendTextures call, since it calls getHandle(). + for (int i = 0; i < u.count; i++) + { + if (u.textures[i] == nullptr) + continue; + + Volatile *v = dynamic_cast(u.textures[i]); + if (v != nullptr) + v->loadVolatile(); + } + + sendTextures(&u, u.textures, u.count, true); + } + } + else + { + size_t datasize = 0; + + switch (u.baseType) + { + case UNIFORM_FLOAT: + datasize = sizeof(float) * u.components * u.count; + u.data = malloc(datasize); + break; + case UNIFORM_INT: + case UNIFORM_BOOL: + case UNIFORM_SAMPLER: + datasize = sizeof(int) * u.components * u.count; + u.data = malloc(datasize); + break; + case UNIFORM_MATRIX: + datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count; + u.data = malloc(datasize); + break; + default: + break; + } + + if (datasize > 0) + { + memset(u.data, 0, datasize); + + if (u.baseType == UNIFORM_SAMPLER) + { + // 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) don't seem to do it... + glUniform1iv(u.location, u.count, u.ints); + + u.textures = new Texture*[u.count]; + memset(u.textures, 0, sizeof(Texture *) * u.count); + } + } + + size_t offset = 0; + + // Store any shader-initialized values in our own memory. + for (int i = 0; i < u.count; i++) + { + GLint location = u.location; + + if (u.count > 1) + { + std::string indexname = u.name + "[" + std::to_string(i) + "]"; + location = glGetUniformLocation(program, indexname.c_str()); + } + + if (location == -1) + continue; + + switch (u.baseType) + { + case UNIFORM_FLOAT: + glGetUniformfv(program, location, &u.floats[offset]); + offset += u.components; + break; + case UNIFORM_INT: + case UNIFORM_BOOL: + glGetUniformiv(program, location, &u.ints[offset]); + offset += u.components; + break; + case UNIFORM_MATRIX: + glGetUniformfv(program, location, &u.floats[offset]); + offset += u.matrix.rows * u.matrix.columns; + break; + default: + break; + } + } + } + + uniforms[u.name] = u; + } + + // Make sure uniforms that existed before but don't exist anymore are + // cleaned up. This theoretically shouldn't happen, but... + for (const auto &p : olduniforms) + { + if (uniforms.find(p.first) == uniforms.end()) + { + free(p.second.data); + + if (p.second.baseType != UNIFORM_SAMPLER) + continue; + + for (int i = 0; i < p.second.count; i++) + { + if (p.second.textures[i] != nullptr) + p.second.textures[i]->release(); + } + + delete[] p.second.textures; + } } gl.useProgram(activeprogram); @@ -251,8 +386,8 @@ bool Shader::loadVolatile() videoTextureUnits[i] = 0; // zero out active texture list - activeTexUnits.clear(); - activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0); + textureUnits.clear(); + textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit()); std::vector shaderids; @@ -348,22 +483,12 @@ void Shader::unloadVolatile() program = 0; } - // decrement global texture id counters for texture units which had textures bound from this shader - for (size_t i = 0; i < activeTexUnits.size(); ++i) - { - if (activeTexUnits[i] > 0) - textureCounters[i] = std::max(textureCounters[i] - 1, 0); - } - // active texture list is probably invalid, clear it - activeTexUnits.clear(); - activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0); + textureUnits.clear(); + textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit()); attributes.clear(); - // same with uniform location list - uniforms.clear(); - // And the locations of any built-in uniform variables. for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) builtinUniforms[i] = -1; @@ -420,10 +545,10 @@ void Shader::attach(bool 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 (int i = 0; i < (int) activeTexUnits.size(); ++i) + for (int i = 1; i < (int) textureUnits.size(); ++i) { - if (activeTexUnits[i] > 0) - gl.bindTextureToUnit(activeTexUnits[i], i + 1, false); + if (textureUnits[i].active) + gl.bindTextureToUnit(textureUnits[i].texture, i, false); } } } @@ -455,160 +580,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const return &(it->second); } -void Shader::sendInts(const UniformInfo *info, const int *vec, int count) +void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate) { - if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL) - return; - - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, !internalUpdate); int location = info->location; + UniformType type = info->baseType; - switch (info->components) + if (type == UNIFORM_FLOAT) { - case 4: - glUniform4iv(location, count, vec); - break; - case 3: - glUniform3iv(location, count, vec); - break; - case 2: - glUniform2iv(location, count, vec); - break; - case 1: - default: - glUniform1iv(location, count, vec); - break; + switch (info->components) + { + case 1: + glUniform1fv(location, count, info->floats); + break; + case 2: + glUniform2fv(location, count, info->floats); + break; + case 3: + glUniform3fv(location, count, info->floats); + break; + case 4: + glUniform4fv(location, count, info->floats); + break; + } + } + else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER) + { + switch (info->components) + { + case 1: + glUniform1iv(location, count, info->ints); + break; + case 2: + glUniform2iv(location, count, info->ints); + break; + case 3: + glUniform3iv(location, count, info->ints); + break; + case 4: + glUniform4iv(location, count, info->ints); + break; + } + } + else if (type == UNIFORM_MATRIX) + { + int columns = info->matrix.columns; + int rows = info->matrix.rows; + + if (columns == 2 && rows == 2) + glUniformMatrix2fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 3) + glUniformMatrix3fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 4) + glUniformMatrix4fv(location, count, GL_FALSE, info->floats); + else if (columns == 2 && rows == 3) + glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats); + else if (columns == 2 && rows == 4) + glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 2) + glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 4) + glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 2) + glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 3) + glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats); } } -void Shader::sendFloats(const UniformInfo *info, const float *vec, int count) +int Shader::getFreeTextureUnits(int count) { - if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL) - return; + int startunit = -1; - TemporaryAttacher attacher(this); - - int location = info->location; - - switch (info->components) + // Ignore the first texture unit for Shader-local texture bindings. + for (int i = 1; i < (int) textureUnits.size(); i++) { - case 4: - glUniform4fv(location, count, vec); - break; - case 3: - glUniform3fv(location, count, vec); - break; - case 2: - glUniform2fv(location, count, vec); - break; - case 1: - default: - glUniform1fv(location, count, vec); - break; + if (!textureUnits[i].active && i + count <= (int) textureUnits.size()) + { + startunit = i; + break; + } } + + if (startunit == -1) + throw love::Exception("No more texture units available for shader."); + + return startunit; } -void Shader::sendMatrices(const UniformInfo *info, const float *m, int count) -{ - if (info->baseType != UNIFORM_MATRIX) - return; - - TemporaryAttacher attacher(this); - - int location = info->location; - - int columns = info->matrix.columns; - int rows = info->matrix.rows; - - if (columns == 2 && rows == 2) - glUniformMatrix2fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 3) - glUniformMatrix3fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 4) - glUniformMatrix4fv(location, count, GL_FALSE, m); - else if (columns == 2 && rows == 3) - glUniformMatrix2x3fv(location, count, GL_FALSE, m); - else if (columns == 2 && rows == 4) - glUniformMatrix2x4fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 2) - glUniformMatrix3x2fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 4) - glUniformMatrix3x4fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 2) - glUniformMatrix4x2fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 3) - glUniformMatrix4x3fv(location, count, GL_FALSE, m); -} - -void Shader::sendTexture(const UniformInfo *info, Texture *texture) +void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate) { if (info->baseType != UNIFORM_SAMPLER) return; - GLuint gltex = *(GLuint *) texture->getHandle(); + count = std::min(count, info->count); + bool updateuniform = false; - TemporaryAttacher attacher(this); - - int texunit = getTextureUnit(info->name); - - // bind texture to assigned texture unit and send uniform to shader program - gl.bindTextureToUnit(gltex, texunit, false); - - glUniform1i(info->location, texunit); - - // increment global shader texture id counter for this texture unit, if we haven't already - if (activeTexUnits[texunit-1] == 0) - ++textureCounters[texunit-1]; - - // store texture id so it can be re-bound to the proper texture unit later - activeTexUnits[texunit-1] = gltex; - - retainObject(info->name, texture); -} - -void Shader::retainObject(const std::string &name, Object *object) -{ - object->retain(); - - auto it = boundRetainables.find(name); - if (it != boundRetainables.end()) - it->second->release(); - - boundRetainables[name] = object; -} - -int Shader::getTextureUnit(const std::string &name) -{ - auto it = texUnitPool.find(name); - - if (it != texUnitPool.end()) - return it->second; - - int texunit = 1; - - // prefer texture units which are unused by all other shaders - auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0); - - if (freeunit_it != textureCounters.end()) + // Make sure the shader's samplers are associated with texture units. + for (int i = 0; i < count; i++) { - // we don't want to use unit 0 - texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1; - } - else - { - // no completely unused texture units exist, try to use next free slot in our own list - auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0); + if (info->ints[i] == 0 && textures[i] != nullptr) + { + int texunit = getFreeTextureUnits(1); + textureUnits[texunit].active = true; - if (nextunit_it == activeTexUnits.end()) - throw love::Exception("No more texture units available for shader."); - - // we don't want to use unit 0 - texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1; + info->ints[i] = texunit; + updateuniform = true; + } } - texUnitPool[name] = texunit; - return texunit; + if (updateuniform) + updateUniform(info, count, internalUpdate); + + // Bind the textures to the texture units. + for (int i = 0; i < count; i++) + { + if (textures[i] != nullptr) + textures[i]->retain(); + + if (info->textures[i] != nullptr) + info->textures[i]->release(); + + info->textures[i] = textures[i]; + + int texunit = info->ints[i]; + + if (textures[i] != nullptr) + { + GLuint gltex = *(GLuint *) textures[i]->getHandle(); + + gl.bindTextureToUnit(gltex, texunit, false); + + // store texture id so it can be re-bound to the proper texture unit later + textureUnits[texunit].texture = gltex; + } + else + { + gl.bindTextureToUnit(0, texunit, false); + textureUnits[texunit].texture = 0; + textureUnits[texunit].active = false; + } + } } bool Shader::hasUniform(const std::string &name) const @@ -635,12 +748,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture) { - TemporaryAttacher attacher(this); - // Set up the texture units that will be used by the shader to sample from // the textures, if they haven't been set up yet. if (videoTextureUnits[0] == 0) { + TemporaryAttacher attacher(this, true); + const GLint locs[3] = { builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL], builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL], @@ -656,14 +769,15 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur { if (locs[i] >= 0 && names[i] != nullptr) { - videoTextureUnits[i] = getTextureUnit(names[i]); + const UniformInfo *info = getUniformInfo(names[i]); + if (info != nullptr) + { + videoTextureUnits[i] = getFreeTextureUnits(1); + textureUnits[videoTextureUnits[i]].active = true; - // Increment global shader texture id counter for this texture - // unit, if we haven't already. - if (activeTexUnits[videoTextureUnits[i] - 1] == 0) - ++textureCounters[videoTextureUnits[i] - 1]; - - glUniform1i(locs[i], videoTextureUnits[i]); + info->ints[0] = videoTextureUnits[i]; + updateUniform(info, 1); + } } } } @@ -676,7 +790,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur if (videoTextureUnits[i] != 0) { // Store texture id so it can be re-bound later. - activeTexUnits[videoTextureUnits[i] - 1] = textures[i]; + textureUnits[videoTextureUnits[i]].texture = textures[i]; gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false); } } @@ -718,7 +832,7 @@ void Shader::checkSetScreenParams() if (location >= 0) { - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); glUniform4fv(location, 1, params); } @@ -735,7 +849,7 @@ void Shader::checkSetPointSize(float size) if (location >= 0) { - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); glUniform1f(location, size); } @@ -755,7 +869,7 @@ void Shader::checkSetBuiltinUniforms() const Matrix4 &curxform = gl.matrices.transform.back(); const Matrix4 &curproj = gl.matrices.projection; - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); bool tpmatrixneedsupdate = false; @@ -802,11 +916,6 @@ void Shader::checkSetBuiltinUniforms() } } -const std::map &Shader::getBoundRetainables() const -{ - return boundRetainables; -} - std::string Shader::getGLSLVersion() { const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 8561f3b1e..2ead90da2 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -96,13 +96,24 @@ public: { int location; int count; + union { int components; MatrixSize matrix; }; + UniformType baseType; std::string name; + + union + { + void *data; + float *floats; + int *ints; + }; + + Texture **textures; }; // Pointer to currently active Shader. @@ -147,11 +158,9 @@ public: std::string getWarnings() const; const UniformInfo *getUniformInfo(const std::string &name) const; + void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false); - 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); + void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false); /** * Gets whether a uniform with the specified name exists and is actively @@ -171,24 +180,11 @@ public: void checkSetPointSize(float size); void checkSetBuiltinUniforms(); - const std::map &getBoundRetainables() const; - GLuint getProgram() const { return program; } - template - T *getScratchBuffer(size_t count) - { - size_t bytes = sizeof(T) * count; - - if (scratchBuffer.size() < bytes) - scratchBuffer.resize(bytes); - - return (T *) scratchBuffer.data(); - } - static std::string getGLSLVersion(); static bool isSupported(); @@ -200,6 +196,12 @@ public: private: + struct TextureUnit + { + GLuint texture = 0; + bool active = false; + }; + // Map active uniform names to their locations. void mapActiveUniforms(); @@ -209,9 +211,7 @@ private: GLuint compileCode(ShaderStage stage, const std::string &code); - int getTextureUnit(const std::string &name); - - void retainObject(const std::string &name, Object *object); + int getFreeTextureUnits(int count); // Get any warnings or errors generated only by the shader program object. std::string getProgramWarnings() const; @@ -237,11 +237,7 @@ private: std::map uniforms; // Texture unit pool for setting images - std::map texUnitPool; // texUnitPool[name] = textureunit - std::vector activeTexUnits; // activeTexUnits[textureunit-1] = textureid - - // Uniform name to retainable objects - std::map boundRetainables; + std::vector textureUnits; bool canvasWasActive; OpenGL::Viewport lastViewport; @@ -253,11 +249,6 @@ private: GLuint videoTextureUnits[3]; - std::vector scratchBuffer; - - // Counts total number of textures bound to each texture unit in all shaders - static std::vector textureCounters; - static StringMap::Entry stageNameEntries[]; static StringMap stageNames; diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index 3e0dfa191..25701c9e0 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -52,10 +52,8 @@ static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info } template -static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count) +static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count) { - T *values = shader->getScratchBuffer(components * count); - if (components == 1) { for (int i = 0; i < count; ++i) @@ -76,16 +74,15 @@ static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components lua_pop(L, components); } } - - return values; } int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors) { int count = _getCount(L, startidx, info); int components = info->components; + float *values = info->floats; - float *values = _getNumbers(L, startidx, shader, components, count); + _updateNumbers(L, startidx, values, components, count); if (colors && graphics::isGammaCorrect()) { @@ -99,15 +96,15 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader } } - luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { int count = _getCount(L, startidx, info); - int *values = _getNumbers(L, startidx, shader, info->components, count); - luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); }); + _updateNumbers(L, startidx, info->ints, info->components, count); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } @@ -116,15 +113,15 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad int count = _getCount(L, startidx, info); int components = info->components; - // We have to send booleans as ints or floats. - float *values = shader->getScratchBuffer(components * count); + // We have to send booleans as ints. + int *values = info->ints; if (components == 1) { for (int i = 0; i < count; i++) { luaL_checktype(L, startidx + i, LUA_TBOOLEAN); - values[i] = (float) lua_toboolean(L, startidx + i); + values[i] = (int) lua_toboolean(L, startidx + i); } } else @@ -137,14 +134,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad { lua_rawgeti(L, startidx + i, k); luaL_checktype(L, -1, LUA_TBOOLEAN); - values[i * components + k - 1] = (float) lua_toboolean(L, -1); + values[i * components + k - 1] = (int) lua_toboolean(L, -1); } lua_pop(L, components); } } - luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } @@ -163,7 +160,7 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad int rows = info->matrix.rows; int elements = columns * rows; - float *values = shader->getScratchBuffer(elements * count); + float *values = info->floats; for (int i = 0; i < count; i++) { @@ -243,15 +240,21 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad } } - shader->sendMatrices(info, values, count); + shader->updateUniform(info, count); return 0; } -int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) +int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { - // We don't support arrays of textures (yet). - Texture *texture = luax_checktexture(L, startidx); - luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); }); + int count = _getCount(L, startidx, info); + + std::vector textures; + textures.reserve(count); + + for (int i = 0; i < count; i++) + textures.push_back(luax_checktexture(L, startidx + i)); + + luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); }); return 0; } @@ -277,7 +280,7 @@ int w_Shader_send(lua_State *L) case Shader::UNIFORM_BOOL: return w_Shader_sendBooleans(L, startidx, shader, info); case Shader::UNIFORM_SAMPLER: - return w_Shader_sendTexture(L, startidx, shader, info); + return w_Shader_sendTextures(L, startidx, shader, info); default: return luaL_error(L, "Unknown variable type for shader uniform '%s", name); }