PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/glamour/shader.d

https://github.com/burner/glamour
D | 470 lines | 317 code | 79 blank | 74 comment | 95 complexity | 57610d8eb24afbe963bc39f9ae25e68a MD5 | raw file
  1. module glamour.shader;
  2. private {
  3. import glamour.gl : GLenum, GLuint, GLint, GLchar, GLboolean,
  4. GL_VERTEX_SHADER,
  5. // GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER,
  6. GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADER,
  7. GL_LINK_STATUS, GL_FALSE, GL_INFO_LOG_LENGTH,
  8. GL_COMPILE_STATUS, GL_TRUE,
  9. glCreateProgram, glCreateShader, glCompileShader,
  10. glLinkProgram, glGetShaderiv, glGetShaderInfoLog,
  11. glGetProgramInfoLog, glGetProgramiv, glShaderSource,
  12. glUseProgram, glAttachShader, glGetAttribLocation,
  13. glDeleteProgram, glDeleteShader, glGetFragDataLocation,
  14. glGetUniformLocation, glUniform1i, glUniform1f,
  15. glUniform2f, glUniform2fv, glUniform3fv,
  16. glUniform4fv, glUniformMatrix2fv, glUniformMatrix2x3fv,
  17. glUniformMatrix2x4fv, glUniformMatrix3fv, glUniformMatrix3x2fv,
  18. glUniformMatrix3x4fv, glUniformMatrix4fv, glUniformMatrix4x2fv,
  19. glUniformMatrix4x3fv, glUniform2iv, glUniform3iv, glUniform4iv;
  20. import glamour.util : checkgl;
  21. import std.conv : to;
  22. import std.file : readText;
  23. import std.path : baseName, stripExtension;
  24. import std.string : format, splitLines, toStringz, toLower, strip;
  25. import std.array : join, split;
  26. import std.algorithm : startsWith, endsWith;
  27. import std.exception : enforceEx;
  28. import std.typecons : Tuple;
  29. version(gl3n) {
  30. import gl3n.util : is_vector, is_matrix, is_quaternion;
  31. }
  32. debug import std.stdio : stderr;
  33. }
  34. GLenum to_opengl_shader(string s, string filename="<unknown>") {
  35. switch(s) {
  36. case "vertex": return GL_VERTEX_SHADER;
  37. case "geometry": return GL_GEOMETRY_SHADER;
  38. case "fragment": return GL_FRAGMENT_SHADER;
  39. default: throw new ShaderException(format("Unknown shader, %s.", s), "load", filename);
  40. }
  41. assert(0);
  42. }
  43. // enum ctr_shader_type = ctRegex!(`^(\w+):`);
  44. /// This exception will be raised when
  45. /// an error occurs while compiling or linking a shader.
  46. class ShaderException : Exception {
  47. /// The filename passed to the ctor.
  48. string filename;
  49. /// The process passed to the ctor. Will be one of "linking" or "compiling".
  50. string process;
  51. /// Params:
  52. /// infolog = Infolog returned from OpenGL.
  53. /// process_ = Error occured while linking or compiling?
  54. /// filename_ = Used to identify the shader.
  55. this(string infolog, string process_, string filename_="<unknown>") {
  56. filename = filename_;
  57. process = process_;
  58. infolog ~= "\nFailed to " ~ process_ ~ " shader: " ~ filename_ ~ ". ";
  59. super(infolog);
  60. }
  61. }
  62. deprecated alias ShaderException ShaderError;
  63. /// Compiles an already created OpenGL shader.
  64. /// Throws: ShaderException on failure.
  65. /// Params:
  66. /// shader = The OpenGL shader.
  67. /// filename = Used to identify the shader, if an error occurs.
  68. void compile_shader(GLuint shader, string filename="<unknown>") {
  69. checkgl!glCompileShader(shader);
  70. GLint status;
  71. checkgl!glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
  72. if(status == GL_FALSE) {
  73. GLint infolog_length;
  74. checkgl!glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infolog_length);
  75. GLchar[] infolog = new GLchar[infolog_length+1];
  76. checkgl!glGetShaderInfoLog(shader, infolog_length, null, infolog.ptr);
  77. throw new ShaderException(infolog.to!string(), "link", filename);
  78. }
  79. }
  80. /// Links an already created OpenGL program.
  81. /// Throws: ShaderException on failure.
  82. /// Params:
  83. /// program = The OpenGL program.
  84. /// filename = Used to identify the shader, if an error occurs.
  85. void link_program(GLuint program, string filename="<unknown>") {
  86. checkgl!glLinkProgram(program);
  87. GLint status;
  88. checkgl!glGetProgramiv(program, GL_LINK_STATUS, &status);
  89. if(status == GL_FALSE) {
  90. GLint infolog_length;
  91. checkgl!glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infolog_length);
  92. GLchar[] infolog = new GLchar[infolog_length + 1];
  93. checkgl!glGetProgramInfoLog(program, infolog_length, null, infolog.ptr);
  94. throw new ShaderException(infolog.to!string(), "compile", filename);
  95. }
  96. }
  97. /// Stores each line of the shader, line and text.
  98. alias Tuple!(size_t, "line", string, "text") Line;
  99. /// Represents an OpenGL program with it's shaders.
  100. /// The constructor must be used to avoid segmentation faults.
  101. class Shader {
  102. /// The OpenGL program.
  103. GLuint program;
  104. /// Alias this to program.
  105. alias program this;
  106. private GLuint[] _shaders;
  107. /// Holds every shaders source.
  108. Line[][string] shader_sources;
  109. /// Holds the directives.
  110. string[] directives;
  111. /// The shaders filename.
  112. string filename;
  113. /// Uniform locations will be cached here.
  114. GLint[string] uniform_locations;
  115. /// Attrib locations will be cached here.
  116. GLint[string] attrib_locations;
  117. /// Frag-data locations will be cached here.
  118. GLint[string] frag_locations;
  119. /// Loads the shaders directly from a file.
  120. this(string file) {
  121. this(stripExtension(baseName(file)), readText(file));
  122. }
  123. /// Loads the shader from the source,
  124. /// filename_ is stored in $(I filename) and will be used to identify the shader.
  125. this(string filename_, string source) {
  126. filename = filename_;
  127. program = checkgl!glCreateProgram();
  128. Line[]* current;
  129. foreach(size_t line, string text; source.splitLines()) {
  130. if(text.startsWith("#")) {
  131. directives ~= text;
  132. } else {
  133. auto m = text.strip().split();
  134. if(m.length >= 1 && m[0].endsWith(":")) {
  135. string type = toLower(m[0][0..$-1]);
  136. shader_sources[type] = null;
  137. current = &(shader_sources[type]);
  138. } else {
  139. if(current !is null) {
  140. *current ~= Line(line, text);
  141. }
  142. }
  143. }
  144. }
  145. if(!directives.length) {
  146. // OSX only supports 3.2 forward contexts
  147. version(OSX) {
  148. directives ~= "#version 150\n";
  149. } else {
  150. directives ~= "#version 130\n";
  151. }
  152. }
  153. foreach(string type, Line[] lines; shader_sources) {
  154. string shader_source = directives.join("\n") ~ "\n\n";
  155. foreach(Line line; lines) {
  156. shader_source ~= format("#line %d\n%s\n", line.line, line.text);
  157. }
  158. GLenum shader_type = to_opengl_shader(type, filename);
  159. GLuint shader = checkgl!glCreateShader(shader_type);
  160. auto ssp = shader_source.ptr;
  161. int ssl = cast(int)(shader_source.length);
  162. checkgl!glShaderSource(shader, 1, &ssp, &ssl);
  163. compile_shader(shader, filename);
  164. _shaders ~= shader;
  165. checkgl!glAttachShader(program, shader);
  166. }
  167. link_program(program, filename);
  168. }
  169. ~this() {
  170. debug if(program != 0) stderr.writefln("OpenGL: Shader resources not released.");
  171. }
  172. /// Deletes all shaders and the program.
  173. void remove() {
  174. foreach(GLuint shader; _shaders) {
  175. checkgl!glDeleteShader(shader);
  176. }
  177. _shaders = [];
  178. checkgl!glDeleteProgram(program);
  179. program = 0;
  180. }
  181. /// Binds the program.
  182. void bind() {
  183. checkgl!glUseProgram(program);
  184. }
  185. /// Unbinds the program.
  186. void unbind() {
  187. checkgl!glUseProgram(0);
  188. }
  189. /// Queries an attrib location from OpenGL and caches it in $(I attrib_locations).
  190. /// If the location was already queried the cache is returned.
  191. GLint get_attrib_location(string name) {
  192. if(auto loc = name in attrib_locations) {
  193. return *loc;
  194. }
  195. debug {
  196. auto loc = checkgl!glGetAttribLocation(program, toStringz(name));
  197. if(loc < 0) {
  198. stderr.writefln(`glGetAttribLocation returned a value < 0 for location: "%s"`, name);
  199. }
  200. return attrib_locations[name] = loc;
  201. } else {
  202. return attrib_locations[name] = checkgl!glGetAttribLocation(program, toStringz(name));
  203. }
  204. }
  205. /// Queries an fragment-data location from OpenGL and caches it in $(I frag_locations).
  206. /// If the location was already queried the cache is returned.
  207. GLint get_frag_location(string name) {
  208. if(auto loc = name in frag_locations) {
  209. return *loc;
  210. }
  211. debug {
  212. auto loc = checkgl!glGetFragDataLocation(program, toStringz(name));
  213. if(loc < 0) {
  214. stderr.writefln(`glGetFragDataLocation returned a value < 0 for location: "%s"`, name);
  215. }
  216. return frag_locations[name] = loc;
  217. } else {
  218. return frag_locations[name] = checkgl!glGetFragDataLocation(program, toStringz(name));
  219. }
  220. }
  221. /// Queries an uniform location from OpenGL and caches it in $(I uniform_locations).
  222. /// If the location was already queried the cache is returned.
  223. GLint get_uniform_location(string name) {
  224. if(auto loc = name in uniform_locations) {
  225. return *loc;
  226. }
  227. debug {
  228. auto loc = checkgl!glGetUniformLocation(program, toStringz(name));
  229. if(loc < 0) {
  230. stderr.writefln(`glGetUniformLocation returned a value < 0 for location: "%s"`, name);
  231. }
  232. return uniform_locations[name] = loc;
  233. } else {
  234. return uniform_locations[name] = checkgl!glGetUniformLocation(program, toStringz(name));
  235. }
  236. }
  237. // gl3n integration
  238. version(gl3n) {
  239. /// If glamour gets compiled with version=gl3n support for
  240. /// vectors, matrices and quaternions is added
  241. void uniform(T)(string name, T value) if(is_vector!T) {
  242. static if(is(T.vt : int)) {
  243. static if(T.dimension == 2) {
  244. checkgl!glUniform2iv(get_uniform_location(name), 1, value.value_ptr);
  245. } else static if(T.dimension == 3) {
  246. checkgl!glUniform3iv(get_uniform_location(name), 1, value.value_ptr);
  247. } else static if(T.dimension == 4) {
  248. checkgl!glUniform4iv(get_uniform_location(name), 1, value.value_ptr);
  249. } else static assert(false);
  250. } else {
  251. static if(T.dimension == 2) {
  252. checkgl!glUniform2fv(get_uniform_location(name), 1, value.value_ptr);
  253. } else static if(T.dimension == 3) {
  254. checkgl!glUniform3fv(get_uniform_location(name), 1, value.value_ptr);
  255. } else static if(T.dimension == 4) {
  256. checkgl!glUniform4fv(get_uniform_location(name), 1, value.value_ptr);
  257. } else static assert(false);
  258. }
  259. }
  260. /// ditto
  261. void uniform(S : string, T)(S name, T value) if(is_matrix!T) {
  262. static if((T.rows == 2) && (T.cols == 2)) {
  263. checkgl!glUniformMatrix2fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  264. } else static if((T.rows == 3) && (T.cols == 3)) {
  265. checkgl!glUniformMatrix3fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  266. } else static if((T.rows == 4) && (T.cols == 4)) {
  267. checkgl!glUniformMatrix4fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  268. } else static if((T.rows == 2) && (T.cols == 3)) {
  269. checkgl!glUniformMatrix2x3fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  270. } else static if((T.rows == 3) && (T.cols == 2)) {
  271. checkgl!glUniformMatrix3x2fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  272. } else static if((T.rows == 2) && (T.cols == 4)) {
  273. checkgl!glUniformMatrix2x4fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  274. } else static if((T.rows == 4) && (T.cols == 2)) {
  275. checkgl!glUniformMatrix4x2fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  276. } else static if((T.rows == 3) && (T.cols == 4)) {
  277. checkgl!glUniformMatrix3x4fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  278. } else static if((T.rows == 4) && (T.cols == 3)) {
  279. checkgl!glUniformMatrix4x3fv(get_uniform_location(name), 1, GL_TRUE, value.value_ptr);
  280. } else static assert(false, "Can not upload type " ~ T.stringof ~ " to GPU as uniform");
  281. }
  282. /// ditto
  283. void uniform(S : string, T)(S name, T value) if(is_quaternion!T) {
  284. checkgl!glUniform4fv(get_uniform_location(name), 1, value.value_ptr);
  285. }
  286. } else {
  287. void uniform(S, T)(S name, T value) {
  288. static assert(false, "you have to compile glamour with version=gl3n to use Shader.uniform");
  289. }
  290. }
  291. /// Sets a shader uniform. Consider the corresponding OpenGL for more information.
  292. void uniform1i(string name, int value) {
  293. checkgl!glUniform1i(get_uniform_location(name), value);
  294. }
  295. /// ditto
  296. void uniform1i(GLint name, int value) {
  297. checkgl!glUniform1i(name, value);
  298. }
  299. /// ditto
  300. void uniform1f(string name, float value) {
  301. checkgl!glUniform1f(get_uniform_location(name), value);
  302. }
  303. /// ditto
  304. void uniform1f(GLint name, float value) {
  305. checkgl!glUniform1f(name, value);
  306. }
  307. /// ditto
  308. void uniform2f(string name, float value1, float value2) {
  309. checkgl!glUniform2f(get_uniform_location(name), value1, value2);
  310. }
  311. /// ditto
  312. void uniform2f(GLint name, float value1, float value2) {
  313. checkgl!glUniform2f(name, value1, value2);
  314. }
  315. /// ditto
  316. void uniform2fv(string name, const float[] value) {
  317. checkgl!glUniform2fv(get_uniform_location(name), cast(int)(value.length/2), value.ptr);
  318. }
  319. /// ditto
  320. void uniform2fv(GLint name, const float[] value) {
  321. checkgl!glUniform2fv(name, cast(int)(value.length/2), value.ptr);
  322. }
  323. /// ditto
  324. void uniform2fv(string name, const float[] value, int count) {
  325. checkgl!glUniform2fv(get_uniform_location(name), count, value.ptr);
  326. }
  327. /// ditto
  328. void uniform2fv(GLint name, const float[] value, int count) {
  329. checkgl!glUniform2fv(name, count, value.ptr);
  330. }
  331. /// ditto
  332. void uniform3fv(string name, const float[] value) {
  333. checkgl!glUniform3fv(get_uniform_location(name), cast(int)(value.length/3), value.ptr);
  334. }
  335. /// ditto
  336. void uniform3fv(string name, const float[] value, int count) {
  337. checkgl!glUniform3fv(get_uniform_location(name), count, value.ptr);
  338. }
  339. /// ditto
  340. void uniform3fv(GLint name, const float[] value, int count) {
  341. checkgl!glUniform3fv(name, count, value.ptr);
  342. }
  343. /// ditto
  344. void uniform4fv(string name, const float[] value) {
  345. checkgl!glUniform4fv(get_uniform_location(name), cast(int)(value.length/4), value.ptr);
  346. }
  347. /// ditto
  348. void uniform4fv(GLint name, const float[] value) {
  349. checkgl!glUniform4fv(name, cast(int)(value.length/4), value.ptr);
  350. }
  351. /// ditto
  352. void uniform4fv(string name, const float[] value, int count) {
  353. checkgl!glUniform4fv(get_uniform_location(name), count, value.ptr);
  354. }
  355. /// ditto
  356. void uniform4fv(GLint name, const float[] value, int count) {
  357. checkgl!glUniform4fv(name, count, value.ptr);
  358. }
  359. /// ditto
  360. void uniform_matrix3fv(string name, const float[] value, GLboolean transpose=GL_TRUE) {
  361. checkgl!glUniformMatrix3fv(get_uniform_location(name), cast(int)(value.length/9), transpose, value.ptr);
  362. }
  363. /// ditto
  364. void uniform_matrix3fv(GLint name, const float[] value, GLboolean transpose=GL_TRUE) {
  365. checkgl!glUniformMatrix3fv(name, cast(int)(value.length/9), transpose, value.ptr);
  366. }
  367. /// ditto
  368. void uniform_matrix3fv(string name, const float[] value, GLboolean transpose=GL_TRUE, int count=1) {
  369. checkgl!glUniformMatrix3fv(get_uniform_location(name), count, transpose, value.ptr);
  370. }
  371. /// ditto
  372. void uniform_matrix3fv(GLint name, const float[] value, GLboolean transpose=GL_TRUE, int count=1) {
  373. checkgl!glUniformMatrix3fv(name, count, transpose, value.ptr);
  374. }
  375. /// ditto
  376. void uniform_matrix4fv(string name, const float[] value, GLboolean transpose=GL_TRUE) {
  377. checkgl!glUniformMatrix4fv(get_uniform_location(name), cast(int)(value.length/16), transpose, value.ptr);
  378. }
  379. /// ditto
  380. void uniform_matrix4fv(GLint name, const float[] value, GLboolean transpose=GL_TRUE) {
  381. checkgl!glUniformMatrix4fv(name, cast(int)(value.length/16), transpose, value.ptr);
  382. }
  383. /// ditto
  384. void uniform_matrix4fv(string name, const float[] value, GLboolean transpose=GL_TRUE, int count=1) {
  385. checkgl!glUniformMatrix4fv(get_uniform_location(name), count, transpose, value.ptr);
  386. }
  387. /// ditto
  388. void uniform_matrix4fv(GLint name, const float[] value, GLboolean transpose=GL_TRUE, int count=1) {
  389. checkgl!glUniformMatrix4fv(name, count, transpose, value.ptr);
  390. }
  391. }