aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs255
1 files changed, 255 insertions, 0 deletions
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