1 module polyplex.core.render.gl.shader; 2 import polyplex.core.render; 3 import bindbc.sdl; 4 import bindbc.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 Gets whenever this shader is attached. 51 */ 52 public override @property bool Attached() { 53 int i; 54 glGetIntegerv(GL_CURRENT_PROGRAM, &i); 55 if (i == shaderprogram) return true; 56 return false; 57 } 58 59 //Uniform stuff. 60 public override void SetUniform(int location, float value) { if (!Attached) Attach(); glUniform1f(cast(GLint)location, cast(GLfloat)value); } 61 public override void SetUniform(int location, Vector2 value) { if (!Attached) Attach(); glUniform2f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y); } 62 public override void SetUniform(int location, Vector3 value) { if (!Attached) Attach(); glUniform3f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z); } 63 public override void SetUniform(int location, Vector4 value) { if (!Attached) Attach(); glUniform4f(cast(GLint)location, cast(GLfloat)value.X, cast(GLfloat)value.Y, cast(GLfloat)value.Z, cast(GLfloat)value.W); } 64 public override void SetUniform(int location, int value) { if (!Attached) Attach(); glUniform1i(cast(GLint)location, cast(GLint)value); } 65 public override void SetUniform(int location, Vector2i value) { if (!Attached) Attach(); glUniform2i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y); } 66 public override void SetUniform(int location, Vector3i value) { if (!Attached) Attach(); glUniform3i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z); } 67 public override void SetUniform(int location, Vector4i value) { if (!Attached) Attach(); glUniform4i(cast(GLint)location, cast(GLint)value.X, cast(GLint)value.Y, cast(GLint)value.Z, cast(GLint)value.W); } 68 public override void SetUniform(int location, Matrix2x2 value) { if (!Attached) Attach(); glUniformMatrix2fv(location, 1, GL_TRUE, value.ptr); } 69 public override void SetUniform(int location, Matrix3x3 value) { if (!Attached) Attach(); glUniformMatrix3fv(location, 1, GL_TRUE, value.ptr); } 70 public override void SetUniform(int location, Matrix4x4 value) { if (!Attached) Attach(); glUniformMatrix4fv(location, 1, GL_TRUE, value.ptr); } 71 72 /** 73 GetUniform gets the position of a uniform. 74 */ 75 public override int GetUniform(string name) { 76 import std.string; 77 return glGetUniformLocation(shaderprogram, name.toStringz); 78 } 79 80 /** 81 HasUniform checks whenever the shader contains a unform with name "name" 82 Returns true if it exists, false otherwise. 83 */ 84 public override bool HasUniform(string name) { 85 auto u = GetUniform(name); 86 if (u == -1) return false; 87 return true; 88 } 89 90 /** 91 Attach attaches the shader. 92 */ 93 public override void Attach() { 94 glUseProgram(this.shaderprogram); 95 } 96 97 /** 98 Detatch detatches the shader. (Binds shader 0) 99 */ 100 public override void Detach() { 101 glUseProgram(0); 102 } 103 104 //Shader linking 105 private void link_shaders() { 106 glLinkProgram(shaderprogram); 107 int c; 108 glGetProgramiv(shaderprogram, GL_LINK_STATUS, &c); 109 if (c == 0) { 110 log_shader(shaderprogram, LogType.Program); 111 return; 112 } 113 c = 0; 114 glValidateProgram(shaderprogram); 115 glGetProgramiv(shaderprogram, GL_VALIDATE_STATUS, &c); 116 if (c == 0) { 117 log_shader(shaderprogram, LogType.Program); 118 return; 119 } 120 //TODO: Add logging 121 //writeln("Compilation completed."); 122 } 123 124 //Compilation of shaders 125 private void compile_shaders() { 126 compile_shader(shadersource, ShaderType.Vertex); 127 if (!(shadersource.Geometry is null)) compile_shader(shadersource, ShaderType.Geometry); 128 compile_shader(shadersource, ShaderType.Fragment); 129 shaderprogram = glCreateProgram(); 130 glAttachShader(shaderprogram, vertexshader); 131 if (!(shadersource.Geometry is null)) glAttachShader(shaderprogram, geometryshader); 132 glAttachShader(shaderprogram, fragmentshader); 133 } 134 135 private void compile_shader(ShaderCode code, ShaderType type) { 136 if (type == ShaderType.Vertex) { 137 vertexshader = glCreateShader(GL_VERTEX_SHADER); 138 139 //Get source 140 int l = cast(int)shadersource.Vertex.length; 141 GLchar* cs = VertGL; 142 glShaderSource(vertexshader, 1, &cs, &l); 143 144 //Compile 145 glCompileShader(vertexshader); 146 int c; 147 glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &c); 148 if (c == 0) { 149 log_shader(vertexshader); 150 return; 151 } 152 } else if (type == ShaderType.Geometry) { 153 geometryshader = glCreateShader(GL_GEOMETRY_SHADER); 154 155 //Get source 156 int l = cast(int)shadersource.Geometry.length; 157 GLchar* cs = GeoGL; 158 glShaderSource(geometryshader, 1, &cs, &l); 159 160 //Compile 161 glCompileShader(geometryshader); 162 int c; 163 glGetShaderiv(geometryshader, GL_COMPILE_STATUS, &c); 164 if (c == 0) { 165 log_shader(geometryshader); 166 return; 167 } 168 } else { 169 fragmentshader = glCreateShader(GL_FRAGMENT_SHADER); 170 171 //Get source 172 int l = cast(int)shadersource.Fragment.length; 173 GLchar* cs = FragGL; 174 glShaderSource(fragmentshader, 1, &cs, &l); 175 176 //Compile 177 glCompileShader(fragmentshader); 178 int c; 179 glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &c); 180 if (c != GL_TRUE) { 181 log_shader(fragmentshader); 182 return; 183 } 184 } 185 } 186 187 enum LogType { 188 Program, 189 Shader 190 } 191 192 private void log_shader(GLuint port, LogType type = LogType.Shader) { 193 if (type == LogType.Shader) { 194 int maxlen = 512; 195 char[] logmsg; 196 logmsg.length = maxlen; 197 glGetShaderInfoLog(port, maxlen, &maxlen, logmsg.ptr); 198 throw new Error("GLSLShaderError (shader: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen])); 199 } 200 int maxlen = 512; 201 char[] logmsg; 202 logmsg.length = maxlen; 203 glGetProgramInfoLog(port, maxlen, &maxlen, logmsg.ptr); 204 throw new Error("GLSLProgramError (program: " ~ to!string(port) ~ ")" ~ to!string(logmsg[0..maxlen])); 205 } 206 } 207