/// <summary> /// Checks if graphics shader code from all stages in memory are equal to the cached shaders. /// </summary> /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param> /// <param name="gpShaders">Cached graphics shaders</param> /// <param name="addresses">GPU virtual addresses of all enabled shader stages</param> /// <returns>True if the code is different, false otherwise</returns> private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle gpShaders, ShaderAddresses addresses) { for (int stage = 0; stage < gpShaders.Shaders.Length; stage++) { ShaderCodeHolder shader = gpShaders.Shaders[stage]; ulong gpuVa = 0; switch (stage) { case 0: gpuVa = addresses.Vertex; break; case 1: gpuVa = addresses.TessControl; break; case 2: gpuVa = addresses.TessEvaluation; break; case 3: gpuVa = addresses.Geometry; break; case 4: gpuVa = addresses.Fragment; break; } if (!IsShaderEqual(memoryManager, shader, gpuVa, addresses.VertexA)) { 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="state">Current GPU state</param> /// <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 ShaderBundle GetComputeShader( GpuState state, ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { bool isCached = _cpPrograms.TryGetValue(gpuVa, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedCpShader in list) { if (IsShaderEqual(cachedCpShader, gpuVa)) { return(cachedCpShader); } } } ShaderCodeHolder shader = TranslateComputeShader( state, gpuVa, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); shader.HostShader = _context.Renderer.CompileShader(shader.Program); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); ShaderBundle cpShader = new ShaderBundle(hostProgram, shader); if (!isCached) { list = new List <ShaderBundle>(); _cpPrograms.Add(gpuVa, list); } list.Add(cpShader); return(cpShader); }
/// <summary> /// Checks if compute shader code in memory is equal to the cached shader. /// </summary> /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param> /// <param name="cpShader">Cached compute shader</param> /// <param name="gpuVa">GPU virtual address of the shader code in memory</param> /// <returns>True if the code is different, false otherwise</returns> private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle cpShader, ulong gpuVa) { return(IsShaderEqual(memoryManager, cpShader.Shaders[0], gpuVa)); }
/// <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">GPU state</param> /// <param name="channel">GPU channel</param> /// <param name="gas">GPU accessor state</param> /// <param name="addresses">Addresses of the shaders for each stage</param> /// <returns>Compiled graphics shader code</returns> public ShaderBundle GetGraphicsShader(ref ThreedClassState state, GpuChannel channel, GpuAccessorState gas, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedGpShaders in list) { if (IsShaderEqual(channel.MemoryManager, cachedGpShaders, addresses)) { return(cachedGpShaders); } } } TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1]; TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(ref state); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } TranslationCounts counts = new TranslationCounts(); if (addresses.VertexA != 0) { shaderContexts[0] = DecodeGraphicsShader(channel, gas, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA); } shaderContexts[1] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Vertex, addresses.Vertex); shaderContexts[2] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationControl, addresses.TessControl); shaderContexts[3] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); shaderContexts[4] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Geometry, addresses.Geometry); shaderContexts[5] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Fragment, addresses.Fragment); bool isShaderCacheEnabled = _cacheManager != null; bool isShaderCacheReadOnly = false; Hash128 programCodeHash = default; GuestShaderCacheEntry[] shaderCacheEntries = null; // Current shader cache doesn't support bindless textures for (int i = 0; i < shaderContexts.Length; i++) { if (shaderContexts[i] != null && shaderContexts[i].UsedFeatures.HasFlag(FeatureFlags.Bindless)) { isShaderCacheEnabled = false; break; } } if (isShaderCacheEnabled) { isShaderCacheReadOnly = _cacheManager.IsReadOnly; // Compute hash and prepare data for shader disk cache comparison. shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel.MemoryManager, shaderContexts); programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd); } ShaderBundle gpShaders; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_gpProgramsDiskCache.TryGetValue(programCodeHash, out gpShaders)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) { shaders[stageIndex] = TranslateShader(_dumper, channel.MemoryManager, shaderContexts, stageIndex + 1); } List <IShader> hostShaders = new List <IShader>(); for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); hostProgram.CheckProgramLink(true); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); gpShaders = new ShaderBundle(hostProgram, shaders); if (isShaderCacheEnabled) { _gpProgramsDiskCache.Add(programCodeHash, gpShaders); if (!isShaderCacheReadOnly) { _cacheManager.SaveProgram(ref programCodeHash, CacheHelper.CreateGuestProgramDump(shaderCacheEntries, tfd), hostProgramBinary); } } } if (!isCached) { list = new List <ShaderBundle>(); _gpPrograms.Add(addresses, list); } list.Add(gpShaders); return(gpShaders); }
/// <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="channel">GPU channel</param> /// <param name="gas">GPU accessor state</param> /// <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 ShaderBundle GetComputeShader( GpuChannel channel, GpuAccessorState gas, ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { bool isCached = _cpPrograms.TryGetValue(gpuVa, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedCpShader in list) { if (IsShaderEqual(channel.MemoryManager, cachedCpShader, gpuVa)) { return(cachedCpShader); } } } TranslatorContext[] shaderContexts = new TranslatorContext[1]; shaderContexts[0] = DecodeComputeShader( channel, gas, gpuVa, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); bool isShaderCacheEnabled = _cacheManager != null; bool isShaderCacheReadOnly = false; Hash128 programCodeHash = default; GuestShaderCacheEntry[] shaderCacheEntries = null; // Current shader cache doesn't support bindless textures if (shaderContexts[0].UsedFeatures.HasFlag(FeatureFlags.Bindless)) { isShaderCacheEnabled = false; } if (isShaderCacheEnabled) { isShaderCacheReadOnly = _cacheManager.IsReadOnly; // Compute hash and prepare data for shader disk cache comparison. shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel.MemoryManager, shaderContexts); programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries); } ShaderBundle cpShader; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_cpProgramsDiskCache.TryGetValue(programCodeHash, out cpShader)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder shader = TranslateShader(_dumper, channel.MemoryManager, shaderContexts[0], null, null); shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); hostProgram.CheckProgramLink(true); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); cpShader = new ShaderBundle(hostProgram, shader); if (isShaderCacheEnabled) { _cpProgramsDiskCache.Add(programCodeHash, cpShader); if (!isShaderCacheReadOnly) { _cacheManager.SaveProgram(ref programCodeHash, CacheHelper.CreateGuestProgramDump(shaderCacheEntries), hostProgramBinary); } } } if (!isCached) { list = new List <ShaderBundle>(); _cpPrograms.Add(gpuVa, list); } list.Add(cpShader); return(cpShader); }
/// <summary> /// Checks if compute shader code in memory is equal to the cached shader. /// </summary> /// <param name="cpShader">Cached compute shader</param> /// <param name="gpuVa">GPU virtual address of the shader code in memory</param> /// <returns>True if the code is different, false otherwise</returns> private bool IsShaderEqual(ShaderBundle cpShader, ulong gpuVa) { return(IsShaderEqual(cpShader.Shaders[0], gpuVa)); }
/// <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 ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedGpShaders in list) { if (IsShaderEqual(cachedGpShaders, addresses)) { return(cachedGpShaders); } } } TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages]; TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(state); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } TranslationCounts counts = new TranslationCounts(); if (addresses.VertexA != 0) { shaderContexts[0] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { shaderContexts[0] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex); } shaderContexts[1] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl); shaderContexts[2] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); shaderContexts[3] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry); shaderContexts[4] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment); bool isShaderCacheEnabled = _cacheManager != null; Hash128 programCodeHash = default; GuestShaderCacheEntry[] shaderCacheEntries = null; if (isShaderCacheEnabled) { // Compute hash and prepare data for shader disk cache comparison. shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts); programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd); } ShaderBundle gpShaders; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_gpProgramsDiskCache.TryGetValue(programCodeHash, out gpShaders)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; shaders[0] = TranslateShader(shaderContexts[0]); shaders[1] = TranslateShader(shaderContexts[1]); shaders[2] = TranslateShader(shaderContexts[2]); shaders[3] = TranslateShader(shaderContexts[3]); shaders[4] = TranslateShader(shaderContexts[4]); List <IShader> hostShaders = new List <IShader>(); for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); gpShaders = new ShaderBundle(hostProgram, shaders); if (isShaderCacheEnabled) { _gpProgramsDiskCache.Add(programCodeHash, gpShaders); _cacheManager.SaveProgram(ref programCodeHash, CacheHelper.CreateGuestProgramDump(shaderCacheEntries, tfd), hostProgramBinary); } } if (!isCached) { list = new List <ShaderBundle>(); _gpPrograms.Add(addresses, list); } list.Add(gpShaders); return(gpShaders); }
/// <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="state">Current GPU state</param> /// <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 ShaderBundle GetComputeShader( GpuState state, ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { bool isCached = _cpPrograms.TryGetValue(gpuVa, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedCpShader in list) { if (IsShaderEqual(cachedCpShader, gpuVa)) { return(cachedCpShader); } } } TranslatorContext[] shaderContexts = new TranslatorContext[1]; shaderContexts[0] = DecodeComputeShader( state, gpuVa, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); bool isShaderCacheEnabled = _cacheManager != null; byte[] programCode = null; Hash128 programCodeHash = default; GuestShaderCacheEntryHeader[] shaderCacheEntries = null; if (isShaderCacheEnabled) { // Compute hash and prepare data for shader disk cache comparison. GetProgramInformations(null, shaderContexts, out programCode, out programCodeHash, out shaderCacheEntries); } ShaderBundle cpShader; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_cpProgramsDiskCache.TryGetValue(programCodeHash, out cpShader)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder shader = TranslateShader(shaderContexts[0]); shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); cpShader = new ShaderBundle(hostProgram, shader); if (isShaderCacheEnabled) { _cpProgramsDiskCache.Add(programCodeHash, cpShader); _cacheManager.SaveProgram(ref programCodeHash, CreateGuestProgramDump(programCode, shaderCacheEntries, null), hostProgramBinary); } } if (!isCached) { list = new List <ShaderBundle>(); _cpPrograms.Add(gpuVa, list); } list.Add(cpShader); return(cpShader); }
/// <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 ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedGpShaders in list) { if (IsShaderEqual(cachedGpShaders, addresses)) { return(cachedGpShaders); } } } ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; var tfd = GetTransformFeedbackDescriptors(state); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } if (addresses.VertexA != 0) { shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex); } shaders[1] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationControl, addresses.TessControl); shaders[2] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); shaders[3] = TranslateGraphicsShader(state, flags, ShaderStage.Geometry, addresses.Geometry); shaders[4] = TranslateGraphicsShader(state, flags, ShaderStage.Fragment, addresses.Fragment); List <IShader> hostShaders = new List <IShader>(); for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); ShaderBundle gpShaders = new ShaderBundle(hostProgram, shaders); if (!isCached) { list = new List <ShaderBundle>(); _gpPrograms.Add(addresses, list); } list.Add(gpShaders); return(gpShaders); }