Started to add support for fragment shaders aswell
This commit is contained in:
parent
63243d16ad
commit
1fa59355b8
24 changed files with 908 additions and 230 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 { }
|
||||
}
|
22
Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
Normal file
22
Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class ShaderIrOper { }
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class ShaderIrOperImm : ShaderIrOper
|
||||
class ShaderIrOperImm : ShaderIrNode
|
||||
{
|
||||
public int Imm { get; private set; }
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class ShaderIrOperImmf : ShaderIrOper
|
||||
class ShaderIrOperImmf : ShaderIrNode
|
||||
{
|
||||
public float Imm { get; private set; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal file
15
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
|
|
8
Ryujinx.Graphics/Gal/Shader/ShaderType.cs
Normal file
8
Ryujinx.Graphics/Gal/Shader/ShaderType.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
enum ShaderType
|
||||
{
|
||||
Vertex,
|
||||
Fragment
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue