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.shaderloader; 44 import dagon.graphics.texture; 45 46 // TODO: move to separate module 47 class MappedList(T): Owner 48 { 49 DynamicArray!T data; 50 Dict!(size_t, string) indices; 51 52 this(Owner o) 53 { 54 super(o); 55 indices = New!(Dict!(size_t, string))(); 56 } 57 58 void set(string name, T val) 59 { 60 data.append(val); 61 indices[name] = data.length - 1; 62 } 63 64 T get(string name) 65 { 66 return data[indices[name]]; 67 } 68 69 ~this() 70 { 71 data.free(); 72 Delete(indices); 73 } 74 } 75 76 /** 77 A shader program class that can be shared between multiple Shaders. 78 */ 79 class ShaderProgram: Owner 80 { 81 immutable GLuint program; 82 83 this(string vertexShaderSrc, string fragmentShaderSrc, Owner o) 84 { 85 super(o); 86 87 GLuint vert = compileShader(vertexShaderSrc, ShaderStage.vertex); 88 GLuint frag = compileShader(fragmentShaderSrc, ShaderStage.fragment); 89 if (vert != 0 && frag != 0) 90 program = linkShaders(vert, frag); 91 } 92 93 void bind() 94 { 95 glUseProgram(program); 96 } 97 98 void unbind() 99 { 100 glUseProgram(0); 101 } 102 } 103 104 /** 105 A shader class that wraps OpenGL shader creation and uniform initialization. 106 */ 107 abstract class BaseShaderParameter: Owner 108 { 109 Shader shader; 110 string name; 111 GLint location; 112 113 this(Shader shader, string name) 114 { 115 super(shader); 116 this.shader = shader; 117 this.name = name; 118 } 119 120 void initUniform(); 121 void bind(); 122 void unbind(); 123 } 124 125 enum ShaderType 126 { 127 Vertex, 128 Fragment 129 } 130 131 class ShaderSubroutine: BaseShaderParameter 132 { 133 ShaderType shaderType; 134 GLint location; 135 GLuint index; 136 string subroutineName; 137 138 this(Shader shader, ShaderType shaderType, string name, string subroutineName) 139 { 140 super(shader, name); 141 this.shaderType = shaderType; 142 this.subroutineName = subroutineName; 143 initUniform(); 144 } 145 146 override void initUniform() 147 { 148 if (shaderType == ShaderType.Vertex) 149 { 150 location = glGetSubroutineUniformLocation(shader.program.program, GL_VERTEX_SHADER, toStringz(name)); 151 index = glGetSubroutineIndex(shader.program.program, GL_VERTEX_SHADER, toStringz(subroutineName)); 152 } 153 else if (shaderType == ShaderType.Fragment) 154 { 155 location = glGetSubroutineUniformLocation(shader.program.program, GL_FRAGMENT_SHADER, toStringz(name)); 156 index = glGetSubroutineIndex(shader.program.program, GL_FRAGMENT_SHADER, toStringz(subroutineName)); 157 } 158 } 159 160 override void bind() 161 { 162 if (shaderType == ShaderType.Vertex) 163 { 164 if (location != -1) 165 shader.vertexSubroutineIndices[location] = index; 166 } 167 else if (shaderType == ShaderType.Fragment) 168 { 169 if (location != -1) 170 shader.fragmentSubroutineIndices[location] = index; 171 } 172 } 173 174 override void unbind() 175 { 176 } 177 } 178 179 class ShaderParameter(T): BaseShaderParameter 180 if (is(T == bool) || 181 is(T == int) || 182 is(T == float) || 183 is(T == Vector2f) || 184 is(T == Vector3f) || 185 is(T == Vector4f) || 186 is(T == Color4f) || 187 is(T == Matrix4x4f)) 188 { 189 T* source; 190 T value; 191 T delegate() callback; 192 193 this(Shader shader, string name, T* source) 194 { 195 super(shader, name); 196 this.source = source; 197 this.callback = null; 198 initUniform(); 199 } 200 201 this(Shader shader, string name, T value) 202 { 203 super(shader, name); 204 this.source = null; 205 this.value = value; 206 this.callback = null; 207 initUniform(); 208 } 209 210 this(Shader shader, string name, T delegate() callback) 211 { 212 super(shader, name); 213 this.source = null; 214 this.value = value; 215 this.callback = callback; 216 initUniform(); 217 } 218 219 override void initUniform() 220 { 221 location = glGetUniformLocation(shader.program.program, toStringz(name)); 222 } 223 224 override void bind() 225 { 226 if (callback) 227 value = callback(); 228 else if (source) 229 value = *source; 230 231 static if (is(T == bool) || is(T == int)) 232 { 233 glUniform1i(location, value); 234 } 235 else static if (is(T == float)) 236 { 237 glUniform1f(location, value); 238 } 239 else static if (is(T == Vector2f)) 240 { 241 glUniform2fv(location, 1, value.arrayof.ptr); 242 } 243 else static if (is(T == Vector3f)) 244 { 245 glUniform3fv(location, 1, value.arrayof.ptr); 246 } 247 else static if (is(T == Vector4f)) 248 { 249 glUniform4fv(location, 1, value.arrayof.ptr); 250 } 251 else static if (is(T == Color4f)) 252 { 253 glUniform4fv(location, 1, value.arrayof.ptr); 254 } 255 else static if (is(T == Matrix4x4f)) 256 { 257 glUniformMatrix4fv(location, 1, GL_FALSE, value.arrayof.ptr); 258 } 259 } 260 261 override void unbind() 262 { 263 //TODO 264 } 265 } 266 267 class Shader: Owner 268 { 269 ShaderProgram program; 270 MappedList!BaseShaderParameter parameters; 271 GLuint[] vertexSubroutineIndices; 272 GLuint[] fragmentSubroutineIndices; 273 274 this(ShaderProgram program, Owner o) 275 { 276 super(o); 277 this.program = program; 278 this.parameters = New!(MappedList!BaseShaderParameter)(this); 279 } 280 281 ShaderSubroutine setParameterSubroutine(string name, ShaderType shaderType, string subroutineName) 282 { 283 if (name in parameters.indices) 284 { 285 auto sp = cast(ShaderSubroutine)parameters.get(name); 286 if (sp is null) 287 { 288 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 289 return null; 290 } 291 sp.shaderType = shaderType; 292 sp.subroutineName = subroutineName; 293 sp.initUniform(); 294 return sp; 295 } 296 else 297 { 298 auto sp = New!ShaderSubroutine(this, shaderType, name, subroutineName); 299 parameters.set(name, sp); 300 return sp; 301 } 302 } 303 304 ShaderParameter!T setParameter(T)(string name, T val) 305 { 306 if (name in parameters.indices) 307 { 308 auto sp = cast(ShaderParameter!T)parameters.get(name); 309 if (sp is null) 310 { 311 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 312 return null; 313 } 314 315 sp.value = val; 316 sp.source = null; 317 return sp; 318 } 319 else 320 { 321 auto sp = New!(ShaderParameter!T)(this, name, val); 322 parameters.set(name, sp); 323 return sp; 324 } 325 } 326 327 ShaderParameter!T setParameterRef(T)(string name, ref T val) 328 { 329 if (name in parameters.indices) 330 { 331 auto sp = cast(ShaderParameter!T)parameters.get(name); 332 if (sp is null) 333 { 334 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 335 return null; 336 } 337 338 sp.source = &val; 339 return sp; 340 } 341 else 342 { 343 auto sp = New!(ShaderParameter!T)(this, name, &val); 344 parameters.set(name, sp); 345 return sp; 346 } 347 } 348 349 ShaderParameter!T setParameterCallback(T)(string name, T delegate() val) 350 { 351 if (name in parameters.indices) 352 { 353 auto sp = cast(ShaderParameter!T)parameters.get(name); 354 if (sp is null) 355 { 356 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 357 return null; 358 } 359 360 sp.callback = val; 361 return sp; 362 } 363 else 364 { 365 auto sp = New!(ShaderParameter!T)(this, name, val); 366 parameters.set(name, sp); 367 return sp; 368 } 369 } 370 371 T getParameter(T)(string name) 372 { 373 if (name in parameters.indices) 374 { 375 auto sp = cast(ShaderParameter!T)parameters.get(name); 376 if (sp is null) 377 { 378 writefln("Warning: type mismatch for shader parameter \"%s\"", name); 379 return T.init; 380 } 381 382 if (sp.source) 383 return *sp.source; 384 else 385 return sp.value; 386 } 387 else 388 { 389 writefln("Warning: unknown shader parameter \"%s\"", name); 390 return T.init; 391 } 392 } 393 394 void bindProgram() 395 { 396 program.bind(); 397 } 398 399 void unbindProgram() 400 { 401 program.unbind(); 402 } 403 404 void bind(RenderingContext* rc) 405 { 406 if (rc.rebindShaderProgram) 407 bindProgram(); 408 409 GLsizei n; 410 glGetProgramStageiv(program.program, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &n); 411 if (n > 0 && n != vertexSubroutineIndices.length) 412 vertexSubroutineIndices = New!(GLuint[])(n); 413 414 glGetProgramStageiv(program.program, GL_FRAGMENT_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &n); 415 if (n > 0 && n != fragmentSubroutineIndices.length) 416 fragmentSubroutineIndices = New!(GLuint[])(n); 417 418 foreach(v; parameters.data) 419 { 420 v.bind(); 421 } 422 423 if (vertexSubroutineIndices.length) 424 glUniformSubroutinesuiv(GL_VERTEX_SHADER, cast(uint)vertexSubroutineIndices.length, vertexSubroutineIndices.ptr); 425 426 if (fragmentSubroutineIndices.length) 427 glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, cast(uint)fragmentSubroutineIndices.length, fragmentSubroutineIndices.ptr); 428 } 429 430 void unbind(RenderingContext* rc) 431 { 432 foreach(v; parameters.data) 433 { 434 v.unbind(); 435 } 436 437 if (rc.rebindShaderProgram) 438 unbindProgram(); 439 } 440 441 void validate() 442 { 443 glValidateProgram(program.program); 444 } 445 446 ~this() 447 { 448 if (vertexSubroutineIndices.length) 449 Delete(vertexSubroutineIndices); 450 if (fragmentSubroutineIndices.length) 451 Delete(fragmentSubroutineIndices); 452 } 453 }