From 53a6922f872b9380d6c2867254a2465dc7c1ea92 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 2 Jun 2018 00:50:56 -0300
Subject: [PATCH] Some small gpu improvements and shader improvements, add
 support for ASTC 4x4 textures (slow!)

---
 Ryujinx.Core/Gpu/NvGpuEngine3d.cs             |  70 ++--------
 Ryujinx.Core/Gpu/TextureReader.cs             |   1 +
 Ryujinx.Graphics/Gal/GalTextureFormat.cs      |  16 ++-
 .../Gal/OpenGL/OGLEnumConverter.cs            |  20 ++-
 Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs      |   2 +-
 Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs     |  63 +++++++++
 Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs |   1 +
 .../Gal/Shader/ShaderDecodeAlu.cs             | 126 +++++++++++++++---
 .../Gal/Shader/ShaderDecodeHelper.cs          |  28 +++-
 .../Gal/Shader/ShaderDecodeMem.cs             | 100 +++++++++++++-
 .../Gal/Shader/ShaderOpCodeTable.cs           |   5 +
 11 files changed, 332 insertions(+), 100 deletions(-)

diff --git a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs
index 5639dd02f1..b827debe23 100644
--- a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs
+++ b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs
@@ -182,6 +182,13 @@ namespace Ryujinx.Core.Gpu
 
             Gpu.Renderer.SetBlendEnable(Enable);
 
+            if (!Enable)
+            {
+                //If blend is not enabled, then the other values have no effect.
+                //Note that if it is disabled, the register may contain invalid values.
+                return;
+            }
+
             bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0;
 
             GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb);
@@ -362,6 +369,7 @@ namespace Ryujinx.Core.Gpu
                 bool Enable = (Control & 0x1000) != 0;
 
                 long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
+                long VertexEndPos   = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
 
                 if (!Enable)
                 {
@@ -374,11 +382,7 @@ namespace Ryujinx.Core.Gpu
 
                 if (IndexCount != 0)
                 {
-                    Size = GetVertexCountFromIndexBuffer(
-                        Vmm,
-                        IndexPosition,
-                        IndexCount,
-                        IndexSize);
+                    Size = (VertexEndPos - VertexPosition) + 1;
                 }
                 else
                 {
@@ -410,62 +414,6 @@ namespace Ryujinx.Core.Gpu
             }
         }
 
-        private int GetVertexCountFromIndexBuffer(
-            NvGpuVmm Vmm,
-            long     IndexPosition,
-            int      IndexCount,
-            int      IndexSize)
-        {
-            int MaxIndex = -1;
-
-            if (IndexSize == 2)
-            {
-                while (IndexCount -- > 0)
-                {
-                    ushort Value = Vmm.ReadUInt16(IndexPosition);
-
-                    IndexPosition += 2;
-
-                    if (MaxIndex < Value)
-                    {
-                        MaxIndex = Value;
-                    }
-                }
-            }
-            else if (IndexSize == 1)
-            {
-                while (IndexCount -- > 0)
-                {
-                    byte Value = Vmm.ReadByte(IndexPosition++);
-
-                    if (MaxIndex < Value)
-                    {
-                        MaxIndex = Value;
-                    }
-                }
-            }
-            else if (IndexSize == 4)
-            {
-                while (IndexCount -- > 0)
-                {
-                    uint Value = Vmm.ReadUInt32(IndexPosition);
-
-                    IndexPosition += 2;
-
-                    if (MaxIndex < Value)
-                    {
-                        MaxIndex = (int)Value;
-                    }
-                }
-            }
-            else
-            {
-                throw new ArgumentOutOfRangeException(nameof(IndexSize));
-            }
-
-            return MaxIndex + 1;
-        }
-
         private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
         {
             long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress);
