PixelEffect.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /**
  2. * Copyright (c) 2006-2012 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include "PixelEffect.h"
  21. #include "GLee.h"
  22. #include "Graphics.h"
  23. namespace
  24. {
  25. // temporarily attaches a shader program (for setting uniforms, etc)
  26. // reattaches the originally active program when destroyed
  27. struct TemporaryAttacher
  28. {
  29. TemporaryAttacher(love::graphics::opengl::PixelEffect *sp) : s(sp)
  30. {
  31. glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram);
  32. s->attach();
  33. }
  34. ~TemporaryAttacher()
  35. {
  36. glUseProgram(activeProgram);
  37. }
  38. love::graphics::opengl::PixelEffect *s;
  39. GLint activeProgram;
  40. };
  41. } // anonymous namespace
  42. namespace love
  43. {
  44. namespace graphics
  45. {
  46. namespace opengl
  47. {
  48. PixelEffect *PixelEffect::current = NULL;
  49. std::map<std::string, GLint> PixelEffect::_texture_unit_pool;
  50. GLint PixelEffect::_current_texture_unit = 0;
  51. GLint PixelEffect::_max_texture_units = 0;
  52. GLint PixelEffect::getTextureUnit(const std::string &name)
  53. {
  54. std::map<std::string, GLint>::const_iterator it = _texture_unit_pool.find(name);
  55. if (it != _texture_unit_pool.end())
  56. return it->second;
  57. if (++_current_texture_unit >= _max_texture_units)
  58. throw love::Exception("No more texture units available");
  59. _texture_unit_pool[name] = _current_texture_unit;
  60. return _current_texture_unit;
  61. }
  62. PixelEffect::PixelEffect(const std::string &code)
  63. : _program(0)
  64. , _code(code)
  65. {
  66. glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &_max_texture_units);
  67. loadVolatile();
  68. }
  69. bool PixelEffect::loadVolatile()
  70. {
  71. _program = glCreateProgram();
  72. // should only fail if this is called between a glBegin()/glEnd() pair
  73. if (_program == 0)
  74. throw love::Exception("Cannot create shader program object.");
  75. GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
  76. // should only fail if this is called between a glBegin()/glEnd() pair
  77. if (shader == 0)
  78. {
  79. glDeleteProgram(_program);
  80. throw love::Exception("Cannot create shader object.");
  81. }
  82. // compile fragment shader code
  83. const char *src = _code.c_str();
  84. GLint strlen = _code.length();
  85. glShaderSource(shader, 1, (const GLchar **)&src, &strlen);
  86. glCompileShader(shader);
  87. GLint compile_ok;
  88. glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_ok);
  89. if (GL_FALSE == compile_ok)
  90. {
  91. // get compiler error
  92. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &strlen);
  93. char *error_str = new char[strlen];
  94. glGetShaderInfoLog(shader, strlen, NULL, error_str);
  95. std::string tmp(error_str);
  96. // cleanup before throw
  97. delete[] error_str;
  98. glDeleteShader(shader);
  99. glDeleteProgram(_program);
  100. // XXX: errorlog may contain escape sequences.
  101. throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
  102. }
  103. // link fragment shader
  104. GLint link_ok;
  105. glAttachShader(_program, shader);
  106. glLinkProgram(_program);
  107. glGetProgramiv(_program, GL_LINK_STATUS, &link_ok);
  108. if (GL_FALSE == link_ok)
  109. {
  110. // this should not happen if compiling is ok, but one can never be too careful
  111. // get linker error
  112. std::string tmp(getWarnings());
  113. // cleanup before throw
  114. glDeleteShader(shader);
  115. glDeleteProgram(_program);
  116. throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
  117. }
  118. glDeleteShader(shader);
  119. return true;
  120. }
  121. PixelEffect::~PixelEffect()
  122. {
  123. unloadVolatile();
  124. }
  125. void PixelEffect::unloadVolatile()
  126. {
  127. glDeleteProgram(_program);
  128. }
  129. std::string PixelEffect::getGLSLVersion()
  130. {
  131. // GL_SHADING_LANGUAGE_VERSION may not be available in OpenGL < 2.0.
  132. const char *tmp = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
  133. if (NULL == tmp)
  134. return "0.0";
  135. // the version string always begins with a version number of the format
  136. // major_number.minor_number
  137. // or
  138. // major_number.minor_number.release_number
  139. std::string versionString(tmp);
  140. size_t minorEndPos = versionString.find(" ");
  141. return versionString.substr(0, minorEndPos);
  142. }
  143. bool PixelEffect::isSupported()
  144. {
  145. return (GLEE_VERSION_2_0 || (GLEE_ARB_shader_objects && GLEE_ARB_fragment_shader)) && getGLSLVersion() >= "1.2";
  146. }
  147. std::string PixelEffect::getWarnings() const
  148. {
  149. GLint strlen, nullpos;
  150. glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &strlen);
  151. char *temp_str = new char[strlen+1];
  152. // be extra sure that the error string will be 0-terminated
  153. memset(temp_str, '\0', strlen+1);
  154. glGetProgramInfoLog(_program, strlen, &nullpos, temp_str);
  155. temp_str[nullpos] = '\0';
  156. std::string warnings(temp_str);
  157. delete[] temp_str;
  158. return warnings;
  159. }
  160. void PixelEffect::attach()
  161. {
  162. glUseProgram(_program);
  163. current = this;
  164. }
  165. void PixelEffect::detach()
  166. {
  167. glUseProgram(0);
  168. current = NULL;
  169. }
  170. void PixelEffect::sendFloat(const std::string &name, int size, const GLfloat *vec, int count)
  171. {
  172. TemporaryAttacher attacher(this);
  173. GLint location = getUniformLocation(name);
  174. if (size < 1 || size > 4)
  175. {
  176. throw love::Exception("Invalid variable size: %d (expected 1-4).", size);
  177. }
  178. switch (size)
  179. {
  180. case 4:
  181. glUniform4fv(location, count, vec);
  182. break;
  183. case 3:
  184. glUniform3fv(location, count, vec);
  185. break;
  186. case 2:
  187. glUniform2fv(location, count, vec);
  188. break;
  189. case 1:
  190. default:
  191. glUniform1fv(location, count, vec);
  192. break;
  193. }
  194. // throw error if needed
  195. checkSetUniformError();
  196. }
  197. void PixelEffect::sendMatrix(const std::string &name, int size, const GLfloat *m, int count)
  198. {
  199. TemporaryAttacher attacher(this);
  200. GLint location = getUniformLocation(name);
  201. if (size < 2 || size > 4)
  202. {
  203. throw love::Exception("Invalid matrix size: %dx%d "
  204. "(can only set 2x2, 3x3 or 4x4 matrices).", size,size);
  205. }
  206. switch (size)
  207. {
  208. case 4:
  209. glUniformMatrix4fv(location, count, GL_FALSE, m);
  210. break;
  211. case 3:
  212. glUniformMatrix3fv(location, count, GL_FALSE, m);
  213. break;
  214. case 2:
  215. default:
  216. glUniformMatrix2fv(location, count, GL_FALSE, m);
  217. break;
  218. }
  219. // throw error if needed
  220. checkSetUniformError();
  221. }
  222. void PixelEffect::sendImage(const std::string &name, const Image &image)
  223. {
  224. GLint texture_unit = getTextureUnit(name);
  225. TemporaryAttacher attacher(this);
  226. GLint location = getUniformLocation(name);
  227. glActiveTexture(GL_TEXTURE0 + texture_unit);
  228. bindTexture(image.getTextureName(), true); // guarantee it gets bound
  229. glUniform1i(location, texture_unit);
  230. // reset texture unit
  231. glActiveTexture(GL_TEXTURE0);
  232. // throw error if needed
  233. checkSetUniformError();
  234. }
  235. void PixelEffect::sendCanvas(const std::string &name, const Canvas &canvas)
  236. {
  237. GLint texture_unit = getTextureUnit(name);
  238. TemporaryAttacher attacher(this);
  239. GLint location = getUniformLocation(name);
  240. glActiveTexture(GL_TEXTURE0 + texture_unit);
  241. bindTexture(canvas.getTextureName(), true); // guarantee it gets bound
  242. glUniform1i(location, texture_unit);
  243. // reset texture unit
  244. glActiveTexture(GL_TEXTURE0);
  245. // throw error if needed
  246. checkSetUniformError();
  247. }
  248. GLint PixelEffect::getUniformLocation(const std::string &name)
  249. {
  250. std::map<std::string, GLint>::const_iterator it = _uniforms.find(name);
  251. if (it != _uniforms.end())
  252. return it->second;
  253. GLint location = glGetUniformLocation(_program, name.c_str());
  254. if (location == -1)
  255. {
  256. throw love::Exception(
  257. "Cannot get location of shader variable `%s'.\n"
  258. "A common error is to define but not use the variable.", name.c_str());
  259. }
  260. _uniforms[name] = location;
  261. return location;
  262. }
  263. void PixelEffect::checkSetUniformError()
  264. {
  265. GLenum error_code = glGetError();
  266. if (GL_INVALID_OPERATION == error_code)
  267. {
  268. throw love::Exception(
  269. "Invalid operation:\n"
  270. "- Trying to send the wrong value type to shader variable, or\n"
  271. "- Trying to send array values with wrong dimension, or\n"
  272. "- Invalid variable name.");
  273. }
  274. }
  275. } // opengl
  276. } // graphics
  277. } // love