diff --git a/Ryujinx.Core/Gpu/TextureFactory.cs b/Ryujinx.Core/Gpu/TextureFactory.cs index 68b48a1fb..5206c125b 100644 --- a/Ryujinx.Core/Gpu/TextureFactory.cs +++ b/Ryujinx.Core/Gpu/TextureFactory.cs @@ -11,6 +11,11 @@ namespace Ryujinx.Core.Gpu GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); + GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7); + GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7); + GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); + GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7); + long TextureAddress = (uint)Tic[1]; TextureAddress |= (long)((ushort)Tic[2]) << 32; @@ -37,7 +42,15 @@ namespace Ryujinx.Core.Gpu byte[] Data = TextureReader.Read(Vmm, Texture); - return new GalTexture(Data, Width, Height, Format); + return new GalTexture( + Data, + Width, + Height, + Format, + XSource, + YSource, + ZSource, + WSource); } public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs index b504f81a3..8aa26dd3f 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -141,6 +141,13 @@ namespace Ryujinx.Core.OsHle.Kernel private void SvcSetThreadCoreMask(AThreadState ThreadState) { + //FIXME: This is wrong, but the "correct" way to handle + //this svc causes deadlocks when more often. + //There is probably something wrong with it still. + ThreadState.X0 = 0; + + return; + int Handle = (int)ThreadState.X0; int IdealCore = (int)ThreadState.X1; long CoreMask = (long)ThreadState.X2; diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs index 7de5a4d9f..0d1aa5e40 100644 --- a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs +++ b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs @@ -18,11 +18,12 @@ namespace Ryujinx.Core.OsHle.Services.Nifm { m_Commands = new Dictionary() { - { 0, GetRequestState }, - { 1, GetResult }, - { 2, GetSystemEventReadableHandles }, - { 3, Cancel }, - { 4, Submit }, + { 0, GetRequestState }, + { 1, GetResult }, + { 2, GetSystemEventReadableHandles }, + { 3, Cancel }, + { 4, Submit }, + { 11, SetConnectionConfirmationOption } }; Event = new KEvent(); @@ -69,6 +70,13 @@ namespace Ryujinx.Core.OsHle.Services.Nifm return 0; } + public long SetConnectionConfirmationOption(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + + return 0; + } + public void Dispose() { Dispose(true); diff --git a/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs index 42e438414..dbf01c6bd 100644 --- a/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs @@ -1,3 +1,4 @@ +using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -13,8 +14,15 @@ namespace Ryujinx.Core.OsHle.Services.Prepo { m_Commands = new Dictionary() { - //... + { 10101, SaveReportWithUser } }; } + + public static long SaveReportWithUser(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServicePrepo, "Stubbed."); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTexture.cs b/Ryujinx.Graphics/Gal/GalTexture.cs index fcf1f1ad2..75aef3072 100644 --- a/Ryujinx.Graphics/Gal/GalTexture.cs +++ b/Ryujinx.Graphics/Gal/GalTexture.cs @@ -9,12 +9,29 @@ namespace Ryujinx.Graphics.Gal public GalTextureFormat Format; - public GalTexture(byte[] Data, int Width, int Height, GalTextureFormat Format) + public GalTextureSource XSource; + public GalTextureSource YSource; + public GalTextureSource ZSource; + public GalTextureSource WSource; + + public GalTexture( + byte[] Data, + int Width, + int Height, + GalTextureFormat Format, + GalTextureSource XSource, + GalTextureSource YSource, + GalTextureSource ZSource, + GalTextureSource WSource) { - this.Data = Data; - this.Width = Width; - this.Height = Height; - this.Format = Format; + this.Data = Data; + this.Width = Width; + this.Height = Height; + this.Format = Format; + this.XSource = XSource; + this.YSource = YSource; + this.ZSource = ZSource; + this.WSource = WSource; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureSource.cs b/Ryujinx.Graphics/Gal/GalTextureSource.cs new file mode 100644 index 000000000..72dbec606 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureSource.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalTextureSource + { + Zero = 0, + Red = 2, + Green = 3, + Blue = 4, + Alpha = 5, + OneInt = 6, + OneFloat = 7 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 4cc0a0397..d266a87a8 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -81,6 +81,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new NotImplementedException(Format.ToString()); } + public static All GetTextureSwizzle(GalTextureSource Source) + { + switch (Source) + { + case GalTextureSource.Zero: return All.Zero; + case GalTextureSource.Red: return All.Red; + case GalTextureSource.Green: return All.Green; + case GalTextureSource.Blue: return All.Blue; + case GalTextureSource.Alpha: return All.Alpha; + case GalTextureSource.OneInt: return All.One; + case GalTextureSource.OneFloat: return All.One; + } + + throw new ArgumentException(nameof(Source)); + } + public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap) { switch (Wrap) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index fff6362b4..e740a32e4 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -87,10 +87,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Create(long Tag, GalShaderType Type, byte[] Data) { - Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data)); + Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Tag, Data)); } - private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data) + private ShaderStage ShaderStageFactory(GalShaderType Type, long Tag, byte[] Data) { GlslProgram Program = GetGlslProgram(Data, Type); @@ -140,11 +140,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL { foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) { - float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4); - int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name); - GL.Uniform1(Location, Value); + int Count = Data.Length >> 2; + + //The Index is the index of the last element, + //so we can add 1 to get the uniform array size. + Count = Math.Min(Count, DeclInfo.Index + 1); + + unsafe + { + fixed (byte* Ptr = Data) + { + GL.Uniform1(Location, Count, (float*)Ptr); + } + } } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 9ea25056c..8dcfb2bdb 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -51,6 +51,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL Type, Texture.Data); } + + int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource); + int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Texture.YSource); + int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Texture.ZSource); + int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Texture.WSource); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, SwizzleB); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA); } public void Bind(int Index) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index cd901747d..2650569e6 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Gal.Shader private Dictionary m_Textures; - private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms; + private Dictionary m_Uniforms; private Dictionary m_InAttributes; private Dictionary m_OutAttributes; @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gal.Shader public IReadOnlyDictionary Textures => m_Textures; - public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms; + public IReadOnlyDictionary Uniforms => m_Uniforms; public IReadOnlyDictionary InAttributes => m_InAttributes; public IReadOnlyDictionary OutAttributes => m_OutAttributes; @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gal.Shader StagePrefix = StagePrefixes[(int)ShaderType] + "_"; - m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>(); + m_Uniforms = new Dictionary(); m_Textures = new Dictionary(); @@ -124,11 +124,27 @@ namespace Ryujinx.Graphics.Gal.Shader case ShaderIrOperCbuf Cbuf: { - string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs; + if (m_Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) + { + DeclInfo.SetCbufOffs(Cbuf.Pos); + } + else + { + string Name = StagePrefix + UniformName + Cbuf.Index; - ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index); + DeclInfo = new ShaderDeclInfo(Name, Cbuf.Pos, Cbuf.Index); - m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo); + m_Uniforms.Add(Cbuf.Index, DeclInfo); + } + + if (Cbuf.Offs != null) + { + //The constant buffer is being accessed as an array, + //we have no way to know the max element it may access in this case. + //Here, we just assume the array size with arbitrary values. + //TODO: Find a better solution for this. + DeclInfo.SetCbufOffs(Cbuf.Pos + 15); + } break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 1e0824d2f..d6f171f4c 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -6,7 +6,7 @@ using System.Text; namespace Ryujinx.Graphics.Gal.Shader { - class GlslDecompiler + public class GlslDecompiler { private delegate string GetInstExpr(ShaderIrOp Op); @@ -31,6 +31,8 @@ namespace Ryujinx.Graphics.Gal.Shader { InstsExpr = new Dictionary() { + { ShaderIrInst.Abs, GetAbsExpr }, + { ShaderIrInst.Add, GetAddExpr }, { ShaderIrInst.And, GetAndExpr }, { ShaderIrInst.Asr, GetAsrExpr }, { ShaderIrInst.Band, GetBandExpr }, @@ -45,8 +47,8 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Clt, GetCltExpr }, { ShaderIrInst.Cne, GetCneExpr }, { ShaderIrInst.Exit, GetExitExpr }, - { ShaderIrInst.Fabs, GetFabsExpr }, - { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fabs, GetAbsExpr }, + { ShaderIrInst.Fadd, GetAddExpr }, { ShaderIrInst.Fceq, GetCeqExpr }, { ShaderIrInst.Fcge, GetCgeExpr }, { ShaderIrInst.Fcgt, GetCgtExpr }, @@ -59,8 +61,10 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Ffma, GetFfmaExpr }, { ShaderIrInst.Flg2, GetFlg2Expr }, { ShaderIrInst.Floor, GetFloorExpr }, - { ShaderIrInst.Fmul, GetFmulExpr }, - { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Fmax, GetFmaxExpr }, + { ShaderIrInst.Fmin, GetFminExpr }, + { ShaderIrInst.Fmul, GetMulExpr }, + { ShaderIrInst.Fneg, GetNegExpr }, { ShaderIrInst.Frcp, GetFrcpExpr }, { ShaderIrInst.Frsq, GetFrsqExpr }, { ShaderIrInst.Fsin, GetFsinExpr }, @@ -68,10 +72,14 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Ftou, GetFtouExpr }, { ShaderIrInst.Ipa, GetIpaExpr }, { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Lsl, GetLslExpr }, { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Mul, GetMulExpr }, + { ShaderIrInst.Neg, GetNegExpr }, { ShaderIrInst.Not, GetNotExpr }, { ShaderIrInst.Or, GetOrExpr }, { ShaderIrInst.Stof, GetStofExpr }, + { ShaderIrInst.Sub, GetSubExpr }, { ShaderIrInst.Texq, GetTexqExpr }, { ShaderIrInst.Texs, GetTexsExpr }, { ShaderIrInst.Trunc, GetTruncExpr }, @@ -100,7 +108,7 @@ namespace Ryujinx.Graphics.Gal.Shader PrintDeclGprs(); PrintDeclPreds(); - PrintBlockScope("void main()", 1, Nodes); + PrintBlockScope(Nodes, 0, Nodes.Length, "void main()", 1); string GlslCode = SB.ToString(); @@ -124,7 +132,7 @@ namespace Ryujinx.Graphics.Gal.Shader foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) { - SB.AppendLine($"uniform {GetDecl(DeclInfo)};"); + SB.AppendLine($"uniform {GetDecl(DeclInfo)}[{DeclInfo.Index + 1}];"); } if (Decl.Uniforms.Count > 0) @@ -221,7 +229,12 @@ namespace Ryujinx.Graphics.Gal.Shader return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name; } - private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes) + private void PrintBlockScope( + ShaderIrNode[] Nodes, + int Start, + int Count, + string ScopeName, + int IdentationLevel) { string Identation = string.Empty; @@ -244,7 +257,7 @@ namespace Ryujinx.Graphics.Gal.Shader Identation += IdentationStr; } - for (int Index = 0; Index < Nodes.Length; Index++) + for (int Index = Start; Index < Start + Count; Index++) { ShaderIrNode Node = Nodes[Index]; @@ -257,9 +270,44 @@ namespace Ryujinx.Graphics.Gal.Shader IfExpr = "!(" + IfExpr + ")"; } - string SubScopeName = "if (" + IfExpr + ")"; + if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) + { + ShaderIrLabel Label = (ShaderIrLabel)Op.OperandA; + + int Target = FindLabel(Nodes, Label, Index + 1); + + int IfCount = Target - Index - 1; + + string SubScopeName = "if (!" + IfExpr + ")"; + + if (Nodes[Index + IfCount] is ShaderIrOp LastOp && LastOp.Inst == ShaderIrInst.Bra) + { + Target = FindLabel(Nodes, (ShaderIrLabel)LastOp.OperandA, Index + 1); + + int ElseCount = Target - (Index + 1 + IfCount); + + PrintBlockScope(Nodes, Index + 1, IfCount - 1, SubScopeName, IdentationLevel + 1); + + PrintBlockScope(Nodes, Index + 1 + IfCount, ElseCount, "else", IdentationLevel + 1); + + Index += IfCount + ElseCount; + } + else + { + PrintBlockScope(Nodes, Index + 1, IfCount, SubScopeName, IdentationLevel + 1); + + Index += IfCount; + } + } + else + { + string SubScopeName = "if (" + IfExpr + ")"; + + ShaderIrNode[] Child = new ShaderIrNode[] { Cond.Child }; + + PrintBlockScope(Child, 0, 1, SubScopeName, IdentationLevel + 1); + } - PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); } else if (Node is ShaderIrAsg Asg) { @@ -288,6 +336,14 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); } + else if (Node is ShaderIrLabel Label) + { + //TODO: Add support for loops here. + } + else if (Node is ShaderIrCmnt Cmnt) + { + SB.AppendLine(Identation + "// " + Cmnt.Comment); + } else { throw new InvalidOperationException(); @@ -297,6 +353,21 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine(LastLine); } + private int FindLabel(ShaderIrNode[] Nodes, ShaderIrLabel Label, int Start) + { + int Target; + + for (Target = Start; Target < Nodes.Length; Target++) + { + if (Nodes[Target] == Label) + { + return Target; + } + } + + throw new InvalidOperationException(); + } + private bool IsValidOutOper(ShaderIrNode Node) { if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) @@ -383,12 +454,23 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperCbuf Cbuf) { - if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo)) + if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) { throw new InvalidOperationException(); } - return DeclInfo.Name; + if (Cbuf.Offs != null) + { + //Note: We assume that the register value is always a multiple of 4. + //This may not be aways the case. + string Offset = "(floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ") >> 2)"; + + return DeclInfo.Name + "[" + Cbuf.Pos + " + " + Offset + "]"; + } + else + { + return DeclInfo.Name + "[" + Cbuf.Pos + "]"; + } } private string GetOutAbufName(ShaderIrOperAbuf Abuf) @@ -473,6 +555,10 @@ namespace Ryujinx.Graphics.Gal.Shader return "xyzw".Substring(Elem, 1); } + private string GetAbsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); + + private string GetAddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); + private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&"); private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>"); @@ -506,10 +592,6 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetExitExpr(ShaderIrOp Op) => "return"; - private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); - - private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); - private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2"); @@ -522,9 +604,8 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); - private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); - - private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); + private string GetFmaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); + private string GetFminExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); @@ -546,12 +627,17 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetKilExpr(ShaderIrOp Op) => "discard"; + private string GetLslExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<<"); private string GetLsrExpr(ShaderIrOp Op) { return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " + GetOperExpr(Op, Op.OperandB) + ")"; } + private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); + + private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); + private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~"); private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|"); @@ -561,6 +647,8 @@ namespace Ryujinx.Graphics.Gal.Shader return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; } + private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-"); + private string GetTexqExpr(ShaderIrOp Op) { ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; @@ -621,6 +709,12 @@ namespace Ryujinx.Graphics.Gal.Shader return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; } + private string GetBinaryCall(ShaderIrOp Op, string FuncName) + { + return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + private string GetTernaryCall(ShaderIrOp Op, string FuncName) { return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + @@ -717,7 +811,10 @@ namespace Ryujinx.Graphics.Gal.Shader { float Value = BitConverter.Int32BitsToSingle(Imm.Value); - return Value.ToString(CultureInfo.InvariantCulture); + if (!float.IsNaN(Value) && !float.IsInfinity(Value)) + { + return Value.ToString(CultureInfo.InvariantCulture); + } } break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs index 729b6f1de..a7af05aef 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.Shader { - struct GlslProgram + public struct GlslProgram { public string Code { get; private set; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 42609bcef..1bb5f4780 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -6,6 +6,21 @@ namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { + public static void Bfe_C(ShaderIrBlock Block, long OpCode) + { + EmitBfe(Block, OpCode, ShaderOper.CR); + } + + public static void Bfe_I(ShaderIrBlock Block, long OpCode) + { + EmitBfe(Block, OpCode, ShaderOper.Imm); + } + + public static void Bfe_R(ShaderIrBlock Block, long OpCode) + { + EmitBfe(Block, OpCode, ShaderOper.RR); + } + public static void Fadd_C(ShaderIrBlock Block, long OpCode) { EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd); @@ -23,25 +38,40 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Ffma_CR(ShaderIrBlock Block, long OpCode) { - EmitAluFfma(Block, OpCode, ShaderOper.CR); + EmitFfma(Block, OpCode, ShaderOper.CR); } public static void Ffma_I(ShaderIrBlock Block, long OpCode) { - EmitAluFfma(Block, OpCode, ShaderOper.Immf); + EmitFfma(Block, OpCode, ShaderOper.Immf); } public static void Ffma_RC(ShaderIrBlock Block, long OpCode) { - EmitAluFfma(Block, OpCode, ShaderOper.RC); + EmitFfma(Block, OpCode, ShaderOper.RC); } public static void Ffma_RR(ShaderIrBlock Block, long OpCode) { - EmitAluFfma(Block, OpCode, ShaderOper.RR); + EmitFfma(Block, OpCode, ShaderOper.RR); } - public static void Fmul32i(ShaderIrBlock Block, long OpCode) + public static void Fmnmx_C(ShaderIrBlock Block, long OpCode) + { + EmitFmnmx(Block, OpCode, ShaderOper.CR); + } + + public static void Fmnmx_I(ShaderIrBlock Block, long OpCode) + { + EmitFmnmx(Block, OpCode, ShaderOper.Immf); + } + + public static void Fmnmx_R(ShaderIrBlock Block, long OpCode) + { + EmitFmnmx(Block, OpCode, ShaderOper.RR); + } + + public static void Fmul_I32(ShaderIrBlock Block, long OpCode) { ShaderIrNode OperA = GetOperGpr8 (OpCode); ShaderIrNode OperB = GetOperImmf32_20(OpCode); @@ -106,6 +136,21 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + public static void Iscadd_C(ShaderIrBlock Block, long OpCode) + { + EmitIscadd(Block, OpCode, ShaderOper.CR); + } + + public static void Iscadd_I(ShaderIrBlock Block, long OpCode) + { + EmitIscadd(Block, OpCode, ShaderOper.Imm); + } + + public static void Iscadd_R(ShaderIrBlock Block, long OpCode) + { + EmitIscadd(Block, OpCode, ShaderOper.RR); + } + public static void Isetp_C(ShaderIrBlock Block, long OpCode) { EmitIsetp(Block, OpCode, ShaderOper.CR); @@ -121,12 +166,12 @@ namespace Ryujinx.Graphics.Gal.Shader EmitIsetp(Block, OpCode, ShaderOper.RR); } - public static void Lop32i(ShaderIrBlock Block, long OpCode) + public static void Lop_I32(ShaderIrBlock Block, long OpCode) { int SubOp = (int)(OpCode >> 53) & 3; - bool Ia = ((OpCode >> 55) & 1) != 0; - bool Ib = ((OpCode >> 56) & 1) != 0; + bool InvA = ((OpCode >> 55) & 1) != 0; + bool InvB = ((OpCode >> 56) & 1) != 0; ShaderIrInst Inst = 0; @@ -137,13 +182,13 @@ namespace Ryujinx.Graphics.Gal.Shader case 2: Inst = ShaderIrInst.Xor; break; } - ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), Ia); + ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); //SubOp == 3 is pass, used by the not instruction //which just moves the inverted register value. if (SubOp < 3) { - ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), Ib); + ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB); @@ -159,8 +204,8 @@ namespace Ryujinx.Graphics.Gal.Shader { int SubOp = (int)(OpCode >> 20) & 7; - bool Aa = ((OpCode >> 46) & 1) != 0; - bool Na = ((OpCode >> 48) & 1) != 0; + bool AbsA = ((OpCode >> 46) & 1) != 0; + bool NegA = ((OpCode >> 48) & 1) != 0; ShaderIrInst Inst = 0; @@ -178,11 +223,26 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode OperA = GetOperGpr8(OpCode); - ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na)); + ShaderIrOp Op = new ShaderIrOp(Inst, GetAluFabsFneg(OperA, AbsA, NegA)); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + public static void Shl_C(ShaderIrBlock Block, long OpCode) + { + EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl); + } + + public static void Shl_I(ShaderIrBlock Block, long OpCode) + { + EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl); + } + + public static void Shl_R(ShaderIrBlock Block, long OpCode) + { + EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl); + } + public static void Shr_C(ShaderIrBlock Block, long OpCode) { EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode)); @@ -233,16 +293,16 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderOper Oper, ShaderIrInst Inst) { - bool Nb = ((OpCode >> 45) & 1) != 0; - bool Aa = ((OpCode >> 46) & 1) != 0; - bool Na = ((OpCode >> 48) & 1) != 0; - bool Ab = ((OpCode >> 49) & 1) != 0; + bool NegB = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 46) & 1) != 0; + bool NegA = ((OpCode >> 48) & 1) != 0; + bool AbsB = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; if (Inst == ShaderIrInst.Fadd) { - OperA = GetAluAbsNeg(OperA, Aa, Na); + OperA = GetAluFabsFneg(OperA, AbsA, NegA); } switch (Oper) @@ -254,17 +314,73 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperB = GetAluAbsNeg(OperB, Ab, Nb); + OperB = GetAluFabsFneg(OperB, AbsB, NegB); ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } - private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + private static void EmitBfe(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { - bool Nb = ((OpCode >> 48) & 1) != 0; - bool Nc = ((OpCode >> 49) & 1) != 0; + //TODO: Handle the case where position + length + //is greater than the word size, in this case the sign bit + //needs to be replicated to fill the remaining space. + bool NegB = ((OpCode >> 48) & 1) != 0; + bool NegA = ((OpCode >> 49) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + ShaderIrNode Op; + + bool Signed = ((OpCode >> 48) & 1) != 0; //? + + if (OperB is ShaderIrOperImm PosLen) + { + int Position = (PosLen.Value >> 0) & 0xff; + int Length = (PosLen.Value >> 8) & 0xff; + + int LSh = 32 - (Position + Length); + + ShaderIrInst RightShift = Signed + ? ShaderIrInst.Asr + : ShaderIrInst.Lsr; + + Op = new ShaderIrOp(ShaderIrInst.Lsl, OperA, new ShaderIrOperImm(LSh)); + Op = new ShaderIrOp(RightShift, Op, new ShaderIrOperImm(LSh + Position)); + } + else + { + ShaderIrOperImm Shift = new ShaderIrOperImm(8); + ShaderIrOperImm Mask = new ShaderIrOperImm(0xff); + + ShaderIrNode OpPos, OpLen; + + OpPos = new ShaderIrOp(ShaderIrInst.And, OperB, Mask); + OpLen = new ShaderIrOp(ShaderIrInst.Lsr, OperB, Shift); + OpLen = new ShaderIrOp(ShaderIrInst.And, OpLen, Mask); + + Op = new ShaderIrOp(ShaderIrInst.Lsr, OperA, OpPos); + + Op = ExtendTo32(Op, Signed, OpLen); + } + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + bool NegB = ((OpCode >> 48) & 1) != 0; + bool NegC = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC; @@ -278,15 +394,15 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperB = GetAluNeg(OperB, Nb); + OperB = GetAluFneg(OperB, NegB); if (Oper == ShaderOper.RC) { - OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc); + OperC = GetAluFneg(GetOperCbuf34(OpCode), NegC); } else { - OperC = GetAluNeg(GetOperGpr39(OpCode), Nc); + OperC = GetAluFneg(GetOperGpr39(OpCode), NegC); } ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC); @@ -294,6 +410,84 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + bool NegB = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 46) & 1) != 0; + bool NegA = ((OpCode >> 48) & 1) != 0; + bool AbsB = ((OpCode >> 49) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperB = GetAluFabsFneg(OperB, AbsB, NegB); + + ShaderIrOperPred Pred = GetOperPred39(OpCode); + + ShaderIrOp Op; + + if (Pred.IsConst) + { + bool IsMax = ((OpCode >> 42) & 1) != 0; + + Op = new ShaderIrOp(IsMax + ? ShaderIrInst.Fmax + : ShaderIrInst.Fmin, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + else + { + ShaderIrNode PredN = GetOperPred39N(OpCode); + + ShaderIrOp OpMax = new ShaderIrOp(ShaderIrInst.Fmax, OperA, OperB); + ShaderIrOp OpMin = new ShaderIrOp(ShaderIrInst.Fmin, OperA, OperB); + + ShaderIrAsg AsgMax = new ShaderIrAsg(GetOperGpr0(OpCode), OpMax); + ShaderIrAsg AsgMin = new ShaderIrAsg(GetOperGpr0(OpCode), OpMin); + + Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMax, Not: true), OpCode)); + Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMin, Not: false), OpCode)); + } + } + + private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + bool NegB = ((OpCode >> 48) & 1) != 0; + bool NegA = ((OpCode >> 49) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + ShaderIrOperImm Scale = GetOperImm5_39(OpCode); + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperA = GetAluIneg(OperA, NegA); + OperB = GetAluIneg(OperB, NegB); + + ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale); + ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode)); + } + private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { EmitSet(Block, OpCode, true, Oper); @@ -306,10 +500,11 @@ namespace Ryujinx.Graphics.Gal.Shader private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { - bool Na = ((OpCode >> 43) & 1) != 0; - bool Ab = ((OpCode >> 44) & 1) != 0; - bool Nb = ((OpCode >> 53) & 1) != 0; - bool Aa = ((OpCode >> 54) & 1) != 0; + bool NegA = ((OpCode >> 43) & 1) != 0; + bool AbsB = ((OpCode >> 44) & 1) != 0; + bool BoolFloat = ((OpCode >> 52) & 1) != 0; + bool NegB = ((OpCode >> 53) & 1) != 0; + bool AbsA = ((OpCode >> 54) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; @@ -327,8 +522,8 @@ namespace Ryujinx.Graphics.Gal.Shader if (IsFloat) { - OperA = GetAluAbsNeg(OperA, Aa, Na); - OperB = GetAluAbsNeg(OperB, Ab, Nb); + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + OperB = GetAluFabsFneg(OperB, AbsB, NegB); CmpInst = GetCmpF(OpCode); } @@ -343,8 +538,18 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrOperPred PNode = GetOperPred39(OpCode); - ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0); - ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1); + ShaderIrNode Imm0, Imm1; + + if (BoolFloat) + { + Imm0 = new ShaderIrOperImmf(0); + Imm1 = new ShaderIrOperImmf(1); + } + else + { + Imm0 = new ShaderIrOperImm(0); + Imm1 = new ShaderIrOperImm(-1); + } ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0); ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1); @@ -378,10 +583,10 @@ namespace Ryujinx.Graphics.Gal.Shader private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { - bool Aa = ((OpCode >> 7) & 1) != 0; - bool Np = ((OpCode >> 42) & 1) != 0; - bool Na = ((OpCode >> 43) & 1) != 0; - bool Ab = ((OpCode >> 44) & 1) != 0; + bool AbsA = ((OpCode >> 7) & 1) != 0; + bool NegP = ((OpCode >> 42) & 1) != 0; + bool NegA = ((OpCode >> 43) & 1) != 0; + bool AbsB = ((OpCode >> 44) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; @@ -399,8 +604,8 @@ namespace Ryujinx.Graphics.Gal.Shader if (IsFloat) { - OperA = GetAluAbsNeg(OperA, Aa, Na); - OperB = GetAluAbs (OperB, Ab); + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + OperB = GetAluFabs (OperB, AbsB); CmpInst = GetCmpF(OpCode); } @@ -426,7 +631,7 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode P2NNode = P2Node; - if (Np) + if (NegP) { P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs index d3feb92e5..b5f3e0b72 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -1,9 +1,27 @@ +using System; + using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { + public static void Bra(ShaderIrBlock Block, long OpCode) + { + if ((OpCode & 0x20) != 0) + { + //This reads the target offset from the constant buffer. + //Almost impossible to support with GLSL. + throw new NotImplementedException(); + } + + int Target = ((int)(OpCode >> 20) << 8) >> 8; + + Target += Block.Position + 8; + + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Block.GetLabel(Target)), OpCode)); + } + public static void Exit(ShaderIrBlock Block, long OpCode) { Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode)); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index efbce20f2..73775ccaf 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -35,6 +35,13 @@ namespace Ryujinx.Graphics.Gal.Shader (int)(OpCode >> 20) & 0x3fff); } + public static ShaderIrOperCbuf GetOperCbuf36(long OpCode) + { + return new ShaderIrOperCbuf( + (int)(OpCode >> 36) & 0x1f, + (int)(OpCode >> 22) & 0x3fff, GetOperGpr8(OpCode)); + } + public static ShaderIrOperGpr GetOperGpr8(long OpCode) { return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff); @@ -60,6 +67,11 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff); } + public static ShaderIrOperImm GetOperImm5_39(long OpCode) + { + return new ShaderIrOperImm((int)(OpCode >> 39) & 0x1f); + } + public static ShaderIrOperImm GetOperImm13_36(long OpCode) { return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff); @@ -210,24 +222,69 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperPred(Pred); } - public static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg) + public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg) { - return GetAluNeg(GetAluAbs(Node, Abs), Neg); + return GetAluFneg(GetAluFabs(Node, Abs), Neg); } - public static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs) + public static ShaderIrNode GetAluFabs(ShaderIrNode Node, bool Abs) { return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node; } - public static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg) + public static ShaderIrNode GetAluFneg(ShaderIrNode Node, bool Neg) { return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node; } + public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg) + { + return GetAluIneg(GetAluIabs(Node, Abs), Neg); + } + + public static ShaderIrNode GetAluIabs(ShaderIrNode Node, bool Abs) + { + return Abs ? new ShaderIrOp(ShaderIrInst.Abs, Node) : Node; + } + + public static ShaderIrNode GetAluIneg(ShaderIrNode Node, bool Neg) + { + return Neg ? new ShaderIrOp(ShaderIrInst.Neg, Node) : Node; + } + public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not) { return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node; } + + public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, int Size) + { + int Shift = 32 - Size; + + ShaderIrInst RightShift = Signed + ? ShaderIrInst.Asr + : ShaderIrInst.Lsr; + + Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, new ShaderIrOperImm(Shift)); + Node = new ShaderIrOp(RightShift, Node, new ShaderIrOperImm(Shift)); + + return Node; + } + + public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, ShaderIrNode Size) + { + ShaderIrOperImm WordSize = new ShaderIrOperImm(32); + + ShaderIrOp Shift = new ShaderIrOp(ShaderIrInst.Sub, WordSize, Size); + + ShaderIrInst RightShift = Signed + ? ShaderIrInst.Asr + : ShaderIrInst.Lsr; + + Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, Shift); + Node = new ShaderIrOp(RightShift, Node, Shift); + + return Node; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index 24a61c0c2..5ca485a3b 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -1,3 +1,5 @@ +using System; + using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; namespace Ryujinx.Graphics.Gal.Shader @@ -20,6 +22,41 @@ namespace Ryujinx.Graphics.Gal.Shader } } + public static void Ld_C(ShaderIrBlock Block, long OpCode) + { + int Type = (int)(OpCode >> 48) & 7; + + if (Type > 5) + { + throw new InvalidOperationException(); + } + + int Count = Type == 5 ? 2 : 1; + + for (int Index = 0; Index < Count; Index++) + { + ShaderIrOperCbuf OperA = GetOperCbuf36(OpCode); + ShaderIrOperGpr OperD = GetOperGpr0 (OpCode); + + OperA.Pos += Index; + OperD.Index += Index; + + ShaderIrNode Node = OperA; + + if (Type < 4) + { + //This is a 8 or 16 bits type. + bool Signed = (Type & 1) != 0; + + int Size = 8 << (Type >> 1); + + Node = ExtendTo32(Node, Signed, Size); + } + + Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Node), OpCode)); + } + } + public static void St_A(ShaderIrBlock Block, long OpCode) { ShaderIrNode[] Opers = GetOperAbuf20(OpCode); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index b9cf02f1f..dfcea905a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -99,6 +99,13 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode)); } + public static void Mov_I32(ShaderIrBlock Block, long OpCode) + { + ShaderIrOperImm Imm = GetOperImm32_20(OpCode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode)); + } + public static void Mov_R(ShaderIrBlock Block, long OpCode) { ShaderIrOperGpr Gpr = GetOperGpr20(OpCode); @@ -106,17 +113,10 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode)); } - public static void Mov32i(ShaderIrBlock Block, long OpCode) - { - ShaderIrOperImm Imm = GetOperImm32_20(OpCode); - - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode)); - } - private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { - bool Na = ((OpCode >> 45) & 1) != 0; - bool Aa = ((OpCode >> 49) & 1) != 0; + bool NegA = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA; @@ -129,7 +129,7 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperA = GetAluAbsNeg(OperA, Aa, Na); + OperA = GetAluFabsFneg(OperA, AbsA, NegA); ShaderIrInst RoundInst = GetRoundInst(OpCode); @@ -153,8 +153,8 @@ namespace Ryujinx.Graphics.Gal.Shader throw new NotImplementedException(); } - bool Na = ((OpCode >> 45) & 1) != 0; - bool Aa = ((OpCode >> 49) & 1) != 0; + bool NegA = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA; @@ -167,7 +167,7 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperA = GetAluAbsNeg(OperA, Aa, Na); + OperA = GetAluFabsFneg(OperA, AbsA, NegA); ShaderIrInst RoundInst = GetRoundInst(OpCode); @@ -224,8 +224,8 @@ namespace Ryujinx.Graphics.Gal.Shader int Sel = (int)(OpCode >> 41) & 3; - bool Na = ((OpCode >> 45) & 1) != 0; - bool Aa = ((OpCode >> 49) & 1) != 0; + bool NegA = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA; @@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperA = GetAluAbsNeg(OperA, Aa, Na); + OperA = GetAluIabsIneg(OperA, AbsA, NegA); bool Signed = Type >= IntType.S8; @@ -253,9 +253,7 @@ namespace Ryujinx.Graphics.Gal.Shader if (Size < 32) { - uint Mask = uint.MaxValue >> (32 - Size); - - OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask)); + OperA = ExtendTo32(OperA, Signed, Size); } ShaderIrInst Inst = Signed @@ -296,7 +294,7 @@ namespace Ryujinx.Graphics.Gal.Shader default: throw new ArgumentException(nameof(Oper)); } - OperA = GetAluAbsNeg(OperA, AbsA, NegA); + OperA = GetAluIabsIneg(OperA, AbsA, NegA); bool Signed = Type >= IntType.S8; @@ -335,7 +333,7 @@ namespace Ryujinx.Graphics.Gal.Shader } else { - OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask)); + OperA = ExtendTo32(OperA, Signed, Size); } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index e44d5b7d7..4958dfcf4 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -2,14 +2,21 @@ namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecoder { + private const bool AddDbgComments = true; + public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset) { ShaderIrBlock Block = new ShaderIrBlock(); while (Offset + 2 <= Code.Length) { - //Ignore scheduling instructions, which are - //written every 32 bytes. + int InstPos = Offset * 4; + + Block.Position = InstPos; + + Block.MarkLabel(InstPos); + + //Ignore scheduling instructions, which are written every 32 bytes. if ((Offset & 7) == 0) { Offset += 2; @@ -24,6 +31,13 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode); + if (AddDbgComments) + { + string DbgOpCode = $"0x{InstPos:x8}: 0x{OpCode:x16} "; + + Block.AddNode(new ShaderIrCmnt(DbgOpCode + (Decode?.Method.Name ?? "???"))); + } + if (Decode == null) { continue; @@ -31,7 +45,7 @@ namespace Ryujinx.Graphics.Gal.Shader Decode(Block, OpCode); - if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst)) + if (Block.GetLastNode() is ShaderIrOp Op && Op.Inst == ShaderIrInst.Exit) { break; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index c920d9fa5..5f365d8c8 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal.Shader { private List Nodes; + private Dictionary LabelsToInsert; + + public int Position; + public ShaderIrBlock() { Nodes = new List(); + + LabelsToInsert = new Dictionary(); } public void AddNode(ShaderIrNode Node) @@ -16,6 +22,28 @@ namespace Ryujinx.Graphics.Gal.Shader Nodes.Add(Node); } + public ShaderIrLabel GetLabel(int Position) + { + if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label)) + { + return Label; + } + + Label = new ShaderIrLabel(); + + LabelsToInsert.Add(Position, Label); + + return Label; + } + + public void MarkLabel(int Position) + { + if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label)) + { + Nodes.Add(Label); + } + } + public ShaderIrNode[] GetNodes() { return Nodes.ToArray(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs new file mode 100644 index 000000000..03031ec5b --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrCmnt : ShaderIrNode + { + public string Comment { get; private set; } + + public ShaderIrCmnt(string Comment) + { + this.Comment = Comment; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index ce2b98fe9..af2ccc3b3 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -36,6 +36,8 @@ namespace Ryujinx.Graphics.Gal.Shader Ffma, Flg2, Floor, + Fmax, + Fmin, Fmul, Fneg, Frcp, @@ -49,6 +51,8 @@ namespace Ryujinx.Graphics.Gal.Shader F_End, I_Start, + Abs, + Add, And, Asr, Ceq, @@ -59,16 +63,21 @@ namespace Ryujinx.Graphics.Gal.Shader Cle, Clt, Cne, + Lsl, Lsr, + Mul, + Neg, Not, Or, Stof, + Sub, Texq, Txlf, Utof, Xor, I_End, + Bra, Exit, Kil } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs new file mode 100644 index 000000000..f5b3585af --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrLabel : ShaderIrNode { } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs index f22720563..b040c5c63 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs @@ -3,11 +3,14 @@ namespace Ryujinx.Graphics.Gal.Shader class ShaderIrOperCbuf : ShaderIrNode { public int Index { get; private set; } - public int Offs { get; private set; } + public int Pos { get; set; } - public ShaderIrOperCbuf(int Index, int Offs) + public ShaderIrNode Offs { get; private set; } + + public ShaderIrOperCbuf(int Index, int Pos, ShaderIrNode Offs = null) { this.Index = Index; + this.Pos = Pos; this.Offs = Offs; } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 65e249286..acfcc147c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -26,6 +26,10 @@ namespace Ryujinx.Graphics.Gal.Shader OpCodes = new ShaderDecodeEntry[1 << EncodingBits]; #region Instructions + Set("0100110000000x", ShaderDecode.Bfe_C); + Set("0011100x00000x", ShaderDecode.Bfe_I); + Set("0101110000000x", ShaderDecode.Bfe_R); + Set("111000100100xx", ShaderDecode.Bra); Set("111000110000xx", ShaderDecode.Exit); Set("0100110010101x", ShaderDecode.F2f_C); Set("0011100x10101x", ShaderDecode.F2f_I); @@ -40,10 +44,13 @@ namespace Ryujinx.Graphics.Gal.Shader Set("001100101xxxxx", ShaderDecode.Ffma_I); Set("010100011xxxxx", ShaderDecode.Ffma_RC); Set("010110011xxxxx", ShaderDecode.Ffma_RR); - Set("00011110xxxxxx", ShaderDecode.Fmul32i); + Set("00011110xxxxxx", ShaderDecode.Fmul_I32); Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_I); Set("0101110001101x", ShaderDecode.Fmul_R); + Set("0100110001100x", ShaderDecode.Fmnmx_C); + Set("0011100x01100x", ShaderDecode.Fmnmx_I); + Set("0101110001100x", ShaderDecode.Fmnmx_R); Set("0100100xxxxxxx", ShaderDecode.Fset_C); Set("0011000xxxxxxx", ShaderDecode.Fset_I); Set("01011000xxxxxx", ShaderDecode.Fset_R); @@ -57,17 +64,24 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0011100x11100x", ShaderDecode.I2i_I); Set("0101110011100x", ShaderDecode.I2i_R); Set("11100000xxxxxx", ShaderDecode.Ipa); + Set("0100110000011x", ShaderDecode.Iscadd_C); + Set("0011100x00011x", ShaderDecode.Iscadd_I); + Set("0101110000011x", ShaderDecode.Iscadd_R); Set("010010110110xx", ShaderDecode.Isetp_C); Set("0011011x0110xx", ShaderDecode.Isetp_I); Set("010110110110xx", ShaderDecode.Isetp_R); Set("111000110011xx", ShaderDecode.Kil); Set("1110111111011x", ShaderDecode.Ld_A); - Set("000001xxxxxxxx", ShaderDecode.Lop32i); + Set("1110111110010x", ShaderDecode.Ld_C); + Set("000001xxxxxxxx", ShaderDecode.Lop_I32); Set("0100110010011x", ShaderDecode.Mov_C); Set("0011100x10011x", ShaderDecode.Mov_I); + Set("000000010000xx", ShaderDecode.Mov_I32); Set("0101110010011x", ShaderDecode.Mov_R); - Set("000000010000xx", ShaderDecode.Mov32i); Set("0101000010000x", ShaderDecode.Mufu); + Set("0100110001001x", ShaderDecode.Shl_C); + Set("0011100x01001x", ShaderDecode.Shl_I); + Set("0101110001001x", ShaderDecode.Shl_R); Set("0100110000101x", ShaderDecode.Shr_C); Set("0011100x00101x", ShaderDecode.Shr_I); Set("0101110000101x", ShaderDecode.Shr_R); diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs index d400850c8..e5ecee950 100644 --- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs +++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs @@ -23,5 +23,13 @@ namespace Ryujinx.Graphics.Gal Size = NewSize; } } + + internal void SetCbufOffs(int Offs) + { + if (Index < Offs) + { + Index = Offs; + } + } } } \ No newline at end of file diff --git a/Ryujinx.sln b/Ryujinx.sln index 036421f90..213f73866 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryushader", "Ryushader\Ryushader.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.Build.0 = Release|Any CPU + {3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryushader/Program.cs b/Ryushader/Program.cs new file mode 100644 index 000000000..21eb3d79b --- /dev/null +++ b/Ryushader/Program.cs @@ -0,0 +1,48 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gal.Shader; +using System; +using System.IO; + +namespace Ryushader +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 2) + { + GlslDecompiler Decompiler = new GlslDecompiler(); + + GalShaderType ShaderType = GalShaderType.Vertex; + + switch (args[0].ToLower()) + { + case "v": ShaderType = GalShaderType.Vertex; break; + case "tc": ShaderType = GalShaderType.TessControl; break; + case "te": ShaderType = GalShaderType.TessEvaluation; break; + case "g": ShaderType = GalShaderType.Geometry; break; + case "f": ShaderType = GalShaderType.Fragment; break; + } + + byte[] Data = File.ReadAllBytes(args[1]); + + int[] Code = new int[Data.Length / 4]; + + for (int Offset = 0; Offset < Data.Length; Offset += 4) + { + int Value = BitConverter.ToInt32(Data, Offset); + + Code[Offset >> 2] = Value; + } + + GlslProgram Program = Decompiler.Decompile(Code, ShaderType); + + Console.WriteLine(Program.Code); + } + else + { + Console.WriteLine("Usage: Ryushader [v|tc|te|g|f] shader.bin"); + } + } + } +} diff --git a/Ryushader/Ryushader.csproj b/Ryushader/Ryushader.csproj new file mode 100644 index 000000000..9eeee2ba9 --- /dev/null +++ b/Ryushader/Ryushader.csproj @@ -0,0 +1,12 @@ + + + + + + + + Exe + netcoreapp2.1 + + +