1 /+ 2 version `VADGL_DebugGLCalls`: 3 prints all OpenGL calls 4 5 version `VADGL_EnableChecks`: 6 This allows calling `glGetError` after every OpenGL call. This might have a 7 significant performance hit which is why it's only enabled by default on debug 8 versions 9 10 version `VADGL_DisableChecks`: 11 Disables `glGetError` checks. This only affects debug builds since cheks 12 are only enabled by default on debug builds. 13 14 - Most functions allocate because of strings for error messages 15 - Must functions don't throw here. 16 All functions which can fail should return a Result!Type 17 Exceptions are only used in a handful of functions dealing with files 18 - Do not overcomplicate anything 19 - Think about OpenGL support fucking later 20 +/ 21 module vadgl.gl3; 22 23 import std.conv : to; 24 import std.format : format; 25 import std.meta : AliasSeq, allSatisfy; 26 27 // OpenGL bindings 28 import bindbc.opengl; 29 import vadgl.types; 30 import vadgl.error; 31 32 import std.string : toStringz; 33 34 import std.algorithm : endsWith, among; 35 36 import result; 37 38 39 version (VADGL_DisableChecks) { 40 enum bool checkGLCalls = false; 41 } 42 else version (VADGL_EnableChecks) { 43 enum bool checkGLCalls = true; 44 } 45 else debug { 46 enum bool checkGLCalls = true; 47 } 48 else { 49 enum bool checkGLCalls = false; 50 } 51 52 enum MAX_GL_VARIABLE_NAME = 256; 53 54 // NOTE: Currently used 55 private auto trust(alias fnc, Args...)(Args args) @trusted => fnc(args); 56 57 private { 58 bool is_integral(GLType type) pure => (type >= GLType.BYTE && type <= GLType.UINT); 59 bool is_floating(GLType type) pure => (GLType.FLOAT || GLType.HALF_FLOAT|| GLType.DOUBLE); 60 bool is_base_type(GLType type) pure => (is_integral(type) || is_floating(type)); 61 62 bool is_vector(GLType type) pure => type.to!string[0..$-1].endsWith("VEC"); 63 } 64 65 GLType to_gltype(GLenum type) => cast(GLType)type; 66 67 private template toDType(GLType type) // make this work with vector and matrix types 68 { 69 mixin("alias toDType = %s;".format(type.to!string().toLower())); 70 } 71 72 // Handle base types 73 private template isGLSLBaseType(T) 74 { 75 // Maybe take into consideration double 76 static if (is(T == float)) { 77 enum isGLSLBaseType = true; 78 } 79 else static if (is(T == bool)) { 80 enum isGLSLBaseType = true; 81 } 82 // else static if (isIntegral!T) { // TODO: maybe use integral 83 // if integral convert to int 84 else static if (is(T == int) || is(T == uint) || is(T == byte) || is(T == ubyte)) { 85 enum isGLSLBaseType = true; 86 } 87 else { 88 enum isGLSLBaseType = false; 89 } 90 } 91 92 private template isGLVector(T) 93 { 94 static if (is(T == V[N], V, size_t N)) { 95 static if (N <= 4) 96 enum bool isGLVector = true; 97 else 98 enum bool isGLVector = false; 99 } 100 else 101 enum bool isGLVector = false; 102 } 103 104 // Doesn't work with struct types 105 private template TypeInfoGLSL(T) 106 { 107 static if (is(T == Bm[Nm][Mm], Bm, size_t Nm, size_t Mm)) { // is matrix 108 // TODO: If size Nm or Mm is <=4 it's invalid 109 static if (isGLSLBaseType!Bm) { 110 alias TypeInfoGLSL = AliasSeq!("matrix", Bm, Nm, Mm); 111 } 112 else { 113 alias TypeInfoGLSL = AliasSeq!("invalid", void, 0, 0); 114 } 115 } 116 else static if (is(T == Bv[Nv], Bv, size_t Nv)) { // is vector 117 static if (isGLSLBaseType!Bv) { 118 static if (Nv > 4) 119 alias TypeInfoGLSL = AliasSeq!("array", Bv, Nv, 1); 120 else 121 alias TypeInfoGLSL = AliasSeq!("vector", Bv, Nv, 1); 122 } 123 else { 124 alias TypeInfoGLSL = AliasSeq!("invalid", void, 0, 0); 125 } 126 } 127 else { 128 static if (isGLSLBaseType!T) { 129 alias TypeInfoGLSL = AliasSeq!("base", T, 1, 1); 130 } 131 else { 132 alias TypeInfoGLSL = AliasSeq!("invalid", void, 0, 0); 133 } 134 } 135 } 136 137 // TODO: make this work with matrix types 138 template to_gltype(T) 139 { 140 import std.string : toUpper; 141 142 alias TInfo = TypeInfoGLSL!T; 143 144 enum string kind = TInfo[0]; 145 146 static assert(kind != "invalid"); 147 148 alias BT = TInfo[1]; 149 enum size_t N = TInfo[2]; 150 enum size_t M = TInfo[3]; 151 152 static if (kind == "vector" && N <= 4) { 153 static immutable string type_name = BT.stringof.toUpper; 154 enum string dims = N.to!string; 155 enum string prefix = (type_name[0] == 'F') ? "" : type_name[0..1]; 156 mixin("enum GLType to_gltype = GLType."~prefix~"VEC"~dims~";"); 157 } 158 else // base type 159 mixin("enum GLType to_gltype = GLType.%s;".format(T.stringof.toUpper())); 160 } 161 162 /++ 163 Returns whether `param` is a parameter that can be passed to `glGetShaderIv` 164 165 Params: 166 param = A GLParam or GLenum represting an parameter for a Shader or Program 167 +/ 168 @safe nothrow pure 169 bool is_shader_param(GLParam param) 170 { 171 with(GLParam) 172 return (param == SHADER_TYPE || param == DELETE_STATUS || 173 param == COMPILE_STATUS || param == INFO_LOG_LENGTH || 174 param == SHADER_SOURCE_LENGTH 175 ); 176 } 177 178 /++ 179 Returns whether `param` is a parameter that can be passed to `glGetProgramiv` 180 181 Params: 182 param = A parameter for a Shader or Program 183 +/ 184 @safe nothrow pure 185 bool is_program_param(GLParam param) 186 { 187 with(GLParam) 188 return (param == GL_DELETE_STATUS || param == GL_LINK_STATUS || 189 param == GL_VALIDATE_STATUS || param == GL_INFO_LOG_LENGTH || 190 param == GL_ATTACHED_SHADERS 191 ); 192 } 193 194 /// Clear all opengl error 195 @trusted @nogc nothrow 196 private static void opengl_clear_errors() 197 { 198 while(glGetError() != GL_NO_ERROR) {} 199 } 200 201 /// Return opengl error 202 @trusted nothrow 203 private GLInternalError opengl_get_error() 204 { 205 while (int my_error = glGetError()) 206 return cast(GLInternalError)my_error; 207 return GLInternalError.NO_ERROR; 208 } 209 210 /++ 211 Run OpenGL function and log it on version `VADGL_DebugGLCalls` 212 Use `gl_wrap` instead for error handling. 213 214 Returns: The result of the opengl function call 215 +/ 216 template gl_call(alias fnc) 217 { 218 private import std.traits : isSomeFunction, ReturnType; 219 static assert(isSomeFunction!fnc, "`fnc` must be a function"); 220 221 enum string fnc_name = __traits(identifier, fnc); 222 alias T = ReturnType!fnc; 223 224 T gl_call(Args...)(Args args) 225 { 226 version(VADGL_DebugGLCalls) 227 { 228 // TODO: Maybe use `std.logger` instead. Also make a way to set debug file 229 import std.stdio; 230 import std.conv : to; 231 import std.exception : assumeWontThrow; 232 // So that I don't have to remove `nothrow` from gl_wrap 233 string args_str = ""; 234 // this could be done better 235 static foreach(i, arg; args) { 236 args_str ~= assumeWontThrow(arg.to!string); 237 static if (i < cast(int)(args.length)-1) 238 args_str ~= ", "; 239 } 240 assumeWontThrow(writeln(fnc_name, "(", args_str, ")")); 241 } 242 // Should work even if `T` is void 243 return fnc(args); 244 } 245 } 246 247 /++ 248 Run opengl function `fnc_` and check for OpenGL errors 249 250 Returns: `Result!(GLInternalError, T)` where `T` is the return type of 251 `fnc_` 252 +/ 253 template gl_wrap(alias fnc_) 254 { 255 private import std.traits : isSomeFunction, ReturnType; 256 257 static if (!isSomeFunction!fnc_) 258 alias fnc = fnc_!Args; 259 else 260 alias fnc = fnc_; 261 262 enum string fnc_name = __traits(identifier, fnc); 263 alias T = ReturnType!fnc; 264 alias ErrorType = InternalError; 265 alias ResultT = Result!(ErrorType, T); 266 267 @trusted nothrow 268 ResultT gl_wrap(Args...)(Args args) 269 { 270 ResultT res = ResultT(); 271 272 static if (checkGLCalls) 273 opengl_clear_errors(); 274 275 static if (!is(T == void)) { 276 res = ResultT(gl_call!fnc(args)); 277 } 278 else { 279 gl_call!fnc(args); 280 } 281 282 static if (checkGLCalls) 283 if (GLInternalError err = opengl_get_error()) 284 return ResultT(ErrorType(cast(GLFuncEnum)fnc_name, err)); 285 286 return res; 287 } 288 } 289 290 @safe @nogc nothrow pure 291 private char[10] uint_to_char_buff(uint x) 292 { 293 import std.conv: toChars; 294 char[10] buff = 0; 295 auto char_range = x.toChars(); 296 assert(char_range.length < buff.length); 297 298 int i = 0; 299 foreach (char c; char_range) { 300 buff[i++] = c; 301 } 302 return buff; 303 } 304 305 @safe @nogc nothrow pure 306 private bool member_in_enum(T)(T value) if (is(T == enum)) 307 { 308 import std.traits : EnumMembers; 309 switch(value) 310 { 311 static foreach(member; EnumMembers!T) { 312 case member: 313 return true; 314 } 315 default: 316 return false; 317 } 318 } 319 320 /++ 321 Return string representation of enum member. 322 For example `enum_to_str(MyEnum.A)` return string `"A"` 323 324 NOTES: 325 $(LIST 326 * this affects compilation times for big enums(>50 members) 327 * Doesn't work if enum has duplicates 328 * Doesn't work if `value` is not a valid enum member 329 ) 330 +/ 331 @safe @nogc nothrow pure 332 private string enum_to_str(T)(T value) if (is(T == enum)) 333 { 334 import std.conv : to; 335 import std.traits : EnumMembers; 336 337 final switch(value) 338 { 339 static foreach(member; EnumMembers!T) { 340 case member: 341 return member.stringof; 342 } 343 } 344 } 345 346 // None of `Shader.Type` members are more than 8 characters, but reserve 32 bytes 347 // anyway. Better safe than sorry 348 @safe @nogc nothrow pure 349 private immutable(char)[32] to_char_buff(Shader.Type type) 350 { 351 char[32] buff = 0; 352 if (type.member_in_enum()) { 353 string str = type.enum_to_str(); 354 assert(str.length <= 32); // Should never happen 355 buff[0..str.length] = str[]; 356 } 357 else { 358 buff[0..10] = uint_to_char_buff(cast(uint)type); 359 } 360 return buff; 361 } 362 363 364 private enum isShader(T) = is(T == Shader); 365 private alias isShaderSeq(Args...) = allSatisfy!(isShader, Args); 366 367 /+++ 368 Abstraction over shader 369 +++/ 370 struct Shader 371 { 372 import std.bitmanip : bitfields; 373 // TODO: Move this to `types.d` 374 enum Type { 375 VERTEX = GL_VERTEX_SHADER, 376 GEOMETRY = GL_GEOMETRY_SHADER, 377 FRAGMENT = GL_FRAGMENT_SHADER, 378 } 379 380 private { 381 uint id_ = 0; 382 string name_ = ""; 383 384 Type type_; 385 386 mixin(bitfields!( 387 bool, "is_created_", 1, 388 bool, "is_source_set_", 1, 389 bool, "is_compiled_", 1, 390 ubyte, "_padding", 5, 391 )); 392 } 393 394 @property @safe @nogc nothrow { 395 // you can look but not touch ;) 396 bool is_created() const => this.is_created_; 397 bool is_source_set() const => this.is_source_set_; 398 bool is_compiled() const => this.is_compiled_; 399 400 Type type() const => type_; 401 uint id() const => this.id_; 402 string name() const => this.name_; 403 } 404 405 // This doesn't throw but can't be marked as nothrow bc of `format` 406 // Wrapper to glCreateShader 407 /+++ 408 Create OpenGL shader 409 +++/ 410 nothrow 411 static GLResult!Shader create_shader(string name, Type type) 412 { 413 auto res = gl_create_shader(type); 414 // error handling 415 switch(res.error.error_flag) 416 { 417 case GLError.Flag.NO_ERROR: 418 if (res.value == 0) goto default; 419 break; 420 case GLError.Flag.INVALID_ENUM: 421 return GLResult!Shader( 422 GLError( 423 GLError.Flag.INVALID_ENUM, 424 ": Type "~type.to_char_buff()~" is not a valid shader type" 425 ) 426 ); 427 default: 428 return GLResult!Shader(GLError(GLError.Flag.UNKNOWN_ERROR)); 429 } 430 Shader shader = Shader(name, type, res.value, true); 431 return GLResult!Shader(shader); 432 } 433 434 // TODO: Might want to add file and line as template params 435 /+++ 436 Create OpenGL shader and call `gl_shader_source` to set source in 437 shader object 438 +++/ 439 nothrow 440 static GLResult!Shader from_src(string name, Type type, string src) 441 { 442 GLResult!Shader shader_res = Shader.create_shader(name, type); 443 if (!shader_res.is_error()) { 444 if (auto res = shader_res.value.set_source(src)) 445 return GLResult!Shader(res.error.append_fnc()); 446 } 447 else 448 shader_res = GLResult!Shader(shader_res.error.append_fnc()); 449 return shader_res; 450 } 451 452 // TODO: 453 // maybe remove this function or put it a version block 454 static GLResult!Shader from_file(string name, Type type, string file_name) 455 { 456 import std.file; 457 458 auto shader_res = Shader.create_shader(name, type); 459 460 if (shader_res.is_error()) 461 return GLResult!Shader(shader_res.error.append_fnc()); 462 463 Shader shader = shader_res.value; 464 if (auto res = shader.set_source(std.file.readText(file_name))) 465 return GLResult!Shader(res.error.append_fnc()); 466 467 return glresult(shader); 468 } 469 470 /++ 471 Intialize shader with values. 472 473 If you have already successfully called `glCreateShader`, 474 `glShaderSource` and `glCompileShader` then you can initialize like so: 475 ```d 476 auto shader = Shader("my_shader", shader_type, shader_id, all_ok: true); 477 ``` 478 479 To actually create a shader use `Shader.create_shader` 480 +/ 481 @safe @nogc nothrow 482 this 483 (string name, Type type, uint id, bool created=false, 484 bool source_set=false, bool compiled=false, bool all_ok=false) 485 { 486 this.name_ = name; 487 this.type_ = type; 488 this.id_ = id; 489 this.is_created_ = created | all_ok; 490 this.is_source_set_ = is_source_set_ | all_ok; 491 this.is_compiled_ = is_compiled_ | all_ok; 492 } 493 494 /+ 495 Set shader source to `src` 496 +/ 497 // Kind of Wrapper to glShaderSource 498 @trusted nothrow 499 GLResult!void set_source(in string src) 500 { 501 // TODO: Add a null check for src 502 503 if (!this.is_created) 504 return glresult(GLError(GLError.Flag.INVALID_SHADER)); 505 506 if (auto res = gl_shader_source(this.id, src)) 507 // This is way longer than I expected 508 return res.error.append_fnc().glresult(); 509 510 this.is_source_set_ = true; 511 return GLError(GLError.Flag.NO_ERROR).glresult(); 512 } 513 514 /* @safe */ 515 /+++ 516 compile shader and check whether compilation is successful 517 518 Returns: GLResult!void with error info if an OpenGL error occurred 519 +++/ 520 @trusted nothrow 521 GLResult!void compile() 522 { 523 if (!is_created) 524 return GLError(GLError.Flag.INVALID_SHADER).glresult(); 525 526 if (!is_source_set) 527 return GLError(GLError.Flag.SHADER_SOURCE_NOT_SET).glresult(); 528 529 // Assume source is already set 530 // should never happen 531 if (auto res = gl_compile_shader(this.id)) 532 return res.error.append_fnc().glresult(); 533 534 GLResult!bool compile_res = this.get_param!(GLParam.COMPILE_STATUS); 535 if (compile_res) 536 return compile_res.error.append_fnc().glresult(); 537 538 // NOTE: add get_info_log 539 if(!compile_res.value) { 540 // Ignore retrun value, no error should occur at this point 541 return GLError( 542 GLError.Flag.SHADER_COMPILATION_ERROR, 543 ": Failed to compile `"~this.name~"` shader\n" ~ 544 "\tHINT: Run get_info_log() to get extra information" 545 ).glresult(); 546 } 547 548 this.is_compiled_ = true; 549 return GLError().glresult(); 550 } 551 552 /// ditto 553 @safe nothrow 554 GLResult!void compile_src(in string src) 555 { 556 if (auto res = this.set_source(src)) 557 return res.error.append_fnc().glresult(); 558 559 if (auto res = this.compile()) 560 return res.error.append_fnc().glresult(); 561 562 return GLError().glresult(); 563 } 564 565 /// Same as `Shader.compile` except this function can throw 566 @safe 567 GLResult!void compile_f(in string file_name) 568 { 569 import std.file; 570 // TODO: Handle exception 571 string shader_src = std.file.readText(file_name); 572 573 return this.compile_src(shader_src); 574 } 575 576 string toString() const 577 => "Shader(name: %s, type: %s, id: %d, created: %s, source_set: %s, compiled: %s)" 578 .format( 579 this.name, this.type, this.id, this.is_created, 580 this.is_source_set, this.is_compiled); 581 } 582 583 struct Program 584 { 585 import std.bitmanip : bitfields; 586 // Nothing here throws 587 nothrow: 588 589 private { 590 uint id_ = 0; 591 string name_ = ""; 592 // TODO: might add a way to cache uniforms 593 mixin(bitfields!( 594 bool, "is_created_", 1, 595 bool, "is_attached_", 1, 596 bool, "is_linked_", 1, 597 bool, "is_validated_", 1, 598 ubyte, "_padding", 4, 599 )); 600 } 601 602 // getters 603 @property @safe @nogc { 604 bool is_created() const => this.is_created_; 605 bool is_attached() const => this.is_attached_; 606 bool is_linked() const => this.is_linked_; 607 bool is_validated() const => this.is_validated_; 608 609 uint id() const => this.id_; 610 string name() const => this.name_; 611 } 612 613 this(string program_name, int program_id, bool created = false) 614 { 615 this.name_ = program_name; 616 this.id_ = program_id; 617 this.is_created_ = created; 618 } 619 620 /+++ 621 Creates program 622 Returns: `GLResult!Program` returns valid program or 623 `GLError.Flag.UNKNOWN_ERROR` if an error ocurred 624 +++/ 625 @trusted 626 static GLResult!Program create_program(string program_name) 627 { 628 // Shouldn't be able to fail, 629 int prog_id = gl_create_program(); 630 631 if (prog_id == 0) { // something went wrong 632 return GLResult!Program(GLError(GLError.Flag.UNKNOWN_ERROR)); 633 } 634 635 Program program = Program(program_name, prog_id, true); 636 return program.glresult(); 637 } 638 639 /+++ 640 Use null program. same as `gl_program_use(0)` 641 +++/ 642 @safe 643 static void use_empty() => cast(void)Program().use(); 644 645 /// Attach 1 shader use [Program.attach] instead 646 @trusted 647 private GLResult!void attach_(Shader s) 648 { 649 if (!this.is_created) { 650 return GLError(GLError.Flag.INVALID_PROGRAM).glresult(); 651 } 652 653 // program.id should be a valid OpenGL program at this point 654 switch(gl_attach_shader(this.id, s.id).error.error_flag) { 655 case GLError.Flag.NO_ERROR: 656 this.is_attached_ = true; 657 return glresult(GLError.NO_ERROR); 658 659 case GLError.Flag.INVALID_OPERATION: 660 { 661 if (!s.is_created) { 662 return glresult(GLError("glAttachShader", GLError.Flag.INVALID_SHADER)); 663 } 664 else if (!s.is_compiled) { 665 return glresult(GLError("glAttachShader", GLError.Flag.INVALID_SHADER)); 666 } 667 else { // if shader `s` is compiled this is the only possibility 668 return glresult(GLError("glAttachShader", GLError.Flag.SHADER_ALREADY_ATTACHED)); 669 } 670 } 671 default: 672 return glresult(GLError.UNKNOWN_ERROR); 673 } 674 } 675 676 // Wrapper to glAttachShader 677 // TODO: maybe check GL_ATTACHED_SHADERS to check if number of shaders 678 // attached is what we expect 679 /++ 680 attach shaders to Program 681 Params: 682 shaders = `AliasSeq` of shaders 683 684 Returns: `GLResult!void` with `GLError`: 685 $(LIST 686 * `GLError.Flag.NO_ERROR`: 687 If attached correctly and there are no errors 688 * `GLError.Flag.INVALID_PROGRAM`: 689 if program hasn't been initialized properly 690 * `GLError.Flag.INVALID_SHADER`: 691 if shader hasn't been initialized 692 if shader hasn't been compiled succesfully 693 * `GLError.Flag.SHADER_ALREADY_ATTACHED`: 694 if Shader `s` has already been attached to program 695 ) 696 697 Example: 698 --- 699 // Initialize program and shaders 700 Program program = ... 701 Shader vertex_shader = ... 702 Shader frag_shader = ... 703 704 // Call [gl_attach_shader] per shader and throw on error 705 program.attach(vertex_shader, frag_shader).throw_on_error; 706 --- 707 +/ 708 @trusted 709 GLResult!void attach(Args...)(Args shaders) if (Args.length > 0 && isShaderSeq!Args) 710 { 711 foreach (shader; shaders) { 712 if (auto res = this.attach_(shader)) return res; 713 } 714 return GLError.NO_ERROR.glresult(); 715 } 716 717 // TODO: Maybe check extra cases 718 /++ 719 Link shaders attached to program 720 Returns: `GLResult!void` with `GLError`: 721 $(LIST 722 * `GLError.Flag.NO_ERROR`: 723 if no opengl errors occurred 724 * `GLError.Flag.INVALID_PROGRAM`: 725 if program is valid 726 * `GLError.Flag.UNKNOWN_ERROR`: 727 if something unexpected went wrong 728 ) 729 +/ 730 @trusted 731 GLResult!void link() 732 { 733 if (!this.is_attached) { // not a single shader is attached 734 return glresult(GLError.INVALID_PROGRAM); 735 } 736 737 auto res = gl_link_program(this.id); 738 switch(res.error.error_flag) 739 { 740 case GLError.Flag.NO_ERROR: break; 741 case GLError.Flag.INVALID_OPERATION: 742 // TODO: Do something else here probably 743 return glresult(GLError.UNKNOWN_ERROR); 744 default: 745 return glresult(GLError.UNKNOWN_ERROR); 746 } 747 748 int linked = 0; 749 if (GLError error = this.get_param(GLParam.LINK_STATUS, linked)) 750 return glresult(error.append_fnc()); 751 752 if (!linked) { 753 /* string error_msg; */ 754 /* auto _ = this.get_info_log(error_msg); */ 755 return glresult(GLError(GLError.Flag.PROGRAM_LINK_ERROR)); 756 } 757 758 this.is_linked_ = true; 759 return glresult(GLError.NO_ERROR); 760 } 761 762 // Wrapper to glValidateProgram 763 // TODO: 764 // - [Add error hint] take care of potential geometry shader shenanigans later 765 /+++ 766 validate program and call `get_param` to check if validation was 767 successful 768 769 Returns: `GLResult!void` with GLError with error information if any 770 +++/ 771 @trusted 772 GLResult!void validate() 773 { 774 if (!this.is_linked) // not a single shader is attached 775 return glresult(GLError(GLError.Flag.INVALID_PROGRAM)); 776 777 int validated = 0; 778 // TODO: Make gl_validate_program function 779 // Shouldn't raise any errors 780 gl_call!glValidateProgram(this.id); 781 782 if (GLError error = this.get_param(GLParam.VALIDATE_STATUS, validated)) 783 return glresult(error.append_fnc); 784 785 if (!validated) { 786 /* string error_msg; */ 787 /* auto _ = this.get_info_log(error_msg); */ 788 return glresult(GLError( 789 GLError.Flag.PROGRAM_VALIDATION_ERROR, 790 "\tHINT: Run get_info_log() to get extra information" 791 )); 792 } 793 this.is_validated_ = true; 794 return glresult(GLError.NO_ERROR); 795 } 796 797 /++ 798 use `this` Program. Use `Program.use_empty()` to use null program 799 800 Returns: `GLResult!void` with GLError with error information if any 801 +/ 802 @trusted 803 GLResult!void use() inout 804 { 805 // NOTE: in theory if this.id_ is 0 it shouldn't be an error in OpenGL 806 // but in really makes no sense to use an invalid program. 807 // Make a `Program.stop_using()` or `Program.use_empty()` 808 return gl_use_program(this.id_); 809 } 810 811 // Cool convinience function 812 // Might be cool to use a variadic template here with `ref Shader` 813 // this solutions is ok thought 814 /++ 815 Compile all `shaders`, attach each to program 816 link and validate program. 817 818 $(WARNING 819 This function may do to much, it may get deleted or 820 renamed. Use the equivalent descripted in [#examples]: 821 ) 822 823 Returns: `GLResult!void` with error information if any 824 Example: 825 --- 826 program.prepare_and_attach(&vert_shader, &frag_shader).throw_on_error; 827 // Program read to use! 828 --- 829 is equivalent to: 830 --- 831 Program program; 832 Shader vert_sh, frag_shader; // our shaders; 833 foreach (shader; [&vert_sh, &frag_shader]) 834 shader.compile().throw_on_error; 835 836 program.attach(vert_sh, frag_shader).throw_on_error; 837 program.link().throw_on_error; 838 program.validate().throw_on_error; 839 // Program ready to use! 840 --- 841 +/ 842 @safe 843 GLResult!void prepare_and_attach(Shader*[] shaders ...) 844 { 845 foreach (Shader *shader; shaders) { 846 if (!shader.is_compiled) { 847 if (auto res = shader.compile()) return res.error.append_fnc().glresult(); 848 } 849 850 if (auto res = this.attach(*shader)) return res.error.append_fnc().glresult(); 851 } 852 853 if (auto res = this.link()) return res.error.append_fnc().glresult(); 854 if (auto res = this.validate()) return res.error.append_fnc().glresult(); 855 856 // Everythin ok 857 return glresult(GLError.NO_ERROR); 858 } 859 860 // Wrapper to glUniformLocation 861 // If loc is negative then uniform `u_name` is not an active uniform 862 /+++ 863 Wrapper to `gl_get_uniform_location` 864 865 Returns: `GLResult!int` with location to uniform and error information 866 if any 867 +++/ 868 @trusted 869 GLResult!int get_uniform_loc(string u_name) inout /* out(result; result > 0) */ 870 { 871 import std.string : toStringz; 872 GLResult!int loc_result = gl_get_uniform_location(this.id, u_name); 873 /* if (loc < 0) { */ 874 /* // TODO: use `std.logger` and put this in a version block */ 875 /* stderr.writeln("[GL_WARNING]: "~u_name~" is not an active uniform name"); */ 876 /* } */ 877 return loc_result; 878 } 879 } 880 881 // TODO: Return `GLResult!void` or `Result!(InternalError, void)` 882 nothrow 883 GLError get_param(ref const(Shader) self, GLParam param, ref int val) 884 { 885 import std.stdio; 886 import std.exception; 887 if (!self.is_created) 888 return GLError.INVALID_SHADER; 889 890 /* assumeWontThrow(writeln(param, ": val: ", val)); */ 891 auto res = gl_wrap!glGetShaderiv(self.id, param, &val); 892 /* assumeWontThrow(writeln(param, ": val after: ", val)); */ 893 /* assumeWontThrow(writeln(res)); */ 894 895 return res.error.to_glerror(); 896 } 897 898 // NOTE: I feel like it might make sense to use GLResult here... 899 // NOTE: can't make this auto ref for some reason 900 // Get param for Program 901 nothrow 902 GLError get_param 903 (ref const(Program) self, GLParam param, out int val) 904 { 905 if (!self.is_created) 906 return GLError(GLError.Flag.INVALID_PROGRAM); 907 908 auto res = gl_wrap!glGetProgramiv(self.id, param, &val); 909 910 GLError err = GLError.NO_ERROR; 911 912 with(GLInternalError) 913 switch(res.error.error_flag) 914 { 915 case NO_ERROR: break; // do nothing 916 case INVALID_ENUM: goto case INVALID_OPERATION; 917 case INVALID_OPERATION: 918 err = GLError.INVALID_PARAMETER; 919 break; 920 default: 921 err = GLError.UNKNOWN_ERROR; 922 break; 923 } 924 return err; 925 } 926 927 // TODO: This can be simiplified with a compile time associative array 928 private alias ParamReturnTypes = AliasSeq!( 929 Shader.Type, // SHADER_TYPE 930 bool, // DELETE_STATUS 931 bool, // COMPILE_STATUS 932 int, // INFO_LOG_LENGTH 933 int, // SHADER_SOURCE_LENGTH 934 bool, // LINK_STATUS 935 bool, // VALIDATE_STATUS 936 ); 937 938 939 private template getParamReturnType(GLParam p) 940 { 941 import std.traits : EnumMembers; 942 943 private template getParamReturnType_(int i, GLParam p) 944 { 945 static if (i == ParamReturnTypes.length) { 946 alias getParamReturnType_ = void; 947 } 948 else static if (EnumMembers!GLParam[i] == p) // found 949 { 950 alias getParamReturnType_ = ParamReturnTypes[i]; 951 } 952 else 953 alias getParamReturnType_ = getParamReturnType_!(i+1, p); 954 } 955 956 alias getParamReturnType = getParamReturnType_!(0, p); 957 } 958 959 /* private template get_param_(S, T) */ 960 /* { */ 961 /* nothrow */ 962 /* GLResult!T get_param_(Param p)(ref inout(S) self) */ 963 /* { */ 964 /* int _val; */ 965 /* GLError err = get_param(self, p, _val).append_fnc(); */ 966 /* return GLResult!T(err, cast(T)_val); */ 967 /* } */ 968 /* } */ 969 970 private enum isValidTypeForParam(T, GLParam p) = 971 (is(T == Shader) && p.is_shader_param()) || 972 (is(T == Program) && p.is_program_param()); 973 974 private template get_param_(GLParam p) 975 { 976 alias T = getParamReturnType!p; 977 978 static assert(!is(T == void)); 979 980 nothrow 981 GLResult!T get_param_(S)(ref inout(S) self) if (isValidTypeForParam!(S, p)) 982 { 983 int _val = -1; 984 GLError err = get_param(self, p, _val).append_fnc(); 985 return GLResult!T(err, cast(T)_val); 986 } 987 } 988 989 alias get_param(GLParam p) = get_param_!p; 990 991 // TODO: This should return an error in a couple of escenarios 992 // NOTE: Vertex Array Objects aren't 993 // available before opengl 3 994 struct VArrayObject 995 { 996 // this is nogc except if version is VADGL_Debug 997 nothrow: 998 999 private uint id_ = 0; 1000 1001 @property @safe uint id() const => this.id_; 1002 1003 @safe pure 1004 this(uint vao_id) { this.id_ = vao_id; } 1005 1006 // If this is not supposed to be able to fail then why do I return a GLResult 1007 @trusted 1008 static GLResult!VArrayObject create() 1009 { 1010 uint vao_id; 1011 gl_call!glGenVertexArrays(1, &vao_id); // Shouldn't be able to fail 1012 return GLResult!VArrayObject(VArrayObject(vao_id)); 1013 } 1014 1015 // cannot fail 1016 void disable() @safe => cast(void)VArrayObject().bind(); 1017 1018 // can fail if id_ is not valid 1019 GLResult!void bind() @trusted => gl_wrap!glBindVertexArray(id_).to_glresult(); 1020 } 1021 1022 struct VBufferObject 1023 { 1024 // this is nogc except if version is VADGL_Debug 1025 nothrow: 1026 1027 private { 1028 uint id_ = 0; 1029 GLenum target = GL_ARRAY_BUFFER; 1030 } 1031 1032 @trusted 1033 static GLResult!VBufferObject create(GLenum target = GL_ARRAY_BUFFER) 1034 { 1035 uint vbo_id; 1036 gl_call!glGenBuffers(1, &vbo_id); // Shouldn't be able to fail 1037 return glresult(VBufferObject(vbo_id, target)); 1038 } 1039 1040 // This could only fail if target is invalid. But that's ok 1041 static void disable(GLenum target) @safe => cast(void)VBufferObject().bind(target); 1042 1043 // Allocates and copys `data` to a gpu buffer 1044 @trusted 1045 static GLResult!void set_data(GLenum target, size_t size, const(void*) data, GLenum usage) 1046 { 1047 return gl_buffer_data(target, size, data, usage); 1048 } 1049 1050 @property uint id() @safe const => this.id_; 1051 1052 this(uint vbo_id, GLenum target = GL_ARRAY_BUFFER) @safe 1053 { 1054 this.id_ = vbo_id; 1055 this.target = target; 1056 } 1057 1058 // can fail if id_ is not valid or if target is not valid 1059 @trusted 1060 GLResult!void bind(GLenum target) => gl_wrap!glBindBuffer(target, id_).to_glresult(); 1061 1062 // can fail if id_ is not valid 1063 @safe 1064 GLResult!void bind() => bind(this.target); 1065 1066 void disable() @safe => cast(void)disable(this.target); 1067 1068 /* 1069 The OpenGL target often has no effect really so it doesn't matter which you use 1070 */ 1071 GLResult!void set_data(size_t size, const(void*) data = null, GLenum usage = GL_STATIC_DRAW) 1072 { 1073 // TODO: Hmmmmmmm, not sure about this one 1074 /* if (auto res = this.bind(GL_ARRAY_BUFFER)) */ 1075 /* return res; */ 1076 1077 /* 1078 NOTE: I'm using `GL_ARRAY_BUFFER` instead of the buffer type because 1079 for copying data, because it doesn't make sense to use something else 1080 anyway. Though I guess this would require me to also bind which may 1081 cause a problem. But binding just makes the most sense here 1082 */ 1083 auto res = VBufferObject.set_data(GL_ARRAY_BUFFER, size, data, usage); 1084 1085 /* VBufferObject.disable(GL_ARRAY_BUFFER); */ 1086 1087 return res; 1088 } 1089 // Copy `data` into buffer + offset 1090 @trusted 1091 GLResult!void set_sub_data(size_t offset, size_t size, const(void*) data) 1092 { 1093 return gl_buffer_sub_data(GL_ARRAY_BUFFER, offset, size, data); 1094 } 1095 1096 1097 // Should be safe 1098 @trusted 1099 GLResult!void set_data(const(void[]) data, GLenum usage) 1100 => set_data(data.length, data.ptr, usage); 1101 1102 @trusted 1103 GLResult!void set_data(T)(const(T[]) data, GLenum usage) if (!is(T == void) && is(T : void[])) 1104 => set_data(cast(void[])data, usage); 1105 } 1106 1107 struct GLAttributeInfo 1108 { 1109 nothrow: 1110 1111 int loc; 1112 GLType type; 1113 int count; 1114 size_t offset_; 1115 bool normalized = false; 1116 1117 static GLResult!GLAttributeInfo from_name 1118 (int program_id, string name, GLType type, int count, size_t offset_, bool normalized=false) 1119 { 1120 GLAttributeInfo attr = GLAttributeInfo(-1, type, count, offset_, normalized); 1121 if (auto res = attr.set_location(name, program_id)) 1122 return GLResult!GLAttributeInfo(res.error); 1123 1124 return attr.glresult(); 1125 } 1126 1127 // TODO: Implement this bullshit 1128 GLResult!void set_location(string name, int program_id) 1129 { 1130 if (auto res = gl_get_attribute_location(name, program_id)) 1131 return res.error.append_fnc().glresult(); 1132 return GLError.no_error().glresult(); 1133 } 1134 1135 GLResult!void enable() 1136 => gl_enable_vertex_attributes(this.loc); 1137 1138 // TODO: A lower version than glSupport could be loaded 1139 // So add a way to set the actual OpenGL version at runtime 1140 // check that, and return an error if the version is lower than expected 1141 static if (glSupport >= GLSupport.gl33) 1142 GLResult!void set_divisor(uint divisor) 1143 => gl_vertex_attrib_divisor(this.loc, divisor); 1144 1145 // Remember about glVertexAttribFormat 1146 GLResult!void set(size_t stride) 1147 => gl_vertex_attribute_conf(loc, count, type, stride, offset_, normalized); 1148 1149 GLResult!void setI(size_t stride) 1150 => gl_vertex_attributeI_conf(loc, count, type, stride, offset_); 1151 } 1152 1153 /++ 1154 Abstraction over uniforms 1155 +/ 1156 struct GLUniform 1157 { 1158 int loc; // TODO: perhaps remove `loc` 1159 1160 static GLResult!GLUniform from_name(string name, uint program_id) 1161 { 1162 auto res = gl_get_uniform_location(program_id, name); 1163 if (res) 1164 return GLResult!GLUniform(res.error); 1165 return GLResult!GLUniform(GLUniform(res.value)); 1166 } 1167 1168 GLResult!void set_location(string name, int program_id) 1169 { 1170 auto res = gl_get_uniform_location(program_id, name); 1171 if (res) 1172 return res.error.glresult(); 1173 return GLResult!void(); 1174 } 1175 1176 GLResult!void set(T)(T value) 1177 if (T.stringof.among("float", "int", "uint")) 1178 { 1179 T[1] val = value; // not great but just easier 1180 return gl_set_uniform(this.loc, 1, val); 1181 } 1182 1183 GLResult!void set_mat4(int n, const(float)[] mat, bool normalized=false) 1184 => set_uniform_mat4(this.loc, n, mat, normalized); 1185 1186 // Will copy as much as possible of mat 1187 GLResult!void set_mat4(const(float)[] mat, bool normalized=false) 1188 => set_uniform_mat4(this.loc, mat, normalized); 1189 1190 GLResult!void set_v(int N, T)(T[N] vec, int n = 1) 1191 if (T.stringof.among("float", "int", "uint") && N <= 4) 1192 => gl_set_uniform(this.loc, n, vec); 1193 1194 GLResult!void set_v(int N, T)(T[] vec, int n = 1) 1195 if (T.stringof.among("float", "int", "uint") && N <= 4) 1196 => gl_set_uniform!N(this.loc, n, vec); 1197 } 1198 1199 GLResult!void gl_uniform_matrix4fv(int loc, int n, bool normlized, const(float*) mat_ptr) 1200 => gl_wrap!glUniformMatrix4fv(loc, n, normlized, mat_ptr).to_glresult(); 1201 1202 GLResult!void set_uniform_mat4(int loc, int n, const(float[]) mat, bool normalized=false) 1203 in(mat.length >= 16 * n) 1204 => gl_uniform_matrix4fv(loc, n, normalized, mat.ptr); 1205 1206 GLResult!void set_uniform_mat4(int loc, const(float[]) mat, bool normalized=false) 1207 { 1208 const int n = cast(int)(mat.length / 16); 1209 return set_uniform_mat4(loc, n, mat, normalized); 1210 } 1211 1212 private template shortBaseTypeNames(T) 1213 { 1214 private enum string type_name = T.stringof; 1215 static assert(type_name.among("float", "int", "uint")); 1216 1217 static if (type_name == "float" || type_name == "int") 1218 enum string shortBaseTypeNames = [type_name[0]]; 1219 else 1220 enum string shortBaseTypeNames = "ui"; 1221 } 1222 1223 // Doesn't compile on ldc 1.35 1224 /* private static immutable string[string] shortBaseTypeNames = [ */ 1225 /* "float": "f", */ 1226 /* "int": "i", */ 1227 /* "uint": "ui" */ 1228 /* ]; */ 1229 1230 GLResult!void gl_set_uniform(int N, T)(int loc, int n, T[N] v) 1231 if (T.stringof.among("float", "int", "uint") && N <= 4) 1232 { 1233 enum string shortT = shortBaseTypeNames!(T); 1234 1235 return gl_wrap!(mixin("glUniform"~N.to!string~shortT~"v"))(loc, n, v.ptr) 1236 .to_glresult(); 1237 } 1238 1239 GLResult!void gl_set_uniform(int N, T)(int loc, int n, T[] v) 1240 if (T.stringof.among("float", "int", "uint") && N <= 4) 1241 in(v.length >= n * N) 1242 { 1243 enum string shortT = shortBaseTypeNames!(T); 1244 1245 return gl_wrap!(mixin("glUniform"~N.to!string~shortT~"v"))(loc, n, v.ptr) 1246 .to_glresult(); 1247 } 1248 1249 GLResult!void gl_set_uniform(int N, T)(int loc, T[N][] v) 1250 if (T.stringof.among("float", "int", "uint") && N <= 4) 1251 { 1252 enum string shortT = shortBaseTypeNames!(T); 1253 1254 return gl_wrap!(mixin("glUniform"~N.to!string~shortT~"v"))(loc, v.length, v.ptr) 1255 .to_glresult(); 1256 } 1257 1258 /* void gl_set_uniform(T, size_t N)(in T[N] vec) */ 1259 /* { */ 1260 /* T[N] vec_cp = vec[]; // Copy V since it's immutable */ 1261 /* mixin("glUniform"~N.to~string~TC~"v(loc, 1, vec_cp.ptr);"); */ 1262 /* } */ 1263 1264 // gl_set_uniform!mat4(0, mpv_mat, true) 1265 1266 // NOTE: This is only an idea 1267 private enum __vadgl_idea = q{ 1268 // TODO: perhaps also assign here the vertex buffer 1269 // for each of these attributes 1270 struct Vertex 1271 { 1272 @gl_divisor(0) @gl_loc(0) int[2] model_pos; 1273 1274 @gl_divisor(1): 1275 1276 @gl_loc(1) int[3] pos; 1277 @gl_loc(2) @gl_integral ubyte[4] color; 1278 @gl_loc(3) uint packed_size; 1279 } 1280 1281 // TOOD: I could make a mixin template to inject 1282 // The relevant methods and maybe attributes 1283 template VertexFormat(VertexType) 1284 { 1285 enum size_t AttribCount = getAttribCount!VertexType; 1286 1287 GLAttribInfo[AttribCount] attributes; 1288 AttributeType.tupleof[] values; 1289 1290 GLResult!void set() 1291 { 1292 static foreach(...) { 1293 static if (isIntegral!GL_Type) { 1294 attribute.setI(); 1295 } 1296 else 1297 attribute.set(); 1298 } 1299 } 1300 } 1301 }; 1302 1303 template glattribute(T) 1304 { 1305 alias TInfo = TypeInfoGLSL!T; 1306 1307 enum string kind = TInfo[0]; 1308 1309 // TODO: Could also use matrices 1310 static assert(kind != "invalid"); 1311 1312 alias BT = TInfo[1]; 1313 enum size_t N = TInfo[2]; 1314 enum size_t M = TInfo[3]; 1315 1316 static immutable GLType type = to_gltype!BT; 1317 1318 @safe @nogc nothrow pure 1319 GLAttributeInfo glattribute(uint loc, size_t offset_, bool normalized=false) 1320 => GLAttributeInfo(loc, type, N * M, offset_, normalized); 1321 } 1322 1323 nothrow 1324 GLResult!void gl_buffer_data(GLenum target, GLsizeiptr size, const(void*) data, GLenum usage) 1325 => gl_wrap!glBufferData(target, size, data, usage).to_glresult(); 1326 1327 nothrow 1328 GLResult!void gl_buffer_sub_data(GLenum target, GLintptr offset, GLsizeiptr size, const(void*) data) 1329 => gl_wrap!glBufferSubData(target, offset, size, data).to_glresult(); 1330 1331 /++ 1332 Wrapper to `glCreateShader` 1333 +/ 1334 nothrow 1335 GLResult!uint gl_create_shader(Shader.Type type) 1336 => gl_wrap!glCreateShader(type).to_glresult(); 1337 1338 /++ 1339 Wrapper to `glCreateShader` 1340 +/ 1341 nothrow 1342 GLResult!void gl_shader_source(uint id, int count, const(char**) strings, const(int*) lengths) 1343 => gl_wrap!glShaderSource(id, count, strings, lengths).to_glresult(); 1344 1345 /++ 1346 Wrapper to `glCreateShader` 1347 +/ 1348 nothrow 1349 GLResult!void gl_shader_source(uint id, const(char*) src_c_str, int length) 1350 => gl_shader_source(id, 1, &src_c_str, &length); 1351 1352 /++ 1353 Wrapper to `glCreateShader` 1354 +/ 1355 nothrow GLResult!void gl_shader_source(uint id, const(char[]) src) 1356 => gl_shader_source(id, src.ptr, cast(int)src.length); 1357 1358 /++ 1359 Wrapper to `glCreateShader` 1360 +/ 1361 nothrow 1362 GLResult!void gl_compile_shader(uint id) 1363 => gl_wrap!glCompileShader(id).to_glresult(); 1364 1365 /++ 1366 Wrapper to `glCreateShader` 1367 +/ 1368 nothrow 1369 uint gl_create_program() 1370 => gl_call!glCreateProgram(); // doesn't generate GLError 1371 1372 /++ 1373 Wrapper to `glCreateShader` 1374 +/ 1375 nothrow 1376 GLResult!void gl_use_program(uint program_id) 1377 => gl_wrap!glUseProgram(program_id).to_glresult(); 1378 1379 /++ 1380 Wrapper to `glCreateShader` 1381 +/ 1382 nothrow 1383 GLResult!void gl_attach_shader(uint program_id, uint shader_id) 1384 => gl_wrap!glAttachShader(program_id, shader_id).to_glresult(); 1385 1386 /++ 1387 Wrapper to `glCreateShader` 1388 +/ 1389 nothrow 1390 GLResult!void gl_link_program(uint program_id) 1391 => gl_wrap!glLinkProgram(program_id).to_glresult(); 1392 1393 /* 1394 NOTE: 1395 this maps directly to `glVertexAttribPointer` but the name is different. 1396 I'm not sure if it's better if I change the name closer to it's OpenGL counterpart 1397 */ 1398 // TODO: change error type 1399 nothrow 1400 GLResult!void gl_vertex_attribute_conf 1401 (uint loc, int count, GLType type, size_t stride, size_t offset_, bool normalized = false) 1402 => gl_wrap!glVertexAttribPointer( 1403 loc, count, type, 1404 normalized, cast(GLsizei)stride, cast(void*)offset_).to_glresult(); 1405 1406 nothrow 1407 GLResult!void gl_vertex_attributeI_conf 1408 (uint loc, int count, GLType type, long stride, size_t offset_) 1409 => gl_wrap!glVertexAttribIPointer( 1410 loc, count, type, 1411 cast(GLsizei)stride, cast(void*)offset_).to_glresult(); 1412 1413 // Maps to `glEnableVertexAttribArray` or `glEnableVertexArrayAttrib` 1414 /* 1415 NOTE (Out of date): 1416 This function calls `glBindVertexArray` when running on versions < gl45 1417 Can generate errors: 1418 - `GL_INVALID_OPERATION`: 1419 + ~~If no vertex array object is bound.~~ 1420 + if vaobj is not the name of an existing vertex array object. 1421 - `GL_INVALID_VALUE`: 1422 + if index is greater than or equal to GL_MAX_VERTEX_ATTRIBS. 1423 1424 NOTE: 1425 Provide a gl_attribute alternative for this 1426 */ 1427 @trusted nothrow 1428 GLResult!void gl_enable_vertex_attributes(uint[] locations...) 1429 { 1430 /* Safe by OpenGL sepecs */ 1431 foreach (loc; locations) 1432 if (auto res = gl_wrap!glEnableVertexAttribArray(loc).to_glresult()) 1433 return res; 1434 return GLResult!void(GLError.NO_ERROR); 1435 } 1436 1437 nothrow 1438 GLResult!void gl_enable_vertex_array_attributes(uint vao_id, uint[] locations...) 1439 { 1440 // `glEnableVertexArrayAttrib` only available on version 4.5 1441 static if (glSupport >= GLSupport.gl45) { 1442 foreach (loc; locations) 1443 if (auto res = gl_wrap!glEnableVertexArrayAttrib(vao_id, loc).to_glresult()) 1444 return res; 1445 1446 return GLResult!void(GLError.NO_ERROR); 1447 } 1448 else { 1449 if (auto res = VArrayObject(vao_id).bind()) 1450 return res; // TODO: Maybe return `GL_INVALID_OPERATION` 1451 1452 return gl_enable_vertex_attributes(locations); 1453 } 1454 } 1455 1456 nothrow 1457 GLResult!void gl_enable_vertex_attributes(VArrayObject vao, uint[] locations...) 1458 => gl_enable_vertex_array_attributes(vao.id, locations); 1459 1460 nothrow 1461 GLResult!void gl_vertex_attrib_divisor(uint loc, uint divisor) 1462 => gl_wrap!glVertexAttribDivisor(loc, divisor).to_glresult(); 1463 1464 nothrow 1465 static GLResult!int gl_get_attribute_location(string name, int program_id) 1466 => gl_wrap!glGetAttribLocation(program_id, name.toStringz).to_glresult(); 1467 1468 /++ 1469 Wrapper to `glGetUniformLocation` 1470 +/ 1471 nothrow 1472 static GLResult!int gl_get_uniform_location(uint program_id, const(char*) name) 1473 => gl_wrap!glGetUniformLocation(program_id, name).to_glresult(); 1474 1475 /* 1476 NOTE: The only way I could make this @nogc is by adding an arbitrary maximum 1477 length, allocating a buffer of that size and reserving the last space for 1478 '\0'. 1479 */ 1480 /++ 1481 Wrapper to `glGetUniformLocation` 1482 1483 $(WARNING 1484 This function calls `toStringz` which mayh allocate. Use the 1485 `const(char*)` overload of this function to avoid allocations 1486 ) 1487 +/ 1488 nothrow 1489 static GLResult!int gl_get_uniform_location(uint program_id, string name) 1490 => gl_get_uniform_location(program_id, name.toStringz); 1491 1492 // TODO: Replace GLEnum with my own `GLPrimitive` type 1493 /++ 1494 Wrapper to `glDrawArrays` 1495 +/ 1496 nothrow 1497 static GLResult!void gl_draw_arrays(GLenum mode, uint first, int count) 1498 => gl_wrap!glDrawArrays(mode, first, count).to_glresult(); 1499 1500 /++ 1501 Wrapper to `glDrawElements` 1502 +/ 1503 nothrow 1504 static GLResult!void gl_draw_elements(GLenum mode, int count, GLType type, size_t indices) 1505 => gl_wrap!glDrawElements(mode, count, type, cast(void*)indices).to_glresult(); 1506 1507 /++ 1508 Wrapper to `glDrawRangeElements` 1509 +/ 1510 nothrow 1511 GLResult!void gl_draw_range_elements 1512 (GLenum mode, uint start, uint end, int count, GLType type, size_t offset_) 1513 => gl_wrap!glDrawRangeElements(mode, start, end, count, type, cast(void*)offset_).to_glresult(); 1514 1515 /++ 1516 Wrapper to `glDrawArraysInstanced` 1517 1518 $(NOTE Requires OpenGL version >= 3.1) 1519 +/ 1520 nothrow 1521 GLResult!void gl_draw_arrays_instanced(GLenum mode, int first, int count, int prim_count) 1522 => gl_wrap!glDrawArraysInstanced(mode, first, count, prim_count).to_glresult(); 1523 1524 /++ 1525 Wrapper to `glDrawElementsInstanced` 1526 +/ 1527 nothrow 1528 GLResult!void gl_draw_elements_instanced 1529 (GLenum mode, int count, GLType type, void* indicies_ptr, int prim_count) 1530 => gl_wrap!glDrawElementsInstanced(mode, count, type, indicies_ptr, prim_count) 1531 .to_glresult(); 1532 1533 /++ 1534 Wrapper to `glClearColor` 1535 +/ 1536 @trusted nothrow 1537 void gl_clear_color(float r=0.0, float g=0.0, float b=0.0, float a=1.0) 1538 => gl_call!glClearColor(r, g, b, a); 1539 1540 1541 /++ 1542 Wrapper to `glClear` 1543 +/ 1544 @trusted nothrow 1545 GLResult!void gl_clear(GLbitfield mask) 1546 => gl_wrap!glClear(mask).to_glresult(); 1547 1548 // TODO: Handle errors by returning GLResult 1549 // TODO: Could use `Args...` instead of T to bind mutiple stuff at the same time 1550 /// Not sure about this, may get removed later 1551 void gl_with(alias fnc, T)(T globject) 1552 { 1553 import std.meta : anySatisfy; 1554 1555 enum bool isT(U) = is(T == U); 1556 // TODO: Add Texture type 1557 enum bool isBindable = anySatisfy(isT, VArrayObject, VBufferObject); 1558 1559 static if (isBindable) globject.bind(); 1560 else static if (is(T == Program)) globject.use(); 1561 fnc(); 1562 static if (isBindable) globject.disable(); 1563 else static if (is(T == Program)) Program.use_empty(); 1564 } 1565 1566 /* 1567 TODO: This is not @nogc because of `fromStringz` and `idup` anway, there's 1568 no @nogc way to return a string here. Provide an override for whomever 1569 might want a @nogc version 1570 */ 1571 /++ 1572 Call `glGet{Program|Shader}InfoLog` and return result as a string 1573 1574 $(WARNING This function allocates) 1575 +/ 1576 @trusted 1577 GLResult!string get_info_log(T)(ref const(T) self) 1578 if (is(T == Shader) || is(T == Program)) 1579 { 1580 import std.format : sformat; 1581 import std.string : fromStringz; 1582 // Maybe repetitive. But you shouldn't be doing 10_000 glGetInfoLog calls 1583 // a second, so the extra check does no harm 1584 if (!self.is_created) { 1585 return GLResult!string(GLError(GLError.Flag.INVALID_SHADER)); 1586 } 1587 1588 enum int LOG_MAX_SIZE = 2048; 1589 1590 char[LOG_MAX_SIZE] error_log = void; // don't bother initializing 1591 int log_size; 1592 1593 enum string sh_type = is(T == Shader) ? "Shader" : "Program"; 1594 1595 // Theorethically since self.id should be a valid program/shader 1596 // at this point and maxLength is > 0 then no OpenGL errors can occur 1597 auto res = mixin(q{ 1598 gl_wrap!glGet%sInfoLog( 1599 self.id, LOG_MAX_SIZE, &log_size, error_log.ptr 1600 ).to_glresult() 1601 }.format(sh_type)); 1602 1603 if (res) 1604 return GLResult!string(res.error); 1605 1606 if (log_size) { 1607 // TODO: all this could be done in a single buffer. 1608 char[2048] log_buff = '\0'; 1609 1610 size_t head_len = log_buff[].sformat("Error on \"%s\" shader\n", self.name).length; 1611 string tail = "\n[LOG SIZE LIMIT REACHED]\0"; 1612 // size to copy from `erro_log` 1613 size_t log_cp_size = 1614 (log_size + head_len > 2047) 1615 ? 2047 - head_len : log_size; 1616 1617 // could be done better 1618 log_buff[head_len..head_len+log_cp_size] = error_log[0..log_cp_size]; 1619 log_buff[$-tail.length..$] = tail[]; 1620 1621 return fromStringz(log_buff).idup.glresult(); 1622 } 1623 return GLResult!string(GLError.NO_ERROR, ""); 1624 } 1625 /* 1626 void some_fnc() 1627 { 1628 gl_with!({ 1629 glBufferData(...) 1630 glBufferData(...) 1631 })(vbo) 1632 } 1633 */