Merge shader branch, adding support for GLSL decompilation, a macro

interpreter, and a rewrite of the GPU code.
This commit is contained in:
gdkchan 2018-04-08 16:17:35 -03:00
parent 7acd0e0122
commit b9aa3966c0
77 changed files with 5301 additions and 766 deletions

View file

@ -0,0 +1,212 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
class GlslDecl
{
public const int VertexIdAttr = 0x2fc;
public const int GlPositionWAttr = 0x7c;
private const int AttrStartIndex = 8;
private const int TexStartIndex = 8;
private const string InAttrName = "in_attr";
private const string OutAttrName = "out_attr";
private const string UniformName = "c";
private const string GprName = "gpr";
private const string PredName = "pred";
private const string TextureName = "tex";
public const string FragmentOutputName = "FragColor";
private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
private string StagePrefix;
private Dictionary<int, ShaderDeclInfo> m_Textures;
private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms;
private Dictionary<int, ShaderDeclInfo> m_InAttributes;
private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
private Dictionary<int, ShaderDeclInfo> m_Gprs;
private Dictionary<int, ShaderDeclInfo> m_Preds;
public IReadOnlyDictionary<int, ShaderDeclInfo> Textures => m_Textures;
public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms;
public IReadOnlyDictionary<int, ShaderDeclInfo> InAttributes => m_InAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs;
public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
public GalShaderType ShaderType { get; private set; }
public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType)
{
this.ShaderType = ShaderType;
StagePrefix = StagePrefixes[(int)ShaderType] + "_";
m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>();
m_Textures = new Dictionary<int, ShaderDeclInfo>();
m_InAttributes = new Dictionary<int, ShaderDeclInfo>();
m_OutAttributes = new Dictionary<int, ShaderDeclInfo>();
m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>();
//FIXME: Only valid for vertex shaders.
if (ShaderType == GalShaderType.Fragment)
{
m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4));
}
else
{
m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4));
}
foreach (ShaderIrNode Node in Nodes)
{
Traverse(null, Node);
}
}
private void Traverse(ShaderIrNode Parent, ShaderIrNode Node)
{
switch (Node)
{
case ShaderIrAsg Asg:
{
Traverse(Asg, Asg.Dst);
Traverse(Asg, Asg.Src);
break;
}
case ShaderIrCond Cond:
{
Traverse(Cond, Cond.Pred);
Traverse(Cond, Cond.Child);
break;
}
case ShaderIrOp Op:
{
Traverse(Op, Op.OperandA);
Traverse(Op, Op.OperandB);
Traverse(Op, Op.OperandC);
if (Op.Inst == ShaderIrInst.Texr ||
Op.Inst == ShaderIrInst.Texg ||
Op.Inst == ShaderIrInst.Texb ||
Op.Inst == ShaderIrInst.Texa)
{
int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
int Index = Handle - TexStartIndex;
string Name = StagePrefix + TextureName + Index;
m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle));
}
break;
}
case ShaderIrOperCbuf Cbuf:
{
string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs;
ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
break;
}
case ShaderIrOperAbuf Abuf:
{
//This is a built-in input variable.
if (Abuf.Offs == VertexIdAttr)
{
break;
}
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
int GlslIndex = Index - AttrStartIndex;
ShaderDeclInfo DeclInfo;
if (Parent is ShaderIrAsg Asg && Asg.Dst == Node)
{
if (!m_OutAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(OutAttrName + GlslIndex, GlslIndex);
m_OutAttributes.Add(Index, DeclInfo);
}
}
else
{
if (!m_InAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new ShaderDeclInfo(InAttrName + GlslIndex, GlslIndex);
m_InAttributes.Add(Index, DeclInfo);
}
}
DeclInfo.Enlarge(Elem + 1);
break;
}
case ShaderIrOperGpr Gpr:
{
if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index))
{
string Name = GprName + Gpr.Index;
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
}
break;
}
case ShaderIrOperPred Pred:
{
if (!Pred.IsConst && !HasName(m_Preds, Pred.Index))
{
string Name = PredName + Pred.Index;
m_Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index));
}
break;
}
}
}
private bool HasName(Dictionary<int, ShaderDeclInfo> Decls, int Index)
{
int VecIndex = Index >> 2;
if (Decls.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
{
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
{
return true;
}
}
return Decls.ContainsKey(Index);
}
}
}

