From 79fa42d207bceacdf8adbfda936a4153e4e3e4c3 Mon Sep 17 00:00:00 2001 From: Alexander Szpakowski Date: Sat, 22 Dec 2012 00:29:11 -0400 Subject: [PATCH] made ShaderEffects individully handle texture units for sent images/canvases, added GL helper function for quickly binding a texture to a specific texture unit, hopefully made ShaderEffects load and unload volatile better --- src/modules/graphics/opengl/Canvas.h | 1 - src/modules/graphics/opengl/OpenGL.cpp | 45 ++++++++++- src/modules/graphics/opengl/OpenGL.h | 8 ++ src/modules/graphics/opengl/ShaderEffect.cpp | 80 +++++++++++++++----- src/modules/graphics/opengl/ShaderEffect.h | 6 +- 5 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 3938ddc39..17f92bb1d 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -30,7 +30,6 @@ #include "common/math.h" #include "common/Matrix.h" #include "OpenGL.h" -#include "GLee.h" namespace love { diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 44234fa4e..4fc003f33 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -57,10 +57,27 @@ void initializeContext() textureUnits.resize(maxtextureunits, 0); - GLenum activeTextureUnit; - glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *)&activeTextureUnit); + GLenum activetextureunit; + glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *)&activetextureunit); - curTextureUnitIndex = activeTextureUnit - GL_TEXTURE0; + curTextureUnitIndex = activetextureunit - GL_TEXTURE0; + + GLuint boundtexture; + for (size_t i = 0; i < textureUnits.size(); ++i) + { + if (GLEE_VERSION_1_3) + glActiveTexture(GL_TEXTURE0 + i); + else + glActiveTextureARB(GL_TEXTURE0 + i); + + glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &boundtexture); + textureUnits[i] = boundtexture; + } + + if (GLEE_VERSION_1_3) + glActiveTexture(activetextureunit); + else + glActiveTextureARB(activetextureunit); } else { @@ -108,6 +125,28 @@ void bindTexture(GLuint texture) } } +void bindTextureToUnit(GLuint texture, GLenum textureunit, bool restoreprev) +{ + initializeContext(); + + int textureunitindex = textureunit - GL_TEXTURE0; + + if (textureunitindex < 0 || (size_t) textureunitindex >= textureUnits.size()) + throw love::Exception("Invalid texture unit index."); + + if (texture != textureUnits[textureunitindex] || texture == 0) + { + int oldtexunitindex = curTextureUnitIndex; + setActiveTextureUnit(textureunit); + + textureUnits[textureunitindex] = texture; + glBindTexture(GL_TEXTURE_2D, texture); + + if (restoreprev) + setActiveTextureUnit(GL_TEXTURE0 + oldtexunitindex); + } +} + void deleteTexture(GLuint texture) { initializeContext(); diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index a0c0b265b..9cd7ac73a 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -47,6 +47,14 @@ void setActiveTextureUnit(GLenum textureunit); **/ void bindTexture(GLuint texture); +/** + * Helper for binding a texture to a specific texture unit + * @param texture The texture to bind + * @param textureunit The texture unit to switch to + * @param resoreprev Restore previous texture unit when done + **/ +void bindTextureToUnit(GLuint texture, GLenum textureunit, bool restoreprev); + /** * Helper for deleting an OpenGL texture. * Cleans up if the texture is currently bound. diff --git a/src/modules/graphics/opengl/ShaderEffect.cpp b/src/modules/graphics/opengl/ShaderEffect.cpp index 4cdb8f17c..4d3f3b9a4 100644 --- a/src/modules/graphics/opengl/ShaderEffect.cpp +++ b/src/modules/graphics/opengl/ShaderEffect.cpp @@ -31,7 +31,7 @@ struct TemporaryAttacher : cureffect(sp) , preveffect(love::graphics::opengl::ShaderEffect::current) { - cureffect->attach(); + cureffect->attach(true); } ~TemporaryAttacher() @@ -76,20 +76,6 @@ ShaderEffect::~ShaderEffect() unloadVolatile(); } -GLint ShaderEffect::getTextureUnit(const std::string &name) -{ - std::map::const_iterator it = _texture_unit_pool.find(name); - - if (it != _texture_unit_pool.end()) - return it->second; - - if (++_current_texture_unit >= _max_texture_units) - throw love::Exception("No more texture units available for shaders"); - - _texture_unit_pool[name] = _current_texture_unit; - return _current_texture_unit; -} - GLuint ShaderEffect::createShader(GLenum type, const std::string &code) { const char *shadertypename = NULL; @@ -166,6 +152,10 @@ void ShaderEffect::createProgram(const std::vector &shaders) bool ShaderEffect::loadVolatile() { + // zero out texture id list + _texture_id_list.resize(_max_texture_units - 1, 0); + _texture_id_list.insert(_texture_id_list.begin(), _max_texture_units-1, 0); + std::vector shaders; if (_vertcode.length() > 0) @@ -195,7 +185,10 @@ bool ShaderEffect::loadVolatile() glDeleteShader(*it); if (current == this) - glUseProgram(_program); + { + current = NULL; // make sure glUseProgram gets called + attach(); + } return true; } @@ -209,6 +202,13 @@ void ShaderEffect::unloadVolatile() glDeleteProgram(_program); _program = 0; + + // texture list is probably invalid, clear it + _texture_id_list.clear(); + _texture_id_list.insert(_texture_id_list.begin(), _max_texture_units-1, 0); + + // same with uniform location list + _uniforms.clear(); } std::string ShaderEffect::getGLSLVersion() @@ -249,12 +249,27 @@ std::string ShaderEffect::getWarnings() const return warnings; } -void ShaderEffect::attach() +void ShaderEffect::attach(bool temporary) { if (current != this) glUseProgram(_program); current = this; + + if (!temporary) + { + // make sure all sent textures are properly bound to their respective TIUs + // note: list potentially contains textureids of deleted/invalid textures! + for (size_t i = 0; i < _texture_id_list.size(); ++i) + { + if (_texture_id_list[i] != 0) + { + GLenum textureunit = GL_TEXTURE0 + i + 1; + bindTextureToUnit(_texture_id_list[i], textureunit, false); + } + } + setActiveTextureUnit(GL_TEXTURE0); + } } void ShaderEffect::detach() @@ -329,16 +344,18 @@ void ShaderEffect::sendTexture(const std::string &name, GLuint texture) { TemporaryAttacher attacher(this); GLint location = getUniformLocation(name); - GLint texture_unit = getTextureUnit(name); - setActiveTextureUnit(GL_TEXTURE0 + texture_unit); - bindTexture(texture); + // bind texture to assigned texture unit and send uniform to bound shader program + bindTextureToUnit(texture, GL_TEXTURE0 + texture_unit, false); glUniform1i(location, texture_unit); // reset texture unit setActiveTextureUnit(GL_TEXTURE0); + // store texture id so it can be re-bound to the proper texture unit when necessary + _texture_id_list[texture_unit-1] = texture; + // throw error if needed checkSetUniformError(); } @@ -371,6 +388,29 @@ GLint ShaderEffect::getUniformLocation(const std::string &name) return location; } +GLint ShaderEffect::getTextureUnit(const std::string &name) +{ + std::map::const_iterator it = _texture_unit_pool.find(name); + + if (it != _texture_unit_pool.end()) + return it->second; + + // prefer texture units which are unused by all other shaders + int nextunitindex = ++_current_texture_unit % _max_texture_units; + if (nextunitindex == 0) + { + // no completely unused texture units, try to use next free slot in our own list + std::vector::iterator nexttexunit = std::find(_texture_id_list.begin(), _texture_id_list.end(), 0); + if (nexttexunit == _texture_id_list.end()) + throw love::Exception("No more texture units available for shader."); + + nextunitindex = std::distance(_texture_id_list.begin(), nexttexunit) + 1; // we don't want to use unit 0 + } + + _texture_unit_pool[name] = nextunitindex; + return nextunitindex; +} + void ShaderEffect::checkSetUniformError() { GLenum error_code = glGetError(); diff --git a/src/modules/graphics/opengl/ShaderEffect.h b/src/modules/graphics/opengl/ShaderEffect.h index ce3f0b2e8..51f1eb95a 100644 --- a/src/modules/graphics/opengl/ShaderEffect.h +++ b/src/modules/graphics/opengl/ShaderEffect.h @@ -35,7 +35,7 @@ namespace graphics { namespace opengl { -// A fragment shader +// A GLSL shader class ShaderEffect : public Object, public Volatile { public: @@ -46,7 +46,7 @@ public: virtual bool loadVolatile(); virtual void unloadVolatile(); - void attach(); + void attach(bool temporary = false); static void detach(); static std::string getGLSLVersion(); @@ -75,7 +75,9 @@ private: // texture unit pool for setting images static GLint _current_texture_unit; static GLint _max_texture_units; + std::map _texture_unit_pool; + std::vector _texture_id_list; GLint getTextureUnit(const std::string &name); void sendTexture(const std::string &name, GLuint texture);