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