Added support for more shader instructions and texture formats, fix swapped channels in RGB565 and RGBA5551? texture formats, allow zero values on blending registers, initial work to build CFG on the shader decoder, update the BRA instruction to work with it (WIP)
This commit is contained in:
parent
9670c096e4
commit
f43dd08064
14 changed files with 749 additions and 154 deletions
|
@ -1,19 +1,133 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
static class ShaderDecoder
|
||||
{
|
||||
private const bool AddDbgComments = true;
|
||||
|
||||
public static ShaderIrBlock DecodeBasicBlock(IGalMemory Memory, long Position)
|
||||
public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start)
|
||||
{
|
||||
ShaderIrBlock Block = new ShaderIrBlock();
|
||||
Dictionary<long, ShaderIrBlock> Visited = new Dictionary<long, ShaderIrBlock>();
|
||||
Dictionary<long, ShaderIrBlock> VisitedEnd = new Dictionary<long, ShaderIrBlock>();
|
||||
|
||||
while (true)
|
||||
Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>();
|
||||
|
||||
ShaderIrBlock Enqueue(long Position, ShaderIrBlock Source = null)
|
||||
{
|
||||
Block.Position = Position;
|
||||
if (!Visited.TryGetValue(Position, out ShaderIrBlock Output))
|
||||
{
|
||||
Output = new ShaderIrBlock(Position);
|
||||
|
||||
Block.MarkLabel(Position);
|
||||
Blocks.Enqueue(Output);
|
||||
|
||||
Visited.Add(Position, Output);
|
||||
}
|
||||
|
||||
if (Source != null)
|
||||
{
|
||||
Output.Sources.Add(Source);
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
ShaderIrBlock Entry = Enqueue(Start);
|
||||
|
||||
while (Blocks.Count > 0)
|
||||
{
|
||||
ShaderIrBlock Current = Blocks.Dequeue();
|
||||
|
||||
FillBlock(Memory, Current);
|
||||
|
||||
//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 Op = GetInnermostOp(LastNode);
|
||||
|
||||
if (Op?.Inst == ShaderIrInst.Bra)
|
||||
{
|
||||
int Offset = ((ShaderIrOperImm)Op.OperandA).Value;
|
||||
|
||||
long Target = Current.EndPosition + Offset;
|
||||
|
||||
Current.Branch = 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)
|
||||
{
|
||||
ulong FirstPos = ulong.MaxValue;
|
||||
|
||||
foreach (ShaderIrBlock Block in Visited.Values)
|
||||
{
|
||||
if (FirstPos > (ulong)Block.Position)
|
||||
FirstPos = (ulong)Block.Position;
|
||||
}
|
||||
|
||||
ShaderIrBlock Current = Visited[(long)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 Position = Block.Position;
|
||||
|
||||
do
|
||||
{
|
||||
//Ignore scheduling instructions, which are written every 32 bytes.
|
||||
if ((Position & 0x1f) == 0)
|
||||
{
|
||||
|
@ -33,9 +147,20 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
if (AddDbgComments)
|
||||
{
|
||||
string DbgOpCode = $"0x{Position:x16}: 0x{OpCode:x16} ";
|
||||
string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} ";
|
||||
|
||||
Block.AddNode(new ShaderIrCmnt(DbgOpCode + (Decode?.Method.Name ?? "???")));
|
||||
DbgOpCode += (Decode?.Method.Name ?? "???");
|
||||
|
||||
if (Decode == ShaderDecode.Bra)
|
||||
{
|
||||
int Offset = ((int)(OpCode >> 20) << 8) >> 8;
|
||||
|
||||
long Target = Position + Offset;
|
||||
|
||||
DbgOpCode += " (0x" + Target.ToString("x16") + ")";
|
||||
}
|
||||
|
||||
Block.AddNode(new ShaderIrCmnt(DbgOpCode));
|
||||
}
|
||||
|
||||
if (Decode == null)
|
||||
|
@ -44,19 +169,36 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
|
||||
Decode(Block, OpCode);
|
||||
|
||||
if (Block.GetLastNode() is ShaderIrOp Op && Op.Inst == ShaderIrInst.Exit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!IsFlowChange(Block.GetLastNode()));
|
||||
|
||||
return Block;
|
||||
Block.EndPosition = Position;
|
||||
}
|
||||
|
||||
private static bool IsFlowChange(ShaderIrInst Inst)
|
||||
private static bool IsFlowChange(ShaderIrNode Node)
|
||||
{
|
||||
return Inst == ShaderIrInst.Exit;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue