1 /* 2 Copyright (c) 2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.graphics.shader; 29 30 import std.stdio; 31 import std.string; 32 33 import dlib.core.ownership; 34 import dlib.core.memory; 35 import dlib.container.array; 36 import dlib.container.dict; 37 import dlib.math.vector; 38 import dlib.math.matrix; 39 import dlib.image.color; 40 41 import dagon.core.libs; 42 import dagon.graphics.rc; 43 import dagon.graphics.texture; 44 45 // TODO: move to separate module 46 class MappedList(T): Owner 47 { 48 DynamicArray!T data; 49 Dict!(size_t, string) indices; 50 51 this(Owner o) 52 { 53 super(o); 54 indices = New!(Dict!(size_t, string))(); 55 } 56 57 void set(string name, T val) 58 { 59 data.append(val); 60 indices[name] = data.length - 1; 61 } 62 63 T get(string name) 64 { 65 return data[indices[name]]; 66 } 67 68 ~this() 69 { 70 data.free(); 71 Delete(indices); 72 } 73 } 74 75 /** 76 A shader program class that can be shared between multiple Shaders. 77 */ 78 class ShaderProgram: Owner 79 { 80 GLuint program; 81 GLuint vertexShader; 82 GLuint fragmentShader; 83 84 this(string vertexShaderSrc, string fragmentShaderSrc, Owner o) 85 { 86 super(o); 87 88 const(char*)pvs = vertexShaderSrc.ptr; 89 const(char*)pfs = fragmentShaderSrc.ptr; 90 91 char[1000] infobuffer = 0; 92 int infobufferlen = 0; 93 94 vertexShader = glCreateShader(GL_VERTEX_SHADER); 95 glShaderSource(vertexShader, 1, &pvs, null); 96 glCompileShader(vertexShader); 97 GLint success = 0; 98 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); 99 if (!success) 100 { 101 GLint logSize = 0; 102 glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logSize); 103 glGetShaderInfoLog(vertexShader, 999, &logSize, infobuffer.ptr); 104 writeln("Error in vertex shader:"); 105 writeln(infobuffer[0..logSize]); 106 } 107 108 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 109 glShaderSource(fragmentShader, 1, &pfs, null); 110 glCompileShader(fragmentShader); 111 success = 0; 112 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 113 if (!success) 114 { 115 GLint logSize = 0; 116 glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logSize); 117 glGetShaderInfoLog(fragmentShader, 999, &logSize, infobuffer.ptr); 118 writeln("Error in fragment shader:"); 119 writeln(infobuffer[0..logSize]); 120 } 121 122 program = glCreateProgram(); 123 glAttachShader(program, vertexShader); 124 glAttachShader(program, fragmentShader); 125 glLinkProgram(program); 126 } 127 128 void bind() 129 { 130 glUseProgram(program); 131 } 132 133 void unbind() 134 { 135 glUseProgram(0); 136 } 137 } 138 139 /** 140 A shader class that wraps OpenGL shader creation and uniform initialization. 141 */ 142 abstract class BaseShaderParameter: Owner 143 { 144 Shader shader; 145 string name; 146 GLint location; 147 148 this(Shader shader, string name) 149 { 150 super(shader); 151 this.shader = shader; 152 this.name = name; 153 } 154 155 void initUniform(); 156 void bind(); 157 void unbind(); 158 } 159 160 enum ShaderType 161 { 162 Vertex, 163 Fragment 164 } 165 166 class ShaderSubroutine: BaseShaderParameter 167 { 168 ShaderType shaderType; 169 GLint location; 170 GLuint index; 171 string subroutineName; 172 173 this(Shader shader, ShaderType shaderType, string name, string subroutineName) 174 { 175 super(shader, name); 176 this.shaderType = shaderType; 177 this.subroutineName = subroutineName; 178 initUniform(); 179 } 180 181 override void initUniform() 182 { 183 if (shaderType == ShaderType.Vertex) 184 { 185 location = glGetSubroutineUniformLocation(shader.program.program, GL_VERTEX_SHADER, toStringz(name)); 186 index = glGetSubroutineIndex(shader.program.program, GL_VERTEX_SHADER, toStringz(subroutineName)); 187 } 188 else if (shaderType == ShaderType.Fragment) 189 { 190 location = glGetSubroutineUniformLocation(shader.program.program, GL_FRAGMENT_SHADER, toStringz(name)); 191 index = glGetSubroutineIndex(shader.program.program, GL_FRAGMENT_SHADER, toStringz(subroutineName)); 192 } 193 } 194 195 override void bind() 196 { 197 if (shaderType == ShaderType.Vertex) 198 { 199 if (location != -1) 200 shader.vertexSubroutineIndices[location] = index; 201 } 202 else if (shaderType == ShaderType.Fragment) 203 { 204 if (location != -1) 205 shader.fragmentSubroutineIndices[location] = index; 206 } 207 } 208 209 override void unbind() 210 { 211 } 212 } 213 214 class ShaderParameter(T): BaseShaderParameter 215 if (is(T == bool) || 216 is(T == int) || 217 is(T == float) || 218 is(T == Vector2f) || 219 is(T == Vector3f) || 220 is(T == Vector4f) || 221 is(T == Color4f) || 222 is(T == Matrix4x4f)) 223 { 224 T* source; 225 T value; 226 T delegate() callback; 227 228 this(Shader shader, string name, T* source) 229 { 230 super(shader, name); 231 this.source = source; 232 this.callback = null; 233 initUniform(); 234 } 235 236 this(Shader shader, string name, T value) 237 { 238 super(shader, name); 239 this.source = null; 240 this.value = value; 241 this.callback = null; 242 initUniform(); 243 } 244 245 this(Shader shader, string name, T delegate() callback) 246 { 247 super(shader, name); 248 this.source = null; 249 this.value = value; 250 this.callback = callback; 251 initUniform(); 252 } 253 254 override void initUniform() 255 { 256 location = glGetUniformLocation(shader.program.program, toStringz(name)); 257 } 258 259 override void bind() 260 { 261 if (callback) 262 value = callback(); 263 else if (source) 264 value = *source; 265 266 static if (is(T == bool) || is(T == int)) 267 { 268 glUniform1i(location, value); 269 } 270 else static if (is(T == float)) 271 { 272 glUniform1f(location, value); 273 } 274 else static if (is(T == Vector2f)) 275 { 276 glUniform2fv(location, 1, value.arrayof.ptr); 277 } 278 else static if (is(T == Vector3f)) 279 { 280 glUniform3fv(location, 1, value.arrayof.ptr); 281 } 282 else static if (is(T == Vector4f)) 283 { 284 glUniform4fv(location, 1, value.arrayof.ptr); 285 } 286 else static if (is(T == Color4f)) 287 { 288 glUniform4fv(location, 1, value.arrayof.ptr); 289 } 290 else static if (is(T == Matrix4x4f)) 291 { 292 glUniformMatrix4fv(location, 1, GL_FALSE, value.arrayof.ptr); 293 } 294 } 295 296 override void unbind() 297 { 298 //TODO 299 } 300 } 301 302 class Shader: Owner 303 { 304 ShaderProgram program; 305 MappedList!BaseShaderParameter parameters; 306 GLuint[] vertexSubroutineIndices; 307 GLuint[] fragmentSubroutineIndices; 308 309 this(ShaderProgram program, Owner o) 310 { 311 super(o); 312 this.program = program; 313 this.parameters = New!(MappedList!BaseShaderParameter)(this); 314 } 315 316 ShaderSubroutine setParameterSubroutine(string name, ShaderType shaderType, string subroutineName) 317 { 318 if (name in parameters.indices) 319 { 320 auto sp = cast(ShaderSubroutine)parameters.get(name); 321 if (sp is null) 322 { 323 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 324 return null; 325 } 326 sp.shaderType = shaderType; 327 sp.subroutineName = subroutineName; 328 sp.initUniform(); 329 return sp; 330 } 331 else 332 { 333 auto sp = New!ShaderSubroutine(this, shaderType, name, subroutineName); 334 parameters.set(name, sp); 335 return sp; 336 } 337 } 338 339 ShaderParameter!T setParameter(T)(string name, T val) 340 { 341 if (name in parameters.indices) 342 { 343 auto sp = cast(ShaderParameter!T)parameters.get(name); 344 if (sp is null) 345 { 346 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 347 return null; 348 } 349 350 sp.value = val; 351 sp.source = null; 352 return sp; 353 } 354 else 355 { 356 auto sp = New!(ShaderParameter!T)(this, name, val); 357 parameters.set(name, sp); 358 return sp; 359 } 360 } 361 362 ShaderParameter!T setParameterRef(T)(string name, ref T val) 363 { 364 if (name in parameters.indices) 365 { 366 auto sp = cast(ShaderParameter!T)parameters.get(name); 367 if (sp is null) 368 { 369 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 370 return null; 371 } 372 373 sp.source = &val; 374 return sp; 375 } 376 else 377 { 378 auto sp = New!(ShaderParameter!T)(this, name, &val); 379 parameters.set(name, sp); 380 return sp; 381 } 382 } 383 384 ShaderParameter!T setParameterCallback(T)(string name, T delegate() val) 385 { 386 if (name in parameters.indices) 387 { 388 auto sp = cast(ShaderParameter!T)parameters.get(name); 389 if (sp is null) 390 { 391 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 392 return null; 393 } 394 395 sp.callback = val; 396 return sp; 397 } 398 else 399 { 400 auto sp = New!(ShaderParameter!T)(this, name, val); 401 parameters.set(name, sp); 402 return sp; 403 } 404 } 405 406 T getParameter(T)(string name) 407 { 408 if (name in parameters.indices) 409 { 410 auto sp = cast(ShaderParameter!T)parameters.get(name); 411 if (sp is null) 412 { 413 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 414 return T.init; 415 } 416 417 if (sp.source) 418 return *sp.source; 419 else 420 return sp.value; 421 } 422 else 423 { 424 writefln("Warning: unknown shader parameter \"%s\"", name); 425 return T.init; 426 } 427 } 428 429 void bind(RenderingContext* rc) 430 { 431 program.bind(); 432 433 GLsizei n; 434 glGetProgramStageiv(program.program, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &n); 435 if (n > 0 && n != vertexSubroutineIndices.length) 436 vertexSubroutineIndices = New!(GLuint[])(n); 437 438 glGetProgramStageiv(program.program, GL_FRAGMENT_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &n); 439 if (n > 0 && n != fragmentSubroutineIndices.length) 440 fragmentSubroutineIndices = New!(GLuint[])(n); 441 442 foreach(v; parameters.data) 443 { 444 v.bind(); 445 } 446 447 if (vertexSubroutineIndices.length) 448 glUniformSubroutinesuiv(GL_VERTEX_SHADER, cast(uint)vertexSubroutineIndices.length, vertexSubroutineIndices.ptr); 449 450 if (fragmentSubroutineIndices.length) 451 glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, cast(uint)fragmentSubroutineIndices.length, fragmentSubroutineIndices.ptr); 452 } 453 454 void unbind(RenderingContext* rc) 455 { 456 foreach(v; parameters.data) 457 { 458 v.unbind(); 459 } 460 program.unbind(); 461 } 462 463 ~this() 464 { 465 if (vertexSubroutineIndices.length) 466 Delete(vertexSubroutineIndices); 467 if (fragmentSubroutineIndices.length) 468 Delete(fragmentSubroutineIndices); 469 } 470 }