diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 804d64f64e..d65a33f5a1 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -6,10 +6,14 @@ namespace Ryujinx.Graphics.Gal.Shader { class GlslDecompiler { - private delegate string GetInstExpr(ShaderIrOperOp Op); + private delegate string GetInstExpr(ShaderIrOp Op); private Dictionary InstsExpr; + private const string IdentationStr = " "; + + private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; + private class Attrib { public string Name; @@ -20,9 +24,7 @@ namespace Ryujinx.Graphics.Gal.Shader this.Name = Name; this.Elems = Elems; } - } - - private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; + } private SortedDictionary InputAttributes; private SortedDictionary OutputAttributes; @@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Gal.Shader private HashSet UsedCbufs; private const int AttrStartIndex = 8; + private const int TexStartIndex = 8; private const string InputAttrPrefix = "in_attr"; private const string OutputAttrPrefix = "out_attr"; @@ -37,11 +40,13 @@ namespace Ryujinx.Graphics.Gal.Shader private const string CbufBuffPrefix = "c"; private const string CbufDataName = "buf"; - private const string GprName = "gpr"; - - private const string IdentationStr = "\t"; + private const string GprName = "gpr"; + private const string PredName = "pred"; + private const string SampName = "samp"; private int GprsCount; + private int PredsCount; + private int SampsCount; private StringBuilder BodySB; @@ -49,15 +54,35 @@ namespace Ryujinx.Graphics.Gal.Shader { InstsExpr = new Dictionary() { + { ShaderIrInst.Band, GetBandExpr }, + { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Clt, GetCltExpr }, + { ShaderIrInst.Ceq, GetCeqExpr }, + { ShaderIrInst.Cle, GetCleExpr }, + { ShaderIrInst.Cgt, GetCgtExpr }, + { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Cge, GetCgeExpr }, { ShaderIrInst.Fabs, GetFabsExpr }, { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fcos, GetFcosExpr }, + { ShaderIrInst.Fex2, GetFex2Expr }, { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Flg2, GetFlg2Expr }, { ShaderIrInst.Fmul, GetFmulExpr }, - { ShaderIrInst.Fneg, GetFnegExpr } + { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Frcp, GetFrcpExpr }, + { ShaderIrInst.Frsq, GetFrsqExpr }, + { ShaderIrInst.Fsin, GetFsinExpr }, + { ShaderIrInst.Ipa, GetIpaExpr }, + { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Texr, GetTexrExpr }, + { ShaderIrInst.Texg, GetTexgExpr }, + { ShaderIrInst.Texb, GetTexbExpr }, + { ShaderIrInst.Texa, GetTexaExpr } }; } - public string Decompile(int[] Code) + public string Decompile(int[] Code, ShaderType Type) { InputAttributes = new SortedDictionary(); OutputAttributes = new SortedDictionary(); @@ -67,13 +92,20 @@ namespace Ryujinx.Graphics.Gal.Shader BodySB = new StringBuilder(); //FIXME: Only valid for vertex shaders. - OutputAttributes.Add(7, new Attrib("gl_Position", 4)); + if (Type == ShaderType.Fragment) + { + OutputAttributes.Add(7, new Attrib("FragColor", 4)); + } + else + { + OutputAttributes.Add(7, new Attrib("gl_Position", 4)); + } - ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0); + ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, Type); ShaderIrNode[] Nodes = Block.GetNodes(); - PrintBlockScope(Nodes, "void main()", 1); + PrintBlockScope("void main()", 1, Nodes); StringBuilder SB = new StringBuilder(); @@ -83,12 +115,16 @@ namespace Ryujinx.Graphics.Gal.Shader PrintDeclInAttributes(SB); PrintDeclOutAttributes(SB); - if (GprsCount > 0) + if (Type == ShaderType.Fragment) { - SB.AppendLine($"float {GprName}[{GprsCount}];"); - SB.AppendLine(string.Empty); + SB.AppendLine($"out vec4 {OutputAttributes[7].Name};"); + SB.AppendLine(); } + PrintDeclSamplers(SB); + PrintDeclGprs(SB); + PrintDeclPreds(SB); + SB.Append(BodySB.ToString()); BodySB.Clear(); @@ -103,12 +139,14 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine($"layout(std430, binding = {Cbuf}) buffer {CbufBuffPrefix}{Cbuf} {{"); SB.AppendLine($"{IdentationStr}float {CbufDataName}[];"); SB.AppendLine("};"); - SB.AppendLine(string.Empty); + SB.AppendLine(); } } private void PrintDeclInAttributes(StringBuilder SB) { + bool PrintNl = false; + foreach (KeyValuePair KV in InputAttributes) { int Index = KV.Key - AttrStartIndex; @@ -118,14 +156,21 @@ namespace Ryujinx.Graphics.Gal.Shader string Type = ElemTypes[KV.Value.Elems]; SB.AppendLine($"layout(location = {Index}) in {Type} {KV.Value.Name};"); + + PrintNl = true; } } - SB.AppendLine(string.Empty); + if (PrintNl) + { + SB.AppendLine(); + } } private void PrintDeclOutAttributes(StringBuilder SB) { + bool PrintNl = false; + foreach (KeyValuePair KV in OutputAttributes) { int Index = KV.Key - AttrStartIndex; @@ -135,13 +180,45 @@ namespace Ryujinx.Graphics.Gal.Shader string Type = ElemTypes[KV.Value.Elems]; SB.AppendLine($"layout(location = {Index}) out {Type} {KV.Value.Name};"); + + PrintNl = true; } } - SB.AppendLine(string.Empty); + if (PrintNl) + { + SB.AppendLine(); + } } - private void PrintBlockScope(ShaderIrNode[] Nodes, string ScopeName, int IdentationLevel) + private void PrintDeclSamplers(StringBuilder SB) + { + if (SampsCount > 0) + { + SB.AppendLine($"uniform sampler2D {SampName}[{SampsCount}];"); + SB.AppendLine(); + } + } + + private void PrintDeclGprs(StringBuilder SB) + { + if (GprsCount > 0) + { + SB.AppendLine($"float {GprName}[{GprsCount}];"); + SB.AppendLine(); + } + } + + private void PrintDeclPreds(StringBuilder SB) + { + if (PredsCount > 0) + { + SB.AppendLine($"bool {PredName}[{PredsCount}];"); + SB.AppendLine(); + } + } + + private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes) { string Identation = string.Empty; @@ -164,43 +241,80 @@ namespace Ryujinx.Graphics.Gal.Shader Identation += IdentationStr; } - foreach (ShaderIrNode Node in Nodes) + for (int Index = 0; Index < Nodes.Length; Index++) { - if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex == ShaderIrOperReg.ZRIndex) - { - continue; - } + ShaderIrNode Node = Nodes[Index]; - BodySB.AppendLine(Identation + - $"{GetOOperName(Node.Dst)} = " + - $"{GetIOperName(Node.Src, true)};"); + if (Node is ShaderIrCond Cond) + { + string SubScopeName = $"if ({GetInOperName(Cond.Pred, true)})"; + + PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); + } + else if (Node is ShaderIrAsg Asg) + { + if (IsValidOutOper(Asg.Dst)) + { + BodySB.AppendLine(Identation + + $"{GetOutOperName(Asg.Dst)} = " + + $"{GetInOperName (Asg.Src, true)};"); + } + } + else if (Node is ShaderIrOp Op) + { + BodySB.AppendLine($"{Identation}{GetInOperName(Op, true)};"); + } + else + { + throw new InvalidOperationException(); + } } BodySB.AppendLine(LastLine); } - private string GetOOperName(ShaderIrOper Oper) + private bool IsValidOutOper(ShaderIrNode Node) { - if (Oper is ShaderIrOperAbuf Abuf) + if (Node is ShaderIrOperGpr Gpr && Gpr.Index == ShaderIrOperGpr.ZRIndex) { - return GetOAbufName(Abuf); + return false; } - else if (Oper is ShaderIrOperReg Reg) + else if (Node is ShaderIrOperPred Pred && Pred.Index == ShaderIrOperPred.UnusedIndex) { - return GetRegName(Reg); + return false; } - throw new ArgumentException(nameof(Oper)); + return true; } - private string GetIOperName(ShaderIrOper Oper, bool Entry = false) + private string GetOutOperName(ShaderIrNode Node) { - switch (Oper) + if (Node is ShaderIrOperAbuf Abuf) { - case ShaderIrOperAbuf Abuf: return GetIAbufName(Abuf); - case ShaderIrOperCbuf Cbuf: return GetCbufName(Cbuf); - case ShaderIrOperReg Reg: return GetRegName(Reg); - case ShaderIrOperOp Op: + return GetOutAbufName(Abuf); + } + else if (Node is ShaderIrOperGpr Gpr) + { + return GetName(Gpr); + } + else if (Node is ShaderIrOperPred Pred) + { + return GetName(Pred); + } + + throw new ArgumentException(nameof(Node)); + } + + private string GetInOperName(ShaderIrNode Node, bool Entry = false) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: return GetName(Abuf); + case ShaderIrOperCbuf Cbuf: return GetName(Cbuf); + case ShaderIrOperGpr Gpr: return GetName(Gpr); + case ShaderIrOperPred Pred: return GetName(Pred); + + case ShaderIrOp Op: string Expr; if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) @@ -219,17 +333,29 @@ namespace Ryujinx.Graphics.Gal.Shader return Expr; - default: throw new ArgumentException(nameof(Oper)); + default: throw new ArgumentException(nameof(Node)); } } private bool IsUnary(ShaderIrInst Inst) { - return Inst == ShaderIrInst.Fabs || - Inst == ShaderIrInst.Fneg; + return Inst == ShaderIrInst.Bnot || + Inst == ShaderIrInst.Fabs || + Inst == ShaderIrInst.Fcos || + Inst == ShaderIrInst.Fex2 || + Inst == ShaderIrInst.Flg2 || + Inst == ShaderIrInst.Fneg || + Inst == ShaderIrInst.Frcp || + Inst == ShaderIrInst.Frsq || + Inst == ShaderIrInst.Fsin || + Inst == ShaderIrInst.Ipa || + Inst == ShaderIrInst.Texr || + Inst == ShaderIrInst.Texg || + Inst == ShaderIrInst.Texb || + Inst == ShaderIrInst.Texa; } - private string GetOAbufName(ShaderIrOperAbuf Abuf) + private string GetOutAbufName(ShaderIrOperAbuf Abuf) { int AttrIndex = Abuf.Offs >> 4; @@ -250,7 +376,7 @@ namespace Ryujinx.Graphics.Gal.Shader return $"{Attr.Name}.{GetAttrSwizzle(Elem)}"; } - private string GetIAbufName(ShaderIrOperAbuf Abuf) + private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true) { int AttrIndex = Abuf.Offs >> 4; @@ -268,63 +394,224 @@ namespace Ryujinx.Graphics.Gal.Shader Attr.Elems = Elem; } - return $"{Attr.Name}.{GetAttrSwizzle(Elem)}"; + return Attr.Name + (Swizzle ? $".{GetAttrSwizzle(Elem)}" : string.Empty); } - private string GetAttrSwizzle(int Elem) - { - return "xyzw".Substring(Elem, 1); - } - - private string GetCbufName(ShaderIrOperCbuf Cbuf) + private string GetName(ShaderIrOperCbuf Cbuf) { UsedCbufs.Add(Cbuf.Index); return $"{CbufBuffPrefix}{Cbuf.Index}.{CbufDataName}[{Cbuf.Offs}]"; } - private string GetRegName(ShaderIrOperReg Reg) + private string GetName(ShaderIrOperGpr Gpr) { - if (GprsCount < Reg.GprIndex + 1) + if (GprsCount < Gpr.Index + 1) { - GprsCount = Reg.GprIndex + 1; + GprsCount = Gpr.Index + 1; } - return GetRegName(Reg.GprIndex); + return GetRegName(Gpr.Index); + } + + private string GetName(ShaderIrOperPred Pred) + { + if (PredsCount < Pred.Index + 1) + { + PredsCount = Pred.Index + 1; + } + + return GetPredName(Pred.Index); } private string GetRegName(int GprIndex) { - return GprIndex == ShaderIrOperReg.ZRIndex ? "0" : $"{GprName}[{GprIndex}]"; + return GprIndex == ShaderIrOperGpr.ZRIndex ? "0" : $"{GprName}[{GprIndex}]"; } - private string GetFabsExpr(ShaderIrOperOp Op) + private string GetPredName(int PredIndex) { - return $"abs({GetIOperName(Op.OperandA)})"; + return PredIndex == ShaderIrOperPred.UnusedIndex ? "true" : $"{PredName}[{PredIndex}]"; } - private string GetFaddExpr(ShaderIrOperOp Op) + private string GetBandExpr(ShaderIrOp Op) { - return $"{GetIOperName(Op.OperandA)} + " + - $"{GetIOperName(Op.OperandB)}"; + return $"{GetInOperName(Op.OperandA)} && " + + $"{GetInOperName(Op.OperandB)}"; } - private string GetFfmaExpr(ShaderIrOperOp Op) + private string GetBnotExpr(ShaderIrOp Op) { - return $"{GetIOperName(Op.OperandA)} * " + - $"{GetIOperName(Op.OperandB)} + " + - $"{GetIOperName(Op.OperandC)}"; + return $"!{GetInOperName(Op.OperandA)}"; } - private string GetFmulExpr(ShaderIrOperOp Op) + private string GetCltExpr(ShaderIrOp Op) { - return $"{GetIOperName(Op.OperandA)} * " + - $"{GetIOperName(Op.OperandB)}"; + return $"{GetInOperName(Op.OperandA)} < " + + $"{GetInOperName(Op.OperandB)}"; } - private string GetFnegExpr(ShaderIrOperOp Op) + private string GetCeqExpr(ShaderIrOp Op) { - return $"-{GetIOperName(Op.OperandA)}"; + return $"{GetInOperName(Op.OperandA)} == " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetCleExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} <= " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetCgtExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} > " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetCneExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} != " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetCgeExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} >= " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetFabsExpr(ShaderIrOp Op) + { + return $"abs({GetInOperName(Op.OperandA)})"; + } + + private string GetFaddExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} + " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetFcosExpr(ShaderIrOp Op) + { + return $"cos({GetInOperName(Op.OperandA)})"; + } + + private string GetFex2Expr(ShaderIrOp Op) + { + return $"exp2({GetInOperName(Op.OperandA)})"; + } + + private string GetFfmaExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} * " + + $"{GetInOperName(Op.OperandB)} + " + + $"{GetInOperName(Op.OperandC)}"; + } + + private string GetFlg2Expr(ShaderIrOp Op) + { + return $"log2({GetInOperName(Op.OperandA)})"; + } + + private string GetFmulExpr(ShaderIrOp Op) + { + return $"{GetInOperName(Op.OperandA)} * " + + $"{GetInOperName(Op.OperandB)}"; + } + + private string GetFnegExpr(ShaderIrOp Op) + { + return $"-{GetInOperName(Op.OperandA)}"; + } + + private string GetFrcpExpr(ShaderIrOp Op) + { + return $"1 / {GetInOperName(Op.OperandA)}"; + } + + private string GetFrsqExpr(ShaderIrOp Op) + { + return $"inversesqrt({GetInOperName(Op.OperandA)})"; + } + + private string GetFsinExpr(ShaderIrOp Op) + { + return $"sin({GetInOperName(Op.OperandA)})"; + } + + private string GetIpaExpr(ShaderIrOp Op) + { + return GetInOperName(Op.OperandA); + } + + private string GetKilExpr(ShaderIrOp Op) + { + return "discard"; + } + + private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r'); + private string GetTexgExpr(ShaderIrOp Op) => GetTexExpr(Op, 'g'); + private string GetTexbExpr(ShaderIrOp Op) => GetTexExpr(Op, 'b'); + private string GetTexaExpr(ShaderIrOp Op) => GetTexExpr(Op, 'a'); + + private string GetTexExpr(ShaderIrOp Op, char Ch) + { + return $"texture({GetTexSamplerName(Op)}, {GetTexSamplerCoords(Op)}).{Ch}"; + } + + private string GetTexSamplerName(ShaderIrOp Op) + { + ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; + + int Handle = Node.Imm - TexStartIndex; + + if (SampsCount < Handle + 1) + { + SampsCount = Handle + 1; + } + + return $"{SampName}[{Handle}]"; + } + + private string GetTexSamplerCoords(ShaderIrOp Op) + { + if (GetInnerNode(Op.OperandA) is ShaderIrOperAbuf AAbuf && + GetInnerNode(Op.OperandB) is ShaderIrOperAbuf BAbuf) + { + if (AAbuf.GprIndex == ShaderIrOperGpr.ZRIndex && + BAbuf.GprIndex == ShaderIrOperGpr.ZRIndex && + (AAbuf.Offs >> 4) == (BAbuf.Offs >> 4)) + { + string AttrName = GetName(AAbuf, Swizzle: false); + + //Needs to call this to ensure it registers all elements used. + GetName(BAbuf); + + return $"{AttrName}." + + $"{GetAttrSwizzle((AAbuf.Offs >> 2) & 3)}" + + $"{GetAttrSwizzle((BAbuf.Offs >> 2) & 3)}"; + } + } + + return "vec2(" + + $"{GetInOperName(Op.OperandA)}, " + + $"{GetInOperName(Op.OperandB)})"; + } + + private ShaderIrNode GetInnerNode(ShaderIrNode Node) + { + if (Node is ShaderIrOp Op && Op.Inst == ShaderIrInst.Ipa) + { + return Op.OperandA; + } + + return Node; + } + + private string GetAttrSwizzle(int Elem) + { + return "xyzw".Substring(Elem, 1); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index be282fd8f5..d4c3e16b54 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -64,6 +64,83 @@ namespace Ryujinx.Graphics.Gal.Shader EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Fmul); } + public static void Fsetp_C(ShaderIrBlock Block, long OpCode) + { + bool Aa = ((OpCode >> 7) & 1) != 0; + bool Na = ((OpCode >> 43) & 1) != 0; + bool Ab = ((OpCode >> 44) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperCbuf34(OpCode); + + ShaderIrInst CmpInst = GetCmp(OpCode); + + ShaderIrOp Op = new ShaderIrOp(CmpInst, + GetAluAbsNeg(OperA, Aa, Na), + GetAluAbs (OperB, Ab)); + + ShaderIrOperPred P0Node = GetOperPred3 (OpCode); + ShaderIrOperPred P1Node = GetOperPred0 (OpCode); + ShaderIrOperPred P2Node = GetOperPred39(OpCode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); + + ShaderIrInst LopInst = GetBLop(OpCode); + + if (LopInst == ShaderIrInst.Band && + P1Node.Index == ShaderIrOperPred.UnusedIndex && + P2Node.Index == ShaderIrOperPred.UnusedIndex) + { + return; + } + + ShaderIrNode P2NNode = GetOperPred39N(OpCode); + + Op = new ShaderIrOp(LopInst, new ShaderIrOp(ShaderIrInst.Bnot, P0Node), P2NNode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode)); + + Op = new ShaderIrOp(LopInst, P0Node, P2NNode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); + } + + public static void Ipa(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperAbuf28(OpCode); + ShaderIrNode OperB = GetOperGpr20 (OpCode); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + public static void Mufu(ShaderIrBlock Block, long OpCode) + { + int SubOp = (int)(OpCode >> 20) & 7; + + bool Aa = ((OpCode >> 46) & 1) != 0; + bool Na = ((OpCode >> 48) & 1) != 0; + + ShaderIrInst Inst = 0; + + switch (SubOp) + { + case 0: Inst = ShaderIrInst.Fcos; break; + case 1: Inst = ShaderIrInst.Fsin; break; + case 2: Inst = ShaderIrInst.Fex2; break; + case 3: Inst = ShaderIrInst.Flg2; break; + case 4: Inst = ShaderIrInst.Frcp; break; + case 5: Inst = ShaderIrInst.Frsq; break; + } + + ShaderIrNode OperA = GetOperGpr8(OpCode); + + ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na)); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + private static void EmitAluBinary( ShaderIrBlock Block, long OpCode, @@ -76,8 +153,7 @@ namespace Ryujinx.Graphics.Gal.Shader bool Ab = ((OpCode >> 49) & 1) != 0; bool Ad = ((OpCode >> 50) & 1) != 0; - ShaderIrOper OperA = GetAluOperANode_R(OpCode); - ShaderIrOper OperB; + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; if (Inst == ShaderIrInst.Fadd) { @@ -86,18 +162,20 @@ namespace Ryujinx.Graphics.Gal.Shader switch (Oper) { - case ShaderOper.RR: OperB = GetAluOperBNode_RR (OpCode); break; - case ShaderOper.CR: OperB = GetAluOperBCNode_C (OpCode); break; - case ShaderOper.Imm: OperB = GetAluOperBNode_Imm(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImmf19_20(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } OperB = GetAluAbsNeg(OperB, Ab, Nb); - ShaderIrOper Op = GetAluAbs(new ShaderIrOperOp(Inst, OperA, OperB), Ad); + ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); - Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op)); + Op = GetAluAbs(Op, Ad); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) @@ -105,16 +183,14 @@ namespace Ryujinx.Graphics.Gal.Shader bool Nb = ((OpCode >> 48) & 1) != 0; bool Nc = ((OpCode >> 49) & 1) != 0; - ShaderIrOper OperA = GetAluOperANode_R(OpCode); - ShaderIrOper OperB; - ShaderIrOper OperC; + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC; switch (Oper) { - 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; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.RC: OperB = GetOperGpr39 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImmf19_20(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } @@ -123,31 +199,31 @@ namespace Ryujinx.Graphics.Gal.Shader if (Oper == ShaderOper.RC) { - OperC = GetAluNeg(GetAluOperBCNode_C(OpCode), Nc); + OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc); } else { - OperC = GetAluNeg(GetAluOperBCNode_R(OpCode), Nc); + OperC = GetAluNeg(GetOperGpr39(OpCode), Nc); } - ShaderIrOper Op = new ShaderIrOperOp(ShaderIrInst.Ffma, OperA, OperB, OperC); + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC); - Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op)); + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } - private static ShaderIrOper GetAluAbsNeg(ShaderIrOper Node, bool Abs, bool Neg) + private static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg) { return GetAluNeg(GetAluAbs(Node, Abs), Neg); } - private static ShaderIrOper GetAluAbs(ShaderIrOper Node, bool Abs) + private static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs) { - return Abs ? new ShaderIrOperOp(ShaderIrInst.Fabs, Node) : Node; + return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node; } - private static ShaderIrOper GetAluNeg(ShaderIrOper Node, bool Neg) + private static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg) { - return Neg ? new ShaderIrOperOp(ShaderIrInst.Fneg, Node) : Node; + return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs new file mode 100644 index 0000000000..7d6796be27 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -0,0 +1,12 @@ +using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static partial class ShaderDecode + { + public static void Kil(ShaderIrBlock Block, long OpCode) + { + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index 96452bbdbb..23330dce77 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -1,8 +1,10 @@ +using System; + namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecodeHelper { - public static ShaderIrOperAbuf[] GetAluOperANode_A(long OpCode) + public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode) { int Abuf = (int)(OpCode >> 20) & 0x3ff; int Reg = (int)(OpCode >> 39) & 0xff; @@ -18,37 +20,141 @@ namespace Ryujinx.Graphics.Gal.Shader return Opers; } - public static ShaderIrOperReg GetAluOperANode_R(long OpCode) + public static ShaderIrOperAbuf GetOperAbuf28(long OpCode) { - return new ShaderIrOperReg((int)(OpCode >> 8) & 0xff); + int Abuf = (int)(OpCode >> 28) & 0x3ff; + int Reg = (int)(OpCode >> 39) & 0xff; + + return new ShaderIrOperAbuf(Abuf, Reg); } - public static ShaderIrOperReg GetAluOperBNode_RR(long OpCode) - { - return new ShaderIrOperReg((int)(OpCode >> 20) & 0xff); - } - - public static ShaderIrOperReg GetAluOperBCNode_R(long OpCode) - { - return new ShaderIrOperReg((int)(OpCode >> 39) & 0xff); - } - - public static ShaderIrOperCbuf GetAluOperBCNode_C(long OpCode) + public static ShaderIrOperCbuf GetOperCbuf34(long OpCode) { return new ShaderIrOperCbuf( (int)(OpCode >> 34) & 0x1f, (int)(OpCode >> 20) & 0x3fff); } - public static ShaderIrOperReg GetAluOperDNode(long OpCode) + public static ShaderIrOperGpr GetOperGpr8(long OpCode) { - return new ShaderIrOperReg((int)(OpCode >> 0) & 0xff); + return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff); } - public static ShaderIrOper GetAluOperBNode_Imm(long OpCode) + public static ShaderIrOperGpr GetOperGpr20(long OpCode) + { + return new ShaderIrOperGpr((int)(OpCode >> 20) & 0xff); + } + + public static ShaderIrOperGpr GetOperGpr39(long OpCode) + { + return new ShaderIrOperGpr((int)(OpCode >> 39) & 0xff); + } + + public static ShaderIrOperGpr GetOperGpr0(long OpCode) + { + return new ShaderIrOperGpr((int)(OpCode >> 0) & 0xff); + } + + public static ShaderIrOperGpr GetOperGpr28(long OpCode) + { + return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff); + } + + public static ShaderIrNode GetOperImmf19_20(long OpCode) { //TODO - return new ShaderIrOper(); + return new ShaderIrNode(); + } + + public static ShaderIrOperImm GetOperImm13_36(long OpCode) + { + return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff); + } + + public static ShaderIrOperPred GetOperPred3(long OpCode) + { + return new ShaderIrOperPred((int)(OpCode >> 3) & 7); + } + + public static ShaderIrOperPred GetOperPred0(long OpCode) + { + return new ShaderIrOperPred((int)(OpCode >> 0) & 7); + } + + public static ShaderIrNode GetOperPred39N(long OpCode) + { + ShaderIrNode Node = GetOperPred39(OpCode); + + if (((OpCode >> 42) & 1) != 0) + { + Node = new ShaderIrOp(ShaderIrInst.Bnot, Node); + } + + return Node; + } + + public static ShaderIrOperPred GetOperPred39(long OpCode) + { + return new ShaderIrOperPred((int)(OpCode >> 39) & 7); + } + + public static ShaderIrInst GetCmp(long OpCode) + { + switch ((int)(OpCode >> 48) & 0xf) + { + case 0x1: return ShaderIrInst.Clt; + case 0x2: return ShaderIrInst.Ceq; + case 0x3: return ShaderIrInst.Cle; + case 0x4: return ShaderIrInst.Cgt; + case 0x5: return ShaderIrInst.Cne; + case 0x6: return ShaderIrInst.Cge; + case 0x7: return ShaderIrInst.Cnum; + case 0x8: return ShaderIrInst.Cnan; + case 0x9: return ShaderIrInst.Cltu; + case 0xa: return ShaderIrInst.Cequ; + case 0xb: return ShaderIrInst.Cleu; + case 0xc: return ShaderIrInst.Cgtu; + case 0xd: return ShaderIrInst.Cneu; + case 0xe: return ShaderIrInst.Cgeu; + } + + throw new ArgumentException(nameof(OpCode)); + } + + public static ShaderIrInst GetBLop(long OpCode) + { + switch ((int)(OpCode >> 45) & 3) + { + case 0: return ShaderIrInst.Band; + case 1: return ShaderIrInst.Bor; + case 2: return ShaderIrInst.Bxor; + } + + throw new ArgumentException(nameof(OpCode)); + } + + public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode) + { + ShaderIrOperPred Pred = GetPredNode(OpCode); + + if (Pred.Index != ShaderIrOperPred.UnusedIndex) + { + Node = new ShaderIrCond(Pred, Node); + } + + return Node; + } + + private static ShaderIrOperPred GetPredNode(long OpCode) + { + int Pred = (int)(OpCode >> 16) & 0xf; + + if (Pred != 0xf) + { + Pred &= 7; + } + + return new ShaderIrOperPred(Pred); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index b2f6bf8e8d..fd18ce0771 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -6,34 +6,54 @@ namespace Ryujinx.Graphics.Gal.Shader { public static void Ld_A(ShaderIrBlock Block, long OpCode) { - ShaderIrOper[] Opers = GetAluOperANode_A(OpCode); + ShaderIrNode[] Opers = GetOperAbuf20(OpCode); int Index = 0; - foreach (ShaderIrOper OperA in Opers) + foreach (ShaderIrNode OperA in Opers) { - ShaderIrOperReg OperD = GetAluOperDNode(OpCode); + ShaderIrOperGpr OperD = GetOperGpr0(OpCode); - OperD.GprIndex += Index++; + OperD.Index += Index++; - Block.AddNode(new ShaderIrNode(OperD, OperA)); + Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, OperA), OpCode)); } } public static void St_A(ShaderIrBlock Block, long OpCode) { - ShaderIrOper[] Opers = GetAluOperANode_A(OpCode); + ShaderIrNode[] Opers = GetOperAbuf20(OpCode); int Index = 0; - foreach (ShaderIrOper OperA in Opers) + foreach (ShaderIrNode OperA in Opers) { - ShaderIrOperReg OperD = GetAluOperDNode(OpCode); + ShaderIrOperGpr OperD = GetOperGpr0(OpCode); - OperD.GprIndex += Index++; + OperD.Index += Index++; - Block.AddNode(new ShaderIrNode(OperA, OperD)); + Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, OperD), OpCode)); } } + + public static void Texs(ShaderIrBlock Block, long OpCode) + { + //TODO: Support other formats. + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperGpr20 (OpCode); + ShaderIrNode OperC = GetOperGpr28 (OpCode); + ShaderIrNode OperD = GetOperImm13_36(OpCode); + + for (int Ch = 0; Ch < 4; Ch++) + { + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texr + Ch, OperA, OperB, OperD); + + ShaderIrOperGpr Dst = GetOperGpr0(OpCode); + + Dst.Index += Ch; + + Block.AddNode(new ShaderIrAsg(Dst, Op)); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index f2c2a1dc3d..76d3a6c137 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecoder { - public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset) + public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, ShaderType Type) { ShaderIrBlock Block = new ShaderIrBlock(); @@ -23,6 +23,14 @@ namespace Ryujinx.Graphics.Gal.Shader Decode(Block, OpCode); } + if (Type == ShaderType.Fragment) + { + Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x70, 0), new ShaderIrOperGpr(0))); + Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x74, 0), new ShaderIrOperGpr(1))); + Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x78, 0), new ShaderIrOperGpr(2))); + Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x7c, 0), new ShaderIrOperGpr(3))); + } + Block.RunOptimizationPasses(); return Block; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs new file mode 100644 index 0000000000..00f8f6a5e5 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrAsg : ShaderIrNode + { + public ShaderIrNode Dst { get; set; } + public ShaderIrNode Src { get; set; } + + public ShaderIrAsg(ShaderIrNode Dst, ShaderIrNode Src) + { + this.Dst = Dst; + this.Src = Src; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs new file mode 100644 index 0000000000..d8c87b4907 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrCond : ShaderIrNode + { + public ShaderIrNode Pred { get; set; } + public ShaderIrNode Child { get; set; } + + public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child) + { + this.Pred = Pred; + this.Child = Child; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 626bfa3656..f4826ff095 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -2,10 +2,40 @@ namespace Ryujinx.Graphics.Gal.Shader { enum ShaderIrInst { + Band, + Bnot, + Bor, + Bxor, + Clt, + Ceq, + Cle, + Cgt, + Cne, + Cge, + Cnum, + Cnan, + Cltu, + Cequ, + Cleu, + Cgtu, + Cneu, + Cgeu, Fabs, Fadd, + Fcos, + Fex2, Ffma, + Flg2, Fmul, Fneg, + Frcp, + Frsq, + Fsin, + Ipa, + Kil, + Texr, + Texg, + Texb, + Texa } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs index 9069047c28..2648164a11 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs @@ -1,14 +1,4 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrNode - { - public ShaderIrOper Dst { get; set; } - public ShaderIrOper Src { get; set; } - - public ShaderIrNode(ShaderIrOper Dst, ShaderIrOper Src) - { - this.Dst = Dst; - this.Src = Src; - } - } + class ShaderIrNode { } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs new file mode 100644 index 0000000000..cd2107570c --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOp : ShaderIrNode + { + public ShaderIrInst Inst { get; private set; } + public ShaderIrNode OperandA { get; set; } + public ShaderIrNode OperandB { get; set; } + public ShaderIrNode OperandC { get; set; } + + public ShaderIrOp( + ShaderIrInst Inst, + ShaderIrNode OperandA = null, + ShaderIrNode OperandB = null, + ShaderIrNode 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/ShaderIrOper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOper.cs deleted file mode 100644 index df34955263..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOper.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOper { } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs index 2817f315b2..fa612de76a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrOperAbuf : ShaderIrOper + class ShaderIrOperAbuf : ShaderIrNode { public int Offs { get; private set; } public int GprIndex { get; private set; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs index ab8e1a3839..f227205630 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrOperCbuf : ShaderIrOper + class ShaderIrOperCbuf : ShaderIrNode { public int Index { get; private set; } public int Offs { get; private set; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs new file mode 100644 index 0000000000..c76265f6d5 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperGpr : ShaderIrNode + { + public const int ZRIndex = 0xff; + + public int Index { get; set; } + + public ShaderIrOperGpr(int Index) + { + this.Index = Index; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs index dfc8f243b0..421d55a17a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrOperImm : ShaderIrOper + class ShaderIrOperImm : ShaderIrNode { public int Imm { get; private set; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs index 993441734c..5fe3e00b40 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrOperImmf : ShaderIrOper + class ShaderIrOperImmf : ShaderIrNode { public float Imm { get; private set; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs deleted file mode 100644 index ad9e02ea36..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperOp.cs +++ /dev/null @@ -1,22 +0,0 @@ -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/ShaderIrOperPred.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs new file mode 100644 index 0000000000..a6ca2b2847 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperPred : ShaderIrNode + { + public const int UnusedIndex = 0x7; + public const int NeverExecute = 0xf; + + public int Index { get; set; } + + public ShaderIrOperPred(int Index) + { + this.Index = Index; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs deleted file mode 100644 index 87c70ca714..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperReg : ShaderIrOper - { - public const int ZRIndex = 0xff; - - public int GprIndex { get; set; } - - public ShaderIrOperReg(int GprIndex) - { - this.GprIndex = GprIndex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 19a1583663..77a98d3933 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -23,11 +23,16 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0101110001101x", ShaderDecode.Fmul_R); Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_Imm); + Set("010010111011xx", ShaderDecode.Fsetp_C); + Set("11100000xxxxxx", ShaderDecode.Ipa); + Set("111000110011xx", ShaderDecode.Kil); Set("1110111111011x", ShaderDecode.Ld_A); + Set("0101000010000x", ShaderDecode.Mufu); Set("1110111111110x", ShaderDecode.St_A); + Set("1101100xxxxxxx", ShaderDecode.Texs); #endregion } - + private static void Set(string Encoding, ShaderDecodeFunc Func) { if (Encoding.Length != EncodingBits) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs index 384f8971e8..36bce6d581 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -20,7 +20,11 @@ namespace Ryujinx.Graphics.Gal.Shader private class RegUse { - public ShaderIrNode Node { get; private set; } + public ShaderIrAsg Asg { get; private set; } + + public int AsgIndex { get; private set; } + + private bool Propagate; private List Sites; @@ -36,45 +40,60 @@ namespace Ryujinx.Graphics.Gal.Shader 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)) + //This happens when a untiliazied register is used, + //this usually indicates a decoding error, but may also + //be cased by bogus programs (?). In any case, we just + //keep the unitialized access and avoid trying to propagate + //the expression (since we can't propagate what doesn't yet exist). + if (Asg == null || !Propagate) + { + return false; + } + + if (Sites.Count > 0) { foreach (UseSite Site in Sites) { - if (Site.Parent is ShaderIrOperOp Op) + if (Site.Parent is ShaderIrCond Cond) { switch (Site.OperIndex) { - case 0: Op.OperandA = Node.Src; break; - case 1: Op.OperandB = Node.Src; break; - case 2: Op.OperandC = Node.Src; break; + case 0: Cond.Pred = Asg.Src; break; + case 1: Cond.Child = Asg.Src; break; default: throw new InvalidOperationException(); } } - else if (Site.Parent is ShaderIrNode SiteNode) + else if (Site.Parent is ShaderIrOp Op) { - SiteNode.Src = Node.Src; + switch (Site.OperIndex) + { + case 0: Op.OperandA = Asg.Src; break; + case 1: Op.OperandB = Asg.Src; break; + case 2: Op.OperandC = Asg.Src; break; + + default: throw new InvalidOperationException(); + } + } + else if (Site.Parent is ShaderIrAsg SiteAsg) + { + SiteAsg.Src = Asg.Src; } else { throw new InvalidOperationException(); } } - - return true; } - return Sites.Count == 0; + return true; } - public void SetNewAsg(ShaderIrNode Node) + public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate) { - this.Node = Node; + this.Asg = Asg; + this.AsgIndex = AsgIndex; + this.Propagate = Propagate; Sites.Clear(); } @@ -84,69 +103,147 @@ namespace Ryujinx.Graphics.Gal.Shader { Dictionary Uses = new Dictionary(); - RegUse GetRegUse(int GprIndex) + RegUse GetUse(int Key) { RegUse Use; - if (!Uses.TryGetValue(GprIndex, out Use)) + if (!Uses.TryGetValue(Key, out Use)) { Use = new RegUse(); - Uses.Add(GprIndex, Use); + Uses.Add(Key, Use); } return Use; } - void TryAddRegUse(object Parent, ShaderIrOper Oper, int OperIndex = 0) + int GetGprKey(int GprIndex) { - if (Oper is ShaderIrOperOp Op) + return GprIndex; + } + + int GetPredKey(int PredIndex) + { + return PredIndex | 0x10000000; + } + + RegUse GetGprUse(int GprIndex) + { + return GetUse(GetGprKey(GprIndex)); + } + + RegUse GetPredUse(int PredIndex) + { + return GetUse(GetPredKey(PredIndex)); + } + + void FindRegUses(List<(int, UseSite)> UseList, object Parent, ShaderIrNode Node, int OperIndex = 0) + { + if (Node is ShaderIrAsg Asg) { - TryAddRegUse(Op, Op.OperandA, 0); - TryAddRegUse(Op, Op.OperandB, 1); - TryAddRegUse(Op, Op.OperandC, 2); + FindRegUses(UseList, Asg, Asg.Src); } - else if (Oper is ShaderIrOperReg Reg && Reg.GprIndex != ShaderIrOperReg.ZRIndex) + else if (Node is ShaderIrCond Cond) { - GetRegUse(Reg.GprIndex).AddUseSite(new UseSite(Parent, OperIndex)); + FindRegUses(UseList, Cond, Cond.Pred, 0); + FindRegUses(UseList, Cond, Cond.Child, 1); + } + else if (Node is ShaderIrOp Op) + { + FindRegUses(UseList, Op, Op.OperandA, 0); + FindRegUses(UseList, Op, Op.OperandB, 1); + FindRegUses(UseList, Op, Op.OperandC, 2); + } + else if (Node is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) + { + UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex))); + } + else if (Node is ShaderIrOperPred Pred) + { + UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, OperIndex))); } } - for (int Index = 0; Index < Nodes.Count; Index++) + void TryAddRegUseSite(ShaderIrNode Node) + { + List<(int, UseSite)> UseList = new List<(int, UseSite)>(); + + FindRegUses(UseList, null, Node); + + foreach ((int Key, UseSite Site) in UseList) + { + GetUse(Key).AddUseSite(Site); + } + } + + bool TryPropagate(RegUse Use) + { + //We can only propagate if the registers that the expression depends + //on weren't assigned after the original expression assignment + //to a register took place. We traverse the expression tree to find + //all registers being used, if any of those registers was assigned + //after the assignment to be propagated, then we can't propagate. + List<(int, UseSite)> UseList = new List<(int, UseSite)>(); + + FindRegUses(UseList, Use.Asg, Use.Asg.Src); + + foreach ((int Key, UseSite Site) in UseList) + { + if (GetUse(Key).AsgIndex >= Use.AsgIndex) + { + return false; + } + } + + return Use.TryPropagate(); + } + + for (int Index = 0, AsgIndex = 0; Index < Nodes.Count; Index++, AsgIndex++) { ShaderIrNode Node = Nodes[Index]; - if (Node.Src is ShaderIrOperOp Op) + bool IsConditional = Node is ShaderIrCond; + + TryAddRegUseSite(Node); + + while (Node is ShaderIrCond Cond) { - TryAddRegUse(Node, Op); - } - else if (Node.Src is ShaderIrOperReg) - { - TryAddRegUse(Node, Node.Src); + Node = Cond.Child; } - if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex != ShaderIrOperReg.ZRIndex) + if (!(Node is ShaderIrAsg Asg)) { - RegUse Use = GetRegUse(Reg.GprIndex); - - if (Use.Node != null && Use.TryPropagate()) - { - Nodes.Remove(Use.Node); - - Index--; - } - - Use.SetNewAsg(Node); + continue; } + + RegUse Use = null; + + if (Asg.Dst is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) + { + Use = GetGprUse(Gpr.Index); + } + else if (Asg.Dst is ShaderIrOperPred Pred) + { + Use = GetPredUse(Pred.Index); + } + + if (Use?.Asg != null && !IsConditional && TryPropagate(Use)) + { + Nodes.Remove(Use.Asg); + + Index--; + } + + //All nodes inside conditional nodes can't be propagated, + //as we don't even know if they will be executed to begin with. + Use?.SetNewAsg(Asg, AsgIndex, !IsConditional); } - //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()) + if (TryPropagate(Use)) { - Nodes.Remove(Use.Node); + Nodes.Remove(Use.Asg); } } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs index c567336ca1..f218e4c335 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gal.Shader { 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)) + using (System.IO.FileStream FS = new System.IO.FileStream("D:\\puyo_fsh.bin", System.IO.FileMode.Open)) { System.IO.BinaryReader Reader = new System.IO.BinaryReader(FS); @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Gal.Shader GlslDecompiler Decompiler = new GlslDecompiler(); - System.Console.WriteLine(Decompiler.Decompile(Code)); + System.Console.WriteLine(Decompiler.Decompile(Code, ShaderType.Fragment)); System.Console.WriteLine("Done!"); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderType.cs b/Ryujinx.Graphics/Gal/Shader/ShaderType.cs new file mode 100644 index 0000000000..fc7d477e1f --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderType.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + enum ShaderType + { + Vertex, + Fragment + } +} \ No newline at end of file