More work on GPU refactoring

This commit is contained in:
gdkchan 2018-03-29 22:46:04 -03:00
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 string Name { get; private set; }
public int Index { get; private set; } public int Index { get; private set; }
public int Cbuf { get; private set; }
public int Size { 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.Name = Name;
this.Index = Index; this.Index = Index;
this.Cbuf = Cbuf;
this.Size = Size; this.Size = Size;
} }

View file

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

View file

@ -4,6 +4,11 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
static partial class ShaderDecode 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) public static void Kil(ShaderIrBlock Block, long OpCode)
{ {
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode)); Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));

View file

@ -62,8 +62,8 @@ namespace Ryujinx.Graphics.Gal.Shader
public static ShaderIrNode GetOperImmf19_20(long OpCode) public static ShaderIrNode GetOperImmf19_20(long OpCode)
{ {
//TODO //TODO: This should be a float immediate.
return new ShaderIrNode(); return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
} }
public static ShaderIrOperImm GetOperImm13_36(long OpCode) public static ShaderIrOperImm GetOperImm13_36(long OpCode)

View file

@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
static class ShaderDecoder 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(); ShaderIrBlock Block = new ShaderIrBlock();
@ -21,19 +21,21 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
Decode(Block, OpCode); Decode(Block, OpCode);
}
if (Type == GalShaderType.Fragment) if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
{ {
Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x70, 0), new ShaderIrOperGpr(0))); break;
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; 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); Nodes.Add(Node);
} }
public void RunOptimizationPasses() public void RunOptimizationPasses(GalShaderType ShaderType)
{ {
ShaderOptExprProp.Optimize(Nodes); ShaderOptExprProp.Optimize(Nodes, ShaderType);
} }
public ShaderIrNode[] GetNodes() public ShaderIrNode[] GetNodes()
{ {
return Nodes.ToArray(); 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, Cgtu,
Cneu, Cneu,
Cgeu, Cgeu,
Exit,
Fabs, Fabs,
Fadd, Fadd,
Fcos, Fcos,

View file

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

View file

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

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gal.Shader
OpCodes = new ShaderDecodeFunc[1 << EncodingBits]; OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
#region Instructions #region Instructions
Set("111000110000xx", ShaderDecode.Exit);
Set("0101110001011x", ShaderDecode.Fadd_R); Set("0101110001011x", ShaderDecode.Fadd_R);
Set("0100110001011x", ShaderDecode.Fadd_C); Set("0100110001011x", ShaderDecode.Fadd_C);
Set("0011100x01011x", ShaderDecode.Fadd_Imm); 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>(); 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 //to a register took place. We traverse the expression tree to find
//all registers being used, if any of those registers was assigned //all registers being used, if any of those registers was assigned
//after the assignment to be propagated, then we can't propagate. //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)>(); List<(int, UseSite)> UseList = new List<(int, UseSite)>();
FindRegUses(UseList, Use.Asg, Use.Asg.Src); FindRegUses(UseList, Use.Asg, Use.Asg.Src);
@ -227,7 +232,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Use = GetPredUse(Pred.Index); Use = GetPredUse(Pred.Index);
} }
if (Use?.Asg != null && !IsConditional && TryPropagate(Use)) if (!IsConditional && TryPropagate(Use))
{ {
Nodes.Remove(Use.Asg); Nodes.Remove(Use.Asg);
@ -241,6 +246,16 @@ namespace Ryujinx.Graphics.Gal.Shader
foreach (RegUse Use in Uses.Values) 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)) if (TryPropagate(Use))
{ {
Nodes.Remove(Use.Asg); Nodes.Remove(Use.Asg);

View file

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

View file

@ -1,4 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
@ -63,11 +65,52 @@ namespace Ryujinx.Graphics.Gpu
int TexCbuf = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); int TexCbuf = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
int TexHandle = ReadCb(Memory, TexCbuf, 0x20); 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) 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 Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];

View file

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

View file

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