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 }