View file

@ -0,0 +1,644 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Ryujinx.Graphics.Gal.Shader
{
class GlslDecompiler
{
private delegate string GetInstExpr(ShaderIrOp Op);
private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
private enum OperType
{
Bool,
F32,
I32
}
private const string IdentationStr = " ";
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
private GlslDecl Decl;
private StringBuilder SB;
public GlslDecompiler()
{
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
{
{ ShaderIrInst.And, GetAndExpr },
{ ShaderIrInst.Asr, GetAsrExpr },
{ 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.Exit, GetExitExpr },
{ 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.Frcp, GetFrcpExpr },
{ ShaderIrInst.Frsq, GetFrsqExpr },
{ ShaderIrInst.Fsin, GetFsinExpr },
{ ShaderIrInst.Ipa, GetIpaExpr },
{ ShaderIrInst.Kil, GetKilExpr },
{ ShaderIrInst.Lsr, GetLsrExpr },
{ ShaderIrInst.Not, GetNotExpr },
{ ShaderIrInst.Or, GetOrExpr },
{ ShaderIrInst.Stof, GetStofExpr },
{ ShaderIrInst.Utof, GetUtofExpr },
{ ShaderIrInst.Texr, GetTexrExpr },
{ ShaderIrInst.Texg, GetTexgExpr },
{ ShaderIrInst.Texb, GetTexbExpr },
{ ShaderIrInst.Texa, GetTexaExpr },
{ ShaderIrInst.Xor, GetXorExpr },
};
}
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
{
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
ShaderIrNode[] Nodes = Block.GetNodes();
Decl = new GlslDecl(Nodes, ShaderType);
SB = new StringBuilder();
SB.AppendLine("#version 330 core");
PrintDeclTextures();
PrintDeclUniforms();
PrintDeclInAttributes();
PrintDeclOutAttributes();
PrintDeclGprs();
PrintDeclPreds();
PrintBlockScope("void main()", 1, Nodes);
string GlslCode = SB.ToString();
return new GlslProgram(
GlslCode,
Decl.Textures.Values,
Decl.Uniforms.Values);
}
private void PrintDeclTextures()
{
PrintDecls(Decl.Textures, "uniform sampler2D");
}
private void PrintDeclUniforms()
{
foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
{
SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
}
if (Decl.Uniforms.Count > 0)
{
SB.AppendLine();
}
}
private void PrintDeclInAttributes()
{
PrintDeclAttributes(Decl.InAttributes.Values, "in");
}
private void PrintDeclOutAttributes()
{
PrintDeclAttributes(Decl.OutAttributes.Values, "out");
}
private void PrintDeclAttributes(IEnumerable<ShaderDeclInfo> Decls, string InOut)
{
int Count = 0;
foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
{
if (DeclInfo.Index >= 0)
{
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};");
Count++;
}
}
if (Count > 0)
{
SB.AppendLine();
}
}
private void PrintDeclGprs()
{
PrintDecls(Decl.Gprs);
}
private void PrintDeclPreds()
{
PrintDecls(Decl.Preds, "bool");
}
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null)
{
foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
{
string Name;
if (CustomType != null)
{
Name = CustomType + " " + DeclInfo.Name + ";";
}
else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
{
Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";";
}
else
{
Name = GetDecl(DeclInfo) + ";";
}
SB.AppendLine(Name);
}
if (Dict.Count > 0)
{
SB.AppendLine();
}
}
private int DeclKeySelector(ShaderDeclInfo DeclInfo)
{
return DeclInfo.Cbuf << 24 | DeclInfo.Index;
}
private string GetDecl(ShaderDeclInfo DeclInfo)
{
return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
}
private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
{
string Identation = string.Empty;
for (int Index = 0; Index < IdentationLevel - 1; Index++)
{
Identation += IdentationStr;
}
if (ScopeName != string.Empty)
{
ScopeName += " ";
}
SB.AppendLine(Identation + ScopeName + "{");
string LastLine = Identation + "}";
if (IdentationLevel > 0)
{
Identation += IdentationStr;
}
for (int Index = 0; Index < Nodes.Length; Index++)
{
ShaderIrNode Node = Nodes[Index];
if (Node is ShaderIrCond Cond)
{
string SubScopeName = "if (" + GetSrcExpr(Cond.Pred, true) + ")";
PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
}
else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst))
{
string Expr = GetSrcExpr(Asg.Src, true);
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
}
else if (Node is ShaderIrOp Op)
{
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
}
else
{
throw new InvalidOperationException();
}
}
SB.AppendLine(LastLine);
}
private bool IsValidOutOper(ShaderIrNode Node)
{
if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
{
return false;
}
else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
{
return false;
}
return true;
}
private string GetDstOperName(ShaderIrNode Node)
{
if (Node is ShaderIrOperAbuf Abuf)
{
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 GetSrcExpr(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 ShaderIrOperImm Imm: return GetValue(Imm);
case ShaderIrOperImmf Immf: return GetValue(Immf);
case ShaderIrOperPred Pred: return GetName (Pred);
case ShaderIrOp Op:
string Expr;
if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
{
Expr = GetExpr(Op);
}
else
{
throw new NotImplementedException(Op.Inst.ToString());
}
if (!Entry && NeedsParentheses(Op))
{
Expr = "(" + Expr + ")";
}
return Expr;
default: throw new ArgumentException(nameof(Node));
}
}
private static bool NeedsParentheses(ShaderIrOp Op)
{
switch (Op.Inst)
{
case ShaderIrInst.Frcp:
return true;
case ShaderIrInst.Ipa:
case ShaderIrInst.Texr:
case ShaderIrInst.Texg:
case ShaderIrInst.Texb:
case ShaderIrInst.Texa:
return false;
}
return Op.OperandB != null ||
Op.OperandC != null;
}
private string GetName(ShaderIrOperCbuf Cbuf)
{
if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo))
{
throw new InvalidOperationException();
}
return DeclInfo.Name;
}
private string GetOutAbufName(ShaderIrOperAbuf Abuf)
{
return GetName(Decl.OutAttributes, Abuf);
}
private string GetName(ShaderIrOperAbuf Abuf)
{
if (Abuf.Offs == GlslDecl.GlPositionWAttr && Decl.ShaderType == GalShaderType.Fragment)
{
return "(1f / gl_FragCoord.w)";
}
if (Abuf.Offs == GlslDecl.VertexIdAttr)
{
return "gl_VertexID";
}
return GetName(Decl.InAttributes, Abuf);
}
private string GetName(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, ShaderIrOperAbuf Abuf)
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
if (!Dict.TryGetValue(Index, out ShaderDeclInfo DeclInfo))
{
throw new InvalidOperationException();
}
return DeclInfo.Size > 1 ? DeclInfo.Name + "." + GetAttrSwizzle(Elem) : DeclInfo.Name;
}
private string GetName(ShaderIrOperGpr Gpr)
{
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
}
private string GetValue(ShaderIrOperImm Imm)
{
//Only use hex is the value is too big and would likely be hard to read as int.
if (Imm.Value > 0xfff ||
Imm.Value < -0xfff)
{
return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture);
}
else
{
return Imm.Value.ToString(CultureInfo.InvariantCulture);
}
}
private string GetValue(ShaderIrOperImmf Immf)
{
return Immf.Value.ToString(CultureInfo.InvariantCulture) + "f";
}
private string GetName(ShaderIrOperPred Pred)
{
return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index);
}
private string GetNameWithSwizzle(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, int Index)
{
int VecIndex = Index >> 2;
if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
{
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
{
return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3);
}
}
if (!Dict.TryGetValue(Index, out DeclInfo))
{
throw new InvalidOperationException();
}
return DeclInfo.Name;
}
private string GetAttrSwizzle(int Elem)
{
return "xyzw".Substring(Elem, 1);
}
private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&");
private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>");
private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&");
private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!");
private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">");
private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!=");
private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">=");
private string GetExitExpr(ShaderIrOp Op) => "return";
private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs");
private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+");
private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2");
private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+");
private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1f / ");
private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt");
private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin");
private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
private string GetKilExpr(ShaderIrOp Op) => "discard";
private string GetLsrExpr(ShaderIrOp Op)
{
return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " +
GetOperExpr(Op, Op.OperandB) + ")";
}
private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~");
private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|");
private string GetStofExpr(ShaderIrOp Op)
{
return "float(" + GetOperExpr(Op, Op.OperandA) + ")";
}
private string GetUtofExpr(ShaderIrOp Op)
{
return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
}
private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^");
private string GetUnaryCall(ShaderIrOp Op, string FuncName)
{
return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")";
}
private string GetUnaryExpr(ShaderIrOp Op, string Opr)
{
return Opr + GetOperExpr(Op, Op.OperandA);
}
private string GetBinaryExpr(ShaderIrOp Op, string Opr)
{
return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " +
GetOperExpr(Op, Op.OperandB);
}
private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2)
{
return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " +
GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " +
GetOperExpr(Op, Op.OperandC);
}
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 = ((ShaderIrOperImm)Op.OperandC).Value;
if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo))
{
throw new InvalidOperationException();
}
return DeclInfo.Name;
}
private string GetTexSamplerCoords(ShaderIrOp Op)
{
return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
GetOperExpr(Op, Op.OperandB) + ")";
}
private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
{
return GetExprWithCast(Op, Oper, GetSrcExpr(Oper));
}
private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr)
{
//Note: The "DstType" (of the cast) is the type that the operation
//uses on the source operands, while the "SrcType" is the destination
//type of the operand result (if it is a operation) or just the type
//of the variable for registers/uniforms/attributes.
OperType DstType = GetSrcNodeType(Dst);
OperType SrcType = GetDstNodeType(Src);
if (DstType != SrcType)
{
//Check for invalid casts
//(like bool to int/float and others).
if (SrcType != OperType.F32 &&
SrcType != OperType.I32)
{
throw new InvalidOperationException();
}
//For integer immediates being used as float,
//it's better (for readability) to just return the float value.
if (Src is ShaderIrOperImm Imm && DstType == OperType.F32)
{
float Value = BitConverter.Int32BitsToSingle(Imm.Value);
return Value.ToString(CultureInfo.InvariantCulture) + "f";
}
switch (DstType)
{
case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break;
case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break;
}
}
return Expr;
}
private static OperType GetDstNodeType(ShaderIrNode Node)
{
if (Node is ShaderIrOp Op)
{
switch (Op.Inst)
{
case ShaderIrInst.Stof: return OperType.F32;
case ShaderIrInst.Utof: return OperType.F32;
}
}
return GetSrcNodeType(Node);
}
private static OperType GetSrcNodeType(ShaderIrNode Node)
{
switch (Node)
{
case ShaderIrOperAbuf Abuf:
return Abuf.Offs == GlslDecl.VertexIdAttr
? OperType.I32
: OperType.F32;
case ShaderIrOperCbuf Cbuf: return OperType.F32;
case ShaderIrOperGpr Gpr: return OperType.F32;
case ShaderIrOperImm Imm: return OperType.I32;
case ShaderIrOperImmf Immf: return OperType.F32;
case ShaderIrOperPred Pred: return OperType.Bool;
case ShaderIrOp Op:
if (Op.Inst > ShaderIrInst.B_Start &&
Op.Inst < ShaderIrInst.B_End)
{
return OperType.Bool;
}
else if (Op.Inst > ShaderIrInst.F_Start &&
Op.Inst < ShaderIrInst.F_End)
{
return OperType.F32;
}
else if (Op.Inst > ShaderIrInst.I_Start &&
Op.Inst < ShaderIrInst.I_End)
{
return OperType.I32;
}
break;
}
throw new ArgumentException(nameof(Node));
}
}
}

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
struct GlslProgram
{
public string Code { get; private set; }
public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
public GlslProgram(
string Code,
IEnumerable<ShaderDeclInfo> Textures,
IEnumerable<ShaderDeclInfo> Uniforms)
{
this.Code = Code;
this.Textures = Textures;
this.Uniforms = Uniforms;
}
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Graphics.Gal.Shader
{
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
}

View file

@ -0,0 +1,315 @@
using System;
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Fadd_C(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd);
}
public static void Fadd_I(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fadd);
}
public static void Fadd_R(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fadd);
}
public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
{
EmitAluFfma(Block, OpCode, ShaderOper.CR);
}
public static void Ffma_I(ShaderIrBlock Block, long OpCode)
{
EmitAluFfma(Block, OpCode, ShaderOper.Immf);
}
public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
{
EmitAluFfma(Block, OpCode, ShaderOper.RC);
}
public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
{
EmitAluFfma(Block, OpCode, ShaderOper.RR);
}
public static void Fmul_C(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul);
}
public static void Fmul_I(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fmul);
}
public static void Fmul_R(ShaderIrBlock Block, long OpCode)
{
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul);
}
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
{
EmitFsetp(Block, OpCode, ShaderOper.CR);
}
public static void Fsetp_I(ShaderIrBlock Block, long OpCode)
{
EmitFsetp(Block, OpCode, ShaderOper.Immf);
}
public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
{
EmitFsetp(Block, OpCode, ShaderOper.RR);
}
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 Lop32i(ShaderIrBlock Block, long OpCode)
{
int SubOp = (int)(OpCode >> 53) & 3;
bool Ia = ((OpCode >> 55) & 1) != 0;
bool Ib = ((OpCode >> 56) & 1) != 0;
ShaderIrInst Inst = 0;
switch (SubOp)
{
case 0: Inst = ShaderIrInst.And; break;
case 1: Inst = ShaderIrInst.Or; break;
case 2: Inst = ShaderIrInst.Xor; break;
}
ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), Ia);
//SubOp == 3 is pass, used by the not instruction
//which just moves the inverted register value.
if (SubOp < 3)
{
ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), Ib);
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
else
{
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), 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;
default: throw new NotImplementedException(SubOp.ToString());
}
ShaderIrNode OperA = GetOperGpr8(OpCode);
ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na));
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Shr_C(ShaderIrBlock Block, long OpCode)
{
EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
}
public static void Shr_I(ShaderIrBlock Block, long OpCode)
{
EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
}
public static void Shr_R(ShaderIrBlock Block, long OpCode)
{
EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
}
private static ShaderIrInst GetShrInst(long OpCode)
{
bool Signed = ((OpCode >> 48) & 1) != 0;
return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
}
private static void EmitAluBinary(
ShaderIrBlock Block,
long OpCode,
ShaderOper Oper,
ShaderIrInst Inst)
{
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
switch (Oper)
{
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
private static void EmitAluBinaryF(
ShaderIrBlock Block,
long OpCode,
ShaderOper Oper,
ShaderIrInst Inst)
{
bool Nb = ((OpCode >> 45) & 1) != 0;
bool Aa = ((OpCode >> 46) & 1) != 0;
bool Na = ((OpCode >> 48) & 1) != 0;
bool Ab = ((OpCode >> 49) & 1) != 0;
bool Ad = ((OpCode >> 50) & 1) != 0;
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
if (Inst == ShaderIrInst.Fadd)
{
OperA = GetAluAbsNeg(OperA, Aa, Na);
}
switch (Oper)
{
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
OperB = GetAluAbsNeg(OperB, Ab, Nb);
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
Op = GetAluAbs(Op, Ad);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool Nb = ((OpCode >> 48) & 1) != 0;
bool Nc = ((OpCode >> 49) & 1) != 0;
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
switch (Oper)
{
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
case ShaderOper.RC: OperB = GetOperGpr39 (OpCode); break;
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
OperB = GetAluNeg(OperB, Nb);
if (Oper == ShaderOper.RC)
{
OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc);
}
else
{
OperC = GetAluNeg(GetOperGpr39(OpCode), Nc);
}
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool Aa = ((OpCode >> 7) & 1) != 0;
bool Np = ((OpCode >> 42) & 1) != 0;
bool Na = ((OpCode >> 43) & 1) != 0;
bool Ab = ((OpCode >> 44) & 1) != 0;
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
switch (Oper)
{
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
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.IsConst && P2Node.IsConst)
{
return;
}
ShaderIrNode P2NNode = P2Node;
if (Np)
{
P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
}
Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);
Op = new ShaderIrOp(LopInst, Op, P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
}
}
}

View file

@ -0,0 +1,17 @@
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Exit(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode));
}
public static void Kil(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
}
}
}

