| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- using System.Collections.Generic;
- namespace Ryujinx.Graphics.Gal.Shader
- {
- static class ShaderDecoder
- {
- private const long HeaderSize = 0x50;
- private const bool AddDbgComments = true;
- public static ShaderIrBlock[] Decode(IGalMemory memory, long start)
- {
- Dictionary<int, ShaderIrBlock> visited = new Dictionary<int, ShaderIrBlock>();
- Dictionary<int, ShaderIrBlock> visitedEnd = new Dictionary<int, ShaderIrBlock>();
- Queue<ShaderIrBlock> blocks = new Queue<ShaderIrBlock>();
- long beginning = start + HeaderSize;
- ShaderIrBlock Enqueue(int position, ShaderIrBlock source = null)
- {
- if (!visited.TryGetValue(position, out ShaderIrBlock output))
- {
- output = new ShaderIrBlock(position);
- blocks.Enqueue(output);
- visited.Add(position, output);
- }
- if (source != null)
- {
- output.Sources.Add(source);
- }
- return output;
- }
- ShaderIrBlock entry = Enqueue(0);
- while (blocks.Count > 0)
- {
- ShaderIrBlock current = blocks.Dequeue();
- FillBlock(memory, current, beginning);
- //Set child blocks. "Branch" is the block the branch instruction
- //points to (when taken), "Next" is the block at the next address,
- //executed when the branch is not taken. For Unconditional Branches
- //or end of shader, Next is null.
- if (current.Nodes.Count > 0)
- {
- ShaderIrNode lastNode = current.GetLastNode();
- ShaderIrOp innerOp = GetInnermostOp(lastNode);
- if (innerOp?.Inst == ShaderIrInst.Bra)
- {
- int target = ((ShaderIrOperImm)innerOp.OperandA).Value;
- current.Branch = Enqueue(target, current);
- }
- foreach (ShaderIrNode node in current.Nodes)
- {
- innerOp = GetInnermostOp(node);
- if (innerOp is ShaderIrOp currOp && currOp.Inst == ShaderIrInst.Ssy)
- {
- int target = ((ShaderIrOperImm)currOp.OperandA).Value;
- Enqueue(target, current);
- }
- }
- if (NodeHasNext(lastNode))
- {
- current.Next = Enqueue(current.EndPosition);
- }
- }
- //If we have on the graph two blocks with the same end position,
- //then we need to split the bigger block and have two small blocks,
- //the end position of the bigger "Current" block should then be == to
- //the position of the "Smaller" block.
- while (visitedEnd.TryGetValue(current.EndPosition, out ShaderIrBlock smaller))
- {
- if (current.Position > smaller.Position)
- {
- ShaderIrBlock temp = smaller;
- smaller = current;
- current = temp;
- }
- current.EndPosition = smaller.Position;
- current.Next = smaller;
- current.Branch = null;
- current.Nodes.RemoveRange(
- current.Nodes.Count - smaller.Nodes.Count,
- smaller.Nodes.Count);
- visitedEnd[smaller.EndPosition] = smaller;
- }
- visitedEnd.Add(current.EndPosition, current);
- }
- //Make and sort Graph blocks array by position.
- ShaderIrBlock[] graph = new ShaderIrBlock[visited.Count];
- while (visited.Count > 0)
- {
- uint firstPos = uint.MaxValue;
- foreach (ShaderIrBlock block in visited.Values)
- {
- if (firstPos > (uint)block.Position)
- firstPos = (uint)block.Position;
- }
- ShaderIrBlock current = visited[(int)firstPos];
- do
- {
- graph[graph.Length - visited.Count] = current;
- visited.Remove(current.Position);
- current = current.Next;
- }
- while (current != null);
- }
- return graph;
- }
- private static void FillBlock(IGalMemory memory, ShaderIrBlock block, long beginning)
- {
- int position = block.Position;
- do
- {
- //Ignore scheduling instructions, which are written every 32 bytes.
- if ((position & 0x1f) == 0)
- {
- position += 8;
- continue;
- }
- uint word0 = (uint)memory.ReadInt32(position + beginning + 0);
- uint word1 = (uint)memory.ReadInt32(position + beginning + 4);
- position += 8;
- long opCode = word0 | (long)word1 << 32;
- ShaderDecodeFunc decode = ShaderOpCodeTable.GetDecoder(opCode);
- if (AddDbgComments)
- {
- string dbgOpCode = $"0x{(position - 8):x16}: 0x{opCode:x16} ";
- dbgOpCode += (decode?.Method.Name ?? "???");
- if (decode == ShaderDecode.Bra || decode == ShaderDecode.Ssy)
- {
- int offset = ((int)(opCode >> 20) << 8) >> 8;
- long target = position + offset;
- dbgOpCode += " (0x" + target.ToString("x16") + ")";
- }
- block.AddNode(new ShaderIrCmnt(dbgOpCode));
- }
- if (decode == null)
- {
- continue;
- }
- decode(block, opCode, position);
- }
- while (!IsFlowChange(block.GetLastNode()));
- block.EndPosition = position;
- }
- private static bool IsFlowChange(ShaderIrNode node)
- {
- return !NodeHasNext(GetInnermostOp(node));
- }
- private static ShaderIrOp GetInnermostOp(ShaderIrNode node)
- {
- if (node is ShaderIrCond cond)
- {
- node = cond.Child;
- }
- return node is ShaderIrOp op ? op : null;
- }
- private static bool NodeHasNext(ShaderIrNode node)
- {
- if (!(node is ShaderIrOp op))
- {
- return true;
- }
- return op.Inst != ShaderIrInst.Exit &&
- op.Inst != ShaderIrInst.Bra;
- }
- }
- }
|