/ src / Ryujinx.Graphics.Gpu / Shader / ShaderDumper.cs
ShaderDumper.cs
  1  using System.IO;
  2  
  3  namespace Ryujinx.Graphics.Gpu.Shader
  4  {
  5      /// <summary>
  6      /// Shader dumper, writes binary shader code to disk.
  7      /// </summary>
  8      class ShaderDumper
  9      {
 10          private string _runtimeDir;
 11          private string _dumpPath;
 12  
 13          /// <summary>
 14          /// Current index of the shader dump binary file.
 15          /// This is incremented after each save, in order to give unique names to the files.
 16          /// </summary>
 17          public int CurrentDumpIndex { get; private set; }
 18  
 19          /// <summary>
 20          /// Creates a new instance of the shader dumper.
 21          /// </summary>
 22          public ShaderDumper()
 23          {
 24              CurrentDumpIndex = 1;
 25          }
 26  
 27          /// <summary>
 28          /// Dumps shader code to disk.
 29          /// </summary>
 30          /// <param name="code">Code to be dumped</param>
 31          /// <param name="compute">True for compute shader code, false for graphics shader code</param>
 32          /// <returns>Paths where the shader code was dumped</returns>
 33          public ShaderDumpPaths Dump(byte[] code, bool compute)
 34          {
 35              _dumpPath = GraphicsConfig.ShadersDumpPath;
 36  
 37              if (string.IsNullOrWhiteSpace(_dumpPath))
 38              {
 39                  return default;
 40              }
 41  
 42              string fileName = "Shader" + CurrentDumpIndex.ToString("d4") + ".bin";
 43  
 44              string fullPath = Path.Combine(FullDir(), fileName);
 45              string codePath = Path.Combine(CodeDir(), fileName);
 46  
 47              CurrentDumpIndex++;
 48  
 49              using MemoryStream stream = new(code);
 50              BinaryReader codeReader = new(stream);
 51  
 52              using FileStream fullFile = File.Create(fullPath);
 53              using FileStream codeFile = File.Create(codePath);
 54              BinaryWriter fullWriter = new(fullFile);
 55              BinaryWriter codeWriter = new(codeFile);
 56  
 57              int headerSize = compute ? 0 : 0x50;
 58  
 59              fullWriter.Write(codeReader.ReadBytes(headerSize));
 60  
 61              byte[] temp = codeReader.ReadBytes(code.Length - headerSize);
 62  
 63              fullWriter.Write(temp);
 64              codeWriter.Write(temp);
 65  
 66              // Align to meet nvdisasm requirements.
 67              while (codeFile.Length % 0x20 != 0)
 68              {
 69                  codeWriter.Write(0);
 70              }
 71  
 72              return new ShaderDumpPaths(fullPath, codePath);
 73          }
 74  
 75          /// <summary>
 76          /// Returns the output directory for shader code with header.
 77          /// </summary>
 78          /// <returns>Directory path</returns>
 79          private string FullDir()
 80          {
 81              return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
 82          }
 83  
 84          /// <summary>
 85          /// Returns the output directory for shader code without header.
 86          /// </summary>
 87          /// <returns>Directory path</returns>
 88          private string CodeDir()
 89          {
 90              return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
 91          }
 92  
 93          /// <summary>
 94          /// Returns the full output directory for the current shader dump.
 95          /// </summary>
 96          /// <returns>Directory path</returns>
 97          private string DumpDir()
 98          {
 99              if (string.IsNullOrEmpty(_runtimeDir))
100              {
101                  int index = 1;
102  
103                  do
104                  {
105                      _runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2"));
106  
107                      index++;
108                  }
109                  while (Directory.Exists(_runtimeDir));
110  
111                  Directory.CreateDirectory(_runtimeDir);
112              }
113  
114              return _runtimeDir;
115          }
116  
117          /// <summary>
118          /// Creates a new specified directory if needed.
119          /// </summary>
120          /// <param name="dir">The directory to create</param>
121          /// <returns>The same directory passed to the method</returns>
122          private static string CreateAndReturn(string dir)
123          {
124              Directory.CreateDirectory(dir);
125  
126              return dir;
127          }
128      }
129  }