View file

@ -0,0 +1,211 @@
using System;
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecodeHelper
{
public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode)
{
int Abuf = (int)(OpCode >> 20) & 0x3ff;
int Reg = (int)(OpCode >> 39) & 0xff;
int Size = (int)(OpCode >> 47) & 3;
ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1];
for (int Index = 0; Index <= Size; Index++)
{
Opers[Index] = new ShaderIrOperAbuf(Abuf, Reg);
}
return Opers;
}
public static ShaderIrOperAbuf GetOperAbuf28(long OpCode)
{
int Abuf = (int)(OpCode >> 28) & 0x3ff;
int Reg = (int)(OpCode >> 39) & 0xff;
return new ShaderIrOperAbuf(Abuf, Reg);
}
public static ShaderIrOperCbuf GetOperCbuf34(long OpCode)
{
return new ShaderIrOperCbuf(
(int)(OpCode >> 34) & 0x1f,
(int)(OpCode >> 20) & 0x3fff);
}
public static ShaderIrOperGpr GetOperGpr8(long OpCode)
{
return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
}
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 GetOperImm19_20(long OpCode)
{
int Value = (int)(OpCode >> 20) & 0x7ffff;
bool Neg = ((OpCode >> 56) & 1) != 0;
if (Neg)
{
Value = -Value;
}
return new ShaderIrOperImm((int)Value);
}
public static ShaderIrNode GetOperImmf19_20(long OpCode)
{
uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
bool Neg = ((OpCode >> 56) & 1) != 0;
Imm <<= 12;
if (Neg)
{
Imm |= 0x80000000;
}
float Value = BitConverter.Int32BitsToSingle((int)Imm);
return new ShaderIrOperImmf(Value);
}
public static ShaderIrOperImm GetOperImm13_36(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
}
public static ShaderIrOperImm GetOperImm32_20(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 20));
}
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);
}
public static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg)
{
return GetAluNeg(GetAluAbs(Node, Abs), Neg);
}
public static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs)
{
return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node;
}
public static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg)
{
return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node;
}
public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not)
{
return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node;
}
}
}

