PixelEffect.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include "PixelEffect.h"
  2. #include "GLee.h"
  3. #include <limits>
  4. #include <sstream>
  5. namespace
  6. {
  7. // temporarily attaches a shader program (for setting uniforms, etc)
  8. // reattaches the originally active program when destroyed
  9. struct TemporaryAttacher
  10. {
  11. TemporaryAttacher(love::graphics::opengl::PixelEffect* sp) : s(sp)
  12. {
  13. glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgram);
  14. s->attach();
  15. }
  16. ~TemporaryAttacher() { glUseProgram(activeProgram); }
  17. love::graphics::opengl::PixelEffect* s;
  18. GLint activeProgram;
  19. };
  20. } // anonymous namespace
  21. namespace love
  22. {
  23. namespace graphics
  24. {
  25. namespace opengl
  26. {
  27. std::map<std::string, GLint> PixelEffect::_texture_unit_pool;
  28. GLint PixelEffect::_current_texture_unit = 0;
  29. GLint PixelEffect::_max_texture_units = 0;
  30. GLint PixelEffect::getTextureUnit(const std::string& name)
  31. {
  32. std::map<std::string, GLint>::const_iterator it = _texture_unit_pool.find(name);
  33. if (it != _texture_unit_pool.end())
  34. return it->second;
  35. if (++_current_texture_unit >= _max_texture_units)
  36. throw love::Exception("No more texture units available");
  37. _texture_unit_pool[name] = _current_texture_unit;
  38. return _current_texture_unit;
  39. }
  40. PixelEffect::PixelEffect(const std::string& code)
  41. : _program(0), _code(code)
  42. {
  43. glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &_max_texture_units);
  44. loadVolatile();
  45. }
  46. bool PixelEffect::loadVolatile()
  47. {
  48. _program = glCreateProgram();
  49. // should only fail if this is called between a glBegin()/glEnd() pair
  50. if (_program == 0)
  51. throw love::Exception("Cannot create shader program object.");
  52. GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
  53. // should only fail if this is called between a glBegin()/glEnd() pair
  54. if (shader == 0) {
  55. glDeleteProgram(_program);
  56. throw love::Exception("Cannot create shader object.");
  57. }
  58. // compile fragment shader code
  59. const char* src = _code.c_str();
  60. GLint strlen = _code.length();
  61. glShaderSource(shader, 1, (const GLchar**)&src, &strlen);
  62. glCompileShader(shader);
  63. GLint compile_ok;
  64. glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_ok);
  65. if (GL_FALSE == compile_ok) {
  66. // get compiler error
  67. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &strlen);
  68. char *error_str = new char[strlen];
  69. glGetShaderInfoLog(shader, strlen, NULL, error_str);
  70. std::string tmp(error_str);
  71. // cleanup before throw
  72. delete[] error_str;
  73. glDeleteShader(shader);
  74. glDeleteProgram(_program);
  75. // XXX: errorlog may contain escape sequences.
  76. throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
  77. }
  78. // link fragment shader
  79. GLint link_ok;
  80. glAttachShader(_program, shader);
  81. glLinkProgram(_program);
  82. glGetProgramiv(_program, GL_LINK_STATUS, &link_ok);
  83. if (GL_FALSE == link_ok) {
  84. // this should not happen if compiling is ok, but one can never be too careful
  85. // get linker error
  86. std::string tmp(getWarnings());
  87. // cleanup before throw
  88. glDeleteShader(shader);
  89. glDeleteProgram(_program);
  90. throw love::Exception("Cannot compile shader:\n%s", tmp.c_str());
  91. }
  92. glDeleteShader(shader);
  93. return true;
  94. }
  95. PixelEffect::~PixelEffect()
  96. {
  97. unloadVolatile();
  98. }
  99. void PixelEffect::unloadVolatile()
  100. {
  101. glDeleteProgram(_program);
  102. }
  103. bool PixelEffect::isSupported()
  104. {
  105. return GLEE_VERSION_2_0 && GLEE_ARB_shader_objects && GLEE_ARB_fragment_shader;
  106. }
  107. std::string PixelEffect::getWarnings() const
  108. {
  109. GLint strlen;
  110. glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &strlen);
  111. char *temp_str = new char[strlen];
  112. glGetProgramInfoLog(_program, strlen, NULL, temp_str);
  113. std::string warnings(temp_str);
  114. delete[] temp_str;
  115. return warnings;
  116. }
  117. void PixelEffect::attach()
  118. {
  119. glUseProgram(_program);
  120. }
  121. void PixelEffect::detach()
  122. {
  123. glUseProgram(0);
  124. }
  125. void PixelEffect::sendFloat(const std::string& name, int size, const GLfloat* vec, int count)
  126. {
  127. TemporaryAttacher attacher(this);
  128. GLint location = getUniformLocation(name);
  129. if (size < 1 || size > 4) {
  130. throw love::Exception("Invalid variable size: %d (expected 1-4).", size);
  131. }
  132. switch (size) {
  133. case 4:
  134. glUniform4fv(location, count, vec);
  135. break;
  136. case 3:
  137. glUniform3fv(location, count, vec);
  138. break;
  139. case 2:
  140. glUniform2fv(location, count, vec);
  141. break;
  142. case 1:
  143. default:
  144. glUniform1fv(location, count, vec);
  145. break;
  146. }
  147. // throw error if needed
  148. checkSetUniformError();
  149. }
  150. void PixelEffect::sendMatrix(const std::string& name, int size, const GLfloat* m, int count)
  151. {
  152. TemporaryAttacher attacher(this);
  153. GLint location = getUniformLocation(name);
  154. if (size < 2 || size > 4) {
  155. throw love::Exception("Invalid matrix size: %dx%d "
  156. "(can only set 2x2, 3x3 or 4x4 matrices).", size,size);
  157. }
  158. switch (size) {
  159. case 4:
  160. glUniformMatrix4fv(location, count, GL_FALSE, m);
  161. break;
  162. case 3:
  163. glUniformMatrix3fv(location, count, GL_FALSE, m);
  164. break;
  165. case 2:
  166. default:
  167. glUniformMatrix2fv(location, count, GL_FALSE, m);
  168. break;
  169. }
  170. // throw error if needed
  171. checkSetUniformError();
  172. }
  173. void PixelEffect::sendImage(const std::string& name, const Image& image)
  174. {
  175. GLint texture_unit = getTextureUnit(name);
  176. TemporaryAttacher attacher(this);
  177. GLint location = getUniformLocation(name);
  178. glActiveTexture(GL_TEXTURE0 + texture_unit);
  179. glBindTexture(GL_TEXTURE_2D, image.getTextureName());
  180. glUniform1i(location, texture_unit);
  181. // reset texture unit
  182. glActiveTexture(GL_TEXTURE0);
  183. // throw error if needed
  184. checkSetUniformError();
  185. }
  186. void PixelEffect::sendCanvas(const std::string& name, const Canvas& canvas)
  187. {
  188. GLint texture_unit = getTextureUnit(name);
  189. TemporaryAttacher attacher(this);
  190. GLint location = getUniformLocation(name);
  191. glActiveTexture(GL_TEXTURE0 + texture_unit);
  192. glBindTexture(GL_TEXTURE_2D, canvas.getTextureName());
  193. glUniform1i(location, texture_unit);
  194. // reset texture unit
  195. glActiveTexture(GL_TEXTURE0);
  196. // throw error if needed
  197. checkSetUniformError();
  198. }
  199. GLint PixelEffect::getUniformLocation(const std::string& name)
  200. {
  201. GLint location = glGetUniformLocation(_program, name.c_str());
  202. if (location == -1) {
  203. throw love::Exception(
  204. "Cannot get location of shader variable `%s'.\n"
  205. "A common error is to define but not use the variable.", name.c_str());
  206. }
  207. return location;
  208. }
  209. void PixelEffect::checkSetUniformError()
  210. {
  211. GLenum error_code = glGetError();
  212. if (GL_INVALID_OPERATION == error_code) {
  213. throw love::Exception(
  214. "Invalid operation:\n"
  215. "- Trying to send the wrong value type to shader variable, or\n"
  216. "- Trying to send array values with wrong dimension, or\n"
  217. "- Invalid variable name.");
  218. }
  219. }
  220. } // opengl
  221. } // graphics
  222. } // love