1 /**
2  * Copyright:   Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:     $(Gabriel Gheorghe)
4  * License:     $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:      $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/graphics/opengl/shader.d, _shader.d)
6  * Documentation:
7  * Coverage:
8  */
9 module liberty.graphics.opengl.shader;
10 import liberty.graphics.renderer;
11 import liberty.graphics.video.shader : ShaderProgram, ShaderType;
12 import liberty.graphics.video.backend : UnsupportedVideoFeatureException;
13 import liberty.graphics.opengl.vertex : GLAttribute;
14 import liberty.graphics.opengl.backend : GLException;
15 import liberty.math.vector : Vector2F, Vector3F, Vector4F;
16 import liberty.math.matrix: Matrix4F;
17 import std.string : fromStringz;
18 import derelict.opengl;
19 ///
20 final class GLShaderProgram : ShaderProgram {
21 	private {
22 		GLAttribute[string] _activeAttributes;
23 	}
24     /// Construct using a vertex string and a fragment string
25     this(string vertex_code, string fragment_code) {
26 		_vertexShaderID = loadShader(vertex_code, ShaderType.Vertex);
27 		_fragmentShaderID = loadShader(fragment_code, ShaderType.Fragment);
28 		_programID = glCreateProgram();
29 		glAttachShader(_programID, _vertexShaderID);
30         glAttachShader(_programID, _fragmentShaderID);
31         //bindAttributes();
32         link();
33         glValidateProgram(_programID);
34         //allUniformLocations();
35     }
36     ~this() {
37         cleanUp();
38     }
39     /// Gets the linking report.
40     /// Returns: Log output of the GLSL linker. Can return null!
41     /// Throws: $(D GLException) on error.
42     const(char)[] linkLog() {
43         GLint logLength;
44         glGetProgramiv(_programID, GL_INFO_LOG_LENGTH, &logLength);
45         if (logLength <= 0) {
46             return null;
47         }
48         char[] log = new char[logLength + 1];
49         GLint dummy;
50         glGetProgramInfoLog(_programID, logLength, &dummy, log.ptr);
51         GraphicsEngine.get.backend.runtimeCheck();
52         return fromStringz(log.ptr);
53     }
54     ///
55     void link() {
56         glLinkProgram(_programID);
57         GraphicsEngine.get.backend.runtimeCheck();
58 		int res;
59         glGetProgramiv(_programID, GL_LINK_STATUS, &res);
60         if (res != true) {
61             const(char)[] linkLog = linkLog();
62             if (linkLog != null) {
63                 //_gl._logger.errorf("%s", linkLog);
64             }
65             throw new GLException("Cannot link program");
66         }
67         enum SAFETY_SPACE = 128;
68         // get active uniforms
69         {
70             GLint uniformNameMaxLength;
71             glGetProgramiv(_programID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength);
72             GLchar[] buffer = new GLchar[uniformNameMaxLength + SAFETY_SPACE];
73             GLint numActiveUniforms;
74             glGetProgramiv(_programID, GL_ACTIVE_UNIFORMS, &numActiveUniforms);
75             // get uniform block indices (if > 0, it's a block uniform)
76             GLuint[] uniformIndex;
77             GLint[] blockIndex;
78             uniformIndex.length = numActiveUniforms;
79             blockIndex.length = numActiveUniforms;
80             for (GLint i = 0; i < numActiveUniforms; i++) {
81                 uniformIndex[i] = cast(GLuint)i;
82             }
83             glGetActiveUniformsiv(_programID, cast(GLint)uniformIndex.length, uniformIndex.ptr, GL_UNIFORM_BLOCK_INDEX, blockIndex.ptr);
84             GraphicsEngine.get.backend.runtimeCheck();
85             // get active uniform blocks
86             //uniformBlocks(this);
87             //for (GLint i = 0; i < numActiveUniforms; i++) {
88             //    if(blockIndex[i] >= 0) {
89             //        continue;
90             //    }
91             //    GLint size;
92             //    GLenum type;
93             //    GLsizei length;
94             //    glGetActiveUniform(_programID, cast(GLuint)i, cast(GLint)(buffer.length), &length, &size, &type, buffer.ptr);
95             //    GraphicsEngine.get.backend.runtimeCheck();
96             //    string name = fromStringz(buffer.ptr).idup;
97             //   _activeUniforms[name] = new GLUniform(_programID, type, name, size);
98             //}
99         }
100         // get active attributes
101         {
102             GLint attribNameMaxLength;
103             glGetProgramiv(_programID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attribNameMaxLength);
104             GLchar[] buffer = new GLchar[attribNameMaxLength + SAFETY_SPACE];
105             GLint numActiveAttribs;
106             glGetProgramiv(_programID, GL_ACTIVE_ATTRIBUTES, &numActiveAttribs);
107             for (GLint i = 0; i < numActiveAttribs; ++i) {
108                 GLint size;
109                 GLenum type;
110                 GLsizei length;
111                 glGetActiveAttrib(_programID, cast(GLuint)i, cast(GLint)(buffer.length), &length, &size, &type, buffer.ptr);
112                 GraphicsEngine.get.backend.runtimeCheck();
113                 string name = fromStringz(buffer.ptr).idup;
114                 GLint location = glGetAttribLocation(_programID, buffer.ptr);
115                 GraphicsEngine.get.backend.runtimeCheck();
116                 _activeAttributes[name] = new GLAttribute(name, location, type, size);
117             }
118         }
119     }
120     ///
121     GLAttribute attribute(string name) {
122         GLAttribute* a = name in _activeAttributes;
123         if (a is null) {
124             _activeAttributes[name] = new GLAttribute(name);
125             return _activeAttributes[name];
126         }
127         return *a;
128     }
129     ///
130     //protected abstract void allUniformLocations();
131     /// Get uniform location at specified name
132     //int uniformLocation(string uniform_name) {
133     //    return glGetUniformLocation(_programID, uniform_name.ptr); // todo ?
134     //}
135     ///
136     override uint programID() {
137         return _programID;
138     }
139     ///
140     override void start() {
141         glUseProgram(_programID);
142     }
143     ///
144     override void stop() {
145         glUseProgram(0);
146     }
147     ///
148     override void cleanUp() {
149         stop();
150         glDetachShader(_programID, _vertexShaderID);
151         glDetachShader(_programID, _fragmentShaderID);
152         glDeleteShader(_vertexShaderID);
153         glDeleteShader(_fragmentShaderID);
154         glDeleteProgram(_programID);
155     }
156     ///
157     override void bindAttribute(int attribute, string var_name) {
158         import std.string : toStringz;
159         glBindAttribLocation(_programID, attribute, var_name.toStringz);
160     }
161     //protected abstract void bindAttributes();
162     /// Load bool uniform using location id and value.
163     override void loadUniform(int locationID, bool value) nothrow @trusted @nogc {
164         glUniform1i(locationID, cast(int)value);
165     }
166     /// Load int uniform using location id and value.
167     override void loadUniform(int locationID, int value) nothrow @trusted @nogc {
168         glUniform1i(locationID, value);
169     }
170     /// Load uint uniform using location id and value.
171     override void loadUniform(int locationID, uint value) nothrow @trusted @nogc {
172         glUniform1ui(locationID, value);
173     }
174     /// Load float uniform using location id and value.
175     override void loadUniform(int locationID, float value) nothrow @trusted @nogc {
176         glUniform1f(locationID, value);
177     }
178     /// Load Vector2F uniform using location id and value.
179     override void loadUniform(int locationID, Vector2F vector) nothrow @trusted @nogc {
180         glUniform2f(locationID, vector.x, vector.y);
181     }
182     /// Load Vector3F uniform using location id and value.
183     override void loadUniform(int locationID, Vector3F vector) nothrow @trusted @nogc {
184         glUniform3f(locationID, vector.x, vector.y, vector.z);
185     }
186     /// Load Vector4F uniform using location id and value.
187 	override void loadUniform(int locationID, Vector4F vector) nothrow @trusted @nogc {
188         glUniform4f(locationID, vector.x, vector.y, vector.z, vector.w);
189     }
190     /// Load Matrix4F uniform using location id and value.
191     override void loadUniform(int locationID, Matrix4F matrix) nothrow @trusted @nogc {
192         //glUniform4fv(locationID, matrix.ptr); // TODO?
193     }
194     /// Load bool uniform using uniform name and value.
195     override void loadUniform(string name, bool value) nothrow @trusted @nogc {
196         glUniform1i(glGetUniformLocation(_programID, cast(const(char)*)name), cast(int)value);
197     }
198     /// Load int uniform using uniform name and value.
199     override void loadUniform(string name, int value) nothrow @trusted @nogc {
200         glUniform1i(glGetUniformLocation(_programID, cast(const(char)*)name), value);
201     }
202     /// Load uint uniform using uniform name and value.
203     override void loadUniform(string name, uint value) nothrow @trusted @nogc {
204         glUniform1ui(glGetUniformLocation(_programID, cast(const(char)*)name), value);
205     }
206     /// Load float uniform using uniform name and value.
207     override void loadUniform(string name, float value) nothrow @trusted @nogc {
208         glUniform1f(glGetUniformLocation(_programID, cast(const(char)*)name), value);
209     }
210     /// Load Vector2F uniform using uniform name and value.
211     override void loadUniform(string name, Vector2F vector) nothrow @trusted @nogc {
212         glUniform2f(glGetUniformLocation(_programID, cast(const(char)*)name), vector.x, vector.y);
213     }
214     /// Load Vector3F uniform using uniform name and value.
215     override void loadUniform(string name, Vector3F vector) nothrow @trusted @nogc {
216         glUniform3f(glGetUniformLocation(_programID, cast(const(char)*)name), vector.x, vector.y, vector.z);
217     }
218     /// Load Vector4F uniform using uniform name and value.
219     override void loadUniform(string name, Vector4F vector) nothrow @trusted @nogc {
220         glUniform4f(glGetUniformLocation(_programID, cast(const(char)*)name), vector.x, vector.y, vector.z, vector.w);
221     }
222     /// Load Matrix4F uniform using uniform name and value.
223     override void loadUniform(string name, Matrix4F matrix) nothrow @trusted @nogc {
224         glUniformMatrix4fv(glGetUniformLocation(_programID, cast(const(char)*)name), 1, GL_TRUE, matrix.ptr);
225     }
226     private static int loadShader(string code, ShaderType type) {
227         import std.string : splitLines;
228         string[] lines = splitLines(code);
229         size_t lineCount = lines.length;
230         auto lengths = new int[lineCount];
231         auto addresses = new immutable(char)*[lineCount];
232         auto localLines = new string[lineCount];
233         for (size_t i = 0; i < lineCount; i++) {
234             localLines[i] = lines[i] ~ "\n";
235             lengths[i] = cast(int)(localLines[i].length);
236             addresses[i] = localLines[i].ptr;
237         }
238         int shaderID;
239         final switch(type) with(ShaderType) {
240             case Vertex:
241                 shaderID = glCreateShader(GL_VERTEX_SHADER);
242                 break;
243             case Fragment:
244                 shaderID = glCreateShader(GL_FRAGMENT_SHADER);
245                 break;
246             case Geometry:
247                 throw new UnsupportedVideoFeatureException("Geometry shader is currently unsupported!");
248             case TesselationControl:
249                 throw new UnsupportedVideoFeatureException("TesselationControl shader is currently unsupported!");
250             case TesselationEval:
251                 throw new UnsupportedVideoFeatureException("TesselationEval shader is currently unsupported!");
252             case Compute:
253                 throw new UnsupportedVideoFeatureException("Compute shader is currently unsupported!");
254             case Primitive:
255                 throw new UnsupportedVideoFeatureException("Primitive shader is currently unsupported!");
256             case Stencil:
257                 throw new UnsupportedVideoFeatureException("Stencil shader is currently unsupported!");
258         }
259         glShaderSource(shaderID, cast(int)lineCount, cast(const(char*)*)addresses.ptr, cast(const(int)*)(lengths.ptr));
260         glCompileShader(shaderID);
261         //int success;
262         //char[512] infoLog;
263         //glGetShaderiv(type, GL_COMPILE_STATUS, &success);
264         //if(!success) {
265         //    glGetShaderInfoLog(type, 512, null, infoLog.ptr);
266         //    assert(0, "ERROR::SHADER::VERTEX::COMPILATION_FAILED:" ~ infoLog);
267         //}
268         return shaderID;
269     }
270 }