ShaderDecoder.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using System.Collections.Generic;
  2. namespace Ryujinx.Graphics.Gal.Shader
  3. {
  4. static class ShaderDecoder
  5. {
  6. private const long HeaderSize = 0x50;
  7. private const bool AddDbgComments = true;
  8. public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start)
  9. {
  10. Dictionary<int, ShaderIrBlock> Visited = new Dictionary<int, ShaderIrBlock>();
  11. Dictionary<int, ShaderIrBlock> VisitedEnd = new Dictionary<int, ShaderIrBlock>();
  12. Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>();
  13. long Beginning = Start + HeaderSize;
  14. ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null)
  15. {
  16. if (!Visited.TryGetValue(Position, out ShaderIrBlock Output))
  17. {
  18. Output = new ShaderIrBlock(Position);
  19. Blocks.Enqueue(Output);
  20. Visited.Add(Position, Output);
  21. }
  22. if (Source != null)
  23. {
  24. Output.Sources.Add(Source);
  25. }
  26. return Output;
  27. }
  28. ShaderIrBlock Entry = Enqueue(0);
  29. while (Blocks.Count > 0)
  30. {
  31. ShaderIrBlock Current = Blocks.Dequeue();
  32. FillBlock(Memory, Current, Beginning);
  33. //Set child blocks. "Branch" is the block the branch instruction
  34. //points to (when taken), "Next" is the block at the next address,
  35. //executed when the branch is not taken. For Unconditional Branches
  36. //or end of shader, Next is null.
  37. if (Current.Nodes.Count > 0)
  38. {
  39. ShaderIrNode LastNode = Current.GetLastNode();
  40. ShaderIrOp InnerOp = GetInnermostOp(LastNode);
  41. if (InnerOp?.Inst == ShaderIrInst.Bra)
  42. {
  43. int Target = ((ShaderIrOperImm)InnerOp.OperandA).Value;
  44. Current.Branch = Enqueue(Target, Current);
  45. }
  46. foreach (ShaderIrNode Node in Current.Nodes)
  47. {
  48. InnerOp = GetInnermostOp(Node);
  49. if (InnerOp is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy)
  50. {
  51. int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value;
  52. Enqueue(Target, Current);
  53. }
  54. }
  55. if (NodeHasNext(LastNode))
  56. {
  57. Current.Next = Enqueue(Current.EndPosition);
  58. }
  59. }
  60. //If we have on the graph two blocks with the same end position,
  61. //then we need to split the bigger block and have two small blocks,
  62. //the end position of the bigger "Current" block should then be == to
  63. //the position of the "Smaller" block.
  64. while (VisitedEnd.TryGetValue(Current.EndPosition, out ShaderIrBlock Smaller))
  65. {
  66. if (Current.Position > Smaller.Position)
  67. {
  68. ShaderIrBlock Temp = Smaller;
  69. Smaller = Current;
  70. Current = Temp;
  71. }
  72. Current.EndPosition = Smaller.Position;
  73. Current.Next = Smaller;
  74. Current.Branch = null;
  75. Current.Nodes.RemoveRange(
  76. Current.Nodes.Count - Smaller.Nodes.Count,
  77. Smaller.Nodes.Count);
  78. VisitedEnd[Smaller.EndPosition] = Smaller;
  79. }
  80. VisitedEnd.Add(Current.EndPosition, Current);
  81. }
  82. //Make and sort Graph blocks array by position.
  83. ShaderIrBlock[] Graph = new ShaderIrBlock[Visited.Count];
  84. while (Visited.Count > 0)
  85. {
  86. uint FirstPos = uint.MaxValue;
  87. foreach (ShaderIrBlock Block in Visited.Values)
  88. {
  89. if (FirstPos > (uint)Block.Position)
  90. FirstPos = (uint)Block.Position;
  91. }
  92. ShaderIrBlock Current = Visited[(int)FirstPos];
  93. do
  94. {
  95. Graph[Graph.Length - Visited.Count] = Current;
  96. Visited.Remove(Current.Position);
  97. Current = Current.Next;
  98. }
  99. while (Current != null);
  100. }
  101. return Graph;
  102. }
  103. private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning)
  104. {
  105. int Position = Block.Position;
  106. do
  107. {
  108. //Ignore scheduling instructions, which are written every 32 bytes.
  109. if ((Position & 0x1f) == 0)
  110. {
  111. Position += 8;
  112. continue;
  113. }
  114. uint Word0 = (uint)Memory.ReadInt32(Position + Beginning + 0);
  115. uint Word1 = (uint)Memory.ReadInt32(Position + Beginning + 4);
  116. Position += 8;
  117. long OpCode = Word0 | (long)Word1 << 32;
  118. ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
  119. if (AddDbgComments)
  120. {
  121. string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} ";
  122. DbgOpCode += (Decode?.Method.Name ?? "???");
  123. if (Decode == ShaderDecode.Bra || Decode == ShaderDecode.Ssy)
  124. {
  125. int Offset = ((int)(OpCode >> 20) << 8) >> 8;
  126. long Target = Position + Offset;
  127. DbgOpCode += " (0x" + Target.ToString("x16") + ")";
  128. }
  129. Block.AddNode(new ShaderIrCmnt(DbgOpCode));
  130. }
  131. if (Decode == null)
  132. {
  133. continue;
  134. }
  135. Decode(Block, OpCode, Position);
  136. }
  137. while (!IsFlowChange(Block.GetLastNode()));
  138. Block.EndPosition = Position;
  139. }
  140. private static bool IsFlowChange(ShaderIrNode Node)
  141. {
  142. return !NodeHasNext(GetInnermostOp(Node));
  143. }
  144. private static ShaderIrOp GetInnermostOp(ShaderIrNode Node)
  145. {
  146. if (Node is ShaderIrCond Cond)
  147. {
  148. Node = Cond.Child;
  149. }
  150. return Node is ShaderIrOp Op ? Op : null;
  151. }
  152. private static bool NodeHasNext(ShaderIrNode Node)
  153. {
  154. if (!(Node is ShaderIrOp Op))
  155. {
  156. return true;
  157. }
  158. return Op.Inst != ShaderIrInst.Exit &&
  159. Op.Inst != ShaderIrInst.Bra;
  160. }
  161. }
  162. }