aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Shader
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader')
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs617
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs273
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs168
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs38
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs13
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs97
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs67
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs10
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs88
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs69
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs42
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs38
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs41
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs222
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs114
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs42
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs258
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs19
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs58
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs8
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs183
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs31
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs146
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs49
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs61
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs150
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs80
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs167
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs82
30 files changed, 783 insertions, 2450 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs
deleted file mode 100644
index a98531f6..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs
+++ /dev/null
@@ -1,617 +0,0 @@
-using ICSharpCode.SharpZipLib.Zip;
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- /// <summary>
- /// Represent a cache collection handling one shader cache.
- /// </summary>
- class CacheCollection : IDisposable
- {
- /// <summary>
- /// Possible operation to do on the <see cref="_fileWriterWorkerQueue"/>.
- /// </summary>
- private enum CacheFileOperation
- {
- /// <summary>
- /// Save a new entry in the temp cache.
- /// </summary>
- SaveTempEntry,
-
- /// <summary>
- /// Save the hash manifest.
- /// </summary>
- SaveManifest,
-
- /// <summary>
- /// Remove entries from the hash manifest and save it.
- /// </summary>
- RemoveManifestEntries,
-
- /// <summary>
- /// Remove entries from the hash manifest and save it, and also deletes the temporary file.
- /// </summary>
- RemoveManifestEntryAndTempFile,
-
- /// <summary>
- /// Flush temporary cache to archive.
- /// </summary>
- FlushToArchive,
-
- /// <summary>
- /// Signal when hitting this point. This is useful to know if all previous operations were performed.
- /// </summary>
- Synchronize
- }
-
- /// <summary>
- /// Represent an operation to perform on the <see cref="_fileWriterWorkerQueue"/>.
- /// </summary>
- private class CacheFileOperationTask
- {
- /// <summary>
- /// The type of operation to perform.
- /// </summary>
- public CacheFileOperation Type;
-
- /// <summary>
- /// The data associated to this operation or null.
- /// </summary>
- public object Data;
- }
-
- /// <summary>
- /// Data associated to the <see cref="CacheFileOperation.SaveTempEntry"/> operation.
- /// </summary>
- private class CacheFileSaveEntryTaskData
- {
- /// <summary>
- /// The key of the entry to cache.
- /// </summary>
- public Hash128 Key;
-
- /// <summary>
- /// The value of the entry to cache.
- /// </summary>
- public byte[] Value;
- }
-
- /// <summary>
- /// The directory of the shader cache.
- /// </summary>
- private readonly string _cacheDirectory;
-
- /// <summary>
- /// The version of the cache.
- /// </summary>
- private readonly ulong _version;
-
- /// <summary>
- /// The hash type of the cache.
- /// </summary>
- private readonly CacheHashType _hashType;
-
- /// <summary>
- /// The graphics API of the cache.
- /// </summary>
- private readonly CacheGraphicsApi _graphicsApi;
-
- /// <summary>
- /// The table of all the hash registered in the cache.
- /// </summary>
- private HashSet<Hash128> _hashTable;
-
- /// <summary>
- /// The queue of operations to be performed by the file writer worker.
- /// </summary>
- private AsyncWorkQueue<CacheFileOperationTask> _fileWriterWorkerQueue;
-
- /// <summary>
- /// Main storage of the cache collection.
- /// </summary>
- private ZipFile _cacheArchive;
-
- /// <summary>
- /// Indicates if the cache collection supports modification.
- /// </summary>
- public bool IsReadOnly { get; }
-
- /// <summary>
- /// Immutable copy of the hash table.
- /// </summary>
- public ReadOnlySpan<Hash128> HashTable => _hashTable.ToArray();
-
- /// <summary>
- /// Get the temp path to the cache data directory.
- /// </summary>
- /// <returns>The temp path to the cache data directory</returns>
- private string GetCacheTempDataPath() => CacheHelper.GetCacheTempDataPath(_cacheDirectory);
-
- /// <summary>
- /// The path to the cache archive file.
- /// </summary>
- /// <returns>The path to the cache archive file</returns>
- private string GetArchivePath() => CacheHelper.GetArchivePath(_cacheDirectory);
-
- /// <summary>
- /// The path to the cache manifest file.
- /// </summary>
- /// <returns>The path to the cache manifest file</returns>
- private string GetManifestPath() => CacheHelper.GetManifestPath(_cacheDirectory);
-
- /// <summary>
- /// Create a new temp path to the given cached file via its hash.
- /// </summary>
- /// <param name="key">The hash of the cached data</param>
- /// <returns>New path to the given cached file</returns>
- private string GenCacheTempFilePath(Hash128 key) => CacheHelper.GenCacheTempFilePath(_cacheDirectory, key);
-
- /// <summary>
- /// Create a new cache collection.
- /// </summary>
- /// <param name="baseCacheDirectory">The directory of the shader cache</param>
- /// <param name="hashType">The hash type of the shader cache</param>
- /// <param name="graphicsApi">The graphics api of the shader cache</param>
- /// <param name="shaderProvider">The shader provider name of the shader cache</param>
- /// <param name="cacheName">The name of the cache</param>
- /// <param name="version">The version of the cache</param>
- public CacheCollection(string baseCacheDirectory, CacheHashType hashType, CacheGraphicsApi graphicsApi, string shaderProvider, string cacheName, ulong version)
- {
- if (hashType != CacheHashType.XxHash128)
- {
- throw new NotImplementedException($"{hashType}");
- }
-
- _cacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, graphicsApi, shaderProvider, cacheName);
- _graphicsApi = graphicsApi;
- _hashType = hashType;
- _version = version;
- _hashTable = new HashSet<Hash128>();
- IsReadOnly = CacheHelper.IsArchiveReadOnly(GetArchivePath());
-
- Load();
-
- _fileWriterWorkerQueue = new AsyncWorkQueue<CacheFileOperationTask>(HandleCacheTask, $"CacheCollection.Worker.{cacheName}");
- }
-
- /// <summary>
- /// Load the cache manifest file and recreate it if invalid.
- /// </summary>
- private void Load()
- {
- bool isValid = false;
-
- if (Directory.Exists(_cacheDirectory))
- {
- string manifestPath = GetManifestPath();
-
- if (File.Exists(manifestPath))
- {
- Memory<byte> rawManifest = File.ReadAllBytes(manifestPath);
-
- if (MemoryMarshal.TryRead(rawManifest.Span, out CacheManifestHeader manifestHeader))
- {
- Memory<byte> hashTableRaw = rawManifest.Slice(Unsafe.SizeOf<CacheManifestHeader>());
-
- isValid = manifestHeader.IsValid(_graphicsApi, _hashType, hashTableRaw.Span) && _version == manifestHeader.Version;
-
- if (isValid)
- {
- ReadOnlySpan<Hash128> hashTable = MemoryMarshal.Cast<byte, Hash128>(hashTableRaw.Span);
-
- foreach (Hash128 hash in hashTable)
- {
- _hashTable.Add(hash);
- }
- }
- }
- }
- }
-
- if (!isValid)
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Shader collection \"{_cacheDirectory}\" got invalidated, cache will need to be rebuilt.");
-
- if (Directory.Exists(_cacheDirectory))
- {
- Directory.Delete(_cacheDirectory, true);
- }
-
- Directory.CreateDirectory(_cacheDirectory);
-
- SaveManifest();
- }
-
- FlushToArchive();
- }
-
- /// <summary>
- /// Queue a task to remove entries from the hash manifest.
- /// </summary>
- /// <param name="entries">Entries to remove from the manifest</param>
- public void RemoveManifestEntriesAsync(HashSet<Hash128> entries)
- {
- if (IsReadOnly)
- {
- Logger.Warning?.Print(LogClass.Gpu, "Trying to remove manifest entries on a read-only cache, ignoring.");
-
- return;
- }
-
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.RemoveManifestEntries,
- Data = entries
- });
- }
-
- /// <summary>
- /// Remove given entries from the manifest.
- /// </summary>
- /// <param name="entries">Entries to remove from the manifest</param>
- private void RemoveManifestEntries(HashSet<Hash128> entries)
- {
- lock (_hashTable)
- {
- foreach (Hash128 entry in entries)
- {
- _hashTable.Remove(entry);
- }
-
- SaveManifest();
- }
- }
-
- /// <summary>
- /// Remove given entry from the manifest and delete the temporary file.
- /// </summary>
- /// <param name="entry">Entry to remove from the manifest</param>
- private void RemoveManifestEntryAndTempFile(Hash128 entry)
- {
- lock (_hashTable)
- {
- _hashTable.Remove(entry);
- SaveManifest();
- }
-
- File.Delete(GenCacheTempFilePath(entry));
- }
-
- /// <summary>
- /// Queue a task to flush temporary files to the archive on the worker.
- /// </summary>
- public void FlushToArchiveAsync()
- {
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.FlushToArchive
- });
- }
-
- /// <summary>
- /// Wait for all tasks before this given point to be done.
- /// </summary>
- public void Synchronize()
- {
- using (ManualResetEvent evnt = new ManualResetEvent(false))
- {
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.Synchronize,
- Data = evnt
- });
-
- evnt.WaitOne();
- }
- }
-
- /// <summary>
- /// Flush temporary files to the archive.
- /// </summary>
- /// <remarks>This dispose <see cref="_cacheArchive"/> if not null and reinstantiate it.</remarks>
- private void FlushToArchive()
- {
- EnsureArchiveUpToDate();
-
- // Open the zip in readonly to avoid anyone modifying/corrupting it during normal operations.
- _cacheArchive = new ZipFile(File.OpenRead(GetArchivePath()));
- }
-
- /// <summary>
- /// Save temporary files not in archive.
- /// </summary>
- /// <remarks>This dispose <see cref="_cacheArchive"/> if not null.</remarks>
- public void EnsureArchiveUpToDate()
- {
- // First close previous opened instance if found.
- if (_cacheArchive != null)
- {
- _cacheArchive.Close();
- }
-
- string archivePath = GetArchivePath();
-
- if (IsReadOnly)
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Cache collection archive in read-only, archiving task skipped.");
-
- return;
- }
-
- if (CacheHelper.IsArchiveReadOnly(archivePath))
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Cache collection archive in use, archiving task skipped.");
-
- return;
- }
-
- if (!File.Exists(archivePath))
- {
- using (ZipFile newZip = ZipFile.Create(archivePath))
- {
- // Workaround for SharpZipLib issue #395
- newZip.BeginUpdate();
- newZip.CommitUpdate();
- }
- }
-
- // Open the zip in read/write.
- _cacheArchive = new ZipFile(File.Open(archivePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None));
-
- Logger.Info?.Print(LogClass.Gpu, $"Updating cache collection archive {archivePath}...");
-
- // Update the content of the zip.
- lock (_hashTable)
- {
- CacheHelper.EnsureArchiveUpToDate(_cacheDirectory, _cacheArchive, _hashTable);
-
- // Close the instance to force a flush.
- _cacheArchive.Close();
- _cacheArchive = null;
-
- string cacheTempDataPath = GetCacheTempDataPath();
-
- // Create the cache data path if missing.
- if (!Directory.Exists(cacheTempDataPath))
- {
- Directory.CreateDirectory(cacheTempDataPath);
- }
- }
-
- Logger.Info?.Print(LogClass.Gpu, $"Updated cache collection archive {archivePath}.");
- }
-
- /// <summary>
- /// Save the manifest file.
- /// </summary>
- private void SaveManifest()
- {
- byte[] data;
-
- lock (_hashTable)
- {
- data = CacheHelper.ComputeManifest(_version, _graphicsApi, _hashType, _hashTable);
- }
-
- File.WriteAllBytes(GetManifestPath(), data);
- }
-
- /// <summary>
- /// Get a cached file with the given hash.
- /// </summary>
- /// <param name="keyHash">The given hash</param>
- /// <returns>The cached file if present or null</returns>
- public byte[] GetValueRaw(ref Hash128 keyHash)
- {
- return GetValueRawFromArchive(ref keyHash) ?? GetValueRawFromFile(ref keyHash);
- }
-
- /// <summary>
- /// Get a cached file with the given hash that is present in the archive.
- /// </summary>
- /// <param name="keyHash">The given hash</param>
- /// <returns>The cached file if present or null</returns>
- private byte[] GetValueRawFromArchive(ref Hash128 keyHash)
- {
- bool found;
-
- lock (_hashTable)
- {
- found = _hashTable.Contains(keyHash);
- }
-
- if (found)
- {
- return CacheHelper.ReadFromArchive(_cacheArchive, keyHash);
- }
-
- return null;
- }
-
- /// <summary>
- /// Get a cached file with the given hash that is not present in the archive.
- /// </summary>
- /// <param name="keyHash">The given hash</param>
- /// <returns>The cached file if present or null</returns>
- private byte[] GetValueRawFromFile(ref Hash128 keyHash)
- {
- bool found;
-
- lock (_hashTable)
- {
- found = _hashTable.Contains(keyHash);
- }
-
- if (found)
- {
- return CacheHelper.ReadFromFile(GetCacheTempDataPath(), keyHash);
- }
-
- return null;
- }
-
- private void HandleCacheTask(CacheFileOperationTask task)
- {
- switch (task.Type)
- {
- case CacheFileOperation.SaveTempEntry:
- SaveTempEntry((CacheFileSaveEntryTaskData)task.Data);
- break;
- case CacheFileOperation.SaveManifest:
- SaveManifest();
- break;
- case CacheFileOperation.RemoveManifestEntries:
- RemoveManifestEntries((HashSet<Hash128>)task.Data);
- break;
- case CacheFileOperation.RemoveManifestEntryAndTempFile:
- RemoveManifestEntryAndTempFile((Hash128)task.Data);
- break;
- case CacheFileOperation.FlushToArchive:
- FlushToArchive();
- break;
- case CacheFileOperation.Synchronize:
- ((ManualResetEvent)task.Data).Set();
- break;
- default:
- throw new NotImplementedException($"{task.Type}");
- }
-
- }
-
- /// <summary>
- /// Save a new entry in the temp cache.
- /// </summary>
- /// <param name="entry">The entry to save in the temp cache</param>
- private void SaveTempEntry(CacheFileSaveEntryTaskData entry)
- {
- string tempPath = GenCacheTempFilePath(entry.Key);
-
- File.WriteAllBytes(tempPath, entry.Value);
- }
-
- /// <summary>
- /// Add a new value in the cache with a given hash.
- /// </summary>
- /// <param name="keyHash">The hash to use for the value in the cache</param>
- /// <param name="value">The value to cache</param>
- public void AddValue(ref Hash128 keyHash, byte[] value)
- {
- if (IsReadOnly)
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Trying to add {keyHash} on a read-only cache, ignoring.");
-
- return;
- }
-
- Debug.Assert(value != null);
-
- bool isAlreadyPresent;
-
- lock (_hashTable)
- {
- isAlreadyPresent = !_hashTable.Add(keyHash);
- }
-
- if (isAlreadyPresent)
- {
- // NOTE: Used for debug
- File.WriteAllBytes(GenCacheTempFilePath(new Hash128()), value);
-
- throw new InvalidOperationException($"Cache collision found on {GenCacheTempFilePath(keyHash)}");
- }
-
- // Queue file change operations
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.SaveTempEntry,
- Data = new CacheFileSaveEntryTaskData
- {
- Key = keyHash,
- Value = value
- }
- });
-
- // Save the manifest changes
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.SaveManifest,
- });
- }
-
- /// <summary>
- /// Replace a value at the given hash in the cache.
- /// </summary>
- /// <param name="keyHash">The hash to use for the value in the cache</param>
- /// <param name="value">The value to cache</param>
- public void ReplaceValue(ref Hash128 keyHash, byte[] value)
- {
- if (IsReadOnly)
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Trying to replace {keyHash} on a read-only cache, ignoring.");
-
- return;
- }
-
- Debug.Assert(value != null);
-
- // Only queue file change operations
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.SaveTempEntry,
- Data = new CacheFileSaveEntryTaskData
- {
- Key = keyHash,
- Value = value
- }
- });
- }
-
- /// <summary>
- /// Removes a value at the given hash from the cache.
- /// </summary>
- /// <param name="keyHash">The hash of the value in the cache</param>
- public void RemoveValue(ref Hash128 keyHash)
- {
- if (IsReadOnly)
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Trying to remove {keyHash} on a read-only cache, ignoring.");
-
- return;
- }
-
- // Only queue file change operations
- _fileWriterWorkerQueue.Add(new CacheFileOperationTask
- {
- Type = CacheFileOperation.RemoveManifestEntryAndTempFile,
- Data = keyHash
- });
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- // Make sure all operations on _fileWriterWorkerQueue are done.
- Synchronize();
-
- _fileWriterWorkerQueue.Dispose();
- EnsureArchiveUpToDate();
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
deleted file mode 100644
index d16afb65..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
+++ /dev/null
@@ -1,273 +0,0 @@
-using ICSharpCode.SharpZipLib.Zip;
-using Ryujinx.Common;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using Ryujinx.Graphics.Shader;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- /// <summary>
- /// Helper to manipulate the disk shader cache.
- /// </summary>
- static class CacheHelper
- {
- /// <summary>
- /// Compute a cache manifest from runtime data.
- /// </summary>
- /// <param name="version">The version of the cache</param>
- /// <param name="graphicsApi">The graphics api used by the cache</param>
- /// <param name="hashType">The hash type of the cache</param>
- /// <param name="entries">The entries in the cache</param>
- /// <returns>The cache manifest from runtime data</returns>
- public static byte[] ComputeManifest(ulong version, CacheGraphicsApi graphicsApi, CacheHashType hashType, HashSet<Hash128> entries)
- {
- if (hashType != CacheHashType.XxHash128)
- {
- throw new NotImplementedException($"{hashType}");
- }
-
- CacheManifestHeader manifestHeader = new CacheManifestHeader(version, graphicsApi, hashType);
-
- byte[] data = new byte[Unsafe.SizeOf<CacheManifestHeader>() + entries.Count * Unsafe.SizeOf<Hash128>()];
-
- // CacheManifestHeader has the same size as a Hash128.
- Span<Hash128> dataSpan = MemoryMarshal.Cast<byte, Hash128>(data.AsSpan()).Slice(1);
-
- int i = 0;
-
- foreach (Hash128 hash in entries)
- {
- dataSpan[i++] = hash;
- }
-
- manifestHeader.UpdateChecksum(data.AsSpan(Unsafe.SizeOf<CacheManifestHeader>()));
-
- MemoryMarshal.Write(data, ref manifestHeader);
-
- return data;
- }
-
- /// <summary>
- /// Get the base directory of the shader cache for a given title id.
- /// </summary>
- /// <param name="titleId">The title id of the target application</param>
- /// <returns>The base directory of the shader cache for a given title id</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetBaseCacheDirectory(string titleId) => Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "shader");
-
- /// <summary>
- /// Get the temp path to the cache data directory.
- /// </summary>
- /// <param name="cacheDirectory">The cache directory</param>
- /// <returns>The temp path to the cache data directory</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetCacheTempDataPath(string cacheDirectory) => Path.Combine(cacheDirectory, "temp");
-
- /// <summary>
- /// The path to the cache archive file.
- /// </summary>
- /// <param name="cacheDirectory">The cache directory</param>
- /// <returns>The path to the cache archive file</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetArchivePath(string cacheDirectory) => Path.Combine(cacheDirectory, "cache.zip");
-
- /// <summary>
- /// The path to the cache manifest file.
- /// </summary>
- /// <param name="cacheDirectory">The cache directory</param>
- /// <returns>The path to the cache manifest file</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetManifestPath(string cacheDirectory) => Path.Combine(cacheDirectory, "cache.info");
-
- /// <summary>
- /// Create a new temp path to the given cached file via its hash.
- /// </summary>
- /// <param name="cacheDirectory">The cache directory</param>
- /// <param name="key">The hash of the cached data</param>
- /// <returns>New path to the given cached file</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GenCacheTempFilePath(string cacheDirectory, Hash128 key) => Path.Combine(GetCacheTempDataPath(cacheDirectory), key.ToString());
-
- /// <summary>
- /// Generate the path to the cache directory.
- /// </summary>
- /// <param name="baseCacheDirectory">The base of the cache directory</param>
- /// <param name="graphicsApi">The graphics api in use</param>
- /// <param name="shaderProvider">The name of the shader provider in use</param>
- /// <param name="cacheName">The name of the cache</param>
- /// <returns>The path to the cache directory</returns>
- public static string GenerateCachePath(string baseCacheDirectory, CacheGraphicsApi graphicsApi, string shaderProvider, string cacheName)
- {
- string graphicsApiName = graphicsApi switch
- {
- CacheGraphicsApi.OpenGL => "opengl",
- CacheGraphicsApi.OpenGLES => "opengles",
- CacheGraphicsApi.Vulkan => "vulkan",
- CacheGraphicsApi.DirectX => "directx",
- CacheGraphicsApi.Metal => "metal",
- CacheGraphicsApi.Guest => "guest",
- _ => throw new NotImplementedException(graphicsApi.ToString()),
- };
-
- return Path.Combine(baseCacheDirectory, graphicsApiName, shaderProvider, cacheName);
- }
-
- /// <summary>
- /// Read a cached file with the given hash that is present in the archive.
- /// </summary>
- /// <param name="archive">The archive in use</param>
- /// <param name="entry">The given hash</param>
- /// <returns>The cached file if present or null</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static byte[] ReadFromArchive(ZipFile archive, Hash128 entry)
- {
- if (archive != null)
- {
- ZipEntry archiveEntry = archive.GetEntry($"{entry}");
-
- if (archiveEntry != null)
- {
- try
- {
- byte[] result = new byte[archiveEntry.Size];
-
- using (Stream archiveStream = archive.GetInputStream(archiveEntry))
- {
- archiveStream.Read(result);
-
- return result;
- }
- }
- catch (Exception e)
- {
- Logger.Error?.Print(LogClass.Gpu, $"Cannot load cache file {entry} from archive");
- Logger.Error?.Print(LogClass.Gpu, e.ToString());
- }
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Read a cached file with the given hash that is not present in the archive.
- /// </summary>
- /// <param name="cacheDirectory">The cache directory</param>
- /// <param name="entry">The given hash</param>
- /// <returns>The cached file if present or null</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static byte[] ReadFromFile(string cacheDirectory, Hash128 entry)
- {
- string cacheTempFilePath = GenCacheTempFilePath(cacheDirectory, entry);
-
- try
- {
- return File.ReadAllBytes(cacheTempFilePath);
- }
- catch (Exception e)
- {
- Logger.Error?.Print(LogClass.Gpu, $"Cannot load cache file at {cacheTempFilePath}");
- Logger.Error?.Print(LogClass.Gpu, e.ToString());
- }
-
- return null;
- }
-
- /// <summary>
- /// Read transform feedback descriptors from guest.
- /// </summary>
- /// <param name="data">The raw guest transform feedback descriptors</param>
- /// <param name="header">The guest shader program header</param>
- /// <returns>The transform feedback descriptors read from guest</returns>
- public static TransformFeedbackDescriptorOld[] ReadTransformFeedbackInformation(ref ReadOnlySpan<byte> data, GuestShaderCacheHeader header)
- {
- if (header.TransformFeedbackCount != 0)
- {
- TransformFeedbackDescriptorOld[] result = new TransformFeedbackDescriptorOld[header.TransformFeedbackCount];
-
- for (int i = 0; i < result.Length; i++)
- {
- GuestShaderCacheTransformFeedbackHeader feedbackHeader = MemoryMarshal.Read<GuestShaderCacheTransformFeedbackHeader>(data);
-
- result[i] = new TransformFeedbackDescriptorOld(feedbackHeader.BufferIndex, feedbackHeader.Stride, data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>(), feedbackHeader.VaryingLocationsLength).ToArray());
-
- data = data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>() + feedbackHeader.VaryingLocationsLength);
- }
-
- return result;
- }
-
- return null;
- }
-
- /// <summary>
- /// Save temporary files not in archive.
- /// </summary>
- /// <param name="baseCacheDirectory">The base of the cache directory</param>
- /// <param name="archive">The archive to use</param>
- /// <param name="entries">The entries in the cache</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EnsureArchiveUpToDate(string baseCacheDirectory, ZipFile archive, HashSet<Hash128> entries)
- {
- List<string> filesToDelete = new List<string>();
-
- archive.BeginUpdate();
-
- foreach (Hash128 hash in entries)
- {
- string cacheTempFilePath = GenCacheTempFilePath(baseCacheDirectory, hash);
-
- if (File.Exists(cacheTempFilePath))
- {
- string cacheHash = $"{hash}";
-
- ZipEntry entry = archive.GetEntry(cacheHash);
-
- if (entry != null)
- {
- archive.Delete(entry);
- }
-
- // We enforce deflate compression here to avoid possible incompatibilities on older version of Ryujinx that use System.IO.Compression.
- archive.Add(new StaticDiskDataSource(cacheTempFilePath), cacheHash, CompressionMethod.Deflated);
- filesToDelete.Add(cacheTempFilePath);
- }
- }
-
- archive.CommitUpdate();
-
- foreach (string filePath in filesToDelete)
- {
- File.Delete(filePath);
- }
- }
-
- public static bool IsArchiveReadOnly(string archivePath)
- {
- FileInfo info = new FileInfo(archivePath);
-
- if (!info.Exists)
- {
- return false;
- }
-
- try
- {
- using (FileStream stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.None))
- {
- return false;
- }
- }
- catch (IOException)
- {
- return true;
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs
deleted file mode 100644
index e67221e7..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- /// <summary>
- /// Global Manager of the shader cache.
- /// </summary>
- class CacheManager : IDisposable
- {
- private CacheGraphicsApi _graphicsApi;
- private CacheHashType _hashType;
- private string _shaderProvider;
-
- /// <summary>
- /// Cache storing raw Maxwell shaders as programs.
- /// </summary>
- private CacheCollection _guestProgramCache;
-
- /// <summary>
- /// Cache storing raw host programs.
- /// </summary>
- private CacheCollection _hostProgramCache;
-
- /// <summary>
- /// Version of the guest cache shader (to increment when guest cache structure change).
- /// </summary>
- private const ulong GuestCacheVersion = 1759;
-
- public bool IsReadOnly => _guestProgramCache.IsReadOnly || _hostProgramCache.IsReadOnly;
-
- /// <summary>
- /// Create a new cache manager instance
- /// </summary>
- /// <param name="graphicsApi">The graphics api in use</param>
- /// <param name="hashType">The hash type in use for the cache</param>
- /// <param name="shaderProvider">The name of the codegen provider</param>
- /// <param name="titleId">The guest application title ID</param>
- /// <param name="shaderCodeGenVersion">Version of the codegen</param>
- public CacheManager(CacheGraphicsApi graphicsApi, CacheHashType hashType, string shaderProvider, string titleId, ulong shaderCodeGenVersion)
- {
- _graphicsApi = graphicsApi;
- _hashType = hashType;
- _shaderProvider = shaderProvider;
-
- string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(titleId);
-
- _guestProgramCache = new CacheCollection(baseCacheDirectory, _hashType, CacheGraphicsApi.Guest, "", "program", GuestCacheVersion);
- _hostProgramCache = new CacheCollection(baseCacheDirectory, _hashType, _graphicsApi, _shaderProvider, "host", shaderCodeGenVersion);
- }
-
-
- /// <summary>
- /// Entries to remove from the manifest.
- /// </summary>
- /// <param name="entries">Entries to remove from the manifest of all caches</param>
- public void RemoveManifestEntries(HashSet<Hash128> entries)
- {
- _guestProgramCache.RemoveManifestEntriesAsync(entries);
- _hostProgramCache.RemoveManifestEntriesAsync(entries);
- }
-
- /// <summary>
- /// Queue a task to flush temporary files to the archives.
- /// </summary>
- public void FlushToArchive()
- {
- _guestProgramCache.FlushToArchiveAsync();
- _hostProgramCache.FlushToArchiveAsync();
- }
-
- /// <summary>
- /// Wait for all tasks before this given point to be done.
- /// </summary>
- public void Synchronize()
- {
- _guestProgramCache.Synchronize();
- _hostProgramCache.Synchronize();
- }
-
- /// <summary>
- /// Save a shader program not present in the program cache.
- /// </summary>
- /// <param name="programCodeHash">Target program code hash</param>
- /// <param name="guestProgram">Guest program raw data</param>
- /// <param name="hostProgram">Host program raw data</param>
- public void SaveProgram(ref Hash128 programCodeHash, byte[] guestProgram, byte[] hostProgram)
- {
- _guestProgramCache.AddValue(ref programCodeHash, guestProgram);
- _hostProgramCache.AddValue(ref programCodeHash, hostProgram);
- }
-
- /// <summary>
- /// Add a host shader program not present in the program cache.
- /// </summary>
- /// <param name="programCodeHash">Target program code hash</param>
- /// <param name="data">Host program raw data</param>
- public void AddHostProgram(ref Hash128 programCodeHash, byte[] data)
- {
- _hostProgramCache.AddValue(ref programCodeHash, data);
- }
-
- /// <summary>
- /// Replace a host shader program present in the program cache.
- /// </summary>
- /// <param name="programCodeHash">Target program code hash</param>
- /// <param name="data">Host program raw data</param>
- public void ReplaceHostProgram(ref Hash128 programCodeHash, byte[] data)
- {
- _hostProgramCache.ReplaceValue(ref programCodeHash, data);
- }
-
- /// <summary>
- /// Removes a shader program present in the program cache.
- /// </summary>
- /// <param name="programCodeHash">Target program code hash</param>
- public void RemoveProgram(ref Hash128 programCodeHash)
- {
- _guestProgramCache.RemoveValue(ref programCodeHash);
- _hostProgramCache.RemoveValue(ref programCodeHash);
- }
-
- /// <summary>
- /// Get all guest program hashes.
- /// </summary>
- /// <returns>All guest program hashes</returns>
- public ReadOnlySpan<Hash128> GetGuestProgramList()
- {
- return _guestProgramCache.HashTable;
- }
-
- /// <summary>
- /// Get a host program by hash.
- /// </summary>
- /// <param name="hash">The given hash</param>
- /// <returns>The host program if present or null</returns>
- public byte[] GetHostProgramByHash(ref Hash128 hash)
- {
- return _hostProgramCache.GetValueRaw(ref hash);
- }
-
- /// <summary>
- /// Get a guest program by hash.
- /// </summary>
- /// <param name="hash">The given hash</param>
- /// <returns>The guest program if present or null</returns>
- public byte[] GetGuestProgramByHash(ref Hash128 hash)
- {
- return _guestProgramCache.GetValueRaw(ref hash);
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _guestProgramCache.Dispose();
- _hostProgramCache.Dispose();
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs
deleted file mode 100644
index 9f8b5c39..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Graphics API type accepted by the shader cache.
- /// </summary>
- enum CacheGraphicsApi : byte
- {
- /// <summary>
- /// OpenGL Core
- /// </summary>
- OpenGL,
-
- /// <summary>
- /// OpenGL ES
- /// </summary>
- OpenGLES,
-
- /// <summary>
- /// Vulkan
- /// </summary>
- Vulkan,
-
- /// <summary>
- /// DirectX
- /// </summary>
- DirectX,
-
- /// <summary>
- /// Metal
- /// </summary>
- Metal,
-
- /// <summary>
- /// Guest, used to cache games raw shader programs.
- /// </summary>
- Guest
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs
deleted file mode 100644
index e4ebe416..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Hash algorithm accepted by the shader cache.
- /// </summary>
- enum CacheHashType : byte
- {
- /// <summary>
- /// xxHash128
- /// </summary>
- XxHash128
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs
deleted file mode 100644
index 0601451d..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Header of the shader cache manifest.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
- struct CacheManifestHeader
- {
- /// <summary>
- /// The version of the cache.
- /// </summary>
- public ulong Version;
-
- /// <summary>
- /// The graphics api used for this cache.
- /// </summary>
- public CacheGraphicsApi GraphicsApi;
-
- /// <summary>
- /// The hash type used for this cache.
- /// </summary>
- public CacheHashType HashType;
-
- /// <summary>
- /// CRC-16 checksum over the data in the file.
- /// </summary>
- public ushort TableChecksum;
-
- /// <summary>
- /// Construct a new cache manifest header.
- /// </summary>
- /// <param name="version">The version of the cache</param>
- /// <param name="graphicsApi">The graphics api used for this cache</param>
- /// <param name="hashType">The hash type used for this cache</param>
- public CacheManifestHeader(ulong version, CacheGraphicsApi graphicsApi, CacheHashType hashType)
- {
- Version = version;
- GraphicsApi = graphicsApi;
- HashType = hashType;
- TableChecksum = 0;
- }
-
- /// <summary>
- /// Update the checksum in the header.
- /// </summary>
- /// <param name="data">The data to perform the checksum on</param>
- public void UpdateChecksum(ReadOnlySpan<byte> data)
- {
- TableChecksum = CalculateCrc16(data);
- }
-
- /// <summary>
- /// Calculate a CRC-16 over data.
- /// </summary>
- /// <param name="data">The data to perform the CRC-16 on</param>
- /// <returns>A CRC-16 over data</returns>
- private static ushort CalculateCrc16(ReadOnlySpan<byte> data)
- {
- int crc = 0;
-
- const ushort poly = 0x1021;
-
- for (int i = 0; i < data.Length; i++)
- {
- crc ^= data[i] << 8;
-
- for (int j = 0; j < 8; j++)
- {
- crc <<= 1;
-
- if ((crc & 0x10000) != 0)
- {
- crc = (crc ^ poly) & 0xFFFF;
- }
- }
- }
-
- return (ushort)crc;
- }
-
- /// <summary>
- /// Check the validity of the header.
- /// </summary>
- /// <param name="graphicsApi">The target graphics api in use</param>
- /// <param name="hashType">The target hash type in use</param>
- /// <param name="data">The data after this header</param>
- /// <returns>True if the header is valid</returns>
- /// <remarks>This doesn't check that versions match</remarks>
- public bool IsValid(CacheGraphicsApi graphicsApi, CacheHashType hashType, ReadOnlySpan<byte> data)
- {
- return GraphicsApi == graphicsApi && HashType == hashType && TableChecksum == CalculateCrc16(data);
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs
deleted file mode 100644
index 2e044750..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using Ryujinx.Graphics.Shader;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Header of a cached guest gpu accessor.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 1)]
- struct GuestGpuAccessorHeader
- {
- /// <summary>
- /// The count of texture descriptors.
- /// </summary>
- public int TextureDescriptorCount;
-
- /// <summary>
- /// Local Size X for compute shaders.
- /// </summary>
- public int ComputeLocalSizeX;
-
- /// <summary>
- /// Local Size Y for compute shaders.
- /// </summary>
- public int ComputeLocalSizeY;
-
- /// <summary>
- /// Local Size Z for compute shaders.
- /// </summary>
- public int ComputeLocalSizeZ;
-
- /// <summary>
- /// Local Memory size in bytes for compute shaders.
- /// </summary>
- public int ComputeLocalMemorySize;
-
- /// <summary>
- /// Shared Memory size in bytes for compute shaders.
- /// </summary>
- public int ComputeSharedMemorySize;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public int Reserved1;
-
- /// <summary>
- /// Current primitive topology for geometry shaders.
- /// </summary>
- public InputTopology PrimitiveTopology;
-
- /// <summary>
- /// Tessellation parameters (packed to fit on a byte).
- /// </summary>
- public byte TessellationModePacked;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public byte Reserved2;
-
- /// <summary>
- /// GPU boolean state that can influence shader compilation.
- /// </summary>
- public GuestGpuStateFlags StateFlags;
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs
deleted file mode 100644
index 4b1fbb06..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- [Flags]
- enum GuestGpuStateFlags : byte
- {
- EarlyZForce = 1 << 0
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs
deleted file mode 100644
index 373fa6c6..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Represent a cached shader entry in a guest shader program.
- /// </summary>
- class GuestShaderCacheEntry
- {
- /// <summary>
- /// The header of the cached shader entry.
- /// </summary>
- public GuestShaderCacheEntryHeader Header { get; }
-
- /// <summary>
- /// The code of this shader.
- /// </summary>
- /// <remarks>If a Vertex A is present, this also contains the code 2 section.</remarks>
- public byte[] Code { get; }
-
- /// <summary>
- /// The textures descriptors used for this shader.
- /// </summary>
- public Dictionary<int, GuestTextureDescriptor> TextureDescriptors { get; }
-
- /// <summary>
- /// Create a new instance of <see cref="GuestShaderCacheEntry"/>.
- /// </summary>
- /// <param name="header">The header of the cached shader entry</param>
- /// <param name="code">The code of this shader</param>
- public GuestShaderCacheEntry(GuestShaderCacheEntryHeader header, byte[] code)
- {
- Header = header;
- Code = code;
- TextureDescriptors = new Dictionary<int, GuestTextureDescriptor>();
- }
-
- /// <summary>
- /// Parse a raw cached user shader program into an array of shader cache entry.
- /// </summary>
- /// <param name="data">The raw cached user shader program</param>
- /// <param name="fileHeader">The user shader program header</param>
- /// <returns>An array of shader cache entry</returns>
- public static GuestShaderCacheEntry[] Parse(ref ReadOnlySpan<byte> data, out GuestShaderCacheHeader fileHeader)
- {
- fileHeader = MemoryMarshal.Read<GuestShaderCacheHeader>(data);
-
- data = data.Slice(Unsafe.SizeOf<GuestShaderCacheHeader>());
-
- ReadOnlySpan<GuestShaderCacheEntryHeader> entryHeaders = MemoryMarshal.Cast<byte, GuestShaderCacheEntryHeader>(data.Slice(0, fileHeader.Count * Unsafe.SizeOf<GuestShaderCacheEntryHeader>()));
-
- data = data.Slice(fileHeader.Count * Unsafe.SizeOf<GuestShaderCacheEntryHeader>());
-
- GuestShaderCacheEntry[] result = new GuestShaderCacheEntry[fileHeader.Count];
-
- for (int i = 0; i < result.Length; i++)
- {
- GuestShaderCacheEntryHeader header = entryHeaders[i];
-
- // Ignore empty entries
- if (header.Size == 0 && header.SizeA == 0)
- {
- continue;
- }
-
- byte[] code = data.Slice(0, header.Size + header.SizeA).ToArray();
-
- data = data.Slice(header.Size + header.SizeA);
-
- result[i] = new GuestShaderCacheEntry(header, code);
-
- ReadOnlySpan<GuestTextureDescriptor> textureDescriptors = MemoryMarshal.Cast<byte, GuestTextureDescriptor>(data.Slice(0, header.GpuAccessorHeader.TextureDescriptorCount * Unsafe.SizeOf<GuestTextureDescriptor>()));
-
- foreach (GuestTextureDescriptor textureDescriptor in textureDescriptors)
- {
- result[i].TextureDescriptors.Add((int)textureDescriptor.Handle, textureDescriptor);
- }
-
- data = data.Slice(header.GpuAccessorHeader.TextureDescriptorCount * Unsafe.SizeOf<GuestTextureDescriptor>());
- }
-
- return result;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs
deleted file mode 100644
index 9b22cac5..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using Ryujinx.Graphics.Shader;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// The header of a guest shader entry in a guest shader program.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x30)]
- struct GuestShaderCacheEntryHeader
- {
- /// <summary>
- /// The stage of this shader.
- /// </summary>
- public ShaderStage Stage;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public byte Reserved1;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public byte Reserved2;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public byte Reserved3;
-
- /// <summary>
- /// The size of the code section.
- /// </summary>
- public int Size;
-
- /// <summary>
- /// The size of the code2 section if present. (Vertex A)
- /// </summary>
- public int SizeA;
-
- /// <summary>
- /// Constant buffer 1 data size.
- /// </summary>
- public int Cb1DataSize;
-
- /// <summary>
- /// The header of the cached gpu accessor.
- /// </summary>
- public GuestGpuAccessorHeader GpuAccessorHeader;
-
- /// <summary>
- /// Create a new guest shader entry header.
- /// </summary>
- /// <param name="stage">The stage of this shader</param>
- /// <param name="size">The size of the code section</param>
- /// <param name="sizeA">The size of the code2 section if present (Vertex A)</param>
- /// <param name="cb1DataSize">Constant buffer 1 data size</param>
- /// <param name="gpuAccessorHeader">The header of the cached gpu accessor</param>
- public GuestShaderCacheEntryHeader(ShaderStage stage, int size, int sizeA, int cb1DataSize, GuestGpuAccessorHeader gpuAccessorHeader) : this()
- {
- Stage = stage;
- Size = size;
- SizeA = sizeA;
- Cb1DataSize = cb1DataSize;
- GpuAccessorHeader = gpuAccessorHeader;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs
deleted file mode 100644
index 700be47d..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// The header of a shader program in the guest cache.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x10)]
- struct GuestShaderCacheHeader
- {
- /// <summary>
- /// The count of shaders defining this program.
- /// </summary>
- public byte Count;
-
- /// <summary>
- /// The count of transform feedback data used in this program.
- /// </summary>
- public byte TransformFeedbackCount;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public ushort Reserved1;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public ulong Reserved2;
-
- /// <summary>
- /// Create a new guest shader cache header.
- /// </summary>
- /// <param name="count">The count of shaders defining this program</param>
- /// <param name="transformFeedbackCount">The count of transform feedback data used in this program</param>
- public GuestShaderCacheHeader(byte count, byte transformFeedbackCount) : this()
- {
- Count = count;
- TransformFeedbackCount = transformFeedbackCount;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs
deleted file mode 100644
index 18cfdf55..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Header for transform feedback.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
- struct GuestShaderCacheTransformFeedbackHeader
- {
- /// <summary>
- /// The buffer index of the transform feedback.
- /// </summary>
- public int BufferIndex;
-
- /// <summary>
- /// The stride of the transform feedback.
- /// </summary>
- public int Stride;
-
- /// <summary>
- /// The length of the varying location buffer of the transform feedback.
- /// </summary>
- public int VaryingLocationsLength;
-
- /// <summary>
- /// Reserved/unused.
- /// </summary>
- public int Reserved1;
-
- public GuestShaderCacheTransformFeedbackHeader(int bufferIndex, int stride, int varyingLocationsLength) : this()
- {
- BufferIndex = bufferIndex;
- Stride = stride;
- VaryingLocationsLength = varyingLocationsLength;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs
deleted file mode 100644
index 9491496d..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Ryujinx.Graphics.Gpu.Image;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Contains part of TextureDescriptor from <see cref="Image"/> used for shader codegen.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Size = 0xC, Pack = 1)]
- struct GuestTextureDescriptor : ITextureDescriptor
- {
- public uint Handle;
- public uint Format;
- public TextureTarget Target;
- [MarshalAs(UnmanagedType.I1)]
- public bool IsSrgb;
- [MarshalAs(UnmanagedType.I1)]
- public bool IsTextureCoordNormalized;
- public byte Reserved;
-
- public uint UnpackFormat()
- {
- return Format;
- }
-
- public bool UnpackSrgb()
- {
- return IsSrgb;
- }
-
- public bool UnpackTextureCoordNormalized()
- {
- return IsTextureCoordNormalized;
- }
-
- public TextureTarget UnpackTextureTarget()
- {
- return Target;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
deleted file mode 100644
index fe79acb3..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.Shader;
-using System;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Host shader entry used for binding information.
- /// </summary>
- class HostShaderCacheEntry
- {
- /// <summary>
- /// The header of the cached shader entry.
- /// </summary>
- public HostShaderCacheEntryHeader Header { get; }
-
- /// <summary>
- /// Cached constant buffers.
- /// </summary>
- public BufferDescriptor[] CBuffers { get; }
-
- /// <summary>
- /// Cached storage buffers.
- /// </summary>
- public BufferDescriptor[] SBuffers { get; }
-
- /// <summary>
- /// Cached texture descriptors.
- /// </summary>
- public TextureDescriptor[] Textures { get; }
-
- /// <summary>
- /// Cached image descriptors.
- /// </summary>
- public TextureDescriptor[] Images { get; }
-
- /// <summary>
- /// Create a new instance of <see cref="HostShaderCacheEntry"/>.
- /// </summary>
- /// <param name="header">The header of the cached shader entry</param>
- /// <param name="cBuffers">Cached constant buffers</param>
- /// <param name="sBuffers">Cached storage buffers</param>
- /// <param name="textures">Cached texture descriptors</param>
- /// <param name="images">Cached image descriptors</param>
- private HostShaderCacheEntry(
- HostShaderCacheEntryHeader header,
- BufferDescriptor[] cBuffers,
- BufferDescriptor[] sBuffers,
- TextureDescriptor[] textures,
- TextureDescriptor[] images)
- {
- Header = header;
- CBuffers = cBuffers;
- SBuffers = sBuffers;
- Textures = textures;
- Images = images;
- }
-
- private HostShaderCacheEntry()
- {
- Header = new HostShaderCacheEntryHeader();
- CBuffers = new BufferDescriptor[0];
- SBuffers = new BufferDescriptor[0];
- Textures = new TextureDescriptor[0];
- Images = new TextureDescriptor[0];
- }
-
- private HostShaderCacheEntry(ShaderProgramInfo programInfo)
- {
- Header = new HostShaderCacheEntryHeader(programInfo.CBuffers.Count,
- programInfo.SBuffers.Count,
- programInfo.Textures.Count,
- programInfo.Images.Count,
- programInfo.UsesInstanceId,
- programInfo.UsesRtLayer,
- programInfo.ClipDistancesWritten,
- programInfo.FragmentOutputMap);
- CBuffers = programInfo.CBuffers.ToArray();
- SBuffers = programInfo.SBuffers.ToArray();
- Textures = programInfo.Textures.ToArray();
- Images = programInfo.Images.ToArray();
- }
-
- /// <summary>
- /// Convert the host shader entry to a <see cref="ShaderProgramInfo"/>.
- /// </summary>
- /// <returns>A new <see cref="ShaderProgramInfo"/> from this instance</returns>
- internal ShaderProgramInfo ToShaderProgramInfo()
- {
- return new ShaderProgramInfo(
- CBuffers,
- SBuffers,
- Textures,
- Images,
- default,
- Header.UseFlags.HasFlag(UseFlags.InstanceId),
- Header.UseFlags.HasFlag(UseFlags.RtLayer),
- Header.ClipDistancesWritten,
- Header.FragmentOutputMap);
- }
-
- /// <summary>
- /// Parse a raw cached user shader program into an array of shader cache entry.
- /// </summary>
- /// <param name="data">The raw cached host shader</param>
- /// <param name="programCode">The host shader program</param>
- /// <returns>An array of shader cache entry</returns>
- internal static HostShaderCacheEntry[] Parse(ReadOnlySpan<byte> data, out ReadOnlySpan<byte> programCode)
- {
- HostShaderCacheHeader fileHeader = MemoryMarshal.Read<HostShaderCacheHeader>(data);
-
- data = data.Slice(Unsafe.SizeOf<HostShaderCacheHeader>());
-
- ReadOnlySpan<HostShaderCacheEntryHeader> entryHeaders = MemoryMarshal.Cast<byte, HostShaderCacheEntryHeader>(data.Slice(0, fileHeader.Count * Unsafe.SizeOf<HostShaderCacheEntryHeader>()));
-
- data = data.Slice(fileHeader.Count * Unsafe.SizeOf<HostShaderCacheEntryHeader>());
-
- HostShaderCacheEntry[] result = new HostShaderCacheEntry[fileHeader.Count];
-
- for (int i = 0; i < result.Length; i++)
- {
- HostShaderCacheEntryHeader header = entryHeaders[i];
-
- if (!header.InUse)
- {
- continue;
- }
-
- int cBufferDescriptorsSize = header.CBuffersCount * Unsafe.SizeOf<BufferDescriptor>();
- int sBufferDescriptorsSize = header.SBuffersCount * Unsafe.SizeOf<BufferDescriptor>();
- int textureDescriptorsSize = header.TexturesCount * Unsafe.SizeOf<TextureDescriptor>();
- int imageDescriptorsSize = header.ImagesCount * Unsafe.SizeOf<TextureDescriptor>();
-
- ReadOnlySpan<BufferDescriptor> cBuffers = MemoryMarshal.Cast<byte, BufferDescriptor>(data.Slice(0, cBufferDescriptorsSize));
- data = data.Slice(cBufferDescriptorsSize);
-
- ReadOnlySpan<BufferDescriptor> sBuffers = MemoryMarshal.Cast<byte, BufferDescriptor>(data.Slice(0, sBufferDescriptorsSize));
- data = data.Slice(sBufferDescriptorsSize);
-
- ReadOnlySpan<TextureDescriptor> textureDescriptors = MemoryMarshal.Cast<byte, TextureDescriptor>(data.Slice(0, textureDescriptorsSize));
- data = data.Slice(textureDescriptorsSize);
-
- ReadOnlySpan<TextureDescriptor> imageDescriptors = MemoryMarshal.Cast<byte, TextureDescriptor>(data.Slice(0, imageDescriptorsSize));
- data = data.Slice(imageDescriptorsSize);
-
- result[i] = new HostShaderCacheEntry(header, cBuffers.ToArray(), sBuffers.ToArray(), textureDescriptors.ToArray(), imageDescriptors.ToArray());
- }
-
- programCode = data.Slice(0, fileHeader.CodeSize);
-
- return result;
- }
-
- /// <summary>
- /// Create a new host shader cache file.
- /// </summary>
- /// <param name="programCode">The host shader program</param>
- /// <param name="codeHolders">The shaders code holder</param>
- /// <returns>Raw data of a new host shader cache file</returns>
- internal static byte[] Create(ReadOnlySpan<byte> programCode, CachedShaderStage[] codeHolders)
- {
- HostShaderCacheHeader header = new HostShaderCacheHeader((byte)codeHolders.Length, programCode.Length);
-
- HostShaderCacheEntry[] entries = new HostShaderCacheEntry[codeHolders.Length];
-
- for (int i = 0; i < codeHolders.Length; i++)
- {
- if (codeHolders[i] == null)
- {
- entries[i] = new HostShaderCacheEntry();
- }
- else
- {
- entries[i] = new HostShaderCacheEntry(codeHolders[i].Info);
- }
- }
-
- using (MemoryStream stream = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(stream);
-
- writer.WriteStruct(header);
-
- foreach (HostShaderCacheEntry entry in entries)
- {
- writer.WriteStruct(entry.Header);
- }
-
- foreach (HostShaderCacheEntry entry in entries)
- {
- foreach (BufferDescriptor cBuffer in entry.CBuffers)
- {
- writer.WriteStruct(cBuffer);
- }
-
- foreach (BufferDescriptor sBuffer in entry.SBuffers)
- {
- writer.WriteStruct(sBuffer);
- }
-
- foreach (TextureDescriptor texture in entry.Textures)
- {
- writer.WriteStruct(texture);
- }
-
- foreach (TextureDescriptor image in entry.Images)
- {
- writer.WriteStruct(image);
- }
- }
-
- writer.Write(programCode);
-
- return stream.ToArray();
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs
deleted file mode 100644
index c3c0de22..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// Flags indicating if the shader accesses certain built-ins, such as the instance ID.
- /// </summary>
- enum UseFlags : byte
- {
- /// <summary>
- /// None of the built-ins are used.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// Indicates whenever the vertex shader reads the gl_InstanceID built-in.
- /// </summary>
- InstanceId = 1 << 0,
-
- /// <summary>
- /// Indicates whenever any of the VTG stages writes to the gl_Layer built-in.
- /// </summary>
- RtLayer = 1 << 1
- }
-
- /// <summary>
- /// Host shader entry header used for binding information.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
- struct HostShaderCacheEntryHeader
- {
- /// <summary>
- /// Count of constant buffer descriptors.
- /// </summary>
- public int CBuffersCount;
-
- /// <summary>
- /// Count of storage buffer descriptors.
- /// </summary>
- public int SBuffersCount;
-
- /// <summary>
- /// Count of texture descriptors.
- /// </summary>
- public int TexturesCount;
-
- /// <summary>
- /// Count of image descriptors.
- /// </summary>
- public int ImagesCount;
-
- /// <summary>
- /// Flags indicating if the shader accesses certain built-ins, such as the instance ID.
- /// </summary>
- public UseFlags UseFlags;
-
- /// <summary>
- /// Set to true if this entry is in use.
- /// </summary>
- [MarshalAs(UnmanagedType.I1)]
- public bool InUse;
-
- /// <summary>
- /// Mask of clip distances that are written to on the shader.
- /// </summary>
- public byte ClipDistancesWritten;
-
- /// <summary>
- /// Reserved / unused.
- /// </summary>
- public byte Reserved;
-
- /// <summary>
- /// Mask of components written by the fragment shader stage.
- /// </summary>
- public int FragmentOutputMap;
-
- /// <summary>
- /// Create a new host shader cache entry header.
- /// </summary>
- /// <param name="cBuffersCount">Count of constant buffer descriptors</param>
- /// <param name="sBuffersCount">Count of storage buffer descriptors</param>
- /// <param name="texturesCount">Count of texture descriptors</param>
- /// <param name="imagesCount">Count of image descriptors</param>
- /// <param name="usesInstanceId">Set to true if the shader uses instance id</param>
- /// <param name="clipDistancesWritten">Mask of clip distances that are written to on the shader</param>
- /// <param name="fragmentOutputMap">Mask of components written by the fragment shader stage</param>
- public HostShaderCacheEntryHeader(
- int cBuffersCount,
- int sBuffersCount,
- int texturesCount,
- int imagesCount,
- bool usesInstanceId,
- bool usesRtLayer,
- byte clipDistancesWritten,
- int fragmentOutputMap) : this()
- {
- CBuffersCount = cBuffersCount;
- SBuffersCount = sBuffersCount;
- TexturesCount = texturesCount;
- ImagesCount = imagesCount;
- ClipDistancesWritten = clipDistancesWritten;
- FragmentOutputMap = fragmentOutputMap;
- InUse = true;
-
- UseFlags = usesInstanceId ? UseFlags.InstanceId : UseFlags.None;
-
- if (usesRtLayer)
- {
- UseFlags |= UseFlags.RtLayer;
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs
deleted file mode 100644
index 27f216cc..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
-{
- /// <summary>
- /// The header of a shader program in the guest cache.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x10)]
- struct HostShaderCacheHeader
- {
- /// <summary>
- /// The count of shaders defining this program.
- /// </summary>
- public byte Count;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public byte Reserved1;
-
- /// <summary>
- /// Unused/reserved.
- /// </summary>
- public ushort Reserved2;
-
- /// <summary>
- /// Size of the shader binary.
- /// </summary>
- public int CodeSize;
-
- /// <summary>
- /// Create a new host shader cache header.
- /// </summary>
- /// <param name="count">The count of shaders defining this program</param>
- /// <param name="codeSize">The size of the shader binary</param>
- public HostShaderCacheHeader(byte count, int codeSize) : this()
- {
- Count = count;
- CodeSize = codeSize;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs
deleted file mode 100644
index 885bcd09..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs
+++ /dev/null
@@ -1,258 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine.Threed;
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using Ryujinx.Graphics.Gpu.Shader.DiskCache;
-using Ryujinx.Graphics.Shader;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- /// <summary>
- /// Class handling shader cache migrations.
- /// </summary>
- static class Migration
- {
- // Last codegen version before the migration to the new cache.
- private const ulong ShaderCodeGenVersion = 3054;
-
- /// <summary>
- /// Migrates from the old cache format to the new one.
- /// </summary>
- /// <param name="context">GPU context</param>
- /// <param name="hostStorage">Disk cache host storage (used to create the new shader files)</param>
- /// <returns>Number of migrated shaders</returns>
- public static int MigrateFromLegacyCache(GpuContext context, DiskCacheHostStorage hostStorage)
- {
- string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId);
- string cacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, CacheGraphicsApi.Guest, "", "program");
-
- // If the directory does not exist, we have no old cache.
- // Exist early as the CacheManager constructor will create the directories.
- if (!Directory.Exists(cacheDirectory))
- {
- return 0;
- }
-
- if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null)
- {
- CacheManager cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion);
-
- bool isReadOnly = cacheManager.IsReadOnly;
-
- HashSet<Hash128> invalidEntries = null;
-
- if (isReadOnly)
- {
- 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();
-
- for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++)
- {
- Hash128 key = guestProgramList[programIndex];
-
- 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?)");
-
- 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];
-
- byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray();
-
- Span<byte> codeSpan = entry.Code;
- byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray();
-
- ShaderProgramInfo info = new ShaderProgramInfo(
- Array.Empty<BufferDescriptor>(),
- Array.Empty<BufferDescriptor>(),
- Array.Empty<TextureDescriptor>(),
- Array.Empty<TextureDescriptor>(),
- ShaderStage.Compute,
- false,
- false,
- 0,
- 0);
-
- GpuChannelComputeState computeState = new GpuChannelComputeState(
- entry.Header.GpuAccessorHeader.ComputeLocalSizeX,
- entry.Header.GpuAccessorHeader.ComputeLocalSizeY,
- entry.Header.GpuAccessorHeader.ComputeLocalSizeZ,
- entry.Header.GpuAccessorHeader.ComputeLocalMemorySize,
- entry.Header.GpuAccessorHeader.ComputeSharedMemorySize);
-
- ShaderSpecializationState specState = new ShaderSpecializationState(computeState);
-
- foreach (var td in entry.TextureDescriptors)
- {
- var handle = td.Key;
- var data = td.Value;
-
- specState.RegisterTexture(
- 0,
- handle,
- -1,
- data.UnpackFormat(),
- data.UnpackSrgb(),
- data.UnpackTextureTarget(),
- data.UnpackTextureCoordNormalized());
- }
-
- CachedShaderStage shader = new CachedShaderStage(info, code, cb1Data);
- CachedShaderProgram program = new CachedShaderProgram(null, specState, shader);
-
- hostStorage.AddShader(context, program, ReadOnlySpan<byte>.Empty);
- }
- else
- {
- Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages);
-
- CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
- List<ShaderProgram> shaderPrograms = new List<ShaderProgram>();
-
- TransformFeedbackDescriptorOld[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader);
-
- GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray();
-
- GuestGpuAccessorHeader accessorHeader = entries[0].Header.GpuAccessorHeader;
-
- TessMode tessMode = new TessMode();
-
- int tessPatchType = accessorHeader.TessellationModePacked & 3;
- int tessSpacing = (accessorHeader.TessellationModePacked >> 2) & 3;
- bool tessCw = (accessorHeader.TessellationModePacked & 0x10) != 0;
-
- tessMode.Packed = (uint)tessPatchType;
- tessMode.Packed |= (uint)(tessSpacing << 4);
-
- if (tessCw)
- {
- tessMode.Packed |= 0x100;
- }
-
- PrimitiveTopology topology = accessorHeader.PrimitiveTopology switch
- {
- InputTopology.Lines => PrimitiveTopology.Lines,
- InputTopology.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
- InputTopology.Triangles => PrimitiveTopology.Triangles,
- InputTopology.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
- _ => PrimitiveTopology.Points
- };
-
- GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
- accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
- topology,
- tessMode,
- false,
- false,
- false);
-
- TransformFeedbackDescriptor[] tfdNew = null;
-
- if (tfd != null)
- {
- tfdNew = new TransformFeedbackDescriptor[tfd.Length];
-
- for (int tfIndex = 0; tfIndex < tfd.Length; tfIndex++)
- {
- Array32<uint> varyingLocations = new Array32<uint>();
- Span<byte> varyingLocationsSpan = MemoryMarshal.Cast<uint, byte>(varyingLocations.ToSpan());
- tfd[tfIndex].VaryingLocations.CopyTo(varyingLocationsSpan.Slice(0, tfd[tfIndex].VaryingLocations.Length));
-
- tfdNew[tfIndex] = new TransformFeedbackDescriptor(
- tfd[tfIndex].BufferIndex,
- tfd[tfIndex].Stride,
- tfd[tfIndex].VaryingLocations.Length,
- ref varyingLocations);
- }
- }
-
- ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, tfdNew);
-
- for (int i = 0; i < entries.Length; i++)
- {
- GuestShaderCacheEntry entry = entries[i];
-
- if (entry == null)
- {
- continue;
- }
-
- ShaderProgramInfo info = new ShaderProgramInfo(
- Array.Empty<BufferDescriptor>(),
- Array.Empty<BufferDescriptor>(),
- Array.Empty<TextureDescriptor>(),
- Array.Empty<TextureDescriptor>(),
- (ShaderStage)(i + 1),
- false,
- false,
- 0,
- 0);
-
- // 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;
-
- Span<byte> codeSpan = entry.Code;
- byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray();
-
- shaders[i + 1] = new CachedShaderStage(info, code, cb1Data);
-
- if (code2 != null)
- {
- shaders[0] = new CachedShaderStage(null, code2, cb1Data);
- }
-
- foreach (var td in entry.TextureDescriptors)
- {
- var handle = td.Key;
- var data = td.Value;
-
- specState.RegisterTexture(
- i,
- handle,
- -1,
- data.UnpackFormat(),
- data.UnpackSrgb(),
- data.UnpackTextureTarget(),
- data.UnpackTextureCoordNormalized());
- }
- }
-
- CachedShaderProgram program = new CachedShaderProgram(null, specState, shaders);
-
- hostStorage.AddShader(context, program, ReadOnlySpan<byte>.Empty);
- }
- }
-
- return guestProgramList.Length;
- }
-
- return 0;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs
deleted file mode 100644
index 5e9c6711..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- struct TransformFeedbackDescriptorOld
- {
- public int BufferIndex { get; }
- public int Stride { get; }
-
- public byte[] VaryingLocations { get; }
-
- public TransformFeedbackDescriptorOld(int bufferIndex, int stride, byte[] varyingLocations)
- {
- BufferIndex = bufferIndex;
- Stride = stride;
- VaryingLocations = varyingLocations ?? throw new ArgumentNullException(nameof(varyingLocations));
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
index 5c5e41c6..98655ed6 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
@@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
_context = context;
_hostStorage = hostStorage;
- _fileWriterWorkerQueue = new AsyncWorkQueue<CacheFileOperationTask>(ProcessTask, "Gpu.BackgroundDiskCacheWriter");
+ _fileWriterWorkerQueue = new AsyncWorkQueue<CacheFileOperationTask>(ProcessTask, "GPU.BackgroundDiskCacheWriter");
}
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index e5476426..68ff4f2a 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -1,6 +1,8 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Runtime.InteropServices;
@@ -16,7 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly ShaderSpecializationState _oldSpecState;
private readonly ShaderSpecializationState _newSpecState;
private readonly int _stageIndex;
- private ResourceCounts _resourceCounts;
+ private readonly bool _isVulkan;
+ private readonly ResourceCounts _resourceCounts;
/// <summary>
/// Creates a new instance of the cached GPU state accessor for shader translation.
@@ -34,13 +37,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderSpecializationState oldSpecState,
ShaderSpecializationState newSpecState,
ResourceCounts counts,
- int stageIndex) : base(context)
+ int stageIndex) : base(context, counts, stageIndex)
{
_data = data;
_cb1Data = cb1Data;
_oldSpecState = oldSpecState;
_newSpecState = newSpecState;
_stageIndex = stageIndex;
+ _isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_resourceCounts = counts;
}
@@ -74,27 +78,33 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
/// <inheritdoc/>
- public int QueryBindingConstantBuffer(int index)
+ public AlphaTestOp QueryAlphaTestCompare()
{
- return _resourceCounts.UniformBuffersCount++;
- }
+ if (!_isVulkan || !_oldSpecState.GraphicsState.AlphaTestEnable)
+ {
+ return AlphaTestOp.Always;
+ }
- /// <inheritdoc/>
- public int QueryBindingStorageBuffer(int index)
- {
- return _resourceCounts.StorageBuffersCount++;
+ return _oldSpecState.GraphicsState.AlphaTestCompare switch
+ {
+ CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never,
+ CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less,
+ CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal,
+ CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual,
+ CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater,
+ CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual,
+ CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual,
+ _ => AlphaTestOp.Always
+ };
}
/// <inheritdoc/>
- public int QueryBindingTexture(int index)
- {
- return _resourceCounts.TexturesCount++;
- }
+ public float QueryAlphaTestReference() => _oldSpecState.GraphicsState.AlphaTestReference;
/// <inheritdoc/>
- public int QueryBindingImage(int index)
+ public AttributeType QueryAttributeType(int location)
{
- return _resourceCounts.ImagesCount++;
+ return _oldSpecState.GraphicsState.AttributeTypes[location];
}
/// <inheritdoc/>
@@ -127,6 +137,18 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
/// <inheritdoc/>
+ public bool QueryProgramPointSize()
+ {
+ return _oldSpecState.GraphicsState.ProgramPointSizeEnable;
+ }
+
+ /// <inheritdoc/>
+ public float QueryPointSize()
+ {
+ return _oldSpecState.GraphicsState.PointSize;
+ }
+
+ /// <inheritdoc/>
public bool QueryTessCw()
{
return _oldSpecState.GraphicsState.TessellationMode.UnpackCw();
@@ -167,6 +189,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
/// <inheritdoc/>
+ public bool QueryTransformDepthMinusOneToOne()
+ {
+ return _oldSpecState.GraphicsState.DepthMode;
+ }
+
+ /// <inheritdoc/>
public bool QueryTransformFeedbackEnabled()
{
return _oldSpecState.TransformFeedbackDescriptors != null;
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs
index 4e338094..01034b49 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const uint TocMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'G' << 24);
private const ushort VersionMajor = 1;
- private const ushort VersionMinor = 0;
+ private const ushort VersionMinor = 1;
private const uint VersionPacked = ((uint)VersionMajor << 16) | VersionMinor;
private const string TocFileName = "guest.toc";
@@ -193,8 +193,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="tocFileStream">Guest TOC file stream</param>
/// <param name="dataFileStream">Guest data file stream</param>
/// <param name="index">Guest shader index</param>
- /// <returns>Tuple with the guest code and constant buffer 1 data, respectively</returns>
- public (byte[], byte[]) LoadShader(Stream tocFileStream, Stream dataFileStream, int index)
+ /// <returns>Guest code and constant buffer 1 data</returns>
+ public GuestCodeAndCbData LoadShader(Stream tocFileStream, Stream dataFileStream, int index)
{
if (_cache == null || index >= _cache.Length)
{
@@ -226,7 +226,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_cache[index] = (guestCode, cb1Data);
}
- return (guestCode, cb1Data);
+ return new GuestCodeAndCbData(guestCode, cb1Data);
}
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index a47af942..b625835c 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Shader.Translation;
using System;
using System.IO;
using System.Numerics;
@@ -19,9 +20,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const uint TexdMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'D' << 24);
private const ushort FileFormatVersionMajor = 1;
- private const ushort FileFormatVersionMinor = 1;
+ private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 3469;
+ private const uint CodeGenVersion = 13;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
@@ -56,14 +57,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
public uint Padding;
/// <summary>
- /// Reserved space, to be used in the future. Write as zero.
+ /// Timestamp of when the file was first created.
/// </summary>
- public ulong Reserved;
+ public ulong Timestamp;
/// <summary>
/// Reserved space, to be used in the future. Write as zero.
/// </summary>
- public ulong Reserved2;
+ public ulong Reserved;
}
/// <summary>
@@ -77,9 +78,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
public ulong Offset;
/// <summary>
- /// Size.
+ /// Size of uncompressed data.
+ /// </summary>
+ public uint UncompressedSize;
+
+ /// <summary>
+ /// Size of compressed data.
/// </summary>
- public uint Size;
+ public uint CompressedSize;
}
/// <summary>
@@ -185,7 +191,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return 0;
}
- return (int)((new FileInfo(tocFilePath).Length - Unsafe.SizeOf<TocHeader>()) / sizeof(ulong));
+ return Math.Max((int)((new FileInfo(tocFilePath).Length - Unsafe.SizeOf<TocHeader>()) / sizeof(ulong)), 0);
}
/// <summary>
@@ -324,7 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
stagesBitMask = 1;
}
- CachedShaderStage[] shaders = new CachedShaderStage[isCompute ? 1 : Constants.ShaderStages + 1];
+ GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[isCompute ? 1 : Constants.ShaderStages + 1];
DataEntryPerStage stageEntry = new DataEntryPerStage();
@@ -334,15 +340,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataReader.Read(ref stageEntry);
- ShaderProgramInfo info = stageIndex != 0 || isCompute ? ReadShaderProgramInfo(ref dataReader) : null;
-
- (byte[] guestCode, byte[] cb1Data) = _guestStorage.LoadShader(
+ guestShaders[stageIndex] = _guestStorage.LoadShader(
guestTocFileStream,
guestDataFileStream,
stageEntry.GuestCodeIndex);
- shaders[stageIndex] = new CachedShaderStage(info, guestCode, cb1Data);
-
stagesBitMask &= ~(1u << stageIndex);
}
@@ -351,17 +353,39 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (loadHostCache)
{
- byte[] hostCode = ReadHostCode(context, ref hostTocFileStream, ref hostDataFileStream, programIndex);
+ (byte[] hostCode, CachedShaderStage[] shaders) = ReadHostCode(
+ context,
+ ref hostTocFileStream,
+ ref hostDataFileStream,
+ guestShaders,
+ programIndex,
+ header.Timestamp);
if (hostCode != null)
{
bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
- IProgram hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, new ShaderInfo(fragmentOutputMap));
+
+ ShaderInfo shaderInfo = specState.PipelineState.HasValue
+ ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
+ : new ShaderInfo(fragmentOutputMap, fromCache: true);
+
+ IProgram hostProgram;
+
+ if (context.Capabilities.Api == TargetApi.Vulkan)
+ {
+ ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode, isCompute);
+
+ hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
+ }
+ else
+ {
+ hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
+ }
CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders);
- loader.QueueHostProgram(program, hostProgram, programIndex, isCompute);
+ loader.QueueHostProgram(program, hostCode, programIndex, isCompute);
}
else
{
@@ -371,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (!loadHostCache)
{
- loader.QueueGuestProgram(shaders, specState, programIndex, isCompute);
+ loader.QueueGuestProgram(guestShaders, specState, programIndex, isCompute);
}
loader.CheckCompilation();
@@ -393,9 +417,17 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="context">GPU context</param>
/// <param name="tocFileStream">Host TOC file stream, intialized if needed</param>
/// <param name="dataFileStream">Host data file stream, initialized if needed</param>
+ /// <param name="guestShaders">Guest shader code for each active stage</param>
/// <param name="programIndex">Index of the program on the cache</param>
+ /// <param name="expectedTimestamp">Timestamp of the shared cache file. The host file must be newer than it</param>
/// <returns>Host binary code, or null if not found</returns>
- private byte[] ReadHostCode(GpuContext context, ref Stream tocFileStream, ref Stream dataFileStream, int programIndex)
+ private (byte[], CachedShaderStage[]) ReadHostCode(
+ GpuContext context,
+ ref Stream tocFileStream,
+ ref Stream dataFileStream,
+ GuestCodeAndCbData?[] guestShaders,
+ int programIndex,
+ ulong expectedTimestamp)
{
if (tocFileStream == null && dataFileStream == null)
{
@@ -404,17 +436,28 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath))
{
- return null;
+ return (null, null);
}
tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false);
dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false);
+
+ BinarySerializer tempTocReader = new BinarySerializer(tocFileStream);
+
+ TocHeader header = new TocHeader();
+
+ tempTocReader.Read(ref header);
+
+ if (header.Timestamp < expectedTimestamp)
+ {
+ return (null, null);
+ }
}
int offset = Unsafe.SizeOf<TocHeader>() + programIndex * Unsafe.SizeOf<OffsetAndSize>();
if (offset + Unsafe.SizeOf<OffsetAndSize>() > tocFileStream.Length)
{
- return null;
+ return (null, null);
}
if ((ulong)offset >= (ulong)dataFileStream.Length)
@@ -436,11 +479,33 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin);
- byte[] hostCode = new byte[offsetAndSize.Size];
+ byte[] hostCode = new byte[offsetAndSize.UncompressedSize];
BinarySerializer.ReadCompressed(dataFileStream, hostCode);
- return hostCode;
+ CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
+ BinarySerializer dataReader = new BinarySerializer(dataFileStream);
+
+ dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin);
+
+ dataReader.BeginCompression();
+
+ for (int index = 0; index < guestShaders.Length; index++)
+ {
+ if (!guestShaders[index].HasValue)
+ {
+ continue;
+ }
+
+ GuestCodeAndCbData guestShader = guestShaders[index].Value;
+ ShaderProgramInfo info = index != 0 || guestShaders.Length == 1 ? ReadShaderProgramInfo(ref dataReader) : null;
+
+ shaders[index] = new CachedShaderStage(info, guestShader.Code, guestShader.Cb1Data);
+ }
+
+ dataReader.EndCompression();
+
+ return (hostCode, shaders);
}
/// <summary>
@@ -484,10 +549,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
var tocFileStream = streams != null ? streams.TocFileStream : DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
var dataFileStream = streams != null ? streams.DataFileStream : DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
+ ulong timestamp = (ulong)DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
+
if (tocFileStream.Length == 0)
{
TocHeader header = new TocHeader();
- CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion);
+ CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion, timestamp);
}
tocFileStream.Seek(0, SeekOrigin.End);
@@ -519,8 +586,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
stageEntry.GuestCodeIndex = _guestStorage.AddShader(shader.Code, shader.Cb1Data);
dataWriter.Write(ref stageEntry);
-
- WriteShaderProgramInfo(ref dataWriter, shader.Info);
}
program.SpecializationState.Write(ref dataWriter);
@@ -537,7 +602,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return;
}
- WriteHostCode(context, hostCode, -1, streams);
+ WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
}
/// <summary>
@@ -575,28 +640,19 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
/// <summary>
- /// Adds a host binary shader to the host cache.
- /// </summary>
- /// <remarks>
- /// This only modifies the host cache. The shader must already exist in the other caches.
- /// This method should only be used for rebuilding the host cache after a clear.
- /// </remarks>
- /// <param name="context">GPU context</param>
- /// <param name="hostCode">Host binary code</param>
- /// <param name="programIndex">Index of the program in the cache</param>
- public void AddHostShader(GpuContext context, ReadOnlySpan<byte> hostCode, int programIndex)
- {
- WriteHostCode(context, hostCode, programIndex);
- }
-
- /// <summary>
/// Writes the host binary code on the host cache.
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="hostCode">Host binary code</param>
- /// <param name="programIndex">Index of the program in the cache</param>
+ /// <param name="shaders">Shader stages to be added to the host cache</param>
/// <param name="streams">Output streams to use</param>
- private void WriteHostCode(GpuContext context, ReadOnlySpan<byte> hostCode, int programIndex, DiskCacheOutputStreams streams = null)
+ /// <param name="timestamp">File creation timestamp</param>
+ private void WriteHostCode(
+ GpuContext context,
+ ReadOnlySpan<byte> hostCode,
+ CachedShaderStage[] shaders,
+ DiskCacheOutputStreams streams,
+ ulong timestamp)
{
var tocFileStream = streams != null ? streams.HostTocFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
var dataFileStream = streams != null ? streams.HostDataFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
@@ -604,29 +660,39 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (tocFileStream.Length == 0)
{
TocHeader header = new TocHeader();
- CreateToc(tocFileStream, ref header, TochMagic, 0);
- }
-
- if (programIndex == -1)
- {
- tocFileStream.Seek(0, SeekOrigin.End);
- }
- else
- {
- tocFileStream.Seek(Unsafe.SizeOf<TocHeader>() + (programIndex * Unsafe.SizeOf<OffsetAndSize>()), SeekOrigin.Begin);
+ CreateToc(tocFileStream, ref header, TochMagic, 0, timestamp);
}
+ tocFileStream.Seek(0, SeekOrigin.End);
dataFileStream.Seek(0, SeekOrigin.End);
BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
+ BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
OffsetAndSize offsetAndSize = new OffsetAndSize();
offsetAndSize.Offset = (ulong)dataFileStream.Position;
- offsetAndSize.Size = (uint)hostCode.Length;
- tocWriter.Write(ref offsetAndSize);
+ offsetAndSize.UncompressedSize = (uint)hostCode.Length;
+
+ long dataStartPosition = dataFileStream.Position;
BinarySerializer.WriteCompressed(dataFileStream, hostCode, DiskCacheCommon.GetCompressionAlgorithm());
+ offsetAndSize.CompressedSize = (uint)(dataFileStream.Position - dataStartPosition);
+
+ tocWriter.Write(ref offsetAndSize);
+
+ dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
+
+ for (int index = 0; index < shaders.Length; index++)
+ {
+ if (shaders[index] != null)
+ {
+ WriteShaderProgramInfo(ref dataWriter, shaders[index].Info);
+ }
+ }
+
+ dataWriter.EndCompression();
+
if (streams == null)
{
tocFileStream.Dispose();
@@ -641,7 +707,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="header">Set to the TOC file header</param>
/// <param name="magic">Magic value to be written</param>
/// <param name="codegenVersion">Shader codegen version, only valid for the host file</param>
- private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion)
+ /// <param name="timestamp">File creation timestamp</param>
+ private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp)
{
BinarySerializer writer = new BinarySerializer(tocFileStream);
@@ -650,7 +717,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
header.CodeGenVersion = codegenVersion;
header.Padding = 0;
header.Reserved = 0;
- header.Reserved2 = 0;
+ header.Timestamp = timestamp;
if (tocFileStream.Length > 0)
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs
new file mode 100644
index 00000000..b1ac819e
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs
@@ -0,0 +1,31 @@
+using Ryujinx.Graphics.Shader;
+
+namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
+{
+ /// <summary>
+ /// Guest shader code and constant buffer data accessed by the shader.
+ /// </summary>
+ struct GuestCodeAndCbData
+ {
+ /// <summary>
+ /// Maxwell binary shader code.
+ /// </summary>
+ public byte[] Code { get; }
+
+ /// <summary>
+ /// Constant buffer 1 data accessed by the shader.
+ /// </summary>
+ public byte[] Cb1Data { get; }
+
+ /// <summary>
+ /// Creates a new instance of the guest shader code and constant buffer data.
+ /// </summary>
+ /// <param name="code">Maxwell binary shader code</param>
+ /// <param name="cb1Data">Constant buffer 1 data accessed by the shader</param>
+ public GuestCodeAndCbData(byte[] code, byte[] cb1Data)
+ {
+ Code = code;
+ Cb1Data = cb1Data;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
index af7579d5..7bf1cf4b 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
@@ -45,9 +45,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
public readonly CachedShaderProgram CachedProgram;
/// <summary>
- /// Host program.
+ /// Optional binary code. If not null, it is used instead of the backend host binary.
/// </summary>
- public readonly IProgram HostProgram;
+ public readonly byte[] BinaryCode;
/// <summary>
/// Program index.
@@ -68,19 +68,19 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// Creates a new program validation entry.
/// </summary>
/// <param name="cachedProgram">Cached shader program</param>
- /// <param name="hostProgram">Host program</param>
+ /// <param name="binaryCode">Optional binary code. If not null, it is used instead of the backend host binary</param>
/// <param name="programIndex">Program index</param>
/// <param name="isCompute">Indicates if the program is a compute shader</param>
/// <param name="isBinary">Indicates if the program is a host binary shader</param>
public ProgramEntry(
CachedShaderProgram cachedProgram,
- IProgram hostProgram,
+ byte[] binaryCode,
int programIndex,
bool isCompute,
bool isBinary)
{
CachedProgram = cachedProgram;
- HostProgram = hostProgram;
+ BinaryCode = binaryCode;
ProgramIndex = programIndex;
IsCompute = isCompute;
IsBinary = isBinary;
@@ -146,9 +146,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private struct AsyncProgramTranslation
{
/// <summary>
- /// Cached shader stages.
+ /// Guest code for each active stage.
/// </summary>
- public readonly CachedShaderStage[] Shaders;
+ public readonly GuestCodeAndCbData?[] GuestShaders;
/// <summary>
/// Specialization state.
@@ -168,17 +168,17 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <summary>
/// Creates a new program translation entry.
/// </summary>
- /// <param name="shaders">Cached shader stages</param>
+ /// <param name="guestShaders">Guest code for each active stage</param>
/// <param name="specState">Specialization state</param>
/// <param name="programIndex">Program index</param>
/// <param name="isCompute">Indicates if the program is a compute shader</param>
public AsyncProgramTranslation(
- CachedShaderStage[] shaders,
+ GuestCodeAndCbData?[] guestShaders,
ShaderSpecializationState specState,
int programIndex,
bool isCompute)
{
- Shaders = shaders;
+ GuestShaders = guestShaders;
SpecializationState = specState;
ProgramIndex = programIndex;
IsCompute = isCompute;
@@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly Queue<ProgramEntry> _validationQueue;
private readonly ConcurrentQueue<ProgramCompilation> _compilationQueue;
private readonly BlockingCollection<AsyncProgramTranslation> _asyncTranslationQueue;
- private readonly SortedList<int, CachedShaderProgram> _programList;
+ private readonly SortedList<int, (CachedShaderProgram, byte[])> _programList;
private int _backendParallelCompileThreads;
private int _compiledCount;
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_validationQueue = new Queue<ProgramEntry>();
_compilationQueue = new ConcurrentQueue<ProgramCompilation>();
_asyncTranslationQueue = new BlockingCollection<AsyncProgramTranslation>(ThreadCount);
- _programList = new SortedList<int, CachedShaderProgram>();
+ _programList = new SortedList<int, (CachedShaderProgram, byte[])>();
_backendParallelCompileThreads = Math.Min(Environment.ProcessorCount, 8); // Must be kept in sync with the backend code.
}
@@ -235,7 +235,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
workThreads[index] = new Thread(ProcessAsyncQueue)
{
- Name = $"Gpu.AsyncTranslationThread.{index}"
+ Name = $"GPU.AsyncTranslationThread.{index}"
};
}
@@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
CheckCompilationBlocking();
- if (_needsHostRegen)
+ if (_needsHostRegen && Active)
{
// Rebuild both shared and host cache files.
// Rebuilding shared is required because the shader information returned by the translator
@@ -310,8 +310,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
break;
}
- CachedShaderProgram program = kv.Value;
- _hostStorage.AddShader(_context, program, program.HostProgram.GetBinary(), streams);
+ (CachedShaderProgram program, byte[] binaryCode) = kv.Value;
+ _hostStorage.AddShader(_context, program, binaryCode, streams);
}
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
@@ -342,24 +342,31 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// Enqueues a host program for compilation.
/// </summary>
/// <param name="cachedProgram">Cached program</param>
- /// <param name="hostProgram">Host program to be compiled</param>
+ /// <param name="binaryCode">Host binary code</param>
/// <param name="programIndex">Program index</param>
/// <param name="isCompute">Indicates if the program is a compute shader</param>
- public void QueueHostProgram(CachedShaderProgram cachedProgram, IProgram hostProgram, int programIndex, bool isCompute)
+ public void QueueHostProgram(CachedShaderProgram cachedProgram, byte[] binaryCode, int programIndex, bool isCompute)
{
- EnqueueForValidation(new ProgramEntry(cachedProgram, hostProgram, programIndex, isCompute, isBinary: true));
+ EnqueueForValidation(new ProgramEntry(cachedProgram, binaryCode, programIndex, isCompute, isBinary: true));
}
/// <summary>
/// Enqueues a guest program for compilation.
/// </summary>
- /// <param name="shaders">Cached shader stages</param>
+ /// <param name="guestShaders">Guest code for each active stage</param>
/// <param name="specState">Specialization state</param>
/// <param name="programIndex">Program index</param>
/// <param name="isCompute">Indicates if the program is a compute shader</param>
- public void QueueGuestProgram(CachedShaderStage[] shaders, ShaderSpecializationState specState, int programIndex, bool isCompute)
+ public void QueueGuestProgram(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute)
{
- _asyncTranslationQueue.Add(new AsyncProgramTranslation(shaders, specState, programIndex, isCompute));
+ try
+ {
+ AsyncProgramTranslation asyncTranslation = new AsyncProgramTranslation(guestShaders, specState, programIndex, isCompute);
+ _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ }
}
/// <summary>
@@ -374,7 +381,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
// If not yet compiled, do nothing. This avoids blocking to wait for shader compilation.
while (_validationQueue.TryPeek(out ProgramEntry entry))
{
- ProgramLinkStatus result = entry.HostProgram.CheckProgramLink(false);
+ ProgramLinkStatus result = entry.CachedProgram.HostProgram.CheckProgramLink(false);
if (result != ProgramLinkStatus.Incomplete)
{
@@ -398,7 +405,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
while (_validationQueue.TryDequeue(out ProgramEntry entry) && Active)
{
- ProcessCompiledProgram(ref entry, entry.HostProgram.CheckProgramLink(true), asyncCompile: false);
+ ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false);
}
}
@@ -427,7 +434,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_needsHostRegen = true;
}
- _programList.Add(entry.ProgramIndex, entry.CachedProgram);
+ _programList.Add(entry.ProgramIndex, (entry.CachedProgram, entry.BinaryCode));
SignalCompiled();
}
else if (entry.IsBinary)
@@ -436,13 +443,25 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
// we still have a chance to recompile from the guest binary.
CachedShaderProgram program = entry.CachedProgram;
+ GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[program.Shaders.Length];
+
+ for (int index = 0; index < program.Shaders.Length; index++)
+ {
+ CachedShaderStage shader = program.Shaders[index];
+
+ if (shader != null)
+ {
+ guestShaders[index] = new GuestCodeAndCbData(shader.Code, shader.Cb1Data);
+ }
+ }
+
if (asyncCompile)
{
- QueueGuestProgram(program.Shaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute);
+ QueueGuestProgram(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute);
}
else
{
- RecompileFromGuestCode(program.Shaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute);
+ RecompileFromGuestCode(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute);
ProcessCompilationQueue();
}
}
@@ -476,10 +495,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
}
- IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, new ShaderInfo(fragmentOutputMap));
+ ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue
+ ? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true)
+ : new ShaderInfo(fragmentOutputMap, fromCache: true);
+
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
- EnqueueForValidation(new ProgramEntry(program, hostProgram, compilation.ProgramIndex, compilation.IsCompute, isBinary: false));
+ byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : hostProgram.GetBinary();
+
+ EnqueueForValidation(new ProgramEntry(program, binaryCode, compilation.ProgramIndex, compilation.IsCompute, isBinary: false));
}
}
@@ -496,7 +521,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
// Submitting more seems to cause NVIDIA OpenGL driver to crash.
if (_validationQueue.Count >= _backendParallelCompileThreads && _validationQueue.TryDequeue(out ProgramEntry entry))
{
- ProcessCompiledProgram(ref entry, entry.HostProgram.CheckProgramLink(true), asyncCompile: false);
+ ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false);
}
}
@@ -513,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
foreach (AsyncProgramTranslation asyncCompilation in _asyncTranslationQueue.GetConsumingEnumerable(ct))
{
RecompileFromGuestCode(
- asyncCompilation.Shaders,
+ asyncCompilation.GuestShaders,
asyncCompilation.SpecializationState,
asyncCompilation.ProgramIndex,
asyncCompilation.IsCompute);
@@ -527,21 +552,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <summary>
/// Recompiles a program from guest code.
/// </summary>
- /// <param name="shaders">Shader stages</param>
+ /// <param name="guestShaders">Guest code for each active stage</param>
/// <param name="specState">Specialization state</param>
/// <param name="programIndex">Program index</param>
/// <param name="isCompute">Indicates if the program is a compute shader</param>
- private void RecompileFromGuestCode(CachedShaderStage[] shaders, ShaderSpecializationState specState, int programIndex, bool isCompute)
+ private void RecompileFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute)
{
try
{
if (isCompute)
{
- RecompileComputeFromGuestCode(shaders, specState, programIndex);
+ RecompileComputeFromGuestCode(guestShaders, specState, programIndex);
}
else
{
- RecompileGraphicsFromGuestCode(shaders, specState, programIndex);
+ RecompileGraphicsFromGuestCode(guestShaders, specState, programIndex);
}
}
catch (DiskCacheLoadException diskCacheLoadException)
@@ -556,41 +581,47 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <summary>
/// Recompiles a graphics program from guest code.
/// </summary>
- /// <param name="shaders">Shader stages</param>
+ /// <param name="guestShaders">Guest code for each active stage</param>
/// <param name="specState">Specialization state</param>
/// <param name="programIndex">Program index</param>
- private void RecompileGraphicsFromGuestCode(CachedShaderStage[] shaders, ShaderSpecializationState specState, int programIndex)
+ private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex)
{
- ShaderSpecializationState newSpecState = new ShaderSpecializationState(specState.GraphicsState, specState.TransformFeedbackDescriptors);
+ ShaderSpecializationState newSpecState = new ShaderSpecializationState(
+ ref specState.GraphicsState,
+ specState.PipelineState,
+ specState.TransformFeedbackDescriptors);
+
ResourceCounts counts = new ResourceCounts();
TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
TranslatorContext nextStage = null;
+ TargetApi api = _context.Capabilities.Api;
+
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{
- CachedShaderStage shader = shaders[stageIndex + 1];
-
- if (shader != null)
+ if (guestShaders[stageIndex + 1].HasValue)
{
+ GuestCodeAndCbData shader = guestShaders[stageIndex + 1].Value;
+
byte[] guestCode = shader.Code;
byte[] cb1Data = shader.Cb1Data;
DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex);
- TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, 0);
+ TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
if (nextStage != null)
{
currentStage.SetNextStage(nextStage);
}
- if (stageIndex == 0 && shaders[0] != null)
+ if (stageIndex == 0 && guestShaders[0].HasValue)
{
- byte[] guestCodeA = shaders[0].Code;
- byte[] cb1DataA = shaders[0].Cb1Data;
+ byte[] guestCodeA = guestShaders[0].Value.Code;
+ byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
DiskCacheGpuAccessor gpuAccessorA = new DiskCacheGpuAccessor(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0);
- translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, DefaultFlags | TranslationFlags.VertexA, 0);
+ translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
}
translatorContexts[stageIndex + 1] = currentStage;
@@ -598,6 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
}
+ CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
@@ -608,15 +640,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
ShaderProgram program;
- byte[] guestCode = shaders[stageIndex + 1].Code;
- byte[] cb1Data = shaders[stageIndex + 1].Cb1Data;
+ byte[] guestCode = guestShaders[stageIndex + 1].Value.Code;
+ byte[] cb1Data = guestShaders[stageIndex + 1].Value.Cb1Data;
- if (stageIndex == 0 && shaders[0] != null)
+ if (stageIndex == 0 && guestShaders[0].HasValue)
{
program = currentStage.Translate(translatorContexts[0]);
- byte[] guestCodeA = shaders[0].Code;
- byte[] cb1DataA = shaders[0].Cb1Data;
+ byte[] guestCodeA = guestShaders[0].Value.Code;
+ byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
shaders[0] = new CachedShaderStage(null, guestCodeA, cb1DataA);
shaders[1] = new CachedShaderStage(program.Info, guestCode, cb1Data);
@@ -641,21 +673,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <summary>
/// Recompiles a compute program from guest code.
/// </summary>
- /// <param name="shaders">Shader stages</param>
+ /// <param name="guestShaders">Guest code for each active stage</param>
/// <param name="specState">Specialization state</param>
/// <param name="programIndex">Program index</param>
- private void RecompileComputeFromGuestCode(CachedShaderStage[] shaders, ShaderSpecializationState specState, int programIndex)
+ private void RecompileComputeFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex)
{
- CachedShaderStage shader = shaders[0];
+ GuestCodeAndCbData shader = guestShaders[0].Value;
ResourceCounts counts = new ResourceCounts();
- ShaderSpecializationState newSpecState = new ShaderSpecializationState(specState.ComputeState);
+ ShaderSpecializationState newSpecState = new ShaderSpecializationState(ref specState.ComputeState);
DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0);
- TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, 0);
+ TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);
ShaderProgram program = translatorContext.Translate();
- shaders[0] = new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data);
+ CachedShaderStage[] shaders = new[] { new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data) };
_compilationQueue.Enqueue(new ProgramCompilation(new[] { program }, shaders, newSpecState, programIndex, isCompute: true));
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
new file mode 100644
index 00000000..11e54220
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
@@ -0,0 +1,49 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Shader.Translation;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
+{
+ static class ShaderBinarySerializer
+ {
+ public static byte[] Pack(ShaderSource[] sources)
+ {
+ using MemoryStream output = new MemoryStream();
+ using BinaryWriter writer = new BinaryWriter(output);
+
+ for (int i = 0; i < sources.Length; i++)
+ {
+ writer.Write(sources[i].BinaryCode.Length);
+ writer.Write(sources[i].BinaryCode);
+ }
+
+ return output.ToArray();
+ }
+
+ public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code, bool compute)
+ {
+ using MemoryStream input = new MemoryStream(code);
+ using BinaryReader reader = new BinaryReader(input);
+
+ List<ShaderSource> output = new List<ShaderSource>();
+
+ for (int i = compute ? 0 : 1; i < stages.Length; i++)
+ {
+ CachedShaderStage stage = stages[i];
+
+ if (stage == null)
+ {
+ continue;
+ }
+
+ int binaryCodeLength = reader.ReadInt32();
+ byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
+
+ output.Add(new ShaderSource(binaryCode, ShaderCache.GetBindings(stage.Info), stage.Info.Stage, TargetLanguage.Spirv));
+ }
+
+ return output.ToArray();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 5317aab9..44c26efb 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -1,6 +1,8 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Runtime.InteropServices;
@@ -15,6 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly GpuAccessorState _state;
private readonly int _stageIndex;
private readonly bool _compute;
+ private readonly bool _isVulkan;
/// <summary>
/// Creates a new instance of the GPU state accessor for graphics shader translation.
@@ -23,8 +26,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</param>
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
- public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context)
+ public GpuAccessor(
+ GpuContext context,
+ GpuChannel channel,
+ GpuAccessorState state,
+ int stageIndex) : base(context, state.ResourceCounts, stageIndex)
{
+ _isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_channel = channel;
_state = state;
_stageIndex = stageIndex;
@@ -36,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</param>
- public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context)
+ public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0)
{
_channel = channel;
_state = state;
@@ -73,27 +81,36 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <inheritdoc/>
- public int QueryBindingConstantBuffer(int index)
+ public AlphaTestOp QueryAlphaTestCompare()
{
- return _state.ResourceCounts.UniformBuffersCount++;
- }
+ if (!_isVulkan || !_state.GraphicsState.AlphaTestEnable)
+ {
+ return AlphaTestOp.Always;
+ }
- /// <inheritdoc/>
- public int QueryBindingStorageBuffer(int index)
- {
- return _state.ResourceCounts.StorageBuffersCount++;
+ return _state.GraphicsState.AlphaTestCompare switch
+ {
+ CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never,
+ CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less,
+ CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal,
+ CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual,
+ CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater,
+ CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual,
+ CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual,
+ _ => AlphaTestOp.Always
+ };
}
/// <inheritdoc/>
- public int QueryBindingTexture(int index)
+ public float QueryAlphaTestReference()
{
- return _state.ResourceCounts.TexturesCount++;
+ return _state.GraphicsState.AlphaTestReference;
}
/// <inheritdoc/>
- public int QueryBindingImage(int index)
+ public AttributeType QueryAttributeType(int location)
{
- return _state.ResourceCounts.ImagesCount++;
+ return _state.GraphicsState.AttributeTypes[location];
}
/// <inheritdoc/>
@@ -130,6 +147,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <inheritdoc/>
+ public bool QueryProgramPointSize()
+ {
+ return _state.GraphicsState.ProgramPointSizeEnable;
+ }
+
+ /// <inheritdoc/>
+ public float QueryPointSize()
+ {
+ return _state.GraphicsState.PointSize;
+ }
+
+ /// <inheritdoc/>
public bool QueryTessCw()
{
return _state.GraphicsState.TessellationMode.UnpackCw();
@@ -199,6 +228,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <inheritdoc/>
+ public bool QueryTransformDepthMinusOneToOne()
+ {
+ return _state.GraphicsState.DepthMode;
+ }
+
+ /// <inheritdoc/>
public bool QueryTransformFeedbackEnabled()
{
return _state.TransformFeedbackDescriptors != null;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 5f9dd588..7243f643 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -1,7 +1,9 @@
+using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Shader.Translation;
namespace Ryujinx.Graphics.Gpu.Shader
{
@@ -11,74 +13,140 @@ namespace Ryujinx.Graphics.Gpu.Shader
class GpuAccessorBase
{
private readonly GpuContext _context;
+ private readonly ResourceCounts _resourceCounts;
+ private readonly int _stageIndex;
/// <summary>
/// Creates a new GPU accessor.
/// </summary>
/// <param name="context">GPU context</param>
- public GpuAccessorBase(GpuContext context)
+ public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex)
{
_context = context;
+ _resourceCounts = resourceCounts;
+ _stageIndex = stageIndex;
}
- /// <summary>
- /// Queries host about the presence of the FrontFacing built-in variable bug.
- /// </summary>
- /// <returns>True if the bug is present on the host device used, false otherwise</returns>
+ /// <inheritdoc/>
+ public int QueryBindingConstantBuffer(int index)
+ {
+ if (_context.Capabilities.Api == TargetApi.Vulkan)
+ {
+ // We need to start counting from 1 since binding 0 is reserved for the support uniform buffer.
+ return GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer") + 1;
+ }
+ else
+ {
+ return _resourceCounts.UniformBuffersCount++;
+ }
+ }
+
+ /// <inheritdoc/>
+ public int QueryBindingStorageBuffer(int index)
+ {
+ if (_context.Capabilities.Api == TargetApi.Vulkan)
+ {
+ return GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
+ }
+ else
+ {
+ return _resourceCounts.StorageBuffersCount++;
+ }
+ }
+
+ /// <inheritdoc/>
+ public int QueryBindingTexture(int index, bool isBuffer)
+ {
+ if (_context.Capabilities.Api == TargetApi.Vulkan)
+ {
+ if (isBuffer)
+ {
+ index += (int)_context.Capabilities.MaximumTexturesPerStage;
+ }
+
+ return GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
+ }
+ else
+ {
+ return _resourceCounts.TexturesCount++;
+ }
+ }
+
+ /// <inheritdoc/>
+ public int QueryBindingImage(int index, bool isBuffer)
+ {
+ if (_context.Capabilities.Api == TargetApi.Vulkan)
+ {
+ if (isBuffer)
+ {
+ index += (int)_context.Capabilities.MaximumImagesPerStage;
+ }
+
+ return GetBindingFromIndex(index, _context.Capabilities.MaximumImagesPerStage * 2, "Image");
+ }
+ else
+ {
+ return _resourceCounts.ImagesCount++;
+ }
+ }
+
+ private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName)
+ {
+ if ((uint)index >= maxPerStage)
+ {
+ Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
+ }
+
+ return GetStageIndex() * (int)maxPerStage + index;
+ }
+
+ private int GetStageIndex()
+ {
+ // This is just a simple remapping to ensure that most frequently used shader stages
+ // have the lowest binding numbers.
+ // This is useful because if we need to run on a system with a low limit on the bindings,
+ // then we can still get most games working as the most common shaders will have low binding numbers.
+ return _stageIndex switch
+ {
+ 4 => 1, // Fragment
+ 3 => 2, // Geometry
+ 1 => 3, // Tessellation control
+ 2 => 4, // Tessellation evaluation
+ _ => 0 // Vertex/Compute
+ };
+ }
+
+ /// <inheritdoc/>
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
- /// <summary>
- /// Queries host about the presence of the vector indexing bug.
- /// </summary>
- /// <returns>True if the bug is present on the host device used, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
- /// <summary>
- /// Queries host storage buffer alignment required.
- /// </summary>
- /// <returns>Host storage buffer alignment in bytes</returns>
+ /// <inheritdoc/>
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
- /// <summary>
- /// Queries host support for texture formats with BGRA component order (such as BGRA8).
- /// </summary>
- /// <returns>True if BGRA formats are supported, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
- /// <summary>
- /// Queries host support for fragment shader ordering critical sections on the shader code.
- /// </summary>
- /// <returns>True if fragment shader interlock is supported, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
- /// <summary>
- /// Queries host support for fragment shader ordering scoped critical sections on the shader code.
- /// </summary>
- /// <returns>True if fragment shader ordering is supported, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
- /// <summary>
- /// Queries host support for readable images without a explicit format declaration on the shader.
- /// </summary>
- /// <returns>True if formatted image load is supported, false otherwise</returns>
+ /// <inheritdoc/>
+ public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
+
+ /// <inheritdoc/>
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
- /// <summary>
- /// Queries host GPU non-constant texture offset support.
- /// </summary>
- /// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
- /// <summary>
- /// Queries host GPU shader ballot support.
- /// </summary>
- /// <returns>True if the GPU and driver supports shader ballot, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
- /// <summary>
- /// Queries host GPU texture shadow LOD support.
- /// </summary>
- /// <returns>True if the GPU and driver supports texture shadow LOD, false otherwise</returns>
+ /// <inheritdoc/>
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
index fae670ea..82252ced 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
@@ -1,5 +1,7 @@
+using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.Shader
{
@@ -26,19 +28,54 @@ namespace Ryujinx.Graphics.Gpu.Shader
public readonly TessMode TessellationMode;
/// <summary>
- /// Indicates whenever the viewport transform is disabled.
+ /// Indicates whether alpha-to-coverage is enabled.
+ /// </summary>
+ public readonly bool AlphaToCoverageEnable;
+
+ /// <summary>
+ /// Indicates whether alpha-to-coverage dithering is enabled.
+ /// </summary>
+ public readonly bool AlphaToCoverageDitherEnable;
+
+ /// <summary>
+ /// Indicates whether the viewport transform is disabled.
/// </summary>
public readonly bool ViewportTransformDisable;
/// <summary>
- /// Indicates whenever alpha-to-coverage is enabled.
+ /// Depth mode zero to one or minus one to one.
/// </summary>
- public readonly bool AlphaToCoverageEnable;
+ public readonly bool DepthMode;
/// <summary>
- /// Indicates whenever alpha-to-coverage dithering is enabled.
+ /// Indicates if the point size is set on the shader or is fixed.
/// </summary>
- public readonly bool AlphaToCoverageDitherEnable;
+ public readonly bool ProgramPointSizeEnable;
+
+ /// <summary>
+ /// Point size used if <see cref="ProgramPointSizeEnable" /> is false.
+ /// </summary>
+ public readonly float PointSize;
+
+ /// <summary>
+ /// Indicates whether alpha test is enabled.
+ /// </summary>
+ public readonly bool AlphaTestEnable;
+
+ /// <summary>
+ /// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded.
+ /// </summary>
+ public readonly CompareOp AlphaTestCompare;
+
+ /// <summary>
+ /// When alpha test is enabled, indicates the value to compare with the fragment output alpha.
+ /// </summary>
+ public readonly float AlphaTestReference;
+
+ /// <summary>
+ /// Type of the vertex attributes consumed by the shader.
+ /// </summary>
+ public Array32<AttributeType> AttributeTypes;
/// <summary>
/// Creates a new GPU graphics state.
@@ -46,23 +83,44 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="earlyZForce">Early Z force enable</param>
/// <param name="topology">Primitive topology</param>
/// <param name="tessellationMode">Tessellation mode</param>
- /// <param name="viewportTransformDisable">Indicates whenever the viewport transform is disabled</param>
- /// <param name="alphaToCoverageEnable">Indicates whenever alpha-to-coverage is enabled</param>
- /// <param name="alphaToCoverageDitherEnable">Indicates whenever alpha-to-coverage dithering is enabled</param>
+ /// <param name="alphaToCoverageEnable">Indicates whether alpha-to-coverage is enabled</param>
+ /// <param name="alphaToCoverageDitherEnable">Indicates whether alpha-to-coverage dithering is enabled</param>
+ /// <param name="viewportTransformDisable">Indicates whether the viewport transform is disabled</param>
+ /// <param name="depthMode">Depth mode zero to one or minus one to one</param>
+ /// <param name="programPointSizeEnable">Indicates if the point size is set on the shader or is fixed</param>
+ /// <param name="pointSize">Point size if not set from shader</param>
+ /// <param name="alphaTestEnable">Indicates whether alpha test is enabled</param>
+ /// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param>
+ /// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
+ /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
TessMode tessellationMode,
- bool viewportTransformDisable,
bool alphaToCoverageEnable,
- bool alphaToCoverageDitherEnable)
+ bool alphaToCoverageDitherEnable,
+ bool viewportTransformDisable,
+ bool depthMode,
+ bool programPointSizeEnable,
+ float pointSize,
+ bool alphaTestEnable,
+ CompareOp alphaTestCompare,
+ float alphaTestReference,
+ ref Array32<AttributeType> attributeTypes)
{
EarlyZForce = earlyZForce;
Topology = topology;
TessellationMode = tessellationMode;
- ViewportTransformDisable = viewportTransformDisable;
AlphaToCoverageEnable = alphaToCoverageEnable;
AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable;
+ ViewportTransformDisable = viewportTransformDisable;
+ DepthMode = depthMode;
+ ProgramPointSizeEnable = programPointSizeEnable;
+ PointSize = pointSize;
+ AlphaTestEnable = alphaTestEnable;
+ AlphaTestCompare = alphaTestCompare;
+ AlphaTestReference = alphaTestReference;
+ AttributeTypes = attributeTypes;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 0779bf2c..c998fe09 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -1,13 +1,17 @@
+using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.Shader.Cache;
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Shader
@@ -59,11 +63,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
public readonly CachedShaderProgram CachedProgram;
public readonly IProgram HostProgram;
+ public readonly byte[] BinaryCode;
- public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram)
+ public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram, byte[] binaryCode)
{
CachedProgram = cachedProgram;
HostProgram = hostProgram;
+ BinaryCode = binaryCode;
}
}
@@ -94,9 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_programsToSaveQueue = new Queue<ProgramToSave>();
- string diskCacheTitleId = GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
- ? CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId)
- : null;
+ string diskCacheTitleId = GetDiskCachePath();
_computeShaderCache = new ComputeShaderCacheHashTable();
_graphicsShaderCache = new ShaderCacheHashTable();
@@ -109,6 +113,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Gets the path where the disk cache for the current application is stored.
+ /// </summary>
+ private static string GetDiskCachePath()
+ {
+ return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
+ ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
+ : null;
+ }
+
+ /// <summary>
/// Processes the queue of shaders that must save their binaries to the disk cache.
/// </summary>
public void ProcessShaderCacheQueue()
@@ -123,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (result == ProgramLinkStatus.Success)
{
- _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.HostProgram.GetBinary());
+ _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.BinaryCode ?? programToSave.HostProgram.GetBinary());
}
_programsToSaveQueue.Dequeue();
@@ -143,16 +157,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (_diskCacheHostStorage.CacheEnabled)
{
- if (!_diskCacheHostStorage.CacheExists())
- {
- // 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...");
-
- int migrationCount = Migration.MigrateFromLegacyCache(_context, _diskCacheHostStorage);
-
- Logger.Info?.Print(LogClass.Gpu, $"Migrated {migrationCount} shaders.");
- }
-
ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader(
_context,
_graphicsShaderCache,
@@ -210,26 +214,75 @@ namespace Ryujinx.Graphics.Gpu.Shader
return cpShader;
}
- ShaderSpecializationState specState = new ShaderSpecializationState(computeState);
+ ShaderSpecializationState specState = new ShaderSpecializationState(ref computeState);
GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState);
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
- TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, gpuVa);
+ TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
- IProgram hostProgram = _context.Renderer.CreateProgram(new ShaderSource[] { CreateShaderSource(translatedShader.Program) }, new ShaderInfo(-1));
+ ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
+
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
_computeShaderCache.Add(cpShader);
- EnqueueProgramToSave(new ProgramToSave(cpShader, hostProgram));
+ EnqueueProgramToSave(cpShader, hostProgram, shaderSourcesArray);
_cpPrograms[gpuVa] = cpShader;
return cpShader;
}
/// <summary>
+ /// Updates the shader pipeline state based on the current GPU state.
+ /// </summary>
+ /// <param name="state">Current GPU 3D engine state</param>
+ /// <param name="pipeline">Shader pipeline state to be updated</param>
+ /// <param name="graphicsState">Current graphics state</param>
+ /// <param name="channel">Current GPU channel</param>
+ private void UpdatePipelineInfo(
+ ref ThreedClassState state,
+ ref ProgramPipelineState pipeline,
+ GpuChannelGraphicsState graphicsState,
+ GpuChannel channel)
+ {
+ channel.TextureManager.UpdateRenderTargets();
+
+ var rtControl = state.RtControl;
+ var msaaMode = state.RtMsaaMode;
+
+ pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
+
+ int count = rtControl.UnpackCount();
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ int rtIndex = rtControl.UnpackPermutationIndex(index);
+
+ var colorState = state.RtColorState[rtIndex];
+
+ if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
+ {
+ pipeline.AttachmentEnable[index] = false;
+ pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
+ }
+ else
+ {
+ pipeline.AttachmentEnable[index] = true;
+ pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
+ }
+ }
+
+ pipeline.DepthStencilEnable = state.RtDepthStencilEnable;
+ pipeline.DepthStencilFormat = pipeline.DepthStencilEnable ? state.RtDepthStencilState.Format.Convert().Format : Format.D24UnormS8Uint;
+
+ pipeline.VertexBufferCount = Constants.TotalVertexBuffers;
+ pipeline.Topology = graphicsState.Topology;
+ }
+
+ /// <summary>
/// Gets a graphics shader program from the shader cache.
/// This includes all the specified shader stages.
/// </summary>
@@ -237,6 +290,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This automatically translates, compiles and adds the code to the cache if not present.
/// </remarks>
/// <param name="state">GPU state</param>
+ /// <param name="pipeline">Pipeline state</param>
/// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">3D engine state</param>
@@ -244,6 +298,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Compiled graphics shader code</returns>
public CachedShaderProgram GetGraphicsShader(
ref ThreedClassState state,
+ ref ProgramPipelineState pipeline,
GpuChannel channel,
GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
@@ -262,7 +317,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state);
- ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, transformFeedbackDescriptors);
+ UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel);
+
+ ShaderSpecializationState specState = new ShaderSpecializationState(ref graphicsState, ref pipeline, transformFeedbackDescriptors);
GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors);
ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
@@ -270,6 +327,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
TranslatorContext nextStage = null;
+ TargetApi api = _context.Capabilities.Api;
+
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{
ulong gpuVa = addressesSpan[stageIndex + 1];
@@ -277,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (gpuVa != 0)
{
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex);
- TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, gpuVa);
+ TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
if (nextStage != null)
{
@@ -286,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (stageIndex == 0 && addresses.VertexA != 0)
{
- translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
+ translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
}
translatorContexts[stageIndex + 1] = currentStage;
@@ -336,13 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
}
+ ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
+
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
- IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap));
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
_graphicsShaderCache.Add(gpShaders);
- EnqueueProgramToSave(new ProgramToSave(gpShaders, hostProgram));
+ EnqueueProgramToSave(gpShaders, hostProgram, shaderSourcesArray);
_gpPrograms[addresses] = gpShaders;
return gpShaders;
@@ -355,7 +416,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Shader source</returns>
public static ShaderSource CreateShaderSource(ShaderProgram program)
{
- return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
+ return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
}
/// <summary>
@@ -364,11 +425,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <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)
+ /// <param name="program">Cached shader program</param>
+ /// <param name="hostProgram">Host program</param>
+ /// <param name="sources">Source for each shader stage</param>
+ private void EnqueueProgramToSave(CachedShaderProgram program, IProgram hostProgram, ShaderSource[] sources)
{
if (_diskCacheHostStorage.CacheEnabled)
{
+ byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(sources) : null;
+ ProgramToSave programToSave = new ProgramToSave(program, hostProgram, binaryCode);
+
_programsToSaveQueue.Enqueue(programToSave);
}
}
@@ -480,11 +546,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Decode the binary Maxwell shader code to a translator context.
/// </summary>
/// <param name="gpuAccessor">GPU state accessor</param>
+ /// <param name="api">Graphics API that will be used with the shader</param>
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
/// <returns>The generated translator context</returns>
- public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, ulong gpuVa)
+ public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
{
- var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute);
+ var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -495,12 +562,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
/// </remarks>
/// <param name="gpuAccessor">GPU state accessor</param>
+ /// <param name="api">Graphics API that will be used with the shader</param>
/// <param name="flags">Flags that controls shader translation</param>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <returns>The generated translator context</returns>
- public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TranslationFlags flags, ulong gpuVa)
+ public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
{
- var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
+ var options = CreateTranslationOptions(api, flags);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -596,6 +664,41 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Gets information about the bindings used by a shader program.
+ /// </summary>
+ /// <param name="info">Shader program information to get the information from</param>
+ /// <returns>Shader bindings</returns>
+ public static ShaderBindings GetBindings(ShaderProgramInfo info)
+ {
+ var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
+ var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
+ var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
+ var imageBindings = info.Images.Select(x => x.Binding).ToArray();
+
+ return new ShaderBindings(
+ uniformBufferBindings,
+ storageBufferBindings,
+ textureBindings,
+ imageBindings);
+ }
+
+ /// <summary>
+ /// Creates shader translation options with the requested graphics API and flags.
+ /// The shader language is choosen based on the current configuration and graphics API.
+ /// </summary>
+ /// <param name="api">Target graphics API</param>
+ /// <param name="flags">Translation flags</param>
+ /// <returns>Translation options</returns>
+ private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
+ {
+ TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
+ ? TargetLanguage.Spirv
+ : TargetLanguage.Glsl;
+
+ return new TranslationOptions(lang, api, flags);
+ }
+
+ /// <summary>
/// Disposes the shader cache, deleting all the cached shaders.
/// It's an error to use the shader cache after disposal.
/// </summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index 7e39c8a3..c927f33d 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -1,6 +1,7 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
+using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
using Ryujinx.Graphics.Shader;
using System;
@@ -19,6 +20,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private const uint TfbdMagic = (byte)'T' | ((byte)'F' << 8) | ((byte)'B' << 16) | ((byte)'D' << 24);
private const uint TexkMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'K' << 24);
private const uint TexsMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24);
+ private const uint PgpsMagic = (byte)'P' | ((byte)'G' << 8) | ((byte)'P' << 16) | ((byte)'S' << 24);
/// <summary>
/// Flags indicating GPU state that is used by the shader.
@@ -52,6 +54,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
public Array5<uint> ConstantBufferUse;
/// <summary>
+ /// Pipeline state captured at the time of shader use.
+ /// </summary>
+ public ProgramPipelineState? PipelineState;
+
+ /// <summary>
/// Transform feedback buffers active at the time the shader was compiled.
/// </summary>
public TransformFeedbackDescriptor[] TransformFeedbackDescriptors;
@@ -179,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Creates a new instance of the shader specialization state.
/// </summary>
/// <param name="state">Current compute engine state</param>
- public ShaderSpecializationState(GpuChannelComputeState state) : this()
+ public ShaderSpecializationState(ref GpuChannelComputeState state) : this()
{
ComputeState = state;
_compute = true;
@@ -190,7 +197,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
/// <param name="state">Current 3D engine state</param>
/// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
- public ShaderSpecializationState(GpuChannelGraphicsState state, TransformFeedbackDescriptor[] descriptors) : this()
+ private ShaderSpecializationState(ref GpuChannelGraphicsState state, TransformFeedbackDescriptor[] descriptors) : this()
{
GraphicsState = state;
_compute = false;
@@ -245,6 +252,34 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Creates a new instance of the shader specialization state.
+ /// </summary>
+ /// <param name="state">Current 3D engine state</param>
+ /// <param name="pipelineState">Current program pipeline state</param>
+ /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
+ public ShaderSpecializationState(
+ ref GpuChannelGraphicsState state,
+ ref ProgramPipelineState pipelineState,
+ TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors)
+ {
+ PipelineState = pipelineState;
+ }
+
+ /// <summary>
+ /// Creates a new instance of the shader specialization state.
+ /// </summary>
+ /// <param name="state">Current 3D engine state</param>
+ /// <param name="pipelineState">Current program pipeline state</param>
+ /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
+ public ShaderSpecializationState(
+ ref GpuChannelGraphicsState state,
+ ProgramPipelineState? pipelineState,
+ TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors)
+ {
+ PipelineState = pipelineState;
+ }
+
+ /// <summary>
/// Indicates that the shader accesses the early Z force state.
/// </summary>
public void RecordEarlyZForce()
@@ -463,6 +498,28 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
+ if (graphicsState.DepthMode != GraphicsState.DepthMode)
+ {
+ return false;
+ }
+
+ if (graphicsState.AlphaTestEnable != GraphicsState.AlphaTestEnable)
+ {
+ return false;
+ }
+
+ if (graphicsState.AlphaTestEnable &&
+ (graphicsState.AlphaTestCompare != GraphicsState.AlphaTestCompare ||
+ graphicsState.AlphaTestReference != GraphicsState.AlphaTestReference))
+ {
+ return false;
+ }
+
+ if (!graphicsState.AttributeTypes.ToSpan().SequenceEqual(GraphicsState.AttributeTypes.ToSpan()))
+ {
+ return false;
+ }
+
return Matches(channel, poolState, checkTextures, isCompute: false);
}
@@ -685,6 +742,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
constantBufferUsePerStageMask &= ~(1 << index);
}
+ bool hasPipelineState = false;
+
+ dataReader.Read(ref hasPipelineState);
+
+ if (hasPipelineState)
+ {
+ ProgramPipelineState pipelineState = default;
+ dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
+ specState.PipelineState = pipelineState;
+ }
+
if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
{
ushort tfCount = 0;
@@ -743,6 +811,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
constantBufferUsePerStageMask &= ~(1 << index);
}
+ bool hasPipelineState = PipelineState.HasValue;
+
+ dataWriter.Write(ref hasPipelineState);
+
+ if (hasPipelineState)
+ {
+ ProgramPipelineState pipelineState = PipelineState.Value;
+ dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic);
+ }
+
if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
{
ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;