More work on GPU refactoring

This commit is contained in:
gdkchan 2018-03-29 22:46:04 -03:00
parent 0a0cc69a7b
commit 0dc4053599
15 changed files with 565 additions and 274 deletions

View file

@ -5,12 +5,14 @@ namespace Ryujinx.Graphics.Gal.Shader
public string Name { get; private set; }
public int Index { get; private set; }
public int Cbuf { get; private set; }
public int Size { get; private set; }
public GlslDeclInfo(string Name, int Index, int Size)
public GlslDeclInfo(string Name, int Index, int Cbuf = 0, int Size = 1)
{
this.Name = Name;
this.Index = Index;
this.Cbuf = Cbuf;
this.Size = Size;
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
@ -15,29 +16,28 @@ namespace Ryujinx.Graphics.Gal.Shader
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
private SortedDictionary<int, GlslDeclInfo> InputAttributes;
private SortedDictionary<int, GlslDeclInfo> OutputAttributes;
private Dictionary<int, GlslDeclInfo> Textures;
private HashSet<int> UsedCbufs;
private Dictionary<(int, int), GlslDeclInfo> Uniforms;
private Dictionary<int, GlslDeclInfo> InputAttributes;
private Dictionary<int, GlslDeclInfo> OutputAttributes;
private Dictionary<int, GlslDeclInfo> Gprs;
private Dictionary<int, GlslDeclInfo> Preds;
private const int AttrStartIndex = 8;
private const int TexStartIndex = 8;
private const string InputAttrPrefix = "in_attr";
private const string OutputAttrPrefix = "out_attr";
private const string InputAttrName = "in_attr";
private const string OutputName = "out_attr";
private const string UniformName = "c";
private const string CbufBuffPrefix = "c";
private const string CbufDataName = "buf";
private const string GprName = "gpr";
private const string PredName = "pred";
private const string TextureName = "tex";
private const string GprName = "gpr";
private const string PredName = "pred";
private const string SampName = "samp";
private int GprsCount;
private int PredsCount;
private int SampsCount;
private StringBuilder BodySB;
private StringBuilder SB;
public GlslDecompiler()
{
@ -51,6 +51,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ ShaderIrInst.Cgt, GetCgtExpr },
{ ShaderIrInst.Cne, GetCneExpr },
{ ShaderIrInst.Cge, GetCgeExpr },
{ ShaderIrInst.Exit, GetExitExpr },
{ ShaderIrInst.Fabs, GetFabsExpr },
{ ShaderIrInst.Fadd, GetFaddExpr },
{ ShaderIrInst.Fcos, GetFcosExpr },
@ -73,50 +74,47 @@ namespace Ryujinx.Graphics.Gal.Shader
public GlslProgram Decompile(int[] Code, GalShaderType Type)
{
InputAttributes = new SortedDictionary<int, GlslDeclInfo>();
OutputAttributes = new SortedDictionary<int, GlslDeclInfo>();
Uniforms = new Dictionary<(int, int), GlslDeclInfo>();
UsedCbufs = new HashSet<int>();
Textures = new Dictionary<int, GlslDeclInfo>();
BodySB = new StringBuilder();
InputAttributes = new Dictionary<int, GlslDeclInfo>();
OutputAttributes = new Dictionary<int, GlslDeclInfo>();
Gprs = new Dictionary<int, GlslDeclInfo>();
Preds = new Dictionary<int, GlslDeclInfo>();
SB = new StringBuilder();
//FIXME: Only valid for vertex shaders.
if (Type == GalShaderType.Fragment)
{
OutputAttributes.Add(7, new GlslDeclInfo("FragColor", -1, 4));
Gprs.Add(0, new GlslDeclInfo("FragColor", 0, 0, 4));
}
else
{
OutputAttributes.Add(7, new GlslDeclInfo("gl_Position", -1, 4));
OutputAttributes.Add(7, new GlslDeclInfo("gl_Position", -1, 0, 4));
}
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, Type);
ShaderIrNode[] Nodes = Block.GetNodes();
PrintBlockScope("void main()", 1, Nodes);
StringBuilder SB = new StringBuilder();
foreach (ShaderIrNode Node in Nodes)
{
Traverse(null, Node);
}
SB.AppendLine("#version 430");
PrintDeclUBOs(SB);
PrintDeclInAttributes(SB);
PrintDeclOutAttributes(SB);
PrintDeclTextures();
PrintDeclUniforms();
PrintDeclInAttributes();
PrintDeclOutAttributes();
PrintDeclGprs();
PrintDeclPreds();
if (Type == GalShaderType.Fragment)
{
SB.AppendLine($"out vec4 {OutputAttributes[7].Name};");
SB.AppendLine();
}
PrintDeclSamplers(SB);
PrintDeclGprs(SB);
PrintDeclPreds(SB);
SB.Append(BodySB.ToString());
BodySB.Clear();
PrintBlockScope("void main()", 1, Nodes);
GlslProgram Program = new GlslProgram();
@ -124,33 +122,158 @@ namespace Ryujinx.Graphics.Gal.Shader
Program.Attributes = InputAttributes.Values.ToArray();
SB.Clear();
return Program;
}
private void PrintDeclUBOs(StringBuilder SB)
private void Traverse(ShaderIrNode Parent, ShaderIrNode Node)
{
foreach (int Cbuf in UsedCbufs)
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).Imm;
int Index = Handle - TexStartIndex;
string Name = $"{TextureName}{Index}";
Textures.TryAdd(Handle, new GlslDeclInfo(Name, Index));
}
break;
}
case ShaderIrOperCbuf Cbuf:
{
string Name = $"{UniformName}{Cbuf.Index}_{Cbuf.Offs}";
GlslDeclInfo DeclInfo = new GlslDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
break;
}
case ShaderIrOperAbuf Abuf:
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
int GlslIndex = Index - AttrStartIndex;
GlslDeclInfo DeclInfo;
if (Parent is ShaderIrAsg Asg && Asg.Dst == Node)
{
if (!OutputAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new GlslDeclInfo(OutputName + GlslIndex, GlslIndex);
OutputAttributes.Add(Index, DeclInfo);
}
}
else
{
if (!InputAttributes.TryGetValue(Index, out DeclInfo))
{
DeclInfo = new GlslDeclInfo(InputAttrName + GlslIndex, GlslIndex);
InputAttributes.Add(Index, DeclInfo);
}
}
DeclInfo.Enlarge(Elem + 1);
break;
}
case ShaderIrOperGpr Gpr:
{
if (!Gpr.IsConst && GetNameWithSwizzle(Gprs, Gpr.Index) == null)
{
string Name = $"{GprName}{Gpr.Index}";
Gprs.TryAdd(Gpr.Index, new GlslDeclInfo(Name, Gpr.Index));
}
break;
}
case ShaderIrOperPred Pred:
{
if (!Pred.IsConst && GetNameWithSwizzle(Preds, Pred.Index) == null)
{
string Name = $"{PredName}{Pred.Index}";
Preds.TryAdd(Pred.Index, new GlslDeclInfo(Name, Pred.Index));
}
break;
}
}
}
private void PrintDeclTextures()
{
PrintDecls(Textures.Values, "uniform sampler2D");
}
private void PrintDeclUniforms()
{
foreach (GlslDeclInfo DeclInfo in Uniforms.Values.OrderBy(DeclKeySelector))
{
SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
}
if (Uniforms.Values.Count > 0)
{
SB.AppendLine($"uniform _{CbufBuffPrefix}{Cbuf} {{");
SB.AppendLine($"{IdentationStr}float {CbufDataName}[];");
SB.AppendLine($"}} {CbufBuffPrefix}{Cbuf};");
SB.AppendLine();
}
}
private void PrintDeclInAttributes(StringBuilder SB)
private void PrintDeclInAttributes()
{
PrintDeclAttributes(InputAttributes.Values, "in");
}
private void PrintDeclOutAttributes()
{
PrintDeclAttributes(OutputAttributes.Values, "out");
}
private void PrintDeclAttributes(ICollection<GlslDeclInfo> Decls, string InOut)
{
bool PrintNl = false;
foreach (KeyValuePair<int, GlslDeclInfo> KV in InputAttributes)
foreach (GlslDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
{
int Index = KV.Key - AttrStartIndex;
if (Index >= 0)
if (DeclInfo.Index >= 0)
{
string Type = ElemTypes[KV.Value.Size];
SB.AppendLine($"layout (location = {Index}) in {Type} {KV.Value.Name};");
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};");
PrintNl = true;
}
@ -162,55 +285,48 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
private void PrintDeclOutAttributes(StringBuilder SB)
private void PrintDeclGprs()
{
bool PrintNl = false;
PrintDecls(Gprs.Values);
}
foreach (KeyValuePair<int, GlslDeclInfo> KV in OutputAttributes)
private void PrintDeclPreds()
{
PrintDecls(Preds.Values, "bool");
}
private void PrintDecls(ICollection<GlslDeclInfo> Decls, string CustomType = null)
{
foreach (GlslDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
{
int Index = KV.Key - AttrStartIndex;
string Name;
if (Index >= 0)
if (CustomType != null)
{
string Type = ElemTypes[KV.Value.Size];
SB.AppendLine($"layout (location = {Index}) out {Type} {KV.Value.Name};");
PrintNl = true;
Name = $"{CustomType} {DeclInfo.Name};";
}
else
{
Name = $"{GetDecl(DeclInfo)};";
}
SB.AppendLine(Name);
}
if (PrintNl)
if (Decls.Count > 0)
{
SB.AppendLine();
}
}
private void PrintDeclSamplers(StringBuilder SB)
private int DeclKeySelector(GlslDeclInfo DeclInfo)
{
if (SampsCount > 0)
{
SB.AppendLine($"uniform sampler2D {SampName}[{SampsCount}];");
SB.AppendLine();
}
return DeclInfo.Cbuf << 24 | DeclInfo.Index;
}
private void PrintDeclGprs(StringBuilder SB)
private string GetDecl(GlslDeclInfo DeclInfo)
{
if (GprsCount > 0)
{
SB.AppendLine($"float {GprName}[{GprsCount}];");
SB.AppendLine();
}
}
private void PrintDeclPreds(StringBuilder SB)
{
if (PredsCount > 0)
{
SB.AppendLine($"bool {PredName}[{PredsCount}];");
SB.AppendLine();
}
return $"{ElemTypes[DeclInfo.Size - 1]} {DeclInfo.Name}";
}
private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
@ -227,7 +343,7 @@ namespace Ryujinx.Graphics.Gal.Shader
ScopeName += " ";
}
BodySB.AppendLine(Identation + ScopeName + "{");
SB.AppendLine(Identation + ScopeName + "{");
string LastLine = Identation + "}";
@ -250,14 +366,14 @@ namespace Ryujinx.Graphics.Gal.Shader
{
if (IsValidOutOper(Asg.Dst))
{
BodySB.AppendLine(Identation +
$"{GetOutOperName(Asg.Dst)} = " +
SB.AppendLine(Identation +
$"{GetOutOperName(Asg.Dst)} = " +
$"{GetInOperName (Asg.Src, true)};");
}
}
else if (Node is ShaderIrOp Op)
{
BodySB.AppendLine($"{Identation}{GetInOperName(Op, true)};");
SB.AppendLine($"{Identation}{GetInOperName(Op, true)};");
}
else
{
@ -265,16 +381,16 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
BodySB.AppendLine(LastLine);
SB.AppendLine(LastLine);
}
private bool IsValidOutOper(ShaderIrNode Node)
{
if (Node is ShaderIrOperGpr Gpr && Gpr.Index == ShaderIrOperGpr.ZRIndex)
if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
{
return false;
}
else if (Node is ShaderIrOperPred Pred && Pred.Index == ShaderIrOperPred.UnusedIndex)
else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
{
return false;
}
@ -307,6 +423,7 @@ namespace Ryujinx.Graphics.Gal.Shader
case ShaderIrOperAbuf Abuf: return GetName(Abuf);
case ShaderIrOperCbuf Cbuf: return GetName(Cbuf);
case ShaderIrOperGpr Gpr: return GetName(Gpr);
case ShaderIrOperImm Imm: return GetName(Imm);
case ShaderIrOperPred Pred: return GetName(Pred);
case ShaderIrOp Op:
@ -327,7 +444,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}
return Expr;
default: throw new ArgumentException(nameof(Node));
}
}
@ -347,84 +464,77 @@ namespace Ryujinx.Graphics.Gal.Shader
Inst == ShaderIrInst.Texr ||
Inst == ShaderIrInst.Texg ||
Inst == ShaderIrInst.Texb ||
Inst == ShaderIrInst.Texa;
}
private string GetOutAbufName(ShaderIrOperAbuf Abuf)
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
if (!OutputAttributes.TryGetValue(Index, out GlslDeclInfo Attr))
{
int GlslIndex = Index - AttrStartIndex;
Attr = new GlslDeclInfo(OutputAttrPrefix + GlslIndex, GlslIndex, Elem);
OutputAttributes.Add(Index, Attr);
}
Attr.Enlarge(Elem);
return $"{Attr.Name}.{GetAttrSwizzle(Elem)}";
}
private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true)
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
if (!InputAttributes.TryGetValue(Index, out GlslDeclInfo Attr))
{
int GlslIndex = Index - AttrStartIndex;
Attr = new GlslDeclInfo(InputAttrPrefix + GlslIndex, GlslIndex, Elem);
InputAttributes.Add(Index, Attr);
}
Attr.Enlarge(Elem);
return Swizzle ? $"{Attr.Name}.{GetAttrSwizzle(Elem)}" : Attr.Name;
Inst == ShaderIrInst.Texa;
}
private string GetName(ShaderIrOperCbuf Cbuf)
{
UsedCbufs.Add(Cbuf.Index);
if (!Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out GlslDeclInfo DeclInfo))
{
throw new InvalidOperationException();
}
return $"{CbufBuffPrefix}{Cbuf.Index}.{CbufDataName}[{Cbuf.Offs}]";
return DeclInfo.Name;
}
private string GetOutAbufName(ShaderIrOperAbuf Abuf)
{
return GetName(OutputAttributes, Abuf, Swizzle: true);
}
private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true)
{
return GetName(InputAttributes, Abuf, Swizzle);
}
private string GetName(Dictionary<int, GlslDeclInfo> Decls, ShaderIrOperAbuf Abuf, bool Swizzle)
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
if (!Decls.TryGetValue(Index, out GlslDeclInfo DeclInfo))
{
throw new InvalidOperationException();
}
Swizzle &= DeclInfo.Size > 1;
return Swizzle ? $"{DeclInfo.Name}.{GetAttrSwizzle(Elem)}" : DeclInfo.Name;
}
private string GetName(ShaderIrOperGpr Gpr)
{
if (GprsCount < Gpr.Index + 1)
{
GprsCount = Gpr.Index + 1;
}
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Gprs, Gpr.Index);
}
return GetRegName(Gpr.Index);
private string GetName(ShaderIrOperImm Imm)
{
return Imm.Imm.ToString(CultureInfo.InvariantCulture);
}
private string GetName(ShaderIrOperPred Pred)
{
if (PredsCount < Pred.Index + 1)
return Pred.IsConst ? "true" : GetNameWithSwizzle(Preds, Pred.Index);
}
private string GetNameWithSwizzle(Dictionary<int, GlslDeclInfo> Decls, int Index)
{
int VecIndex = Index >> 2;
if (Decls.TryGetValue(VecIndex, out GlslDeclInfo DeclInfo))
{
PredsCount = Pred.Index + 1;
if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
{
return $"{DeclInfo.Name}.{GetAttrSwizzle(Index & 3)}";
}
}
return GetPredName(Pred.Index);
}
if (!Decls.TryGetValue(Index, out DeclInfo))
{
return null;
}
private string GetRegName(int GprIndex)
{
return GprIndex == ShaderIrOperGpr.ZRIndex ? "0" : $"{GprName}[{GprIndex}]";
}
private string GetPredName(int PredIndex)
{
return PredIndex == ShaderIrOperPred.UnusedIndex ? "true" : $"{PredName}[{PredIndex}]";
return DeclInfo.Name;
}
private string GetBandExpr(ShaderIrOp Op)
@ -474,6 +584,11 @@ namespace Ryujinx.Graphics.Gal.Shader
$"{GetInOperName(Op.OperandB)}";
}
private string GetExitExpr(ShaderIrOp Op)
{
return "return";
}
private string GetFabsExpr(ShaderIrOp Op)
{
return $"abs({GetInOperName(Op.OperandA)})";
@ -557,14 +672,14 @@ namespace Ryujinx.Graphics.Gal.Shader
{
ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC;
int Handle = Node.Imm - TexStartIndex;
int Handle = ((ShaderIrOperImm)Op.OperandC).Imm;
if (SampsCount < Handle + 1)
if (!Textures.TryGetValue(Handle, out GlslDeclInfo DeclInfo))
{
SampsCount = Handle + 1;
throw new InvalidOperationException();
}
return $"{SampName}[{Handle}]";
return DeclInfo.Name;
}
private string GetTexSamplerCoords(ShaderIrOp Op)

View file

@ -4,6 +4,11 @@ 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

@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gal.Shader
public static ShaderIrOperGpr GetOperGpr39(long OpCode)
{
return new ShaderIrOperGpr((int)(OpCode >> 39) & 0xff);
}
}
public static ShaderIrOperGpr GetOperGpr0(long OpCode)
{
@ -62,8 +62,8 @@ namespace Ryujinx.Graphics.Gal.Shader
public static ShaderIrNode GetOperImmf19_20(long OpCode)
{
//TODO
return new ShaderIrNode();
//TODO: This should be a float immediate.
return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
}
public static ShaderIrOperImm GetOperImm13_36(long OpCode)
@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Gal.Shader
public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode)
{
ShaderIrOperPred Pred = GetPredNode(OpCode);
if (Pred.Index != ShaderIrOperPred.UnusedIndex)
{
Node = new ShaderIrCond(Pred, Node);

View file

@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecoder
{
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType Type)
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
{
ShaderIrBlock Block = new ShaderIrBlock();
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal.Shader
uint Word0 = (uint)Code[Offset++];
uint Word1 = (uint)Code[Offset++];
long OpCode = Word0 | (long)Word1 << 32;
long OpCode = Word0 | (long)Word1 << 32;
ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
@ -21,19 +21,21 @@ namespace Ryujinx.Graphics.Gal.Shader
}
Decode(Block, OpCode);
if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
{
break;
}
}
if (Type == GalShaderType.Fragment)
{
Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x70, 0), new ShaderIrOperGpr(0)));
Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x74, 0), new ShaderIrOperGpr(1)));
Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x78, 0), new ShaderIrOperGpr(2)));
Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x7c, 0), new ShaderIrOperGpr(3)));
}
Block.RunOptimizationPasses();
Block.RunOptimizationPasses(ShaderType);
return Block;
}
private static bool IsFlowChange(ShaderIrInst Inst)
{
return Inst == ShaderIrInst.Exit;
}
}
}

