From feb2680a6ce1512c08980ee55c1c8215b8b5c3e4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 10 Apr 2018 16:50:32 -0300 Subject: [PATCH] [GPU] Add more shader instructions, add support for rgb565 textures --- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 1 + .../Gal/OpenGL/OGLEnumConverter.cs | 12 + Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 41 +-- Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 2 +- Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 12 +- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 249 +++++++++++++----- .../Gal/Shader/ShaderDecodeAlu.cs | 44 +++- .../Gal/Shader/ShaderDecodeHelper.cs | 71 +++-- .../Gal/Shader/ShaderDecodeMem.cs | 50 +++- .../Gal/Shader/ShaderDecodeMove.cs | 184 ++++++++++++- Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 9 + Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs | 5 +- Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs | 49 ++-- Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs | 4 + .../Gal/Shader/ShaderIrMetaTex.cs | 12 + .../Gal/Shader/ShaderIrMetaTexq.cs | 15 ++ Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs | 5 +- .../Gal/Shader/ShaderOpCodeTable.cs | 14 + Ryujinx.Graphics/Gal/Shader/ShaderOper.cs | 6 +- .../Gal/Shader/ShaderOptExprProp.cs | 188 +++++++++---- Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs | 13 + Ryujinx.Graphics/Gpu/TextureReader.cs | 69 ++--- 22 files changed, 817 insertions(+), 238 deletions(-) create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 5b642961..8c2c718a 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Gal { A8B8G8R8 = 0x8, A1B5G5R5 = 0x14, + B5G6R5 = 0x15, BC1 = 0x24, BC2 = 0x25, BC3 = 0x26 diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 6518de5f..03c3ef52 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -55,6 +55,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentException(nameof(Type)); } + public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format) + { + switch (Format) + { + case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte); + case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551); + case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565); + } + + throw new NotImplementedException(Format.ToString()); + } + public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format) { switch (Format) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 559e0eda..681e6d67 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Textures = new int[80]; } - public void Set(int Index, GalTexture Tex) + public void Set(int Index, GalTexture Texture) { GL.ActiveTexture(TextureUnit.Texture0 + Index); @@ -19,29 +19,38 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindTexture(TextureTarget.Texture2D, Handle); - int W = Tex.Width; - int H = Tex.Height; + const int Border = 0; - byte[] Data = Tex.Data; - - int Length = Data.Length; - - if (IsCompressedTextureFormat(Tex.Format)) + if (IsCompressedTextureFormat(Texture.Format)) { - PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Tex.Format); + PixelInternalFormat InternalFmt = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format); - GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data); + GL.CompressedTexImage2D( + TextureTarget.Texture2D, + 0, + InternalFmt, + Texture.Width, + Texture.Height, + Border, + Texture.Data.Length, + Texture.Data); } else { - //TODO: Get those from Texture format. - const PixelInternalFormat Pif = PixelInternalFormat.Rgba; + const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; - const PixelFormat Pf = PixelFormat.Rgba; + (PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format); - const PixelType Pt = PixelType.UnsignedByte; - - GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Data); + GL.TexImage2D( + TextureTarget.Texture2D, + 0, + InternalFmt, + Texture.Width, + Texture.Height, + Border, + Format.Item1, + Format.Item2, + Texture.Data); } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 0b7bf92a..8d8e6425 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { - FbRenderer.Render(); + //FbRenderer.Render(); } public void SetWindowSize(int Width, int Height) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 898b90b5..cd901747 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Gal.Shader private const int AttrStartIndex = 8; private const int TexStartIndex = 8; + public const string PositionOutAttrName = "position"; + private const string InAttrName = "in_attr"; private const string OutAttrName = "out_attr"; private const string UniformName = "c"; @@ -62,10 +64,11 @@ namespace Ryujinx.Graphics.Gal.Shader m_Gprs = new Dictionary(); m_Preds = new Dictionary(); - //FIXME: Only valid for vertex shaders. if (ShaderType == GalShaderType.Fragment) { m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4)); + + m_InAttributes.Add(7, new ShaderDeclInfo(PositionOutAttrName, -1, 0, 4)); } else { @@ -104,10 +107,9 @@ namespace Ryujinx.Graphics.Gal.Shader 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) + if (Op.Inst == ShaderIrInst.Texq || + Op.Inst == ShaderIrInst.Texs || + Op.Inst == ShaderIrInst.Txlf) { int Handle = ((ShaderIrOperImm)Op.OperandC).Value; diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index eda70cef..e155e475 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -31,40 +31,51 @@ namespace Ryujinx.Graphics.Gal.Shader { InstsExpr = new Dictionary() { - { ShaderIrInst.And, GetAndExpr }, - { ShaderIrInst.Asr, GetAsrExpr }, - { ShaderIrInst.Band, GetBandExpr }, - { ShaderIrInst.Bnot, GetBnotExpr }, - { ShaderIrInst.Clt, GetCltExpr }, - { ShaderIrInst.Ceq, GetCeqExpr }, - { ShaderIrInst.Cle, GetCleExpr }, - { ShaderIrInst.Cgt, GetCgtExpr }, - { ShaderIrInst.Cne, GetCneExpr }, - { ShaderIrInst.Cge, GetCgeExpr }, - { ShaderIrInst.Exit, GetExitExpr }, - { ShaderIrInst.Fabs, GetFabsExpr }, - { ShaderIrInst.Fadd, GetFaddExpr }, - { ShaderIrInst.Fcos, GetFcosExpr }, - { ShaderIrInst.Fex2, GetFex2Expr }, - { ShaderIrInst.Ffma, GetFfmaExpr }, - { ShaderIrInst.Flg2, GetFlg2Expr }, - { ShaderIrInst.Fmul, GetFmulExpr }, - { ShaderIrInst.Fneg, GetFnegExpr }, - { ShaderIrInst.Frcp, GetFrcpExpr }, - { ShaderIrInst.Frsq, GetFrsqExpr }, - { ShaderIrInst.Fsin, GetFsinExpr }, - { ShaderIrInst.Ipa, GetIpaExpr }, - { ShaderIrInst.Kil, GetKilExpr }, - { ShaderIrInst.Lsr, GetLsrExpr }, - { ShaderIrInst.Not, GetNotExpr }, - { ShaderIrInst.Or, GetOrExpr }, - { ShaderIrInst.Stof, GetStofExpr }, - { ShaderIrInst.Utof, GetUtofExpr }, - { ShaderIrInst.Texr, GetTexrExpr }, - { ShaderIrInst.Texg, GetTexgExpr }, - { ShaderIrInst.Texb, GetTexbExpr }, - { ShaderIrInst.Texa, GetTexaExpr }, - { ShaderIrInst.Xor, GetXorExpr }, + { ShaderIrInst.And, GetAndExpr }, + { ShaderIrInst.Asr, GetAsrExpr }, + { ShaderIrInst.Band, GetBandExpr }, + { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Ceil, GetCeilExpr }, + { ShaderIrInst.Ceq, GetCeqExpr }, + { ShaderIrInst.Cge, GetCgeExpr }, + { ShaderIrInst.Cgt, GetCgtExpr }, + { ShaderIrInst.Clamp, GetClampExpr }, + { ShaderIrInst.Cle, GetCleExpr }, + { ShaderIrInst.Clt, GetCltExpr }, + { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Exit, GetExitExpr }, + { ShaderIrInst.Fabs, GetFabsExpr }, + { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fceq, GetCeqExpr }, + { ShaderIrInst.Fcge, GetCgeExpr }, + { ShaderIrInst.Fcgt, GetCgtExpr }, + { ShaderIrInst.Fcle, GetCleExpr }, + { ShaderIrInst.Fclt, GetCltExpr }, + { ShaderIrInst.Fcne, GetCneExpr }, + { ShaderIrInst.Fcos, GetFcosExpr }, + { ShaderIrInst.Fex2, GetFex2Expr }, + { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Flg2, GetFlg2Expr }, + { ShaderIrInst.Floor, GetFloorExpr }, + { ShaderIrInst.Fmul, GetFmulExpr }, + { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Frcp, GetFrcpExpr }, + { ShaderIrInst.Frsq, GetFrsqExpr }, + { ShaderIrInst.Fsin, GetFsinExpr }, + { ShaderIrInst.Ftos, GetFtosExpr }, + { ShaderIrInst.Ftou, GetFtouExpr }, + { ShaderIrInst.Ipa, GetIpaExpr }, + { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Not, GetNotExpr }, + { ShaderIrInst.Or, GetOrExpr }, + { ShaderIrInst.Stof, GetStofExpr }, + { ShaderIrInst.Texq, GetTexqExpr }, + { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Trunc, GetTruncExpr }, + { ShaderIrInst.Txlf, GetTxlfExpr }, + { ShaderIrInst.Utof, GetUtofExpr }, + { ShaderIrInst.Xor, GetXorExpr } }; } @@ -117,11 +128,21 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclInAttributes() { + if (Decl.ShaderType == GalShaderType.Fragment) + { + SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";"); + } + PrintDeclAttributes(Decl.InAttributes.Values, "in"); } private void PrintDeclOutAttributes() { + if (Decl.ShaderType == GalShaderType.Vertex) + { + SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";"); + } + PrintDeclAttributes(Decl.OutAttributes.Values, "out"); } @@ -133,7 +154,7 @@ namespace Ryujinx.Graphics.Gal.Shader { if (DeclInfo.Index >= 0) { - SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};"); + SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";"); Count++; } @@ -222,7 +243,14 @@ namespace Ryujinx.Graphics.Gal.Shader if (Node is ShaderIrCond Cond) { - string SubScopeName = "if (" + GetSrcExpr(Cond.Pred, true) + ")"; + string IfExpr = GetSrcExpr(Cond.Pred, true); + + if (Cond.Not) + { + IfExpr = "!(" + IfExpr + ")"; + } + + string SubScopeName = "if (" + IfExpr + ")"; PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); } @@ -236,6 +264,16 @@ namespace Ryujinx.Graphics.Gal.Shader } else if (Node is ShaderIrOp Op) { + if (Op.Inst == ShaderIrInst.Exit) + { + //Do everything that needs to be done before + //the shader ends here. + if (Decl.ShaderType == GalShaderType.Vertex) + { + SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); + } + } + SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); } else @@ -321,10 +359,9 @@ namespace Ryujinx.Graphics.Gal.Shader return true; case ShaderIrInst.Ipa: - case ShaderIrInst.Texr: - case ShaderIrInst.Texg: - case ShaderIrInst.Texb: - case ShaderIrInst.Texa: + case ShaderIrInst.Texq: + case ShaderIrInst.Texs: + case ShaderIrInst.Txlf: return false; } @@ -349,11 +386,6 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperAbuf Abuf) { - if (Abuf.Offs == GlslDecl.GlPositionWAttr && Decl.ShaderType == GalShaderType.Fragment) - { - return "(1f / gl_FragCoord.w)"; - } - if (Abuf.Offs == GlslDecl.VertexIdAttr) { return "gl_VertexID"; @@ -437,6 +469,10 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); + private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); + + private string GetClampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); + private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); @@ -458,6 +494,8 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); + private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); + private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); @@ -468,6 +506,16 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); + private string GetFtosExpr(ShaderIrOp Op) + { + return "int(" + GetOperExpr(Op, Op.OperandA) + ")"; + } + + private string GetFtouExpr(ShaderIrOp Op) + { + return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; + } + private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA); private string GetKilExpr(ShaderIrOp Op) => "discard"; @@ -487,6 +535,54 @@ namespace Ryujinx.Graphics.Gal.Shader return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; } + private string GetTexqExpr(ShaderIrOp Op) + { + ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; + + string Ch = "xyzw".Substring(Meta.Elem, 1); + + if (Meta.Info == ShaderTexqInfo.Dimension) + { + string Sampler = GetTexSamplerName(Op); + + string Lod = GetOperExpr(Op, Op.OperandA); //??? + + return "textureSize(" + Sampler + ", " + Lod + ")." + Ch; + } + else + { + throw new NotImplementedException(Meta.Info.ToString()); + } + } + + private string GetTexsExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + string Sampler = GetTexSamplerName(Op); + + string Coords = GetTexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return "texture(" + Sampler + ", " + Coords + ")." + Ch; + } + + private string GetTxlfExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + string Sampler = GetTexSamplerName(Op); + + string Coords = GetITexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; + } + + private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); + private string GetUtofExpr(ShaderIrOp Op) { return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; @@ -499,6 +595,13 @@ namespace Ryujinx.Graphics.Gal.Shader return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; } + private string GetTernaryCall(ShaderIrOp Op, string FuncName) + { + return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ", " + + GetOperExpr(Op, Op.OperandC) + ")"; + } + private string GetUnaryExpr(ShaderIrOp Op, string Opr) { return Opr + GetOperExpr(Op, Op.OperandA); @@ -517,16 +620,6 @@ namespace Ryujinx.Graphics.Gal.Shader GetOperExpr(Op, Op.OperandC); } - private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r'); - private string GetTexgExpr(ShaderIrOp Op) => GetTexExpr(Op, 'g'); - private string GetTexbExpr(ShaderIrOp Op) => GetTexExpr(Op, 'b'); - private string GetTexaExpr(ShaderIrOp Op) => GetTexExpr(Op, 'a'); - - private string GetTexExpr(ShaderIrOp Op, char Ch) - { - return $"texture({GetTexSamplerName(Op)}, {GetTexSamplerCoords(Op)}).{Ch}"; - } - private string GetTexSamplerName(ShaderIrOp Op) { ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; @@ -547,6 +640,12 @@ namespace Ryujinx.Graphics.Gal.Shader GetOperExpr(Op, Op.OperandB) + ")"; } + private string GetITexSamplerCoords(ShaderIrOp Op) + { + return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + + GetOperExpr(Op, Op.OperandB) + ")"; + } + private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) { return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); @@ -571,13 +670,31 @@ namespace Ryujinx.Graphics.Gal.Shader throw new InvalidOperationException(); } - //For integer immediates being used as float, - //it's better (for readability) to just return the float value. - if (Src is ShaderIrOperImm Imm && DstType == OperType.F32) + switch (Src) { - float Value = BitConverter.Int32BitsToSingle(Imm.Value); + case ShaderIrOperGpr Gpr: + { + //When the Gpr is ZR, just return the 0 value directly, + //since the float encoding for 0 is 0. + if (Gpr.IsConst) + { + return "0"; + } + break; + } - return Value.ToString(CultureInfo.InvariantCulture) + "f"; + case ShaderIrOperImm Imm: + { + //For integer immediates being used as float, + //it's better (for readability) to just return the float value. + if (DstType == OperType.F32) + { + float Value = BitConverter.Int32BitsToSingle(Imm.Value); + + return Value.ToString(CultureInfo.InvariantCulture) + "f"; + } + break; + } } switch (DstType) @@ -592,12 +709,20 @@ namespace Ryujinx.Graphics.Gal.Shader private static OperType GetDstNodeType(ShaderIrNode Node) { + //Special case instructions with the result type different + //from the input types (like integer <-> float conversion) here. if (Node is ShaderIrOp Op) { switch (Op.Inst) { - case ShaderIrInst.Stof: return OperType.F32; - case ShaderIrInst.Utof: return OperType.F32; + case ShaderIrInst.Stof: + case ShaderIrInst.Txlf: + case ShaderIrInst.Utof: + return OperType.F32; + + case ShaderIrInst.Ftos: + case ShaderIrInst.Ftou: + return OperType.I32; } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 5c2f493e..b796ab28 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -81,6 +81,21 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + public static void Isetp_C(ShaderIrBlock Block, long OpCode) + { + EmitIsetp(Block, OpCode, ShaderOper.CR); + } + + public static void Isetp_I(ShaderIrBlock Block, long OpCode) + { + EmitIsetp(Block, OpCode, ShaderOper.Imm); + } + + public static void Isetp_R(ShaderIrBlock Block, long OpCode) + { + EmitIsetp(Block, OpCode, ShaderOper.RR); + } + public static void Lop32i(ShaderIrBlock Block, long OpCode) { int SubOp = (int)(OpCode >> 53) & 3; @@ -258,6 +273,16 @@ namespace Ryujinx.Graphics.Gal.Shader } private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitSetp(Block, OpCode, true, Oper); + } + + private static void EmitIsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitSetp(Block, OpCode, false, Oper); + } + + private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool Aa = ((OpCode >> 7) & 1) != 0; bool Np = ((OpCode >> 42) & 1) != 0; @@ -269,17 +294,28 @@ namespace Ryujinx.Graphics.Gal.Shader switch (Oper) { case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20 (OpCode); break; case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; default: throw new ArgumentException(nameof(Oper)); } - ShaderIrInst CmpInst = GetCmp(OpCode); + ShaderIrInst CmpInst; - ShaderIrOp Op = new ShaderIrOp(CmpInst, - GetAluAbsNeg(OperA, Aa, Na), - GetAluAbs (OperB, Ab)); + if (IsFloat) + { + OperA = GetAluAbsNeg(OperA, Aa, Na); + OperB = GetAluAbs (OperB, Ab); + + CmpInst = GetCmpF(OpCode); + } + else + { + CmpInst = GetCmp(OpCode); + } + + ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); ShaderIrOperPred P0Node = GetOperPred3 (OpCode); ShaderIrOperPred P1Node = GetOperPred0 (OpCode); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index 7989570d..de932dce 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -60,7 +60,17 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff); } - public static ShaderIrNode GetOperImm19_20(long OpCode) + public static ShaderIrOperImm GetOperImm13_36(long OpCode) + { + return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff); + } + + public static ShaderIrOperImm GetOperImm32_20(long OpCode) + { + return new ShaderIrOperImm((int)(OpCode >> 20)); + } + + public static ShaderIrOperImm GetOperImm19_20(long OpCode) { int Value = (int)(OpCode >> 20) & 0x7ffff; @@ -74,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperImm((int)Value); } - public static ShaderIrNode GetOperImmf19_20(long OpCode) + public static ShaderIrOperImmf GetOperImmf19_20(long OpCode) { uint Imm = (uint)(OpCode >> 20) & 0x7ffff; @@ -92,16 +102,6 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperImmf(Value); } - public static ShaderIrOperImm GetOperImm13_36(long OpCode) - { - return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff); - } - - public static ShaderIrOperImm GetOperImm32_20(long OpCode) - { - return new ShaderIrOperImm((int)(OpCode >> 20)); - } - public static ShaderIrOperPred GetOperPred3(long OpCode) { return new ShaderIrOperPred((int)(OpCode >> 3) & 7); @@ -130,23 +130,38 @@ namespace Ryujinx.Graphics.Gal.Shader } public static ShaderIrInst GetCmp(long OpCode) + { + switch ((int)(OpCode >> 49) & 7) + { + case 1: return ShaderIrInst.Clt; + case 2: return ShaderIrInst.Ceq; + case 3: return ShaderIrInst.Cle; + case 4: return ShaderIrInst.Cgt; + case 5: return ShaderIrInst.Cne; + case 6: return ShaderIrInst.Cge; + } + + throw new ArgumentException(nameof(OpCode)); + } + + public static ShaderIrInst GetCmpF(long OpCode) { switch ((int)(OpCode >> 48) & 0xf) { - case 0x1: return ShaderIrInst.Clt; - case 0x2: return ShaderIrInst.Ceq; - case 0x3: return ShaderIrInst.Cle; - case 0x4: return ShaderIrInst.Cgt; - case 0x5: return ShaderIrInst.Cne; - case 0x6: return ShaderIrInst.Cge; - case 0x7: return ShaderIrInst.Cnum; - case 0x8: return ShaderIrInst.Cnan; - case 0x9: return ShaderIrInst.Cltu; - case 0xa: return ShaderIrInst.Cequ; - case 0xb: return ShaderIrInst.Cleu; - case 0xc: return ShaderIrInst.Cgtu; - case 0xd: return ShaderIrInst.Cneu; - case 0xe: return ShaderIrInst.Cgeu; + case 0x1: return ShaderIrInst.Fclt; + case 0x2: return ShaderIrInst.Fceq; + case 0x3: return ShaderIrInst.Fcle; + case 0x4: return ShaderIrInst.Fcgt; + case 0x5: return ShaderIrInst.Fcne; + case 0x6: return ShaderIrInst.Fcge; + case 0x7: return ShaderIrInst.Fcnum; + case 0x8: return ShaderIrInst.Fcnan; + case 0x9: return ShaderIrInst.Fcltu; + case 0xa: return ShaderIrInst.Fcequ; + case 0xb: return ShaderIrInst.Fcleu; + case 0xc: return ShaderIrInst.Fcgtu; + case 0xd: return ShaderIrInst.Fcneu; + case 0xe: return ShaderIrInst.Fcgeu; } throw new ArgumentException(nameof(OpCode)); @@ -170,7 +185,9 @@ namespace Ryujinx.Graphics.Gal.Shader if (Pred.Index != ShaderIrOperPred.UnusedIndex) { - Node = new ShaderIrCond(Pred, Node); + bool Inv = ((OpCode >> 19) & 1) != 0; + + Node = new ShaderIrCond(Pred, Node, Inv); } return Node; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index fd18ce07..6553cfcf 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -36,24 +36,56 @@ namespace Ryujinx.Graphics.Gal.Shader } } + public static void Texq(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperD = GetOperGpr0(OpCode); + ShaderIrNode OperA = GetOperGpr8(OpCode); + + ShaderTexqInfo Info = (ShaderTexqInfo)((OpCode >> 22) & 0x1f); + + ShaderIrMetaTexq Meta0 = new ShaderIrMetaTexq(Info, 0); + ShaderIrMetaTexq Meta1 = new ShaderIrMetaTexq(Info, 1); + + ShaderIrNode OperC = GetOperImm13_36(OpCode); + + ShaderIrOp Op0 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta0); + ShaderIrOp Op1 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta1); + + Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Op0), OpCode)); + Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right? + } + public static void Texs(ShaderIrBlock Block, long OpCode) + { + EmitTex(Block, OpCode, ShaderIrInst.Texs); + } + + public static void Tlds(ShaderIrBlock Block, long OpCode) + { + EmitTex(Block, OpCode, ShaderIrInst.Txlf); + } + + private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. - ShaderIrNode OperA = GetOperGpr8 (OpCode); - ShaderIrNode OperB = GetOperGpr20 (OpCode); - ShaderIrNode OperC = GetOperGpr28 (OpCode); - ShaderIrNode OperD = GetOperImm13_36(OpCode); + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperGpr20 (OpCode); + ShaderIrNode OperC = GetOperImm13_36(OpCode); for (int Ch = 0; Ch < 4; Ch++) { - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texr + Ch, OperA, OperB, OperD); + ShaderIrOperGpr Dst = (Ch >> 1) != 0 + ? GetOperGpr28(OpCode) + : GetOperGpr0 (OpCode); - ShaderIrOperGpr Dst = GetOperGpr0(OpCode); + Dst.Index += Ch & 1; - Dst.Index += Ch; + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - Block.AddNode(new ShaderIrAsg(Dst, Op)); - } + ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); + + Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); + } } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index 50c740bf..6d30cfed 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -25,6 +25,36 @@ namespace Ryujinx.Graphics.Gal.Shader F64 = 3 } + public static void F2f_C(ShaderIrBlock Block, long OpCode) + { + EmitF2f(Block, OpCode, ShaderOper.CR); + } + + public static void F2f_I(ShaderIrBlock Block, long OpCode) + { + EmitF2f(Block, OpCode, ShaderOper.Immf); + } + + public static void F2f_R(ShaderIrBlock Block, long OpCode) + { + EmitF2f(Block, OpCode, ShaderOper.RR); + } + + public static void F2i_C(ShaderIrBlock Block, long OpCode) + { + EmitF2i(Block, OpCode, ShaderOper.CR); + } + + public static void F2i_I(ShaderIrBlock Block, long OpCode) + { + EmitF2i(Block, OpCode, ShaderOper.Immf); + } + + public static void F2i_R(ShaderIrBlock Block, long OpCode) + { + EmitF2i(Block, OpCode, ShaderOper.RR); + } + public static void I2f_C(ShaderIrBlock Block, long OpCode) { EmitI2f(Block, OpCode, ShaderOper.CR); @@ -40,6 +70,131 @@ namespace Ryujinx.Graphics.Gal.Shader EmitI2f(Block, OpCode, ShaderOper.RR); } + public static void Mov_C(ShaderIrBlock Block, long OpCode) + { + ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode)); + } + + public static void Mov_I(ShaderIrBlock Block, long OpCode) + { + ShaderIrOperImm Imm = GetOperImm19_20(OpCode); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode)); + } + + public static void Mov_R(ShaderIrBlock Block, long OpCode) + { + ShaderIrOperGpr Gpr = GetOperGpr20(OpCode); + + 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; + + ShaderIrNode OperA; + + switch (Oper) + { + case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperA = GetAluAbsNeg(OperA, Aa, Na); + + ShaderIrInst RoundInst = GetRoundInst(OpCode); + + if (RoundInst != ShaderIrInst.Invalid) + { + OperA = new ShaderIrOp(RoundInst, OperA); + } + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); + } + + private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + IntType Type = GetIntType(OpCode); + + if (Type == IntType.U64 || + Type == IntType.S64) + { + //TODO: 64-bits support. + //Note: GLSL doesn't support 64-bits integers. + throw new NotImplementedException(); + } + + bool Na = ((OpCode >> 45) & 1) != 0; + bool Aa = ((OpCode >> 49) & 1) != 0; + + ShaderIrNode OperA; + + switch (Oper) + { + case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperA = GetAluAbsNeg(OperA, Aa, Na); + + ShaderIrInst RoundInst = GetRoundInst(OpCode); + + if (RoundInst != ShaderIrInst.Invalid) + { + OperA = new ShaderIrOp(RoundInst, OperA); + } + + bool Signed = Type >= IntType.S8; + + int Size = 8 << ((int)Type & 3); + + if (Size < 32) + { + uint Mask = uint.MaxValue >> (32 - Size); + + float CMin = 0; + float CMax = Mask; + + if (Signed) + { + uint HalfMask = Mask >> 1; + + CMin -= HalfMask + 1; + CMax = HalfMask; + } + + ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin); + ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax); + + OperA = new ShaderIrOp(ShaderIrInst.Clamp, OperA, IMin, IMax); + } + + ShaderIrInst Inst = Signed + ? ShaderIrInst.Ftos + : ShaderIrInst.Ftou; + + ShaderIrNode Op = new ShaderIrOp(Inst, OperA); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { IntType Type = GetIntType(OpCode); @@ -76,18 +231,16 @@ namespace Ryujinx.Graphics.Gal.Shader int Size = 8 << ((int)Type & 3); - ulong Mask = ulong.MaxValue >> (64 - Size); - - int Mask32 = (int)Mask; - if (Shift != 0) { OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift)); } - if (Mask != uint.MaxValue) + if (Size < 32) { - OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm(Mask32)); + uint Mask = uint.MaxValue >> (32 - Size); + + OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask)); } ShaderIrInst Inst = Signed @@ -99,13 +252,6 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } - public static void Mov32i(ShaderIrBlock Block, long OpCode) - { - ShaderIrOperImm Imm = GetOperImm32_20(OpCode); - - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode)); - } - private static IntType GetIntType(long OpCode) { bool Signed = ((OpCode >> 13) & 1) != 0; @@ -124,5 +270,17 @@ namespace Ryujinx.Graphics.Gal.Shader { return (FloatType)((OpCode >> 8) & 3); } + + private static ShaderIrInst GetRoundInst(long OpCode) + { + switch ((OpCode >> 39) & 3) + { + case 1: return ShaderIrInst.Floor; + case 2: return ShaderIrInst.Ceil; + case 3: return ShaderIrInst.Trunc; + } + + return ShaderIrInst.Invalid; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 779bbf92..7bebea62 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -8,6 +8,15 @@ namespace Ryujinx.Graphics.Gal.Shader while (Offset + 2 <= Code.Length) { + //Ignore scheduling instructions, which are + //written every 32 bytes. + if ((Offset & 7) == 0) + { + Offset += 2; + + continue; + } + uint Word0 = (uint)Code[Offset++]; uint Word1 = (uint)Code[Offset++]; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs index d8c87b49..8fb01660 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs @@ -5,10 +5,13 @@ namespace Ryujinx.Graphics.Gal.Shader public ShaderIrNode Pred { get; set; } public ShaderIrNode Child { get; set; } - public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child) + public bool Not { get; private set; } + + public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child, bool Not) { this.Pred = Pred; this.Child = Child; + this.Not = Not; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index b6f4e80b..1b72f647 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -2,53 +2,66 @@ namespace Ryujinx.Graphics.Gal.Shader { enum ShaderIrInst { + Invalid, + B_Start, Band, Bnot, Bor, Bxor, - Clt, - Ceq, - Cle, - Cgt, - Cne, - Cge, - Cnum, - Cnan, - Cltu, - Cequ, - Cleu, - Cgtu, - Cneu, - Cgeu, B_End, F_Start, + Ceil, + Clamp, Fabs, Fadd, + Fceq, + Fcequ, + Fcge, + Fcgeu, + Fcgt, + Fcgtu, + Fcle, + Fcleu, + Fclt, + Fcltu, + Fcnan, + Fcne, + Fcneu, + Fcnum, Fcos, Fex2, Ffma, Flg2, + Floor, Fmul, Fneg, Frcp, Frsq, Fsin, + Ftos, + Ftou, Ipa, - Texr, - Texg, - Texb, - Texa, + Texs, + Trunc, F_End, I_Start, And, Asr, + Ceq, + Cge, + Cgt, + Cle, + Clt, + Cne, Lsr, Not, Or, Stof, + Texq, + Txlf, Utof, Xor, I_End, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs new file mode 100644 index 00000000..afb7503b --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrMeta { } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs new file mode 100644 index 00000000..82f3bb77 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrMetaTex : ShaderIrMeta + { + public int Elem { get; private set; } + + public ShaderIrMetaTex(int Elem) + { + this.Elem = Elem; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs new file mode 100644 index 00000000..92871137 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrMetaTexq : ShaderIrMeta + { + public ShaderTexqInfo Info { get; private set; } + + public int Elem { get; private set; } + + public ShaderIrMetaTexq(ShaderTexqInfo Info, int Elem) + { + this.Info = Info; + this.Elem = Elem; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs index cd210757..12a6123c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs @@ -6,17 +6,20 @@ namespace Ryujinx.Graphics.Gal.Shader public ShaderIrNode OperandA { get; set; } public ShaderIrNode OperandB { get; set; } public ShaderIrNode OperandC { get; set; } + public ShaderIrMeta MetaData { get; set; } public ShaderIrOp( ShaderIrInst Inst, ShaderIrNode OperandA = null, ShaderIrNode OperandB = null, - ShaderIrNode OperandC = null) + ShaderIrNode OperandC = null, + ShaderIrMeta MetaData = null) { this.Inst = Inst; this.OperandA = OperandA; this.OperandB = OperandB; this.OperandC = OperandC; + this.MetaData = MetaData; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 48c3b2ee..a234f7f7 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Gal.Shader #region Instructions Set("111000110000xx", ShaderDecode.Exit); + Set("0100110010101x", ShaderDecode.F2f_C); + Set("0011100x10101x", ShaderDecode.F2f_I); + Set("0101110010101x", ShaderDecode.F2f_R); + Set("0100110010110x", ShaderDecode.F2i_C); + Set("0011100x10110x", ShaderDecode.F2i_I); + Set("0101110010110x", ShaderDecode.F2i_R); Set("0100110001011x", ShaderDecode.Fadd_C); Set("0011100x01011x", ShaderDecode.Fadd_I); Set("0101110001011x", ShaderDecode.Fadd_R); @@ -31,16 +37,24 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0011100x10111x", ShaderDecode.I2f_I); Set("0101110010111x", ShaderDecode.I2f_R); Set("11100000xxxxxx", ShaderDecode.Ipa); + 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("0100110010011x", ShaderDecode.Mov_C); + Set("0011100x10011x", ShaderDecode.Mov_I); + Set("0101110010011x", ShaderDecode.Mov_R); Set("000000010000xx", ShaderDecode.Mov32i); Set("0101000010000x", ShaderDecode.Mufu); Set("0100110000101x", ShaderDecode.Shr_C); Set("0011100x00101x", ShaderDecode.Shr_I); Set("0101110000101x", ShaderDecode.Shr_R); Set("1110111111110x", ShaderDecode.St_A); + Set("1101111101001x", ShaderDecode.Texq); Set("1101100xxxxxxx", ShaderDecode.Texs); + Set("1101101xxxxxxx", ShaderDecode.Tlds); #endregion } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs index 7989deed..aa485482 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs @@ -3,9 +3,9 @@ namespace Ryujinx.Graphics.Gal.Shader enum ShaderOper { CR, - RC, - RR, Imm, - Immf + Immf, + RC, + RR } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs index 69457aeb..9ce7cbe3 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -7,13 +7,22 @@ namespace Ryujinx.Graphics.Gal.Shader { private struct UseSite { - public object Parent; + public ShaderIrNode Parent { get; private set; } + public ShaderIrCond Cond { get; private set; } - public int OperIndex; + public int UseIndex { get; private set; } - public UseSite(object Parent, int OperIndex) + public int OperIndex { get; private set; } + + public UseSite( + ShaderIrNode Parent, + ShaderIrCond Cond, + int UseIndex, + int OperIndex) { this.Parent = Parent; + this.Cond = Cond; + this.UseIndex = UseIndex; this.OperIndex = OperIndex; } } @@ -24,7 +33,9 @@ namespace Ryujinx.Graphics.Gal.Shader public int AsgIndex { get; private set; } - private bool Propagate; + public int LastSiteIndex { get; private set; } + + public ShaderIrCond Cond { get; private set; } private List Sites; @@ -35,6 +46,11 @@ namespace Ryujinx.Graphics.Gal.Shader public void AddUseSite(UseSite Site) { + if (LastSiteIndex < Site.UseIndex) + { + LastSiteIndex = Site.UseIndex; + } + Sites.Add(Site); } @@ -42,14 +58,27 @@ namespace Ryujinx.Graphics.Gal.Shader { //This happens when a untiliazied register is used, //this usually indicates a decoding error, but may also - //be cased by bogus programs (?). In any case, we just + //be caused by bogus programs (?). In any case, we just //keep the unitialized access and avoid trying to propagate //the expression (since we can't propagate what doesn't yet exist). - if (Asg == null || !Propagate) + if (Asg == null) { return false; } + if (Cond != null) + { + //If the assignment is conditional, we can only propagate + //to the use sites that shares the same condition of the assignment. + foreach (UseSite Site in Sites) + { + if (!IsSameCondition(Cond, Site.Cond)) + { + return false; + } + } + } + if (Sites.Count > 0) { foreach (UseSite Site in Sites) @@ -89,11 +118,13 @@ namespace Ryujinx.Graphics.Gal.Shader return true; } - public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate) + public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond) { - this.Asg = Asg; - this.AsgIndex = AsgIndex; - this.Propagate = Propagate; + this.Asg = Asg; + this.AsgIndex = AsgIndex; + this.Cond = Cond; + + LastSiteIndex = 0; Sites.Clear(); } @@ -137,38 +168,52 @@ namespace Ryujinx.Graphics.Gal.Shader return GetUse(GetPredKey(PredIndex)); } - void FindRegUses(List<(int, UseSite)> UseList, object Parent, ShaderIrNode Node, int OperIndex = 0) + void RemoveUse(RegUse Use) { - if (Node is ShaderIrAsg Asg) + if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg)) { - FindRegUses(UseList, Asg, Asg.Src); - } - else if (Node is ShaderIrCond Cond) - { - FindRegUses(UseList, Cond, Cond.Pred, 0); - FindRegUses(UseList, Cond, Cond.Child, 1); - } - else if (Node is ShaderIrOp Op) - { - FindRegUses(UseList, Op, Op.OperandA, 0); - FindRegUses(UseList, Op, Op.OperandB, 1); - FindRegUses(UseList, Op, Op.OperandC, 2); - } - else if (Node is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) - { - UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex))); - } - else if (Node is ShaderIrOperPred Pred) - { - UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, OperIndex))); + throw new InvalidOperationException(); } } - void TryAddRegUseSite(ShaderIrNode Node) + void FindRegUses( + List<(int, UseSite)> UseList, + ShaderIrNode Parent, + ShaderIrNode Node, + ShaderIrCond CondNode, + int UseIndex, + int OperIndex = 0) + { + if (Node is ShaderIrAsg Asg) + { + FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex); + } + else if (Node is ShaderIrCond Cond) + { + FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0); + FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1); + } + else if (Node is ShaderIrOp Op) + { + FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0); + FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1); + FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2); + } + else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst) + { + UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex))); + } + else if (Node is ShaderIrOperPred Pred) + { + UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex))); + } + } + + void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex) { List<(int, UseSite)> UseList = new List<(int, UseSite)>(); - FindRegUses(UseList, null, Node); + FindRegUses(UseList, null, Node, CondNode, UseIndex); foreach ((int Key, UseSite Site) in UseList) { @@ -190,10 +235,22 @@ namespace Ryujinx.Graphics.Gal.Shader List<(int, UseSite)> UseList = new List<(int, UseSite)>(); - FindRegUses(UseList, Use.Asg, Use.Asg.Src); + if (Use.Cond != null) + { + FindRegUses(UseList, null, Use.Cond, null, 0); + } + else + { + FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0); + } foreach ((int Key, UseSite Site) in UseList) { + //TODO: Build an assignment list inside RegUse, + //and check if there is an assignment inside the + //range of Use.AsgIndex and Use.LastSiteIndex, + //and if that's the case, then we should return false. + //The current method is too conservative. if (GetUse(Key).AsgIndex >= Use.AsgIndex) { return false; @@ -203,13 +260,18 @@ namespace Ryujinx.Graphics.Gal.Shader return Use.TryPropagate(); } - for (int Index = 0, AsgIndex = 0; Index < Nodes.Count; Index++, AsgIndex++) + for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++) { ShaderIrNode Node = Nodes[Index]; - bool IsConditional = Node is ShaderIrCond; + ShaderIrCond CondNode = null; - TryAddRegUseSite(Node); + if (Node is ShaderIrCond) + { + CondNode = (ShaderIrCond)Node; + } + + TryAddRegUseSite(Node, CondNode, IterCount);; while (Node is ShaderIrCond Cond) { @@ -223,7 +285,7 @@ namespace Ryujinx.Graphics.Gal.Shader RegUse Use = null; - if (Asg.Dst is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) + if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst) { Use = GetGprUse(Gpr.Index); } @@ -232,16 +294,22 @@ namespace Ryujinx.Graphics.Gal.Shader Use = GetPredUse(Pred.Index); } - if (!IsConditional && TryPropagate(Use)) - { - Nodes.Remove(Use.Asg); + bool CanRemoveAsg = CondNode == null; + CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond); + + if (CanRemoveAsg && TryPropagate(Use)) + { + RemoveUse(Use); + + //Note: Only decrement if the removal was successful. + //RemoveUse throws when this is not the case so we should be good. Index--; } //All nodes inside conditional nodes can't be propagated, //as we don't even know if they will be executed to begin with. - Use?.SetNewAsg(Asg, AsgIndex, !IsConditional); + Use?.SetNewAsg(Asg, IterCount, CondNode); } foreach (RegUse Use in Uses.Values) @@ -258,9 +326,41 @@ namespace Ryujinx.Graphics.Gal.Shader if (TryPropagate(Use)) { - Nodes.Remove(Use.Asg); + RemoveUse(Use); } } } + + private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB) + { + if (CondA == null || CondB == null) + { + return CondA == CondB; + } + + if (CondA.Not != CondB.Not) + { + return false; + } + + if (CondA.Pred is ShaderIrOperPred PredA) + { + if (!(CondB.Pred is ShaderIrOperPred PredB)) + { + return false; + } + + if (PredA.Index != PredB.Index) + { + return false; + } + } + else if (CondA.Pred != CondB.Pred) + { + return false; + } + + return true; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs new file mode 100644 index 00000000..9158662c --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + enum ShaderTexqInfo + { + Dimension = 1, + TextureType = 2, + SamplePos = 5, + Filter = 16, + Lod = 18, + Wrap = 20, + BorderColor = 22 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs index 60285aed..715578b5 100644 --- a/Ryujinx.Graphics/Gpu/TextureReader.cs +++ b/Ryujinx.Graphics/Gpu/TextureReader.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Gpu { case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); case GalTextureFormat.A1B5G5R5: return Read2Bpp (Memory, Texture); + case GalTextureFormat.B5G6R5: return Read2Bpp (Memory, Texture); case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); @@ -20,35 +21,6 @@ namespace Ryujinx.Graphics.Gpu throw new NotImplementedException(Texture.Format.ToString()); } - private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 4]; - - ISwizzle Swizzle = GetSwizzle(Texture, 4); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset); - - *(int*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 4; - } - } - - return Output; - } - private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture) { int Width = Texture.Width; @@ -56,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 2]; - ISwizzle Swizzle = GetSwizzle(Texture, 2); + ISwizzle Swizzle = GetSwizzle(Texture, Width, 2); fixed (byte* BuffPtr = Output) { @@ -78,6 +50,35 @@ namespace Ryujinx.Graphics.Gpu return Output; } + private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 4]; + + ISwizzle Swizzle = GetSwizzle(Texture, Width, 4); + + fixed (byte* BuffPtr = Output) + { + long OutOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset); + + *(int*)(BuffPtr + OutOffs) = Pixel; + + OutOffs += 4; + } + } + + return Output; + } + private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture) { int Width = (Texture.Width + 3) / 4; @@ -85,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 8]; - ISwizzle Swizzle = GetSwizzle(Texture, 8); + ISwizzle Swizzle = GetSwizzle(Texture, Width, 8); fixed (byte* BuffPtr = Output) { @@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu byte[] Output = new byte[Width * Height * 16]; - ISwizzle Swizzle = GetSwizzle(Texture, 16); + ISwizzle Swizzle = GetSwizzle(Texture, Width, 16); fixed (byte* BuffPtr = Output) { @@ -138,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu return Output; } - private static ISwizzle GetSwizzle(Texture Texture, int Bpp) + private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) { switch (Texture.Swizzle) { @@ -148,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu case TextureSwizzle.BlockLinear: case TextureSwizzle.BlockLinearColorKey: - return new BlockLinearSwizzle(Texture.Width, Bpp, Texture.BlockHeight); + return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); } throw new NotImplementedException(Texture.Swizzle.ToString());