diff --git a/Ryujinx.Core/Gpu/TextureReader.cs b/Ryujinx.Core/Gpu/TextureReader.cs
index 0c1c83d5d3..ae3b00007f 100644
--- a/Ryujinx.Core/Gpu/TextureReader.cs
+++ b/Ryujinx.Core/Gpu/TextureReader.cs
@@ -23,6 +23,7 @@ namespace Ryujinx.Core.Gpu
                 case GalTextureFormat.BC3:          return Read16Bpt4x4(Memory, Texture);
                 case GalTextureFormat.BC4:          return Read8Bpt4x4 (Memory, Texture);
                 case GalTextureFormat.BC5:          return Read16Bpt4x4(Memory, Texture);
+                case GalTextureFormat.Astc2D4x4:    return Read16Bpt4x4(Memory, Texture);
             }
 
             throw new NotImplementedException(Texture.Format.ToString());
diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
index b3d8b03d9e..d61495eb70 100644
--- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs
+++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
@@ -14,6 +14,20 @@ namespace Ryujinx.Graphics.Gal
         BC2          = 0x25,
         BC3          = 0x26,
         BC4          = 0x27,
-        BC5          = 0x28
+        BC5          = 0x28,
+        Astc2D4x4    = 0x40,
+        Astc2D5x5    = 0x41,
+        Astc2D6x6    = 0x42,
+        Astc2D8x8    = 0x44,
+        Astc2D10x10  = 0x45,
+        Astc2D12x12  = 0x46,
+        Astc2D5x4    = 0x50,
+        Astc2D6x5    = 0x51,
+        Astc2D8x6    = 0x52,
+        Astc2D10x8   = 0x53,
+        Astc2D12x10  = 0x54,
+        Astc2D8x5    = 0x55,
+        Astc2D10x5   = 0x56,
+        Astc2D10x6   = 0x57
     }
 }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index ee697097f0..d266a87a8e 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -59,14 +59,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             switch (Format)
             {
-                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);
+                case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
+                case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
+                case GalTextureFormat.B5G6R5:   return (PixelFormat.Rgb,  PixelType.UnsignedShort565);
             }
 
             throw new NotImplementedException(Format.ToString());