View file

@ -16,14 +16,24 @@ namespace Ryujinx.Graphics.Gal.Shader
Nodes.Add(Node);
}
public void RunOptimizationPasses()
public void RunOptimizationPasses(GalShaderType ShaderType)
{
ShaderOptExprProp.Optimize(Nodes);
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

@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Cgtu,
Cneu,
Cgeu,
Exit,
Fabs,
Fadd,
Fcos,

View file

@ -4,6 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader
{
public const int ZRIndex = 0xff;
public bool IsConst => Index == ZRIndex;
public int Index { get; set; }
public ShaderIrOperGpr(int Index)

View file

@ -5,6 +5,8 @@ namespace Ryujinx.Graphics.Gal.Shader
public const int UnusedIndex = 0x7;
public const int NeverExecute = 0xf;
public bool IsConst => Index >= UnusedIndex;
public int Index { get; set; }
public ShaderIrOperPred(int Index)

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gal.Shader
OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
#region Instructions
Set("111000110000xx", ShaderDecode.Exit);
Set("0101110001011x", ShaderDecode.Fadd_R);
Set("0100110001011x", ShaderDecode.Fadd_C);
Set("0011100x01011x", ShaderDecode.Fadd_Imm);

View file

@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void Optimize(List<ShaderIrNode> Nodes)
public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
{
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
@ -183,6 +183,11 @@ namespace Ryujinx.Graphics.Gal.Shader
//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);
@ -227,7 +232,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Use = GetPredUse(Pred.Index);
}
if (Use?.Asg != null && !IsConditional && TryPropagate(Use))
if (!IsConditional && TryPropagate(Use))
{
Nodes.Remove(Use.Asg);
@ -241,6 +246,16 @@ namespace Ryujinx.Graphics.Gal.Shader
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);

View file

@ -6,6 +6,41 @@ namespace Ryujinx.Graphics.Gpu
{
class MacroInterpreter
{
private enum AssignmentOperation
{
IgnoreAndFetch = 0,
Move = 1,
MoveAndSetMaddr = 2,
FetchAndSend = 3,
MoveAndSend = 4,
FetchAndSetMaddr = 5,
MoveAndSetMaddrThenFetchAndSend = 6,
MoveAndSetMaddrThenSendHigh = 7
}
private enum AluOperation
{
AluReg = 0,
AddImmediate = 1,
BitfieldReplace = 2,
BitfieldExtractLslImm = 3,
BitfieldExtractLslReg = 4,
ReadImmediate = 5
}
private enum AluRegOperation
{
Add = 0,
AddWithCarry = 1,
Subtract = 2,
SubtractWithBorrow = 3,
BitwiseExclusiveOr = 8,
BitwiseOr = 9,
BitwiseAnd = 10,
BitwiseAndNot = 11,
BitwiseNotAnd = 12
}
private NvGpuFifo PFifo;
private INvGpuEngine Engine;
@ -18,6 +53,10 @@ namespace Ryujinx.Graphics.Gpu
private bool Carry;
private int OpCode;
private int PipeOp;
private long Pc;
public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine)
@ -34,11 +73,17 @@ namespace Ryujinx.Graphics.Gpu
{
Reset();
Pc = Position;
Gprs[1] = Param;
Pc = Position;
FetchOpCode(Memory);
while (Step(Memory));
//Due to the delay slot, we still need to execute
//one more instruction before we actually exit.
Step(Memory);
}
private void Reset()
@ -56,46 +101,98 @@ namespace Ryujinx.Graphics.Gpu
private bool Step(AMemory Memory)
{
long BaseAddr = Pc;
long BaseAddr = Pc - 4;
int OpCode = Memory.ReadInt32(Pc);
FetchOpCode(Memory);
Pc += 4;
int Op = OpCode & 7;
if (Op < 7)
if ((OpCode & 7) < 7)
{
//Operation produces a value.
int AsgOp = (OpCode >> 4) & 7;
AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7);
int Result = GetInstResult(OpCode);
int Result = GetAluResult();
switch (AsgOp)
{
//Fetch parameter and ignore result.
case 0: SetDstGpr(OpCode, FetchParam()); break;
case AssignmentOperation.IgnoreAndFetch:
{
SetDstGpr(FetchParam());
break;
}
//Move result.
case 1: SetDstGpr(OpCode, Result); break;
case AssignmentOperation.Move:
{
SetDstGpr(Result);
break;
}
//Move result and use as Method Address.
case 2: SetDstGpr(OpCode, Result); SetMethAddr(Result); break;
case AssignmentOperation.MoveAndSetMaddr:
{
SetDstGpr(Result);
SetMethAddr(Result);
break;
}
//Fetch parameter and send result.
case 3: SetDstGpr(OpCode, FetchParam()); Send(Memory, Result); break;
case AssignmentOperation.FetchAndSend:
{
SetDstGpr(FetchParam());
Send(Memory, Result);
break;
}
//Move and send result.
case 4: SetDstGpr(OpCode, Result); Send(Memory, Result); break;
case AssignmentOperation.MoveAndSend:
{
SetDstGpr(Result);
Send(Memory, Result);
break;
}
//Fetch parameter and use result as Method Address.
case 5: SetDstGpr(OpCode, FetchParam()); SetMethAddr(Result); break;
case AssignmentOperation.FetchAndSetMaddr:
{
SetDstGpr(FetchParam());
SetMethAddr(Result);
break;
}
//Move result and use as Method Address, then fetch and send paramter.
case 6: SetDstGpr(OpCode, Result); SetMethAddr(Result); Send(Memory, FetchParam()); break;
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
{
SetDstGpr(Result);
SetMethAddr(Result);
Send(Memory, FetchParam());
break;
}
//Move result and use as Method Address, then send bits 17:12 of result.
case 7: SetDstGpr(OpCode, Result); SetMethAddr(Result); Send(Memory, (Result >> 12) & 0x3f); break;
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
{
SetDstGpr(Result);
SetMethAddr(Result);
Send(Memory, (Result >> 12) & 0x3f);
break;
}
}
}
else
@ -104,61 +201,59 @@ namespace Ryujinx.Graphics.Gpu
bool OnNotZero = ((OpCode >> 4) & 1) != 0;
bool Taken = OnNotZero
? GetGprA(OpCode) != 0
: GetGprA(OpCode) == 0;
? GetGprA() != 0
: GetGprA() == 0;
if (Taken)
{
bool KeepExecuting = true;
Pc = BaseAddr + (GetImm() << 2);
//When bit 5 is set, branches executes as if delay slots didn't exist.
if ((OpCode & 0x20) == 0)
bool NoDelays = (OpCode & 0x20) != 0;
if (NoDelays)
{
//Execute one more instruction due to delay slot.
KeepExecuting = Step(Memory);
FetchOpCode(Memory);
}
Pc = BaseAddr + (GetImm(OpCode) << 2);
return KeepExecuting;
return true;
}
}
if ((OpCode & 0x80) != 0)
{
//Exit (with a delay slot).
Step(Memory);
bool Exit = (OpCode & 0x80) != 0;
return false;
}
return true;
return !Exit;
}
private int GetInstResult(int OpCode)
private void FetchOpCode(AMemory Memory)
{
int Low = OpCode & 7;
OpCode = PipeOp;
switch (Low)
PipeOp = Memory.ReadInt32(Pc);
Pc += 4;
}
private int GetAluResult()
{
AluOperation Op = (AluOperation)(OpCode & 7);
switch (Op)
{
//Arithmetic or Logical operation.
case 0:
case AluOperation.AluReg:
{
int AluOp = (OpCode >> 17) & 0x1f;
AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f);
return GetAluResult(AluOp, GetGprA(OpCode), GetGprB(OpCode));
return GetAluResult(AluOp, GetGprA(), GetGprB());
}
//Add Immediate.
case 1:
case AluOperation.AddImmediate:
{
return GetGprA(OpCode) + GetImm(OpCode);
return GetGprA() + GetImm();
}
//Bitfield.
case 2:
case 3:
case 4:
case AluOperation.BitfieldReplace:
case AluOperation.BitfieldExtractLslImm:
case AluOperation.BitfieldExtractLslReg:
{
int BfSrcBit = (OpCode >> 17) & 0x1f;
int BfSize = (OpCode >> 22) & 0x1f;
@ -166,13 +261,12 @@ namespace Ryujinx.Graphics.Gpu
int BfMask = (1 << BfSize) - 1;
int Dst = GetGprA(OpCode);
int Src = GetGprB(OpCode);
int Dst = GetGprA();
int Src = GetGprB();
switch (Low)
switch (Op)
{
//Bitfield move.
case 2:
case AluOperation.BitfieldReplace:
{
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
@ -183,16 +277,14 @@ namespace Ryujinx.Graphics.Gpu
return Dst;
}
//Bitfield extract with left shift by immediate.
case 3:
case AluOperation.BitfieldExtractLslImm:
{
Src = (int)((uint)Src >> Dst) & BfMask;
return Src << BfDstBit;
}
//Bitfield extract with left shift by register.
case 4:
case AluOperation.BitfieldExtractLslReg:
{
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
@ -203,69 +295,66 @@ namespace Ryujinx.Graphics.Gpu
break;
}
case 5:
case AluOperation.ReadImmediate:
{
return Read(GetGprA(OpCode) + GetImm(OpCode));
return Read(GetGprA() + GetImm());
}
}
throw new ArgumentException(nameof(OpCode));
}
private int GetAluResult(int AluOp, int A, int B)
private int GetAluResult(AluRegOperation AluOp, int A, int B)
{
switch (AluOp)
{
//Add.
case 0: return A + B;
//Add with Carry.
case 1:
case AluRegOperation.Add:
{
ulong C = Carry ? 1UL : 0UL;
ulong Result = (ulong)A + (ulong)B + C;
ulong Result = (ulong)A + (ulong)B;
Carry = Result > 0xffffffff;
return (int)Result;
}
//Subtract.
case 2: return A - B;
//Subtract with Borrow.
case 3:
case AluRegOperation.AddWithCarry:
{
ulong C = Carry ? 0UL : 1UL;
ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL);
ulong Result = (ulong)A - (ulong)B - C;
Carry = Result > 0xffffffff;
return (int)Result;
}
case AluRegOperation.Subtract:
{
ulong Result = (ulong)A - (ulong)B;
Carry = Result < 0x100000000;
return (int)Result;
}
//Exclusive Or.
case 8: return A ^ B;
case AluRegOperation.SubtractWithBorrow:
{
ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL);
//Or.
case 9: return A | B;
Carry = Result < 0x100000000;
//And.
case 10: return A & B;
return (int)Result;
}
//And Not.
case 11: return A & ~B;
//Not And.
case 12: return ~(A & B);
case AluRegOperation.BitwiseExclusiveOr: return A ^ B;
case AluRegOperation.BitwiseOr: return A | B;
case AluRegOperation.BitwiseAnd: return A & B;
case AluRegOperation.BitwiseAndNot: return A & ~B;
case AluRegOperation.BitwiseNotAnd: return ~(A & B);
}
throw new ArgumentOutOfRangeException(nameof(AluOp));
}
private int GetImm(int OpCode)
private int GetImm()
{
//Note: The immediate is signed, the sign-extension is intended here.
return OpCode >> 14;
@ -277,17 +366,17 @@ namespace Ryujinx.Graphics.Gpu
MethIncr = (Value >> 12) & 0x3f;
}
private void SetDstGpr(int OpCode, int Value)
private void SetDstGpr(int Value)
{
Gprs[(OpCode >> 8) & 7] = Value;
}
private int GetGprA(int OpCode)
private int GetGprA()
{
return GetGprValue((OpCode >> 11) & 7);
}
private int GetGprB(int OpCode)
private int GetGprB()
{
return GetGprValue((OpCode >> 14) & 7);
}

