1 module polyplex.core.render.gl.shader; 2 import polyplex.core.render; 3 import derelict.sdl2.sdl; 4 import derelict.opengl; 5 import polyplex.math; 6 import polyplex.utils.logging; 7 import std.conv; 8 9 10 class GLShader : Shader { 11 private GLuint shaderprogram; 12 private GLuint vertexshader = 0; 13 private GLuint fragmentshader = 0; 14 private GLuint geometryshader = 0; 15 private ShaderCode shadersource; 16 17 this(ShaderCode code) { 18 shadersource = code; 19 compile_shaders(); 20 link_shaders(); 21 } 22 23 ~this() { 24 glUseProgram(0); 25 glDetachShader(shaderprogram, vertexshader); 26 if (geometryshader != 0) glDetachShader(shaderprogram, geometryshader); 27 glDetachShader(shaderprogram, fragmentshader); 28 glDeleteProgram(shaderprogram); 29 glDeleteShader(vertexshader); 30 if (geometryshader != 0) glDeleteShader(geometryshader); 31 glDeleteShader(fragmentshader); 32 } 33 34 /** 35 Vertex Shader Id. 36 */ 37 public @property GLchar* VertGL() { return cast(GLchar*)(shadersource.Vertex.ptr); } 38 39 /** 40 Fragment Shader Id. 41 */ 42 public @property GLchar* FragGL() { return cast(GLchar*)(shadersource.Fragment.ptr); } 43 44 /** 45 Geometry Shader Id. 46 */ 47 public @property GLchar* GeoGL() { return cast(GLchar*)(shadersource.Geometry.ptr); } 48 49 /** 50 Shader Attributes. 51 */ 52 public @property GLchar*[] AttribGL() { 53 GLchar*[] attributes = new GLchar*[shadersource.Attributes.length]; 54 for (int i = 0; i < attributes.length; i++) { 55 string attr = shadersource.Attributes[i]; 56 char[] buffer = (attr ~ '\0').dup; 57 attributes[i] = buffer.ptr; 58 } 59 return attributes; 60 } 61 62 /** 63 Gets whenever this shader is attached. 64 */ 65 public override @property bool Attached() { 66 int i; 67 glGetIntegerv(GL_CURRENT_PROGRAM, &i); 68 if (i == shaderprogram) return true; 69 return false; 70 } 71 72 //Uniform stuff. 73 public override void SetUniform(int location, float value) { glUniform1f(cast(GLint)location, cast(GLfloat)value); } 74 public override void SetUniform(int location, Vector2 value) { glUniform2f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y); } 75 public override void SetUniform(int location, Vector3 value) { glUniform3f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z); } 76 public override void SetUniform(int location, Vector4 value) { glUniform4f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z, cast(GLfloat)value.W); } 77 public override void SetUniform(int location, int value) { glUniform1i(cast(GLint)location, cast(GLint)value); } 78 public override void SetUniform(int location, Vector2i value) { glUniform2i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y); } 79 public override void SetUniform(int location, Vector3i value) { glUniform3i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z); } 80 public override void SetUniform(int location, Vector4i value) { glUniform4i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z, cast(GLint)value.W); } 81 public override void SetUniform(int location, Matrix2x2 value) { glUniformMatrix2fv(location, 1, GL_TRUE, value.ptr); } 82 public override void SetUniform(int location, Matrix3x3 value) { glUniformMatrix3fv(location, 1, GL_TRUE, value.ptr); } 83 public override void SetUniform(int location, Matrix4x4 value) { glUniformMatrix4fv(location, 1, GL_TRUE, value.ptr); } 84 85 /** 86 GetUniform gets the position of a uniform. 87 */ 88 public override uint GetUniform(string name) { 89 bool wasAttached = true; 90 if (!Attached) { 91 wasAttached = false; 92 Attach(); 93 } 94 uint u = glGetUniformLocation(shaderprogram, name.ptr); 95 if (!wasAttached) Detach(); 96 return u; 97 } 98 99 /** 100 HasUniform checks whenever the shader contains a unform with name "name" 101 Returns true if it exists, false otherwise. 102 */ 103 public override bool HasUniform(string name) { 104 auto u = GetUniform(name); 105 if (u == -1) return false; 106 return true; 107 } 108 109 /** 110 Attach attaches the shader. 111 */ 112 public override void Attach() { 113 glUseProgram(this.shaderprogram); 114 } 115 116 /** 117 Detatch detatches the shader. (Binds shader 0) 118 */ 119 public override void Detach() { 120 glUseProgram(0); 121 } 122 123 //Shader linking 124 private void link_shaders() { 125 bind_attribs(); 126 glLinkProgram(shaderprogram); 127 int c; 128 glGetProgramiv(shaderprogram, GL_LINK_STATUS, &c); 129 if (c == 0) { 130 log_shader(shaderprogram, LogType.Program); 131 return; 132 } 133 c = 0; 134 glValidateProgram(shaderprogram); 135 glGetProgramiv(shaderprogram, GL_VALIDATE_STATUS, &c); 136 if (c == 0) { 137 log_shader(shaderprogram, LogType.Program); 138 return; 139 } 140 //TODO: Add logging 141 //writeln("Compilation completed."); 142 } 143 144 private void bind_attribs() { 145 GLchar*[] attribs = AttribGL; 146 for (int i = 0; i < attribs.length; i++) { 147 //TODO: Add logging 148 //writeln("Binding attribute ", i, " named ", to!string(attribs[i])); 149 Logger.Debug("Binding attribute {0} named {1}...", i, attribs[i]); 150 glBindAttribLocation(shaderprogram, i, attribs[i]); 151 } 152 } 153 154 //Compilation of shaders 155 private void compile_shaders() { 156 compile_shader(shadersource, ShaderType.Vertex); 157 if (!(shadersource.Geometry is null)) compile_shader(shadersource, ShaderType.Geometry); 158 compile_shader(shadersource, ShaderType.Fragment); 159 shaderprogram = glCreateProgram(); 160 glAttachShader(shaderprogram, vertexshader); 161 if (!(shadersource.Geometry is null)) glAttachShader(shaderprogram, geometryshader); 162 glAttachShader(shaderprogram, fragmentshader); 163 } 164 165 private void compile_shader(ShaderCode code, ShaderType type) { 166 if (type == ShaderType.Vertex) { 167 vertexshader = glCreateShader(GL_VERTEX_SHADER); 168 169 //Get source 170 int l = cast(int)shadersource.Vertex.length; 171 GLchar* cs = VertGL; 172 glShaderSource(vertexshader, 1, &cs, &l); 173 174 //Compile 175 glCompileShader(vertexshader); 176 int c; 177 glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &c); 178 if (c == 0) { 179 log_shader(vertexshader); 180 return; 181 } 182 } else if (type == ShaderType.Geometry) { 183 geometryshader = glCreateShader(GL_GEOMETRY_SHADER); 184 185 //Get source 186 int l = cast(int)shadersource.Geometry.length; 187 GLchar* cs = GeoGL; 188 glShaderSource(geometryshader, 1, &cs, &l); 189 190 //Compile 191 glCompileShader(geometryshader); 192 int c; 193 glGetShaderiv(geometryshader, GL_COMPILE_STATUS, &c); 194 if (c == 0) { 195 log_shader(geometryshader); 196 return; 197 } 198 } else { 199 fragmentshader = glCreateShader(GL_FRAGMENT_SHADER); 200 201 //Get source 202 int l = cast(int)shadersource.Fragment.length; 203 GLchar* cs = FragGL; 204 glShaderSource(fragmentshader, 1, &cs, &l); 205 206 //Compile 207 glCompileShader(fragmentshader); 208 int c; 209 glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &c); 210 if (c != GL_TRUE) { 211 log_shader(fragmentshader); 212 return; 213 } 214 } 215 } 216 217 enum LogType { 218 Program, 219 Shader 220 } 221 222 private void log_shader(GLuint port, LogType type = LogType.Shader) { 223 if (type == LogType.Shader) { 224 int maxlen = 512; 225 char[] logmsg; 226 logmsg.length = maxlen; 227 glGetShaderInfoLog(port, maxlen, &maxlen, logmsg.ptr); 228 throw new Error("GLSLShaderError (shader: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen])); 229 } 230 int maxlen = 512; 231 char[] logmsg; 232 logmsg.length = maxlen; 233 glGetProgramInfoLog(port, maxlen, &maxlen, logmsg.ptr); 234 throw new Error("GLSLProgramError (program: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen])); 235 } 236 }