@@ -150,20 +145,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;
@@ -184,13 +179,14 @@ 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;
@@ -211,6 +207,8 @@ 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/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
index a8941d690e..be1bb20b45 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type)
         {
-            GlslProgram Program = GetGlslProgram(Memory, Position,  Type);
+            GlslProgram Program = GetGlslProgram(Memory, Position, Type);
 
             return new ShaderStage(
                 Type,
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index 8dcfb2bdb5..75f09faac9 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -1,4 +1,6 @@
 using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.Gal.Texture;
+using System;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
@@ -36,6 +38,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
             else
             {
+                if (Texture.Format >= GalTextureFormat.Astc2D4x4)
+                {
+                    ConvertAstcTextureToRgba(Texture);
+                }
+
                 const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
 
                 (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
@@ -63,6 +70,62 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
         }
 
+        private void ConvertAstcTextureToRgba(GalTexture Texture)
+        {
+            Texture.Data = ASTCDecoder.DecodeToRGBA8888(
+                Texture.Data,
+                GetAstcBlockWidth(Texture.Format),
+                GetAstcBlockHeight(Texture.Format), 1,
+                Texture.Width,
+                Texture.Height, 1);
+        }
+
+        private int GetAstcBlockWidth(GalTextureFormat Format)
+        {
+            switch (Format)
+            {
+                case GalTextureFormat.Astc2D4x4:   return 4;
+                case GalTextureFormat.Astc2D5x5:   return 5;
+                case GalTextureFormat.Astc2D6x6:   return 6;
+                case GalTextureFormat.Astc2D8x8:   return 8;
+                case GalTextureFormat.Astc2D10x10: return 10;
+                case GalTextureFormat.Astc2D12x12: return 12;
+                case GalTextureFormat.Astc2D5x4:   return 5;
+                case GalTextureFormat.Astc2D6x5:   return 6;
+                case GalTextureFormat.Astc2D8x6:   return 8;
+                case GalTextureFormat.Astc2D10x8:  return 10;
+                case GalTextureFormat.Astc2D12x10: return 12;
+                case GalTextureFormat.Astc2D8x5:   return 8;
+                case GalTextureFormat.Astc2D10x5:  return 10;
+                case GalTextureFormat.Astc2D10x6:  return 10;
+            }
+
+            throw new ArgumentException(nameof(Format));
+        }
+
+        private int GetAstcBlockHeight(GalTextureFormat Format)
+        {
+            switch (Format)
+            {
+                case GalTextureFormat.Astc2D4x4:   return 4;
+                case GalTextureFormat.Astc2D5x5:   return 5;
+                case GalTextureFormat.Astc2D6x6:   return 6;
+                case GalTextureFormat.Astc2D8x8:   return 8;
+                case GalTextureFormat.Astc2D10x10: return 10;
+                case GalTextureFormat.Astc2D12x12: return 12;
+                case GalTextureFormat.Astc2D5x4:   return 4;
+                case GalTextureFormat.Astc2D6x5:   return 5;
+                case GalTextureFormat.Astc2D8x6:   return 6;
+                case GalTextureFormat.Astc2D10x8:  return 8;
+                case GalTextureFormat.Astc2D12x10: return 10;
+                case GalTextureFormat.Astc2D8x5:   return 5;
+                case GalTextureFormat.Astc2D10x5:  return 5;
+                case GalTextureFormat.Astc2D10x6:  return 6;
+            }
+
+            throw new ArgumentException(nameof(Format));
+        }
+
         public void Bind(int Index)
         {
             int Handle = EnsureTextureInitialized(Index);
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 5c4537fe05..71a53a5a3a 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -389,6 +389,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                             SB.AppendLine(Identation + "gl_Position.xy *= flip;");
 
                             SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;");
+                            SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;");
                         }
                     }
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
index ddd3e3e8c5..2e3fb6dd29 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -243,6 +243,75 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
+        public static void Psetp(ShaderIrBlock Block, long OpCode)
+        {
+            bool NegA = ((OpCode >> 15) & 1) != 0;
+            bool NegB = ((OpCode >> 32) & 1) != 0;
+            bool NegP = ((OpCode >> 42) & 1) != 0;
+
+            ShaderIrInst LopInst = GetBLop24(OpCode);
+
+            ShaderIrNode OperA = GetOperPred12(OpCode);
+            ShaderIrNode OperB = GetOperPred29(OpCode);
+
+            if (NegA)
+            {
+                OperA = new ShaderIrOp(ShaderIrInst.Bnot, OperA);
+            }
+
+            if (NegB)
+            {
+                OperB = new ShaderIrOp(ShaderIrInst.Bnot, OperB);
+            }
+
+            ShaderIrOp Op = new ShaderIrOp(LopInst, OperA, OperB);
+
+            ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
+            ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
+            ShaderIrOperPred P2Node = GetOperPred39(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+
+            LopInst = GetBLop45(OpCode);
+
+            if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
+            {
+                return;
+            }
+
+            ShaderIrNode P2NNode = P2Node;
+
+            if (NegP)
+            {
+                P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
+            }
+
+            Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);
+
+            Op = new ShaderIrOp(LopInst, Op, P2NNode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
+
+            Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+        }
+
+        public static void Rro_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitRro(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Rro_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitRro(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void Rro_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitRro(Block, OpCode, ShaderOper.RR);
+        }
+
         public static void Shl_C(ShaderIrBlock Block, long OpCode)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl);
@@ -445,6 +514,33 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
+        private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegA = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            ShaderIrOperImm Scale = GetOperImm5_39(OpCode);
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
+                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
+                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperA = GetAluIneg(OperA, NegA);
+            OperB = GetAluIneg(OperB, NegB);
+
+            ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale);
+            ShaderIrOp AddOp   = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+        }
+
         private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
             EmitMnmx(Block, OpCode, true, Oper);
@@ -524,31 +620,27 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        public static void EmitRro(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 48) & 1) != 0;
-            bool NegA = ((OpCode >> 49) & 1) != 0;
+            //Note: this is a range reduction instruction and is supposed to
+            //be used with Mufu, here it just moves the value and ignores the operation.
+            bool NegA = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 49) & 1) != 0;
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
-
-            ShaderIrOperImm Scale = GetOperImm5_39(OpCode);
+            ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperA = GetAluIneg(OperA, NegA);
-            OperB = GetAluIneg(OperB, NegB);
+            OperA = GetAluFabsFneg(OperA, AbsA, NegA);
 