View file

@ -1,4 +1,6 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu
@ -63,11 +65,52 @@ namespace Ryujinx.Graphics.Gpu
int TexCbuf = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
int TexHandle = ReadCb(Memory, TexCbuf, 0x20);
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
for (int Index = 0; Index < 6; Index++)
{
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderOffset + Index * 0x10);
if (Offset == 0)
{
continue;
}
long Position = Gpu.GetCpuAddr(BasePosition + (uint)Offset);
if (Position == -1)
{
continue;
}
//TODO: Find a better way to calculate the size.
int Size = 0x20000;
byte[] Code = AMemoryHelper.ReadBytes(Memory, Position, (uint)Size);
Gpu.Renderer.CreateShader(Position, Code, GetTypeFromProgram(Index));
}
}
private static GalShaderType GetTypeFromProgram(int Program)
{
switch (Program)
{
case 0:
case 1: return GalShaderType.Vertex;
case 2: return GalShaderType.TessControl;
case 3: return GalShaderType.TessEvaluation;
case 4: return GalShaderType.Geometry;
case 5: return GalShaderType.Fragment;
}
throw new ArgumentOutOfRangeException(nameof(Program));
}
private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry)
{
if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddr, out long Position))
if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position))
{
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];

View file

@ -2,9 +2,14 @@ namespace Ryujinx.Graphics.Gpu
{
enum NvGpuEngine3dReg
{
QueryAddr = 0x6c0,
ShaderAddress = 0x582,
QueryAddress = 0x6c0,
QuerySequence = 0x6c2,
QueryControl = 0x6c3,
ShaderControl = 0x800,
ShaderOffset = 0x801,
ShaderMaxGprs = 0x803,
ShaderType = 0x804,
CbSize = 0x8e0,
CbAddress = 0x8e1,
CbOffset = 0x8e3,

View file

@ -1,6 +1,5 @@
using ChocolArm64.Memory;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu
{