diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 6ca01bf386..be282fd8f5 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -1,5 +1,7 @@ using System; +using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; + namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode @@ -74,29 +76,28 @@ namespace Ryujinx.Graphics.Gal.Shader bool Ab = ((OpCode >> 49) & 1) != 0; bool Ad = ((OpCode >> 50) & 1) != 0; - EmitAluOperANode(Block, OpCode); + ShaderIrOper OperA = GetAluOperANode_R(OpCode); + ShaderIrOper OperB; if (Inst == ShaderIrInst.Fadd) { - EmitAluAbsNeg(Block, Aa, Na); + OperA = GetAluAbsNeg(OperA, Aa, Na); } switch (Oper) { - case ShaderOper.RR: EmitAluOperBNode_RR (Block, OpCode); break; - case ShaderOper.CR: EmitAluOperBCNode_C (Block, OpCode); break; - case ShaderOper.Imm: EmitAluOperBNode_Imm(Block, OpCode); break; + case ShaderOper.RR: OperB = GetAluOperBNode_RR (OpCode); break; + case ShaderOper.CR: OperB = GetAluOperBCNode_C (OpCode); break; + case ShaderOper.Imm: OperB = GetAluOperBNode_Imm(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } - EmitAluAbsNeg(Block, Ab, Nb); + OperB = GetAluAbsNeg(OperB, Ab, Nb); - Block.AddNode(new ShaderIrNode(Inst)); + ShaderIrOper Op = GetAluAbs(new ShaderIrOperOp(Inst, OperA, OperB), Ad); - EmitAluAbs(Block, Ad); - - EmitAluStrResult(Block, OpCode); + Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op)); } private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) @@ -104,88 +105,49 @@ namespace Ryujinx.Graphics.Gal.Shader bool Nb = ((OpCode >> 48) & 1) != 0; bool Nc = ((OpCode >> 49) & 1) != 0; - EmitAluOperANode(Block, OpCode); + ShaderIrOper OperA = GetAluOperANode_R(OpCode); + ShaderIrOper OperB; + ShaderIrOper OperC; switch (Oper) { - case ShaderOper.RR: EmitAluOperBNode_RR (Block, OpCode); break; - case ShaderOper.CR: EmitAluOperBCNode_C (Block, OpCode); break; - case ShaderOper.RC: EmitAluOperBCNode_R (Block, OpCode); break; - case ShaderOper.Imm: EmitAluOperBNode_Imm(Block, OpCode); break; + case ShaderOper.RR: OperB = GetAluOperBNode_RR (OpCode); break; + case ShaderOper.CR: OperB = GetAluOperBCNode_C (OpCode); break; + case ShaderOper.RC: OperB = GetAluOperBCNode_R (OpCode); break; + case ShaderOper.Imm: OperB = GetAluOperBNode_Imm(OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); } - EmitAluNeg(Block, Nb); - - Block.AddNode(new ShaderIrNode(ShaderIrInst.Fmul)); + OperB = GetAluNeg(OperB, Nb); if (Oper == ShaderOper.RC) { - EmitAluOperBCNode_C(Block, OpCode); + OperC = GetAluNeg(GetAluOperBCNode_C(OpCode), Nc); } else { - EmitAluOperBCNode_R(Block, OpCode); + OperC = GetAluNeg(GetAluOperBCNode_R(OpCode), Nc); } - EmitAluNeg(Block, Nc); + ShaderIrOper Op = new ShaderIrOperOp(ShaderIrInst.Ffma, OperA, OperB, OperC); - Block.AddNode(new ShaderIrNode(ShaderIrInst.Fadd)); - - EmitAluStrResult(Block, OpCode); + Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op)); } - private static void EmitAluAbsNeg(ShaderIrBlock Block, bool Abs, bool Neg) + private static ShaderIrOper GetAluAbsNeg(ShaderIrOper Node, bool Abs, bool Neg) { - EmitAluAbs(Block, Abs); - EmitAluNeg(Block, Neg); + return GetAluNeg(GetAluAbs(Node, Abs), Neg); } - private static void EmitAluAbs(ShaderIrBlock Block, bool Abs) + private static ShaderIrOper GetAluAbs(ShaderIrOper Node, bool Abs) { - if (Abs) - { - Block.AddNode(new ShaderIrNode(ShaderIrInst.Fabs)); - } + return Abs ? new ShaderIrOperOp(ShaderIrInst.Fabs, Node) : Node; } - private static void EmitAluNeg(ShaderIrBlock Block, bool Neg) + private static ShaderIrOper GetAluNeg(ShaderIrOper Node, bool Neg) { - if (Neg) - { - Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg)); - } - } - - private static void EmitAluOperANode(ShaderIrBlock Block, long OpCode) - { - Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 8) & 0xff)); - } - - private static void EmitAluOperBNode_RR(ShaderIrBlock Block, long OpCode) - { - Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 20) & 0xff)); - } - - private static void EmitAluOperBCNode_R(ShaderIrBlock Block, long OpCode) - { - Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 39) & 0xff)); - } - - private static void EmitAluOperBCNode_C(ShaderIrBlock Block, long OpCode) - { - Block.AddNode(new ShaderIrNodeLdb( - (int)(OpCode >> 34) & 0x1f, - (int)(OpCode >> 20) & 0x3fff)); - } - - private static void EmitAluOperBNode_Imm(ShaderIrBlock Block, long OpCode) - { - //TODO - } - - private static void EmitAluStrResult(ShaderIrBlock Block, long OpCode) - { - Block.AddNode(new ShaderIrNodeStr((int)(OpCode >> 0) & 0xff)); + return Neg ? new ShaderIrOperOp(ShaderIrInst.Fneg, Node) : Node; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs new file mode 100644 index 0000000000..0a01bb2d3c --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -0,0 +1,54 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + static class ShaderDecodeHelper + { + public static ShaderIrOper[] GetAluOperANode_A(long OpCode) + { + int Abuf = (int)(OpCode >> 20) & 0x3ff; + int Reg = (int)(OpCode >> 39) & 0xff; + int Size = (int)(OpCode >> 47) & 3; + + ShaderIrOper[] Opers = new ShaderIrOper[Size + 1]; + + for (int Index = 0; Index <= Size; Index++) + { + Opers[Index] = new ShaderIrOperAbuf(Abuf, (Reg + Index) & 0xff); + } + + return Opers; + } + + public static ShaderIrOper GetAluOperANode_R(long OpCode) + { + return new ShaderIrOperReg((int)(OpCode >> 8) & 0xff); + } + + public static ShaderIrOper GetAluOperBNode_RR(long OpCode) + { + return new ShaderIrOperReg((int)(OpCode >> 20) & 0xff); + } + + public static ShaderIrOper GetAluOperBCNode_R(long OpCode) + { + return new ShaderIrOperReg((int)(OpCode >> 39) & 0xff); + } + + public static ShaderIrOper GetAluOperBCNode_C(long OpCode) + { + return new ShaderIrOperCbuf( + (int)(OpCode >> 34) & 0x1f, + (int)(OpCode >> 20) & 0x3fff); + } + + public static ShaderIrOper GetAluOperDNode(long OpCode) + { + return new ShaderIrOperReg((int)(OpCode >> 0) & 0xff); + } + + public static ShaderIrOper GetAluOperBNode_Imm(long OpCode) + { + //TODO + return new ShaderIrOper(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs new file mode 100644 index 0000000000..7341518c6a --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -0,0 +1,29 @@ +using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static partial class ShaderDecode + { + public static void Ld_A(ShaderIrBlock Block, long OpCode) + { + ShaderIrOper[] Opers = GetAluOperANode_A(OpCode); + ShaderIrOper OperD = GetAluOperDNode(OpCode); + + foreach (ShaderIrOper OperA in Opers) + { + Block.AddNode(new ShaderIrNode(OperD, OperA)); + } + } + + public static void St_A(ShaderIrBlock Block, long OpCode) + { + ShaderIrOper[] Opers = GetAluOperANode_A(OpCode); + ShaderIrOper OperD = GetAluOperDNode(OpCode); + + foreach (ShaderIrOper OperA in Opers) + { + Block.AddNode(new ShaderIrNode(OperA, OperD)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index cbf89a7c65..f2c2a1dc3d 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gal.Shader Decode(Block, OpCode); } + Block.RunOptimizationPasses(); + return Block; } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index c4a2c335fc..cf4bf961e2 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -16,6 +16,11 @@ namespace Ryujinx.Graphics.Gal.Shader Nodes.Add(Node); } + public void RunOptimizationPasses() + { + ShaderOptExprProp.Optimize(Nodes); + } + public ShaderIrNode[] GetNodes() { return Nodes.ToArray(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 665c3db3f2..626bfa3656 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -4,9 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader { Fabs, Fadd, + Ffma, Fmul, Fneg, - Ld, - St } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs index 6d97b3c525..9069047c28 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs @@ -2,11 +2,13 @@ namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrNode { - public ShaderIrInst Inst; + public ShaderIrOper Dst { get; set; } + public ShaderIrOper Src { get; set; } - public ShaderIrNode(ShaderIrInst Inst) + public ShaderIrNode(ShaderIrOper Dst, ShaderIrOper Src) { - this.Inst = Inst; + this.Dst = Dst; + this.Src = Src; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs deleted file mode 100644 index 6561792d85..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrNodeLdb : ShaderIrNode - { - public int Cbuf { get; private set; } - public int Offs { get; private set; } - - public ShaderIrNodeLdb(int Cbuf, int Offs) : base(ShaderIrInst.Ld) - { - this.Cbuf = Cbuf; - this.Offs = Offs; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs deleted file mode 100644 index e19b325e4a..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrNodeLdr : ShaderIrNode - { - public int GprIndex { get; private set; } - - public ShaderIrNodeLdr(int GprIndex) : base(ShaderIrInst.Ld) - { - this.GprIndex = GprIndex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs deleted file mode 100644 index 31c396acc4..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrNodeStr : ShaderIrNode - { - public int GprIndex { get; private set; } - - public ShaderIrNodeStr(int GprIndex) : base(ShaderIrInst.St) - { - this.GprIndex = GprIndex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs new file mode 100644 index 0000000000..2817f315b2 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperAbuf : ShaderIrOper + { + public int Offs { get; private set; } + public int GprIndex { get; private set; } + + public ShaderIrOperAbuf(int Offs, int GprIndex) + { + this.Offs = Offs; + this.GprIndex = GprIndex; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs new file mode 100644 index 0000000000..ab8e1a3839 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperCbuf : ShaderIrOper + { + public int Index { get; private set; } + public int Offs { get; private set; } + + public ShaderIrOperCbuf(int Index, int Offs) + { + this.Index = Index; + this.Offs = Offs; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs new file mode 100644 index 0000000000..dfc8f243b0 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperImm : ShaderIrOper + { + public int Imm { get; private set; } + + public ShaderIrOperImm(int Imm) + { + this.Imm = Imm; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs new file mode 100644 index 0000000000..993441734c --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperImmf : ShaderIrOper + { + public float Imm { get; private set; } + + public ShaderIrOperImmf(float Imm) + { + this.Imm = Imm; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs new file mode 100644 index 0000000000..ad9e02ea36 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperOp : ShaderIrOper + { + public ShaderIrInst Inst { get; private set; } + public ShaderIrOper OperandA { get; set; } + public ShaderIrOper OperandB { get; set; } + public ShaderIrOper OperandC { get; set; } + + public ShaderIrOperOp( + ShaderIrInst Inst, + ShaderIrOper OperandA = null, + ShaderIrOper OperandB = null, + ShaderIrOper OperandC = null) + { + this.Inst = Inst; + this.OperandA = OperandA; + this.OperandB = OperandB; + this.OperandC = OperandC; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 56a9376f6b..19a1583663 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0101110001101x", ShaderDecode.Fmul_R); Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_Imm); + Set("1110111111011x", ShaderDecode.Ld_A); + Set("1110111111110x", ShaderDecode.St_A); #endregion } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs new file mode 100644 index 0000000000..3a4379751d --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static class ShaderOptExprProp + { + private struct UseSite + { + public object Parent; + + public int OperIndex; + + public UseSite(object Parent, int OperIndex) + { + this.Parent = Parent; + this.OperIndex = OperIndex; + } + } + + private class RegUse + { + public ShaderIrNode Node { get; private set; } + + private List Sites; + + public RegUse() + { + Sites = new List(); + } + + public void AddUseSite(UseSite Site) + { + Sites.Add(Site); + } + + public bool TryPropagate() + { + //If the use count of the register is more than 1, + //then propagating the expression is not worth it, + //because the code will be larger, harder to read, + //and less efficient due to the common sub-expression being + //propagated. + if (Sites.Count == 1 || !(Node.Src is ShaderIrOperOp)) + { + foreach (UseSite Site in Sites) + { + if (Site.Parent is ShaderIrOperOp Op) + { + switch (Site.OperIndex) + { + case 0: Op.OperandA = Node.Src; break; + case 1: Op.OperandB = Node.Src; break; + case 2: Op.OperandC = Node.Src; break; + + default: throw new InvalidOperationException(); + } + } + else if (Site.Parent is ShaderIrNode SiteNode) + { + SiteNode.Src = Node.Src; + } + else + { + throw new InvalidOperationException(); + } + } + + return true; + } + + return Sites.Count == 0; + } + + public void SetNewAsg(ShaderIrNode Node) + { + this.Node = Node; + + Sites.Clear(); + } + } + + public static void Optimize(List Nodes) + { + Dictionary Uses = new Dictionary(); + + RegUse GetRegUse(int GprIndex) + { + RegUse Use; + + if (!Uses.TryGetValue(GprIndex, out Use)) + { + Use = new RegUse(); + + Uses.Add(GprIndex, Use); + } + + return Use; + } + + void TryAddRegUse(object Parent, ShaderIrOper Oper, int OperIndex = 0) + { + if (Oper is ShaderIrOperOp Op) + { + TryAddRegUse(Op, Op.OperandA, 0); + TryAddRegUse(Op, Op.OperandB, 1); + TryAddRegUse(Op, Op.OperandC, 2); + } + else if (Oper is ShaderIrOperReg Reg && Reg.GprIndex != 0xff) + { + GetRegUse(Reg.GprIndex).AddUseSite(new UseSite(Parent, OperIndex)); + } + } + + for (int Index = 0; Index < Nodes.Count; Index++) + { + ShaderIrNode Node = Nodes[Index]; + + if (Node.Src is ShaderIrOperOp Op) + { + TryAddRegUse(Node, Op); + } + else if (Node.Src is ShaderIrOperReg) + { + TryAddRegUse(Node, Node.Src); + } + + if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex != 0xff) + { + RegUse Use = GetRegUse(Reg.GprIndex); + + if (Use.Node != null && Use.TryPropagate()) + { + Nodes.Remove(Use.Node); + + Index--; + } + + Use.SetNewAsg(Node); + } + } + + foreach (RegUse Use in Uses.Values) + { + if (Use.TryPropagate()) + { + Nodes.Remove(Use.Node); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs index 8c2bd6403c..916d85a4b9 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; + namespace Ryujinx.Graphics.Gal.Shader { public static class ShaderTest @@ -26,25 +29,123 @@ namespace Ryujinx.Graphics.Gal.Shader foreach (ShaderIrNode Node in Nodes) { - System.Console.Write(Node.Inst); - - if (Node is ShaderIrNodeLdr LdrNode) - { - System.Console.Write($" r{LdrNode.GprIndex}"); - } - else if (Node is ShaderIrNodeStr StrNode) - { - System.Console.Write($" r{StrNode.GprIndex}"); - } - else if (Node is ShaderIrNodeLdb LdbNode) - { - System.Console.Write($" c{LdbNode.Cbuf}[0x{LdbNode.Offs.ToString("x")}]"); - } - - System.Console.WriteLine(string.Empty); + 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)}"; + } } } \ No newline at end of file