mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-24 23:47:11 +02:00
Merge shader branch, adding support for GLSL decompilation, a macro
interpreter, and a rewrite of the GPU code.
This commit is contained in:
parent
7acd0e0122
commit
b9aa3966c0
77 changed files with 5301 additions and 766 deletions
212
Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
Normal file
212
Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
644
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal file
644
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
Normal file
22
Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
4
Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
Normal file
4
Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
|
||||
}
|
315
Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
Normal file
315
Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal file
17
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
211
Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
Normal file
211
Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
Normal file
59
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
128
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
Normal file
128
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
Normal file
41
Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
39
Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
Normal file
39
Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
Normal file
59
Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
Normal 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
|
||||
}
|
||||
}
|
4
Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
Normal file
4
Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
Normal file
14
Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal file
16
Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal file
17
Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
97
Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
Normal file
97
Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
Normal 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)];
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
Normal file
11
Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
enum ShaderOper
|
||||
{
|
||||
CR,
|
||||
RC,
|
||||
RR,
|
||||
Imm,
|
||||
Immf
|
||||
}
|
||||
}
|
266
Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
Normal file
266
Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue