57d3296ba4
* infra: Migrate to .NET 6 * Rollback version naming change * Workaround .NET 6 ZipArchive API issues * ci: Switch to VS 2022 for AppVeyor CI is now ready for .NET 6 * Suppress WebClient warning in DoUpdateWithMultipleThreads * Attempt to workaround System.Drawing.Common changes on 6.0.0 * Change keyboard rendering from System.Drawing to ImageSharp * Make the software keyboard renderer multithreaded * Bump ImageSharp version to 1.0.4 to fix a bug in Image.Load * Add fallback fonts to the keyboard renderer * Fix warnings * Address caian's comment * Clean up linux workaround as it's uneeded now * Update readme Co-authored-by: Caian Benedicto <caianbene@gmail.com>
175 lines
7.8 KiB
C#
175 lines
7.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|