Started to work on glsl decompiler
This commit is contained in:
parent
06a19248cd
commit
6477368255
4 changed files with 337 additions and 125 deletions
328
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal file
328
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal file
|
@ -0,0 +1,328 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class GlslDecompiler
|
||||
{
|
||||
private delegate string GetInstExpr(ShaderIrOperOp Op);
|
||||
|
||||
private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
|
||||
|
||||
private class Attrib
|
||||
{
|
||||
public string Name;
|
||||
public int Elems;
|
||||
|
||||
public Attrib(string Name, int Elems)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Elems = Elems;
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
|
||||
|
||||
private SortedDictionary<int, Attrib> InputAttributes;
|
||||
private SortedDictionary<int, Attrib> OutputAttributes;
|
||||
|
||||
private HashSet<int> UsedCbufs;
|
||||
|
||||
private const int AttrStartIndex = 8;
|
||||
|
||||
private const string InputAttrPrefix = "in_attr";
|
||||
private const string OutputAttrPrefix = "out_attr";
|
||||
|
||||
private const string CbufBuffPrefix = "c";
|
||||
private const string CbufDataName = "buf";
|
||||
|
||||
private const string GprName = "gpr";
|
||||
|
||||
private string IdentationStr = "\t";
|
||||
|
||||
private int GprsCount;
|
||||
|
||||
private StringBuilder BodySB;
|
||||
|
||||
public GlslDecompiler()
|
||||
{
|
||||
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
|
||||
{
|
||||
{ ShaderIrInst.Fabs, GetFabsExpr },
|
||||
{ ShaderIrInst.Fadd, GetFaddExpr },
|
||||
{ ShaderIrInst.Ffma, GetFfmaExpr },
|
||||
{ ShaderIrInst.Fmul, GetFmulExpr },
|
||||
{ ShaderIrInst.Fneg, GetFnegExpr }
|
||||
};
|
||||
}
|
||||
|
||||
public string Decompile(int[] Code)
|
||||
{
|
||||
InputAttributes = new SortedDictionary<int, Attrib>();
|
||||
OutputAttributes = new SortedDictionary<int, Attrib>();
|
||||
|
||||
UsedCbufs = new HashSet<int>();
|
||||
|
||||
BodySB = new StringBuilder();
|
||||
|
||||
//FIXME: Only valid for vertex shaders.
|
||||
OutputAttributes.Add(7, new Attrib("gl_Position", 4));
|
||||
|
||||
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
|
||||
|
||||
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||
|
||||
PrintBlockScope(Nodes, "void main()", 1);
|
||||
|
||||
StringBuilder SB = new StringBuilder();
|
||||
|
||||
PrintDeclUBOs(SB);
|
||||
PrintDeclInAttributes(SB);
|
||||
PrintDeclOutAttributes(SB);
|
||||
|
||||
if (GprsCount > 0)
|
||||
{
|
||||
SB.AppendLine($"float {GprName}[{GprsCount}];");
|
||||
SB.AppendLine(string.Empty);
|
||||
}
|
||||
|
||||
SB.Append(BodySB.ToString());
|
||||
|
||||
BodySB.Clear();
|
||||
|
||||
return SB.ToString();
|
||||
}
|
||||
|
||||
private void PrintDeclUBOs(StringBuilder SB)
|
||||
{
|
||||
foreach (int Cbuf in UsedCbufs)
|
||||
{
|
||||
SB.AppendLine($"layout(std430, binding = {Cbuf}) buffer {CbufBuffPrefix}{Cbuf} {{");
|
||||
SB.AppendLine($"{IdentationStr}float[] {CbufDataName};");
|
||||
SB.AppendLine("};");
|
||||
SB.AppendLine(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintDeclInAttributes(StringBuilder SB)
|
||||
{
|
||||
foreach (KeyValuePair<int, Attrib> KV in InputAttributes)
|
||||
{
|
||||
int Index = KV.Key - AttrStartIndex;
|
||||
|
||||
if (Index >= 0)
|
||||
{
|
||||
string Type = ElemTypes[KV.Value.Elems];
|
||||
|
||||
SB.AppendLine($"layout(location = {Index}) in {Type} {KV.Value.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
SB.AppendLine(string.Empty);
|
||||
}
|
||||
|
||||
private void PrintDeclOutAttributes(StringBuilder SB)
|
||||
{
|
||||
foreach (KeyValuePair<int, Attrib> KV in OutputAttributes)
|
||||
{
|
||||
int Index = KV.Key - AttrStartIndex;
|
||||
|
||||
if (Index >= 0)
|
||||
{
|
||||
string Type = ElemTypes[KV.Value.Elems];
|
||||
|
||||
SB.AppendLine($"layout(location = {Index}) out {Type} {KV.Value.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
SB.AppendLine(string.Empty);
|
||||
}
|
||||
|
||||
private void PrintBlockScope(ShaderIrNode[] Nodes, string ScopeName, int IdentationLevel)
|
||||
{
|
||||
string Identation = string.Empty;
|
||||
|
||||
for (int Index = 0; Index < IdentationLevel - 1; Index++)
|
||||
{
|
||||
Identation += IdentationStr;
|
||||
}
|
||||
|
||||
if (ScopeName != string.Empty)
|
||||
{
|
||||
ScopeName += " ";
|
||||
}
|
||||
|
||||
BodySB.AppendLine(Identation + ScopeName + "{");
|
||||
|
||||
string LastLine = Identation + "}";
|
||||
|
||||
if (IdentationLevel > 0)
|
||||
{
|
||||
Identation += IdentationStr;
|
||||
}
|
||||
|
||||
foreach (ShaderIrNode Node in Nodes)
|
||||
{
|
||||
if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex == ShaderIrOperReg.ZRIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BodySB.AppendLine(Identation +
|
||||
$"{GetOOperName(Node.Dst)} = " +
|
||||
$"{GetIOperName(Node.Src, true)};");
|
||||
}
|
||||
|
||||
BodySB.AppendLine(LastLine);
|
||||
}
|
||||
|
||||
private string GetOOperName(ShaderIrOper Oper)
|
||||
{
|
||||
if (Oper is ShaderIrOperAbuf Abuf)
|
||||
{
|
||||
return GetOAbufName(Abuf);
|
||||
}
|
||||
else if (Oper is ShaderIrOperReg Reg)
|
||||
{
|
||||
return GetRegName(Reg);
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(Oper));
|
||||
}
|
||||
|
||||
private string GetIOperName(ShaderIrOper Oper, bool Entry = false)
|
||||
{
|
||||
switch (Oper)
|
||||
{
|
||||
case ShaderIrOperAbuf Abuf: return GetIAbufName(Abuf);
|
||||
case ShaderIrOperCbuf Cbuf: return GetCbufName(Cbuf);
|
||||
case ShaderIrOperReg Reg: return GetRegName(Reg);
|
||||
case ShaderIrOperOp Op:
|
||||
string Expr;
|
||||
|
||||
if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
|
||||
{
|
||||
Expr = GetExpr(Op);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(Op.Inst.ToString());
|
||||
}
|
||||
|
||||
if (!(Entry || IsUnary(Op.Inst)))
|
||||
{
|
||||
Expr = $"({Expr})";
|
||||
}
|
||||
|
||||
return Expr;
|
||||
|
||||
default: throw new ArgumentException(nameof(Oper));
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsUnary(ShaderIrInst Inst)
|
||||
{
|
||||
return Inst == ShaderIrInst.Fabs ||
|
||||
Inst == ShaderIrInst.Fneg;
|
||||
}
|
||||
|
||||
private string GetOAbufName(ShaderIrOperAbuf Abuf)
|
||||
{
|
||||
int AttrIndex = Abuf.Offs >> 4;
|
||||
|
||||
int Elem = (Abuf.Offs >> 2) & 3;
|
||||
|
||||
if (!OutputAttributes.TryGetValue(AttrIndex, out Attrib Attr))
|
||||
{
|
||||
Attr = new Attrib(OutputAttrPrefix + (AttrIndex - AttrStartIndex), Elem);
|
||||
|
||||
OutputAttributes.Add(AttrIndex, Attr);
|
||||
}
|
||||
|
||||
if (Attr.Elems < Elem)
|
||||
{
|
||||
Attr.Elems = Elem;
|
||||
}
|
||||
|
||||
return $"{Attr.Name}.{GetAttrSwizzle(Elem)}";
|
||||
}
|
||||
|
||||
private string GetIAbufName(ShaderIrOperAbuf Abuf)
|
||||
{
|
||||
int AttrIndex = Abuf.Offs >> 4;
|
||||
|
||||
int Elem = (Abuf.Offs >> 2) & 3;
|
||||
|
||||
if (!InputAttributes.TryGetValue(AttrIndex, out Attrib Attr))
|
||||
{
|
||||
Attr = new Attrib(InputAttrPrefix + (AttrIndex - AttrStartIndex), Elem);
|
||||
|
||||
InputAttributes.Add(AttrIndex, Attr);
|
||||
}
|
||||
|
||||
if (Attr.Elems < Elem)
|
||||
{
|
||||
Attr.Elems = Elem;
|
||||
}
|
||||
|
||||
return $"{Attr.Name}.{GetAttrSwizzle(Elem)}";
|
||||
}
|
||||
|
||||
private string GetAttrSwizzle(int Elem)
|
||||
{
|
||||
return "xyzw".Substring(Elem, 1);
|
||||
}
|
||||
|
||||
private string GetCbufName(ShaderIrOperCbuf Cbuf)
|
||||
{
|
||||
UsedCbufs.Add(Cbuf.Index);
|
||||
|
||||
return $"{CbufBuffPrefix}{Cbuf.Index}.{CbufDataName}[{Cbuf.Offs}]";
|
||||
}
|
||||
|
||||
private string GetRegName(ShaderIrOperReg Reg)
|
||||
{
|
||||
if (GprsCount < Reg.GprIndex + 1)
|
||||
{
|
||||
GprsCount = Reg.GprIndex + 1;
|
||||
}
|
||||
|
||||
return GetRegName(Reg.GprIndex);
|
||||
}
|
||||
|
||||
private string GetRegName(int GprIndex)
|
||||
{
|
||||
return GprIndex == ShaderIrOperReg.ZRIndex ? "0" : $"{GprName}[{GprIndex}]";
|
||||
}
|
||||
|
||||
private string GetFabsExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"abs({GetIOperName(Op.OperandA)})";
|
||||
}
|
||||
|
||||
private string GetFaddExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetIOperName(Op.OperandA)} + " +
|
||||
$"{GetIOperName(Op.OperandB)}";
|
||||
}
|
||||
|
||||
private string GetFfmaExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetIOperName(Op.OperandA)} * " +
|
||||
$"{GetIOperName(Op.OperandB)} + " +
|
||||
$"{GetIOperName(Op.OperandC)}";
|
||||
}
|
||||
|
||||
private string GetFmulExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetIOperName(Op.OperandA)} * " +
|
||||
$"{GetIOperName(Op.OperandB)}";
|
||||
}
|
||||
|
||||
private string GetFnegExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"-{GetIOperName(Op.OperandA)}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
class ShaderIrOperReg : ShaderIrOper
|
||||
{
|
||||
public const int ZRIndex = 0xff;
|
||||
|
||||
public int GprIndex { get; private set; }
|
||||
|
||||
public ShaderIrOperReg(int GprIndex)
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
TryAddRegUse(Op, Op.OperandB, 1);
|
||||
TryAddRegUse(Op, Op.OperandC, 2);
|
||||
}
|
||||
else if (Oper is ShaderIrOperReg Reg && Reg.GprIndex != 0xff)
|
||||
else if (Oper is ShaderIrOperReg Reg && Reg.GprIndex != ShaderIrOperReg.ZRIndex)
|
||||
{
|
||||
GetRegUse(Reg.GprIndex).AddUseSite(new UseSite(Parent, OperIndex));
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
TryAddRegUse(Node, Node.Src);
|
||||
}
|
||||
|
||||
if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex != 0xff)
|
||||
if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex != ShaderIrOperReg.ZRIndex)
|
||||
{
|
||||
RegUse Use = GetRegUse(Reg.GprIndex);
|
||||
|
||||
|
@ -140,6 +140,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: On the fragment shader, we should keep the values on r0-r3,
|
||||
//because they are the fragment shader color output.
|
||||
foreach (RegUse Use in Uses.Values)
|
||||
{
|
||||
if (Use.TryPropagate())
|
||||
|
|
|
@ -7,8 +7,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
public static void Test()
|
||||
{
|
||||
System.Console.WriteLine("Starting test code...");
|
||||
|
||||
System.Collections.Generic.List<int> CodeList = new System.Collections.Generic.List<int>();
|
||||
|
||||
using (System.IO.FileStream FS = new System.IO.FileStream("D:\\puyo_vsh.bin", System.IO.FileMode.Open))
|
||||
|
@ -23,129 +21,11 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
int[] Code = CodeList.ToArray();
|
||||
|
||||
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
|
||||
GlslDecompiler Decompiler = new GlslDecompiler();
|
||||
|
||||
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||
System.Console.WriteLine(Decompiler.Decompile(Code));
|
||||
|
||||
foreach (ShaderIrNode Node in Nodes)
|
||||
{
|
||||
System.Console.WriteLine($"{GetOutOperName(Node.Dst)} = {GetInOperName(Node.Src, true)}");
|
||||
}
|
||||
|
||||
System.Console.WriteLine("Test code finished!");
|
||||
}
|
||||
|
||||
private static string GetOutOperName(ShaderIrOper Oper)
|
||||
{
|
||||
switch (Oper)
|
||||
{
|
||||
case ShaderIrOperAbuf Abuf: return GetOAbufName(Abuf);
|
||||
case ShaderIrOperReg Reg: return GetRegName(Reg);
|
||||
|
||||
default: throw new ArgumentException(nameof(Oper));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetInOperName(ShaderIrOper Oper, bool Entry = false)
|
||||
{
|
||||
switch (Oper)
|
||||
{
|
||||
case ShaderIrOperAbuf Abuf: return GetIAbufName(Abuf);
|
||||
case ShaderIrOperCbuf Cbuf: return GetCbufName(Cbuf);
|
||||
case ShaderIrOperReg Reg: return GetRegName(Reg);
|
||||
case ShaderIrOperOp Op:
|
||||
string Expr;
|
||||
|
||||
if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
|
||||
{
|
||||
Expr = GetExpr(Op);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(Op.Inst.ToString());
|
||||
}
|
||||
|
||||
if (!(Entry || IsUnary(Op.Inst)))
|
||||
{
|
||||
Expr = $"({Expr})";
|
||||
}
|
||||
|
||||
return Expr;
|
||||
|
||||
default: throw new ArgumentException(nameof(Oper));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUnary(ShaderIrInst Inst)
|
||||
{
|
||||
return Inst == ShaderIrInst.Fabs ||
|
||||
Inst == ShaderIrInst.Fneg;
|
||||
}
|
||||
|
||||
private static string GetOAbufName(ShaderIrOperAbuf Abuf)
|
||||
{
|
||||
return $"a_out[0x{Abuf.Offs:x} + {GetRegName(Abuf.GprIndex)}]";
|
||||
}
|
||||
|
||||
private static string GetIAbufName(ShaderIrOperAbuf Abuf)
|
||||
{
|
||||
return $"a_in[0x{Abuf.Offs:x} + {GetRegName(Abuf.GprIndex)}]";
|
||||
}
|
||||
|
||||
private static string GetCbufName(ShaderIrOperCbuf Cbuf)
|
||||
{
|
||||
return $"c{Cbuf.Index}[{Cbuf.Offs}]";
|
||||
}
|
||||
|
||||
private static string GetRegName(ShaderIrOperReg Reg)
|
||||
{
|
||||
return GetRegName(Reg.GprIndex);
|
||||
}
|
||||
|
||||
private static string GetRegName(int GprIndex)
|
||||
{
|
||||
return GprIndex == 0xff ? "0" : $"r{GprIndex}";
|
||||
}
|
||||
|
||||
private delegate string GetInstExpr(ShaderIrOperOp Op);
|
||||
|
||||
private static Dictionary<ShaderIrInst, GetInstExpr> InstsExpr = new
|
||||
Dictionary<ShaderIrInst, GetInstExpr>()
|
||||
{
|
||||
{ ShaderIrInst.Fabs, GetFabsExpr },
|
||||
{ ShaderIrInst.Fadd, GetFaddExpr },
|
||||
{ ShaderIrInst.Ffma, GetFfmaExpr },
|
||||
{ ShaderIrInst.Fmul, GetFmulExpr },
|
||||
{ ShaderIrInst.Fneg, GetFnegExpr },
|
||||
};
|
||||
|
||||
private static string GetFabsExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"abs({GetInOperName(Op.OperandA)})";
|
||||
}
|
||||
|
||||
private static string GetFaddExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetInOperName(Op.OperandA)} + " +
|
||||
$"{GetInOperName(Op.OperandB)}";
|
||||
}
|
||||
|
||||
private static string GetFfmaExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetInOperName(Op.OperandA)} * " +
|
||||
$"{GetInOperName(Op.OperandB)} + " +
|
||||
$"{GetInOperName(Op.OperandC)}";
|
||||
}
|
||||
|
||||
private static string GetFmulExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"{GetInOperName(Op.OperandA)} * " +
|
||||
$"{GetInOperName(Op.OperandB)}";
|
||||
}
|
||||
|
||||
private static string GetFnegExpr(ShaderIrOperOp Op)
|
||||
{
|
||||
return $"-{GetInOperName(Op.OperandA)}";
|
||||
System.Console.WriteLine("Done!");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue