1 /*
2 Copyright (c) 2019-2022 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 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     
119     // get log
120     GLint infolen;
121     glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infolen); // includes \0
122     if (infolen > 1)
123     {
124         char[logMaxLen + 1] infobuffer = 0;
125         glGetShaderInfoLog(shaderID, logMaxLen, null, infobuffer.ptr);
126         infolen = min2(infolen - 1, logMaxLen);
127         char[] s = stripRight(infobuffer[0..infolen]);
128         
129         // it can be some warning
130         if (!ok)
131             writefln("Failed to compile %s shader:", stage);
132         writeln(s);
133     }
134     return ok;
135 }
136 
137 private bool checkLinking(const GLuint programID)
138 {
139     // get status
140     GLint status = GL_FALSE;
141     glGetProgramiv(programID, GL_LINK_STATUS, &status);
142     const bool ok = status != GL_FALSE;
143     
144     // get log
145     GLint infolen;
146     glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infolen); // includes \0
147     if (infolen > 1)
148     {
149         char[logMaxLen + 1] infobuffer = 0;
150         glGetProgramInfoLog(programID, logMaxLen, null, infobuffer.ptr);
151         infolen = min2(infolen - 1, logMaxLen);
152         char[] s = stripRight(infobuffer[0..infolen]);
153         
154         // it can be some warning
155         if (!ok)
156             writeln("Failed to link shaders:");
157         writeln(s);
158     }
159     return ok;
160 }