From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- .../Shader/DiskCache/BackgroundDiskCacheWriter.cs | 138 ---- .../Shader/DiskCache/BinarySerializer.cs | 216 ------ .../Shader/DiskCache/CompressionAlgorithm.cs | 18 - .../Shader/DiskCache/DiskCacheCommon.cs | 57 -- .../Shader/DiskCache/DiskCacheGpuAccessor.cs | 266 ------- .../Shader/DiskCache/DiskCacheGuestStorage.cs | 459 ----------- .../Shader/DiskCache/DiskCacheHostStorage.cs | 839 --------------------- .../Shader/DiskCache/DiskCacheLoadException.cs | 48 -- .../Shader/DiskCache/DiskCacheLoadResult.cs | 72 -- .../Shader/DiskCache/DiskCacheOutputStreams.cs | 57 -- .../Shader/DiskCache/GuestCodeAndCbData.cs | 29 - .../Shader/DiskCache/ParallelDiskCacheLoader.cs | 725 ------------------ .../Shader/DiskCache/ShaderBinarySerializer.cs | 66 -- 13 files changed, 2990 deletions(-) delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs delete mode 100644 Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs (limited to 'Ryujinx.Graphics.Gpu/Shader/DiskCache') diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs deleted file mode 100644 index 568fe968..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using System; -using System.IO; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Represents a background disk cache writer. - /// - class BackgroundDiskCacheWriter : IDisposable - { - /// - /// Possible operation to do on the . - /// - private enum CacheFileOperation - { - /// - /// Operation to add a shader to the cache. - /// - AddShader - } - - /// - /// Represents an operation to perform on the . - /// - private readonly struct CacheFileOperationTask - { - /// - /// The type of operation to perform. - /// - public readonly CacheFileOperation Type; - - /// - /// The data associated to this operation or null. - /// - public readonly object Data; - - public CacheFileOperationTask(CacheFileOperation type, object data) - { - Type = type; - Data = data; - } - } - - /// - /// Background shader cache write information. - /// - private readonly struct AddShaderData - { - /// - /// Cached shader program. - /// - public readonly CachedShaderProgram Program; - - /// - /// Binary host code. - /// - public readonly byte[] HostCode; - - /// - /// Creates a new background shader cache write information. - /// - /// Cached shader program - /// Binary host code - public AddShaderData(CachedShaderProgram program, byte[] hostCode) - { - Program = program; - HostCode = hostCode; - } - } - - private readonly GpuContext _context; - private readonly DiskCacheHostStorage _hostStorage; - private readonly AsyncWorkQueue _fileWriterWorkerQueue; - - /// - /// Creates a new background disk cache writer. - /// - /// GPU context - /// Disk cache host storage - public BackgroundDiskCacheWriter(GpuContext context, DiskCacheHostStorage hostStorage) - { - _context = context; - _hostStorage = hostStorage; - _fileWriterWorkerQueue = new AsyncWorkQueue(ProcessTask, "GPU.BackgroundDiskCacheWriter"); - } - - /// - /// Processes a shader cache background operation. - /// - /// Task to process - private void ProcessTask(CacheFileOperationTask task) - { - switch (task.Type) - { - case CacheFileOperation.AddShader: - AddShaderData data = (AddShaderData)task.Data; - try - { - _hostStorage.AddShader(_context, data.Program, data.HostCode); - } - catch (DiskCacheLoadException diskCacheLoadException) - { - Logger.Error?.Print(LogClass.Gpu, $"Error writing shader to disk cache. {diskCacheLoadException.Message}"); - } - catch (IOException ioException) - { - Logger.Error?.Print(LogClass.Gpu, $"Error writing shader to disk cache. {ioException.Message}"); - } - break; - } - } - - /// - /// Adds a shader program to be cached in the background. - /// - /// Shader program to cache - /// Host binary code of the program - public void AddShader(CachedShaderProgram program, byte[] hostCode) - { - _fileWriterWorkerQueue.Add(new CacheFileOperationTask(CacheFileOperation.AddShader, new AddShaderData(program, hostCode))); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _fileWriterWorkerQueue.Dispose(); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs deleted file mode 100644 index 50e37033..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Binary data serializer. - /// - struct BinarySerializer - { - private readonly Stream _stream; - private Stream _activeStream; - - /// - /// Creates a new binary serializer. - /// - /// Stream to read from or write into - public BinarySerializer(Stream stream) - { - _stream = stream; - _activeStream = stream; - } - - /// - /// Reads data from the stream. - /// - /// Type of the data - /// Data read - public void Read(ref T data) where T : unmanaged - { - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref data, 1)); - for (int offset = 0; offset < buffer.Length;) - { - offset += _activeStream.Read(buffer.Slice(offset)); - } - } - - /// - /// Tries to read data from the stream. - /// - /// Type of the data - /// Data read - /// True if the read was successful, false otherwise - public bool TryRead(ref T data) where T : unmanaged - { - // Length is unknown on compressed streams. - if (_activeStream == _stream) - { - int size = Unsafe.SizeOf(); - if (_activeStream.Length - _activeStream.Position < size) - { - return false; - } - } - - Read(ref data); - return true; - } - - /// - /// Reads data prefixed with a magic and size from the stream. - /// - /// Type of the data - /// Data read - /// Expected magic value, for validation - public void ReadWithMagicAndSize(ref T data, uint magic) where T : unmanaged - { - uint actualMagic = 0; - int size = 0; - Read(ref actualMagic); - Read(ref size); - - if (actualMagic != magic) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidMagic); - } - - // Structs are expected to expand but not shrink between versions. - if (size > Unsafe.SizeOf()) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidLength); - } - - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref data, 1)).Slice(0, size); - for (int offset = 0; offset < buffer.Length;) - { - offset += _activeStream.Read(buffer.Slice(offset)); - } - } - - /// - /// Writes data into the stream. - /// - /// Type of the data - /// Data to be written - public void Write(ref T data) where T : unmanaged - { - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref data, 1)); - _activeStream.Write(buffer); - } - - /// - /// Writes data prefixed with a magic and size into the stream. - /// - /// Type of the data - /// Data to write - /// Magic value to write - public void WriteWithMagicAndSize(ref T data, uint magic) where T : unmanaged - { - int size = Unsafe.SizeOf(); - Write(ref magic); - Write(ref size); - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref data, 1)); - _activeStream.Write(buffer); - } - - /// - /// Indicates that all data that will be read from the stream has been compressed. - /// - public void BeginCompression() - { - CompressionAlgorithm algorithm = CompressionAlgorithm.None; - Read(ref algorithm); - - if (algorithm == CompressionAlgorithm.Deflate) - { - _activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true); - } - } - - /// - /// Indicates that all data that will be written into the stream should be compressed. - /// - /// Compression algorithm that should be used - public void BeginCompression(CompressionAlgorithm algorithm) - { - Write(ref algorithm); - - if (algorithm == CompressionAlgorithm.Deflate) - { - _activeStream = new DeflateStream(_stream, CompressionLevel.SmallestSize, true); - } - } - - /// - /// Indicates the end of a compressed chunck. - /// - /// - /// Any data written after this will not be compressed unless is called again. - /// Any data read after this will be assumed to be uncompressed unless is called again. - /// - public void EndCompression() - { - if (_activeStream != _stream) - { - _activeStream.Dispose(); - _activeStream = _stream; - } - } - - /// - /// Reads compressed data from the stream. - /// - /// - /// must have the exact length of the uncompressed data, - /// otherwise decompression will fail. - /// - /// Stream to read from - /// Buffer to write the uncompressed data into - public static void ReadCompressed(Stream stream, Span data) - { - CompressionAlgorithm algorithm = (CompressionAlgorithm)stream.ReadByte(); - - switch (algorithm) - { - case CompressionAlgorithm.None: - stream.Read(data); - break; - case CompressionAlgorithm.Deflate: - stream = new DeflateStream(stream, CompressionMode.Decompress, true); - for (int offset = 0; offset < data.Length;) - { - offset += stream.Read(data.Slice(offset)); - } - stream.Dispose(); - break; - } - } - - /// - /// Compresses and writes the compressed data into the stream. - /// - /// Stream to write into - /// Data to compress - /// Compression algorithm to be used - public static void WriteCompressed(Stream stream, ReadOnlySpan data, CompressionAlgorithm algorithm) - { - stream.WriteByte((byte)algorithm); - - switch (algorithm) - { - case CompressionAlgorithm.None: - stream.Write(data); - break; - case CompressionAlgorithm.Deflate: - stream = new DeflateStream(stream, CompressionLevel.SmallestSize, true); - stream.Write(data); - stream.Dispose(); - break; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs deleted file mode 100644 index a46e1ef7..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/CompressionAlgorithm.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Algorithm used to compress the cache. - /// - enum CompressionAlgorithm : byte - { - /// - /// No compression, the data is stored as-is. - /// - None, - - /// - /// Deflate compression (RFC 1951). - /// - Deflate - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs deleted file mode 100644 index c8a9f7ff..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheCommon.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Ryujinx.Common.Logging; -using System.IO; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Common disk cache utility methods. - /// - static class DiskCacheCommon - { - /// - /// Opens a file for read or write. - /// - /// Base path of the file (should not include the file name) - /// Name of the file - /// Indicates if the file will be read or written - /// File stream - public static FileStream OpenFile(string basePath, string fileName, bool writable) - { - string fullPath = Path.Combine(basePath, fileName); - - FileMode mode; - FileAccess access; - - if (writable) - { - mode = FileMode.OpenOrCreate; - access = FileAccess.ReadWrite; - } - else - { - mode = FileMode.Open; - access = FileAccess.Read; - } - - try - { - return new FileStream(fullPath, mode, access, FileShare.Read); - } - catch (IOException ioException) - { - Logger.Error?.Print(LogClass.Gpu, $"Could not access file \"{fullPath}\". {ioException.Message}"); - - throw new DiskCacheLoadException(DiskCacheLoadResult.NoAccess); - } - } - - /// - /// Gets the compression algorithm that should be used when writing the disk cache. - /// - /// Compression algorithm - public static CompressionAlgorithm GetCompressionAlgorithm() - { - return CompressionAlgorithm.Deflate; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs deleted file mode 100644 index 17639ca1..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ /dev/null @@ -1,266 +0,0 @@ -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; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Represents a GPU state and memory accessor. - /// - class DiskCacheGpuAccessor : GpuAccessorBase, IGpuAccessor - { - private readonly ReadOnlyMemory _data; - private readonly ReadOnlyMemory _cb1Data; - private readonly ShaderSpecializationState _oldSpecState; - private readonly ShaderSpecializationState _newSpecState; - private readonly int _stageIndex; - private readonly bool _isVulkan; - private readonly ResourceCounts _resourceCounts; - - /// - /// Creates a new instance of the cached GPU state accessor for shader translation. - /// - /// GPU context - /// The data of the shader - /// The constant buffer 1 data of the shader - /// Shader specialization state of the cached shader - /// Shader specialization state of the recompiled shader - /// Shader stage index - public DiskCacheGpuAccessor( - GpuContext context, - ReadOnlyMemory data, - ReadOnlyMemory cb1Data, - ShaderSpecializationState oldSpecState, - ShaderSpecializationState newSpecState, - ResourceCounts counts, - int stageIndex) : base(context, counts, stageIndex) - { - _data = data; - _cb1Data = cb1Data; - _oldSpecState = oldSpecState; - _newSpecState = newSpecState; - _stageIndex = stageIndex; - _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; - _resourceCounts = counts; - } - - /// - public uint ConstantBuffer1Read(int offset) - { - if (offset + sizeof(uint) > _cb1Data.Length) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.InvalidCb1DataLength); - } - - return MemoryMarshal.Cast(_cb1Data.Span.Slice(offset))[0]; - } - - /// - public void Log(string message) - { - Logger.Warning?.Print(LogClass.Gpu, $"Shader translator: {message}"); - } - - /// - public ReadOnlySpan GetCode(ulong address, int minimumSize) - { - return MemoryMarshal.Cast(_data.Span.Slice((int)address)); - } - - /// - public bool QueryAlphaToCoverageDitherEnable() - { - return _oldSpecState.GraphicsState.AlphaToCoverageEnable && _oldSpecState.GraphicsState.AlphaToCoverageDitherEnable; - } - - /// - public AlphaTestOp QueryAlphaTestCompare() - { - if (!_isVulkan || !_oldSpecState.GraphicsState.AlphaTestEnable) - { - return AlphaTestOp.Always; - } - - 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 - }; - } - - /// - public float QueryAlphaTestReference() => _oldSpecState.GraphicsState.AlphaTestReference; - - /// - public AttributeType QueryAttributeType(int location) - { - return _oldSpecState.GraphicsState.AttributeTypes[location]; - } - - /// - public AttributeType QueryFragmentOutputType(int location) - { - return _oldSpecState.GraphicsState.FragmentOutputTypes[location]; - } - - /// - public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX; - - /// - public int QueryComputeLocalSizeY() => _oldSpecState.ComputeState.LocalSizeY; - - /// - public int QueryComputeLocalSizeZ() => _oldSpecState.ComputeState.LocalSizeZ; - - /// - public int QueryComputeLocalMemorySize() => _oldSpecState.ComputeState.LocalMemorySize; - - /// - public int QueryComputeSharedMemorySize() => _oldSpecState.ComputeState.SharedMemorySize; - - /// - public uint QueryConstantBufferUse() - { - _newSpecState.RecordConstantBufferUse(_stageIndex, _oldSpecState.ConstantBufferUse[_stageIndex]); - return _oldSpecState.ConstantBufferUse[_stageIndex]; - } - - /// - public bool QueryHasConstantBufferDrawParameters() - { - return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; - } - - /// - public bool QueryDualSourceBlendEnable() - { - return _oldSpecState.GraphicsState.DualSourceBlendEnable; - } - - /// - public InputTopology QueryPrimitiveTopology() - { - _newSpecState.RecordPrimitiveTopology(); - return ConvertToInputTopology(_oldSpecState.GraphicsState.Topology, _oldSpecState.GraphicsState.TessellationMode); - } - - /// - public bool QueryProgramPointSize() - { - return _oldSpecState.GraphicsState.ProgramPointSizeEnable; - } - - /// - public float QueryPointSize() - { - return _oldSpecState.GraphicsState.PointSize; - } - - /// - public bool QueryTessCw() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackCw(); - } - - /// - public TessPatchType QueryTessPatchType() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackPatchType(); - } - - /// - public TessSpacing QueryTessSpacing() - { - return _oldSpecState.GraphicsState.TessellationMode.UnpackSpacing(); - } - - /// - public TextureFormat QueryTextureFormat(int handle, int cbufSlot) - { - _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot); - (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot); - return ConvertToTextureFormat(format, formatSrgb); - } - - /// - public SamplerType QuerySamplerType(int handle, int cbufSlot) - { - _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot); - return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType(); - } - - /// - public bool QueryTextureCoordNormalized(int handle, int cbufSlot) - { - _newSpecState.RecordTextureCoordNormalized(_stageIndex, handle, cbufSlot); - return _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); - } - - /// - public bool QueryTransformDepthMinusOneToOne() - { - return _oldSpecState.GraphicsState.DepthMode; - } - - /// - public bool QueryTransformFeedbackEnabled() - { - return _oldSpecState.TransformFeedbackDescriptors != null; - } - - /// - public ReadOnlySpan QueryTransformFeedbackVaryingLocations(int bufferIndex) - { - return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].AsSpan(); - } - - /// - public int QueryTransformFeedbackStride(int bufferIndex) - { - return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].Stride; - } - - /// - public bool QueryEarlyZForce() - { - _newSpecState.RecordEarlyZForce(); - return _oldSpecState.GraphicsState.EarlyZForce; - } - - /// - public bool QueryHasUnalignedStorageBuffer() - { - return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer; - } - - /// - public bool QueryViewportTransformDisable() - { - return _oldSpecState.GraphicsState.ViewportTransformDisable; - } - - /// - public void RegisterTexture(int handle, int cbufSlot) - { - if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot)) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureDescriptor); - } - - (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot); - TextureTarget target = _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot); - bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); - _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized); - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs deleted file mode 100644 index 01034b49..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs +++ /dev/null @@ -1,459 +0,0 @@ -using Ryujinx.Common; -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// On-disk shader cache storage for guest code. - /// - class DiskCacheGuestStorage - { - private const uint TocMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'G' << 24); - - private const ushort VersionMajor = 1; - private const ushort VersionMinor = 1; - private const uint VersionPacked = ((uint)VersionMajor << 16) | VersionMinor; - - private const string TocFileName = "guest.toc"; - private const string DataFileName = "guest.data"; - - private readonly string _basePath; - - /// - /// TOC (Table of contents) file header. - /// - private struct TocHeader - { - /// - /// Magic value, for validation and identification purposes. - /// - public uint Magic; - - /// - /// File format version. - /// - public uint Version; - - /// - /// Header padding. - /// - public uint Padding; - - /// - /// Number of modifications to the file, also the shaders count. - /// - public uint ModificationsCount; - - /// - /// Reserved space, to be used in the future. Write as zero. - /// - public ulong Reserved; - - /// - /// Reserved space, to be used in the future. Write as zero. - /// - public ulong Reserved2; - } - - /// - /// TOC (Table of contents) file entry. - /// - private struct TocEntry - { - /// - /// Offset of the data on the data file. - /// - public uint Offset; - - /// - /// Code size. - /// - public uint CodeSize; - - /// - /// Constant buffer 1 data size. - /// - public uint Cb1DataSize; - - /// - /// Hash of the code and constant buffer data. - /// - public uint Hash; - } - - /// - /// TOC (Table of contents) memory cache entry. - /// - private struct TocMemoryEntry - { - /// - /// Offset of the data on the data file. - /// - public uint Offset; - - /// - /// Code size. - /// - public uint CodeSize; - - /// - /// Constant buffer 1 data size. - /// - public uint Cb1DataSize; - - /// - /// Index of the shader on the cache. - /// - public readonly int Index; - - /// - /// Creates a new TOC memory entry. - /// - /// Offset of the data on the data file - /// Code size - /// Constant buffer 1 data size - /// Index of the shader on the cache - public TocMemoryEntry(uint offset, uint codeSize, uint cb1DataSize, int index) - { - Offset = offset; - CodeSize = codeSize; - Cb1DataSize = cb1DataSize; - Index = index; - } - } - - private Dictionary> _toc; - private uint _tocModificationsCount; - - private (byte[], byte[])[] _cache; - - /// - /// Creates a new disk cache guest storage. - /// - /// Base path of the disk shader cache - public DiskCacheGuestStorage(string basePath) - { - _basePath = basePath; - } - - /// - /// Checks if the TOC (table of contents) file for the guest cache exists. - /// - /// True if the file exists, false otherwise - public bool TocFileExists() - { - return File.Exists(Path.Combine(_basePath, TocFileName)); - } - - /// - /// Checks if the data file for the guest cache exists. - /// - /// True if the file exists, false otherwise - public bool DataFileExists() - { - return File.Exists(Path.Combine(_basePath, DataFileName)); - } - - /// - /// Opens the guest cache TOC (table of contents) file. - /// - /// File stream - public Stream OpenTocFileStream() - { - return DiskCacheCommon.OpenFile(_basePath, TocFileName, writable: false); - } - - /// - /// Opens the guest cache data file. - /// - /// File stream - public Stream OpenDataFileStream() - { - return DiskCacheCommon.OpenFile(_basePath, DataFileName, writable: false); - } - - /// - /// Clear all content from the guest cache files. - /// - public void ClearCache() - { - using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, TocFileName, writable: true); - using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, DataFileName, writable: true); - - tocFileStream.SetLength(0); - dataFileStream.SetLength(0); - } - - /// - /// Loads the guest cache from file or memory cache. - /// - /// Guest TOC file stream - /// Guest data file stream - /// Guest shader index - /// Guest code and constant buffer 1 data - public GuestCodeAndCbData LoadShader(Stream tocFileStream, Stream dataFileStream, int index) - { - if (_cache == null || index >= _cache.Length) - { - _cache = new (byte[], byte[])[Math.Max(index + 1, GetShadersCountFromLength(tocFileStream.Length))]; - } - - (byte[] guestCode, byte[] cb1Data) = _cache[index]; - - if (guestCode == null || cb1Data == null) - { - BinarySerializer tocReader = new BinarySerializer(tocFileStream); - tocFileStream.Seek(Unsafe.SizeOf() + index * Unsafe.SizeOf(), SeekOrigin.Begin); - - TocEntry entry = new TocEntry(); - tocReader.Read(ref entry); - - guestCode = new byte[entry.CodeSize]; - cb1Data = new byte[entry.Cb1DataSize]; - - if (entry.Offset >= (ulong)dataFileStream.Length) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin); - dataFileStream.Read(cb1Data); - BinarySerializer.ReadCompressed(dataFileStream, guestCode); - - _cache[index] = (guestCode, cb1Data); - } - - return new GuestCodeAndCbData(guestCode, cb1Data); - } - - /// - /// Clears guest code memory cache, forcing future loads to be from file. - /// - public void ClearMemoryCache() - { - _cache = null; - } - - /// - /// Calculates the guest shaders count from the TOC file length. - /// - /// TOC file length - /// Shaders count - private static int GetShadersCountFromLength(long length) - { - return (int)((length - Unsafe.SizeOf()) / Unsafe.SizeOf()); - } - - /// - /// Adds a guest shader to the cache. - /// - /// - /// If the shader is already on the cache, the existing index will be returned and nothing will be written. - /// - /// Guest code - /// Constant buffer 1 data accessed by the code - /// Index of the shader on the cache - public int AddShader(ReadOnlySpan data, ReadOnlySpan cb1Data) - { - using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, TocFileName, writable: true); - using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, DataFileName, writable: true); - - TocHeader header = new TocHeader(); - - LoadOrCreateToc(tocFileStream, ref header); - - uint hash = CalcHash(data, cb1Data); - - if (_toc.TryGetValue(hash, out var list)) - { - foreach (var entry in list) - { - if (data.Length != entry.CodeSize || cb1Data.Length != entry.Cb1DataSize) - { - continue; - } - - dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin); - byte[] cachedCode = new byte[entry.CodeSize]; - byte[] cachedCb1Data = new byte[entry.Cb1DataSize]; - dataFileStream.Read(cachedCb1Data); - BinarySerializer.ReadCompressed(dataFileStream, cachedCode); - - if (data.SequenceEqual(cachedCode) && cb1Data.SequenceEqual(cachedCb1Data)) - { - return entry.Index; - } - } - } - - return WriteNewEntry(tocFileStream, dataFileStream, ref header, data, cb1Data, hash); - } - - /// - /// Loads the guest cache TOC file, or create a new one if not present. - /// - /// Guest TOC file stream - /// Set to the TOC file header - private void LoadOrCreateToc(Stream tocFileStream, ref TocHeader header) - { - BinarySerializer reader = new BinarySerializer(tocFileStream); - - if (!reader.TryRead(ref header) || header.Magic != TocMagic || header.Version != VersionPacked) - { - CreateToc(tocFileStream, ref header); - } - - if (_toc == null || header.ModificationsCount != _tocModificationsCount) - { - if (!LoadTocEntries(tocFileStream, ref reader)) - { - CreateToc(tocFileStream, ref header); - } - - _tocModificationsCount = header.ModificationsCount; - } - } - - /// - /// Creates a new guest cache TOC file. - /// - /// Guest TOC file stream - /// Set to the TOC header - private void CreateToc(Stream tocFileStream, ref TocHeader header) - { - BinarySerializer writer = new BinarySerializer(tocFileStream); - - header.Magic = TocMagic; - header.Version = VersionPacked; - header.Padding = 0; - header.ModificationsCount = 0; - header.Reserved = 0; - header.Reserved2 = 0; - - if (tocFileStream.Length > 0) - { - tocFileStream.Seek(0, SeekOrigin.Begin); - tocFileStream.SetLength(0); - } - - writer.Write(ref header); - } - - /// - /// Reads all the entries on the guest TOC file. - /// - /// Guest TOC file stream - /// TOC file reader - /// True if the operation was successful, false otherwise - private bool LoadTocEntries(Stream tocFileStream, ref BinarySerializer reader) - { - _toc = new Dictionary>(); - - TocEntry entry = new TocEntry(); - int index = 0; - - while (tocFileStream.Position < tocFileStream.Length) - { - if (!reader.TryRead(ref entry)) - { - return false; - } - - AddTocMemoryEntry(entry.Offset, entry.CodeSize, entry.Cb1DataSize, entry.Hash, index++); - } - - return true; - } - - /// - /// Writes a new guest code entry into the file. - /// - /// TOC file stream - /// Data file stream - /// TOC header, to be updated with the new count - /// Guest code - /// Constant buffer 1 data accessed by the guest code - /// Code and constant buffer data hash - /// Entry index - private int WriteNewEntry( - Stream tocFileStream, - Stream dataFileStream, - ref TocHeader header, - ReadOnlySpan data, - ReadOnlySpan cb1Data, - uint hash) - { - BinarySerializer tocWriter = new BinarySerializer(tocFileStream); - - dataFileStream.Seek(0, SeekOrigin.End); - uint dataOffset = checked((uint)dataFileStream.Position); - uint codeSize = (uint)data.Length; - uint cb1DataSize = (uint)cb1Data.Length; - dataFileStream.Write(cb1Data); - BinarySerializer.WriteCompressed(dataFileStream, data, DiskCacheCommon.GetCompressionAlgorithm()); - - _tocModificationsCount = ++header.ModificationsCount; - tocFileStream.Seek(0, SeekOrigin.Begin); - tocWriter.Write(ref header); - - TocEntry entry = new TocEntry() - { - Offset = dataOffset, - CodeSize = codeSize, - Cb1DataSize = cb1DataSize, - Hash = hash - }; - - tocFileStream.Seek(0, SeekOrigin.End); - int index = (int)((tocFileStream.Position - Unsafe.SizeOf()) / Unsafe.SizeOf()); - - tocWriter.Write(ref entry); - - AddTocMemoryEntry(dataOffset, codeSize, cb1DataSize, hash, index); - - return index; - } - - /// - /// Adds an entry to the memory TOC cache. This can be used to avoid reading the TOC file all the time. - /// - /// Offset of the code and constant buffer data in the data file - /// Code size - /// Constant buffer 1 data size - /// Code and constant buffer data hash - /// Index of the data on the cache - private void AddTocMemoryEntry(uint dataOffset, uint codeSize, uint cb1DataSize, uint hash, int index) - { - if (!_toc.TryGetValue(hash, out var list)) - { - _toc.Add(hash, list = new List()); - } - - list.Add(new TocMemoryEntry(dataOffset, codeSize, cb1DataSize, index)); - } - - /// - /// Calculates the hash for a data pair. - /// - /// Data 1 - /// Data 2 - /// Hash of both data - private static uint CalcHash(ReadOnlySpan data, ReadOnlySpan data2) - { - return CalcHash(data2) * 23 ^ CalcHash(data); - } - - /// - /// Calculates the hash for data. - /// - /// Data to be hashed - /// Hash of the data - private static uint CalcHash(ReadOnlySpan data) - { - return (uint)XXHash128.ComputeHash(data).Low; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs deleted file mode 100644 index b182f299..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ /dev/null @@ -1,839 +0,0 @@ -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.IO; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// On-disk shader cache storage for host code. - /// - class DiskCacheHostStorage - { - private const uint TocsMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'S' << 24); - private const uint TochMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'H' << 24); - private const uint ShdiMagic = (byte)'S' | ((byte)'H' << 8) | ((byte)'D' << 16) | ((byte)'I' << 24); - private const uint BufdMagic = (byte)'B' | ((byte)'U' << 8) | ((byte)'F' << 16) | ((byte)'D' << 24); - private const uint TexdMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'D' << 24); - - private const ushort FileFormatVersionMajor = 1; - private const ushort FileFormatVersionMinor = 2; - private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4735; - - private const string SharedTocFileName = "shared.toc"; - private const string SharedDataFileName = "shared.data"; - - private readonly string _basePath; - - public bool CacheEnabled => !string.IsNullOrEmpty(_basePath); - - /// - /// TOC (Table of contents) file header. - /// - private struct TocHeader - { - /// - /// Magic value, for validation and identification. - /// - public uint Magic; - - /// - /// File format version. - /// - public uint FormatVersion; - - /// - /// Generated shader code version. - /// - public uint CodeGenVersion; - - /// - /// Header padding. - /// - public uint Padding; - - /// - /// Timestamp of when the file was first created. - /// - public ulong Timestamp; - - /// - /// Reserved space, to be used in the future. Write as zero. - /// - public ulong Reserved; - } - - /// - /// Offset and size pair. - /// - private struct OffsetAndSize - { - /// - /// Offset. - /// - public ulong Offset; - - /// - /// Size of uncompressed data. - /// - public uint UncompressedSize; - - /// - /// Size of compressed data. - /// - public uint CompressedSize; - } - - /// - /// Per-stage data entry. - /// - private struct DataEntryPerStage - { - /// - /// Index of the guest code on the guest code cache TOC file. - /// - public int GuestCodeIndex; - } - - /// - /// Per-program data entry. - /// - private struct DataEntry - { - /// - /// Bit mask where each bit set is a used shader stage. Should be zero for compute shaders. - /// - public uint StagesBitMask; - } - - /// - /// Per-stage shader information, returned by the translator. - /// - private struct DataShaderInfo - { - /// - /// Total constant buffers used. - /// - public ushort CBuffersCount; - - /// - /// Total storage buffers used. - /// - public ushort SBuffersCount; - - /// - /// Total textures used. - /// - public ushort TexturesCount; - - /// - /// Total images used. - /// - public ushort ImagesCount; - - /// - /// Shader stage. - /// - public ShaderStage Stage; - - /// - /// Indicates if the shader accesses the Instance ID built-in variable. - /// - public bool UsesInstanceId; - - /// - /// Indicates if the shader modifies the Layer built-in variable. - /// - public bool UsesRtLayer; - - /// - /// Bit mask with the clip distances written on the vertex stage. - /// - public byte ClipDistancesWritten; - - /// - /// Bit mask of the render target components written by the fragment stage. - /// - public int FragmentOutputMap; - - /// - /// Indicates if the vertex shader accesses draw parameters. - /// - public bool UsesDrawParameters; - } - - private readonly DiskCacheGuestStorage _guestStorage; - - /// - /// Creates a disk cache host storage. - /// - /// Base path of the shader cache - public DiskCacheHostStorage(string basePath) - { - _basePath = basePath; - _guestStorage = new DiskCacheGuestStorage(basePath); - - if (CacheEnabled) - { - Directory.CreateDirectory(basePath); - } - } - - /// - /// Gets the total of host programs on the cache. - /// - /// Host programs count - public int GetProgramCount() - { - string tocFilePath = Path.Combine(_basePath, SharedTocFileName); - - if (!File.Exists(tocFilePath)) - { - return 0; - } - - return Math.Max((int)((new FileInfo(tocFilePath).Length - Unsafe.SizeOf()) / sizeof(ulong)), 0); - } - - /// - /// Guest the name of the host program cache file, with extension. - /// - /// GPU context - /// Name of the file, without extension - private static string GetHostFileName(GpuContext context) - { - string apiName = context.Capabilities.Api.ToString().ToLowerInvariant(); - string vendorName = RemoveInvalidCharacters(context.Capabilities.VendorName.ToLowerInvariant()); - return $"{apiName}_{vendorName}"; - } - - /// - /// Removes invalid path characters and spaces from a file name. - /// - /// File name - /// Filtered file name - private static string RemoveInvalidCharacters(string fileName) - { - int indexOfSpace = fileName.IndexOf(' '); - if (indexOfSpace >= 0) - { - fileName = fileName.Substring(0, indexOfSpace); - } - - return string.Concat(fileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries)); - } - - /// - /// Gets the name of the TOC host file. - /// - /// GPU context - /// File name - private static string GetHostTocFileName(GpuContext context) - { - return GetHostFileName(context) + ".toc"; - } - - /// - /// Gets the name of the data host file. - /// - /// GPU context - /// File name - private static string GetHostDataFileName(GpuContext context) - { - return GetHostFileName(context) + ".data"; - } - - /// - /// Checks if a disk cache exists for the current application. - /// - /// True if a disk cache exists, false otherwise - public bool CacheExists() - { - string tocFilePath = Path.Combine(_basePath, SharedTocFileName); - string dataFilePath = Path.Combine(_basePath, SharedDataFileName); - - if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath) || !_guestStorage.TocFileExists() || !_guestStorage.DataFileExists()) - { - return false; - } - - return true; - } - - /// - /// Loads all shaders from the cache. - /// - /// GPU context - /// Parallel disk cache loader - public void LoadShaders(GpuContext context, ParallelDiskCacheLoader loader) - { - if (!CacheExists()) - { - return; - } - - Stream hostTocFileStream = null; - Stream hostDataFileStream = null; - - try - { - using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: false); - using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: false); - - using var guestTocFileStream = _guestStorage.OpenTocFileStream(); - using var guestDataFileStream = _guestStorage.OpenDataFileStream(); - - BinarySerializer tocReader = new BinarySerializer(tocFileStream); - BinarySerializer dataReader = new BinarySerializer(dataFileStream); - - TocHeader header = new TocHeader(); - - if (!tocReader.TryRead(ref header) || header.Magic != TocsMagic) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - if (header.FormatVersion != FileFormatVersionPacked) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.IncompatibleVersion); - } - - bool loadHostCache = header.CodeGenVersion == CodeGenVersion; - - int programIndex = 0; - - DataEntry entry = new DataEntry(); - - while (tocFileStream.Position < tocFileStream.Length && loader.Active) - { - ulong dataOffset = 0; - tocReader.Read(ref dataOffset); - - if ((ulong)dataOffset >= (ulong)dataFileStream.Length) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - dataFileStream.Seek((long)dataOffset, SeekOrigin.Begin); - - dataReader.BeginCompression(); - dataReader.Read(ref entry); - uint stagesBitMask = entry.StagesBitMask; - - if ((stagesBitMask & ~0x3fu) != 0) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - bool isCompute = stagesBitMask == 0; - if (isCompute) - { - stagesBitMask = 1; - } - - GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[isCompute ? 1 : Constants.ShaderStages + 1]; - - DataEntryPerStage stageEntry = new DataEntryPerStage(); - - while (stagesBitMask != 0) - { - int stageIndex = BitOperations.TrailingZeroCount(stagesBitMask); - - dataReader.Read(ref stageEntry); - - guestShaders[stageIndex] = _guestStorage.LoadShader( - guestTocFileStream, - guestDataFileStream, - stageEntry.GuestCodeIndex); - - stagesBitMask &= ~(1u << stageIndex); - } - - ShaderSpecializationState specState = ShaderSpecializationState.Read(ref dataReader); - dataReader.EndCompression(); - - if (loadHostCache) - { - (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; - - 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); - - hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo); - } - else - { - hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo); - } - - CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders); - - loader.QueueHostProgram(program, hostCode, programIndex, isCompute); - } - else - { - loadHostCache = false; - } - } - - if (!loadHostCache) - { - loader.QueueGuestProgram(guestShaders, specState, programIndex, isCompute); - } - - loader.CheckCompilation(); - programIndex++; - } - } - finally - { - _guestStorage.ClearMemoryCache(); - - hostTocFileStream?.Dispose(); - hostDataFileStream?.Dispose(); - } - } - - /// - /// Reads the host code for a given shader, if existent. - /// - /// GPU context - /// Host TOC file stream, intialized if needed - /// Host data file stream, initialized if needed - /// Guest shader code for each active stage - /// Index of the program on the cache - /// Timestamp of the shared cache file. The host file must be newer than it - /// Host binary code, or null if not found - private (byte[], CachedShaderStage[]) ReadHostCode( - GpuContext context, - ref Stream tocFileStream, - ref Stream dataFileStream, - GuestCodeAndCbData?[] guestShaders, - int programIndex, - ulong expectedTimestamp) - { - if (tocFileStream == null && dataFileStream == null) - { - string tocFilePath = Path.Combine(_basePath, GetHostTocFileName(context)); - string dataFilePath = Path.Combine(_basePath, GetHostDataFileName(context)); - - if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath)) - { - 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() + programIndex * Unsafe.SizeOf(); - if (offset + Unsafe.SizeOf() > tocFileStream.Length) - { - return (null, null); - } - - if ((ulong)offset >= (ulong)dataFileStream.Length) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - tocFileStream.Seek(offset, SeekOrigin.Begin); - - BinarySerializer tocReader = new BinarySerializer(tocFileStream); - - OffsetAndSize offsetAndSize = new OffsetAndSize(); - tocReader.Read(ref offsetAndSize); - - if (offsetAndSize.Offset >= (ulong)dataFileStream.Length) - { - throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); - } - - dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin); - - byte[] hostCode = new byte[offsetAndSize.UncompressedSize]; - - BinarySerializer.ReadCompressed(dataFileStream, 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); - } - - /// - /// Gets output streams for the disk cache, for faster batch writing. - /// - /// The GPU context, used to determine the host disk cache - /// A collection of disk cache output streams - public DiskCacheOutputStreams GetOutputStreams(GpuContext context) - { - var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true); - var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true); - - var hostTocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true); - var hostDataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true); - - return new DiskCacheOutputStreams(tocFileStream, dataFileStream, hostTocFileStream, hostDataFileStream); - } - - /// - /// Adds a shader to the cache. - /// - /// GPU context - /// Cached program - /// Optional host binary code - /// Output streams to use - public void AddShader(GpuContext context, CachedShaderProgram program, ReadOnlySpan hostCode, DiskCacheOutputStreams streams = null) - { - uint stagesBitMask = 0; - - for (int index = 0; index < program.Shaders.Length; index++) - { - var shader = program.Shaders[index]; - if (shader == null || (shader.Info != null && shader.Info.Stage == ShaderStage.Compute)) - { - continue; - } - - stagesBitMask |= 1u << index; - } - - 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, timestamp); - } - - tocFileStream.Seek(0, SeekOrigin.End); - dataFileStream.Seek(0, SeekOrigin.End); - - BinarySerializer tocWriter = new BinarySerializer(tocFileStream); - BinarySerializer dataWriter = new BinarySerializer(dataFileStream); - - ulong dataOffset = (ulong)dataFileStream.Position; - tocWriter.Write(ref dataOffset); - - DataEntry entry = new DataEntry(); - - entry.StagesBitMask = stagesBitMask; - - dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm()); - dataWriter.Write(ref entry); - - DataEntryPerStage stageEntry = new DataEntryPerStage(); - - for (int index = 0; index < program.Shaders.Length; index++) - { - var shader = program.Shaders[index]; - if (shader == null) - { - continue; - } - - stageEntry.GuestCodeIndex = _guestStorage.AddShader(shader.Code, shader.Cb1Data); - - dataWriter.Write(ref stageEntry); - } - - program.SpecializationState.Write(ref dataWriter); - dataWriter.EndCompression(); - - if (streams == null) - { - tocFileStream.Dispose(); - dataFileStream.Dispose(); - } - - if (hostCode.IsEmpty) - { - return; - } - - WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); - } - - /// - /// Clears all content from the guest cache files. - /// - public void ClearGuestCache() - { - _guestStorage.ClearCache(); - } - - /// - /// Clears all content from the shared cache files. - /// - /// GPU context - public void ClearSharedCache() - { - using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true); - using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true); - - tocFileStream.SetLength(0); - dataFileStream.SetLength(0); - } - - /// - /// Deletes all content from the host cache files. - /// - /// GPU context - public void ClearHostCache(GpuContext context) - { - using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true); - using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true); - - tocFileStream.SetLength(0); - dataFileStream.SetLength(0); - } - - /// - /// Writes the host binary code on the host cache. - /// - /// GPU context - /// Host binary code - /// Shader stages to be added to the host cache - /// Output streams to use - /// File creation timestamp - private void WriteHostCode( - GpuContext context, - ReadOnlySpan 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); - - if (tocFileStream.Length == 0) - { - TocHeader header = new TocHeader(); - 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.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(); - dataFileStream.Dispose(); - } - } - - /// - /// Creates a TOC file for the host or shared cache. - /// - /// TOC file stream - /// Set to the TOC file header - /// Magic value to be written - /// Shader codegen version, only valid for the host file - /// File creation timestamp - private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp) - { - BinarySerializer writer = new BinarySerializer(tocFileStream); - - header.Magic = magic; - header.FormatVersion = FileFormatVersionPacked; - header.CodeGenVersion = codegenVersion; - header.Padding = 0; - header.Reserved = 0; - header.Timestamp = timestamp; - - if (tocFileStream.Length > 0) - { - tocFileStream.Seek(0, SeekOrigin.Begin); - tocFileStream.SetLength(0); - } - - writer.Write(ref header); - } - - /// - /// Reads the shader program info from the cache. - /// - /// Cache data reader - /// Shader program info - private static ShaderProgramInfo ReadShaderProgramInfo(ref BinarySerializer dataReader) - { - DataShaderInfo dataInfo = new DataShaderInfo(); - - dataReader.ReadWithMagicAndSize(ref dataInfo, ShdiMagic); - - BufferDescriptor[] cBuffers = new BufferDescriptor[dataInfo.CBuffersCount]; - BufferDescriptor[] sBuffers = new BufferDescriptor[dataInfo.SBuffersCount]; - TextureDescriptor[] textures = new TextureDescriptor[dataInfo.TexturesCount]; - TextureDescriptor[] images = new TextureDescriptor[dataInfo.ImagesCount]; - - for (int index = 0; index < dataInfo.CBuffersCount; index++) - { - dataReader.ReadWithMagicAndSize(ref cBuffers[index], BufdMagic); - } - - for (int index = 0; index < dataInfo.SBuffersCount; index++) - { - dataReader.ReadWithMagicAndSize(ref sBuffers[index], BufdMagic); - } - - for (int index = 0; index < dataInfo.TexturesCount; index++) - { - dataReader.ReadWithMagicAndSize(ref textures[index], TexdMagic); - } - - for (int index = 0; index < dataInfo.ImagesCount; index++) - { - dataReader.ReadWithMagicAndSize(ref images[index], TexdMagic); - } - - return new ShaderProgramInfo( - cBuffers, - sBuffers, - textures, - images, - ShaderIdentification.None, - 0, - dataInfo.Stage, - dataInfo.UsesInstanceId, - dataInfo.UsesDrawParameters, - dataInfo.UsesRtLayer, - dataInfo.ClipDistancesWritten, - dataInfo.FragmentOutputMap); - } - - /// - /// Writes the shader program info into the cache. - /// - /// Cache data writer - /// Program info - private static void WriteShaderProgramInfo(ref BinarySerializer dataWriter, ShaderProgramInfo info) - { - if (info == null) - { - return; - } - - DataShaderInfo dataInfo = new DataShaderInfo(); - - dataInfo.CBuffersCount = (ushort)info.CBuffers.Count; - dataInfo.SBuffersCount = (ushort)info.SBuffers.Count; - dataInfo.TexturesCount = (ushort)info.Textures.Count; - dataInfo.ImagesCount = (ushort)info.Images.Count; - dataInfo.Stage = info.Stage; - dataInfo.UsesInstanceId = info.UsesInstanceId; - dataInfo.UsesDrawParameters = info.UsesDrawParameters; - dataInfo.UsesRtLayer = info.UsesRtLayer; - dataInfo.ClipDistancesWritten = info.ClipDistancesWritten; - dataInfo.FragmentOutputMap = info.FragmentOutputMap; - - dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic); - - for (int index = 0; index < info.CBuffers.Count; index++) - { - var entry = info.CBuffers[index]; - dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic); - } - - for (int index = 0; index < info.SBuffers.Count; index++) - { - var entry = info.SBuffers[index]; - dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic); - } - - for (int index = 0; index < info.Textures.Count; index++) - { - var entry = info.Textures[index]; - dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic); - } - - for (int index = 0; index < info.Images.Count; index++) - { - var entry = info.Images[index]; - dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs deleted file mode 100644 index d6e23302..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadException.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Disk cache load exception. - /// - class DiskCacheLoadException : Exception - { - /// - /// Result of the cache load operation. - /// - public DiskCacheLoadResult Result { get; } - - /// - /// Creates a new instance of the disk cache load exception. - /// - public DiskCacheLoadException() - { - } - - /// - /// Creates a new instance of the disk cache load exception. - /// - /// Exception message - public DiskCacheLoadException(string message) : base(message) - { - } - - /// - /// Creates a new instance of the disk cache load exception. - /// - /// Exception message - /// Inner exception - public DiskCacheLoadException(string message, Exception inner) : base(message, inner) - { - } - - /// - /// Creates a new instance of the disk cache load exception. - /// - /// Result code - public DiskCacheLoadException(DiskCacheLoadResult result) : base(result.GetMessage()) - { - Result = result; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs deleted file mode 100644 index b3ffa4a7..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheLoadResult.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Result of a shader cache load operation. - /// - enum DiskCacheLoadResult - { - /// - /// No error. - /// - Success, - - /// - /// File can't be accessed. - /// - NoAccess, - - /// - /// The constant buffer 1 data length is too low for the translation of the guest shader. - /// - InvalidCb1DataLength, - - /// - /// The cache is missing the descriptor of a texture used by the shader. - /// - MissingTextureDescriptor, - - /// - /// File is corrupted. - /// - FileCorruptedGeneric, - - /// - /// File is corrupted, detected by magic value check. - /// - FileCorruptedInvalidMagic, - - /// - /// File is corrupted, detected by length check. - /// - FileCorruptedInvalidLength, - - /// - /// File might be valid, but is incompatible with the current emulator version. - /// - IncompatibleVersion - } - - static class DiskCacheLoadResultExtensions - { - /// - /// Gets an error message from a result code. - /// - /// Result code - /// Error message - public static string GetMessage(this DiskCacheLoadResult result) - { - return result switch - { - DiskCacheLoadResult.Success => "No error.", - DiskCacheLoadResult.NoAccess => "Could not access the cache file.", - DiskCacheLoadResult.InvalidCb1DataLength => "Constant buffer 1 data length is too low.", - DiskCacheLoadResult.MissingTextureDescriptor => "Texture descriptor missing from the cache file.", - DiskCacheLoadResult.FileCorruptedGeneric => "The cache file is corrupted.", - DiskCacheLoadResult.FileCorruptedInvalidMagic => "Magic check failed, the cache file is corrupted.", - DiskCacheLoadResult.FileCorruptedInvalidLength => "Length check failed, the cache file is corrupted.", - DiskCacheLoadResult.IncompatibleVersion => "The version of the disk cache is not compatible with this version of the emulator.", - _ => "Unknown error." - }; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs deleted file mode 100644 index 1e0df264..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheOutputStreams.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.IO; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Output streams for the disk shader cache. - /// - class DiskCacheOutputStreams : IDisposable - { - /// - /// Shared table of contents (TOC) file stream. - /// - public readonly FileStream TocFileStream; - - /// - /// Shared data file stream. - /// - public readonly FileStream DataFileStream; - - /// - /// Host table of contents (TOC) file stream. - /// - public readonly FileStream HostTocFileStream; - - /// - /// Host data file stream. - /// - public readonly FileStream HostDataFileStream; - - /// - /// Creates a new instance of a disk cache output stream container. - /// - /// Stream for the shared table of contents file - /// Stream for the shared data file - /// Stream for the host table of contents file - /// Stream for the host data file - public DiskCacheOutputStreams(FileStream tocFileStream, FileStream dataFileStream, FileStream hostTocFileStream, FileStream hostDataFileStream) - { - TocFileStream = tocFileStream; - DataFileStream = dataFileStream; - HostTocFileStream = hostTocFileStream; - HostDataFileStream = hostDataFileStream; - } - - /// - /// Disposes the output file streams. - /// - public void Dispose() - { - TocFileStream.Dispose(); - DataFileStream.Dispose(); - HostTocFileStream.Dispose(); - HostDataFileStream.Dispose(); - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs deleted file mode 100644 index 959d6e18..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - /// - /// Guest shader code and constant buffer data accessed by the shader. - /// - readonly struct GuestCodeAndCbData - { - /// - /// Maxwell binary shader code. - /// - public byte[] Code { get; } - - /// - /// Constant buffer 1 data accessed by the shader. - /// - public byte[] Cb1Data { get; } - - /// - /// Creates a new instance of the guest shader code and constant buffer data. - /// - /// Maxwell binary shader code - /// Constant buffer 1 data accessed by the shader - 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 deleted file mode 100644 index 77fb3ca4..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ /dev/null @@ -1,725 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using static Ryujinx.Graphics.Gpu.Shader.ShaderCache; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - class ParallelDiskCacheLoader - { - private const int ThreadCount = 8; - - private readonly GpuContext _context; - private readonly ShaderCacheHashTable _graphicsCache; - private readonly ComputeShaderCacheHashTable _computeCache; - private readonly DiskCacheHostStorage _hostStorage; - private readonly CancellationToken _cancellationToken; - private readonly Action _stateChangeCallback; - - /// - /// Indicates if the cache should be loaded. - /// - public bool Active => !_cancellationToken.IsCancellationRequested; - - private bool _needsHostRegen; - - /// - /// Number of shaders that failed to compile from the cache. - /// - public int ErrorCount { get; private set; } - - /// - /// Program validation entry. - /// - private readonly struct ProgramEntry - { - /// - /// Cached shader program. - /// - public readonly CachedShaderProgram CachedProgram; - - /// - /// Optional binary code. If not null, it is used instead of the backend host binary. - /// - public readonly byte[] BinaryCode; - - /// - /// Program index. - /// - public readonly int ProgramIndex; - - /// - /// Indicates if the program is a compute shader. - /// - public readonly bool IsCompute; - - /// - /// Indicates if the program is a host binary shader. - /// - public readonly bool IsBinary; - - /// - /// Creates a new program validation entry. - /// - /// Cached shader program - /// Optional binary code. If not null, it is used instead of the backend host binary - /// Program index - /// Indicates if the program is a compute shader - /// Indicates if the program is a host binary shader - public ProgramEntry( - CachedShaderProgram cachedProgram, - byte[] binaryCode, - int programIndex, - bool isCompute, - bool isBinary) - { - CachedProgram = cachedProgram; - BinaryCode = binaryCode; - ProgramIndex = programIndex; - IsCompute = isCompute; - IsBinary = isBinary; - } - } - - /// - /// Translated shader compilation entry. - /// - private readonly struct ProgramCompilation - { - /// - /// Translated shader stages. - /// - public readonly ShaderProgram[] TranslatedStages; - - /// - /// Cached shaders. - /// - public readonly CachedShaderStage[] Shaders; - - /// - /// Specialization state. - /// - public readonly ShaderSpecializationState SpecializationState; - - /// - /// Program index. - /// - public readonly int ProgramIndex; - - /// - /// Indicates if the program is a compute shader. - /// - public readonly bool IsCompute; - - /// - /// Creates a new translated shader compilation entry. - /// - /// Translated shader stages - /// Cached shaders - /// Specialization state - /// Program index - /// Indicates if the program is a compute shader - public ProgramCompilation( - ShaderProgram[] translatedStages, - CachedShaderStage[] shaders, - ShaderSpecializationState specState, - int programIndex, - bool isCompute) - { - TranslatedStages = translatedStages; - Shaders = shaders; - SpecializationState = specState; - ProgramIndex = programIndex; - IsCompute = isCompute; - } - } - - /// - /// Program translation entry. - /// - private readonly struct AsyncProgramTranslation - { - /// - /// Guest code for each active stage. - /// - public readonly GuestCodeAndCbData?[] GuestShaders; - - /// - /// Specialization state. - /// - public readonly ShaderSpecializationState SpecializationState; - - /// - /// Program index. - /// - public readonly int ProgramIndex; - - /// - /// Indicates if the program is a compute shader. - /// - public readonly bool IsCompute; - - /// - /// Creates a new program translation entry. - /// - /// Guest code for each active stage - /// Specialization state - /// Program index - /// Indicates if the program is a compute shader - public AsyncProgramTranslation( - GuestCodeAndCbData?[] guestShaders, - ShaderSpecializationState specState, - int programIndex, - bool isCompute) - { - GuestShaders = guestShaders; - SpecializationState = specState; - ProgramIndex = programIndex; - IsCompute = isCompute; - } - } - - private readonly Queue _validationQueue; - private readonly ConcurrentQueue _compilationQueue; - private readonly BlockingCollection _asyncTranslationQueue; - private readonly SortedList _programList; - - private int _backendParallelCompileThreads; - private int _compiledCount; - private int _totalCount; - - /// - /// Creates a new parallel disk cache loader. - /// - /// GPU context - /// Graphics shader cache - /// Compute shader cache - /// Disk cache host storage - /// Cancellation token - /// Function to be called when there is a state change, reporting state, compiled and total shaders count - public ParallelDiskCacheLoader( - GpuContext context, - ShaderCacheHashTable graphicsCache, - ComputeShaderCacheHashTable computeCache, - DiskCacheHostStorage hostStorage, - CancellationToken cancellationToken, - Action stateChangeCallback) - { - _context = context; - _graphicsCache = graphicsCache; - _computeCache = computeCache; - _hostStorage = hostStorage; - _cancellationToken = cancellationToken; - _stateChangeCallback = stateChangeCallback; - _validationQueue = new Queue(); - _compilationQueue = new ConcurrentQueue(); - _asyncTranslationQueue = new BlockingCollection(ThreadCount); - _programList = new SortedList(); - _backendParallelCompileThreads = Math.Min(Environment.ProcessorCount, 8); // Must be kept in sync with the backend code. - } - - /// - /// Loads all shaders from the cache. - /// - public void LoadShaders() - { - Thread[] workThreads = new Thread[ThreadCount]; - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index] = new Thread(ProcessAsyncQueue) - { - Name = $"GPU.AsyncTranslationThread.{index}" - }; - } - - int programCount = _hostStorage.GetProgramCount(); - - _compiledCount = 0; - _totalCount = programCount; - - _stateChangeCallback(ShaderCacheState.Start, 0, programCount); - - Logger.Info?.Print(LogClass.Gpu, $"Loading {programCount} shaders from the cache..."); - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index].Start(_cancellationToken); - } - - try - { - _hostStorage.LoadShaders(_context, this); - } - catch (DiskCacheLoadException diskCacheLoadException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error loading the shader cache. {diskCacheLoadException.Message}"); - - // If we can't even access the file, then we also can't rebuild. - if (diskCacheLoadException.Result != DiskCacheLoadResult.NoAccess) - { - _needsHostRegen = true; - } - } - catch (InvalidDataException invalidDataException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error decompressing the shader cache file. {invalidDataException.Message}"); - _needsHostRegen = true; - } - catch (IOException ioException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error reading the shader cache file. {ioException.Message}"); - _needsHostRegen = true; - } - - _asyncTranslationQueue.CompleteAdding(); - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index].Join(); - } - - CheckCompilationBlocking(); - - if (_needsHostRegen && Active) - { - // Rebuild both shared and host cache files. - // Rebuilding shared is required because the shader information returned by the translator - // might have changed, and so we have to reconstruct the file with the new information. - try - { - _hostStorage.ClearSharedCache(); - _hostStorage.ClearHostCache(_context); - - if (_programList.Count != 0) - { - Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders..."); - - using var streams = _hostStorage.GetOutputStreams(_context); - - foreach (var kv in _programList) - { - if (!Active) - { - break; - } - - (CachedShaderProgram program, byte[] binaryCode) = kv.Value; - _hostStorage.AddShader(_context, program, binaryCode, streams); - } - - Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully."); - } - else - { - _hostStorage.ClearGuestCache(); - - Logger.Info?.Print(LogClass.Gpu, "Shader cache deleted due to corruption."); - } - } - catch (DiskCacheLoadException diskCacheLoadException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error deleting the shader cache. {diskCacheLoadException.Message}"); - } - catch (IOException ioException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error deleting the shader cache file. {ioException.Message}"); - } - } - - Logger.Info?.Print(LogClass.Gpu, "Shader cache loaded."); - - _stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount); - } - - /// - /// Enqueues a host program for compilation. - /// - /// Cached program - /// Host binary code - /// Program index - /// Indicates if the program is a compute shader - public void QueueHostProgram(CachedShaderProgram cachedProgram, byte[] binaryCode, int programIndex, bool isCompute) - { - EnqueueForValidation(new ProgramEntry(cachedProgram, binaryCode, programIndex, isCompute, isBinary: true)); - } - - /// - /// Enqueues a guest program for compilation. - /// - /// Guest code for each active stage - /// Specialization state - /// Program index - /// Indicates if the program is a compute shader - public void QueueGuestProgram(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute) - { - try - { - AsyncProgramTranslation asyncTranslation = new AsyncProgramTranslation(guestShaders, specState, programIndex, isCompute); - _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); - } - catch (OperationCanceledException) - { - } - } - - /// - /// Check the state of programs that have already been compiled, - /// and add to the cache if the compilation was successful. - /// - public void CheckCompilation() - { - ProcessCompilationQueue(); - - // Process programs that already finished compiling. - // If not yet compiled, do nothing. This avoids blocking to wait for shader compilation. - while (_validationQueue.TryPeek(out ProgramEntry entry)) - { - ProgramLinkStatus result = entry.CachedProgram.HostProgram.CheckProgramLink(false); - - if (result != ProgramLinkStatus.Incomplete) - { - ProcessCompiledProgram(ref entry, result); - _validationQueue.Dequeue(); - } - else - { - break; - } - } - } - - /// - /// Waits until all programs finishes compiling, then adds the ones - /// with successful compilation to the cache. - /// - private void CheckCompilationBlocking() - { - ProcessCompilationQueue(); - - while (_validationQueue.TryDequeue(out ProgramEntry entry) && Active) - { - ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false); - } - } - - /// - /// Process a compiled program result. - /// - /// Compiled program entry - /// Compilation result - /// For failed host compilations, indicates if a guest compilation should be done asynchronously - private void ProcessCompiledProgram(ref ProgramEntry entry, ProgramLinkStatus result, bool asyncCompile = true) - { - if (result == ProgramLinkStatus.Success) - { - // Compilation successful, add to memory cache. - if (entry.IsCompute) - { - _computeCache.Add(entry.CachedProgram); - } - else - { - _graphicsCache.Add(entry.CachedProgram); - } - - if (!entry.IsBinary) - { - _needsHostRegen = true; - } - - // Fetch the binary code from the backend if it isn't already present. - byte[] binaryCode = entry.BinaryCode ?? entry.CachedProgram.HostProgram.GetBinary(); - - _programList.Add(entry.ProgramIndex, (entry.CachedProgram, binaryCode)); - SignalCompiled(); - } - else if (entry.IsBinary) - { - // If this is a host binary and compilation failed, - // 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(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute); - } - else - { - RecompileFromGuestCode(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute); - ProcessCompilationQueue(); - } - } - else - { - // Failed to compile from both host and guest binary. - ErrorCount++; - SignalCompiled(); - } - } - - /// - /// Processes the queue of translated guest programs that should be compiled on the host. - /// - private void ProcessCompilationQueue() - { - while (_compilationQueue.TryDequeue(out ProgramCompilation compilation) && Active) - { - ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - - int fragmentOutputMap = -1; - - for (int index = 0; index < compilation.TranslatedStages.Length; index++) - { - ShaderProgram shader = compilation.TranslatedStages[index]; - shaderSources[index] = CreateShaderSource(shader); - - if (shader.Info.Stage == ShaderStage.Fragment) - { - fragmentOutputMap = shader.Info.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); - - // Vulkan's binary code is the SPIR-V used for compilation, so it is ready immediately. Other APIs get this after compilation. - byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : null; - - EnqueueForValidation(new ProgramEntry(program, binaryCode, compilation.ProgramIndex, compilation.IsCompute, isBinary: false)); - } - } - - /// - /// Enqueues a program for validation, which will check if the program was compiled successfully. - /// - /// Program entry to be validated - private void EnqueueForValidation(ProgramEntry newEntry) - { - _validationQueue.Enqueue(newEntry); - - // Do not allow more than N shader compilation in-flight, where N is the maximum number of threads - // the driver will be using for parallel compilation. - // Submitting more seems to cause NVIDIA OpenGL driver to crash. - if (_validationQueue.Count >= _backendParallelCompileThreads && _validationQueue.TryDequeue(out ProgramEntry entry)) - { - ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false); - } - } - - /// - /// Processses the queue of programs that should be translated from guest code. - /// - /// Cancellation token - private void ProcessAsyncQueue(object state) - { - CancellationToken ct = (CancellationToken)state; - - try - { - foreach (AsyncProgramTranslation asyncCompilation in _asyncTranslationQueue.GetConsumingEnumerable(ct)) - { - RecompileFromGuestCode( - asyncCompilation.GuestShaders, - asyncCompilation.SpecializationState, - asyncCompilation.ProgramIndex, - asyncCompilation.IsCompute); - } - } - catch (OperationCanceledException) - { - } - } - - /// - /// Recompiles a program from guest code. - /// - /// Guest code for each active stage - /// Specialization state - /// Program index - /// Indicates if the program is a compute shader - private void RecompileFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute) - { - try - { - if (isCompute) - { - RecompileComputeFromGuestCode(guestShaders, specState, programIndex); - } - else - { - RecompileGraphicsFromGuestCode(guestShaders, specState, programIndex); - } - } - catch (Exception exception) - { - Logger.Error?.Print(LogClass.Gpu, $"Error translating guest shader. {exception.Message}"); - - ErrorCount++; - SignalCompiled(); - } - } - - /// - /// Recompiles a graphics program from guest code. - /// - /// Guest code for each active stage - /// Specialization state - /// Program index - private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) - { - 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--) - { - 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, api, DefaultFlags, 0); - - if (nextStage != null) - { - currentStage.SetNextStage(nextStage); - } - - if (stageIndex == 0 && guestShaders[0].HasValue) - { - 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, api, DefaultFlags | TranslationFlags.VertexA, 0); - } - - translatorContexts[stageIndex + 1] = currentStage; - nextStage = currentStage; - } - } - - if (!_context.Capabilities.SupportsGeometryShader) - { - ShaderCache.TryRemoveGeometryStage(translatorContexts); - } - - CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; - List translatedStages = new List(); - - TranslatorContext previousStage = null; - - for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) - { - TranslatorContext currentStage = translatorContexts[stageIndex + 1]; - - if (currentStage != null) - { - ShaderProgram program; - - byte[] guestCode = guestShaders[stageIndex + 1].Value.Code; - byte[] cb1Data = guestShaders[stageIndex + 1].Value.Cb1Data; - - if (stageIndex == 0 && guestShaders[0].HasValue) - { - program = currentStage.Translate(translatorContexts[0]); - - 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); - } - else - { - program = currentStage.Translate(); - - shaders[stageIndex + 1] = new CachedShaderStage(program.Info, guestCode, cb1Data); - } - - if (program != null) - { - translatedStages.Add(program); - } - - previousStage = currentStage; - } - else if ( - previousStage != null && - previousStage.LayerOutputWritten && - stageIndex == 3 && - !_context.Capabilities.SupportsLayerVertexTessellation) - { - translatedStages.Add(previousStage.GenerateGeometryPassthrough()); - } - } - - _compilationQueue.Enqueue(new ProgramCompilation(translatedStages.ToArray(), shaders, newSpecState, programIndex, isCompute: false)); - } - - /// - /// Recompiles a compute program from guest code. - /// - /// Guest code for each active stage - /// Specialization state - /// Program index - private void RecompileComputeFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) - { - GuestCodeAndCbData shader = guestShaders[0].Value; - ResourceCounts counts = new ResourceCounts(); - ShaderSpecializationState newSpecState = new ShaderSpecializationState(ref specState.ComputeState); - DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0); - - TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0); - - ShaderProgram program = translatorContext.Translate(); - - CachedShaderStage[] shaders = new[] { new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data) }; - - _compilationQueue.Enqueue(new ProgramCompilation(new[] { program }, shaders, newSpecState, programIndex, isCompute: true)); - } - - /// - /// Signals that compilation of a program has been finished successfully, - /// or that it failed and guest recompilation has also been attempted. - /// - private void SignalCompiled() - { - _stateChangeCallback(ShaderCacheState.Loading, ++_compiledCount, _totalCount); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs deleted file mode 100644 index 77e52667..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -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 = MemoryStreamManager.Shared.GetStream(); - - output.Write(sources.Length); - - foreach (ShaderSource source in sources) - { - output.Write((int)source.Stage); - output.Write(source.BinaryCode.Length); - output.Write(source.BinaryCode); - } - - return output.ToArray(); - } - - public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code) - { - using MemoryStream input = new MemoryStream(code); - using BinaryReader reader = new BinaryReader(input); - - List output = new List(); - - int count = reader.ReadInt32(); - - for (int i = 0; i < count; i++) - { - ShaderStage stage = (ShaderStage)reader.ReadInt32(); - int binaryCodeLength = reader.ReadInt32(); - byte[] binaryCode = reader.ReadBytes(binaryCodeLength); - - output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv)); - } - - return output.ToArray(); - } - - private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage) - { - for (int i = 0; i < stages.Length; i++) - { - CachedShaderStage currentStage = stages[i]; - - if (currentStage?.Info != null && currentStage.Info.Stage == stage) - { - return ShaderCache.GetBindings(currentStage.Info); - } - } - - return new ShaderBindings(Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); - } - } -} \ No newline at end of file -- cgit v1.2.3