2018-07-15 18:37:27 -04:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Gal
|
|
|
|
|
{
|
|
|
|
|
static class ShaderDumper
|
|
|
|
|
{
|
|
|
|
|
private static string RuntimeDir;
|
|
|
|
|
|
2018-10-17 17:02:23 -04:00
|
|
|
|
public static int DumpIndex { get; private set; } = 1;
|
2018-07-15 18:37:27 -04:00
|
|
|
|
|
|
|
|
|
public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "")
|
|
|
|
|
{
|
2018-10-17 17:02:23 -04:00
|
|
|
|
if (!IsDumpEnabled())
|
2018-07-15 18:37:27 -04:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string FileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(Type) + ExtSuffix + ".bin";
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
string FullPath = Path.Combine(FullDir(), FileName);
|
|
|
|
|
string CodePath = Path.Combine(CodeDir(), FileName);
|
2018-07-15 18:37:27 -04:00
|
|
|
|
|
|
|
|
|
DumpIndex++;
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
using (FileStream FullFile = File.Create(FullPath))
|
|
|
|
|
using (FileStream CodeFile = File.Create(CodePath))
|
2018-07-15 18:37:27 -04:00
|
|
|
|
{
|
2018-10-17 17:02:23 -04:00
|
|
|
|
BinaryWriter FullWriter = new BinaryWriter(FullFile);
|
|
|
|
|
BinaryWriter CodeWriter = new BinaryWriter(CodeFile);
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
for (long i = 0; i < 0x50; i += 4)
|
|
|
|
|
{
|
|
|
|
|
FullWriter.Write(Memory.ReadInt32(Position + i));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 18:37:27 -04:00
|
|
|
|
long Offset = 0;
|
|
|
|
|
|
|
|
|
|
ulong Instruction = 0;
|
|
|
|
|
|
|
|
|
|
//Dump until a NOP instruction is found
|
2019-01-31 07:43:24 -05:00
|
|
|
|
while ((Instruction >> 48 & 0xfff8) != 0x50b0)
|
2018-07-15 18:37:27 -04:00
|
|
|
|
{
|
2018-07-19 01:33:27 -04:00
|
|
|
|
uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0);
|
|
|
|
|
uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4);
|
2018-07-15 18:37:27 -04:00
|
|
|
|
|
|
|
|
|
Instruction = Word0 | (ulong)Word1 << 32;
|
|
|
|
|
|
|
|
|
|
//Zero instructions (other kind of NOP) stop immediatly,
|
|
|
|
|
//this is to avoid two rows of zeroes
|
|
|
|
|
if (Instruction == 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
FullWriter.Write(Instruction);
|
|
|
|
|
CodeWriter.Write(Instruction);
|
2018-07-15 18:37:27 -04:00
|
|
|
|
|
|
|
|
|
Offset += 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Align to meet nvdisasm requeriments
|
|
|
|
|
while (Offset % 0x20 != 0)
|
|
|
|
|
{
|
2018-07-19 01:33:27 -04:00
|
|
|
|
FullWriter.Write(0);
|
|
|
|
|
CodeWriter.Write(0);
|
2018-07-15 18:37:27 -04:00
|
|
|
|
|
|
|
|
|
Offset += 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-17 17:02:23 -04:00
|
|
|
|
public static bool IsDumpEnabled()
|
|
|
|
|
{
|
|
|
|
|
return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
private static string FullDir()
|
|
|
|
|
{
|
|
|
|
|
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string CodeDir()
|
|
|
|
|
{
|
|
|
|
|
return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 18:37:27 -04:00
|
|
|
|
private static string DumpDir()
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(RuntimeDir))
|
|
|
|
|
{
|
|
|
|
|
int Index = 1;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
RuntimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + Index.ToString("d2"));
|
|
|
|
|
|
|
|
|
|
Index++;
|
|
|
|
|
}
|
|
|
|
|
while (Directory.Exists(RuntimeDir));
|
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory(RuntimeDir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return RuntimeDir;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-19 01:33:27 -04:00
|
|
|
|
private static string CreateAndReturn(string Dir)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(Dir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(Dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Dir;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 18:37:27 -04:00
|
|
|
|
private static string ShaderExtension(GalShaderType Type)
|
|
|
|
|
{
|
|
|
|
|
switch (Type)
|
|
|
|
|
{
|
|
|
|
|
case GalShaderType.Vertex: return "vert";
|
|
|
|
|
case GalShaderType.TessControl: return "tesc";
|
|
|
|
|
case GalShaderType.TessEvaluation: return "tese";
|
|
|
|
|
case GalShaderType.Geometry: return "geom";
|
|
|
|
|
case GalShaderType.Fragment: return "frag";
|
|
|
|
|
|
|
|
|
|
default: throw new ArgumentException(nameof(Type));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|