A game framework written with osu! in mind.

Allow overriding of GL-specific shader logic for testing purposes

+91 -78
+88 -77
osu.Framework/Graphics/Shaders/Shader.cs
··· 19 private int programID = -1; 20 21 internal readonly Dictionary<string, IUniform> Uniforms = new Dictionary<string, IUniform>(); 22 - private IUniform[] uniformsArray; 23 private readonly List<ShaderPart> parts; 24 25 internal Shader(string name, List<ShaderPart> parts) ··· 34 { 35 parts.RemoveAll(p => p == null); 36 Uniforms.Clear(); 37 - uniformsArray = null; 38 39 if (parts.Count == 0) 40 return; 41 42 - programID = GL.CreateProgram(); 43 44 foreach (ShaderPart p in parts) 45 { 46 if (!p.Compiled) p.Compile(); ··· 56 foreach (var part in parts) 57 GL.DetachShader(this, part); 58 59 - IsLoaded = linkResult == 1; 60 61 - if (!IsLoaded) 62 - throw new ProgramLinkingFailedException(name, GL.GetProgramInfoLog(this)); 63 - 64 - // Obtain all the shader uniforms 65 GL.GetProgram(this, GetProgramParameterName.ActiveUniforms, out int uniformCount); 66 - uniformsArray = new IUniform[uniformCount]; 67 68 for (int i = 0; i < uniformCount; i++) 69 { 70 GL.GetActiveUniform(this, i, 100, out _, out _, out ActiveUniformType type, out string uniformName); 71 72 - IUniform createUniform<T>(string name) 73 - where T : struct, IEquatable<T> 74 - { 75 - int location = GL.GetUniformLocation(this, name); 76 - 77 - if (GlobalPropertyManager.CheckGlobalExists(name)) return new GlobalUniform<T>(this, name, location); 78 - 79 - return new Uniform<T>(this, name, location); 80 - } 81 - 82 - IUniform uniform; 83 - 84 switch (type) 85 { 86 case ActiveUniformType.Bool: 87 - uniform = createUniform<bool>(uniformName); 88 break; 89 90 case ActiveUniformType.Float: 91 - uniform = createUniform<float>(uniformName); 92 break; 93 94 case ActiveUniformType.Int: 95 - uniform = createUniform<int>(uniformName); 96 break; 97 98 case ActiveUniformType.FloatMat3: 99 - uniform = createUniform<Matrix3>(uniformName); 100 break; 101 102 case ActiveUniformType.FloatMat4: 103 - uniform = createUniform<Matrix4>(uniformName); 104 break; 105 106 case ActiveUniformType.FloatVec2: 107 - uniform = createUniform<Vector2>(uniformName); 108 break; 109 110 case ActiveUniformType.FloatVec3: 111 - uniform = createUniform<Vector3>(uniformName); 112 break; 113 114 case ActiveUniformType.FloatVec4: 115 - uniform = createUniform<Vector4>(uniformName); 116 break; 117 118 case ActiveUniformType.Sampler2D: 119 - uniform = createUniform<int>(uniformName); 120 break; 121 - 122 - default: 123 - continue; 124 } 125 - 126 - uniformsArray[i] = uniform; 127 - Uniforms.Add(uniformName, uniformsArray[i]); 128 } 129 130 - GlobalPropertyManager.Register(this); 131 - } 132 - 133 - internal void EnsureLoaded() 134 - { 135 - if (!IsLoaded) 136 - Compile(); 137 - } 138 - 139 - public void Bind() 140 - { 141 - if (IsBound) 142 - return; 143 - 144 - EnsureLoaded(); 145 - 146 - GLWrapper.UseProgram(this); 147 148 - foreach (var uniform in uniformsArray) 149 - uniform?.Update(); 150 151 - IsBound = true; 152 } 153 154 - public void Unbind() 155 - { 156 - if (!IsBound) 157 - return; 158 159 - GLWrapper.UseProgram(null); 160 161 - IsBound = false; 162 } 163 164 public override string ToString() => $@"{name} Shader (Compiled: {programID != -1})"; 165 166 - /// <summary> 167 - /// Returns a uniform from the shader. 168 - /// </summary> 169 - /// <param name="name">The name of the uniform.</param> 170 - /// <returns>Returns a base uniform.</returns> 171 - public Uniform<T> GetUniform<T>(string name) 172 - where T : struct, IEquatable<T> 173 - { 174 - EnsureLoaded(); 175 176 - return (Uniform<T>)Uniforms[name]; 177 - } 178 - 179 - public static implicit operator int(Shader shader) => shader.programID; 180 181 protected internal bool IsDisposed { get; private set; } 182 ··· 199 200 GlobalPropertyManager.Unregister(this); 201 202 - if (programID != -1) 203 - GL.DeleteProgram(this); 204 } 205 } 206 207 public class PartCompilationFailedException : Exception 208 {
··· 19 private int programID = -1; 20 21 internal readonly Dictionary<string, IUniform> Uniforms = new Dictionary<string, IUniform>(); 22 private readonly List<ShaderPart> parts; 23 24 internal Shader(string name, List<ShaderPart> parts) ··· 33 { 34 parts.RemoveAll(p => p == null); 35 Uniforms.Clear(); 36 37 if (parts.Count == 0) 38 return; 39 40 + programID = CreateProgram(); 41 + 42 + if (!CompileInternal()) 43 + throw new ProgramLinkingFailedException(name, GetProgramLog()); 44 + 45 + IsLoaded = true; 46 + 47 + SetupUniforms(); 48 + 49 + GlobalPropertyManager.Register(this); 50 + } 51 + 52 + internal void EnsureLoaded() 53 + { 54 + if (!IsLoaded) 55 + Compile(); 56 + } 57 + 58 + public void Bind() 59 + { 60 + if (IsBound) 61 + return; 62 + 63 + EnsureLoaded(); 64 + 65 + GLWrapper.UseProgram(this); 66 + 67 + foreach (var uniform in Uniforms.Values) 68 + uniform?.Update(); 69 + 70 + IsBound = true; 71 + } 72 + 73 + public void Unbind() 74 + { 75 + if (!IsBound) 76 + return; 77 + 78 + GLWrapper.UseProgram(null); 79 + 80 + IsBound = false; 81 + } 82 + 83 + /// <summary> 84 + /// Returns a uniform from the shader. 85 + /// </summary> 86 + /// <param name="name">The name of the uniform.</param> 87 + /// <returns>Returns a base uniform.</returns> 88 + public Uniform<T> GetUniform<T>(string name) 89 + where T : struct, IEquatable<T> 90 + { 91 + EnsureLoaded(); 92 + 93 + return (Uniform<T>)Uniforms[name]; 94 + } 95 96 + protected virtual bool CompileInternal() 97 + { 98 foreach (ShaderPart p in parts) 99 { 100 if (!p.Compiled) p.Compile(); ··· 110 foreach (var part in parts) 111 GL.DetachShader(this, part); 112 113 + return linkResult == 1; 114 + } 115 116 + protected virtual void SetupUniforms() 117 + { 118 GL.GetProgram(this, GetProgramParameterName.ActiveUniforms, out int uniformCount); 119 120 for (int i = 0; i < uniformCount; i++) 121 { 122 GL.GetActiveUniform(this, i, 100, out _, out _, out ActiveUniformType type, out string uniformName); 123 124 switch (type) 125 { 126 case ActiveUniformType.Bool: 127 + Uniforms.Add(uniformName, createUniform<bool>(uniformName)); 128 break; 129 130 case ActiveUniformType.Float: 131 + Uniforms.Add(uniformName, createUniform<float>(uniformName)); 132 break; 133 134 case ActiveUniformType.Int: 135 + Uniforms.Add(uniformName, createUniform<int>(uniformName)); 136 break; 137 138 case ActiveUniformType.FloatMat3: 139 + Uniforms.Add(uniformName, createUniform<Matrix3>(uniformName)); 140 break; 141 142 case ActiveUniformType.FloatMat4: 143 + Uniforms.Add(uniformName, createUniform<Matrix4>(uniformName)); 144 break; 145 146 case ActiveUniformType.FloatVec2: 147 + Uniforms.Add(uniformName, createUniform<Vector2>(uniformName)); 148 break; 149 150 case ActiveUniformType.FloatVec3: 151 + Uniforms.Add(uniformName, createUniform<Vector3>(uniformName)); 152 break; 153 154 case ActiveUniformType.FloatVec4: 155 + Uniforms.Add(uniformName, createUniform<Vector4>(uniformName)); 156 break; 157 158 case ActiveUniformType.Sampler2D: 159 + Uniforms.Add(uniformName, createUniform<int>(uniformName)); 160 break; 161 } 162 } 163 164 + IUniform createUniform<T>(string name) 165 + where T : struct, IEquatable<T> 166 + { 167 + int location = GL.GetUniformLocation(this, name); 168 169 + if (GlobalPropertyManager.CheckGlobalExists(name)) return new GlobalUniform<T>(this, name, location); 170 171 + return new Uniform<T>(this, name, location); 172 + } 173 } 174 175 + protected virtual string GetProgramLog() => GL.GetProgramInfoLog(this); 176 177 + protected virtual int CreateProgram() => GL.CreateProgram(); 178 179 + protected virtual void DeleteProgram(int id) 180 + { 181 + if (id != -1) 182 + GL.DeleteProgram(id); 183 } 184 185 public override string ToString() => $@"{name} Shader (Compiled: {programID != -1})"; 186 187 + public static implicit operator int(Shader shader) => shader.programID; 188 189 + #region IDisposable Support 190 191 protected internal bool IsDisposed { get; private set; } 192 ··· 209 210 GlobalPropertyManager.Unregister(this); 211 212 + DeleteProgram(this); 213 } 214 } 215 + 216 + #endregion 217 218 public class PartCompilationFailedException : Exception 219 {
+3 -1
osu.Framework/Graphics/Shaders/ShaderManager.cs
··· 55 createShaderPart(fragment, ShaderType.FragmentShader) 56 }; 57 58 - return shaderCache[tuple] = new Shader($"{vertex}/{fragment}", parts); 59 } 60 61 private ShaderPart createShaderPart(string name, ShaderType type, bool bypassCache = false) 62 {
··· 55 createShaderPart(fragment, ShaderType.FragmentShader) 56 }; 57 58 + return shaderCache[tuple] = CreateShader($"{vertex}/{fragment}", parts); 59 } 60 + 61 + internal virtual Shader CreateShader(string name, List<ShaderPart> parts) => new Shader(name, parts); 62 63 private ShaderPart createShaderPart(string name, ShaderType type, bool bypassCache = false) 64 {