diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 1133 |
1 files changed, 321 insertions, 812 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index f3870900..03d5ecad 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -1,18 +1,14 @@ -using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Shader.Cache; -using Ryujinx.Graphics.Gpu.Shader.Cache.Definition; +using Ryujinx.Graphics.Gpu.Shader.DiskCache; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks; namespace Ryujinx.Graphics.Gpu.Shader { @@ -21,30 +17,66 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> class ShaderCache : IDisposable { - private const TranslationFlags DefaultFlags = TranslationFlags.DebugMode; + /// <summary> + /// Default flags used on the shader translation process. + /// </summary> + public const TranslationFlags DefaultFlags = TranslationFlags.DebugMode; + + private struct TranslatedShader + { + public readonly CachedShaderStage Shader; + public readonly ShaderProgram Program; + + public TranslatedShader(CachedShaderStage shader, ShaderProgram program) + { + Shader = shader; + Program = program; + } + } + + private struct TranslatedShaderVertexPair + { + public readonly CachedShaderStage VertexA; + public readonly CachedShaderStage VertexB; + public readonly ShaderProgram Program; + + public TranslatedShaderVertexPair(CachedShaderStage vertexA, CachedShaderStage vertexB, ShaderProgram program) + { + VertexA = vertexA; + VertexB = vertexB; + Program = program; + } + } private readonly GpuContext _context; private readonly ShaderDumper _dumper; - private readonly Dictionary<ulong, List<ShaderBundle>> _cpPrograms; - private readonly Dictionary<ShaderAddresses, List<ShaderBundle>> _gpPrograms; + private readonly Dictionary<ulong, CachedShaderProgram> _cpPrograms; + private readonly Dictionary<ShaderAddresses, CachedShaderProgram> _gpPrograms; - private CacheManager _cacheManager; + private struct ProgramToSave + { + public readonly CachedShaderProgram CachedProgram; + public readonly IProgram HostProgram; + + public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram) + { + CachedProgram = cachedProgram; + HostProgram = hostProgram; + } + } - private Dictionary<Hash128, ShaderBundle> _gpProgramsDiskCache; - private Dictionary<Hash128, ShaderBundle> _cpProgramsDiskCache; + private Queue<ProgramToSave> _programsToSaveQueue; - private Queue<(IProgram, Action<byte[]>)> _programsToSaveQueue; + private readonly ComputeShaderCacheHashTable _computeShaderCache; + private readonly ShaderCacheHashTable _graphicsShaderCache; + private readonly DiskCacheHostStorage _diskCacheHostStorage; + private readonly BackgroundDiskCacheWriter _cacheWriter; /// <summary> - /// Version of the codegen (to be changed when codegen or guest format change). + /// Event for signalling shader cache loading progress. /// </summary> - private const ulong ShaderCodeGenVersion = 3251; - - // Progress reporting helpers - private volatile int _shaderCount; - private volatile int _totalShaderCount; public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged; /// <summary> @@ -57,12 +89,23 @@ namespace Ryujinx.Graphics.Gpu.Shader _dumper = new ShaderDumper(); - _cpPrograms = new Dictionary<ulong, List<ShaderBundle>>(); - _gpPrograms = new Dictionary<ShaderAddresses, List<ShaderBundle>>(); - _gpProgramsDiskCache = new Dictionary<Hash128, ShaderBundle>(); - _cpProgramsDiskCache = new Dictionary<Hash128, ShaderBundle>(); + _cpPrograms = new Dictionary<ulong, CachedShaderProgram>(); + _gpPrograms = new Dictionary<ShaderAddresses, CachedShaderProgram>(); + + _programsToSaveQueue = new Queue<ProgramToSave>(); + + string diskCacheTitleId = GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null + ? CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId) + : null; + + _computeShaderCache = new ComputeShaderCacheHashTable(); + _graphicsShaderCache = new ShaderCacheHashTable(); + _diskCacheHostStorage = new DiskCacheHostStorage(diskCacheTitleId); - _programsToSaveQueue = new Queue<(IProgram, Action<byte[]>)>(); + if (_diskCacheHostStorage.CacheEnabled) + { + _cacheWriter = new BackgroundDiskCacheWriter(context, _diskCacheHostStorage); + } } /// <summary> @@ -72,13 +115,17 @@ namespace Ryujinx.Graphics.Gpu.Shader { // Check to see if the binaries for previously compiled shaders are ready, and save them out. - while (_programsToSaveQueue.Count > 0) + while (_programsToSaveQueue.TryPeek(out ProgramToSave programToSave)) { - (IProgram program, Action<byte[]> dataAction) = _programsToSaveQueue.Peek(); + ProgramLinkStatus result = programToSave.HostProgram.CheckProgramLink(false); - if (program.CheckProgramLink(false) != ProgramLinkStatus.Incomplete) + if (result != ProgramLinkStatus.Incomplete) { - dataAction(program.GetBinary()); + if (result == ProgramLinkStatus.Success) + { + _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.HostProgram.GetBinary()); + } + _programsToSaveQueue.Dequeue(); } else @@ -91,463 +138,48 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <summary> /// Initialize the cache. /// </summary> - internal void Initialize() + /// <param name="cancellationToken">Cancellation token to cancel the shader cache initialization process</param> + internal void Initialize(CancellationToken cancellationToken) { - if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null) + if (_diskCacheHostStorage.CacheEnabled) { - _cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion); - - bool isReadOnly = _cacheManager.IsReadOnly; - - HashSet<Hash128> invalidEntries = null; - - if (isReadOnly) + if (!_diskCacheHostStorage.CacheExists()) { - Logger.Warning?.Print(LogClass.Gpu, "Loading shader cache in read-only mode (cache in use by another program!)"); - } - else - { - invalidEntries = new HashSet<Hash128>(); - } - - ReadOnlySpan<Hash128> guestProgramList = _cacheManager.GetGuestProgramList(); + // If we don't have a shader cache on the new format, try to perform migration from the old shader cache. + Logger.Info?.Print(LogClass.Gpu, "No shader cache found, trying to migrate from legacy shader cache..."); - using AutoResetEvent progressReportEvent = new AutoResetEvent(false); + int migrationCount = Migration.MigrateFromLegacyCache(_context, _diskCacheHostStorage); - _shaderCount = 0; - _totalShaderCount = guestProgramList.Length; - - ShaderCacheStateChanged?.Invoke(ShaderCacheState.Start, _shaderCount, _totalShaderCount); - Thread progressReportThread = null; - - if (guestProgramList.Length > 0) - { - progressReportThread = new Thread(ReportProgress) - { - Name = "ShaderCache.ProgressReporter", - Priority = ThreadPriority.Lowest, - IsBackground = true - }; - - progressReportThread.Start(progressReportEvent); + Logger.Info?.Print(LogClass.Gpu, $"Migrated {migrationCount} shaders."); } - // Make sure these are initialized before doing compilation. - Capabilities caps = _context.Capabilities; - - int maxTaskCount = Math.Min(Environment.ProcessorCount, 8); - int programIndex = 0; - List<ShaderCompileTask> activeTasks = new List<ShaderCompileTask>(); - - using AutoResetEvent taskDoneEvent = new AutoResetEvent(false); - - // This thread dispatches tasks to do shader translation, and creates programs that OpenGL will link in the background. - // The program link status is checked in a non-blocking manner so that multiple shaders can be compiled at once. - - while (programIndex < guestProgramList.Length || activeTasks.Count > 0) - { - if (activeTasks.Count < maxTaskCount && programIndex < guestProgramList.Length) - { - // Begin a new shader compilation. - Hash128 key = guestProgramList[programIndex]; - - byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key); - bool hasHostCache = hostProgramBinary != null; - - IProgram hostProgram = null; - - // If the program sources aren't in the cache, compile from saved guest program. - byte[] guestProgram = _cacheManager.GetGuestProgramByHash(ref key); - - if (guestProgram == null) - { - Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); - - // Should not happen, but if someone messed with the cache it's better to catch it. - invalidEntries?.Add(key); - - _shaderCount = ++programIndex; - - continue; - } - - ReadOnlySpan<byte> guestProgramReadOnlySpan = guestProgram; - - ReadOnlySpan<GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); - - if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) - { - Debug.Assert(cachedShaderEntries.Length == 1); - - GuestShaderCacheEntry entry = cachedShaderEntries[0]; - - HostShaderCacheEntry[] hostShaderEntries = null; - - // Try loading host shader binary. - if (hasHostCache) - { - hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan); - hostProgramBinary = hostProgramBinarySpan.ToArray(); - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary, false, new ShaderInfo(-1)); - } - - ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); - activeTasks.Add(task); - - task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => - { - ShaderProgram program = null; - ShaderProgramInfo shaderProgramInfo = null; - - if (isHostProgramValid) - { - // Reconstruct code holder. - - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); - - byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); - - ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, code); - - _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); - - return true; - } - else - { - // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. - - Task compileTask = Task.Run(() => - { - var binaryCode = new Memory<byte>(entry.Code); - - var gpuAccessor = new CachedGpuAccessor( - _context, - binaryCode, - binaryCode.Slice(binaryCode.Length - entry.Header.Cb1DataSize), - entry.Header.GpuAccessorHeader, - entry.TextureDescriptors, - null); - - var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute); - program = Translator.CreateContext(0, gpuAccessor, options).Translate(out shaderProgramInfo); - }); - - task.OnTask(compileTask, (bool _, ShaderCompileTask task) => - { - if (task.IsFaulted) - { - Logger.Warning?.Print(LogClass.Gpu, $"Host shader {key} is corrupted or incompatible, discarding..."); + ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader( + _context, + _graphicsShaderCache, + _computeShaderCache, + _diskCacheHostStorage, + cancellationToken, + ShaderCacheStateUpdate); - _cacheManager.RemoveProgram(ref key); - return true; // Exit early, the decoding step failed. - } + loader.LoadShaders(); - byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); - - ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, code); - - Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); - - // Compile shader and create program as the shader program binary got invalidated. - shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, program.Code); - hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, new ShaderInfo(-1)); - - task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => - { - // As the host program was invalidated, save the new entry in the cache. - hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); - - if (!isReadOnly) - { - if (hasHostCache) - { - _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); - } - else - { - Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); - - _cacheManager.AddHostProgram(ref key, hostProgramBinary); - } - } - - _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); - - return true; - }); - - return false; // Not finished: still need to compile the host program. - }); - - return false; // Not finished: translating the program. - } - }); - } - else - { - Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); - - ShaderCodeHolder[] shaders = new ShaderCodeHolder[cachedShaderEntries.Length]; - List<ShaderProgram> shaderPrograms = new List<ShaderProgram>(); - - TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); - - TranslationCounts counts = new TranslationCounts(); - - HostShaderCacheEntry[] hostShaderEntries = null; - - // Try loading host shader binary. - if (hasHostCache) - { - hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> hostProgramBinarySpan); - hostProgramBinary = hostProgramBinarySpan.ToArray(); - - bool hasFragmentShader = false; - int fragmentOutputMap = -1; - int fragmentIndex = (int)ShaderStage.Fragment - 1; - - if (hostShaderEntries[fragmentIndex] != null && hostShaderEntries[fragmentIndex].Header.InUse) - { - hasFragmentShader = true; - fragmentOutputMap = hostShaderEntries[fragmentIndex].Header.FragmentOutputMap; - } - - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary, hasFragmentShader, new ShaderInfo(fragmentOutputMap)); - } - - ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); - activeTasks.Add(task); - - GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); - - task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => - { - Task compileTask = Task.Run(() => - { - TranslatorContext[] shaderContexts = null; - - if (!isHostProgramValid) - { - shaderContexts = new TranslatorContext[1 + entries.Length]; - - for (int i = 0; i < entries.Length; i++) - { - GuestShaderCacheEntry entry = entries[i]; - - if (entry == null) - { - continue; - } - - var binaryCode = new Memory<byte>(entry.Code); - - var gpuAccessor = new CachedGpuAccessor( - _context, - binaryCode, - binaryCode.Slice(binaryCode.Length - entry.Header.Cb1DataSize), - entry.Header.GpuAccessorHeader, - entry.TextureDescriptors, - tfd); - - var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags); - - shaderContexts[i + 1] = Translator.CreateContext(0, gpuAccessor, options, counts); - - if (entry.Header.SizeA != 0) - { - var options2 = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.VertexA); - - shaderContexts[0] = Translator.CreateContext((ulong)entry.Header.Size, gpuAccessor, options2, counts); - } - } - } - - // Reconstruct code holder. - for (int i = 0; i < entries.Length; i++) - { - GuestShaderCacheEntry entry = entries[i]; - - if (entry == null) - { - continue; - } - - ShaderProgram program; - ShaderProgramInfo shaderProgramInfo; - - if (isHostProgramValid) - { - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); - } - else - { - int stageIndex = i + 1; - - TranslatorContext currentStage = shaderContexts[stageIndex]; - TranslatorContext nextStage = GetNextStageContext(shaderContexts, stageIndex); - TranslatorContext vertexA = stageIndex == 1 ? shaderContexts[0] : null; - - program = currentStage.Translate(out shaderProgramInfo, nextStage, vertexA); - } - - // NOTE: Vertex B comes first in the shader cache. - byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); - byte[] code2 = entry.Header.SizeA != 0 ? entry.Code.AsSpan(entry.Header.Size, entry.Header.SizeA).ToArray() : null; - - shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, code, code2); - - shaderPrograms.Add(program); - } - }); - - task.OnTask(compileTask, (bool _, ShaderCompileTask task) => - { - if (task.IsFaulted) - { - Logger.Warning?.Print(LogClass.Gpu, $"Host shader {key} is corrupted or incompatible, discarding..."); - - _cacheManager.RemoveProgram(ref key); - return true; // Exit early, the decoding step failed. - } - - // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. - if (!isHostProgramValid) - { - Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); - - List<IShader> hostShaders = new List<IShader>(); - - // Compile shaders and create program as the shader program binary got invalidated. - 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); - } - - int fragmentIndex = (int)ShaderStage.Fragment - 1; - int fragmentOutputMap = -1; - - if (shaders[fragmentIndex] != null) - { - fragmentOutputMap = shaders[fragmentIndex].Info.FragmentOutputMap; - } - - hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), new ShaderInfo(fragmentOutputMap)); - - task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => - { - // As the host program was invalidated, save the new entry in the cache. - hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); - - if (!isReadOnly) - { - if (hasHostCache) - { - _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); - } - else - { - Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); - - _cacheManager.AddHostProgram(ref key, hostProgramBinary); - } - } - - _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); - - return true; - }); - - return false; // Not finished: still need to compile the host program. - } - else - { - _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); - - return true; - } - }); - - return false; // Not finished: translating the program. - }); - } - - _shaderCount = ++programIndex; - } - - // Process the queue. - for (int i = 0; i < activeTasks.Count; i++) - { - ShaderCompileTask task = activeTasks[i]; - - if (task.IsDone()) - { - activeTasks.RemoveAt(i--); - } - } - - if (activeTasks.Count == maxTaskCount) - { - // Wait for a task to be done, or for 1ms. - // Host shader compilation cannot signal when it is done, - // so the 1ms timeout is required to poll status. - - taskDoneEvent.WaitOne(1); - } - } - - if (!isReadOnly) + int errorCount = loader.ErrorCount; + if (errorCount != 0) { - // Remove entries that are broken in the cache - _cacheManager.RemoveManifestEntries(invalidEntries); - _cacheManager.FlushToArchive(); - _cacheManager.Synchronize(); + Logger.Warning?.Print(LogClass.Gpu, $"Failed to load {errorCount} shaders from the disk cache."); } - - progressReportEvent.Set(); - progressReportThread?.Join(); - - ShaderCacheStateChanged?.Invoke(ShaderCacheState.Loaded, _shaderCount, _totalShaderCount); - - Logger.Info?.Print(LogClass.Gpu, $"Shader cache loaded {_shaderCount} entries."); } } /// <summary> - /// Raises ShaderCacheStateChanged events periodically. + /// Shader cache state update handler. /// </summary> - private void ReportProgress(object state) + /// <param name="state">Current state of the shader cache load process</param> + /// <param name="current">Number of the current shader being processed</param> + /// <param name="total">Total number of shaders to process</param> + private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total) { - const int refreshRate = 50; // ms - - AutoResetEvent endEvent = (AutoResetEvent)state; - - int count = 0; - - do - { - int newCount = _shaderCount; - - if (count != newCount) - { - ShaderCacheStateChanged?.Invoke(ShaderCacheState.Loading, newCount, _totalShaderCount); - count = newCount; - } - } - while (!endEvent.WaitOne(refreshRate)); + ShaderCacheStateChanged?.Invoke(state, current, total); } /// <summary> @@ -557,112 +189,42 @@ namespace Ryujinx.Graphics.Gpu.Shader /// 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="poolState">Texture pool state</param> + /// <param name="computeState">Compute engine 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( + public CachedShaderProgram GetComputeShader( GpuChannel channel, - GpuAccessorState gas, - ulong gpuVa, - int localSizeX, - int localSizeY, - int localSizeZ, - int localMemorySize, - int sharedMemorySize) + GpuChannelPoolState poolState, + GpuChannelComputeState computeState, + ulong gpuVa) { - 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)) + if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, cpShader, gpuVa)) { - isShaderCacheEnabled = false; + return cpShader; } - if (isShaderCacheEnabled) + if (_computeShaderCache.TryFind(channel, poolState, gpuVa, out cpShader, out byte[] cachedGuestCode)) { - isShaderCacheReadOnly = _cacheManager.IsReadOnly; - - // Compute hash and prepare data for shader disk cache comparison. - shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel, shaderContexts); - programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries); + _cpPrograms[gpuVa] = cpShader; + return cpShader; } - 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); + ShaderSpecializationState specState = new ShaderSpecializationState(computeState); + GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState); + GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState); - IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, new ShaderInfo(-1)); + TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, gpuVa); - cpShader = new ShaderBundle(hostProgram, shader); - - if (isShaderCacheEnabled) - { - _cpProgramsDiskCache.Add(programCodeHash, cpShader); - - if (!isShaderCacheReadOnly) - { - byte[] guestProgramDump = CacheHelper.CreateGuestProgramDump(shaderCacheEntries); - _programsToSaveQueue.Enqueue((hostProgram, (byte[] hostProgramBinary) => - { - _cacheManager.SaveProgram(ref programCodeHash, guestProgramDump, HostShaderCacheEntry.Create(hostProgramBinary, new ShaderCodeHolder[] { shader })); - })); - } - } - } + TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode); - if (!isCached) - { - list = new List<ShaderBundle>(); + IProgram hostProgram = _context.Renderer.CreateProgram(new ShaderSource[] { CreateShaderSource(translatedShader.Program) }, new ShaderInfo(-1)); - _cpPrograms.Add(gpuVa, list); - } + cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); - list.Add(cpShader); + _computeShaderCache.Add(cpShader); + EnqueueProgramToSave(new ProgramToSave(cpShader, hostProgram)); + _cpPrograms[gpuVa] = cpShader; return cpShader; } @@ -676,144 +238,142 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </remarks> /// <param name="state">GPU state</param> /// <param name="channel">GPU channel</param> - /// <param name="gas">GPU accessor state</param> + /// <param name="poolState">Texture pool state</param> + /// <param name="graphicsState">3D engine 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) + public CachedShaderProgram GetGraphicsShader( + ref ThreedClassState state, + GpuChannel channel, + GpuChannelPoolState poolState, + GpuChannelGraphicsState graphicsState, + ShaderAddresses addresses) { - bool isCached = _gpPrograms.TryGetValue(addresses, out List<ShaderBundle> list); - - if (isCached) + if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, gpShaders, addresses)) { - foreach (ShaderBundle cachedGpShaders in list) - { - if (IsShaderEqual(channel.MemoryManager, cachedGpShaders, addresses)) - { - return cachedGpShaders; - } - } + return gpShaders; } - TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1]; - - TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(ref state); - - gas.TransformFeedbackDescriptors = tfd; - - TranslationCounts counts = new TranslationCounts(); - - if (addresses.VertexA != 0) + if (_graphicsShaderCache.TryFind(channel, poolState, addresses, out gpShaders, out var cachedGuestCode)) { - shaderContexts[0] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA); + _gpPrograms[addresses] = gpShaders; + return gpShaders; } - shaderContexts[1] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags, ShaderStage.Vertex, addresses.Vertex); - shaderContexts[2] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags, ShaderStage.TessellationControl, addresses.TessControl); - shaderContexts[3] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); - shaderContexts[4] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags, ShaderStage.Geometry, addresses.Geometry); - shaderContexts[5] = DecodeGraphicsShader(channel, gas, counts, DefaultFlags, 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; - } - } + TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state); - if (isShaderCacheEnabled) - { - isShaderCacheReadOnly = _cacheManager.IsReadOnly; + ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, transformFeedbackDescriptors); + GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors); - // Compute hash and prepare data for shader disk cache comparison. - shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel, shaderContexts); - programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd); - } + ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan(); - ShaderBundle gpShaders; + TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1]; + TranslatorContext nextStage = null; - // Search for the program hash in loaded shaders. - if (!isShaderCacheEnabled || !_gpProgramsDiskCache.TryGetValue(programCodeHash, out gpShaders)) + for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--) { - 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>(); + ulong gpuVa = addressesSpan[stageIndex + 1]; - for (int stage = 0; stage < Constants.ShaderStages; stage++) + if (gpuVa != 0) { - ShaderProgram program = shaders[stage]?.Program; + GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex); + TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, gpuVa); - if (program == null) + if (nextStage != null) { - continue; + currentStage.SetNextStage(nextStage); } - IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); - - shaders[stage].HostShader = hostShader; + if (stageIndex == 0 && addresses.VertexA != 0) + { + translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA); + } - hostShaders.Add(hostShader); + translatorContexts[stageIndex + 1] = currentStage; + nextStage = currentStage; } + } - int fragmentIndex = (int)ShaderStage.Fragment - 1; - int fragmentOutputMap = -1; + CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; + List<ShaderSource> shaderSources = new List<ShaderSource>(); + + for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) + { + TranslatorContext currentStage = translatorContexts[stageIndex + 1]; - if (shaders[fragmentIndex] != null) + if (currentStage != null) { - fragmentOutputMap = shaders[fragmentIndex].Info.FragmentOutputMap; - } + ShaderProgram program; - IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), new ShaderInfo(fragmentOutputMap)); + if (stageIndex == 0 && translatorContexts[0] != null) + { + TranslatedShaderVertexPair translatedShader = TranslateShader( + _dumper, + channel, + currentStage, + translatorContexts[0], + cachedGuestCode.VertexACode, + cachedGuestCode.VertexBCode); + + shaders[0] = translatedShader.VertexA; + shaders[1] = translatedShader.VertexB; + program = translatedShader.Program; + } + else + { + byte[] code = cachedGuestCode.GetByIndex(stageIndex); - gpShaders = new ShaderBundle(hostProgram, shaders); + TranslatedShader translatedShader = TranslateShader(_dumper, channel, currentStage, code); - if (isShaderCacheEnabled) - { - _gpProgramsDiskCache.Add(programCodeHash, gpShaders); + shaders[stageIndex + 1] = translatedShader.Shader; + program = translatedShader.Program; + } - if (!isShaderCacheReadOnly) + if (program != null) { - byte[] guestProgramDump = CacheHelper.CreateGuestProgramDump(shaderCacheEntries, tfd); - _programsToSaveQueue.Enqueue((hostProgram, (byte[] hostProgramBinary) => - { - _cacheManager.SaveProgram(ref programCodeHash, guestProgramDump, HostShaderCacheEntry.Create(hostProgramBinary, shaders)); - })); + shaderSources.Add(CreateShaderSource(program)); } } } - if (!isCached) - { - list = new List<ShaderBundle>(); + int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1; + IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap)); - _gpPrograms.Add(addresses, list); - } + gpShaders = new CachedShaderProgram(hostProgram, specState, shaders); - list.Add(gpShaders); + _graphicsShaderCache.Add(gpShaders); + EnqueueProgramToSave(new ProgramToSave(gpShaders, hostProgram)); + _gpPrograms[addresses] = gpShaders; return gpShaders; } /// <summary> + /// Creates a shader source for use with the backend from a translated shader program. + /// </summary> + /// <param name="program">Translated shader program</param> + /// <returns>Shader source</returns> + public static ShaderSource CreateShaderSource(ShaderProgram program) + { + return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language); + } + + /// <summary> + /// Puts a program on the queue of programs to be saved on the disk cache. + /// </summary> + /// <remarks> + /// This will not do anything if disk shader cache is disabled. + /// </remarks> + /// <param name="programToSave">Program to be saved on disk</param> + private void EnqueueProgramToSave(ProgramToSave programToSave) + { + if (_diskCacheHostStorage.CacheEnabled) + { + _programsToSaveQueue.Enqueue(programToSave); + } + } + + /// <summary> /// Gets transform feedback state from the current GPU state. /// </summary> /// <param name="state">Current GPU state</param> @@ -821,7 +381,6 @@ namespace Ryujinx.Graphics.Gpu.Shader private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state) { bool tfEnable = state.TfEnable; - if (!tfEnable) { return null; @@ -833,11 +392,11 @@ namespace Ryujinx.Graphics.Gpu.Shader { var tf = state.TfState[i]; - int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80); - - var varyingLocations = MemoryMarshal.Cast<uint, byte>(state.TfVaryingLocations[i].ToSpan()).Slice(0, length); - - descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations.ToArray()); + descs[i] = new TransformFeedbackDescriptor( + tf.BufferIndex, + tf.Stride, + tf.VaryingsCount, + ref state.TfVaryingLocations[i]); } return descs; @@ -846,46 +405,54 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <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="channel">GPU channel using the shader</param> + /// <param name="poolState">GPU channel state to verify shader compatibility</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) + private static bool IsShaderEqual( + GpuChannel channel, + GpuChannelPoolState poolState, + CachedShaderProgram cpShader, + ulong gpuVa) { - return IsShaderEqual(memoryManager, cpShader.Shaders[0], gpuVa); + if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa)) + { + return cpShader.SpecializationState.MatchesCompute(channel, poolState); + } + + return false; } /// <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="channel">GPU channel using the shader</param> + /// <param name="poolState">GPU channel state to verify shader compatibility</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) + private static bool IsShaderEqual( + GpuChannel channel, + GpuChannelPoolState poolState, + CachedShaderProgram gpShaders, + ShaderAddresses addresses) { - for (int stage = 0; stage < gpShaders.Shaders.Length; stage++) - { - ShaderCodeHolder shader = gpShaders.Shaders[stage]; + ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan(); - ulong gpuVa = 0; + for (int stageIndex = 0; stageIndex < gpShaders.Shaders.Length; stageIndex++) + { + CachedShaderStage shader = gpShaders.Shaders[stageIndex]; - 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; - } + ulong gpuVa = addressesSpan[stageIndex]; - if (!IsShaderEqual(memoryManager, shader, gpuVa, addresses.VertexA)) + if (!IsShaderEqual(channel.MemoryManager, shader, gpuVa)) { return false; } } - return true; + return gpShaders.SpecializationState.MatchesGraphics(channel, poolState); } /// <summary> @@ -894,9 +461,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param> /// <param name="shader">Cached shader to compare with</param> /// <param name="gpuVa">GPU virtual address of the binary shader code</param> - /// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" binary shader code</param> /// <returns>True if the code is different, false otherwise</returns> - private static bool IsShaderEqual(MemoryManager memoryManager, ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0) + private static bool IsShaderEqual(MemoryManager memoryManager, CachedShaderStage shader, ulong gpuVa) { if (shader == null) { @@ -905,47 +471,17 @@ namespace Ryujinx.Graphics.Gpu.Shader ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length); - bool equals = memoryCode.SequenceEqual(shader.Code); - - if (equals && shader.Code2 != null) - { - memoryCode = memoryManager.GetSpan(gpuVaA, shader.Code2.Length); - - equals = memoryCode.SequenceEqual(shader.Code2); - } - - return equals; + return memoryCode.SequenceEqual(shader.Code); } /// <summary> /// Decode the binary Maxwell shader code to a translator context. /// </summary> - /// <param name="channel">GPU channel</param> - /// <param name="gas">GPU accessor state</param> + /// <param name="gpuAccessor">GPU state accessor</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>The generated translator context</returns> - private TranslatorContext DecodeComputeShader( - GpuChannel channel, - GpuAccessorState gas, - ulong gpuVa, - int localSizeX, - int localSizeY, - int localSizeZ, - int localMemorySize, - int sharedMemorySize) + public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, ulong gpuVa) { - if (gpuVa == 0) - { - return null; - } - - GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gas, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); - var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute); return Translator.CreateContext(gpuVa, gpuAccessor, options); } @@ -956,126 +492,105 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <remarks> /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader. /// </remarks> - /// <param name="channel">GPU channel</param> - /// <param name="gas">GPU accessor state</param> - /// <param name="counts">Cumulative shader resource counts</param> + /// <param name="gpuAccessor">GPU state accessor</param> /// <param name="flags">Flags that controls shader translation</param> - /// <param name="stage">Shader stage</param> /// <param name="gpuVa">GPU virtual address of the shader code</param> /// <returns>The generated translator context</returns> - private TranslatorContext DecodeGraphicsShader( - GpuChannel channel, - GpuAccessorState gas, - TranslationCounts counts, - TranslationFlags flags, - ShaderStage stage, - ulong gpuVa) + public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TranslationFlags flags, ulong gpuVa) { - if (gpuVa == 0) - { - return null; - } - - GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gas, (int)stage - 1); - var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags); - return Translator.CreateContext(gpuVa, gpuAccessor, options, counts); + return Translator.CreateContext(gpuVa, gpuAccessor, options); } /// <summary> /// Translates a previously generated translator context to something that the host API accepts. /// </summary> /// <param name="dumper">Optional shader code dumper</param> - /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param> - /// <param name="stages">Translator context of all available shader stages</param> - /// <param name="stageIndex">Index on the stages array to translate</param> + /// <param name="channel">GPU channel using the shader</param> + /// <param name="currentStage">Translator context of the stage to be translated</param> + /// <param name="vertexA">Optional translator context of the shader that should be combined</param> + /// <param name="codeA">Optional Maxwell binary code of the Vertex A shader, if present</param> + /// <param name="codeB">Optional Maxwell binary code of the Vertex B or current stage shader, if present on cache</param> /// <returns>Compiled graphics shader code</returns> - private static ShaderCodeHolder TranslateShader( + private static TranslatedShaderVertexPair TranslateShader( ShaderDumper dumper, - MemoryManager memoryManager, - TranslatorContext[] stages, - int stageIndex) + GpuChannel channel, + TranslatorContext currentStage, + TranslatorContext vertexA, + byte[] codeA, + byte[] codeB) { - TranslatorContext currentStage = stages[stageIndex]; - TranslatorContext nextStage = GetNextStageContext(stages, stageIndex); - TranslatorContext vertexA = stageIndex == 1 ? stages[0] : null; + ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); - return TranslateShader(dumper, memoryManager, currentStage, nextStage, vertexA); - } + var memoryManager = channel.MemoryManager; - /// <summary> - /// Gets the next shader stage context, from an array of contexts and index of the current stage. - /// </summary> - /// <param name="stages">Translator context of all available shader stages</param> - /// <param name="stageIndex">Index on the stages array to translate</param> - /// <returns>The translator context of the next stage, or null if inexistent</returns> - private static TranslatorContext GetNextStageContext(TranslatorContext[] stages, int stageIndex) - { - for (int nextStageIndex = stageIndex + 1; nextStageIndex < stages.Length; nextStageIndex++) + codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); + codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); + byte[] cb1DataA = memoryManager.Physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray(); + byte[] cb1DataB = memoryManager.Physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray(); + + ShaderDumpPaths pathsA = default; + ShaderDumpPaths pathsB = default; + + if (dumper != null) { - if (stages[nextStageIndex] != null) - { - return stages[nextStageIndex]; - } + pathsA = dumper.Dump(codeA, compute: false); + pathsB = dumper.Dump(codeB, compute: false); } - return null; + ShaderProgram program = currentStage.Translate(vertexA); + + pathsB.Prepend(program); + pathsA.Prepend(program); + + CachedShaderStage vertexAStage = new CachedShaderStage(null, codeA, cb1DataA); + CachedShaderStage vertexBStage = new CachedShaderStage(program.Info, codeB, cb1DataB); + + return new TranslatedShaderVertexPair(vertexAStage, vertexBStage, program); } /// <summary> /// Translates a previously generated translator context to something that the host API accepts. /// </summary> /// <param name="dumper">Optional shader code dumper</param> - /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param> - /// <param name="currentStage">Translator context of the stage to be translated</param> - /// <param name="nextStage">Translator context of the next active stage, if existent</param> - /// <param name="vertexA">Optional translator context of the shader that should be combined</param> + /// <param name="channel">GPU channel using the shader</param> + /// <param name="context">Translator context of the stage to be translated</param> + /// <param name="code">Optional Maxwell binary code of the current stage shader, if present on cache</param> /// <returns>Compiled graphics shader code</returns> - private static ShaderCodeHolder TranslateShader( - ShaderDumper dumper, - MemoryManager memoryManager, - TranslatorContext currentStage, - TranslatorContext nextStage, - TranslatorContext vertexA) + private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code) { - if (currentStage == null) - { - return null; - } + var memoryManager = channel.MemoryManager; - if (vertexA != null) - { - byte[] codeA = memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); - byte[] codeB = memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); + ulong cb1DataAddress = context.Stage == ShaderStage.Compute + ? channel.BufferManager.GetComputeUniformBufferAddress(1) + : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1); - ShaderDumpPaths pathsA = default; - ShaderDumpPaths pathsB = default; + byte[] cb1Data = memoryManager.Physical.GetSpan(cb1DataAddress, context.Cb1DataSize).ToArray(); + code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray(); - if (dumper != null) - { - pathsA = dumper.Dump(codeA, compute: false); - pathsB = dumper.Dump(codeB, compute: false); - } + ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default; + ShaderProgram program = context.Translate(); - ShaderProgram program = currentStage.Translate(out ShaderProgramInfo shaderProgramInfo, nextStage, vertexA); + paths.Prepend(program); - pathsB.Prepend(program); - pathsA.Prepend(program); + return new TranslatedShader(new CachedShaderStage(program.Info, code, cb1Data), program); + } - return new ShaderCodeHolder(program, shaderProgramInfo, codeB, codeA); - } - else + /// <summary> + /// Gets the index of a stage from a <see cref="ShaderStage"/>. + /// </summary> + /// <param name="stage">Stage to get the index from</param> + /// <returns>Stage index</returns> + private static int StageToStageIndex(ShaderStage stage) + { + return stage switch { - byte[] code = memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); - - ShaderDumpPaths paths = dumper?.Dump(code, currentStage.Stage == ShaderStage.Compute) ?? default; - - ShaderProgram program = currentStage.Translate(out ShaderProgramInfo shaderProgramInfo, nextStage); - - paths.Prepend(program); - - return new ShaderCodeHolder(program, shaderProgramInfo, code); - } + ShaderStage.TessellationControl => 1, + ShaderStage.TessellationEvaluation => 2, + ShaderStage.Geometry => 3, + ShaderStage.Fragment => 4, + _ => 0 + }; } /// <summary> @@ -1084,23 +599,17 @@ namespace Ryujinx.Graphics.Gpu.Shader /// </summary> public void Dispose() { - foreach (List<ShaderBundle> list in _cpPrograms.Values) + foreach (CachedShaderProgram program in _graphicsShaderCache.GetPrograms()) { - foreach (ShaderBundle bundle in list) - { - bundle.Dispose(); - } + program.Dispose(); } - foreach (List<ShaderBundle> list in _gpPrograms.Values) + foreach (CachedShaderProgram program in _computeShaderCache.GetPrograms()) { - foreach (ShaderBundle bundle in list) - { - bundle.Dispose(); - } + program.Dispose(); } - _cacheManager?.Dispose(); + _cacheWriter?.Dispose(); } } } |