View file

@ -0,0 +1,59 @@
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Ld_A(ShaderIrBlock Block, long OpCode)
{
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
int Index = 0;
foreach (ShaderIrNode OperA in Opers)
{
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
OperD.Index += Index++;
Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, OperA), OpCode));
}
}
public static void St_A(ShaderIrBlock Block, long OpCode)
{
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
int Index = 0;
foreach (ShaderIrNode OperA in Opers)
{
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
OperD.Index += Index++;
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

@ -0,0 +1,128 @@
using System;
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
private enum IntType
{
U8 = 0,
U16 = 1,
U32 = 2,
U64 = 3,
S8 = 4,
S16 = 5,
S32 = 6,
S64 = 7
}
private enum FloatType
{
F16 = 1,
F32 = 2,
F64 = 3
}
public static void I2f_C(ShaderIrBlock Block, long OpCode)
{
EmitI2f(Block, OpCode, ShaderOper.CR);
}
public static void I2f_I(ShaderIrBlock Block, long OpCode)
{
EmitI2f(Block, OpCode, ShaderOper.Imm);
}
public static void I2f_R(ShaderIrBlock Block, long OpCode)
{
EmitI2f(Block, OpCode, ShaderOper.RR);
}
private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
IntType Type = GetIntType(OpCode);
if (Type == IntType.U64 ||
Type == IntType.S64)
{
//TODO: 64-bits support.
//Note: GLSL doesn't support 64-bits integers.
throw new NotImplementedException();
}
int Sel = (int)(OpCode >> 41) & 3;
bool Na = ((OpCode >> 45) & 1) != 0;
bool Aa = ((OpCode >> 49) & 1) != 0;
ShaderIrNode OperA;
switch (Oper)
{
case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break;
case ShaderOper.Imm: OperA = GetOperImm19_20(OpCode); break;
case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
OperA = GetAluAbsNeg(OperA, Aa, Na);
bool Signed = Type >= IntType.S8;
int Shift = Sel * 8;
int Size = 8 << ((int)Type & 3);
ulong Mask = ulong.MaxValue >> (64 - Size);
int Mask32 = (int)Mask;
if (Shift != 0)
{
OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift));
}
if (Mask != uint.MaxValue)
{
OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm(Mask32));
}
ShaderIrInst Inst = Signed
? ShaderIrInst.Stof
: ShaderIrInst.Utof;
ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Mov32i(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
private static IntType GetIntType(long OpCode)
{
bool Signed = ((OpCode >> 13) & 1) != 0;
IntType Type = (IntType)((OpCode >> 10) & 3);
if (Signed)
{
Type += (int)IntType.S8;
}
return Type;
}
private static FloatType GetFloatType(long OpCode)
{
return (FloatType)((OpCode >> 8) & 3);
}
}
}

View file

@ -0,0 +1,41 @@
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecoder
{
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
{
ShaderIrBlock Block = new ShaderIrBlock();
while (Offset + 2 <= Code.Length)
{
uint Word0 = (uint)Code[Offset++];
uint Word1 = (uint)Code[Offset++];
long OpCode = Word0 | (long)Word1 << 32;
ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
if (Decode == null)
{
continue;
}
Decode(Block, OpCode);
if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
{
break;
}
}
Block.RunOptimizationPasses(ShaderType);
return Block;
}
private static bool IsFlowChange(ShaderIrInst Inst)
{
return Inst == ShaderIrInst.Exit;
}
}
}

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,39 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrBlock
{
private List<ShaderIrNode> Nodes;
public ShaderIrBlock()
{
Nodes = new List<ShaderIrNode>();
}
public void AddNode(ShaderIrNode Node)
{
Nodes.Add(Node);
}
public void RunOptimizationPasses(GalShaderType ShaderType)
{
ShaderOptExprProp.Optimize(Nodes, ShaderType);
}
public ShaderIrNode[] GetNodes()
{
return Nodes.ToArray();
}
public ShaderIrNode GetLastNode()
{
if (Nodes.Count > 0)
{
return Nodes[Nodes.Count - 1];
}
return null;
}
}
}

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

