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 */