diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs b/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs index 29da3dfcbc..19a54d1bb3 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs @@ -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; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 42112c48fe..aa7d9d711d 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -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 InputAttributes; - private SortedDictionary OutputAttributes; + private Dictionary Textures; - private HashSet UsedCbufs; + private Dictionary<(int, int), GlslDeclInfo> Uniforms; + + private Dictionary InputAttributes; + private Dictionary OutputAttributes; + + private Dictionary Gprs; + private Dictionary 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(); - OutputAttributes = new SortedDictionary(); + Uniforms = new Dictionary<(int, int), GlslDeclInfo>(); - UsedCbufs = new HashSet(); + Textures = new Dictionary(); - BodySB = new StringBuilder(); + InputAttributes = new Dictionary(); + OutputAttributes = new Dictionary(); + + Gprs = new Dictionary(); + Preds = new Dictionary(); + + 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 Decls, string InOut) { bool PrintNl = false; - foreach (KeyValuePair 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 KV in OutputAttributes) + private void PrintDeclPreds() + { + PrintDecls(Preds.Values, "bool"); + } + + private void PrintDecls(ICollection 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 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 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) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs index 7d6796be27..d3feb92e56 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -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)); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index 23330dce77..2ab1bcbf67 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -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); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 694d84a43e..779bbf9230 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -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; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index cf4bf961e2..1a96d3be90 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -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; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index f4826ff095..b9ca8bfb29 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gal.Shader Cgtu, Cneu, Cgeu, + Exit, Fabs, Fadd, Fcos, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs index c76265f6d5..5c69d6a67a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs @@ -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) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs index a6ca2b2847..74cca0efef 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs @@ -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) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 77a98d3933..d9c85f3697 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -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); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs index 36bce6d581..69457aebde 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void Optimize(List Nodes) + public static void Optimize(List Nodes, GalShaderType ShaderType) { Dictionary Uses = new Dictionary(); @@ -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); diff --git a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs index caaf2e2e66..233baac8bd 100644 --- a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs +++ b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs @@ -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); } diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index 1a56d172ea..2814b65e6e 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -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]; diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs index 6e87e365eb..033e8a107f 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -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, diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs index 0de7365d66..c2be18cba2 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs @@ -1,6 +1,5 @@ using ChocolArm64.Memory; using System.Collections.Concurrent; -using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu {