ShaderDecoder.cs 6.4 KB

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