Started to add support for fragment shaders aswell

This commit is contained in:
gdkchan 2018-03-25 17:04:37 -03:00
parent 63243d16ad
commit 1fa59355b8
24 changed files with 908 additions and 230 deletions

View file

@ -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<ShaderIrInst, GetInstExpr> 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<int, Attrib> InputAttributes;
private SortedDictionary<int, Attrib> OutputAttributes;
@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private HashSet<int> 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, GetInstExpr>()
{
{ 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<int, Attrib>();
OutputAttributes = new SortedDictionary<int, Attrib>();
@ -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<int, Attrib> 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<int, Attrib> 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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}
}

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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 { }
}

View file

@ -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;
}
}
}

View file

@ -1,4 +0,0 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOper { }
}

View file

@ -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; }

View file

@ -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; }

View file

@ -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;
}
}
}

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImm : ShaderIrOper
class ShaderIrOperImm : ShaderIrNode
{
public int Imm { get; private set; }

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImmf : ShaderIrOper
class ShaderIrOperImmf : ShaderIrNode
{
public float Imm { get; private set; }

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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)

View file

@ -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<UseSite> 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<int, RegUse> Uses = new Dictionary<int, RegUse>();
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);
}
}
}

View file

@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
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))
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!");
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderType
{
Vertex,
Fragment
}
}