/// <summary> /// Check the completion status of the shader compile task, and run callbacks on step completion. /// Calling this periodically is required to progress through steps of the compilation. /// </summary> /// <returns>True if the task is complete, false if it is in progress</returns> public bool IsDone() { if (_compiling) { ProgramLinkStatus status = _program.CheckProgramLink(false); if (status != ProgramLinkStatus.Incomplete) { return(_action(status == ProgramLinkStatus.Success, this)); } } else { // Waiting on the task. if (_programsTask.IsCompleted) { return(_action(true, this)); } } return(false); }
/// <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); }