diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs new file mode 100644 index 0000000000..8af40d2028 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -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 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 InputAttributes; + private SortedDictionary OutputAttributes; + + private HashSet 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.Fabs, GetFabsExpr }, + { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Fmul, GetFmulExpr }, + { ShaderIrInst.Fneg, GetFnegExpr } + }; + } + + public string Decompile(int[] Code) + { + InputAttributes = new SortedDictionary(); + OutputAttributes = new SortedDictionary(); + + UsedCbufs = new HashSet(); + + 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 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 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)}"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs index 24f9aa3598..a8db8b0c0f 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs @@ -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) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs index 3a4379751d..384f8971e8 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -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()) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs index 916d85a4b9..156f60fa20 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs @@ -7,8 +7,6 @@ namespace Ryujinx.Graphics.Gal.Shader { public static void Test() { - System.Console.WriteLine("Starting test code..."); - System.Collections.Generic.List CodeList = new System.Collections.Generic.List(); 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 InstsExpr = new - Dictionary() - { - { 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!"); } } } \ No newline at end of file