-            ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale);
-            ShaderIrOp AddOp   = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp);
-
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
         }
 
         private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -597,7 +689,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
 
-            ShaderIrInst LopInst = GetBLop(OpCode);
+            ShaderIrInst LopInst = GetBLop45(OpCode);
 
             ShaderIrOperPred PNode = GetOperPred39(OpCode);
 
@@ -685,7 +777,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
 
-            ShaderIrInst LopInst = GetBLop(OpCode);
+            ShaderIrInst LopInst = GetBLop45(OpCode);
 
             if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
             {
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
index 73775ccaf7..3299ebab6b 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
@@ -119,14 +119,24 @@ namespace Ryujinx.Graphics.Gal.Shader
             return new ShaderIrOperImmf(Value);
         }
 
+        public static ShaderIrOperPred GetOperPred0(long OpCode)
+        {
+            return new ShaderIrOperPred((int)(OpCode >> 0) & 7);
+        }
+
         public static ShaderIrOperPred GetOperPred3(long OpCode)
         {
             return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
         }
 
-        public static ShaderIrOperPred GetOperPred0(long OpCode)
+        public static ShaderIrOperPred GetOperPred12(long OpCode)
         {
-            return new ShaderIrOperPred((int)(OpCode >> 0) & 7);
+            return new ShaderIrOperPred((int)(OpCode >> 12) & 7);
+        }
+
+        public static ShaderIrOperPred GetOperPred29(long OpCode)
+        {
+            return new ShaderIrOperPred((int)(OpCode >> 29) & 7);
         }
 
         public static ShaderIrNode GetOperPred39N(long OpCode)
@@ -184,7 +194,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             throw new ArgumentException(nameof(OpCode));
         }
 
