/// <summary> /// Creates the shader cache file format structure. /// </summary> /// <param name="hash">Hash of the guest shader code</param> /// <param name="pack">Spans of guest shader code</param> /// <param name="code">Host binary shader code</param> /// <param name="info">Shader program information</param> public ShaderCacheFileFormat(int hash, ShaderPack pack, byte[] code, ShaderProgramInfo[] info) { Hash = hash; GuestCode = pack.ToArray(); Code = code; Info = info; }
/// <summary> /// Loads all pre-compiled shaders cached on disk. /// </summary> public void LoadShaderCache() { if (!Configuration.Enabled) { return; } ShaderCacheFileFormat[] cached = ShaderCacheFile.Load(Configuration.ShaderPath); foreach (ShaderCacheFileFormat scff in cached) { IProgram hostProgram = _context.Renderer.CreateProgramFromGpuBinary(scff.Code); Shader shader = new Shader(hostProgram, new ShaderMeta(hostProgram, scff.Info)); ShaderPack pack = new ShaderPack(); for (int index = 0; index < scff.GuestCode.Length; index++) { pack.Add(scff.GuestCode[index]); } _cache.Add(scff.Hash, shader, pack); } }
/// <summary> /// Gets the host shader for the guest shader code, /// or the default value for the type if not found. /// </summary> /// <param name="pack">Pack with spans of guest shader code</param> /// <param name="hash">Calculated hash of all the shader code on the pack</param> /// <returns>Host shader, or the default value if not found</returns> public T Get(ShaderPack pack, out int hash) { Fnv1a hasher = new Fnv1a(); hasher.Initialize(); for (int index = 0; index < pack.Count; index++) { hasher.Add(pack[index]); } hash = hasher.Hash; if (_shaders.TryGetValue(hash, out List <ShaderEntry> list)) { for (int index = 0; index < list.Count; index++) { ShaderEntry entry = list[index]; if (entry.CodeEquals(pack)) { return(entry.Shader); } } } return(default);
/// <summary> /// Gets a graphics shader program from the shader cache. /// This includes all the specified shader stages. /// </summary> /// <remarks> /// This automatically translates, compiles and adds the code to the cache if not present. /// </remarks> /// <param name="state">Current GPU state</param> /// <param name="addresses">Addresses of the shaders for each stage</param> /// <returns>Compiled graphics shader code</returns> public Shader GetGraphicsShader(GpuState state, ShaderAddresses addresses) { ShaderPack pack = new ShaderPack(); if (addresses.Vertex != 0) { pack.Add(GetShaderCode(addresses.Vertex)); } if (addresses.TessControl != 0) { pack.Add(GetShaderCode(addresses.TessControl)); } if (addresses.TessEvaluation != 0) { pack.Add(GetShaderCode(addresses.TessEvaluation)); } gpShaders.Shaders[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl); gpShaders.Shaders[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); gpShaders.Shaders[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry); gpShaders.Shaders[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment); List<IShader> hostShaders = new List<IShader>(); for (int stage = 0; stage < gpShaders.Shaders.Length; stage++) if (addresses.Geometry != 0) { pack.Add(GetShaderCode(addresses.Geometry)); } if (addresses.Fragment != 0) { pack.Add(GetShaderCode(addresses.Fragment)); } Shader gs = _cache.Get(pack, out int hash); if (gs != null) { return gs; } ShaderProgram[] programs = new ShaderProgram[5]; programs[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex); programs[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl); programs[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); programs[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry); programs[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment); BackpropQualifiers(programs); IShader[] shaders = new IShader[programs.Count(x => x != null)];
/// <summary> /// Saves shader code to disk. /// </summary> /// <param name="basePath">Base path of the shader cache</param> /// <param name="info">Array of shader program information for all stages</param> /// <param name="pack">Pack with spans of guest shader code</param> /// <param name="code">Host binary shader code</param> /// <param name="hash">Hash calculated from the guest shader code</param> /// <param name="index">Index to disambiguate the shader in case of hash collisions</param> public static void Save(string basePath, ShaderProgramInfo[] info, ShaderPack pack, byte[] code, int hash, int index) { ShaderCacheFileFormat scff = new ShaderCacheFileFormat(hash, pack, code, info); BinaryFormatter formatter = new BinaryFormatter(); string fileName = GetShaderPath(basePath, hash, index); using FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); formatter.Serialize(fs, scff); }
/// <summary> /// Checks if the shader code on this entry is equal to the guest shader code in memory. /// </summary> /// <param name="pack">Pack of guest shader code</param> /// <returns>True if the code is equal, false otherwise</returns> public bool CodeEquals(ShaderPack pack) { if (pack.Count != _code.Length) { return(false); } for (int index = 0; index < pack.Count; index++) { if (!pack[index].SequenceEqual(_code[index])) { return(false); } } return(true); }
/// <summary> /// Gets a compute shader from the cache. /// </summary> /// <remarks> /// This automatically translates, compiles and adds the code to the cache if not present. /// </remarks> /// <param name="gpuVa">GPU virtual address of the binary shader code</param> /// <param name="localSizeX">Local group size X of the computer shader</param> /// <param name="localSizeY">Local group size Y of the computer shader</param> /// <param name="localSizeZ">Local group size Z of the computer shader</param> /// <param name="localMemorySize">Local memory size of the compute shader</param> /// <param name="sharedMemorySize">Shared memory size of the compute shader</param> /// <returns>Compiled compute shader code</returns> public Shader GetComputeShader( ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { if (gpuVa == 0) { return(null); } ShaderPack pack = new ShaderPack(); ReadOnlySpan <byte> code = GetShaderCodeHeaderless(gpuVa); pack.Add(code); Shader cs = _cache.Get(pack, out int hash); if (cs != null) { return(cs); } _dumper.Dump(code, compute: true, out string fullPath, out string codePath); int QueryInfo(QueryInfoName info, int index) { return(info switch { QueryInfoName.ComputeLocalSizeX => localSizeX, QueryInfoName.ComputeLocalSizeY => localSizeY, QueryInfoName.ComputeLocalSizeZ => localSizeZ, QueryInfoName.ComputeLocalMemorySize => localMemorySize, QueryInfoName.ComputeSharedMemorySize => sharedMemorySize, _ => QueryInfoCommon(info) }); }
public Shader GetComputeShader( ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { if (gpuVa == 0) { return null; } ShaderPack pack = new ShaderPack(); ReadOnlySpan<byte> code = GetShaderCodeHeaderless(gpuVa); pack.Add(code); Shader cs = _cache.Get(pack, out int hash); if (cs != null) { return cs; } _dumper.Dump(code, compute: true, out string fullPath, out string codePath); int QueryInfo(QueryInfoName info, int index) { return info switch { QueryInfoName.ComputeLocalSizeX => localSizeX, QueryInfoName.ComputeLocalSizeY => localSizeY, QueryInfoName.ComputeLocalSizeZ => localSizeZ, QueryInfoName.ComputeLocalMemorySize => localMemorySize, QueryInfoName.ComputeSharedMemorySize => sharedMemorySize, _ => QueryInfoCommon(info) }; } CachedShader shader = TranslateComputeShader( state, gpuVa, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); TranslatorCallbacks callbacks = new TranslatorCallbacks(QueryInfo, PrintLog); ShaderProgram program = Translator.Translate(code, callbacks, DefaultFlags | TranslationFlags.Compute); if (fullPath != null && codePath != null) { program.Prepend("// " + codePath); program.Prepend("// " + fullPath); } IShader shader = _context.Renderer.CompileShader(program); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader }); cs = new Shader(hostProgram, new ShaderMeta(hostProgram, program.Info)); int insertIndex = _cache.Add(hash, cs, pack); if (Configuration.Enabled) { ShaderProgramInfo[] info = new ShaderProgramInfo[] { program.Info }; ShaderCacheFile.Save(Configuration.ShaderPath, info, pack, hostProgram.GetGpuBinary(), hash, insertIndex); } return cs; }