A game framework written with osu! in mind.
at master 156 lines 5.1 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4#nullable enable 5 6using System; 7using System.Collections.Concurrent; 8using System.Collections.Generic; 9using osu.Framework.Graphics.OpenGL; 10using osu.Framework.IO.Stores; 11using osuTK.Graphics.ES30; 12 13namespace osu.Framework.Graphics.Shaders 14{ 15 public class ShaderManager : IDisposable 16 { 17 private const string shader_prefix = @"sh_"; 18 19 private readonly ConcurrentDictionary<string, ShaderPart> partCache = new ConcurrentDictionary<string, ShaderPart>(); 20 private readonly ConcurrentDictionary<(string, string), Shader> shaderCache = new ConcurrentDictionary<(string, string), Shader>(); 21 22 private readonly IResourceStore<byte[]> store; 23 24 /// <summary> 25 /// Constructs a new <see cref="ShaderManager"/>. 26 /// </summary> 27 public ShaderManager(IResourceStore<byte[]> store) 28 { 29 this.store = store; 30 } 31 32 /// <summary> 33 /// Retrieves raw shader data from the store. 34 /// Use <see cref="Load"/> to retrieve a usable <see cref="IShader"/> instead. 35 /// </summary> 36 /// <param name="name">The shader name.</param> 37 public virtual byte[]? LoadRaw(string name) => store.Get(name); 38 39 /// <summary> 40 /// Retrieves a usable <see cref="IShader"/> given the vertex and fragment shaders. 41 /// </summary> 42 /// <param name="vertex">The vertex shader name.</param> 43 /// <param name="fragment">The fragment shader name.</param> 44 /// <param name="continuousCompilation"></param> 45 public IShader Load(string vertex, string fragment, bool continuousCompilation = false) 46 { 47 var tuple = (vertex, fragment); 48 49 if (shaderCache.TryGetValue(tuple, out Shader? shader)) 50 return shader; 51 52 List<ShaderPart> parts = new List<ShaderPart> 53 { 54 createShaderPart(vertex, ShaderType.VertexShader), 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 { 65 name = ensureValidName(name, type); 66 67 if (!bypassCache && partCache.TryGetValue(name, out ShaderPart? part)) 68 return part; 69 70 byte[]? rawData = LoadRaw(name); 71 72 part = new ShaderPart(name, rawData, type, this); 73 74 //cache even on failure so we don't try and fail every time. 75 partCache[name] = part; 76 return part; 77 } 78 79 private string ensureValidName(string name, ShaderType type) 80 { 81 string ending = getFileEnding(type); 82 83 if (!name.StartsWith(shader_prefix, StringComparison.Ordinal)) 84 name = shader_prefix + name; 85 if (name.EndsWith(ending, StringComparison.Ordinal)) 86 return name; 87 88 return name + ending; 89 } 90 91 private string getFileEnding(ShaderType type) 92 { 93 switch (type) 94 { 95 case ShaderType.FragmentShader: 96 return @".fs"; 97 98 case ShaderType.VertexShader: 99 return @".vs"; 100 } 101 102 return string.Empty; 103 } 104 105 #region IDisposable Support 106 107 private bool isDisposed; 108 109 public void Dispose() 110 { 111 Dispose(true); 112 GC.SuppressFinalize(this); 113 } 114 115 protected virtual void Dispose(bool disposing) 116 { 117 if (!isDisposed) 118 { 119 isDisposed = true; 120 121 store.Dispose(); 122 123 GLWrapper.ScheduleDisposal(() => 124 { 125 foreach (var shader in shaderCache.Values) 126 shader.Dispose(); 127 128 foreach (var part in partCache.Values) 129 part.Dispose(); 130 }); 131 } 132 } 133 134 #endregion 135 } 136 137 public static class VertexShaderDescriptor 138 { 139 public const string TEXTURE_2 = "Texture2D"; 140 public const string TEXTURE_3 = "Texture3D"; 141 public const string POSITION = "Position"; 142 public const string COLOUR = "Colour"; 143 } 144 145 public static class FragmentShaderDescriptor 146 { 147 public const string TEXTURE = "Texture"; 148 public const string TEXTURE_ROUNDED = "TextureRounded"; 149 public const string COLOUR = "Colour"; 150 public const string COLOUR_ROUNDED = "ColourRounded"; 151 public const string GLOW = "Glow"; 152 public const string BLUR = "Blur"; 153 public const string VIDEO = "Video"; 154 public const string VIDEO_ROUNDED = "VideoRounded"; 155 } 156}