diff options
Diffstat (limited to 'Ryujinx.Graphics/Gal/Shader')
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 26 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 320 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs | 613 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs | 5 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs | 1 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs | 18 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs | 2 |
7 files changed, 930 insertions, 55 deletions
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 43923da7..f7ae34fa 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Gal.OpenGL; +using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; @@ -224,6 +226,7 @@ namespace Ryujinx.Graphics.Gal.Shader if (Op.Inst == ShaderIrInst.Texq || Op.Inst == ShaderIrInst.Texs || + Op.Inst == ShaderIrInst.Tld4 || Op.Inst == ShaderIrInst.Txlf) { int Handle = ((ShaderIrOperImm)Op.OperandC).Value; @@ -232,7 +235,25 @@ namespace Ryujinx.Graphics.Gal.Shader string Name = StagePrefix + TextureName + Index; - m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle)); + GalTextureTarget TextureTarget; + + TextureInstructionSuffix TextureInstructionSuffix; + + // TODO: Non 2D texture type for TEXQ? + if (Op.Inst == ShaderIrInst.Texq) + { + TextureTarget = GalTextureTarget.TwoD; + TextureInstructionSuffix = TextureInstructionSuffix.None; + } + else + { + ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData); + + TextureTarget = Meta.TextureTarget; + TextureInstructionSuffix = Meta.TextureInstructionSuffix; + } + + m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle, false, 0, 1, TextureTarget, TextureInstructionSuffix)); } else if (Op.Inst == ShaderIrInst.Texb) { @@ -257,9 +278,10 @@ namespace Ryujinx.Graphics.Gal.Shader if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf) { + ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData); string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos; - m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index)); + m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index, 1, Meta.TextureTarget, Meta.TextureInstructionSuffix)); } else { diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 854c827e..5f809525 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -1,3 +1,5 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; using System.Globalization; @@ -33,7 +35,9 @@ namespace Ryujinx.Graphics.Gal.Shader public int MaxUboSize { get; } - public GlslDecompiler(int MaxUboSize) + private bool IsNvidiaDriver; + + public GlslDecompiler(int MaxUboSize, bool IsNvidiaDriver) { InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>() { @@ -103,6 +107,7 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Texb, GetTexbExpr }, { ShaderIrInst.Texq, GetTexqExpr }, { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Tld4, GetTld4Expr }, { ShaderIrInst.Trunc, GetTruncExpr }, { ShaderIrInst.Txlf, GetTxlfExpr }, { ShaderIrInst.Utof, GetUtofExpr }, @@ -110,6 +115,7 @@ namespace Ryujinx.Graphics.Gal.Shader }; this.MaxUboSize = MaxUboSize / 16; + this.IsNvidiaDriver = IsNvidiaDriver; } public GlslProgram Decompile( @@ -219,14 +225,70 @@ namespace Ryujinx.Graphics.Gal.Shader } } + private string GetSamplerType(TextureTarget TextureTarget, bool HasShadow) + { + string Result; + + switch (TextureTarget) + { + case TextureTarget.Texture1D: + Result = "sampler1D"; + break; + case TextureTarget.Texture2D: + Result = "sampler2D"; + break; + case TextureTarget.Texture3D: + Result = "sampler3D"; + break; + case TextureTarget.TextureCubeMap: + Result = "samplerCube"; + break; + case TextureTarget.TextureRectangle: + Result = "sampler2DRect"; + break; + case TextureTarget.Texture1DArray: + Result = "sampler1DArray"; + break; + case TextureTarget.Texture2DArray: + Result = "sampler2DArray"; + break; + case TextureTarget.TextureCubeMapArray: + Result = "samplerCubeArray"; + break; + case TextureTarget.TextureBuffer: + Result = "samplerBuffer"; + break; + case TextureTarget.Texture2DMultisample: + Result = "sampler2DMS"; + break; + case TextureTarget.Texture2DMultisampleArray: + Result = "sampler2DMSArray"; + break; + default: + throw new NotSupportedException(); + } + + if (HasShadow) + Result += "Shadow"; + + return Result; + } + private void PrintDeclTextures() { foreach (ShaderDeclInfo DeclInfo in IterateCbTextures()) { - SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";"); + TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget); + SB.AppendLine($"// {DeclInfo.TextureSuffix}"); + SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";"); } - PrintDecls(Decl.Textures, "uniform sampler2D"); + foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values.OrderBy(DeclKeySelector)) + { + TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget); + SB.AppendLine($"// {DeclInfo.TextureSuffix}"); + SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";"); + } } private IEnumerable<ShaderDeclInfo> IterateCbTextures() @@ -778,6 +840,7 @@ namespace Ryujinx.Graphics.Gal.Shader case ShaderIrInst.Ipa: case ShaderIrInst.Texq: case ShaderIrInst.Texs: + case ShaderIrInst.Tld4: case ShaderIrInst.Txlf: return false; } @@ -1124,7 +1187,7 @@ namespace Ryujinx.Graphics.Gal.Shader string Ch = "rgba".Substring(Meta.Elem, 1); - return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch; + return GetTextureOperation(Op, DeclInfo.Name, Coords, Ch); } private string GetTexqExpr(ShaderIrOp Op) @@ -1157,20 +1220,50 @@ namespace Ryujinx.Graphics.Gal.Shader string Ch = "rgba".Substring(Meta.Elem, 1); - return "texture(" + Sampler + ", " + Coords + ")." + Ch; + return GetTextureOperation(Op, Sampler, Coords, Ch); + } + + private string GetTld4Expr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + string Sampler = GetTexSamplerName(Op); + + string Coords = GetTexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return GetTextureGatherOperation(Op, Sampler, Coords, Ch); } + // TODO: support AOFFI on non nvidia drivers private string GetTxlfExpr(ShaderIrOp Op) { + // TODO: Support all suffixes ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + string Sampler = GetTexSamplerName(Op); string Coords = GetITexSamplerCoords(Op); string Ch = "rgba".Substring(Meta.Elem, 1); - return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; + string Lod = "0"; + + if (Meta.LevelOfDetail != null) + { + Lod = GetOperExpr(Op, Meta.LevelOfDetail); + } + + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, GetOperExpr(Op, Meta.Offset)); + return "texelFetchOffset(" + Sampler + ", " + Coords + ", " + Lod + ", " + Offset + ")." + Ch; + } + + return "texelFetch(" + Sampler + ", " + Coords + ", " + Lod + ")." + Ch; } private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); @@ -1246,14 +1339,205 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetTexSamplerCoords(ShaderIrOp Op) { - return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + bool HasDepth = (Meta.TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0; + + int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget); + + bool IsArray = ImageUtils.IsArray(Meta.TextureTarget); + + + string GetLastArgument(ShaderIrNode Node) + { + string Result = GetOperExpr(Op, Node); + + // array index is actually an integer so we need to pass it correctly + if (IsArray) + { + Result = "float(floatBitsToInt(" + Result + "))"; + } + + return Result; + } + + string LastArgument; + string DepthArgument = ""; + + int VecSize = Coords; + if (HasDepth && Op.Inst != ShaderIrInst.Tld4) + { + VecSize++; + DepthArgument = $", {GetOperExpr(Op, Meta.DepthCompare)}"; + } + + switch (Coords) + { + case 1: + if (HasDepth) + { + return $"vec3({GetOperExpr(Op, Meta.Coordinates[0])}, 0.0{DepthArgument})"; + } + + return GetOperExpr(Op, Meta.Coordinates[0]); + case 2: + LastArgument = GetLastArgument(Meta.Coordinates[1]); + + return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {LastArgument}{DepthArgument})"; + case 3: + LastArgument = GetLastArgument(Meta.Coordinates[2]); + + return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {LastArgument}{DepthArgument})"; + case 4: + LastArgument = GetLastArgument(Meta.Coordinates[3]); + + return $"vec4({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {GetOperExpr(Op, Meta.Coordinates[2])}, {LastArgument}){DepthArgument}"; + default: + throw new InvalidOperationException(); + } + + } + + private string GetTextureOffset(ShaderIrMetaTex Meta, string Oper, int Shift = 4, int Mask = 0xF) + { + string GetOffset(string Operation, int Index) + { + return $"({Operation} >> {Index * Shift}) & 0x{Mask:x}"; + } + + int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget); + + if (ImageUtils.IsArray(Meta.TextureTarget)) + Coords -= 1; + + switch (Coords) + { + case 1: + return GetOffset(Oper, 0); + case 2: + return "ivec2(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ")"; + case 3: + return "ivec3(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ")"; + case 4: + return "ivec4(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ", " + GetOffset(Oper, 3) + ")"; + default: + throw new InvalidOperationException(); + } + } + + // TODO: support AOFFI on non nvidia drivers + private string GetTextureGatherOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + + string ChString = "." + Ch; + + string Comp = Meta.Component.ToString(); + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + Comp = GetOperExpr(Op, Meta.DepthCompare); + } + + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))", 8, 0x3F); + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Comp + ", " + Offset + ")" + ChString; + } + + return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + Comp + ")" + ChString; + } + // TODO: Support PTP + else if ((Suffix & TextureInstructionSuffix.PTP) != 0) + { + throw new NotImplementedException(); + } + + return "textureGather(" + Sampler + ", " + Coords + ", " + Comp + ")" + ChString; + } + + // TODO: support AOFFI on non nvidia drivers + private string GetTextureOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix; + + string ChString = "." + Ch; + + if ((Suffix & TextureInstructionSuffix.DC) != 0) + { + ChString = ""; + } + + // TODO: Support LBA and LLA + if ((Suffix & TextureInstructionSuffix.LZ) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureLodOffset(" + Sampler + ", " + Coords + ", 0.0, " + Offset + ")" + ChString; + } + + return "textureLod(" + Sampler + ", " + Coords + ", 0.0)" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.LB) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + + return "texture(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.LL) != 0) + { + if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureLodOffset(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ", " + Offset + ")" + ChString; + } + + return "textureLod(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString; + } + else if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver) + { + string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))"); + + return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ")" + ChString; + } + else + { + return "texture(" + Sampler + ", " + Coords + ")" + ChString; + } + throw new NotImplementedException($"Texture Suffix {Meta.TextureInstructionSuffix} is not implemented"); + } private string GetITexSamplerCoords(ShaderIrOp Op) { - return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + switch (ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget)) + { + case 1: + return GetOperExpr(Op, Meta.Coordinates[0]); + case 2: + return "ivec2(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ")"; + case 3: + return "ivec3(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ", " + GetOperExpr(Op, Meta.Coordinates[2]) + ")"; + default: + throw new InvalidOperationException(); + } } private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) @@ -1292,22 +1576,6 @@ namespace Ryujinx.Graphics.Gal.Shader } break; } - - 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); - - if (!float.IsNaN(Value) && !float.IsInfinity(Value)) - { - return GetFloatConst(Value); - } - } - break; - } } switch (DstType) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index adcc47b9..8b4eacdf 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Texture; using System; using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; @@ -29,6 +30,75 @@ namespace Ryujinx.Graphics.Gal.Shader { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } }; + private static GalTextureTarget TexToTextureTarget(int TexType, bool IsArray) + { + switch (TexType) + { + case 0: + return IsArray ? GalTextureTarget.OneDArray : GalTextureTarget.OneD; + case 2: + return IsArray ? GalTextureTarget.TwoDArray : GalTextureTarget.TwoD; + case 4: + if (IsArray) + throw new InvalidOperationException($"ARRAY bit set on a TEX with 3D texture!"); + return GalTextureTarget.ThreeD; + case 6: + return IsArray ? GalTextureTarget.CubeArray : GalTextureTarget.CubeMap; + default: + throw new InvalidOperationException(); + } + } + + private static GalTextureTarget TexsToTextureTarget(int TexType) + { + switch (TexType) + { + case 0: + return GalTextureTarget.OneD; + case 2: + case 4: + case 6: + case 8: + case 0xa: + case 0xc: + return GalTextureTarget.TwoD; + case 0xe: + case 0x10: + case 0x12: + return GalTextureTarget.TwoDArray; + case 0x14: + case 0x16: + return GalTextureTarget.ThreeD; + case 0x18: + case 0x1a: + return GalTextureTarget.CubeMap; + default: + throw new InvalidOperationException(); + } + } + + public static GalTextureTarget TldsToTextureTarget(int TexType) + { + switch (TexType) + { + case 0: + case 2: + return GalTextureTarget.OneD; + case 4: + case 8: + case 0xa: + case 0xc: + case 0x18: + return GalTextureTarget.TwoD; + case 0x10: + return GalTextureTarget.TwoDArray; + case 0xe: + return GalTextureTarget.ThreeD; + default: + throw new InvalidOperationException(); + } + } + public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode[] Opers = OpCode.Abuf20(); @@ -132,43 +202,166 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Tex(ShaderIrBlock Block, long OpCode, int Position) { - EmitTex(Block, OpCode, GprHandle: false); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x38); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x8: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x10: + Suffix = TextureInstructionSuffix.LB; + break; + case 0x18: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x30: + Suffix = TextureInstructionSuffix.LBA; + break; + case 0x38: + Suffix = TextureInstructionSuffix.LLA; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEX instruction {RawSuffix}"); + } + + bool IsOffset = OpCode.Read(0x36); + + if (IsOffset) + Suffix |= TextureInstructionSuffix.AOffI; + + EmitTex(Block, OpCode, Suffix, GprHandle: false); } public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position) { - EmitTex(Block, OpCode, GprHandle: true); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x24, 0xe); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x2: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x4: + Suffix = TextureInstructionSuffix.LB; + break; + case 0x6: + Suffix = TextureInstructionSuffix.LL; + break; + case 0xc: + Suffix = TextureInstructionSuffix.LBA; + break; + case 0xe: + Suffix = TextureInstructionSuffix.LLA; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEX.B instruction {RawSuffix}"); + } + + bool IsOffset = OpCode.Read(0x23); + + if (IsOffset) + Suffix |= TextureInstructionSuffix.AOffI; + + EmitTex(Block, OpCode, Suffix, GprHandle: true); } - private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) + private static void EmitTex(ShaderIrBlock Block, long OpCode, TextureInstructionSuffix TextureInstructionSuffix, bool GprHandle) { - //TODO: Support other formats. - ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; + bool IsArray = OpCode.HasArray(); + + GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray); + + bool HasDepthCompare = OpCode.Read(0x32); + + if (HasDepthCompare) + { + TextureInstructionSuffix |= TextureInstructionSuffix.DC; + } - for (int Index = 0; Index < Coords.Length; Index++) + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)]; + + int IndexExtraCoord = 0; + + if (IsArray) + { + IndexExtraCoord++; + + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + + for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++) { ShaderIrOperGpr CoordReg = OpCode.Gpr8(); CoordReg.Index += Index; + CoordReg.Index += IndexExtraCoord; + if (!CoordReg.IsValidRegister) { CoordReg.Index = ShaderIrOperGpr.ZRIndex; } - Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); - - Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg)); + Coords[Index] = CoordReg; } int ChMask = OpCode.Read(31, 0xf); + ShaderIrOperGpr LevelOfDetail = null; + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + // TODO: determine first argument when TEX.B is used + int OperBIndex = GprHandle ? 1 : 0; + + if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LB) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LBA) != 0 || + (TextureInstructionSuffix & TextureInstructionSuffix.LLA) != 0) + { + LevelOfDetail = OpCode.Gpr20(); + LevelOfDetail.Index += OperBIndex; + + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + + OperBIndex++; + } + + // ??? ShaderIrNode OperC = GprHandle ? (ShaderIrNode)OpCode.Gpr20() : (ShaderIrNode)OpCode.Imm13_36(); ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; + Coords = CoordsRegistersToTempRegisters(Block, Coords); + int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) @@ -187,9 +380,14 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords) + { + LevelOfDetail = LevelOfDetail, + Offset = Offset, + DepthCompare = DepthCompare + }; - ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); + ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords.Length > 1 ? Coords[1] : null, OperC, Meta); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } @@ -197,17 +395,238 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Texs(ShaderIrBlock Block, long OpCode, int Position) { - EmitTexs(Block, OpCode, ShaderIrInst.Texs); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x1e); + + switch (RawSuffix) + { + case 0: + case 0x4: + case 0x10: + case 0x16: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x6: + case 0x1a: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x8: + Suffix = TextureInstructionSuffix.DC; + break; + case 0x2: + case 0xe: + case 0x14: + case 0x18: + Suffix = TextureInstructionSuffix.None; + break; + case 0xa: + Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.DC; + break; + case 0xc: + case 0x12: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.DC; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TEXS instruction {RawSuffix}"); + } + + GalTextureTarget TextureTarget = TexsToTextureTarget(OpCode.Read(52, 0x1e)); + + EmitTexs(Block, OpCode, ShaderIrInst.Texs, TextureTarget, Suffix); } public static void Tlds(ShaderIrBlock Block, long OpCode, int Position) { - EmitTexs(Block, OpCode, ShaderIrInst.Txlf); + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0x1e); + + switch (RawSuffix) + { + case 0: + case 0x4: + case 0x8: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.AOffI; + break; + case 0xc: + Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.MZ; + break; + case 0xe: + case 0x10: + Suffix = TextureInstructionSuffix.LZ; + break; + case 0x2: + case 0xa: + Suffix = TextureInstructionSuffix.LL; + break; + case 0x18: + Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.AOffI; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TLDS instruction {RawSuffix}"); + } + + GalTextureTarget TextureTarget = TldsToTextureTarget(OpCode.Read(52, 0x1e)); + + EmitTexs(Block, OpCode, ShaderIrInst.Txlf, TextureTarget, Suffix); + } + + public static void Tld4(ShaderIrBlock Block, long OpCode, int Position) + { + TextureInstructionSuffix Suffix; + + int RawSuffix = OpCode.Read(0x34, 0xc); + + switch (RawSuffix) + { + case 0: + Suffix = TextureInstructionSuffix.None; + break; + case 0x4: + Suffix = TextureInstructionSuffix.AOffI; + break; + case 0x8: + Suffix = TextureInstructionSuffix.PTP; + break; + default: + throw new InvalidOperationException($"Invalid Suffix for TLD4 instruction {RawSuffix}"); + } + + bool IsShadow = OpCode.Read(0x32); + + bool IsArray = OpCode.HasArray(); + int ChMask = OpCode.Read(31, 0xf); + + GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray); + + if (IsShadow) + { + Suffix |= TextureInstructionSuffix.DC; + } + + EmitTld4(Block, OpCode, TextureTarget, Suffix, ChMask, OpCode.Read(0x38, 0x3), false); + } + + public static void Tld4s(ShaderIrBlock Block, long OpCode, int Position) + { + TextureInstructionSuffix Suffix = TextureInstructionSuffix.None; + + bool IsOffset = OpCode.Read(0x33); + bool IsShadow = OpCode.Read(0x32); + + if (IsOffset) + { + Suffix |= TextureInstructionSuffix.AOffI; + } + + if (IsShadow) + { + Suffix |= TextureInstructionSuffix.DC; + } + + // TLD4S seems to only support 2D textures with RGBA mask? + EmitTld4(Block, OpCode, GalTextureTarget.TwoD, Suffix, RGBA, OpCode.Read(0x34, 0x3), true); } - private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) + private static void EmitTexs(ShaderIrBlock Block, + long OpCode, + ShaderIrInst Inst, + GalTextureTarget TextureTarget, + TextureInstructionSuffix TextureInstructionSuffix) { - //TODO: Support other formats. + if (Inst == ShaderIrInst.Txlf && TextureTarget == GalTextureTarget.CubeArray) + { + throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!"); + } + + bool IsArray = ImageUtils.IsArray(TextureTarget); + + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)]; + + ShaderIrOperGpr OperA = OpCode.Gpr8(); + ShaderIrOperGpr OperB = OpCode.Gpr20(); + + ShaderIrOperGpr SuffixExtra = OpCode.Gpr20(); + SuffixExtra.Index += 1; + + int CoordStartIndex = 0; + + if (IsArray) + { + CoordStartIndex++; + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + switch (Coords.Length - CoordStartIndex) + { + case 1: + Coords[0] = OpCode.Gpr8(); + + break; + case 2: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + break; + case 3: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + Coords[1] = OpCode.Gpr8(); + Coords[1].Index += 1 + CoordStartIndex; + + break; + default: + throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TEXS"); + } + + int OperBIndex = 0; + + ShaderIrOperGpr LevelOfDetail = null; + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + // OperB is always the last value + // Not applicable to 1d textures + if (Coords.Length - CoordStartIndex != 1) + { + Coords[Coords.Length - CoordStartIndex - 1] = OperB; + OperBIndex++; + } + + // Encoding of TEXS/TLDS is a bit special and change for 2d textures + // NOTE: OperA seems to hold at best two args. + // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB. + if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureInstructionSuffix != TextureInstructionSuffix.LZ && TextureTarget == GalTextureTarget.TwoD) + { + Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8(); + Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1; + OperBIndex--; + } + + // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?) + if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0) + { + LevelOfDetail = OpCode.Gpr20(); + LevelOfDetail.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + OperBIndex++; + } + int LutIndex; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; @@ -276,12 +695,7 @@ namespace Ryujinx.Graphics.Gal.Shader } ShaderIrNode OperC = OpCode.Imm13_36(); - - ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); - ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); - - Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); - Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); + Coords = CoordsRegistersToTempRegisters(Block, Coords); for (int Ch = 0; Ch < 4; Ch++) { @@ -290,9 +704,13 @@ namespace Ryujinx.Graphics.Gal.Shader continue; } - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords) + { + LevelOfDetail = LevelOfDetail, + Offset = Offset, + DepthCompare = DepthCompare + }; + ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); ShaderIrOperGpr Dst = GetDst(); @@ -303,9 +721,156 @@ namespace Ryujinx.Graphics.Gal.Shader } } + private static void EmitTld4(ShaderIrBlock Block, long OpCode, GalTextureTarget TextureType, TextureInstructionSuffix TextureInstructionSuffix, int ChMask, int Component, bool Scalar) + { + ShaderIrOperGpr OperA = OpCode.Gpr8(); + ShaderIrOperGpr OperB = OpCode.Gpr20(); + ShaderIrOperImm OperC = OpCode.Imm13_36(); + + ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureType)]; + + ShaderIrOperGpr Offset = null; + ShaderIrOperGpr DepthCompare = null; + + bool IsArray = ImageUtils.IsArray(TextureType); + + int OperBIndex = 0; + + if (Scalar) + { + int CoordStartIndex = 0; + + if (IsArray) + { + CoordStartIndex++; + Coords[Coords.Length - 1] = OperB; + } + + switch (Coords.Length - CoordStartIndex) + { + case 1: + Coords[0] = OpCode.Gpr8(); + + break; + case 2: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + break; + case 3: + Coords[0] = OpCode.Gpr8(); + Coords[0].Index += CoordStartIndex; + + Coords[1] = OpCode.Gpr8(); + Coords[1].Index += 1 + CoordStartIndex; + + break; + default: + throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TLD4S"); + } + + if (Coords.Length - CoordStartIndex != 1) + { + Coords[Coords.Length - CoordStartIndex - 1] = OperB; + OperBIndex++; + } + + if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureType == GalTextureTarget.TwoD) + { + Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8(); + Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1; + OperBIndex--; + } + } + else + { + int IndexExtraCoord = 0; + + if (IsArray) + { + IndexExtraCoord++; + + Coords[Coords.Length - 1] = OpCode.Gpr8(); + } + + for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++) + { + Coords[Index] = OpCode.Gpr8(); + + Coords[Index].Index += Index; + + Coords[Index].Index += IndexExtraCoord; + + if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex) + { + Coords[Index].Index = ShaderIrOperGpr.ZRIndex; + } + } + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) + { + Offset = OpCode.Gpr20(); + Offset.Index += OperBIndex; + OperBIndex++; + } + + if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0) + { + DepthCompare = OpCode.Gpr20(); + DepthCompare.Index += OperBIndex; + OperBIndex++; + } + + Coords = CoordsRegistersToTempRegisters(Block, Coords); + + int RegInc = 0; + + for (int Ch = 0; Ch < 4; Ch++) + { + if (!IsChannelUsed(ChMask, Ch)) + { + continue; + } + + ShaderIrOperGpr Dst = OpCode.Gpr0(); + + Dst.Index += RegInc++; + + if (!Dst.IsValidRegister || Dst.IsConst) + { + continue; + } + + ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureType, TextureInstructionSuffix, Coords) + { + Component = Component, + Offset = Offset, + DepthCompare = DepthCompare + }; + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Tld4, OperA, OperB, OperC, Meta); + + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); + } + } + private static bool IsChannelUsed(int ChMask, int Ch) { return (ChMask & (1 << Ch)) != 0; } + + private static ShaderIrOperGpr[] CoordsRegistersToTempRegisters(ShaderIrBlock Block, params ShaderIrOperGpr[] Registers) + { + ShaderIrOperGpr[] Res = new ShaderIrOperGpr[Registers.Length]; + + for (int Index = 0; Index < Res.Length; Index++) + { + Res[Index] = ShaderIrOperGpr.MakeTemporary(Index); + Block.AddNode(new ShaderIrAsg(Res[Index], Registers[Index])); + } + + return Res; + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs index f0f92148..e241e1ca 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs @@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gal.Shader return ((int)(OpCode >> 20) << 8) >> 8; } + private static bool HasArray(this long OpCode) + { + return OpCode.Read(0x1c); + } + private static ShaderIrOperAbuf[] Abuf20(this long OpCode) { int Abuf = OpCode.Read(20, 0x3ff); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 35dea612..68ff214e 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Gal.Shader Ipa, Texb, Texs, + Tld4, Trunc, F_End, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs index 82f3bb77..72ea221a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs @@ -1,12 +1,24 @@ +using Ryujinx.Graphics.Texture; + namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrMetaTex : ShaderIrMeta { - public int Elem { get; private set; } + public int Elem { get; private set; } + public GalTextureTarget TextureTarget { get; private set; } + public ShaderIrNode[] Coordinates { get; private set; } + public TextureInstructionSuffix TextureInstructionSuffix { get; private set; } + public ShaderIrOperGpr LevelOfDetail; + public ShaderIrOperGpr Offset; + public ShaderIrOperGpr DepthCompare; + public int Component; // for TLD4(S) - public ShaderIrMetaTex(int Elem) + public ShaderIrMetaTex(int Elem, GalTextureTarget TextureTarget, TextureInstructionSuffix TextureInstructionSuffix, params ShaderIrNode[] Coordinates) { - this.Elem = Elem; + this.Elem = Elem; + this.TextureTarget = TextureTarget; + this.TextureInstructionSuffix = TextureInstructionSuffix; + this.Coordinates = Coordinates; } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 177e36c3..d2bbd38c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -122,6 +122,8 @@ namespace Ryujinx.Graphics.Gal.Shader Set("1101111101001x", ShaderDecode.Texq); Set("1101x00xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); + Set("110010xxxx111x", ShaderDecode.Tld4); + Set("1101111100xxxx", ShaderDecode.Tld4s); Set("01011111xxxxxx", ShaderDecode.Vmad); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); Set("0011011x00xxxx", ShaderDecode.Xmad_I); |
