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 }