-        public static ShaderIrInst GetBLop(long OpCode)
+        public static ShaderIrInst GetBLop45(long OpCode)
         {
             switch ((int)(OpCode >> 45) & 3)
             {
@@ -196,6 +206,18 @@ namespace Ryujinx.Graphics.Gal.Shader
             throw new ArgumentException(nameof(OpCode));
         }
 
+        public static ShaderIrInst GetBLop24(long OpCode)
+        {
+            switch ((int)(OpCode >> 24) & 3)
+            {
+                case 0: return ShaderIrInst.Band;
+                case 1: return ShaderIrInst.Bor;
+                case 2: return ShaderIrInst.Bxor;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
         public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode)
         {
             ShaderIrOperPred Pred = GetPredNode(OpCode);
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
index 5ca485a3bb..ec03622d93 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
+        private const int TempRegStart = 0x100;
+
         public static void Ld_A(ShaderIrBlock Block, long OpCode)
         {
             ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
@@ -92,6 +94,62 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
         }
 
+        public static void Tex(ShaderIrBlock Block, long OpCode)
+        {
+            //TODO: Support other formats.
+            ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2];
+
+            for (int Index = 0; Index < Coords.Length; Index++)
+            {
+                Coords[Index] = GetOperGpr8(OpCode);
+
+                Coords[Index].Index += Index;
+
+                if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex)
+                {
+                    Coords[Index].Index = ShaderIrOperGpr.ZRIndex;
+                }
+            }
+
+            int ChMask = (int)(OpCode >> 31) & 0xf;
+
+            ShaderIrNode OperC = GetOperImm13_36(OpCode);
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
+
+                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
+
+                ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texs, Coords[0], Coords[1], OperC, Meta);
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
+            }
+
+            int RegInc = 0;
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                if (!IsChannelUsed(ChMask, Ch))
+                {
+                    continue;
+                }
+
+                ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
+
+                ShaderIrOperGpr Dst = GetOperGpr0(OpCode);
+
+                Dst.Index += RegInc++;
+
+                if (Dst.Index >= ShaderIrOperGpr.ZRIndex)
+                {
+                    continue;
+                }
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+            }
+        }
+
         public static void Texs(ShaderIrBlock Block, long OpCode)
         {
             EmitTex(Block, OpCode, ShaderIrInst.Texs);
@@ -109,11 +167,24 @@ namespace Ryujinx.Graphics.Gal.Shader
             ShaderIrNode OperB = GetOperGpr20   (OpCode);
             ShaderIrNode OperC = GetOperImm13_36(OpCode);
 
+            bool TwoDests = GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex;
+
+            int ChMask;
+
+            switch ((OpCode >> 50) & 7)
+            {
+                case 0: ChMask = TwoDests ? 0x7 : 0x1; break;
+                case 1: ChMask = TwoDests ? 0xb : 0x2; break;
+                case 2: ChMask = TwoDests ? 0xd : 0x4; break;
+                case 3: ChMask = TwoDests ? 0xe : 0x8; break;
+                case 4: ChMask = TwoDests ? 0xf : 0x3; break;
+
+                default: throw new InvalidOperationException();
+            }
+
             for (int Ch = 0; Ch < 4; Ch++)
             {
-                //Assign it to a temp because the destination registers
-                //may be used as texture coord input aswell.
-                ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
+                ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
 
                 ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
 
@@ -122,15 +193,22 @@ namespace Ryujinx.Graphics.Gal.Shader
                 Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
             }
 
+            int RegInc = 0;
+
             for (int Ch = 0; Ch < 4; Ch++)
             {
-                ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
+                if (!IsChannelUsed(ChMask, Ch))
+                {
+                    continue;
+                }
 
-                ShaderIrOperGpr Dst = (Ch >> 1) != 0
+                ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
+
+                ShaderIrOperGpr Dst = (RegInc >> 1) != 0
                     ? GetOperGpr28(OpCode)
                     : GetOperGpr0 (OpCode);
 
-                Dst.Index += Ch & 1;
+                Dst.Index += RegInc++ & 1;
 
                 if (Dst.Index >= ShaderIrOperGpr.ZRIndex)
                 {
@@ -138,7 +216,17 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
 
                 Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+
+                /*if (IsScalar)
+                {
+                    break;
+                }*/
             }
         }
+
+        private static bool IsChannelUsed(int ChMask, int Ch)
+        {
+            return (ChMask & (1 << Ch)) != 0;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
index 51197fd482..074cfbb28b 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -82,6 +82,10 @@ namespace Ryujinx.Graphics.Gal.Shader
             Set("000000010000xx", ShaderDecode.Mov_I32);
             Set("0101110010011x", ShaderDecode.Mov_R);
             Set("0101000010000x", ShaderDecode.Mufu);
+            Set("0101000010010x", ShaderDecode.Psetp);
+            Set("0100110010010x", ShaderDecode.Rro_C);
+            Set("0011100x10010x", ShaderDecode.Rro_I);
+            Set("0101110010010x", ShaderDecode.Rro_R);
             Set("0100110001001x", ShaderDecode.Shl_C);
             Set("0011100x01001x", ShaderDecode.Shl_I);
             Set("0101110001001x", ShaderDecode.Shl_R);
@@ -89,6 +93,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             Set("0011100x00101x", ShaderDecode.Shr_I);
             Set("0101110000101x", ShaderDecode.Shr_R);
             Set("1110111111110x", ShaderDecode.St_A);
+            Set("110000xxxx111x", ShaderDecode.Tex);
             Set("1101111101001x", ShaderDecode.Texq);
             Set("1101100xxxxxxx", ShaderDecode.Texs);
             Set("1101101xxxxxxx", ShaderDecode.Tlds);