From f43dd080641b3c2508e35bc7651fb0adcb282a2e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 29 May 2018 20:37:10 -0300 Subject: [PATCH] Added support for more shader instructions and texture formats, fix swapped channels in RGB565 and RGBA5551? texture formats, allow zero values on blending registers, initial work to build CFG on the shader decoder, update the BRA instruction to work with it (WIP) --- ChocolArm64/Decoder/ABlock.cs | 2 +- ChocolArm64/Decoder/ADecoder.cs | 2 +- Ryujinx.Core/Gpu/TextureReader.cs | 197 +++++++++++++- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 21 +- .../Gal/OpenGL/OGLEnumConverter.cs | 20 +- Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 13 +- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 253 +++++++++++++----- .../Gal/Shader/ShaderDecodeAlu.cs | 164 +++++++++++- .../Gal/Shader/ShaderDecodeFlow.cs | 6 +- Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 172 ++++++++++-- Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs | 40 +-- Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs | 2 + Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs | 4 - .../Gal/Shader/ShaderOpCodeTable.cs | 7 + 14 files changed, 749 insertions(+), 154 deletions(-) delete mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs diff --git a/ChocolArm64/Decoder/ABlock.cs b/ChocolArm64/Decoder/ABlock.cs index 32974c1ab..7a0fc6077 100644 --- a/ChocolArm64/Decoder/ABlock.cs +++ b/ChocolArm64/Decoder/ABlock.cs @@ -5,7 +5,7 @@ namespace ChocolArm64.Decoder class ABlock { public long Position { get; set; } - public long EndPosition { get; set; } + public long EndPosition { get; set; } public ABlock Next { get; set; } public ABlock Branch { get; set; } diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs index 32a68ad34..b154a54cd 100644 --- a/ChocolArm64/Decoder/ADecoder.cs +++ b/ChocolArm64/Decoder/ADecoder.cs @@ -94,7 +94,7 @@ namespace ChocolArm64.Decoder } } - //If we have on the tree two blocks with the same end position, + //If we have on the graph two blocks with the same end position, //then we need to split the bigger block and have two small blocks, //the end position of the bigger "Current" block should then be == to //the position of the "Smaller" block. diff --git a/Ryujinx.Core/Gpu/TextureReader.cs b/Ryujinx.Core/Gpu/TextureReader.cs index f3e41046a..0c1c83d5d 100644 --- a/Ryujinx.Core/Gpu/TextureReader.cs +++ b/Ryujinx.Core/Gpu/TextureReader.cs @@ -10,19 +10,132 @@ namespace Ryujinx.Core.Gpu { switch (Texture.Format) { - 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); - case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); - case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture); + case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); + case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); + case GalTextureFormat.R32: return Read4Bpp (Memory, Texture); + case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture); + case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture); + case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture); + case GalTextureFormat.R8: return Read1Bpp (Memory, Texture); + case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture); } throw new NotImplementedException(Texture.Format.ToString()); } + private unsafe static byte[] Read1Bpp(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 1); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + 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); + + byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset); + + *(BuffPtr + OutOffs) = Pixel; + + OutOffs++; + } + } + + return Output; + } + + private unsafe static byte[] Read5551(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 2]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + 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); + + uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset); + + Pixel = (Pixel & 0x001f) << 11 | + (Pixel & 0x03e0) << 1 | + (Pixel & 0x7c00) >> 9 | + (Pixel & 0x8000) >> 15; + + *(short*)(BuffPtr + OutOffs) = (short)Pixel; + + OutOffs += 2; + } + } + + return Output; + } + + private unsafe static byte[] Read565(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 2]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + 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); + + uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset); + + Pixel = (Pixel & 0x001f) << 11 | + (Pixel & 0x07e0) | + (Pixel & 0xf800) >> 11; + + *(short*)(BuffPtr + OutOffs) = (short)Pixel; + + OutOffs += 2; + } + } + + return Output; + } + private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture) { int Width = Texture.Width; @@ -89,6 +202,74 @@ namespace Ryujinx.Core.Gpu return Output; } + private unsafe static byte[] Read8Bpp(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 8]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + 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); + + long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset); + + *(long*)(BuffPtr + OutOffs) = Pixel; + + OutOffs += 8; + } + } + + return Output; + } + + private unsafe static byte[] Read16Bpp(IAMemory Memory, Texture Texture) + { + int Width = Texture.Width; + int Height = Texture.Height; + + byte[] Output = new byte[Width * Height * 16]; + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); + + (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( + Memory, + Texture.Position); + + 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); + + long PxLow = CpuMem.ReadInt64Unchecked(Position + Offset + 0); + long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8); + + *(long*)(BuffPtr + OutOffs + 0) = PxLow; + *(long*)(BuffPtr + OutOffs + 8) = PxHigh; + + OutOffs += 16; + } + } + + return Output; + } + private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture) { int Width = (Texture.Width + 3) / 4; diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 37291e18b..b3d8b03d9 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -2,13 +2,18 @@ namespace Ryujinx.Graphics.Gal { public enum GalTextureFormat { - A8B8G8R8 = 0x8, - A1B5G5R5 = 0x14, - B5G6R5 = 0x15, - BC1 = 0x24, - BC2 = 0x25, - BC3 = 0x26, - BC4 = 0x27, - BC5 = 0x28 + R32G32B32A32 = 0x1, + R16G16B16A16 = 0x3, + A8B8G8R8 = 0x8, + R32 = 0xf, + A1B5G5R5 = 0x14, + B5G6R5 = 0x15, + G8R8 = 0x18, + R8 = 0x1d, + BC1 = 0x24, + BC2 = 0x25, + BC3 = 0x26, + BC4 = 0x27, + BC5 = 0x28 } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index d266a87a8..ee697097f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -59,9 +59,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL { 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); + case GalTextureFormat.R32G32B32A32: return (PixelFormat.Rgba, PixelType.Float); + case GalTextureFormat.R16G16B16A16: return (PixelFormat.Rgba, PixelType.HalfFloat); + case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte); + case GalTextureFormat.R32: return (PixelFormat.Red, PixelType.Float); + case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551); + case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565); + case GalTextureFormat.G8R8: return (PixelFormat.Rg, PixelType.UnsignedByte); + case GalTextureFormat.R8: return (PixelFormat.Red, PixelType.UnsignedByte); } throw new NotImplementedException(Format.ToString()); @@ -145,20 +150,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (BlendEquation) { + default: case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd; case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract; case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract; case GalBlendEquation.Min: return BlendEquationMode.Min; case GalBlendEquation.Max: return BlendEquationMode.Max; } - - throw new ArgumentException(nameof(BlendEquation)); } public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor) { switch (BlendFactor) { + default: case GalBlendFactor.Zero: return BlendingFactorSrc.Zero; case GalBlendFactor.One: return BlendingFactorSrc.One; case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor; @@ -179,14 +184,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha; case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha; } - - throw new ArgumentException(nameof(BlendFactor)); } public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor) { switch (BlendFactor) { + default: case GalBlendFactor.Zero: return BlendingFactorDest.Zero; case GalBlendFactor.One: return BlendingFactorDest.One; case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor; @@ -207,8 +211,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha; case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha; } - - throw new ArgumentException(nameof(BlendFactor)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 2650569e6..43a04813c 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -4,6 +4,10 @@ namespace Ryujinx.Graphics.Gal.Shader { class GlslDecl { + public const int TessCoordAttrX = 0x2f0; + public const int TessCoordAttrY = 0x2f4; + public const int TessCoordAttrZ = 0x2f8; + public const int InstanceIdAttr = 0x2f8; public const int VertexIdAttr = 0x2fc; public const int GlPositionWAttr = 0x7c; @@ -48,7 +52,7 @@ namespace Ryujinx.Graphics.Gal.Shader public GalShaderType ShaderType { get; private set; } - public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType) + public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType) { this.ShaderType = ShaderType; @@ -75,9 +79,12 @@ namespace Ryujinx.Graphics.Gal.Shader m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4)); } - foreach (ShaderIrNode Node in Nodes) + foreach (ShaderIrBlock Block in Blocks) { - Traverse(null, Node); + foreach (ShaderIrNode Node in Block.GetNodes()) + { + Traverse(null, Node); + } } } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 88e462439..5c4537fe0 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Gal.Shader private GlslDecl Decl; + private ShaderIrBlock[] Blocks; + private StringBuilder SB; public GlslDecompiler() @@ -37,6 +39,8 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Asr, GetAsrExpr }, { ShaderIrInst.Band, GetBandExpr }, { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Bor, GetBorExpr }, + { ShaderIrInst.Bxor, GetBxorExpr }, { ShaderIrInst.Ceil, GetCeilExpr }, { ShaderIrInst.Ceq, GetCeqExpr }, { ShaderIrInst.Cge, GetCgeExpr }, @@ -50,19 +54,27 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Fabs, GetAbsExpr }, { ShaderIrInst.Fadd, GetAddExpr }, { ShaderIrInst.Fceq, GetCeqExpr }, + { ShaderIrInst.Fcequ, GetCequExpr }, { ShaderIrInst.Fcge, GetCgeExpr }, + { ShaderIrInst.Fcgeu, GetCgeuExpr }, { ShaderIrInst.Fcgt, GetCgtExpr }, + { ShaderIrInst.Fcgtu, GetCgtuExpr }, { ShaderIrInst.Fclamp, GetFclampExpr }, { ShaderIrInst.Fcle, GetCleExpr }, + { ShaderIrInst.Fcleu, GetCleuExpr }, { ShaderIrInst.Fclt, GetCltExpr }, + { ShaderIrInst.Fcltu, GetCltuExpr }, + { ShaderIrInst.Fcnan, GetCnanExpr }, { ShaderIrInst.Fcne, GetCneExpr }, + { ShaderIrInst.Fcneu, GetCneuExpr }, + { ShaderIrInst.Fcnum, GetCnumExpr }, { ShaderIrInst.Fcos, GetFcosExpr }, { ShaderIrInst.Fex2, GetFex2Expr }, { ShaderIrInst.Ffma, GetFfmaExpr }, { ShaderIrInst.Flg2, GetFlg2Expr }, { ShaderIrInst.Floor, GetFloorExpr }, - { ShaderIrInst.Fmax, GetFmaxExpr }, - { ShaderIrInst.Fmin, GetFminExpr }, + { ShaderIrInst.Fmax, GetMaxExpr }, + { ShaderIrInst.Fmin, GetMinExpr }, { ShaderIrInst.Fmul, GetMulExpr }, { ShaderIrInst.Fneg, GetNegExpr }, { ShaderIrInst.Frcp, GetFrcpExpr }, @@ -74,6 +86,8 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Kil, GetKilExpr }, { ShaderIrInst.Lsl, GetLslExpr }, { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Max, GetMaxExpr }, + { ShaderIrInst.Min, GetMinExpr }, { ShaderIrInst.Mul, GetMulExpr }, { ShaderIrInst.Neg, GetNegExpr }, { ShaderIrInst.Not, GetNotExpr }, @@ -91,11 +105,9 @@ namespace Ryujinx.Graphics.Gal.Shader public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) { - ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Memory, Position); + Blocks = ShaderDecoder.Decode(Memory, Position); - ShaderIrNode[] Nodes = Block.GetNodes(); - - Decl = new GlslDecl(Nodes, ShaderType); + Decl = new GlslDecl(Blocks, ShaderType); SB = new StringBuilder(); @@ -108,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.Shader PrintDeclGprs(); PrintDeclPreds(); - PrintBlockScope(Nodes, 0, Nodes.Length, "void main()", 1); + PrintBlockScope(Blocks[0], null, null, "void main()", IdentationStr); string GlslCode = SB.ToString(); @@ -230,37 +242,75 @@ namespace Ryujinx.Graphics.Gal.Shader } private void PrintBlockScope( - ShaderIrNode[] Nodes, - int Start, - int Count, - string ScopeName, - int IdentationLevel) + ShaderIrBlock Block, + ShaderIrBlock EndBlock, + ShaderIrBlock LoopBlock, + string ScopeName, + string Identation, + bool IsDoWhile = false) { - string Identation = string.Empty; + string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length); - for (int Index = 0; Index < IdentationLevel - 1; Index++) + if (IsDoWhile) { - Identation += IdentationStr; + SB.AppendLine(UpIdent + "do {"); + } + else + { + SB.AppendLine(UpIdent + ScopeName + " {"); } - if (ScopeName != string.Empty) + while (Block != null && Block != EndBlock) { - ScopeName += " "; + ShaderIrNode[] Nodes = Block.GetNodes(); + + Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes); } - SB.AppendLine(Identation + ScopeName + "{"); - - string LastLine = Identation + "}"; - - if (IdentationLevel > 0) + if (IsDoWhile) { - Identation += IdentationStr; + SB.AppendLine(UpIdent + "} " + ScopeName + ";"); + } + else + { + SB.AppendLine(UpIdent + "}"); + } + } + + private ShaderIrBlock PrintNodes( + ShaderIrBlock Block, + ShaderIrBlock EndBlock, + ShaderIrBlock LoopBlock, + string Identation, + params ShaderIrNode[] Nodes) + { + /* + * Notes about control flow and if-else/loop generation: + * The code assumes that the program has sane control flow, + * that is, there's no jumps to a location after another jump or + * jump target (except for the end of an if-else block), and backwards + * jumps to a location before the last loop dominator. + * Such cases needs to be transformed on a step before the GLSL code + * generation to ensure that we have sane graphs to work with. + * TODO: Such transformation is not yet implemented. + */ + string NewIdent = Identation + IdentationStr; + + ShaderIrBlock LoopTail = GetLoopTailBlock(Block); + + if (LoopTail != null && LoopBlock != Block) + { + //Shoock! kuma shock! We have a loop here! + //The entire sequence needs to be inside a do-while block. + ShaderIrBlock LoopEnd = GetDownBlock(LoopTail); + + PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true); + + return LoopEnd; } - for (int Index = Start; Index < Start + Count; Index++) + foreach (ShaderIrNode Node in Nodes) { - ShaderIrNode Node = Nodes[Index]; - if (Node is ShaderIrCond Cond) { string IfExpr = GetSrcExpr(Cond.Pred, true); @@ -272,42 +322,41 @@ namespace Ryujinx.Graphics.Gal.Shader if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) { - ShaderIrLabel Label = (ShaderIrLabel)Op.OperandA; + //Branch is a loop branch and would result in infinite recursion. + if (Block.Branch.Position <= Block.Position) + { + SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - int Target = FindLabel(Nodes, Label, Index + 1); + SB.AppendLine(Identation + IdentationStr + "continue;"); - int IfCount = Target - Index - 1; + SB.AppendLine(Identation + "}"); + + continue; + } string SubScopeName = "if (!" + IfExpr + ")"; - if (Nodes[Index + IfCount] is ShaderIrOp LastOp && LastOp.Inst == ShaderIrInst.Bra) + PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent); + + ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch; + + if (IfElseEnd?.Position > Block.Branch.Position) { - Target = FindLabel(Nodes, (ShaderIrLabel)LastOp.OperandA, Index + 1); + PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent); - 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; + return IfElseEnd; } - else - { - PrintBlockScope(Nodes, Index + 1, IfCount, SubScopeName, IdentationLevel + 1); - Index += IfCount; - } + return Block.Branch; } else { - string SubScopeName = "if (" + IfExpr + ")"; + SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - ShaderIrNode[] Child = new ShaderIrNode[] { Cond.Child }; + PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child); - PrintBlockScope(Child, 0, 1, SubScopeName, IdentationLevel + 1); + SB.AppendLine(Identation + "}"); } - } else if (Node is ShaderIrAsg Asg) { @@ -322,7 +371,16 @@ namespace Ryujinx.Graphics.Gal.Shader } else if (Node is ShaderIrOp Op) { - if (Op.Inst == ShaderIrInst.Exit) + if (Op.Inst == ShaderIrInst.Bra) + { + if (Block.Branch.Position <= Block.Position) + { + SB.AppendLine(Identation + "continue;"); + } + + continue; + } + else if (Op.Inst == ShaderIrInst.Exit) { //Do everything that needs to be done before //the shader ends here. @@ -336,10 +394,6 @@ 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); @@ -350,22 +404,35 @@ namespace Ryujinx.Graphics.Gal.Shader } } - SB.AppendLine(LastLine); + return Block.Next; } - private int FindLabel(ShaderIrNode[] Nodes, ShaderIrLabel Label, int Start) + private ShaderIrBlock GetUpBlock(ShaderIrBlock Block) { - int Target; + return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position); + } - for (Target = Start; Target < Nodes.Length; Target++) + private ShaderIrBlock GetDownBlock(ShaderIrBlock Block) + { + return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition); + } + + private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead) + { + ShaderIrBlock Tail = null; + + foreach (ShaderIrBlock Block in LoopHead.Sources) { - if (Nodes[Target] == Label) + if (Block.Position >= LoopHead.Position) { - return Target; + if (Tail == null || Tail.Position < Block.Position) + { + Tail = Block; + } } } - throw new InvalidOperationException(); + return Tail; } private bool IsValidOutOper(ShaderIrNode Node) @@ -480,9 +547,22 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperAbuf Abuf) { - if (Abuf.Offs == GlslDecl.VertexIdAttr) + if (Decl.ShaderType == GalShaderType.Vertex) { - return "gl_VertexID"; + switch (Abuf.Offs) + { + case GlslDecl.VertexIdAttr: return "gl_VertexID"; + case GlslDecl.InstanceIdAttr: return "gl_InstanceID"; + } + } + else if (Decl.ShaderType == GalShaderType.TessEvaluation) + { + switch (Abuf.Offs) + { + case GlslDecl.TessCoordAttrX: return "gl_TessCoord.x"; + case GlslDecl.TessCoordAttrY: return "gl_TessCoord.y"; + case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z"; + } } return GetName(Decl.InAttributes, Abuf); @@ -567,6 +647,10 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); + private string GetBorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "||"); + + private string GetBxorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^^"); + private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); private string GetClampsExpr(ShaderIrOp Op) @@ -583,13 +667,34 @@ namespace Ryujinx.Graphics.Gal.Shader "uint(" + GetOperExpr(Op, Op.OperandC) + ")))"; } - private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); - private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); - private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); - private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); + + private string GetCequExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "=="); + private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">="); + private string GetCgeuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">="); + + private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); + + private string GetCgtuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">"); + + private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); + + private string GetCleuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<="); + + private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); + + private string GetCltuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<"); + + private string GetCnanExpr(ShaderIrOp Op) => GetUnaryCall(Op, "isnan"); + + private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); + + private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!="); + + private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan"); + private string GetExitExpr(ShaderIrOp Op) => "return"; private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); @@ -604,9 +709,6 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); - private string GetFmaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); - private string GetFminExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); - private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt"); @@ -634,6 +736,9 @@ namespace Ryujinx.Graphics.Gal.Shader GetOperExpr(Op, Op.OperandB) + ")"; } + private string GetMaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); + private string GetMinExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); + private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); @@ -733,6 +838,18 @@ namespace Ryujinx.Graphics.Gal.Shader GetOperExpr(Op, Op.OperandB); } + private string GetBinaryExprWithNaN(ShaderIrOp Op, string Opr) + { + string A = GetOperExpr(Op, Op.OperandA); + string B = GetOperExpr(Op, Op.OperandB); + + string NaNCheck = + " || isnan(" + A + ")" + + " || isnan(" + B + ")"; + + return A + " " + Opr + " " + B + NaNCheck; + } + private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2) { return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " + diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 1bb5f4780..ddd3e3e8c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -126,6 +126,21 @@ namespace Ryujinx.Graphics.Gal.Shader EmitFsetp(Block, OpCode, ShaderOper.RR); } + public static void Imnmx_C(ShaderIrBlock Block, long OpCode) + { + EmitImnmx(Block, OpCode, ShaderOper.CR); + } + + public static void Imnmx_I(ShaderIrBlock Block, long OpCode) + { + EmitImnmx(Block, OpCode, ShaderOper.Imm); + } + + public static void Imnmx_R(ShaderIrBlock Block, long OpCode) + { + EmitImnmx(Block, OpCode, ShaderOper.RR); + } + public static void Ipa(ShaderIrBlock Block, long OpCode) { ShaderIrNode OperA = GetOperAbuf28(OpCode); @@ -265,6 +280,26 @@ namespace Ryujinx.Graphics.Gal.Shader return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr; } + public static void Xmad_CR(ShaderIrBlock Block, long OpCode) + { + EmitXmad(Block, OpCode, ShaderOper.CR); + } + + public static void Xmad_I(ShaderIrBlock Block, long OpCode) + { + EmitXmad(Block, OpCode, ShaderOper.Imm); + } + + public static void Xmad_RC(ShaderIrBlock Block, long OpCode) + { + EmitXmad(Block, OpCode, ShaderOper.RC); + } + + public static void Xmad_RR(ShaderIrBlock Block, long OpCode) + { + EmitXmad(Block, OpCode, ShaderOper.RR); + } + private static void EmitAluBinary( ShaderIrBlock Block, long OpCode, @@ -411,6 +446,16 @@ namespace Ryujinx.Graphics.Gal.Shader } private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitMnmx(Block, OpCode, true, Oper); + } + + private static void EmitImnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + EmitMnmx(Block, OpCode, false, Oper); + } + + private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool NegB = ((OpCode >> 45) & 1) != 0; bool AbsA = ((OpCode >> 46) & 1) != 0; @@ -419,30 +464,48 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; - OperA = GetAluFabsFneg(OperA, AbsA, NegA); + if (IsFloat) + { + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + } + else + { + OperA = GetAluIabsIneg(OperA, AbsA, NegA); + } 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)); } - OperB = GetAluFabsFneg(OperB, AbsB, NegB); + if (IsFloat) + { + OperB = GetAluFabsFneg(OperB, AbsB, NegB); + } + else + { + OperB = GetAluIabsIneg(OperB, AbsB, NegB); + } ShaderIrOperPred Pred = GetOperPred39(OpCode); ShaderIrOp Op; + ShaderIrInst MaxInst = IsFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max; + ShaderIrInst MinInst = IsFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min; + if (Pred.IsConst) { bool IsMax = ((OpCode >> 42) & 1) != 0; Op = new ShaderIrOp(IsMax - ? ShaderIrInst.Fmax - : ShaderIrInst.Fmin, OperA, OperB); + ? MaxInst + : MinInst, OperA, OperB); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } @@ -450,8 +513,8 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrNode PredN = GetOperPred39N(OpCode); - ShaderIrOp OpMax = new ShaderIrOp(ShaderIrInst.Fmax, OperA, OperB); - ShaderIrOp OpMin = new ShaderIrOp(ShaderIrInst.Fmin, OperA, OperB); + ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB); + ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB); ShaderIrAsg AsgMax = new ShaderIrAsg(GetOperGpr0(OpCode), OpMax); ShaderIrAsg AsgMin = new ShaderIrAsg(GetOperGpr0(OpCode), OpMin); @@ -646,5 +709,94 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); } + + private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + //TODO: Confirm SignAB/C, it is just a guess. + //TODO: Implement Mode 3 (CSFU), what it does? + bool SignAB = ((OpCode >> 48) & 1) != 0; + bool SignC = ((OpCode >> 49) & 1) != 0; + bool HighB = ((OpCode >> 52) & 1) != 0; + bool HighA = ((OpCode >> 53) & 1) != 0; + + int Mode = (int)(OpCode >> 50) & 7; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC; + + ShaderIrOperImm Imm16 = new ShaderIrOperImm(16); + ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff); + + ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr; + ShaderIrInst ShiftC = SignC ? ShaderIrInst.Asr : ShaderIrInst.Lsr; + + if (HighA) + { + OperA = new ShaderIrOp(ShiftAB, OperA, Imm16); + } + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RC: OperB = GetOperGpr39 (OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + bool ProductShiftLeft = false, Merge = false; + + if (Oper == ShaderOper.RC) + { + OperC = GetOperCbuf34(OpCode); + } + else + { + OperC = GetOperGpr39(OpCode); + + ProductShiftLeft = ((OpCode >> 36) & 1) != 0; + Merge = ((OpCode >> 37) & 1) != 0; + } + + switch (Mode) + { + //CLO. + case 1: OperC = ExtendTo32(OperC, SignC, 16); break; + + //CHI. + case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break; + } + + ShaderIrNode OperBH = OperB; + + if (HighB) + { + OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16); + } + + ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH); + + if (ProductShiftLeft) + { + MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16); + } + + ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC); + + if (Merge) + { + AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk); + OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); + AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); + } + + if (Mode == 4) + { + OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); + AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); + } + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode)); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs index 6f48d1a8c..89949d62e 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -15,11 +15,11 @@ namespace Ryujinx.Graphics.Gal.Shader throw new NotImplementedException(); } - long Target = ((int)(OpCode >> 20) << 8) >> 8; + int Target = ((int)(OpCode >> 20) << 8) >> 8; - Target += Block.Position + 8; + ShaderIrOperImm Imm = new ShaderIrOperImm(Target); - Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Block.GetLabel(Target)), OpCode)); + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm), OpCode)); } public static void Exit(ShaderIrBlock Block, long OpCode) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 024fa3648..85522ff95 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -1,19 +1,133 @@ +using System.Collections.Generic; + namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecoder { private const bool AddDbgComments = true; - public static ShaderIrBlock DecodeBasicBlock(IGalMemory Memory, long Position) + public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start) { - ShaderIrBlock Block = new ShaderIrBlock(); + Dictionary Visited = new Dictionary(); + Dictionary VisitedEnd = new Dictionary(); - while (true) + Queue Blocks = new Queue(); + + ShaderIrBlock Enqueue(long Position, ShaderIrBlock Source = null) { - Block.Position = Position; + if (!Visited.TryGetValue(Position, out ShaderIrBlock Output)) + { + Output = new ShaderIrBlock(Position); - Block.MarkLabel(Position); + Blocks.Enqueue(Output); + Visited.Add(Position, Output); + } + + if (Source != null) + { + Output.Sources.Add(Source); + } + + return Output; + } + + ShaderIrBlock Entry = Enqueue(Start); + + while (Blocks.Count > 0) + { + ShaderIrBlock Current = Blocks.Dequeue(); + + FillBlock(Memory, Current); + + //Set child blocks. "Branch" is the block the branch instruction + //points to (when taken), "Next" is the block at the next address, + //executed when the branch is not taken. For Unconditional Branches + //or end of shader, Next is null. + if (Current.Nodes.Count > 0) + { + ShaderIrNode LastNode = Current.GetLastNode(); + + ShaderIrOp Op = GetInnermostOp(LastNode); + + if (Op?.Inst == ShaderIrInst.Bra) + { + int Offset = ((ShaderIrOperImm)Op.OperandA).Value; + + long Target = Current.EndPosition + Offset; + + Current.Branch = Enqueue(Target, Current); + } + + if (NodeHasNext(LastNode)) + { + Current.Next = Enqueue(Current.EndPosition); + } + } + + //If we have on the graph two blocks with the same end position, + //then we need to split the bigger block and have two small blocks, + //the end position of the bigger "Current" block should then be == to + //the position of the "Smaller" block. + while (VisitedEnd.TryGetValue(Current.EndPosition, out ShaderIrBlock Smaller)) + { + if (Current.Position > Smaller.Position) + { + ShaderIrBlock Temp = Smaller; + + Smaller = Current; + Current = Temp; + } + + Current.EndPosition = Smaller.Position; + Current.Next = Smaller; + Current.Branch = null; + + Current.Nodes.RemoveRange( + Current.Nodes.Count - Smaller.Nodes.Count, + Smaller.Nodes.Count); + + VisitedEnd[Smaller.EndPosition] = Smaller; + } + + VisitedEnd.Add(Current.EndPosition, Current); + } + + //Make and sort Graph blocks array by position. + ShaderIrBlock[] Graph = new ShaderIrBlock[Visited.Count]; + + while (Visited.Count > 0) + { + ulong FirstPos = ulong.MaxValue; + + foreach (ShaderIrBlock Block in Visited.Values) + { + if (FirstPos > (ulong)Block.Position) + FirstPos = (ulong)Block.Position; + } + + ShaderIrBlock Current = Visited[(long)FirstPos]; + + do + { + Graph[Graph.Length - Visited.Count] = Current; + + Visited.Remove(Current.Position); + + Current = Current.Next; + } + while (Current != null); + } + + return Graph; + } + + private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block) + { + long Position = Block.Position; + + do + { //Ignore scheduling instructions, which are written every 32 bytes. if ((Position & 0x1f) == 0) { @@ -33,9 +147,20 @@ namespace Ryujinx.Graphics.Gal.Shader if (AddDbgComments) { - string DbgOpCode = $"0x{Position:x16}: 0x{OpCode:x16} "; + string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} "; - Block.AddNode(new ShaderIrCmnt(DbgOpCode + (Decode?.Method.Name ?? "???"))); + DbgOpCode += (Decode?.Method.Name ?? "???"); + + if (Decode == ShaderDecode.Bra) + { + int Offset = ((int)(OpCode >> 20) << 8) >> 8; + + long Target = Position + Offset; + + DbgOpCode += " (0x" + Target.ToString("x16") + ")"; + } + + Block.AddNode(new ShaderIrCmnt(DbgOpCode)); } if (Decode == null) @@ -44,19 +169,36 @@ namespace Ryujinx.Graphics.Gal.Shader } Decode(Block, OpCode); - - if (Block.GetLastNode() is ShaderIrOp Op && Op.Inst == ShaderIrInst.Exit) - { - break; - } } + while (!IsFlowChange(Block.GetLastNode())); - return Block; + Block.EndPosition = Position; } - private static bool IsFlowChange(ShaderIrInst Inst) + private static bool IsFlowChange(ShaderIrNode Node) { - return Inst == ShaderIrInst.Exit; + return !NodeHasNext(GetInnermostOp(Node)); + } + + private static ShaderIrOp GetInnermostOp(ShaderIrNode Node) + { + if (Node is ShaderIrCond Cond) + { + Node = Cond.Child; + } + + return Node is ShaderIrOp Op ? Op : null; + } + + private static bool NodeHasNext(ShaderIrNode Node) + { + if (!(Node is ShaderIrOp Op)) + { + return true; + } + + return Op.Inst != ShaderIrInst.Exit && + Op.Inst != ShaderIrInst.Bra; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index 31d721696..50e563b84 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -4,17 +4,23 @@ namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrBlock { - private List Nodes; + public long Position { get; set; } + public long EndPosition { get; set; } - private Dictionary LabelsToInsert; + public ShaderIrBlock Next { get; set; } + public ShaderIrBlock Branch { get; set; } - public long Position; + public List Sources { get; private set; } - public ShaderIrBlock() + public List Nodes { get; private set; } + + public ShaderIrBlock(long Position) { - Nodes = new List(); + this.Position = Position; - LabelsToInsert = new Dictionary(); + Sources = new List(); + + Nodes = new List(); } public void AddNode(ShaderIrNode Node) @@ -22,28 +28,6 @@ namespace Ryujinx.Graphics.Gal.Shader Nodes.Add(Node); } - public ShaderIrLabel GetLabel(long Position) - { - if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label)) - { - return Label; - } - - Label = new ShaderIrLabel(); - - LabelsToInsert.Add(Position, Label); - - return Label; - } - - public void MarkLabel(long Position) - { - if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label)) - { - Nodes.Add(Label); - } - } - public ShaderIrNode[] GetNodes() { return Nodes.ToArray(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index af2ccc3b3..2de50a4a7 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -65,6 +65,8 @@ namespace Ryujinx.Graphics.Gal.Shader Cne, Lsl, Lsr, + Max, + Min, Mul, Neg, Not, diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs deleted file mode 100644 index f5b3585af..000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrLabel : ShaderIrNode { } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index acfcc147c..51197fd48 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -63,6 +63,9 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110011100x", ShaderDecode.I2i_C); Set("0011100x11100x", ShaderDecode.I2i_I); Set("0101110011100x", ShaderDecode.I2i_R); + Set("0100110000100x", ShaderDecode.Imnmx_C); + Set("0011100x00100x", ShaderDecode.Imnmx_I); + Set("0101110000100x", ShaderDecode.Imnmx_R); Set("11100000xxxxxx", ShaderDecode.Ipa); Set("0100110000011x", ShaderDecode.Iscadd_C); Set("0011100x00011x", ShaderDecode.Iscadd_I); @@ -89,6 +92,10 @@ namespace Ryujinx.Graphics.Gal.Shader Set("1101111101001x", ShaderDecode.Texq); Set("1101100xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); + Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); + Set("0011011x00xxxx", ShaderDecode.Xmad_I); + Set("010100010xxxxx", ShaderDecode.Xmad_RC); + Set("0101101100xxxx", ShaderDecode.Xmad_RR); #endregion }