aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Shader/Cache
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/Cache')
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs331
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs175
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs255
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs19
6 files changed, 279 insertions, 506 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
index 464436ea..d16afb65 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
@@ -2,11 +2,8 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using Ryujinx.Graphics.Shader;
-using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,70 +18,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
static class CacheHelper
{
/// <summary>
- /// Try to read the manifest header from a given file path.
- /// </summary>
- /// <param name="manifestPath">The path to the manifest file</param>
- /// <param name="header">The manifest header read</param>
- /// <returns>Return true if the manifest header was read</returns>
- public static bool TryReadManifestHeader(string manifestPath, out CacheManifestHeader header)
- {
- header = default;
-
- if (File.Exists(manifestPath))
- {
- Memory<byte> rawManifest = File.ReadAllBytes(manifestPath);
-
- if (MemoryMarshal.TryRead(rawManifest.Span, out header))
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Try to read the manifest from a given file path.
- /// </summary>
- /// <param name="manifestPath">The path to the manifest file</param>
- /// <param name="graphicsApi">The graphics api used by the cache</param>
- /// <param name="hashType">The hash type of the cache</param>
- /// <param name="header">The manifest header read</param>
- /// <param name="entries">The entries read from the cache manifest</param>
- /// <returns>Return true if the manifest was read</returns>
- public static bool TryReadManifestFile(string manifestPath, CacheGraphicsApi graphicsApi, CacheHashType hashType, out CacheManifestHeader header, out HashSet<Hash128> entries)
- {
- header = default;
- entries = new HashSet<Hash128>();
-
- if (File.Exists(manifestPath))
- {
- Memory<byte> rawManifest = File.ReadAllBytes(manifestPath);
-
- if (MemoryMarshal.TryRead(rawManifest.Span, out header))
- {
- Memory<byte> hashTableRaw = rawManifest.Slice(Unsafe.SizeOf<CacheManifestHeader>());
-
- bool isValid = header.IsValid(graphicsApi, hashType, hashTableRaw.Span);
-
- if (isValid)
- {
- ReadOnlySpan<Hash128> hashTable = MemoryMarshal.Cast<byte, Hash128>(hashTableRaw.Span);
-
- foreach (Hash128 hash in hashTable)
- {
- entries.Add(hash);
- }
- }
-
- return isValid;
- }
- }
-
- return false;
- }
-
- /// <summary>
/// Compute a cache manifest from runtime data.
/// </summary>
/// <param name="version">The version of the cache</param>
@@ -247,81 +180,22 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
}
/// <summary>
- /// Compute the guest program code for usage while dumping to disk or hash.
- /// </summary>
- /// <param name="cachedShaderEntries">The guest shader entries to use</param>
- /// <param name="tfd">The transform feedback descriptors</param>
- /// <param name="forHashCompute">Used to determine if the guest program code is generated for hashing</param>
- /// <returns>The guest program code for usage while dumping to disk or hash</returns>
- private static byte[] ComputeGuestProgramCode(ReadOnlySpan<GuestShaderCacheEntry> cachedShaderEntries, TransformFeedbackDescriptor[] tfd, bool forHashCompute = false)
- {
- using (MemoryStream stream = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(stream);
-
- foreach (GuestShaderCacheEntry cachedShaderEntry in cachedShaderEntries)
- {
- if (cachedShaderEntry != null)
- {
- // Code (and Code A if present)
- stream.Write(cachedShaderEntry.Code);
-
- if (forHashCompute)
- {
- // Guest GPU accessor header (only write this for hashes, already present in the header for dumps)
- writer.WriteStruct(cachedShaderEntry.Header.GpuAccessorHeader);
- }
-
- // Texture descriptors
- foreach (GuestTextureDescriptor textureDescriptor in cachedShaderEntry.TextureDescriptors.Values)
- {
- writer.WriteStruct(textureDescriptor);
- }
- }
- }
-
- // Transform feedback
- if (tfd != null)
- {
- foreach (TransformFeedbackDescriptor transform in tfd)
- {
- writer.WriteStruct(new GuestShaderCacheTransformFeedbackHeader(transform.BufferIndex, transform.Stride, transform.VaryingLocations.Length));
- writer.Write(transform.VaryingLocations);
- }
- }
-
- return stream.ToArray();
- }
- }
-
- /// <summary>
- /// Compute a guest hash from shader entries.
- /// </summary>
- /// <param name="cachedShaderEntries">The guest shader entries to use</param>
- /// <param name="tfd">The optional transform feedback descriptors</param>
- /// <returns>A guest hash from shader entries</returns>
- public static Hash128 ComputeGuestHashFromCache(ReadOnlySpan<GuestShaderCacheEntry> cachedShaderEntries, TransformFeedbackDescriptor[] tfd = null)
- {
- return XXHash128.ComputeHash(ComputeGuestProgramCode(cachedShaderEntries, tfd, true));
- }
-
- /// <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 TransformFeedbackDescriptor[] ReadTransformFeedbackInformation(ref ReadOnlySpan<byte> data, GuestShaderCacheHeader header)
+ public static TransformFeedbackDescriptorOld[] ReadTransformFeedbackInformation(ref ReadOnlySpan<byte> data, GuestShaderCacheHeader header)
{
if (header.TransformFeedbackCount != 0)
{
- TransformFeedbackDescriptor[] result = new TransformFeedbackDescriptor[header.TransformFeedbackCount];
+ TransformFeedbackDescriptorOld[] result = new TransformFeedbackDescriptorOld[header.TransformFeedbackCount];
for (int i = 0; i < result.Length; i++)
{
GuestShaderCacheTransformFeedbackHeader feedbackHeader = MemoryMarshal.Read<GuestShaderCacheTransformFeedbackHeader>(data);
- result[i] = new TransformFeedbackDescriptor(feedbackHeader.BufferIndex, feedbackHeader.Stride, data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>(), feedbackHeader.VaryingLocationsLength).ToArray());
+ result[i] = new TransformFeedbackDescriptorOld(feedbackHeader.BufferIndex, feedbackHeader.Stride, data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>(), feedbackHeader.VaryingLocationsLength).ToArray());
data = data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>() + feedbackHeader.VaryingLocationsLength);
}
@@ -333,205 +207,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
}
/// <summary>
- /// Builds gpu state flags using information from the given gpu accessor.
- /// </summary>
- /// <param name="gpuAccessor">The gpu accessor</param>
- /// <returns>The gpu state flags</returns>
- private static GuestGpuStateFlags GetGpuStateFlags(IGpuAccessor gpuAccessor)
- {
- GuestGpuStateFlags flags = 0;
-
- if (gpuAccessor.QueryEarlyZForce())
- {
- flags |= GuestGpuStateFlags.EarlyZForce;
- }
-
- return flags;
- }
-
- /// <summary>
- /// Packs the tessellation parameters from the gpu accessor.
- /// </summary>
- /// <param name="gpuAccessor">The gpu accessor</param>
- /// <returns>The packed tessellation parameters</returns>
- private static byte GetTessellationModePacked(IGpuAccessor gpuAccessor)
- {
- byte value;
-
- value = (byte)((int)gpuAccessor.QueryTessPatchType() & 3);
- value |= (byte)(((int)gpuAccessor.QueryTessSpacing() & 3) << 2);
-
- if (gpuAccessor.QueryTessCw())
- {
- value |= 0x10;
- }
-
- return value;
- }
-
- /// <summary>
- /// Create a new instance of <see cref="GuestGpuAccessorHeader"/> from an gpu accessor.
- /// </summary>
- /// <param name="gpuAccessor">The gpu accessor</param>
- /// <returns>A new instance of <see cref="GuestGpuAccessorHeader"/></returns>
- public static GuestGpuAccessorHeader CreateGuestGpuAccessorCache(IGpuAccessor gpuAccessor)
- {
- return new GuestGpuAccessorHeader
- {
- ComputeLocalSizeX = gpuAccessor.QueryComputeLocalSizeX(),
- ComputeLocalSizeY = gpuAccessor.QueryComputeLocalSizeY(),
- ComputeLocalSizeZ = gpuAccessor.QueryComputeLocalSizeZ(),
- ComputeLocalMemorySize = gpuAccessor.QueryComputeLocalMemorySize(),
- ComputeSharedMemorySize = gpuAccessor.QueryComputeSharedMemorySize(),
- PrimitiveTopology = gpuAccessor.QueryPrimitiveTopology(),
- TessellationModePacked = GetTessellationModePacked(gpuAccessor),
- StateFlags = GetGpuStateFlags(gpuAccessor)
- };
- }
-
- /// <summary>
- /// Create guest shader cache entries from the runtime contexts.
- /// </summary>
- /// <param name="channel">The GPU channel in use</param>
- /// <param name="shaderContexts">The runtime contexts</param>
- /// <returns>Guest shader cahe entries from the runtime contexts</returns>
- public static GuestShaderCacheEntry[] CreateShaderCacheEntries(GpuChannel channel, ReadOnlySpan<TranslatorContext> shaderContexts)
- {
- MemoryManager memoryManager = channel.MemoryManager;
-
- int startIndex = shaderContexts.Length > 1 ? 1 : 0;
-
- GuestShaderCacheEntry[] entries = new GuestShaderCacheEntry[shaderContexts.Length - startIndex];
-
- for (int i = startIndex; i < shaderContexts.Length; i++)
- {
- TranslatorContext context = shaderContexts[i];
-
- if (context == null)
- {
- continue;
- }
-
- GpuAccessor gpuAccessor = context.GpuAccessor as GpuAccessor;
-
- ulong cb1DataAddress;
- int cb1DataSize = gpuAccessor?.Cb1DataSize ?? 0;
-
- if (context.Stage == ShaderStage.Compute)
- {
- cb1DataAddress = channel.BufferManager.GetComputeUniformBufferAddress(1);
- }
- else
- {
- int stageIndex = context.Stage switch
- {
- ShaderStage.TessellationControl => 1,
- ShaderStage.TessellationEvaluation => 2,
- ShaderStage.Geometry => 3,
- ShaderStage.Fragment => 4,
- _ => 0
- };
-
- cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, 1);
- }
-
- int size = context.Size;
-
- TranslatorContext translatorContext2 = i == 1 ? shaderContexts[0] : null;
-
- int sizeA = translatorContext2 != null ? translatorContext2.Size : 0;
-
- byte[] code = new byte[size + cb1DataSize + sizeA];
-
- memoryManager.GetSpan(context.Address, size).CopyTo(code);
-
- if (cb1DataAddress != 0 && cb1DataSize != 0)
- {
- memoryManager.Physical.GetSpan(cb1DataAddress, cb1DataSize).CopyTo(code.AsSpan(size, cb1DataSize));
- }
-
- if (translatorContext2 != null)
- {
- memoryManager.GetSpan(translatorContext2.Address, sizeA).CopyTo(code.AsSpan(size + cb1DataSize, sizeA));
- }
-
- GuestGpuAccessorHeader gpuAccessorHeader = CreateGuestGpuAccessorCache(context.GpuAccessor);
-
- if (gpuAccessor != null)
- {
- gpuAccessorHeader.TextureDescriptorCount = context.TextureHandlesForCache.Count;
- }
-
- GuestShaderCacheEntryHeader header = new GuestShaderCacheEntryHeader(
- context.Stage,
- size + cb1DataSize,
- sizeA,
- cb1DataSize,
- gpuAccessorHeader);
-
- GuestShaderCacheEntry entry = new GuestShaderCacheEntry(header, code);
-
- if (gpuAccessor != null)
- {
- foreach (int textureHandle in context.TextureHandlesForCache)
- {
- GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle, -1)).ToCache();
-
- textureDescriptor.Handle = (uint)textureHandle;
-
- entry.TextureDescriptors.Add(textureHandle, textureDescriptor);
- }
- }
-
- entries[i - startIndex] = entry;
- }
-
- return entries;
- }
-
- /// <summary>
- /// Create a guest shader program.
- /// </summary>
- /// <param name="shaderCacheEntries">The entries composing the guest program dump</param>
- /// <param name="tfd">The transform feedback descriptors in use</param>
- /// <returns>The resulting guest shader program</returns>
- public static byte[] CreateGuestProgramDump(GuestShaderCacheEntry[] shaderCacheEntries, TransformFeedbackDescriptor[] tfd = null)
- {
- using (MemoryStream resultStream = new MemoryStream())
- {
- BinaryWriter resultStreamWriter = new BinaryWriter(resultStream);
-
- byte transformFeedbackCount = 0;
-
- if (tfd != null)
- {
- transformFeedbackCount = (byte)tfd.Length;
- }
-
- // Header
- resultStreamWriter.WriteStruct(new GuestShaderCacheHeader((byte)shaderCacheEntries.Length, transformFeedbackCount));
-
- // Write all entries header
- foreach (GuestShaderCacheEntry entry in shaderCacheEntries)
- {
- if (entry == null)
- {
- resultStreamWriter.WriteStruct(new GuestShaderCacheEntryHeader());
- }
- else
- {
- resultStreamWriter.WriteStruct(entry.Header);
- }
- }
-
- // Finally, write all program code and all transform feedback information.
- resultStreamWriter.Write(ComputeGuestProgramCode(shaderCacheEntries, tfd));
-
- return resultStream.ToArray();
- }
- }
-
- /// <summary>
/// Save temporary files not in archive.
/// </summary>
/// <param name="baseCacheDirectory">The base of the cache directory</param>
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs
index 3fc11e82..e67221e7 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs
@@ -47,8 +47,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(titleId);
- CacheMigration.Run(baseCacheDirectory, graphicsApi, hashType, shaderProvider);
-
_guestProgramCache = new CacheCollection(baseCacheDirectory, _hashType, CacheGraphicsApi.Guest, "", "program", GuestCacheVersion);
_hostProgramCache = new CacheCollection(baseCacheDirectory, _hashType, _graphicsApi, _shaderProvider, "host", shaderCodeGenVersion);
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs
deleted file mode 100644
index 5b4a1713..00000000
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheMigration.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using ICSharpCode.SharpZipLib.Zip;
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-namespace Ryujinx.Graphics.Gpu.Shader.Cache
-{
- /// <summary>
- /// Class handling shader cache migrations.
- /// </summary>
- static class CacheMigration
- {
- /// <summary>
- /// Check if the given cache version need to recompute its hash.
- /// </summary>
- /// <param name="version">The version in use</param>
- /// <param name="newVersion">The new version after migration</param>
- /// <returns>True if a hash recompute is needed</returns>
- public static bool NeedHashRecompute(ulong version, out ulong newVersion)
- {
- const ulong TargetBrokenVersion = 1717;
- const ulong TargetFixedVersion = 1759;
-
- newVersion = TargetFixedVersion;
-
- if (version == TargetBrokenVersion)
- {
- return true;
- }
-
- return false;
- }
-
- private class StreamZipEntryDataSource : IStaticDataSource
- {
- private readonly ZipFile Archive;
- private readonly ZipEntry Entry;
- public StreamZipEntryDataSource(ZipFile archive, ZipEntry entry)
- {
- Archive = archive;
- Entry = entry;
- }
-
- public Stream GetSource()
- {
- return Archive.GetInputStream(Entry);
- }
- }
-
- /// <summary>
- /// Move a file with the name of a given hash to another in the cache archive.
- /// </summary>
- /// <param name="archive">The archive in use</param>
- /// <param name="oldKey">The old key</param>
- /// <param name="newKey">The new key</param>
- private static void MoveEntry(ZipFile archive, Hash128 oldKey, Hash128 newKey)
- {
- ZipEntry oldGuestEntry = archive.GetEntry($"{oldKey}");
-
- if (oldGuestEntry != null)
- {
- archive.Add(new StreamZipEntryDataSource(archive, oldGuestEntry), $"{newKey}", CompressionMethod.Deflated);
- archive.Delete(oldGuestEntry);
- }
- }
-
- /// <summary>
- /// Recompute all the hashes of a given cache.
- /// </summary>
- /// <param name="guestBaseCacheDirectory">The guest cache directory path</param>
- /// <param name="hostBaseCacheDirectory">The host cache directory path</param>
- /// <param name="graphicsApi">The graphics api in use</param>
- /// <param name="hashType">The hash type in use</param>
- /// <param name="newVersion">The version to write in the host and guest manifest after migration</param>
- private static void RecomputeHashes(string guestBaseCacheDirectory, string hostBaseCacheDirectory, CacheGraphicsApi graphicsApi, CacheHashType hashType, ulong newVersion)
- {
- string guestManifestPath = CacheHelper.GetManifestPath(guestBaseCacheDirectory);
- string hostManifestPath = CacheHelper.GetManifestPath(hostBaseCacheDirectory);
-
- if (CacheHelper.TryReadManifestFile(guestManifestPath, CacheGraphicsApi.Guest, hashType, out _, out HashSet<Hash128> guestEntries))
- {
- CacheHelper.TryReadManifestFile(hostManifestPath, graphicsApi, hashType, out _, out HashSet<Hash128> hostEntries);
-
- Logger.Info?.Print(LogClass.Gpu, "Shader cache hashes need to be recomputed, performing migration...");
-
- string guestArchivePath = CacheHelper.GetArchivePath(guestBaseCacheDirectory);
- string hostArchivePath = CacheHelper.GetArchivePath(hostBaseCacheDirectory);
-
- ZipFile guestArchive = new ZipFile(File.Open(guestArchivePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None));
- ZipFile hostArchive = new ZipFile(File.Open(hostArchivePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None));
-
- CacheHelper.EnsureArchiveUpToDate(guestBaseCacheDirectory, guestArchive, guestEntries);
- CacheHelper.EnsureArchiveUpToDate(hostBaseCacheDirectory, hostArchive, hostEntries);
-
- int programIndex = 0;
-
- HashSet<Hash128> newEntries = new HashSet<Hash128>();
-
- foreach (Hash128 oldHash in guestEntries)
- {
- byte[] guestProgram = CacheHelper.ReadFromArchive(guestArchive, oldHash);
-
- Logger.Info?.Print(LogClass.Gpu, $"Migrating shader {oldHash} ({programIndex + 1} / {guestEntries.Count})");
-
- if (guestProgram != null)
- {
- ReadOnlySpan<byte> guestProgramReadOnlySpan = guestProgram;
-
- ReadOnlySpan<GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader);
-
- TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader);
-
- Hash128 newHash = CacheHelper.ComputeGuestHashFromCache(cachedShaderEntries, tfd);
-
- if (newHash != oldHash)
- {
- MoveEntry(guestArchive, oldHash, newHash);
- MoveEntry(hostArchive, oldHash, newHash);
- }
- else
- {
- Logger.Warning?.Print(LogClass.Gpu, $"Same hashes for shader {oldHash}");
- }
-
- newEntries.Add(newHash);
- }
-
- programIndex++;
- }
-
- byte[] newGuestManifestContent = CacheHelper.ComputeManifest(newVersion, CacheGraphicsApi.Guest, hashType, newEntries);
- byte[] newHostManifestContent = CacheHelper.ComputeManifest(newVersion, graphicsApi, hashType, newEntries);
-
- File.WriteAllBytes(guestManifestPath, newGuestManifestContent);
- File.WriteAllBytes(hostManifestPath, newHostManifestContent);
-
- guestArchive.CommitUpdate();
- hostArchive.CommitUpdate();
-
- guestArchive.Close();
- hostArchive.Close();
- }
- }
-
- /// <summary>
- /// Check and run cache migration if needed.
- /// </summary>
- /// <param name="baseCacheDirectory">The base path of the cache</param>
- /// <param name="graphicsApi">The graphics api in use</param>
- /// <param name="hashType">The hash type in use</param>
- /// <param name="shaderProvider">The shader provider name of the cache</param>
- public static void Run(string baseCacheDirectory, CacheGraphicsApi graphicsApi, CacheHashType hashType, string shaderProvider)
- {
- string guestBaseCacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, CacheGraphicsApi.Guest, "", "program");
- string hostBaseCacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, graphicsApi, shaderProvider, "host");
-
- string guestArchivePath = CacheHelper.GetArchivePath(guestBaseCacheDirectory);
- string hostArchivePath = CacheHelper.GetArchivePath(hostBaseCacheDirectory);
-
- bool isReadOnly = CacheHelper.IsArchiveReadOnly(guestArchivePath) || CacheHelper.IsArchiveReadOnly(hostArchivePath);
-
- if (!isReadOnly && CacheHelper.TryReadManifestHeader(CacheHelper.GetManifestPath(guestBaseCacheDirectory), out CacheManifestHeader header))
- {
- if (NeedHashRecompute(header.Version, out ulong newVersion))
- {
- RecomputeHashes(guestBaseCacheDirectory, hostBaseCacheDirectory, graphicsApi, hashType, newVersion);
- }
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
index 819c6bcc..fe79acb3 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs
@@ -96,6 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
SBuffers,
Textures,
Images,
+ default,
Header.UseFlags.HasFlag(UseFlags.InstanceId),
Header.UseFlags.HasFlag(UseFlags.RtLayer),
Header.ClipDistancesWritten,
@@ -160,7 +161,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
/// <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, ShaderCodeHolder[] codeHolders)
+ internal static byte[] Create(ReadOnlySpan<byte> programCode, CachedShaderStage[] codeHolders)
{
HostShaderCacheHeader header = new HostShaderCacheHeader((byte)codeHolders.Length, programCode.Length);
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs
new file mode 100644
index 00000000..27fac8f3
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs
@@ -0,0 +1,255 @@
+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);
+
+ 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
new file mode 100644
index 00000000..5e9c6711
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs
@@ -0,0 +1,19 @@
+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));
+ }
+ }
+}