1 /*
2 Copyright (c) 2019-2020 dayllenger
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.shaderloader;
29 
30 import std.stdio;
31 import std..string: stripRight;
32 import dlib.math.utils: min2;
33 
34 import dagon.core.bindings;
35 
36 enum ShaderStage: ubyte
37 {
38     vertex = 1,
39     tessControl = 2,
40     tessEval = 4,
41     geometry = 8,
42     fragment = 16,
43     compute = 32
44 }
45 
46 private GLenum shaderStageToGLenum(ShaderStage stage)
47 {
48     final switch (stage) with(ShaderStage)
49     {
50         case vertex:      return GL_VERTEX_SHADER;
51         case tessControl: return GL_TESS_CONTROL_SHADER;
52         case tessEval:    return GL_TESS_EVALUATION_SHADER;
53         case geometry:    return GL_GEOMETRY_SHADER;
54         case fragment:    return GL_FRAGMENT_SHADER;
55     static if (glSupport >= GLSupport.gl43)
56         case compute:     return GL_COMPUTE_SHADER;
57     else
58         case compute:     return 0;
59     }
60 }
61 
62 /// Compile single shader from source
63 GLuint compileShader(string source, const ShaderStage stage)
64 {
65     // create a shader
66     GLuint shaderID = glCreateShader(shaderStageToGLenum(stage));
67 
68     // compile the shader
69     const char* csource = source.ptr;
70     GLint length = cast(GLint)source.length;
71     glShaderSource(shaderID, 1, &csource, &length);
72     glCompileShader(shaderID);
73 
74     // check the shader
75     if (!checkCompilation(shaderID, stage))
76     {
77         shaderID = 0;
78         glDeleteShader(shaderID);
79     }
80 
81     return shaderID;
82 }
83 
84 /// Link compiled shaders
85 GLuint linkShaders(const GLuint[] shaderIDs...)
86 {
87     // create and link program
88     GLuint programID = glCreateProgram();
89     foreach(sh; shaderIDs)
90         glAttachShader(programID, sh);
91     glLinkProgram(programID);
92 
93     // check the program
94     if (!checkLinking(programID))
95     {
96         programID = 0;
97         glDeleteProgram(programID);
98     }
99 
100     // delete the program parts
101     foreach(sh; shaderIDs)
102     {
103         glDetachShader(programID, sh);
104         glDeleteShader(sh);
105     }
106 
107     return programID;
108 }
109 
110 private enum logMaxLen = 1023;
111 
112 private bool checkCompilation(const GLuint shaderID, const ShaderStage stage)
113 {
114     // get status
115     GLint status = GL_FALSE;
116     glGetShaderiv(shaderID, GL_COMPILE_STATUS, &status);
117     const bool ok = status != GL_FALSE;
118     // get log
119     GLint infolen;
120     glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infolen); // includes \0
121     if (infolen > 1)
122     {
123         char[logMaxLen + 1] infobuffer = 0;
124         glGetShaderInfoLog(shaderID, logMaxLen, null, infobuffer.ptr);
125         infolen = min2(infolen - 1, logMaxLen);
126         char[] s = stripRight(infobuffer[0..infolen]);
127         // it can be some warning
128         if (!ok)
129             writefln("Failed to compile %s shader:", stage);
130         writeln(s);
131     }
132     return ok;
133 }
134 
135 private bool checkLinking(const GLuint programID)
136 {
137     // get status
138     GLint status = GL_FALSE;
139     glGetProgramiv(programID, GL_LINK_STATUS, &status);
140     const bool ok = status != GL_FALSE;
141     // get log
142     GLint infolen;
143     glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infolen); // includes \0
144     if (infolen > 1)
145     {
146         char[logMaxLen + 1] infobuffer = 0;
147         glGetProgramInfoLog(programID, logMaxLen, null, infobuffer.ptr);
148         infolen = min2(infolen - 1, logMaxLen);
149         char[] s = stripRight(infobuffer[0..infolen]);
150         // it can be some warning
151         if (!ok)
152             writeln("Failed to link shaders:");
153         writeln(s);
154     }
155     return ok;
156 }