2019-11-10 09:03:38 -05:00
|
|
|
using Ryujinx.Graphics.Shader.Translation;
|
|
|
|
using System;
|
2019-10-13 02:02:07 -04:00
|
|
|
using System.IO;
|
|
|
|
|
2019-11-14 13:26:40 -05:00
|
|
|
namespace Ryujinx.Graphics.Gpu.Shader
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Shader dumper, writes binary shader code to disk.
|
|
|
|
/// </summary>
|
2019-10-13 02:02:07 -04:00
|
|
|
class ShaderDumper
|
|
|
|
{
|
|
|
|
private string _runtimeDir;
|
|
|
|
private string _dumpPath;
|
|
|
|
private int _dumpIndex;
|
|
|
|
|
|
|
|
public int CurrentDumpIndex => _dumpIndex;
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
public ShaderDumper()
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
|
|
|
_dumpIndex = 1;
|
|
|
|
}
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Dumps shader code to disk.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="code">Code to be dumped</param>
|
|
|
|
/// <param name="compute">True for compute shader code, false for graphics shader code</param>
|
|
|
|
/// <param name="fullPath">Output path for the shader code with header included</param>
|
|
|
|
/// <param name="codePath">Output path for the shader code without header</param>
|
2020-01-12 18:27:50 -05:00
|
|
|
public void Dump(ReadOnlySpan<byte> code, bool compute, out string fullPath, out string codePath)
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
|
|
|
_dumpPath = GraphicsConfig.ShadersDumpPath;
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_dumpPath))
|
|
|
|
{
|
2019-11-10 09:03:38 -05:00
|
|
|
fullPath = null;
|
|
|
|
codePath = null;
|
|
|
|
|
2019-10-13 02:02:07 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin";
|
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
fullPath = Path.Combine(FullDir(), fileName);
|
|
|
|
codePath = Path.Combine(CodeDir(), fileName);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
|
|
|
_dumpIndex++;
|
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
code = Translator.ExtractCode(code, compute, out int headerSize);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
using (MemoryStream stream = new MemoryStream(code.ToArray()))
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
2019-11-10 09:03:38 -05:00
|
|
|
BinaryReader codeReader = new BinaryReader(stream);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
using (FileStream fullFile = File.Create(fullPath))
|
|
|
|
using (FileStream codeFile = File.Create(codePath))
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
2019-11-10 09:03:38 -05:00
|
|
|
BinaryWriter fullWriter = new BinaryWriter(fullFile);
|
|
|
|
BinaryWriter codeWriter = new BinaryWriter(codeFile);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
fullWriter.Write(codeReader.ReadBytes(headerSize));
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
byte[] temp = codeReader.ReadBytes(code.Length - headerSize);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
fullWriter.Write(temp);
|
|
|
|
codeWriter.Write(temp);
|
2019-10-13 02:02:07 -04:00
|
|
|
|
2019-11-10 09:03:38 -05:00
|
|
|
// Align to meet nvdisasm requirements.
|
|
|
|
while (codeFile.Length % 0x20 != 0)
|
2019-10-13 02:02:07 -04:00
|
|
|
{
|
2019-11-10 09:03:38 -05:00
|
|
|
codeWriter.Write(0);
|
2019-10-13 02:02:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Returns the output directory for shader code with header.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>Directory path</returns>
|
2019-10-13 02:02:07 -04:00
|
|
|
private string FullDir()
|
|
|
|
{
|
|
|
|
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
|
|
|
|
}
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Returns the output directory for shader code without header.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>Directory path</returns>
|
2019-10-13 02:02:07 -04:00
|
|
|
private string CodeDir()
|
|
|
|
{
|
|
|
|
return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
|
|
|
|
}
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Returns the full output directory for the current shader dump.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>Directory path</returns>
|
2019-10-13 02:02:07 -04:00
|
|
|
private string DumpDir()
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(_runtimeDir))
|
|
|
|
{
|
|
|
|
int index = 1;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
_runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2"));
|
|
|
|
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
while (Directory.Exists(_runtimeDir));
|
|
|
|
|
|
|
|
Directory.CreateDirectory(_runtimeDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
return _runtimeDir;
|
|
|
|
}
|
|
|
|
|
2019-12-30 23:46:57 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Creates a new specified directory if needed.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="dir">The directory to create</param>
|
|
|
|
/// <returns>The same directory passed to the method</returns>
|
2019-10-13 02:02:07 -04:00
|
|
|
private static string CreateAndReturn(string dir)
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(dir);
|
|
|
|
|
|
|
|
return dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|