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 { /// /// Host shader entry used for binding information. /// class HostShaderCacheEntry { /// /// The header of the cached shader entry. /// public HostShaderCacheEntryHeader Header { get; } /// /// Cached constant buffers. /// public BufferDescriptor[] CBuffers { get; } /// /// Cached storage buffers. /// public BufferDescriptor[] SBuffers { get; } /// /// Cached texture descriptors. /// public TextureDescriptor[] Textures { get; } /// /// Cached image descriptors. /// public TextureDescriptor[] Images { get; } /// /// Create a new instance of . /// /// The header of the cached shader entry /// Cached constant buffers /// Cached storage buffers /// Cached texture descriptors /// Cached image descriptors 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.ClipDistancesWritten); CBuffers = programInfo.CBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray(); Textures = programInfo.Textures.ToArray(); Images = programInfo.Images.ToArray(); } /// /// Convert the host shader entry to a . /// /// A new from this instance internal ShaderProgramInfo ToShaderProgramInfo() { return new ShaderProgramInfo(CBuffers, SBuffers, Textures, Images, Header.UsesInstanceId, Header.ClipDistancesWritten); } /// /// Parse a raw cached user shader program into an array of shader cache entry. /// /// The raw cached host shader /// The host shader program /// An array of shader cache entry internal static HostShaderCacheEntry[] Parse(ReadOnlySpan data, out ReadOnlySpan programCode) { HostShaderCacheHeader fileHeader = MemoryMarshal.Read(data); data = data.Slice(Unsafe.SizeOf()); ReadOnlySpan entryHeaders = MemoryMarshal.Cast(data.Slice(0, fileHeader.Count * Unsafe.SizeOf())); data = data.Slice(fileHeader.Count * Unsafe.SizeOf()); 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(); int sBufferDescriptorsSize = header.SBuffersCount * Unsafe.SizeOf(); int textureDescriptorsSize = header.TexturesCount * Unsafe.SizeOf(); int imageDescriptorsSize = header.ImagesCount * Unsafe.SizeOf(); ReadOnlySpan cBuffers = MemoryMarshal.Cast(data.Slice(0, cBufferDescriptorsSize)); data = data.Slice(cBufferDescriptorsSize); ReadOnlySpan sBuffers = MemoryMarshal.Cast(data.Slice(0, sBufferDescriptorsSize)); data = data.Slice(sBufferDescriptorsSize); ReadOnlySpan textureDescriptors = MemoryMarshal.Cast(data.Slice(0, textureDescriptorsSize)); data = data.Slice(textureDescriptorsSize); ReadOnlySpan imageDescriptors = MemoryMarshal.Cast(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; } /// /// Create a new host shader cache file. /// /// The host shader program /// The shaders code holder /// Raw data of a new host shader cache file internal static byte[] Create(ReadOnlySpan programCode, ShaderCodeHolder[] 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(); } } } }