aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2022-04-10 10:49:44 -0300
committerGitHub <noreply@github.com>2022-04-10 10:49:44 -0300
commit43ebd7a9bbba0c1290a9e98b9224f0752627c400 (patch)
tree02e1006242113be10aa0c6312de51120eeff91a7 /Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
parent26a881176eb6513a98889648e0d5b7fe647cd0e3 (diff)
New shader cache implementation (#3194)1.1.101
* New shader cache implementation * Remove some debug code * Take transform feedback varying count into account * Create shader cache directory if it does not exist + fragment output map related fixes * Remove debug code * Only check texture descriptors if the constant buffer is bound * Also check CPU VA on GetSpanMapped * Remove more unused code and move cache related code * XML docs + remove more unused methods * Better codegen for TransformFeedbackDescriptor.AsSpan * Support migration from old cache format, remove more unused code Shader cache rebuild now also rewrites the shared toc and data files * Fix migration error with BRX shaders * Add a limit to the async translation queue Avoid async translation threads not being able to keep up and the queue growing very large * Re-create specialization state on recompile This might be required if a new version of the shader translator requires more or less state, or if there is a bug related to the GPU state access * Make shader cache more error resilient * Add some missing XML docs and move GpuAccessor docs to the interface/use inheritdoc * Address early PR feedback * Fix rebase * Remove IRenderer.CompileShader and IShader interface, replace with new ShaderSource struct passed to CreateProgram directly * Handle some missing exceptions * Make shader cache purge delete both old and new shader caches * Register textures on new specialization state * Translate and compile shaders in forward order (eliminates diffs due to different binding numbers) * Limit in-flight shader compilation to the maximum number of compilation threads * Replace ParallelDiskCacheLoader state changed event with a callback function * Better handling for invalid constant buffer 1 data length * Do not create the old cache directory structure if the old cache does not exist * Constant buffer use should be per-stage. This change will invalidate existing new caches (file format version was incremented) * Replace rectangle texture with just coordinate normalization * Skip incompatible shaders that are missing texture information, instead of crashing This is required if we, for example, support new texture instruction to the shader translator, and then they allow access to textures that were not accessed before. In this scenario, the old cache entry is no longer usable * Fix coordinates normalization on cubemap textures * Check if title ID is null before combining shader cache path * More robust constant buffer address validation on spec state * More robust constant buffer address validation on spec state (2) * Regenerate shader cache with one stream, rather than one per shader. * Only create shader cache directory during initialization * Logging improvements * Proper shader program disposal * PR feedback, and add a comment on serialized structs * XML docs for RegisterTexture Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs1133
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();
}
}
}