diff --git a/.hgignore b/.hgignore index ebcc9bc92..506e9d83c 100644 --- a/.hgignore +++ b/.hgignore @@ -22,7 +22,8 @@ glob:*.lib glob:*.ncb glob:*.exe glob:*.bat -glob:platform/macosx/build +glob:platform/macosx/build +glob:platform/macosx/DerivedData glob:platform/macosx/love.xcodeproj/bill* glob:platform/macosx/love.xcodeproj/xcuserdata glob:platform/macosx/love.xcodeproj/project.xcworkspace diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index e500d87ab..baced4abd 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -455,7 +455,7 @@ PixelEffect *Graphics::newPixelEffect(const std::string &code) PixelEffect *effect = NULL; try { - effect = new PixelEffect(code); + effect = new PixelEffect("", code); } catch(love::Exception &e) { diff --git a/src/modules/graphics/opengl/PixelEffect.cpp b/src/modules/graphics/opengl/PixelEffect.cpp index 8e570fb27..8b4b2cd38 100644 --- a/src/modules/graphics/opengl/PixelEffect.cpp +++ b/src/modules/graphics/opengl/PixelEffect.cpp @@ -69,72 +69,130 @@ GLint PixelEffect::getTextureUnit(const std::string &name) return _current_texture_unit; } -PixelEffect::PixelEffect(const std::string &code) + PixelEffect::PixelEffect(const std::string &vertcode, const std::string &fragcode) : _program(0) - , _code(code) + , _vertcode(vertcode) + , _fragcode(fragcode) { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &_max_texture_units); loadVolatile(); } -bool PixelEffect::loadVolatile() +GLuint PixelEffect::createShader(GLenum type, const std::string &code) { - _program = glCreateProgram(); - // should only fail if this is called between a glBegin()/glEnd() pair - if (_program == 0) - throw love::Exception("Cannot create shader program object."); - - GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); - // should only fail if this is called between a glBegin()/glEnd() pair - if (shader == 0) + const char *shadertypename = NULL; + switch (type) { - glDeleteProgram(_program); - throw love::Exception("Cannot create shader object."); + case GL_VERTEX_SHADER: + shadertypename = "vertex"; + break; + case GL_GEOMETRY_SHADER_ARB: + shadertypename = "geometry"; + case GL_FRAGMENT_SHADER: + default: + shadertypename = "fragment"; + break; } - - // compile fragment shader code - const char *src = _code.c_str(); - GLint strlen = _code.length(); - glShaderSource(shader, 1, (const GLchar **)&src, &strlen); + + GLuint shader = glCreateShader(type); + if (shader == 0) // should only fail when called between glBegin() and glEnd() + throw love::Exception("Cannot create %s shader object.", shadertypename); + + const char *src = code.c_str(); + size_t srclen = code.length(); + glShaderSource(shader, 1, (const GLchar **)&src, (GLint *)&srclen); + glCompileShader(shader); - - GLint compile_ok; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_ok); - if (GL_FALSE == compile_ok) + + GLint compile_status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + if (compile_status == GL_FALSE) { - // get compiler error - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &strlen); - char *error_str = new char[strlen]; - glGetShaderInfoLog(shader, strlen, NULL, error_str); - std::string tmp(error_str); - - // cleanup before throw - delete[] error_str; + GLint infologlen; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologlen); + + GLchar *errorlog = new GLchar[infologlen + 1]; + glGetShaderInfoLog(shader, infologlen, NULL, errorlog); + + std::string tmp(errorlog); + + delete[] errorlog; glDeleteShader(shader); - glDeleteProgram(_program); - - // XXX: errorlog may contain escape sequences. - throw love::Exception("Cannot compile shader:\n%s", tmp.c_str()); + + throw love::Exception("Cannot compile %s shader:\n%s", shadertypename, tmp.c_str()); } + + return shader; +} - // link fragment shader +GLuint PixelEffect::createProgram(const std::vector &shaders) +{ + GLuint program = glCreateProgram(); + if (program == 0) // should only fail when called between glBegin() and glEnd() + throw love::Exception("Cannot create shader program object."); + + std::vector::const_iterator it; + for (it = shaders.begin(); it != shaders.end(); ++it) + glAttachShader(program, *it); + + glLinkProgram(program); + + for (it = shaders.begin(); it != shaders.end(); ++it) + glDetachShader(program, *it); + GLint link_ok; - glAttachShader(_program, shader); - glLinkProgram(_program); - glGetProgramiv(_program, GL_LINK_STATUS, &link_ok); - if (GL_FALSE == link_ok) + glGetProgramiv(program, GL_LINK_STATUS, &link_ok); + if (link_ok == GL_FALSE) { - // this should not happen if compiling is ok, but one can never be too careful - // get linker error - std::string tmp(getWarnings()); + GLint strlen, nullpos; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &strlen); + + char *temp_str = new char[strlen+1]; + // be extra sure that the error string will be 0-terminated + memset(temp_str, '\0', strlen+1); + glGetProgramInfoLog(program, strlen, &nullpos, temp_str); + temp_str[nullpos] = '\0'; + + std::string warnings(temp_str); + delete[] temp_str; - // cleanup before throw - glDeleteShader(shader); - glDeleteProgram(_program); - throw love::Exception("Cannot compile shader:\n%s", tmp.c_str()); + glDeleteProgram(program); + + throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str()); } + + return program; +} - glDeleteShader(shader); +bool PixelEffect::loadVolatile() +{ + std::vector shaders; + + if (_vertcode.length() > 0) + shaders.push_back(createShader(GL_VERTEX_SHADER, _vertcode)); + + if (_fragcode.length() > 0) + shaders.push_back(createShader(GL_FRAGMENT_SHADER, _fragcode)); + + if (shaders.size() == 0) + throw love::Exception("Cannot create PixelEffect: no source code!"); + + try + { + _program = createProgram(shaders); + } + catch (love::Exception &e) + { + std::vector::iterator it; + for (it = shaders.begin(); it != shaders.end(); ++it) + glDeleteShader(*it); + + throw; + } + + std::vector::iterator it; + for (it = shaders.begin(); it != shaders.end(); ++it) + glDeleteShader(*it); return true; } @@ -146,7 +204,8 @@ PixelEffect::~PixelEffect() void PixelEffect::unloadVolatile() { - glDeleteProgram(_program); + if (_program != 0) + glDeleteProgram(_program); } std::string PixelEffect::getGLSLVersion() diff --git a/src/modules/graphics/opengl/PixelEffect.h b/src/modules/graphics/opengl/PixelEffect.h index e1818e735..7c1d65c8d 100644 --- a/src/modules/graphics/opengl/PixelEffect.h +++ b/src/modules/graphics/opengl/PixelEffect.h @@ -24,6 +24,7 @@ #include "common/Object.h" #include #include +#include #include "OpenGL.h" #include "Image.h" #include "Canvas.h" @@ -38,7 +39,7 @@ namespace opengl class PixelEffect : public Object, public Volatile { public: - PixelEffect(const std::string &code); + PixelEffect(const std::string &vertcode, const std::string &fragcode); virtual ~PixelEffect(); std::string getWarnings() const; @@ -60,8 +61,12 @@ public: private: GLint getUniformLocation(const std::string &name); void checkSetUniformError(); + GLuint createShader(GLenum type, const std::string &code); + GLuint createProgram(const std::vector &shaders); + GLuint _program; - std::string _code; // volatile and stuff + std::string _vertcode; + std::string _fragcode; // volatile and stuff // uniform location buffer std::map _uniforms;