@ -0,0 +1,59 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderIrInst
{
B_Start,
Band,
Bnot,
Bor,
Bxor,
Clt,
Ceq,
Cle,
Cgt,
Cne,
Cge,
Cnum,
Cnan,
Cltu,
Cequ,
Cleu,
Cgtu,
Cneu,
Cgeu,
B_End,
F_Start,
Fabs,
Fadd,
Fcos,
Fex2,
Ffma,
Flg2,
Fmul,
Fneg,
Frcp,
Frsq,
Fsin,
Ipa,
Texr,
Texg,
Texb,
Texa,
F_End,
I_Start,
And,
Asr,
Lsr,
Not,
Or,
Stof,
Utof,
Xor,
I_End,
Exit,
Kil
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Graphics.Gal.Shader
{
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

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperAbuf : ShaderIrNode
{
public int Offs { get; private set; }
public int GprIndex { get; private set; }
public ShaderIrOperAbuf(int Offs, int GprIndex)
{
this.Offs = Offs;
this.GprIndex = GprIndex;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperCbuf : ShaderIrNode
{
public int Index { get; private set; }
public int Offs { get; private set; }
public ShaderIrOperCbuf(int Index, int Offs)
{
this.Index = Index;
this.Offs = Offs;
}
}
}

View file

@ -0,0 +1,16 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperGpr : ShaderIrNode
{
public const int ZRIndex = 0xff;
public bool IsConst => Index == ZRIndex;
public int Index { get; set; }
public ShaderIrOperGpr(int Index)
{
this.Index = Index;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImm : ShaderIrNode
{
public int Value { get; private set; }
public ShaderIrOperImm(int Value)
{
this.Value = Value;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImmf : ShaderIrNode
{
public float Value { get; private set; }
public ShaderIrOperImmf(float Value)
{
this.Value = Value;
}
}
}

View file

@ -0,0 +1,17 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperPred : ShaderIrNode
{
public const int UnusedIndex = 0x7;
public const int NeverExecute = 0xf;
public bool IsConst => Index >= UnusedIndex;
public int Index { get; set; }
public ShaderIrOperPred(int Index)
{
this.Index = Index;
}
}
}

View file

@ -0,0 +1,97 @@
using System;
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderOpCodeTable
{
private const int EncodingBits = 14;
private static ShaderDecodeFunc[] OpCodes;
static ShaderOpCodeTable()
{
OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
#region Instructions
Set("111000110000xx", ShaderDecode.Exit);
Set("0100110001011x", ShaderDecode.Fadd_C);
Set("0011100x01011x", ShaderDecode.Fadd_I);
Set("0101110001011x", ShaderDecode.Fadd_R);
Set("010010011xxxxx", ShaderDecode.Ffma_CR);
Set("001100101xxxxx", ShaderDecode.Ffma_I);
Set("010100011xxxxx", ShaderDecode.Ffma_RC);
Set("010110011xxxxx", ShaderDecode.Ffma_RR);
Set("0100110001101x", ShaderDecode.Fmul_C);
Set("0011100x01101x", ShaderDecode.Fmul_I);
Set("0101110001101x", ShaderDecode.Fmul_R);
Set("010010111011xx", ShaderDecode.Fsetp_C);
Set("0011011x1011xx", ShaderDecode.Fsetp_I);
Set("010110111011xx", ShaderDecode.Fsetp_R);
Set("0100110010111x", ShaderDecode.I2f_C);
Set("0011100x10111x", ShaderDecode.I2f_I);
Set("0101110010111x", ShaderDecode.I2f_R);
Set("11100000xxxxxx", ShaderDecode.Ipa);
Set("111000110011xx", ShaderDecode.Kil);
Set("1110111111011x", ShaderDecode.Ld_A);
Set("000001xxxxxxxx", ShaderDecode.Lop32i);
Set("000000010000xx", ShaderDecode.Mov32i);
Set("0101000010000x", ShaderDecode.Mufu);
Set("0100110000101x", ShaderDecode.Shr_C);
Set("0011100x00101x", ShaderDecode.Shr_I);
Set("0101110000101x", ShaderDecode.Shr_R);
Set("1110111111110x", ShaderDecode.St_A);
Set("1101100xxxxxxx", ShaderDecode.Texs);
#endregion
}
private static void Set(string Encoding, ShaderDecodeFunc Func)
{
if (Encoding.Length != EncodingBits)
{
throw new ArgumentException(nameof(Encoding));
}
int Bit = Encoding.Length - 1;
int Value = 0;
int XMask = 0;
int XBits = 0;
int[] XPos = new int[Encoding.Length];
for (int Index = 0; Index < Encoding.Length; Index++, Bit--)
{
char Chr = Encoding[Index];
if (Chr == '1')
{
Value |= 1 << Bit;
}
else if (Chr == 'x')
{
XMask |= 1 << Bit;
XPos[XBits++] = Bit;
}
}
XMask = ~XMask;
for (int Index = 0; Index < (1 << XBits); Index++)
{
Value &= XMask;
for (int X = 0; X < XBits; X++)
{
Value |= ((Index >> X) & 1) << XPos[X];
}
OpCodes[Value] = Func;
}
}
public static ShaderDecodeFunc GetDecoder(long OpCode)
{
return OpCodes[(ulong)OpCode >> (64 - EncodingBits)];
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderOper
{
CR,
RC,
RR,
Imm,
Immf
}
}

View file

@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderOptExprProp
{
private struct UseSite
{
public object Parent;
public int OperIndex;
public UseSite(object Parent, int OperIndex)
{
this.Parent = Parent;
this.OperIndex = OperIndex;
}
}
private class RegUse
{
public ShaderIrAsg Asg { get; private set; }
public int AsgIndex { get; private set; }
private bool Propagate;
private List<UseSite> Sites;
public RegUse()
{
Sites = new List<UseSite>();
}
public void AddUseSite(UseSite Site)
{
Sites.Add(Site);
}
public bool TryPropagate()
{
//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 ShaderIrCond Cond)
{
switch (Site.OperIndex)
{
case 0: Cond.Pred = Asg.Src; break;
case 1: Cond.Child = Asg.Src; break;
default: throw new InvalidOperationException();
}
}
else if (Site.Parent is ShaderIrOp Op)
{
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;
}
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate)
{
this.Asg = Asg;
this.AsgIndex = AsgIndex;
this.Propagate = Propagate;
Sites.Clear();
}
}
public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
{
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
RegUse GetUse(int Key)
{
RegUse Use;
if (!Uses.TryGetValue(Key, out Use))
{
Use = new RegUse();
Uses.Add(Key, Use);
}
return Use;
}
int GetGprKey(int GprIndex)
{
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)
{
FindRegUses(UseList, Asg, Asg.Src);
}
else if (Node is ShaderIrCond Cond)
{
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)));
}
}
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.
if (Use?.Asg == null)
{
return false;
}
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];
bool IsConditional = Node is ShaderIrCond;
TryAddRegUseSite(Node);
while (Node is ShaderIrCond Cond)
{
Node = Cond.Child;
}
if (!(Node is ShaderIrAsg Asg))
{
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 (!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);
}
foreach (RegUse Use in Uses.Values)
{
//Gprs 0-3 are the color output on fragment shaders,
//so we can't remove the last assignments to those registers.
if (ShaderType == GalShaderType.Fragment)
{
if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
{
continue;
}
}
if (TryPropagate(Use))
{
Nodes.Remove(Use.Asg);
}
}
}
}
}