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

This commit is contained in:
Alexander Szpakowski
2012-12-22 00:29:11 -04:00
parent 16f925affa
commit 79fa42d207
5 changed files with 114 additions and 26 deletions
-1
View File
@@ -30,7 +30,6 @@
#include "common/math.h"
#include "common/Matrix.h"
#include "OpenGL.h"
#include "GLee.h"
namespace love
{
+42 -3
View File
@@ -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();
+8
View File
@@ -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.
+60 -20
View File
@@ -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<std::string, GLint>::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<GLuint> &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<GLuint> 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<std::string, GLint>::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<GLuint>::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();
+4 -2
View File
@@ -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<std::string, GLint> _texture_unit_pool;
std::vector<GLuint> _texture_id_list;
GLint getTextureUnit(const std::string &name);
void sendTexture(const std::string &name, GLuint texture);