PixelEffect.cpp 6.5 KB

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