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 }