1 /* 2 Copyright (c) 2013 Andrey Penechko 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license the "Software" to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module anchovy.graphics.shaderprogram; 30 31 import std.exception; 32 import std.stdio; 33 import std..string; 34 import std.variant; 35 36 public import derelict.opengl3.gl3; 37 38 39 struct Uniform 40 { 41 uint location; 42 uint type; 43 uint num; 44 float[4] lastValue; 45 } 46 47 class ShaderParameters 48 { 49 50 } 51 52 53 class ShaderProgram 54 { 55 56 public: 57 58 /++ 59 + Creates an empty shader program in OpenGL state. 60 + OpenGL must be initialized before creating. 61 +/ 62 this() 63 { 64 programHandler = glCreateProgram(); 65 _isInited = true; 66 } 67 68 this(in string vertShaderSource, in string fragShaderSource) 69 { 70 programHandler = glCreateProgram(); 71 72 attachShader(GL_VERTEX_SHADER, vertShaderSource); 73 attachShader(GL_FRAGMENT_SHADER, fragShaderSource); 74 75 _isInited = true; 76 } 77 78 void close() 79 { 80 if (_isInited) 81 { 82 glDeleteProgram(programHandler); 83 _isInited = false; 84 } 85 } 86 87 /++ 88 + Binds shader to the current OpenGL state. 89 + Must be used only after attaching all the shaders and compilation. 90 + If compilation fails, no shader will be used. 91 +/ 92 void bind() 93 { 94 glUseProgram(programHandler); 95 } 96 97 static void unbind() 98 { 99 glUseProgram(0); 100 } 101 102 /++ 103 + Compiles shader source of the given type and attaches it to the shader program. 104 + If shader compilation fails, writes compilation info to the error log of the shader program (_errorLog) 105 + 106 + Params: 107 + shaderType = GL_VERTEX_SHADER or GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER 108 + shaderSource = the string that contains the source of given shader 109 +/ 110 void attachShader(in GLenum shaderType, in string shaderSource) 111 { 112 113 GLuint shader = glCreateShader(shaderType); 114 115 const char* fileData=toStringz(shaderSource); 116 glShaderSource(shader, 1, &fileData, null); 117 118 glCompileShader(shader); 119 120 int status, length; 121 glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 122 123 if (status == GL_FALSE) 124 { 125 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); 126 127 char[] error=new char[length]; 128 glGetShaderInfoLog(shader, length, null, cast(char*)error); 129 130 string shaderTypeString; 131 switch(shaderType) 132 { 133 case GL_VERTEX_SHADER: shaderTypeString = "vertex"; break; 134 case GL_GEOMETRY_SHADER: shaderTypeString = "geometry"; break; 135 case GL_FRAGMENT_SHADER: shaderTypeString = "fragment"; break; 136 default: break; 137 } 138 139 _errorLog ~= "Compile failure in " ~ shaderTypeString ~ " shader:\n" ~ error~"\n"; 140 } 141 glAttachShader(programHandler, shader); 142 } 143 144 /++ 145 + Compiles shaderProgram. If any error occurs, writes log to _errorLog. 146 + After compile detaches shaders from program. 147 + See_Also: errorLog 148 +/ 149 bool compile() 150 { 151 152 glLinkProgram(programHandler); 153 154 GLint linkStatus; 155 156 glGetProgramiv(programHandler, GL_LINK_STATUS, &linkStatus); 157 158 /++ 159 + Detaches all the shaders after compilation 160 +/ 161 scope(exit) 162 { 163 GLuint[3] shaders; 164 GLsizei count; 165 166 glGetAttachedShaders(programHandler, 3, &count, cast(uint*)shaders); 167 168 for( uint i=0; i<count; ++i ) 169 { 170 glDetachShader(programHandler, shaders[i]); 171 glDeleteShader(shaders[i]); 172 } 173 } 174 175 if ( linkStatus == GL_FALSE ) 176 { 177 GLint infoLogLength; 178 glGetProgramiv(programHandler, GL_INFO_LOG_LENGTH, &infoLogLength); 179 180 char[] strInfoLog = new char[infoLogLength]; 181 glGetProgramInfoLog(programHandler, infoLogLength, null, cast(char*)strInfoLog); 182 _errorLog ~= "Linker failure: " ~ strInfoLog ~ "\n"; 183 184 return false; 185 } 186 187 int total = -1; 188 glGetProgramiv(programHandler, GL_ACTIVE_UNIFORMS, &total); 189 for(int i=0; i<total; ++i) 190 { 191 int name_len = -1, num = -1; 192 GLenum type = GL_ZERO; 193 char[100] name; 194 glGetActiveUniform(programHandler, i, name.length-1, 195 &name_len, &num, &type, name.ptr); 196 197 name[name_len] = 0; 198 GLuint location = glGetUniformLocation(programHandler, name.ptr); 199 string uniformName = name[0..name_len].idup; 200 uniforms[uniformName] = Uniform(location, type, num); 201 } 202 // Magic bug. Cannot remove this statement 203 writef("",uniforms); 204 uniforms.rehash; 205 206 return true; 207 } 208 209 /++ 210 + Returns: empty string if the shader was compiled without errors, or error log otherwise. 211 + Examples: 212 + --- 213 + if ( !myShaderProgram.compile ){ 214 + writeln( myShaderProgram.errorLog ); //Writes error log to console 215 + } 216 + --- 217 + See_Also: compile 218 +/ 219 @property string errorLog() 220 { 221 return _errorLog; 222 } 223 224 private static string getParType(T)() 225 { 226 string res; 227 if (is(T : int) || is(T : byte) || is(T : short)) 228 { 229 res = "i"; 230 } 231 if (is(T : uint) || is(T : ubyte) || is(T : ushort)) 232 { 233 res = "ui"; 234 } 235 else if (is(T : float)) 236 { 237 res = "f"; 238 } 239 return res; 240 } 241 242 private static uint getGlType(T)() 243 { 244 uint type; 245 static if (is(T : byte)) type = GL_BYTE; 246 else if (is(T : ubyte)) type = GL_UNSIGNED_BYTE; 247 else if (is(T : short)) type = GL_SHORT; 248 else if (is(T : ushort)) type = GL_UNSIGNED_SHORT; 249 else if (is(T : int)) type = GL_INT; 250 else if (is(T : uint)) type = GL_UNSIGNED_INT; 251 else if (is(T : float)) type = GL_FLOAT; 252 else if (is(T : double)) type = GL_DOUBLE; 253 return type; 254 } 255 //glUniform{1|2|3|4}{f|i|ui}v 256 void setUniform(T)(string uniName, T t1) 257 { 258 Uniform* uni = &getUniform(uniName); 259 if (uni.lastValue[0] != t1) 260 { 261 uni.lastValue[0] = t1; 262 mixin("glUniform1"~getParType!T~"(uni.location, t1);"); 263 } 264 /+for(uint i = 0; i<t.length-1; ++i) 265 { 266 params ~= "t["~to!string(i)~"], "; 267 } 268 params ~= "t["~to!string(t.length-1)~"]"; 269 //Uniform* uni = uniName in uniforms; 270 assert(uni !is null, "Unknown uniform name"); 271 assert(uni.type == getGlType(t[0])(), "Wrong uniform type"); 272 273 writeln("glUniform1" ~ 274 getParType!(typeof(t[0]))() ~ 275 "(" ~ 276 "uniName, " ~ 277 params ~ 278 ");");+/ 279 } 280 281 void setUniform2(T)(string uniName, T t1, T t2) 282 { 283 Uniform* uni = &getUniform(uniName); 284 if (uni.lastValue[0..2] != [t1,t2]) 285 { 286 uni.lastValue[0..2] = [t1,t2]; 287 mixin("glUniform2"~getParType!T~"(uni.location, t1, t2);"); 288 } 289 } 290 291 void setUniform3(T)(string uniName, T t1, T t2, T t3) 292 { 293 Uniform* uni = &getUniform(uniName); 294 if (uni.lastValue[0..3] != [t1, t2, t3]) 295 { 296 uni.lastValue[0..3] = [t1, t2, t3]; 297 mixin("glUniform3"~getParType!T~"(uni.location, t1, t2, t3);"); 298 } 299 } 300 301 void setUniform4(T)(string uniName, T t1, T t2, T t3, T t4) 302 { 303 Uniform* uni = &getUniform(uniName); 304 if (uni.lastValue[0..4] != [t1, t2, t3, t4]) 305 { 306 uni.lastValue[0..4] = [t1, t2, t3, t4]; 307 mixin("glUniform4"~getParType!T~"(uni.location, t1, t2, t3, t4);"); 308 } 309 } 310 311 uint getUniformLoc(string name) 312 { 313 //assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'"); 314 return uniforms[name].location; 315 } 316 317 ref Uniform getUniform(string name) 318 { 319 //assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'"); 320 return uniforms[name]; 321 } 322 323 /+ 324 + Returns: shader program pointer of type GLuint, the result of glCreateProgram(); 325 + Can be used in some OpenGL functions. 326 + Examples: 327 + --- 328 + myShaderProgram = new CShaderProgram(); 329 + --- 330 + ... 331 + --- 332 + GLuint myShaderAttribute = glGetUniformLocation( myShaderProgram.program, "myShaderAttribute" ); 333 + --- 334 +/ 335 @property GLuint program(){ return programHandler; } 336 337 //void setUniform(T)(string unifName, 338 //TODO: Add methods for uniforms setting 339 340 private: 341 342 /++ 343 + The result of glCreateProgram() get in constructor 344 + 345 + See_Also: this 346 +/ 347 GLuint programHandler = 0; 348 Uniform[string] uniforms; 349 350 /++ 351 + The result of: 352 + --- 353 + glGetShaderiv( _shaderPointer, GL_COMPILE_STATUS, _compileStatus ); 354 + --- 355 +/ 356 GLuint _compileStatus; 357 358 /++ 359 + Contains full compile log if compile error. 360 +/ 361 string _errorLog; 362 363 bool _isInited; 364 365 } 366 367 static const string[uint] glTypes; 368 369 static this() 370 { 371 glTypes = [ 372 0x1400 : "GL_BYTE", 373 0x1401 : "GL_UNSIGNED_BYTE", 374 0x1402 : "GL_SHORT", 375 0x1403 : "GL_UNSIGNED_SHORT", 376 0x1404 : "GL_INT", 377 0x1405 : "GL_UNSIGNED_INT", 378 0x1406 : "GL_FLOAT", 379 0x140A : "GL_DOUBLE",]; 380 }