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.UsesRtLayer,
programInfo.ClipDistancesWritten,
programInfo.FragmentOutputMap);
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,
default,
Header.UseFlags.HasFlag(UseFlags.InstanceId),
Header.UseFlags.HasFlag(UseFlags.RtLayer),
Header.ClipDistancesWritten,
Header.FragmentOutputMap);
}
///
/// 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, 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();
}
}
}
}