diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/Cache')
18 files changed, 0 insertions, 2216 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs deleted file mode 100644 index a98531f6..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheCollection.cs +++ /dev/null @@ -1,617 +0,0 @@ -using ICSharpCode.SharpZipLib.Zip; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Gpu.Shader.Cache.Definition; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache -{ - /// <summary> - /// Represent a cache collection handling one shader cache. - /// </summary> - class CacheCollection : IDisposable - { - /// <summary> - /// Possible operation to do on the <see cref="_fileWriterWorkerQueue"/>. - /// </summary> - private enum CacheFileOperation - { - /// <summary> - /// Save a new entry in the temp cache. - /// </summary> - SaveTempEntry, - - /// <summary> - /// Save the hash manifest. - /// </summary> - SaveManifest, - - /// <summary> - /// Remove entries from the hash manifest and save it. - /// </summary> - RemoveManifestEntries, - - /// <summary> - /// Remove entries from the hash manifest and save it, and also deletes the temporary file. - /// </summary> - RemoveManifestEntryAndTempFile, - - /// <summary> - /// Flush temporary cache to archive. - /// </summary> - FlushToArchive, - - /// <summary> - /// Signal when hitting this point. This is useful to know if all previous operations were performed. - /// </summary> - Synchronize - } - - /// <summary> - /// Represent an operation to perform on the <see cref="_fileWriterWorkerQueue"/>. - /// </summary> - private class CacheFileOperationTask - { - /// <summary> - /// The type of operation to perform. - /// </summary> - public CacheFileOperation Type; - - /// <summary> - /// The data associated to this operation or null. - /// </summary> - public object Data; - } - - /// <summary> - /// Data associated to the <see cref="CacheFileOperation.SaveTempEntry"/> operation. - /// </summary> - private class CacheFileSaveEntryTaskData - { - /// <summary> - /// The key of the entry to cache. - /// </summary> - public Hash128 Key; - - /// <summary> - /// The value of the entry to cache. - /// </summary> - public byte[] Value; - } - - /// <summary> - /// The directory of the shader cache. - /// </summary> - private readonly string _cacheDirectory; - - /// <summary> - /// The version of the cache. - /// </summary> - private readonly ulong _version; - - /// <summary> - /// The hash type of the cache. - /// </summary> - private readonly CacheHashType _hashType; - - /// <summary> - /// The graphics API of the cache. - /// </summary> - private readonly CacheGraphicsApi _graphicsApi; - - /// <summary> - /// The table of all the hash registered in the cache. - /// </summary> - private HashSet<Hash128> _hashTable; - - /// <summary> - /// The queue of operations to be performed by the file writer worker. - /// </summary> - private AsyncWorkQueue<CacheFileOperationTask> _fileWriterWorkerQueue; - - /// <summary> - /// Main storage of the cache collection. - /// </summary> - private ZipFile _cacheArchive; - - /// <summary> - /// Indicates if the cache collection supports modification. - /// </summary> - public bool IsReadOnly { get; } - - /// <summary> - /// Immutable copy of the hash table. - /// </summary> - public ReadOnlySpan<Hash128> HashTable => _hashTable.ToArray(); - - /// <summary> - /// Get the temp path to the cache data directory. - /// </summary> - /// <returns>The temp path to the cache data directory</returns> - private string GetCacheTempDataPath() => CacheHelper.GetCacheTempDataPath(_cacheDirectory); - - /// <summary> - /// The path to the cache archive file. - /// </summary> - /// <returns>The path to the cache archive file</returns> - private string GetArchivePath() => CacheHelper.GetArchivePath(_cacheDirectory); - - /// <summary> - /// The path to the cache manifest file. - /// </summary> - /// <returns>The path to the cache manifest file</returns> - private string GetManifestPath() => CacheHelper.GetManifestPath(_cacheDirectory); - - /// <summary> - /// Create a new temp path to the given cached file via its hash. - /// </summary> - /// <param name="key">The hash of the cached data</param> - /// <returns>New path to the given cached file</returns> - private string GenCacheTempFilePath(Hash128 key) => CacheHelper.GenCacheTempFilePath(_cacheDirectory, key); - - /// <summary> - /// Create a new cache collection. - /// </summary> - /// <param name="baseCacheDirectory">The directory of the shader cache</param> - /// <param name="hashType">The hash type of the shader cache</param> - /// <param name="graphicsApi">The graphics api of the shader cache</param> - /// <param name="shaderProvider">The shader provider name of the shader cache</param> - /// <param name="cacheName">The name of the cache</param> - /// <param name="version">The version of the cache</param> - public CacheCollection(string baseCacheDirectory, CacheHashType hashType, CacheGraphicsApi graphicsApi, string shaderProvider, string cacheName, ulong version) - { - if (hashType != CacheHashType.XxHash128) - { - throw new NotImplementedException($"{hashType}"); - } - - _cacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, graphicsApi, shaderProvider, cacheName); - _graphicsApi = graphicsApi; - _hashType = hashType; - _version = version; - _hashTable = new HashSet<Hash128>(); - IsReadOnly = CacheHelper.IsArchiveReadOnly(GetArchivePath()); - - Load(); - - _fileWriterWorkerQueue = new AsyncWorkQueue<CacheFileOperationTask>(HandleCacheTask, $"CacheCollection.Worker.{cacheName}"); - } - - /// <summary> - /// Load the cache manifest file and recreate it if invalid. - /// </summary> - private void Load() - { - bool isValid = false; - - if (Directory.Exists(_cacheDirectory)) - { - string manifestPath = GetManifestPath(); - - if (File.Exists(manifestPath)) - { - Memory<byte> rawManifest = File.ReadAllBytes(manifestPath); - - if (MemoryMarshal.TryRead(rawManifest.Span, out CacheManifestHeader manifestHeader)) - { - Memory<byte> hashTableRaw = rawManifest.Slice(Unsafe.SizeOf<CacheManifestHeader>()); - - isValid = manifestHeader.IsValid(_graphicsApi, _hashType, hashTableRaw.Span) && _version == manifestHeader.Version; - - if (isValid) - { - ReadOnlySpan<Hash128> hashTable = MemoryMarshal.Cast<byte, Hash128>(hashTableRaw.Span); - - foreach (Hash128 hash in hashTable) - { - _hashTable.Add(hash); - } - } - } - } - } - - if (!isValid) - { - Logger.Warning?.Print(LogClass.Gpu, $"Shader collection \"{_cacheDirectory}\" got invalidated, cache will need to be rebuilt."); - - if (Directory.Exists(_cacheDirectory)) - { - Directory.Delete(_cacheDirectory, true); - } - - Directory.CreateDirectory(_cacheDirectory); - - SaveManifest(); - } - - FlushToArchive(); - } - - /// <summary> - /// Queue a task to remove entries from the hash manifest. - /// </summary> - /// <param name="entries">Entries to remove from the manifest</param> - public void RemoveManifestEntriesAsync(HashSet<Hash128> entries) - { - if (IsReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, "Trying to remove manifest entries on a read-only cache, ignoring."); - - return; - } - - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.RemoveManifestEntries, - Data = entries - }); - } - - /// <summary> - /// Remove given entries from the manifest. - /// </summary> - /// <param name="entries">Entries to remove from the manifest</param> - private void RemoveManifestEntries(HashSet<Hash128> entries) - { - lock (_hashTable) - { - foreach (Hash128 entry in entries) - { - _hashTable.Remove(entry); - } - - SaveManifest(); - } - } - - /// <summary> - /// Remove given entry from the manifest and delete the temporary file. - /// </summary> - /// <param name="entry">Entry to remove from the manifest</param> - private void RemoveManifestEntryAndTempFile(Hash128 entry) - { - lock (_hashTable) - { - _hashTable.Remove(entry); - SaveManifest(); - } - - File.Delete(GenCacheTempFilePath(entry)); - } - - /// <summary> - /// Queue a task to flush temporary files to the archive on the worker. - /// </summary> - public void FlushToArchiveAsync() - { - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.FlushToArchive - }); - } - - /// <summary> - /// Wait for all tasks before this given point to be done. - /// </summary> - public void Synchronize() - { - using (ManualResetEvent evnt = new ManualResetEvent(false)) - { - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.Synchronize, - Data = evnt - }); - - evnt.WaitOne(); - } - } - - /// <summary> - /// Flush temporary files to the archive. - /// </summary> - /// <remarks>This dispose <see cref="_cacheArchive"/> if not null and reinstantiate it.</remarks> - private void FlushToArchive() - { - EnsureArchiveUpToDate(); - - // Open the zip in readonly to avoid anyone modifying/corrupting it during normal operations. - _cacheArchive = new ZipFile(File.OpenRead(GetArchivePath())); - } - - /// <summary> - /// Save temporary files not in archive. - /// </summary> - /// <remarks>This dispose <see cref="_cacheArchive"/> if not null.</remarks> - public void EnsureArchiveUpToDate() - { - // First close previous opened instance if found. - if (_cacheArchive != null) - { - _cacheArchive.Close(); - } - - string archivePath = GetArchivePath(); - - if (IsReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, $"Cache collection archive in read-only, archiving task skipped."); - - return; - } - - if (CacheHelper.IsArchiveReadOnly(archivePath)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Cache collection archive in use, archiving task skipped."); - - return; - } - - if (!File.Exists(archivePath)) - { - using (ZipFile newZip = ZipFile.Create(archivePath)) - { - // Workaround for SharpZipLib issue #395 - newZip.BeginUpdate(); - newZip.CommitUpdate(); - } - } - - // Open the zip in read/write. - _cacheArchive = new ZipFile(File.Open(archivePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); - - Logger.Info?.Print(LogClass.Gpu, $"Updating cache collection archive {archivePath}..."); - - // Update the content of the zip. - lock (_hashTable) - { - CacheHelper.EnsureArchiveUpToDate(_cacheDirectory, _cacheArchive, _hashTable); - - // Close the instance to force a flush. - _cacheArchive.Close(); - _cacheArchive = null; - - string cacheTempDataPath = GetCacheTempDataPath(); - - // Create the cache data path if missing. - if (!Directory.Exists(cacheTempDataPath)) - { - Directory.CreateDirectory(cacheTempDataPath); - } - } - - Logger.Info?.Print(LogClass.Gpu, $"Updated cache collection archive {archivePath}."); - } - - /// <summary> - /// Save the manifest file. - /// </summary> - private void SaveManifest() - { - byte[] data; - - lock (_hashTable) - { - data = CacheHelper.ComputeManifest(_version, _graphicsApi, _hashType, _hashTable); - } - - File.WriteAllBytes(GetManifestPath(), data); - } - - /// <summary> - /// Get a cached file with the given hash. - /// </summary> - /// <param name="keyHash">The given hash</param> - /// <returns>The cached file if present or null</returns> - public byte[] GetValueRaw(ref Hash128 keyHash) - { - return GetValueRawFromArchive(ref keyHash) ?? GetValueRawFromFile(ref keyHash); - } - - /// <summary> - /// Get a cached file with the given hash that is present in the archive. - /// </summary> - /// <param name="keyHash">The given hash</param> - /// <returns>The cached file if present or null</returns> - private byte[] GetValueRawFromArchive(ref Hash128 keyHash) - { - bool found; - - lock (_hashTable) - { - found = _hashTable.Contains(keyHash); - } - - if (found) - { - return CacheHelper.ReadFromArchive(_cacheArchive, keyHash); - } - - return null; - } - - /// <summary> - /// Get a cached file with the given hash that is not present in the archive. - /// </summary> - /// <param name="keyHash">The given hash</param> - /// <returns>The cached file if present or null</returns> - private byte[] GetValueRawFromFile(ref Hash128 keyHash) - { - bool found; - - lock (_hashTable) - { - found = _hashTable.Contains(keyHash); - } - - if (found) - { - return CacheHelper.ReadFromFile(GetCacheTempDataPath(), keyHash); - } - - return null; - } - - private void HandleCacheTask(CacheFileOperationTask task) - { - switch (task.Type) - { - case CacheFileOperation.SaveTempEntry: - SaveTempEntry((CacheFileSaveEntryTaskData)task.Data); - break; - case CacheFileOperation.SaveManifest: - SaveManifest(); - break; - case CacheFileOperation.RemoveManifestEntries: - RemoveManifestEntries((HashSet<Hash128>)task.Data); - break; - case CacheFileOperation.RemoveManifestEntryAndTempFile: - RemoveManifestEntryAndTempFile((Hash128)task.Data); - break; - case CacheFileOperation.FlushToArchive: - FlushToArchive(); - break; - case CacheFileOperation.Synchronize: - ((ManualResetEvent)task.Data).Set(); - break; - default: - throw new NotImplementedException($"{task.Type}"); - } - - } - - /// <summary> - /// Save a new entry in the temp cache. - /// </summary> - /// <param name="entry">The entry to save in the temp cache</param> - private void SaveTempEntry(CacheFileSaveEntryTaskData entry) - { - string tempPath = GenCacheTempFilePath(entry.Key); - - File.WriteAllBytes(tempPath, entry.Value); - } - - /// <summary> - /// Add a new value in the cache with a given hash. - /// </summary> - /// <param name="keyHash">The hash to use for the value in the cache</param> - /// <param name="value">The value to cache</param> - public void AddValue(ref Hash128 keyHash, byte[] value) - { - if (IsReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, $"Trying to add {keyHash} on a read-only cache, ignoring."); - - return; - } - - Debug.Assert(value != null); - - bool isAlreadyPresent; - - lock (_hashTable) - { - isAlreadyPresent = !_hashTable.Add(keyHash); - } - - if (isAlreadyPresent) - { - // NOTE: Used for debug - File.WriteAllBytes(GenCacheTempFilePath(new Hash128()), value); - - throw new InvalidOperationException($"Cache collision found on {GenCacheTempFilePath(keyHash)}"); - } - - // Queue file change operations - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.SaveTempEntry, - Data = new CacheFileSaveEntryTaskData - { - Key = keyHash, - Value = value - } - }); - - // Save the manifest changes - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.SaveManifest, - }); - } - - /// <summary> - /// Replace a value at the given hash in the cache. - /// </summary> - /// <param name="keyHash">The hash to use for the value in the cache</param> - /// <param name="value">The value to cache</param> - public void ReplaceValue(ref Hash128 keyHash, byte[] value) - { - if (IsReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, $"Trying to replace {keyHash} on a read-only cache, ignoring."); - - return; - } - - Debug.Assert(value != null); - - // Only queue file change operations - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.SaveTempEntry, - Data = new CacheFileSaveEntryTaskData - { - Key = keyHash, - Value = value - } - }); - } - - /// <summary> - /// Removes a value at the given hash from the cache. - /// </summary> - /// <param name="keyHash">The hash of the value in the cache</param> - public void RemoveValue(ref Hash128 keyHash) - { - if (IsReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, $"Trying to remove {keyHash} on a read-only cache, ignoring."); - - return; - } - - // Only queue file change operations - _fileWriterWorkerQueue.Add(new CacheFileOperationTask - { - Type = CacheFileOperation.RemoveManifestEntryAndTempFile, - Data = keyHash - }); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - // Make sure all operations on _fileWriterWorkerQueue are done. - Synchronize(); - - _fileWriterWorkerQueue.Dispose(); - EnsureArchiveUpToDate(); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs deleted file mode 100644 index d16afb65..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs +++ /dev/null @@ -1,273 +0,0 @@ -using ICSharpCode.SharpZipLib.Zip; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Gpu.Shader.Cache.Definition; -using Ryujinx.Graphics.Shader; -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache -{ - /// <summary> - /// Helper to manipulate the disk shader cache. - /// </summary> - static class CacheHelper - { - /// <summary> - /// Compute a cache manifest from runtime data. - /// </summary> - /// <param name="version">The version of the cache</param> - /// <param name="graphicsApi">The graphics api used by the cache</param> - /// <param name="hashType">The hash type of the cache</param> - /// <param name="entries">The entries in the cache</param> - /// <returns>The cache manifest from runtime data</returns> - public static byte[] ComputeManifest(ulong version, CacheGraphicsApi graphicsApi, CacheHashType hashType, HashSet<Hash128> entries) - { - if (hashType != CacheHashType.XxHash128) - { - throw new NotImplementedException($"{hashType}"); - } - - CacheManifestHeader manifestHeader = new CacheManifestHeader(version, graphicsApi, hashType); - - byte[] data = new byte[Unsafe.SizeOf<CacheManifestHeader>() + entries.Count * Unsafe.SizeOf<Hash128>()]; - - // CacheManifestHeader has the same size as a Hash128. - Span<Hash128> dataSpan = MemoryMarshal.Cast<byte, Hash128>(data.AsSpan()).Slice(1); - - int i = 0; - - foreach (Hash128 hash in entries) - { - dataSpan[i++] = hash; - } - - manifestHeader.UpdateChecksum(data.AsSpan(Unsafe.SizeOf<CacheManifestHeader>())); - - MemoryMarshal.Write(data, ref manifestHeader); - - return data; - } - - /// <summary> - /// Get the base directory of the shader cache for a given title id. - /// </summary> - /// <param name="titleId">The title id of the target application</param> - /// <returns>The base directory of the shader cache for a given title id</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetBaseCacheDirectory(string titleId) => Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "shader"); - - /// <summary> - /// Get the temp path to the cache data directory. - /// </summary> - /// <param name="cacheDirectory">The cache directory</param> - /// <returns>The temp path to the cache data directory</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetCacheTempDataPath(string cacheDirectory) => Path.Combine(cacheDirectory, "temp"); - - /// <summary> - /// The path to the cache archive file. - /// </summary> - /// <param name="cacheDirectory">The cache directory</param> - /// <returns>The path to the cache archive file</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetArchivePath(string cacheDirectory) => Path.Combine(cacheDirectory, "cache.zip"); - - /// <summary> - /// The path to the cache manifest file. - /// </summary> - /// <param name="cacheDirectory">The cache directory</param> - /// <returns>The path to the cache manifest file</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetManifestPath(string cacheDirectory) => Path.Combine(cacheDirectory, "cache.info"); - - /// <summary> - /// Create a new temp path to the given cached file via its hash. - /// </summary> - /// <param name="cacheDirectory">The cache directory</param> - /// <param name="key">The hash of the cached data</param> - /// <returns>New path to the given cached file</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GenCacheTempFilePath(string cacheDirectory, Hash128 key) => Path.Combine(GetCacheTempDataPath(cacheDirectory), key.ToString()); - - /// <summary> - /// Generate the path to the cache directory. - /// </summary> - /// <param name="baseCacheDirectory">The base of the cache directory</param> - /// <param name="graphicsApi">The graphics api in use</param> - /// <param name="shaderProvider">The name of the shader provider in use</param> - /// <param name="cacheName">The name of the cache</param> - /// <returns>The path to the cache directory</returns> - public static string GenerateCachePath(string baseCacheDirectory, CacheGraphicsApi graphicsApi, string shaderProvider, string cacheName) - { - string graphicsApiName = graphicsApi switch - { - CacheGraphicsApi.OpenGL => "opengl", - CacheGraphicsApi.OpenGLES => "opengles", - CacheGraphicsApi.Vulkan => "vulkan", - CacheGraphicsApi.DirectX => "directx", - CacheGraphicsApi.Metal => "metal", - CacheGraphicsApi.Guest => "guest", - _ => throw new NotImplementedException(graphicsApi.ToString()), - }; - - return Path.Combine(baseCacheDirectory, graphicsApiName, shaderProvider, cacheName); - } - - /// <summary> - /// Read a cached file with the given hash that is present in the archive. - /// </summary> - /// <param name="archive">The archive in use</param> - /// <param name="entry">The given hash</param> - /// <returns>The cached file if present or null</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] ReadFromArchive(ZipFile archive, Hash128 entry) - { - if (archive != null) - { - ZipEntry archiveEntry = archive.GetEntry($"{entry}"); - - if (archiveEntry != null) - { - try - { - byte[] result = new byte[archiveEntry.Size]; - - using (Stream archiveStream = archive.GetInputStream(archiveEntry)) - { - archiveStream.Read(result); - - return result; - } - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Gpu, $"Cannot load cache file {entry} from archive"); - Logger.Error?.Print(LogClass.Gpu, e.ToString()); - } - } - } - - return null; - } - - /// <summary> - /// Read a cached file with the given hash that is not present in the archive. - /// </summary> - /// <param name="cacheDirectory">The cache directory</param> - /// <param name="entry">The given hash</param> - /// <returns>The cached file if present or null</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] ReadFromFile(string cacheDirectory, Hash128 entry) - { - string cacheTempFilePath = GenCacheTempFilePath(cacheDirectory, entry); - - try - { - return File.ReadAllBytes(cacheTempFilePath); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Gpu, $"Cannot load cache file at {cacheTempFilePath}"); - Logger.Error?.Print(LogClass.Gpu, e.ToString()); - } - - return null; - } - - /// <summary> - /// Read transform feedback descriptors from guest. - /// </summary> - /// <param name="data">The raw guest transform feedback descriptors</param> - /// <param name="header">The guest shader program header</param> - /// <returns>The transform feedback descriptors read from guest</returns> - public static TransformFeedbackDescriptorOld[] ReadTransformFeedbackInformation(ref ReadOnlySpan<byte> data, GuestShaderCacheHeader header) - { - if (header.TransformFeedbackCount != 0) - { - TransformFeedbackDescriptorOld[] result = new TransformFeedbackDescriptorOld[header.TransformFeedbackCount]; - - for (int i = 0; i < result.Length; i++) - { - GuestShaderCacheTransformFeedbackHeader feedbackHeader = MemoryMarshal.Read<GuestShaderCacheTransformFeedbackHeader>(data); - - result[i] = new TransformFeedbackDescriptorOld(feedbackHeader.BufferIndex, feedbackHeader.Stride, data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>(), feedbackHeader.VaryingLocationsLength).ToArray()); - - data = data.Slice(Unsafe.SizeOf<GuestShaderCacheTransformFeedbackHeader>() + feedbackHeader.VaryingLocationsLength); - } - - return result; - } - - return null; - } - - /// <summary> - /// Save temporary files not in archive. - /// </summary> - /// <param name="baseCacheDirectory">The base of the cache directory</param> - /// <param name="archive">The archive to use</param> - /// <param name="entries">The entries in the cache</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureArchiveUpToDate(string baseCacheDirectory, ZipFile archive, HashSet<Hash128> entries) - { - List<string> filesToDelete = new List<string>(); - - archive.BeginUpdate(); - - foreach (Hash128 hash in entries) - { - string cacheTempFilePath = GenCacheTempFilePath(baseCacheDirectory, hash); - - if (File.Exists(cacheTempFilePath)) - { - string cacheHash = $"{hash}"; - - ZipEntry entry = archive.GetEntry(cacheHash); - - if (entry != null) - { - archive.Delete(entry); - } - - // We enforce deflate compression here to avoid possible incompatibilities on older version of Ryujinx that use System.IO.Compression. - archive.Add(new StaticDiskDataSource(cacheTempFilePath), cacheHash, CompressionMethod.Deflated); - filesToDelete.Add(cacheTempFilePath); - } - } - - archive.CommitUpdate(); - - foreach (string filePath in filesToDelete) - { - File.Delete(filePath); - } - } - - public static bool IsArchiveReadOnly(string archivePath) - { - FileInfo info = new FileInfo(archivePath); - - if (!info.Exists) - { - return false; - } - - try - { - using (FileStream stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.None)) - { - return false; - } - } - catch (IOException) - { - return true; - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs deleted file mode 100644 index e67221e7..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheManager.cs +++ /dev/null @@ -1,168 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Gpu.Shader.Cache.Definition; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache -{ - /// <summary> - /// Global Manager of the shader cache. - /// </summary> - class CacheManager : IDisposable - { - private CacheGraphicsApi _graphicsApi; - private CacheHashType _hashType; - private string _shaderProvider; - - /// <summary> - /// Cache storing raw Maxwell shaders as programs. - /// </summary> - private CacheCollection _guestProgramCache; - - /// <summary> - /// Cache storing raw host programs. - /// </summary> - private CacheCollection _hostProgramCache; - - /// <summary> - /// Version of the guest cache shader (to increment when guest cache structure change). - /// </summary> - private const ulong GuestCacheVersion = 1759; - - public bool IsReadOnly => _guestProgramCache.IsReadOnly || _hostProgramCache.IsReadOnly; - - /// <summary> - /// Create a new cache manager instance - /// </summary> - /// <param name="graphicsApi">The graphics api in use</param> - /// <param name="hashType">The hash type in use for the cache</param> - /// <param name="shaderProvider">The name of the codegen provider</param> - /// <param name="titleId">The guest application title ID</param> - /// <param name="shaderCodeGenVersion">Version of the codegen</param> - public CacheManager(CacheGraphicsApi graphicsApi, CacheHashType hashType, string shaderProvider, string titleId, ulong shaderCodeGenVersion) - { - _graphicsApi = graphicsApi; - _hashType = hashType; - _shaderProvider = shaderProvider; - - string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(titleId); - - _guestProgramCache = new CacheCollection(baseCacheDirectory, _hashType, CacheGraphicsApi.Guest, "", "program", GuestCacheVersion); - _hostProgramCache = new CacheCollection(baseCacheDirectory, _hashType, _graphicsApi, _shaderProvider, "host", shaderCodeGenVersion); - } - - - /// <summary> - /// Entries to remove from the manifest. - /// </summary> - /// <param name="entries">Entries to remove from the manifest of all caches</param> - public void RemoveManifestEntries(HashSet<Hash128> entries) - { - _guestProgramCache.RemoveManifestEntriesAsync(entries); - _hostProgramCache.RemoveManifestEntriesAsync(entries); - } - - /// <summary> - /// Queue a task to flush temporary files to the archives. - /// </summary> - public void FlushToArchive() - { - _guestProgramCache.FlushToArchiveAsync(); - _hostProgramCache.FlushToArchiveAsync(); - } - - /// <summary> - /// Wait for all tasks before this given point to be done. - /// </summary> - public void Synchronize() - { - _guestProgramCache.Synchronize(); - _hostProgramCache.Synchronize(); - } - - /// <summary> - /// Save a shader program not present in the program cache. - /// </summary> - /// <param name="programCodeHash">Target program code hash</param> - /// <param name="guestProgram">Guest program raw data</param> - /// <param name="hostProgram">Host program raw data</param> - public void SaveProgram(ref Hash128 programCodeHash, byte[] guestProgram, byte[] hostProgram) - { - _guestProgramCache.AddValue(ref programCodeHash, guestProgram); - _hostProgramCache.AddValue(ref programCodeHash, hostProgram); - } - - /// <summary> - /// Add a host shader program not present in the program cache. - /// </summary> - /// <param name="programCodeHash">Target program code hash</param> - /// <param name="data">Host program raw data</param> - public void AddHostProgram(ref Hash128 programCodeHash, byte[] data) - { - _hostProgramCache.AddValue(ref programCodeHash, data); - } - - /// <summary> - /// Replace a host shader program present in the program cache. - /// </summary> - /// <param name="programCodeHash">Target program code hash</param> - /// <param name="data">Host program raw data</param> - public void ReplaceHostProgram(ref Hash128 programCodeHash, byte[] data) - { - _hostProgramCache.ReplaceValue(ref programCodeHash, data); - } - - /// <summary> - /// Removes a shader program present in the program cache. - /// </summary> - /// <param name="programCodeHash">Target program code hash</param> - public void RemoveProgram(ref Hash128 programCodeHash) - { - _guestProgramCache.RemoveValue(ref programCodeHash); - _hostProgramCache.RemoveValue(ref programCodeHash); - } - - /// <summary> - /// Get all guest program hashes. - /// </summary> - /// <returns>All guest program hashes</returns> - public ReadOnlySpan<Hash128> GetGuestProgramList() - { - return _guestProgramCache.HashTable; - } - - /// <summary> - /// Get a host program by hash. - /// </summary> - /// <param name="hash">The given hash</param> - /// <returns>The host program if present or null</returns> - public byte[] GetHostProgramByHash(ref Hash128 hash) - { - return _hostProgramCache.GetValueRaw(ref hash); - } - - /// <summary> - /// Get a guest program by hash. - /// </summary> - /// <param name="hash">The given hash</param> - /// <returns>The guest program if present or null</returns> - public byte[] GetGuestProgramByHash(ref Hash128 hash) - { - return _guestProgramCache.GetValueRaw(ref hash); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _guestProgramCache.Dispose(); - _hostProgramCache.Dispose(); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs deleted file mode 100644 index 9f8b5c39..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheGraphicsApi.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Graphics API type accepted by the shader cache. - /// </summary> - enum CacheGraphicsApi : byte - { - /// <summary> - /// OpenGL Core - /// </summary> - OpenGL, - - /// <summary> - /// OpenGL ES - /// </summary> - OpenGLES, - - /// <summary> - /// Vulkan - /// </summary> - Vulkan, - - /// <summary> - /// DirectX - /// </summary> - DirectX, - - /// <summary> - /// Metal - /// </summary> - Metal, - - /// <summary> - /// Guest, used to cache games raw shader programs. - /// </summary> - Guest - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs deleted file mode 100644 index e4ebe416..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheHashType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Hash algorithm accepted by the shader cache. - /// </summary> - enum CacheHashType : byte - { - /// <summary> - /// xxHash128 - /// </summary> - XxHash128 - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs deleted file mode 100644 index 0601451d..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/CacheManifestHeader.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Header of the shader cache manifest. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] - struct CacheManifestHeader - { - /// <summary> - /// The version of the cache. - /// </summary> - public ulong Version; - - /// <summary> - /// The graphics api used for this cache. - /// </summary> - public CacheGraphicsApi GraphicsApi; - - /// <summary> - /// The hash type used for this cache. - /// </summary> - public CacheHashType HashType; - - /// <summary> - /// CRC-16 checksum over the data in the file. - /// </summary> - public ushort TableChecksum; - - /// <summary> - /// Construct a new cache manifest header. - /// </summary> - /// <param name="version">The version of the cache</param> - /// <param name="graphicsApi">The graphics api used for this cache</param> - /// <param name="hashType">The hash type used for this cache</param> - public CacheManifestHeader(ulong version, CacheGraphicsApi graphicsApi, CacheHashType hashType) - { - Version = version; - GraphicsApi = graphicsApi; - HashType = hashType; - TableChecksum = 0; - } - - /// <summary> - /// Update the checksum in the header. - /// </summary> - /// <param name="data">The data to perform the checksum on</param> - public void UpdateChecksum(ReadOnlySpan<byte> data) - { - TableChecksum = CalculateCrc16(data); - } - - /// <summary> - /// Calculate a CRC-16 over data. - /// </summary> - /// <param name="data">The data to perform the CRC-16 on</param> - /// <returns>A CRC-16 over data</returns> - private static ushort CalculateCrc16(ReadOnlySpan<byte> data) - { - int crc = 0; - - const ushort poly = 0x1021; - - for (int i = 0; i < data.Length; i++) - { - crc ^= data[i] << 8; - - for (int j = 0; j < 8; j++) - { - crc <<= 1; - - if ((crc & 0x10000) != 0) - { - crc = (crc ^ poly) & 0xFFFF; - } - } - } - - return (ushort)crc; - } - - /// <summary> - /// Check the validity of the header. - /// </summary> - /// <param name="graphicsApi">The target graphics api in use</param> - /// <param name="hashType">The target hash type in use</param> - /// <param name="data">The data after this header</param> - /// <returns>True if the header is valid</returns> - /// <remarks>This doesn't check that versions match</remarks> - public bool IsValid(CacheGraphicsApi graphicsApi, CacheHashType hashType, ReadOnlySpan<byte> data) - { - return GraphicsApi == graphicsApi && HashType == hashType && TableChecksum == CalculateCrc16(data); - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs deleted file mode 100644 index 2e044750..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Ryujinx.Graphics.Shader; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Header of a cached guest gpu accessor. - /// </summary> - [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 1)] - struct GuestGpuAccessorHeader - { - /// <summary> - /// The count of texture descriptors. - /// </summary> - public int TextureDescriptorCount; - - /// <summary> - /// Local Size X for compute shaders. - /// </summary> - public int ComputeLocalSizeX; - - /// <summary> - /// Local Size Y for compute shaders. - /// </summary> - public int ComputeLocalSizeY; - - /// <summary> - /// Local Size Z for compute shaders. - /// </summary> - public int ComputeLocalSizeZ; - - /// <summary> - /// Local Memory size in bytes for compute shaders. - /// </summary> - public int ComputeLocalMemorySize; - - /// <summary> - /// Shared Memory size in bytes for compute shaders. - /// </summary> - public int ComputeSharedMemorySize; - - /// <summary> - /// Unused/reserved. - /// </summary> - public int Reserved1; - - /// <summary> - /// Current primitive topology for geometry shaders. - /// </summary> - public InputTopology PrimitiveTopology; - - /// <summary> - /// Tessellation parameters (packed to fit on a byte). - /// </summary> - public byte TessellationModePacked; - - /// <summary> - /// Unused/reserved. - /// </summary> - public byte Reserved2; - - /// <summary> - /// GPU boolean state that can influence shader compilation. - /// </summary> - public GuestGpuStateFlags StateFlags; - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs deleted file mode 100644 index 4b1fbb06..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuStateFlags.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - [Flags] - enum GuestGpuStateFlags : byte - { - EarlyZForce = 1 << 0 - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs deleted file mode 100644 index 373fa6c6..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntry.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Represent a cached shader entry in a guest shader program. - /// </summary> - class GuestShaderCacheEntry - { - /// <summary> - /// The header of the cached shader entry. - /// </summary> - public GuestShaderCacheEntryHeader Header { get; } - - /// <summary> - /// The code of this shader. - /// </summary> - /// <remarks>If a Vertex A is present, this also contains the code 2 section.</remarks> - public byte[] Code { get; } - - /// <summary> - /// The textures descriptors used for this shader. - /// </summary> - public Dictionary<int, GuestTextureDescriptor> TextureDescriptors { get; } - - /// <summary> - /// Create a new instance of <see cref="GuestShaderCacheEntry"/>. - /// </summary> - /// <param name="header">The header of the cached shader entry</param> - /// <param name="code">The code of this shader</param> - public GuestShaderCacheEntry(GuestShaderCacheEntryHeader header, byte[] code) - { - Header = header; - Code = code; - TextureDescriptors = new Dictionary<int, GuestTextureDescriptor>(); - } - - /// <summary> - /// Parse a raw cached user shader program into an array of shader cache entry. - /// </summary> - /// <param name="data">The raw cached user shader program</param> - /// <param name="fileHeader">The user shader program header</param> - /// <returns>An array of shader cache entry</returns> - public static GuestShaderCacheEntry[] Parse(ref ReadOnlySpan<byte> data, out GuestShaderCacheHeader fileHeader) - { - fileHeader = MemoryMarshal.Read<GuestShaderCacheHeader>(data); - - data = data.Slice(Unsafe.SizeOf<GuestShaderCacheHeader>()); - - ReadOnlySpan<GuestShaderCacheEntryHeader> entryHeaders = MemoryMarshal.Cast<byte, GuestShaderCacheEntryHeader>(data.Slice(0, fileHeader.Count * Unsafe.SizeOf<GuestShaderCacheEntryHeader>())); - - data = data.Slice(fileHeader.Count * Unsafe.SizeOf<GuestShaderCacheEntryHeader>()); - - GuestShaderCacheEntry[] result = new GuestShaderCacheEntry[fileHeader.Count]; - - for (int i = 0; i < result.Length; i++) - { - GuestShaderCacheEntryHeader header = entryHeaders[i]; - - // Ignore empty entries - if (header.Size == 0 && header.SizeA == 0) - { - continue; - } - - byte[] code = data.Slice(0, header.Size + header.SizeA).ToArray(); - - data = data.Slice(header.Size + header.SizeA); - - result[i] = new GuestShaderCacheEntry(header, code); - - ReadOnlySpan<GuestTextureDescriptor> textureDescriptors = MemoryMarshal.Cast<byte, GuestTextureDescriptor>(data.Slice(0, header.GpuAccessorHeader.TextureDescriptorCount * Unsafe.SizeOf<GuestTextureDescriptor>())); - - foreach (GuestTextureDescriptor textureDescriptor in textureDescriptors) - { - result[i].TextureDescriptors.Add((int)textureDescriptor.Handle, textureDescriptor); - } - - data = data.Slice(header.GpuAccessorHeader.TextureDescriptorCount * Unsafe.SizeOf<GuestTextureDescriptor>()); - } - - return result; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs deleted file mode 100644 index 9b22cac5..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheEntryHeader.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Ryujinx.Graphics.Shader; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// The header of a guest shader entry in a guest shader program. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x30)] - struct GuestShaderCacheEntryHeader - { - /// <summary> - /// The stage of this shader. - /// </summary> - public ShaderStage Stage; - - /// <summary> - /// Unused/reserved. - /// </summary> - public byte Reserved1; - - /// <summary> - /// Unused/reserved. - /// </summary> - public byte Reserved2; - - /// <summary> - /// Unused/reserved. - /// </summary> - public byte Reserved3; - - /// <summary> - /// The size of the code section. - /// </summary> - public int Size; - - /// <summary> - /// The size of the code2 section if present. (Vertex A) - /// </summary> - public int SizeA; - - /// <summary> - /// Constant buffer 1 data size. - /// </summary> - public int Cb1DataSize; - - /// <summary> - /// The header of the cached gpu accessor. - /// </summary> - public GuestGpuAccessorHeader GpuAccessorHeader; - - /// <summary> - /// Create a new guest shader entry header. - /// </summary> - /// <param name="stage">The stage of this shader</param> - /// <param name="size">The size of the code section</param> - /// <param name="sizeA">The size of the code2 section if present (Vertex A)</param> - /// <param name="cb1DataSize">Constant buffer 1 data size</param> - /// <param name="gpuAccessorHeader">The header of the cached gpu accessor</param> - public GuestShaderCacheEntryHeader(ShaderStage stage, int size, int sizeA, int cb1DataSize, GuestGpuAccessorHeader gpuAccessorHeader) : this() - { - Stage = stage; - Size = size; - SizeA = sizeA; - Cb1DataSize = cb1DataSize; - GpuAccessorHeader = gpuAccessorHeader; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs deleted file mode 100644 index 700be47d..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheHeader.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// The header of a shader program in the guest cache. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x10)] - struct GuestShaderCacheHeader - { - /// <summary> - /// The count of shaders defining this program. - /// </summary> - public byte Count; - - /// <summary> - /// The count of transform feedback data used in this program. - /// </summary> - public byte TransformFeedbackCount; - - /// <summary> - /// Unused/reserved. - /// </summary> - public ushort Reserved1; - - /// <summary> - /// Unused/reserved. - /// </summary> - public ulong Reserved2; - - /// <summary> - /// Create a new guest shader cache header. - /// </summary> - /// <param name="count">The count of shaders defining this program</param> - /// <param name="transformFeedbackCount">The count of transform feedback data used in this program</param> - public GuestShaderCacheHeader(byte count, byte transformFeedbackCount) : this() - { - Count = count; - TransformFeedbackCount = transformFeedbackCount; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs deleted file mode 100644 index 18cfdf55..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestShaderCacheTransformFeedbackHeader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Header for transform feedback. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] - struct GuestShaderCacheTransformFeedbackHeader - { - /// <summary> - /// The buffer index of the transform feedback. - /// </summary> - public int BufferIndex; - - /// <summary> - /// The stride of the transform feedback. - /// </summary> - public int Stride; - - /// <summary> - /// The length of the varying location buffer of the transform feedback. - /// </summary> - public int VaryingLocationsLength; - - /// <summary> - /// Reserved/unused. - /// </summary> - public int Reserved1; - - public GuestShaderCacheTransformFeedbackHeader(int bufferIndex, int stride, int varyingLocationsLength) : this() - { - BufferIndex = bufferIndex; - Stride = stride; - VaryingLocationsLength = varyingLocationsLength; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs deleted file mode 100644 index 9491496d..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestTextureDescriptor.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Ryujinx.Graphics.Gpu.Image; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Contains part of TextureDescriptor from <see cref="Image"/> used for shader codegen. - /// </summary> - [StructLayout(LayoutKind.Sequential, Size = 0xC, Pack = 1)] - struct GuestTextureDescriptor : ITextureDescriptor - { - public uint Handle; - public uint Format; - public TextureTarget Target; - [MarshalAs(UnmanagedType.I1)] - public bool IsSrgb; - [MarshalAs(UnmanagedType.I1)] - public bool IsTextureCoordNormalized; - public byte Reserved; - - public uint UnpackFormat() - { - return Format; - } - - public bool UnpackSrgb() - { - return IsSrgb; - } - - public bool UnpackTextureCoordNormalized() - { - return IsTextureCoordNormalized; - } - - public TextureTarget UnpackTextureTarget() - { - return Target; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs deleted file mode 100644 index fe79acb3..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Shader; -using System; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Host shader entry used for binding information. - /// </summary> - class HostShaderCacheEntry - { - /// <summary> - /// The header of the cached shader entry. - /// </summary> - public HostShaderCacheEntryHeader Header { get; } - - /// <summary> - /// Cached constant buffers. - /// </summary> - public BufferDescriptor[] CBuffers { get; } - - /// <summary> - /// Cached storage buffers. - /// </summary> - public BufferDescriptor[] SBuffers { get; } - - /// <summary> - /// Cached texture descriptors. - /// </summary> - public TextureDescriptor[] Textures { get; } - - /// <summary> - /// Cached image descriptors. - /// </summary> - public TextureDescriptor[] Images { get; } - - /// <summary> - /// Create a new instance of <see cref="HostShaderCacheEntry"/>. - /// </summary> - /// <param name="header">The header of the cached shader entry</param> - /// <param name="cBuffers">Cached constant buffers</param> - /// <param name="sBuffers">Cached storage buffers</param> - /// <param name="textures">Cached texture descriptors</param> - /// <param name="images">Cached image descriptors</param> - private HostShaderCacheEntry( - HostShaderCacheEntryHeader header, - BufferDescriptor[] cBuffers, - BufferDescriptor[] sBuffers, - TextureDescriptor[] textures, - TextureDescriptor[] images) - { - Header = header; - CBuffers = cBuffers; - SBuffers = sBuffers; - Textures = textures; - Images = images; - } - - private HostShaderCacheEntry() - { - Header = new HostShaderCacheEntryHeader(); - CBuffers = new BufferDescriptor[0]; - SBuffers = new BufferDescriptor[0]; - Textures = new TextureDescriptor[0]; - Images = new TextureDescriptor[0]; - } - - private HostShaderCacheEntry(ShaderProgramInfo programInfo) - { - Header = new HostShaderCacheEntryHeader(programInfo.CBuffers.Count, - programInfo.SBuffers.Count, - programInfo.Textures.Count, - programInfo.Images.Count, - programInfo.UsesInstanceId, - programInfo.UsesRtLayer, - programInfo.ClipDistancesWritten, - programInfo.FragmentOutputMap); - CBuffers = programInfo.CBuffers.ToArray(); - SBuffers = programInfo.SBuffers.ToArray(); - Textures = programInfo.Textures.ToArray(); - Images = programInfo.Images.ToArray(); - } - - /// <summary> - /// Convert the host shader entry to a <see cref="ShaderProgramInfo"/>. - /// </summary> - /// <returns>A new <see cref="ShaderProgramInfo"/> from this instance</returns> - internal ShaderProgramInfo ToShaderProgramInfo() - { - return new ShaderProgramInfo( - CBuffers, - SBuffers, - Textures, - Images, - default, - Header.UseFlags.HasFlag(UseFlags.InstanceId), - Header.UseFlags.HasFlag(UseFlags.RtLayer), - Header.ClipDistancesWritten, - Header.FragmentOutputMap); - } - - /// <summary> - /// Parse a raw cached user shader program into an array of shader cache entry. - /// </summary> - /// <param name="data">The raw cached host shader</param> - /// <param name="programCode">The host shader program</param> - /// <returns>An array of shader cache entry</returns> - internal static HostShaderCacheEntry[] Parse(ReadOnlySpan<byte> data, out ReadOnlySpan<byte> programCode) - { - HostShaderCacheHeader fileHeader = MemoryMarshal.Read<HostShaderCacheHeader>(data); - - data = data.Slice(Unsafe.SizeOf<HostShaderCacheHeader>()); - - ReadOnlySpan<HostShaderCacheEntryHeader> entryHeaders = MemoryMarshal.Cast<byte, HostShaderCacheEntryHeader>(data.Slice(0, fileHeader.Count * Unsafe.SizeOf<HostShaderCacheEntryHeader>())); - - data = data.Slice(fileHeader.Count * Unsafe.SizeOf<HostShaderCacheEntryHeader>()); - - HostShaderCacheEntry[] result = new HostShaderCacheEntry[fileHeader.Count]; - - for (int i = 0; i < result.Length; i++) - { - HostShaderCacheEntryHeader header = entryHeaders[i]; - - if (!header.InUse) - { - continue; - } - - int cBufferDescriptorsSize = header.CBuffersCount * Unsafe.SizeOf<BufferDescriptor>(); - int sBufferDescriptorsSize = header.SBuffersCount * Unsafe.SizeOf<BufferDescriptor>(); - int textureDescriptorsSize = header.TexturesCount * Unsafe.SizeOf<TextureDescriptor>(); - int imageDescriptorsSize = header.ImagesCount * Unsafe.SizeOf<TextureDescriptor>(); - - ReadOnlySpan<BufferDescriptor> cBuffers = MemoryMarshal.Cast<byte, BufferDescriptor>(data.Slice(0, cBufferDescriptorsSize)); - data = data.Slice(cBufferDescriptorsSize); - - ReadOnlySpan<BufferDescriptor> sBuffers = MemoryMarshal.Cast<byte, BufferDescriptor>(data.Slice(0, sBufferDescriptorsSize)); - data = data.Slice(sBufferDescriptorsSize); - - ReadOnlySpan<TextureDescriptor> textureDescriptors = MemoryMarshal.Cast<byte, TextureDescriptor>(data.Slice(0, textureDescriptorsSize)); - data = data.Slice(textureDescriptorsSize); - - ReadOnlySpan<TextureDescriptor> imageDescriptors = MemoryMarshal.Cast<byte, TextureDescriptor>(data.Slice(0, imageDescriptorsSize)); - data = data.Slice(imageDescriptorsSize); - - result[i] = new HostShaderCacheEntry(header, cBuffers.ToArray(), sBuffers.ToArray(), textureDescriptors.ToArray(), imageDescriptors.ToArray()); - } - - programCode = data.Slice(0, fileHeader.CodeSize); - - return result; - } - - /// <summary> - /// Create a new host shader cache file. - /// </summary> - /// <param name="programCode">The host shader program</param> - /// <param name="codeHolders">The shaders code holder</param> - /// <returns>Raw data of a new host shader cache file</returns> - internal static byte[] Create(ReadOnlySpan<byte> programCode, CachedShaderStage[] codeHolders) - { - HostShaderCacheHeader header = new HostShaderCacheHeader((byte)codeHolders.Length, programCode.Length); - - HostShaderCacheEntry[] entries = new HostShaderCacheEntry[codeHolders.Length]; - - for (int i = 0; i < codeHolders.Length; i++) - { - if (codeHolders[i] == null) - { - entries[i] = new HostShaderCacheEntry(); - } - else - { - entries[i] = new HostShaderCacheEntry(codeHolders[i].Info); - } - } - - using (MemoryStream stream = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(stream); - - writer.WriteStruct(header); - - foreach (HostShaderCacheEntry entry in entries) - { - writer.WriteStruct(entry.Header); - } - - foreach (HostShaderCacheEntry entry in entries) - { - foreach (BufferDescriptor cBuffer in entry.CBuffers) - { - writer.WriteStruct(cBuffer); - } - - foreach (BufferDescriptor sBuffer in entry.SBuffers) - { - writer.WriteStruct(sBuffer); - } - - foreach (TextureDescriptor texture in entry.Textures) - { - writer.WriteStruct(texture); - } - - foreach (TextureDescriptor image in entry.Images) - { - writer.WriteStruct(image); - } - } - - writer.Write(programCode); - - return stream.ToArray(); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs deleted file mode 100644 index c3c0de22..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// Flags indicating if the shader accesses certain built-ins, such as the instance ID. - /// </summary> - enum UseFlags : byte - { - /// <summary> - /// None of the built-ins are used. - /// </summary> - None = 0, - - /// <summary> - /// Indicates whenever the vertex shader reads the gl_InstanceID built-in. - /// </summary> - InstanceId = 1 << 0, - - /// <summary> - /// Indicates whenever any of the VTG stages writes to the gl_Layer built-in. - /// </summary> - RtLayer = 1 << 1 - } - - /// <summary> - /// Host shader entry header used for binding information. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] - struct HostShaderCacheEntryHeader - { - /// <summary> - /// Count of constant buffer descriptors. - /// </summary> - public int CBuffersCount; - - /// <summary> - /// Count of storage buffer descriptors. - /// </summary> - public int SBuffersCount; - - /// <summary> - /// Count of texture descriptors. - /// </summary> - public int TexturesCount; - - /// <summary> - /// Count of image descriptors. - /// </summary> - public int ImagesCount; - - /// <summary> - /// Flags indicating if the shader accesses certain built-ins, such as the instance ID. - /// </summary> - public UseFlags UseFlags; - - /// <summary> - /// Set to true if this entry is in use. - /// </summary> - [MarshalAs(UnmanagedType.I1)] - public bool InUse; - - /// <summary> - /// Mask of clip distances that are written to on the shader. - /// </summary> - public byte ClipDistancesWritten; - - /// <summary> - /// Reserved / unused. - /// </summary> - public byte Reserved; - - /// <summary> - /// Mask of components written by the fragment shader stage. - /// </summary> - public int FragmentOutputMap; - - /// <summary> - /// Create a new host shader cache entry header. - /// </summary> - /// <param name="cBuffersCount">Count of constant buffer descriptors</param> - /// <param name="sBuffersCount">Count of storage buffer descriptors</param> - /// <param name="texturesCount">Count of texture descriptors</param> - /// <param name="imagesCount">Count of image descriptors</param> - /// <param name="usesInstanceId">Set to true if the shader uses instance id</param> - /// <param name="clipDistancesWritten">Mask of clip distances that are written to on the shader</param> - /// <param name="fragmentOutputMap">Mask of components written by the fragment shader stage</param> - public HostShaderCacheEntryHeader( - int cBuffersCount, - int sBuffersCount, - int texturesCount, - int imagesCount, - bool usesInstanceId, - bool usesRtLayer, - byte clipDistancesWritten, - int fragmentOutputMap) : this() - { - CBuffersCount = cBuffersCount; - SBuffersCount = sBuffersCount; - TexturesCount = texturesCount; - ImagesCount = imagesCount; - ClipDistancesWritten = clipDistancesWritten; - FragmentOutputMap = fragmentOutputMap; - InUse = true; - - UseFlags = usesInstanceId ? UseFlags.InstanceId : UseFlags.None; - - if (usesRtLayer) - { - UseFlags |= UseFlags.RtLayer; - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs deleted file mode 100644 index 27f216cc..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheHeader.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition -{ - /// <summary> - /// The header of a shader program in the guest cache. - /// </summary> - [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = 0x10)] - struct HostShaderCacheHeader - { - /// <summary> - /// The count of shaders defining this program. - /// </summary> - public byte Count; - - /// <summary> - /// Unused/reserved. - /// </summary> - public byte Reserved1; - - /// <summary> - /// Unused/reserved. - /// </summary> - public ushort Reserved2; - - /// <summary> - /// Size of the shader binary. - /// </summary> - public int CodeSize; - - /// <summary> - /// Create a new host shader cache header. - /// </summary> - /// <param name="count">The count of shaders defining this program</param> - /// <param name="codeSize">The size of the shader binary</param> - public HostShaderCacheHeader(byte count, int codeSize) : this() - { - Count = count; - CodeSize = codeSize; - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs deleted file mode 100644 index 885bcd09..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs +++ /dev/null @@ -1,258 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Gpu.Engine.Threed; -using Ryujinx.Graphics.Gpu.Shader.Cache.Definition; -using Ryujinx.Graphics.Gpu.Shader.DiskCache; -using Ryujinx.Graphics.Shader; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache -{ - /// <summary> - /// Class handling shader cache migrations. - /// </summary> - static class Migration - { - // Last codegen version before the migration to the new cache. - private const ulong ShaderCodeGenVersion = 3054; - - /// <summary> - /// Migrates from the old cache format to the new one. - /// </summary> - /// <param name="context">GPU context</param> - /// <param name="hostStorage">Disk cache host storage (used to create the new shader files)</param> - /// <returns>Number of migrated shaders</returns> - public static int MigrateFromLegacyCache(GpuContext context, DiskCacheHostStorage hostStorage) - { - string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId); - string cacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, CacheGraphicsApi.Guest, "", "program"); - - // If the directory does not exist, we have no old cache. - // Exist early as the CacheManager constructor will create the directories. - if (!Directory.Exists(cacheDirectory)) - { - return 0; - } - - if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null) - { - CacheManager cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion); - - bool isReadOnly = cacheManager.IsReadOnly; - - HashSet<Hash128> invalidEntries = null; - - if (isReadOnly) - { - Logger.Warning?.Print(LogClass.Gpu, "Loading shader cache in read-only mode (cache in use by another program!)"); - } - else - { - invalidEntries = new HashSet<Hash128>(); - } - - ReadOnlySpan<Hash128> guestProgramList = cacheManager.GetGuestProgramList(); - - for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++) - { - Hash128 key = guestProgramList[programIndex]; - - byte[] guestProgram = cacheManager.GetGuestProgramByHash(ref key); - - if (guestProgram == null) - { - Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); - - continue; - } - - ReadOnlySpan<byte> guestProgramReadOnlySpan = guestProgram; - - ReadOnlySpan<GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); - - if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) - { - Debug.Assert(cachedShaderEntries.Length == 1); - - GuestShaderCacheEntry entry = cachedShaderEntries[0]; - - byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); - - Span<byte> codeSpan = entry.Code; - byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray(); - - ShaderProgramInfo info = new ShaderProgramInfo( - Array.Empty<BufferDescriptor>(), - Array.Empty<BufferDescriptor>(), - Array.Empty<TextureDescriptor>(), - Array.Empty<TextureDescriptor>(), - ShaderStage.Compute, - false, - false, - 0, - 0); - - GpuChannelComputeState computeState = new GpuChannelComputeState( - entry.Header.GpuAccessorHeader.ComputeLocalSizeX, - entry.Header.GpuAccessorHeader.ComputeLocalSizeY, - entry.Header.GpuAccessorHeader.ComputeLocalSizeZ, - entry.Header.GpuAccessorHeader.ComputeLocalMemorySize, - entry.Header.GpuAccessorHeader.ComputeSharedMemorySize); - - ShaderSpecializationState specState = new ShaderSpecializationState(computeState); - - foreach (var td in entry.TextureDescriptors) - { - var handle = td.Key; - var data = td.Value; - - specState.RegisterTexture( - 0, - handle, - -1, - data.UnpackFormat(), - data.UnpackSrgb(), - data.UnpackTextureTarget(), - data.UnpackTextureCoordNormalized()); - } - - CachedShaderStage shader = new CachedShaderStage(info, code, cb1Data); - CachedShaderProgram program = new CachedShaderProgram(null, specState, shader); - - hostStorage.AddShader(context, program, ReadOnlySpan<byte>.Empty); - } - else - { - Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); - - CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; - List<ShaderProgram> shaderPrograms = new List<ShaderProgram>(); - - TransformFeedbackDescriptorOld[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); - - GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); - - GuestGpuAccessorHeader accessorHeader = entries[0].Header.GpuAccessorHeader; - - TessMode tessMode = new TessMode(); - - int tessPatchType = accessorHeader.TessellationModePacked & 3; - int tessSpacing = (accessorHeader.TessellationModePacked >> 2) & 3; - bool tessCw = (accessorHeader.TessellationModePacked & 0x10) != 0; - - tessMode.Packed = (uint)tessPatchType; - tessMode.Packed |= (uint)(tessSpacing << 4); - - if (tessCw) - { - tessMode.Packed |= 0x100; - } - - PrimitiveTopology topology = accessorHeader.PrimitiveTopology switch - { - InputTopology.Lines => PrimitiveTopology.Lines, - InputTopology.LinesAdjacency => PrimitiveTopology.LinesAdjacency, - InputTopology.Triangles => PrimitiveTopology.Triangles, - InputTopology.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency, - _ => PrimitiveTopology.Points - }; - - GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState( - accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce), - topology, - tessMode, - false, - false, - false); - - TransformFeedbackDescriptor[] tfdNew = null; - - if (tfd != null) - { - tfdNew = new TransformFeedbackDescriptor[tfd.Length]; - - for (int tfIndex = 0; tfIndex < tfd.Length; tfIndex++) - { - Array32<uint> varyingLocations = new Array32<uint>(); - Span<byte> varyingLocationsSpan = MemoryMarshal.Cast<uint, byte>(varyingLocations.ToSpan()); - tfd[tfIndex].VaryingLocations.CopyTo(varyingLocationsSpan.Slice(0, tfd[tfIndex].VaryingLocations.Length)); - - tfdNew[tfIndex] = new TransformFeedbackDescriptor( - tfd[tfIndex].BufferIndex, - tfd[tfIndex].Stride, - tfd[tfIndex].VaryingLocations.Length, - ref varyingLocations); - } - } - - ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, tfdNew); - - for (int i = 0; i < entries.Length; i++) - { - GuestShaderCacheEntry entry = entries[i]; - - if (entry == null) - { - continue; - } - - ShaderProgramInfo info = new ShaderProgramInfo( - Array.Empty<BufferDescriptor>(), - Array.Empty<BufferDescriptor>(), - Array.Empty<TextureDescriptor>(), - Array.Empty<TextureDescriptor>(), - (ShaderStage)(i + 1), - false, - false, - 0, - 0); - - // NOTE: Vertex B comes first in the shader cache. - byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); - byte[] code2 = entry.Header.SizeA != 0 ? entry.Code.AsSpan(entry.Header.Size, entry.Header.SizeA).ToArray() : null; - - Span<byte> codeSpan = entry.Code; - byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray(); - - shaders[i + 1] = new CachedShaderStage(info, code, cb1Data); - - if (code2 != null) - { - shaders[0] = new CachedShaderStage(null, code2, cb1Data); - } - - foreach (var td in entry.TextureDescriptors) - { - var handle = td.Key; - var data = td.Value; - - specState.RegisterTexture( - i, - handle, - -1, - data.UnpackFormat(), - data.UnpackSrgb(), - data.UnpackTextureTarget(), - data.UnpackTextureCoordNormalized()); - } - } - - CachedShaderProgram program = new CachedShaderProgram(null, specState, shaders); - - hostStorage.AddShader(context, program, ReadOnlySpan<byte>.Empty); - } - } - - return guestProgramList.Length; - } - - return 0; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs deleted file mode 100644 index 5e9c6711..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/TransformFeedbackDescriptorOld.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gpu.Shader.Cache -{ - struct TransformFeedbackDescriptorOld - { - public int BufferIndex { get; } - public int Stride { get; } - - public byte[] VaryingLocations { get; } - - public TransformFeedbackDescriptorOld(int bufferIndex, int stride, byte[] varyingLocations) - { - BufferIndex = bufferIndex; - Stride = stride; - VaryingLocations = varyingLocations ?? throw new ArgumentNullException(nameof(varyingLocations)); - } - } -} |