diff --git a/Ryujinx.Common/Utilities/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs
index 135b397d3d..b6fba4fba1 100644
--- a/Ryujinx.Common/Utilities/BitUtils.cs
+++ b/Ryujinx.Common/Utilities/BitUtils.cs
@@ -34,6 +34,11 @@ namespace Ryujinx.Common
             return value & -(long)size;
         }
 
+        public static int DivRoundUp(int value, int dividend)
+        {
+            return (value + dividend - 1) / dividend;
+        }
+
         public static ulong DivRoundUp(ulong value, uint dividend)
         {
             return (value + dividend - 1) / dividend;
@@ -44,6 +49,24 @@ namespace Ryujinx.Common
             return (value + dividend - 1) / dividend;
         }
 
+        public static int Pow2RoundUp(int value)
+        {
+            value--;
+
+            value |= (value >>  1);
+            value |= (value >>  2);
+            value |= (value >>  4);
+            value |= (value >>  8);
+            value |= (value >> 16);
+
+            return ++value;
+        }
+
+        public static int Pow2RoundDown(int value)
+        {
+            return IsPowerOfTwo32(value) ? value : Pow2RoundUp(value) >> 1;
+        }
+
         public static bool IsPowerOfTwo32(int value)
         {
             return value != 0 && (value & (value - 1)) == 0;
@@ -85,6 +108,18 @@ namespace Ryujinx.Common
             return (ulong)count;
         }
 
+        public static int CountTrailingZeros32(int value)
+        {
+            int count = 0;
+
+            while (((value >> count) & 1) == 0)
+            {
+                count++;
+            }
+
+            return count;
+        }
+
         public static long ReverseBits64(long value)
         {
             return (long)ReverseBits64((ulong)value);
diff --git a/Ryujinx.Graphics/DepthCompareFunc.cs b/Ryujinx.Graphics/DepthCompareFunc.cs
new file mode 100644
index 0000000000..24c8854a4b
--- /dev/null
+++ b/Ryujinx.Graphics/DepthCompareFunc.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics
+{
+    public enum DepthCompareFunc
+    {
+        Never    = 0,
+        Less     = 1,
+        Equal    = 2,
+        LEqual   = 3,
+        Greater  = 4,
+        NotEqual = 5,
+        GEqual   = 6,
+        Always   = 7
+    }
+}
diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs
index 92f43cc9d4..fb904b0925 100644
--- a/Ryujinx.Graphics/Gal/GalImage.cs
+++ b/Ryujinx.Graphics/Gal/GalImage.cs
@@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal
     {
         public int Width;
         public int Height;
+
+        // FIXME: separate layer and depth
+        public int Depth;
+        public int LayerCount;
         public int TileWidth;
         public int GobBlockHeight;
+        public int GobBlockDepth;
         public int Pitch;
+        public int MaxMipmapLevel;
 
         public GalImageFormat   Format;
         public GalMemoryLayout  Layout;
@@ -16,34 +22,45 @@ namespace Ryujinx.Graphics.Gal
         public GalTextureSource YSource;
         public GalTextureSource ZSource;
         public GalTextureSource WSource;
+        public GalTextureTarget TextureTarget;
 
         public GalImage(
             int              Width,
             int              Height,
+            int              Depth,
+            int              LayerCount,
             int              TileWidth,
             int              GobBlockHeight,
+            int              GobBlockDepth,
             GalMemoryLayout  Layout,
             GalImageFormat   Format,
-            GalTextureSource XSource = GalTextureSource.Red,
-            GalTextureSource YSource = GalTextureSource.Green,
-            GalTextureSource ZSource = GalTextureSource.Blue,
-            GalTextureSource WSource = GalTextureSource.Alpha)
+            GalTextureTarget TextureTarget,
+            int              MaxMipmapLevel = 1,
+            GalTextureSource XSource        = GalTextureSource.Red,
+            GalTextureSource YSource        = GalTextureSource.Green,
+            GalTextureSource ZSource        = GalTextureSource.Blue,
+            GalTextureSource WSource        = GalTextureSource.Alpha)
         {
             this.Width          = Width;
             this.Height         = Height;
+            this.LayerCount     = LayerCount;
+            this.Depth          = Depth;
             this.TileWidth      = TileWidth;
             this.GobBlockHeight = GobBlockHeight;
+            this.GobBlockDepth  = GobBlockDepth;
             this.Layout         = Layout;
             this.Format         = Format;
+            this.MaxMipmapLevel = MaxMipmapLevel;
             this.XSource        = XSource;
             this.YSource        = YSource;
             this.ZSource        = ZSource;
             this.WSource        = WSource;
+            this.TextureTarget  = TextureTarget;
 
             Pitch = ImageUtils.GetPitch(Format, Width);
         }
 
-        public bool SizeMatches(GalImage Image)
+        public bool SizeMatches(GalImage Image, bool IgnoreLayer = false)
         {
             if (ImageUtils.GetBytesPerPixel(Format) !=
                 ImageUtils.GetBytesPerPixel(Image.Format))
@@ -57,7 +74,14 @@ namespace Ryujinx.Graphics.Gal
                 return false;
             }
 
-            return Height == Image.Height;
+            bool Result = Height == Image.Height && Depth == Image.Depth;
+
+            if (!IgnoreLayer)
+            {
+                Result = Result && LayerCount == Image.LayerCount;
+            }
+
+            return Result;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs
index b9e5c7658d..1d658cea85 100644
--- a/Ryujinx.Graphics/Gal/GalTextureSampler.cs
+++ b/Ryujinx.Graphics/Gal/GalTextureSampler.cs
@@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Gal
 
         public GalColorF BorderColor { get; private set; }
 
+        public bool             DepthCompare     { get; private set; }
+        public DepthCompareFunc DepthCompareFunc { get; private set; }
+
         public GalTextureSampler(
             GalTextureWrap      AddressU,
             GalTextureWrap      AddressV,
@@ -19,7 +22,9 @@ namespace Ryujinx.Graphics.Gal
             GalTextureFilter    MinFilter,
             GalTextureFilter    MagFilter,
             GalTextureMipFilter MipFilter,
-            GalColorF           BorderColor)
+            GalColorF           BorderColor,
+            bool                DepthCompare,
+            DepthCompareFunc    DepthCompareFunc)
         {
             this.AddressU    = AddressU;
             this.AddressV    = AddressV;
@@ -28,6 +33,9 @@ namespace Ryujinx.Graphics.Gal
             this.MagFilter   = MagFilter;
             this.MipFilter   = MipFilter;
             this.BorderColor = BorderColor;
+
+            this.DepthCompare     = DepthCompare;
+            this.DepthCompareFunc = DepthCompareFunc;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureTarget.cs b/Ryujinx.Graphics/Gal/GalTextureTarget.cs
new file mode 100644
index 0000000000..bcc0c49a51
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureTarget.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureTarget
+    {
+        OneD         = 0,
+        TwoD         = 1,
+        ThreeD       = 2,
+        CubeMap      = 3,
+        OneDArray    = 4,
+        TwoDArray    = 5,
+        OneDBuffer   = 6,
+        TwoDNoMipMap = 7,
+        CubeArray    = 8,
+    }
+}
diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs
index f941ccd584..90cad856d9 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs
@@ -25,16 +25,20 @@ namespace Ryujinx.Graphics.Gal
         void Render();
 
         void Copy(
-            long SrcKey,
-            long DstKey,
-            int  SrcX0,
-            int  SrcY0,
-            int  SrcX1,
-            int  SrcY1,
-            int  DstX0,
-            int  DstY0,
-            int  DstX1,
-            int  DstY1);
+            GalImage SrcImage,
+            GalImage DstImage,
+            long     SrcKey,
+            long     DstKey,
+            int      SrcLayer,
+            int      DstLayer,
+            int      SrcX0,
+            int      SrcY0,
+            int      SrcX1,
+            int      SrcY1,
+            int      DstX0,
+            int      DstY0,
+            int      DstX1,
+            int      DstY1);
 
         void Reinterpret(long Key, GalImage NewImage);
     }
diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs
index aeecdf1ac5..de4ba9cba7 100644
--- a/Ryujinx.Graphics/Gal/IGalTexture.cs
+++ b/Ryujinx.Graphics/Gal/IGalTexture.cs
@@ -13,6 +13,6 @@ namespace Ryujinx.Graphics.Gal
 
         void Bind(long Key, int Index, GalImage Image);
 
-        void SetSampler(GalTextureSampler Sampler);
+        void SetSampler(GalImage Image, GalTextureSampler Sampler);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
index 8db0b8a8c9..5714f3d891 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public int Width  => Image.Width;
         public int Height => Image.Height;
+        public int Depth  => Image.Depth;
 
         public GalImageFormat Format => Image.Format;
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index f2afe7b556..3a25fff7a5 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -189,6 +189,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
         }
 
+        public static All GetDepthCompareFunc(DepthCompareFunc DepthCompareFunc)
+        {
+            switch (DepthCompareFunc)
+            {
+                case DepthCompareFunc.LEqual:
+                    return All.Lequal;
+                case DepthCompareFunc.GEqual:
+                    return All.Gequal;
+                case DepthCompareFunc.Less:
+                    return All.Less;
+                case DepthCompareFunc.Greater:
+                    return All.Greater;
+                case DepthCompareFunc.Equal:
+                    return All.Equal;
+                case DepthCompareFunc.NotEqual:
+                    return All.Notequal;
+                case DepthCompareFunc.Always:
+                    return All.Always;
+                case DepthCompareFunc.Never:
+                    return All.Never;
+                default:
+                    throw new ArgumentException(nameof(DepthCompareFunc) + " \"" + DepthCompareFunc + "\" is not valid!");
+            }
+        }
+
         public static InternalFormat GetCompressedImageFormat(GalImageFormat Format)
         {
             switch (Format)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs
index 11daeb593c..52b3d0ce31 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs
@@ -9,9 +9,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
         private static Lazy<bool> s_ViewportArray      = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
 
+        private static Lazy<bool> s_NvidiaDriver      = new Lazy<bool>(() => IsNvidiaDriver());
+
         public static bool EnhancedLayouts    => s_EnhancedLayouts.Value;
         public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
         public static bool ViewportArray      => s_ViewportArray.Value;
+        public static bool NvidiaDrvier       => s_NvidiaDriver.Value;
 
         private static bool HasExtension(string Name)
         {
@@ -27,5 +30,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             return false;
         }
+
+        private static bool IsNvidiaDriver() {
+            return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
index 0d7bb3cd0a..8dd3b37fc2 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
@@ -389,16 +389,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         }
 
         public void Copy(
-            long SrcKey,
-            long DstKey,
-            int  SrcX0,
-            int  SrcY0,
-            int  SrcX1,
-            int  SrcY1,
-            int  DstX0,
-            int  DstY0,
-            int  DstX1,
-            int  DstY1)
+            GalImage SrcImage,
+            GalImage DstImage,
+            long     SrcKey,
+            long     DstKey,
+            int      SrcLayer,
+            int      DstLayer,
+            int      SrcX0,
+            int      SrcY0,
+            int      SrcX1,
+            int      SrcY1,
+            int      DstX0,
+            int      DstY0,
+            int      DstX1,
+            int      DstY1)
         {
             if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
                 Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
@@ -425,8 +429,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                 FramebufferAttachment Attachment = GetAttachment(SrcTex);
 
-                GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
-                GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
+                if (ImageUtils.IsArray(SrcImage.TextureTarget) && SrcLayer > 0)
+                {
+                    GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0, SrcLayer);
+                }
+                else
+                {
+                    GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
+                }
+
+                if (ImageUtils.IsArray(DstImage.TextureTarget) && DstLayer > 0)
+                {
+                    GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0, DstLayer);
+                }
+                else
+                {
+                    GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
+                }
+
 
                 BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
 
@@ -452,7 +472,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             if (NewImage.Format == OldImage.Format &&
                 NewImage.Width  == OldImage.Width  &&
-                NewImage.Height == OldImage.Height)
+                NewImage.Height == OldImage.Height &&
+                NewImage.Depth == OldImage.Depth &&
+                NewImage.LayerCount == OldImage.LayerCount &&
+                NewImage.TextureTarget == OldImage.TextureTarget)
             {
                 return;
             }
@@ -477,9 +500,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
 
-            GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
+            TextureTarget Target = ImageUtils.GetTextureTarget(NewImage.TextureTarget);
 
-            GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero);
+            GL.BindTexture(Target, CachedImage.Handle);
+
+            GL.GetTexImage(Target, 0, Format, Type, IntPtr.Zero);
 
             GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
             GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
index 10a9120df2..dc168ff919 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             GlslProgram Program;
 
-            GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize);
+            GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize, OGLExtension.NvidiaDrvier);
 
             int ShaderDumpIndex = ShaderDumper.DumpIndex;
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index ef984b1ed3..4fef11d296 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -38,7 +38,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             int Handle = GL.GenTexture();
 
-            GL.BindTexture(TextureTarget.Texture2D, Handle);
+            TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
+
+            GL.BindTexture(Target, Handle);
 
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
@@ -54,23 +56,70 @@ namespace Ryujinx.Graphics.Gal.OpenGL
              PixelFormat         Format,
              PixelType           Type) = OGLEnumConverter.GetImageFormat(Image.Format);
 
-            GL.TexImage2D(
-                TextureTarget.Texture2D,
-                Level,
-                InternalFmt,
-                Image.Width,
-                Image.Height,
-                Border,
-                Format,
-                Type,
-                IntPtr.Zero);
+            switch (Target)
+            {
+                case TextureTarget.Texture1D:
+                    GL.TexImage1D(
+                        Target,
+                        Level,
+                        InternalFmt,
+                        Image.Width,
+                        Border,
+                        Format,
+                        Type,
+                        IntPtr.Zero);
+                    break;
+
+                case TextureTarget.Texture2D:
+                    GL.TexImage2D(
+                        Target,
+                        Level,
+                        InternalFmt,
+                        Image.Width,
+                        Image.Height,
+                        Border,
+                        Format,
+                        Type,
+                        IntPtr.Zero);
+                    break;
+                case TextureTarget.Texture3D:
+                    GL.TexImage3D(
+                        Target,
+                        Level,
+                        InternalFmt,
+                        Image.Width,
+                        Image.Height,
+                        Image.Depth,
+                        Border,
+                        Format,
+                        Type,
+                        IntPtr.Zero);
+                    break;
+                case TextureTarget.Texture2DArray:
+                    GL.TexImage3D(
+                        Target,
+                        Level,
+                        InternalFmt,
+                        Image.Width,
+                        Image.Height,
+                        Image.LayerCount,
+                        Border,
+                        Format,
+                        Type,
+                        IntPtr.Zero);
+                    break;
+                default:
+                    throw new NotImplementedException($"Unsupported texture target type: {Target}");
+            }
         }
 
         public void Create(long Key, byte[] Data, GalImage Image)
         {
             int Handle = GL.GenTexture();
 
-            GL.BindTexture(TextureTarget.Texture2D, Handle);
+            TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
+
+            GL.BindTexture(Target, Handle);
 
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
@@ -81,15 +130,56 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
 
-                GL.CompressedTexImage2D(
-                    TextureTarget.Texture2D,
-                    Level,
-                    InternalFmt,
-                    Image.Width,
-                    Image.Height,
-                    Border,
-                    Data.Length,
-                    Data);
+                switch (Target)
+                {
+                    case TextureTarget.Texture1D:
+                        GL.CompressedTexImage1D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Border,
+                            Data.Length,
+                            Data);
+                        break;
+                    case TextureTarget.Texture2D:
+                        GL.CompressedTexImage2D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Border,
+                            Data.Length,
+                            Data);
+                        break;
+                    case TextureTarget.Texture3D:
+                        GL.CompressedTexImage3D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Image.Depth,
+                            Border,
+                            Data.Length,
+                            Data);
+                        break;
+                    case TextureTarget.Texture2DArray:
+                        GL.CompressedTexImage3D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Image.LayerCount,
+                            Border,
+                            Data.Length,
+                            Data);
+                        break;
+                    default:
+                        throw new NotImplementedException($"Unsupported texture target type: {Target}");
+                }
             }
             else
             {
@@ -98,13 +188,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 {
                     int TextureBlockWidth  = ImageUtils.GetBlockWidth(Image.Format);
                     int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
+                    int TextureBlockDepth  = ImageUtils.GetBlockDepth(Image.Format);
 
                     Data = ASTCDecoder.DecodeToRGBA8888(
                         Data,
                         TextureBlockWidth,
-                        TextureBlockHeight, 1,
+                        TextureBlockHeight,
+                        TextureBlockDepth,
                         Image.Width,
-                        Image.Height, 1);
+                        Image.Height,
+                        Image.Depth);
 
                     Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask);
                 }
@@ -113,16 +206,80 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                  PixelFormat         Format,
                  PixelType           Type) = OGLEnumConverter.GetImageFormat(Image.Format);
 
-                GL.TexImage2D(
-                    TextureTarget.Texture2D,
-                    Level,
-                    InternalFmt,
-                    Image.Width,
-                    Image.Height,
-                    Border,
-                    Format,
-                    Type,
-                    Data);
+
+                switch (Target)
+                {
+                    case TextureTarget.Texture1D:
+                        GL.TexImage1D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Border,
+                            Format,
+                            Type,
+                            Data);
+                        break;
+                    case TextureTarget.Texture2D:
+                        GL.TexImage2D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Border,
+                            Format,
+                            Type,
+                            Data);
+                        break;
+                    case TextureTarget.Texture3D:
+                        GL.TexImage3D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Image.Depth,
+                            Border,
+                            Format,
+                            Type,
+                            Data);
+                        break;
+                    case TextureTarget.Texture2DArray:
+                        GL.TexImage3D(
+                            Target,
+                            Level,
+                            InternalFmt,
+                            Image.Width,
+                            Image.Height,
+                            Image.LayerCount,
+                            Border,
+                            Format,
+                            Type,
+                            Data);
+                        break;
+                    case TextureTarget.TextureCubeMap:
+                        Span<byte> Array = new Span<byte>(Data);
+
+                        int FaceSize = ImageUtils.GetSize(Image) / 6;
+
+                        for (int Face = 0; Face < 6; Face++)
+                        {
+                            GL.TexImage2D(
+                                TextureTarget.TextureCubeMapPositiveX + Face,
+                                Level,
+                                InternalFmt,
+                                Image.Width,
+                                Image.Height,
+                                Border,
+                                Format,
+                                Type,
+                                Array.Slice(Face * FaceSize, FaceSize).ToArray());
+                        }
+                        break;
+                    default:
+                        throw new NotImplementedException($"Unsupported texture target type: {Target}");
+                }
             }
         }
 
@@ -165,7 +322,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
-                GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
+                TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
+
+                GL.BindTexture(Target, CachedImage.Handle);
 
                 int[] SwizzleRgba = new int[]
                 {
@@ -175,23 +334,27 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
                 };
 
-                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
+                GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
             }
         }
 
-        public void SetSampler(GalTextureSampler Sampler)
+        public void SetSampler(GalImage Image, GalTextureSampler Sampler)
         {
             int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
             int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
+            int WrapR = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressP);
 
             int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
             int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
 
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
+            TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
 
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
+            GL.TexParameter(Target, TextureParameterName.TextureWrapS, WrapS);
+            GL.TexParameter(Target, TextureParameterName.TextureWrapT, WrapT);
+            GL.TexParameter(Target, TextureParameterName.TextureWrapR, WrapR);
+
+            GL.TexParameter(Target, TextureParameterName.TextureMinFilter, MinFilter);
+            GL.TexParameter(Target, TextureParameterName.TextureMagFilter, MagFilter);
 
             float[] Color = new float[]
             {
@@ -201,7 +364,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 Sampler.BorderColor.Alpha
             };
 
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
+            GL.TexParameter(Target, TextureParameterName.TextureBorderColor, Color);
+
+            if (Sampler.DepthCompare)
+            {
+                GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
+                GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)OGLEnumConverter.GetDepthCompareFunc(Sampler.DepthCompareFunc));
+            }
+            else
+            {
+                GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.None);
+                GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)All.Never);
+            }
         }
     }
 }
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
index 43923da742..f7ae34faa5 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Graphics.Gal.OpenGL;
+using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
 
@@ -224,6 +226,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                     if (Op.Inst == ShaderIrInst.Texq ||
                         Op.Inst == ShaderIrInst.Texs ||
+                        Op.Inst == ShaderIrInst.Tld4 ||
                         Op.Inst == ShaderIrInst.Txlf)
                     {
                         int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
@@ -232,7 +235,25 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                         string Name = StagePrefix + TextureName + Index;
 
-                        m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle));
+                        GalTextureTarget TextureTarget;
+                        
+                        TextureInstructionSuffix TextureInstructionSuffix;
+
+                        // TODO: Non 2D texture type for TEXQ?
+                        if (Op.Inst == ShaderIrInst.Texq)
+                        {
+                            TextureTarget            = GalTextureTarget.TwoD;
+                            TextureInstructionSuffix = TextureInstructionSuffix.None;
+                        }
+                        else
+                        {
+                            ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData);
+
+                            TextureTarget            = Meta.TextureTarget;
+                            TextureInstructionSuffix = Meta.TextureInstructionSuffix;
+                        }
+
+                        m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle, false, 0, 1, TextureTarget, TextureInstructionSuffix));
                     }
                     else if (Op.Inst == ShaderIrInst.Texb)
                     {
@@ -257,9 +278,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                         if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf)
                         {
+                            ShaderIrMetaTex Meta = ((ShaderIrMetaTex)Op.MetaData);
                             string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos;
 
-                            m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index));
+                            m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index, 1, Meta.TextureTarget, Meta.TextureInstructionSuffix));
                         }
                         else
                         {
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 854c827ee0..5f809525f9 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -1,3 +1,5 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -33,7 +35,9 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public int MaxUboSize { get; }
 
-        public GlslDecompiler(int MaxUboSize)
+        private bool IsNvidiaDriver;
+
+        public GlslDecompiler(int MaxUboSize, bool IsNvidiaDriver)
         {
             InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
             {
@@ -103,6 +107,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 { ShaderIrInst.Texb,   GetTexbExpr   },
                 { ShaderIrInst.Texq,   GetTexqExpr   },
                 { ShaderIrInst.Texs,   GetTexsExpr   },
+                { ShaderIrInst.Tld4,   GetTld4Expr   },
                 { ShaderIrInst.Trunc,  GetTruncExpr  },
                 { ShaderIrInst.Txlf,   GetTxlfExpr   },
                 { ShaderIrInst.Utof,   GetUtofExpr   },
@@ -110,6 +115,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             };
 
             this.MaxUboSize = MaxUboSize / 16;
+            this.IsNvidiaDriver = IsNvidiaDriver;
         }
 
         public GlslProgram Decompile(
@@ -219,14 +225,70 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
+        private string GetSamplerType(TextureTarget TextureTarget, bool HasShadow)
+        {
+            string Result;
+
+            switch (TextureTarget)
+            {
+                case TextureTarget.Texture1D:
+                    Result = "sampler1D";
+                    break;
+                case TextureTarget.Texture2D:
+                    Result = "sampler2D";
+                    break;
+                case TextureTarget.Texture3D:
+                    Result = "sampler3D";
+                    break;
+                case TextureTarget.TextureCubeMap:
+                    Result = "samplerCube";
+                    break;
+                case TextureTarget.TextureRectangle:
+                    Result = "sampler2DRect";
+                    break;
+                case TextureTarget.Texture1DArray:
+                    Result = "sampler1DArray";
+                    break;
+                case TextureTarget.Texture2DArray:
+                    Result = "sampler2DArray";
+                    break;
+                case TextureTarget.TextureCubeMapArray:
+                    Result = "samplerCubeArray";
+                    break;
+                case TextureTarget.TextureBuffer:
+                    Result = "samplerBuffer";
+                    break;
+                case TextureTarget.Texture2DMultisample:
+                    Result = "sampler2DMS";
+                    break;
+                case TextureTarget.Texture2DMultisampleArray:
+                    Result = "sampler2DMSArray";
+                    break;
+                default:
+                    throw new NotSupportedException();
+            }
+
+            if (HasShadow)
+                Result += "Shadow";
+
+            return Result;
+        }
+
         private void PrintDeclTextures()
         {
             foreach (ShaderDeclInfo DeclInfo in IterateCbTextures())
             {
-                SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";");
+                TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget);
+                SB.AppendLine($"// {DeclInfo.TextureSuffix}");
+                SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";");
             }
 
-            PrintDecls(Decl.Textures, "uniform sampler2D");
+            foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values.OrderBy(DeclKeySelector))
+            {
+                TextureTarget Target = ImageUtils.GetTextureTarget(DeclInfo.TextureTarget);
+                SB.AppendLine($"// {DeclInfo.TextureSuffix}");
+                SB.AppendLine("uniform " + GetSamplerType(Target, (DeclInfo.TextureSuffix & TextureInstructionSuffix.DC) != 0) + " " + DeclInfo.Name + ";");
+            }
         }
 
         private IEnumerable<ShaderDeclInfo> IterateCbTextures()
@@ -778,6 +840,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 case ShaderIrInst.Ipa:
                 case ShaderIrInst.Texq:
                 case ShaderIrInst.Texs:
+                case ShaderIrInst.Tld4:
                 case ShaderIrInst.Txlf:
                     return false;
             }
@@ -1124,7 +1187,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             string Ch = "rgba".Substring(Meta.Elem, 1);
 
-            return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch;
+            return GetTextureOperation(Op, DeclInfo.Name, Coords, Ch);
         }
 
         private string GetTexqExpr(ShaderIrOp Op)
@@ -1157,20 +1220,50 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             string Ch = "rgba".Substring(Meta.Elem, 1);
 
-            return "texture(" + Sampler + ", " + Coords + ")." + Ch;
+            return GetTextureOperation(Op, Sampler, Coords, Ch);
         }
 
-        private string GetTxlfExpr(ShaderIrOp Op)
+        private string GetTld4Expr(ShaderIrOp Op)
         {
             ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
 
             string Sampler = GetTexSamplerName(Op);
 
+            string Coords = GetTexSamplerCoords(Op);
+
+            string Ch = "rgba".Substring(Meta.Elem, 1);
+
+            return GetTextureGatherOperation(Op, Sampler, Coords, Ch);
+        }
+
+        // TODO: support AOFFI on non nvidia drivers
+        private string GetTxlfExpr(ShaderIrOp Op)
+        {
+            // TODO: Support all suffixes
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
+
+            string Sampler = GetTexSamplerName(Op);
+
             string Coords = GetITexSamplerCoords(Op);
 
             string Ch = "rgba".Substring(Meta.Elem, 1);
 
-            return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch;
+            string Lod = "0";
+
+            if (Meta.LevelOfDetail != null)
+            {
+                Lod = GetOperExpr(Op, Meta.LevelOfDetail);
+            }
+
+            if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+            {
+                string Offset = GetTextureOffset(Meta, GetOperExpr(Op, Meta.Offset));
+                return "texelFetchOffset(" + Sampler + ", " + Coords + ", " + Lod + ", " + Offset + ")." + Ch;
+            }
+
+            return "texelFetch(" + Sampler + ", " + Coords + ", " + Lod + ")." + Ch;
         }
 
         private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc");
@@ -1246,14 +1339,205 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private string GetTexSamplerCoords(ShaderIrOp Op)
         {
-            return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
-                             GetOperExpr(Op, Op.OperandB) + ")";
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            bool HasDepth = (Meta.TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0;
+
+            int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget);
+
+            bool IsArray = ImageUtils.IsArray(Meta.TextureTarget);
+
+
+            string GetLastArgument(ShaderIrNode Node)
+            {
+                string Result = GetOperExpr(Op, Node);
+
+                // array index is actually an integer so we need to pass it correctly
+                if (IsArray)
+                {
+                    Result = "float(floatBitsToInt(" + Result + "))";
+                }
+
+                return Result;
+            }
+
+            string LastArgument;
+            string DepthArgument = "";
+
+            int VecSize = Coords;
+            if (HasDepth && Op.Inst != ShaderIrInst.Tld4)
+            {
+                VecSize++;
+                DepthArgument = $", {GetOperExpr(Op, Meta.DepthCompare)}";
+            }
+
+            switch (Coords)
+            {
+                case 1:
+                    if (HasDepth)
+                    {
+                        return $"vec3({GetOperExpr(Op, Meta.Coordinates[0])}, 0.0{DepthArgument})";
+                    }
+
+                    return GetOperExpr(Op, Meta.Coordinates[0]);
+                case 2:
+                    LastArgument = GetLastArgument(Meta.Coordinates[1]);
+
+                    return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {LastArgument}{DepthArgument})";
+                case 3:
+                    LastArgument = GetLastArgument(Meta.Coordinates[2]);
+
+                    return $"vec{VecSize}({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {LastArgument}{DepthArgument})";
+                case 4:
+                    LastArgument = GetLastArgument(Meta.Coordinates[3]);
+
+                    return $"vec4({GetOperExpr(Op, Meta.Coordinates[0])}, {GetOperExpr(Op, Meta.Coordinates[1])}, {GetOperExpr(Op, Meta.Coordinates[2])}, {LastArgument}){DepthArgument}";
+                default:
+                    throw new InvalidOperationException();
+            }
+
+        }
+
+        private string GetTextureOffset(ShaderIrMetaTex Meta, string Oper, int Shift = 4, int Mask = 0xF)
+        {
+            string GetOffset(string Operation, int Index)
+            {
+                return $"({Operation} >> {Index * Shift}) & 0x{Mask:x}";
+            }
+
+            int Coords = ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget);
+
+            if (ImageUtils.IsArray(Meta.TextureTarget))
+                Coords -= 1;
+
+            switch (Coords)
+            {
+                case 1:
+                    return GetOffset(Oper, 0);
+                case 2:
+                    return "ivec2(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ")";
+                case 3:
+                    return "ivec3(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ")";
+                case 4:
+                    return "ivec4(" + GetOffset(Oper, 0) + ", " + GetOffset(Oper, 1) + ", " + GetOffset(Oper, 2) + ", " + GetOffset(Oper, 3) + ")";
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
+        // TODO: support AOFFI on non nvidia drivers
+        private string GetTextureGatherOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch)
+        {
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
+
+            string ChString = "." + Ch;
+
+            string Comp = Meta.Component.ToString();
+
+            if ((Suffix & TextureInstructionSuffix.DC) != 0)
+            {
+                Comp = GetOperExpr(Op, Meta.DepthCompare);
+            }
+
+            if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+            {
+                string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))", 8, 0x3F);
+
+                if ((Suffix & TextureInstructionSuffix.DC) != 0)
+                {
+                    return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Comp + ", " + Offset + ")" + ChString;
+                }
+
+                return "textureGatherOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + Comp + ")" + ChString;
+            }
+            // TODO: Support PTP
+            else if ((Suffix & TextureInstructionSuffix.PTP) != 0)
+            {
+                throw new NotImplementedException();
+            }
+
+            return "textureGather(" + Sampler + ", " + Coords + ", " + Comp + ")" + ChString;
+        }
+
+        // TODO: support AOFFI on non nvidia drivers
+        private string GetTextureOperation(ShaderIrOp Op, string Sampler, string Coords, string Ch)
+        {
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            TextureInstructionSuffix Suffix = Meta.TextureInstructionSuffix;
+
+            string ChString = "." + Ch;
+
+            if ((Suffix & TextureInstructionSuffix.DC) != 0)
+            {
+                ChString = "";
+            }
+
+            // TODO: Support LBA and LLA
+            if ((Suffix & TextureInstructionSuffix.LZ) != 0)
+            {
+                if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+                {
+                    string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
+
+                    return "textureLodOffset(" + Sampler + ", " + Coords + ", 0.0, " + Offset + ")" + ChString;
+                }
+
+                return "textureLod(" + Sampler + ", " + Coords + ", 0.0)" + ChString;
+            }
+            else if ((Suffix & TextureInstructionSuffix.LB) != 0)
+            {
+                if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+                {
+                    string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
+
+                    return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
+                }
+
+                return "texture(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
+            }
+            else if ((Suffix & TextureInstructionSuffix.LL) != 0)
+            {
+                if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+                {
+                    string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
+
+                    return "textureLodOffset(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ", " + Offset + ")" + ChString;
+                }
+
+                return "textureLod(" + Sampler + ", " + Coords + ", " + GetOperExpr(Op, Meta.LevelOfDetail) + ")" + ChString;
+            }
+            else if ((Suffix & TextureInstructionSuffix.AOffI) != 0 && IsNvidiaDriver)
+            {
+                string Offset = GetTextureOffset(Meta, "floatBitsToInt((" + GetOperExpr(Op, Meta.Offset) + "))");
+
+                return "textureOffset(" + Sampler + ", " + Coords + ", " + Offset + ")" + ChString;
+            }
+            else
+            {
+                return "texture(" + Sampler + ", " + Coords + ")" + ChString;
+            }
+            throw new NotImplementedException($"Texture Suffix {Meta.TextureInstructionSuffix} is not implemented");
+
         }
 
         private string GetITexSamplerCoords(ShaderIrOp Op)
         {
-            return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
-                              GetOperExpr(Op, Op.OperandB) + ")";
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            switch (ImageUtils.GetCoordsCountTextureTarget(Meta.TextureTarget))
+            {
+                case 1:
+                    return GetOperExpr(Op, Meta.Coordinates[0]);
+                case 2:
+                    return "ivec2(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ")";
+                case 3:
+                    return "ivec3(" + GetOperExpr(Op, Meta.Coordinates[0]) + ", " + GetOperExpr(Op, Meta.Coordinates[1]) + ", " + GetOperExpr(Op, Meta.Coordinates[2]) + ")";
+                default:
+                    throw new InvalidOperationException();
+            }
         }
 
         private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
@@ -1292,22 +1576,6 @@ namespace Ryujinx.Graphics.Gal.Shader
                         }
                         break;
                     }
-
-                    case ShaderIrOperImm Imm:
-                    {
-                        //For integer immediates being used as float,
-                        //it's better (for readability) to just return the float value.
-                        if (DstType == OperType.F32)
-                        {
-                            float Value = BitConverter.Int32BitsToSingle(Imm.Value);
-
-                            if (!float.IsNaN(Value) && !float.IsInfinity(Value))
-                            {
-                                return GetFloatConst(Value);
-                            }
-                        }
-                        break;
-                    }
                 }
 
                 switch (DstType)
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
index adcc47b955..8b4eacdf20 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Graphics.Texture;
 using System;
 
 using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
@@ -29,6 +30,75 @@ namespace Ryujinx.Graphics.Gal.Shader
             { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
         };
 
+        private static GalTextureTarget TexToTextureTarget(int TexType, bool IsArray)
+        {
+            switch (TexType)
+            {
+                case 0:
+                    return IsArray ? GalTextureTarget.OneDArray : GalTextureTarget.OneD;
+                case 2:
+                    return IsArray ? GalTextureTarget.TwoDArray : GalTextureTarget.TwoD;
+                case 4:
+                    if (IsArray)
+                        throw new InvalidOperationException($"ARRAY bit set on a TEX with 3D texture!");
+                    return GalTextureTarget.ThreeD;
+                case 6:
+                    return IsArray ? GalTextureTarget.CubeArray : GalTextureTarget.CubeMap;
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
+        private static GalTextureTarget TexsToTextureTarget(int TexType)
+        {
+            switch (TexType)
+            {
+                case 0:
+                    return GalTextureTarget.OneD;
+                case 2:
+                case 4:
+                case 6:
+                case 8:
+                case 0xa:
+                case 0xc:
+                    return GalTextureTarget.TwoD;
+                case 0xe:
+                case 0x10:
+                case 0x12:
+                    return GalTextureTarget.TwoDArray;
+                case 0x14:
+                case 0x16:
+                    return GalTextureTarget.ThreeD;
+                case 0x18:
+                case 0x1a:
+                    return GalTextureTarget.CubeMap;
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
+        public static GalTextureTarget TldsToTextureTarget(int TexType)
+        {
+            switch (TexType)
+            {
+                case 0:
+                case 2:
+                    return GalTextureTarget.OneD;
+                case 4:
+                case 8:
+                case 0xa:
+                case 0xc:
+                case 0x18:
+                    return GalTextureTarget.TwoD;
+                case 0x10:
+                    return GalTextureTarget.TwoDArray;
+                case 0xe:
+                    return GalTextureTarget.ThreeD;
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
         public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode[] Opers = OpCode.Abuf20();
@@ -132,43 +202,166 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Tex(ShaderIrBlock Block, long OpCode, int Position)
         {
-            EmitTex(Block, OpCode, GprHandle: false);
+            TextureInstructionSuffix Suffix;
+
+            int RawSuffix = OpCode.Read(0x34, 0x38);
+
+            switch (RawSuffix)
+            {
+                case 0:
+                    Suffix = TextureInstructionSuffix.None;
+                    break;
+                case 0x8:
+                    Suffix = TextureInstructionSuffix.LZ;
+                    break;
+                case 0x10:
+                    Suffix = TextureInstructionSuffix.LB;
+                    break;
+                case 0x18:
+                    Suffix = TextureInstructionSuffix.LL;
+                    break;
+                case 0x30:
+                    Suffix = TextureInstructionSuffix.LBA;
+                    break;
+                case 0x38:
+                    Suffix = TextureInstructionSuffix.LLA;
+                    break;
+                default:
+                    throw new InvalidOperationException($"Invalid Suffix for TEX instruction {RawSuffix}");
+            }
+
+            bool IsOffset = OpCode.Read(0x36);
+
+            if (IsOffset)
+                Suffix |= TextureInstructionSuffix.AOffI;
+
+            EmitTex(Block, OpCode, Suffix, GprHandle: false);
         }
 
         public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position)
         {
-            EmitTex(Block, OpCode, GprHandle: true);
+            TextureInstructionSuffix Suffix;
+
+            int RawSuffix = OpCode.Read(0x24, 0xe);
+
+            switch (RawSuffix)
+            {
+                case 0:
+                    Suffix = TextureInstructionSuffix.None;
+                    break;
+                case 0x2:
+                    Suffix = TextureInstructionSuffix.LZ;
+                    break;
+                case 0x4:
+                    Suffix = TextureInstructionSuffix.LB;
+                    break;
+                case 0x6:
+                    Suffix = TextureInstructionSuffix.LL;
+                    break;
+                case 0xc:
+                    Suffix = TextureInstructionSuffix.LBA;
+                    break;
+                case 0xe:
+                    Suffix = TextureInstructionSuffix.LLA;
+                    break;
+                default:
+                    throw new InvalidOperationException($"Invalid Suffix for TEX.B instruction {RawSuffix}");
+            }
+
+            bool IsOffset = OpCode.Read(0x23);
+
+            if (IsOffset)
+                Suffix |= TextureInstructionSuffix.AOffI;
+
+            EmitTex(Block, OpCode, Suffix, GprHandle: true);
         }
 
-        private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle)
+        private static void EmitTex(ShaderIrBlock Block, long OpCode, TextureInstructionSuffix TextureInstructionSuffix, bool GprHandle)
         {
-            //TODO: Support other formats.
-            ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2];
+            bool IsArray = OpCode.HasArray();
 
-            for (int Index = 0; Index < Coords.Length; Index++)
+            GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray);
+
+            bool HasDepthCompare = OpCode.Read(0x32);
+
+            if (HasDepthCompare)
+            {
+                TextureInstructionSuffix |= TextureInstructionSuffix.DC;
+            }
+
+            ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)];
+
+            int IndexExtraCoord = 0;
+
+            if (IsArray)
+            {
+                IndexExtraCoord++;
+
+                Coords[Coords.Length - 1] = OpCode.Gpr8();
+            }
+
+
+            for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++)
             {
                 ShaderIrOperGpr CoordReg = OpCode.Gpr8();
 
                 CoordReg.Index += Index;
 
+                CoordReg.Index += IndexExtraCoord;
+
                 if (!CoordReg.IsValidRegister)
                 {
                     CoordReg.Index = ShaderIrOperGpr.ZRIndex;
                 }
 
-                Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index);
-
-                Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg));
+                Coords[Index] = CoordReg;
             }
 
             int ChMask = OpCode.Read(31, 0xf);
 
+            ShaderIrOperGpr LevelOfDetail = null;
+            ShaderIrOperGpr Offset        = null;
+            ShaderIrOperGpr DepthCompare  = null;
+
+            // TODO: determine first argument when TEX.B is used
+            int OperBIndex = GprHandle ? 1 : 0;
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0 ||
+                (TextureInstructionSuffix & TextureInstructionSuffix.LB) != 0 ||
+                (TextureInstructionSuffix & TextureInstructionSuffix.LBA) != 0 ||
+                (TextureInstructionSuffix & TextureInstructionSuffix.LLA) != 0)
+            {
+                LevelOfDetail        = OpCode.Gpr20();
+                LevelOfDetail.Index += OperBIndex;
+
+                OperBIndex++;
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
+            {
+                Offset        = OpCode.Gpr20();
+                Offset.Index += OperBIndex;
+
+                OperBIndex++;
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
+            {
+                DepthCompare        = OpCode.Gpr20();
+                DepthCompare.Index += OperBIndex;
+
+                OperBIndex++;
+            }
+
+            // ???
             ShaderIrNode OperC = GprHandle
                 ? (ShaderIrNode)OpCode.Gpr20()
                 : (ShaderIrNode)OpCode.Imm13_36();
 
             ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;
 
+            Coords = CoordsRegistersToTempRegisters(Block, Coords);
+
             int RegInc = 0;
 
             for (int Ch = 0; Ch < 4; Ch++)
@@ -187,9 +380,14 @@ namespace Ryujinx.Graphics.Gal.Shader
                     continue;
                 }
 
-                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
+                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords)
+                {
+                    LevelOfDetail = LevelOfDetail,
+                    Offset        = Offset,
+                    DepthCompare  = DepthCompare
+                };
 
-                ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
+                ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords.Length > 1 ? Coords[1] : null, OperC, Meta);
 
                 Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
             }
@@ -197,17 +395,238 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Texs(ShaderIrBlock Block, long OpCode, int Position)
         {
-            EmitTexs(Block, OpCode, ShaderIrInst.Texs);
+            TextureInstructionSuffix Suffix;
+
+            int RawSuffix = OpCode.Read(0x34, 0x1e);
+
+            switch (RawSuffix)
+            {
+                case 0:
+                case 0x4:
+                case 0x10:
+                case 0x16:
+                    Suffix = TextureInstructionSuffix.LZ;
+                    break;
+                case 0x6:
+                case 0x1a:
+                    Suffix = TextureInstructionSuffix.LL;
+                    break;
+                case 0x8:
+                    Suffix = TextureInstructionSuffix.DC;
+                    break;
+                case 0x2:
+                case 0xe:
+                case 0x14:
+                case 0x18:
+                    Suffix = TextureInstructionSuffix.None;
+                    break;
+                case 0xa:
+                    Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.DC;
+                    break;
+                case 0xc:
+                case 0x12:
+                    Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.DC;
+                    break;
+                default:
+                    throw new InvalidOperationException($"Invalid Suffix for TEXS instruction {RawSuffix}");
+            }
+
+            GalTextureTarget TextureTarget = TexsToTextureTarget(OpCode.Read(52, 0x1e));
+
+            EmitTexs(Block, OpCode, ShaderIrInst.Texs, TextureTarget, Suffix);
         }
 
         public static void Tlds(ShaderIrBlock Block, long OpCode, int Position)
         {
-            EmitTexs(Block, OpCode, ShaderIrInst.Txlf);
+            TextureInstructionSuffix Suffix;
+
+            int RawSuffix = OpCode.Read(0x34, 0x1e);
+
+            switch (RawSuffix)
+            {
+                case 0:
+                case 0x4:
+                case 0x8:
+                    Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.AOffI;
+                    break;
+                case 0xc:
+                    Suffix = TextureInstructionSuffix.LZ | TextureInstructionSuffix.MZ;
+                    break;
+                case 0xe:
+                case 0x10:
+                    Suffix = TextureInstructionSuffix.LZ;
+                    break;
+                case 0x2:
+                case 0xa:
+                    Suffix = TextureInstructionSuffix.LL;
+                    break;
+                case 0x18:
+                    Suffix = TextureInstructionSuffix.LL | TextureInstructionSuffix.AOffI;
+                    break;
+                default:
+                    throw new InvalidOperationException($"Invalid Suffix for TLDS instruction {RawSuffix}");
+            }
+
+            GalTextureTarget TextureTarget = TldsToTextureTarget(OpCode.Read(52, 0x1e));
+
+            EmitTexs(Block, OpCode, ShaderIrInst.Txlf, TextureTarget, Suffix);
         }
 
-        private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
+        public static void Tld4(ShaderIrBlock Block, long OpCode, int Position)
         {
-            //TODO: Support other formats.
+            TextureInstructionSuffix Suffix;
+
+            int RawSuffix = OpCode.Read(0x34, 0xc);
+
+            switch (RawSuffix)
+            {
+                case 0:
+                    Suffix = TextureInstructionSuffix.None;
+                    break;
+                case 0x4:
+                    Suffix = TextureInstructionSuffix.AOffI;
+                    break;
+                case 0x8:
+                    Suffix = TextureInstructionSuffix.PTP;
+                    break;
+                default:
+                    throw new InvalidOperationException($"Invalid Suffix for TLD4 instruction {RawSuffix}");
+            }
+
+            bool IsShadow = OpCode.Read(0x32);
+
+            bool IsArray = OpCode.HasArray();
+            int  ChMask  = OpCode.Read(31, 0xf);
+
+            GalTextureTarget TextureTarget = TexToTextureTarget(OpCode.Read(28, 6), IsArray);
+
+            if (IsShadow)
+            {
+                Suffix |= TextureInstructionSuffix.DC;
+            }
+
+            EmitTld4(Block, OpCode, TextureTarget, Suffix, ChMask, OpCode.Read(0x38, 0x3), false);
+        }
+
+        public static void Tld4s(ShaderIrBlock Block, long OpCode, int Position)
+        {
+            TextureInstructionSuffix Suffix = TextureInstructionSuffix.None;
+
+            bool IsOffset = OpCode.Read(0x33);
+            bool IsShadow = OpCode.Read(0x32);
+
+            if (IsOffset)
+            {
+                Suffix |= TextureInstructionSuffix.AOffI;
+            }
+
+            if (IsShadow)
+            {
+                Suffix |= TextureInstructionSuffix.DC;
+            }
+
+            // TLD4S seems to only support 2D textures with RGBA mask?
+            EmitTld4(Block, OpCode, GalTextureTarget.TwoD, Suffix, RGBA, OpCode.Read(0x34, 0x3), true);
+        }
+
+        private static void EmitTexs(ShaderIrBlock            Block,
+                                     long                     OpCode,
+                                     ShaderIrInst             Inst,
+                                     GalTextureTarget         TextureTarget,
+                                     TextureInstructionSuffix TextureInstructionSuffix)
+        {
+            if (Inst == ShaderIrInst.Txlf && TextureTarget == GalTextureTarget.CubeArray)
+            {
+                throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!");
+            }
+
+            bool IsArray = ImageUtils.IsArray(TextureTarget);
+
+            ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureTarget)];
+
+            ShaderIrOperGpr OperA = OpCode.Gpr8();
+            ShaderIrOperGpr OperB = OpCode.Gpr20();
+
+            ShaderIrOperGpr SuffixExtra = OpCode.Gpr20();
+            SuffixExtra.Index += 1;
+
+            int CoordStartIndex = 0;
+
+            if (IsArray)
+            {
+                CoordStartIndex++;
+                Coords[Coords.Length - 1] = OpCode.Gpr8();
+            }
+
+            switch (Coords.Length - CoordStartIndex)
+            {
+                case 1:
+                    Coords[0] = OpCode.Gpr8();
+
+                    break;
+                case 2:
+                    Coords[0] = OpCode.Gpr8();
+                    Coords[0].Index += CoordStartIndex;
+
+                    break;
+                case 3:
+                    Coords[0] = OpCode.Gpr8();
+                    Coords[0].Index += CoordStartIndex;
+
+                    Coords[1] = OpCode.Gpr8();
+                    Coords[1].Index += 1 + CoordStartIndex;
+
+                    break;
+                default:
+                    throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TEXS");
+            }
+
+            int OperBIndex = 0;
+
+            ShaderIrOperGpr LevelOfDetail = null;
+            ShaderIrOperGpr Offset        = null;
+            ShaderIrOperGpr DepthCompare  = null;
+
+            // OperB is always the last value
+            // Not applicable to 1d textures
+            if (Coords.Length - CoordStartIndex != 1)
+            {
+                Coords[Coords.Length - CoordStartIndex -  1] = OperB;
+                OperBIndex++;
+            }
+
+            // Encoding of TEXS/TLDS is a bit special and change for 2d textures
+            // NOTE: OperA seems to hold at best two args.
+            // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB.
+            if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureInstructionSuffix != TextureInstructionSuffix.LZ && TextureTarget == GalTextureTarget.TwoD)
+            {
+                Coords[Coords.Length - CoordStartIndex - 1]        = OpCode.Gpr8();
+                Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1;
+                OperBIndex--;
+            }
+
+            // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?)
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.LL) != 0)
+            {
+                LevelOfDetail        = OpCode.Gpr20();
+                LevelOfDetail.Index += OperBIndex;
+                OperBIndex++;
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
+            {
+                Offset        = OpCode.Gpr20();
+                Offset.Index += OperBIndex;
+                OperBIndex++;
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
+            {
+                DepthCompare        = OpCode.Gpr20();
+                DepthCompare.Index += OperBIndex;
+                OperBIndex++;
+            }
+
             int LutIndex;
 
             LutIndex  = !OpCode.Gpr0().IsConst  ? 1 : 0;
@@ -276,12 +695,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
 
             ShaderIrNode OperC = OpCode.Imm13_36();
-
-            ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0);
-            ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1);
-
-            Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8()));
-            Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20()));
+            Coords = CoordsRegistersToTempRegisters(Block, Coords);
 
             for (int Ch = 0; Ch < 4; Ch++)
             {
@@ -290,9 +704,13 @@ namespace Ryujinx.Graphics.Gal.Shader
                     continue;
                 }
 
-                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
-
-                ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta);
+                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureTarget, TextureInstructionSuffix, Coords)
+                {
+                    LevelOfDetail = LevelOfDetail,
+                    Offset        = Offset,
+                    DepthCompare  = DepthCompare
+                };
+                ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
 
                 ShaderIrOperGpr Dst = GetDst();
 
@@ -303,9 +721,156 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
+        private static void EmitTld4(ShaderIrBlock Block, long OpCode, GalTextureTarget TextureType, TextureInstructionSuffix TextureInstructionSuffix, int ChMask, int Component, bool Scalar)
+        {
+            ShaderIrOperGpr OperA = OpCode.Gpr8();
+            ShaderIrOperGpr OperB = OpCode.Gpr20();
+            ShaderIrOperImm OperC = OpCode.Imm13_36();
+
+            ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(TextureType)];
+
+            ShaderIrOperGpr Offset       = null;
+            ShaderIrOperGpr DepthCompare = null;
+
+            bool IsArray = ImageUtils.IsArray(TextureType);
+
+            int OperBIndex = 0;
+
+            if (Scalar)
+            {
+                int CoordStartIndex = 0;
+
+                if (IsArray)
+                {
+                    CoordStartIndex++;
+                    Coords[Coords.Length - 1] = OperB;
+                }
+
+                switch (Coords.Length - CoordStartIndex)
+                {
+                    case 1:
+                        Coords[0] = OpCode.Gpr8();
+
+                        break;
+                    case 2:
+                        Coords[0] = OpCode.Gpr8();
+                        Coords[0].Index += CoordStartIndex;
+
+                        break;
+                    case 3:
+                        Coords[0] = OpCode.Gpr8();
+                        Coords[0].Index += CoordStartIndex;
+
+                        Coords[1] = OpCode.Gpr8();
+                        Coords[1].Index += 1 + CoordStartIndex;
+
+                        break;
+                    default:
+                        throw new NotSupportedException($"{Coords.Length - CoordStartIndex} coords textures aren't supported in TLD4S");
+                }
+
+                if (Coords.Length - CoordStartIndex != 1)
+                {
+                    Coords[Coords.Length - CoordStartIndex - 1] = OperB;
+                    OperBIndex++;
+                }
+
+                if (TextureInstructionSuffix != TextureInstructionSuffix.None && TextureType == GalTextureTarget.TwoD)
+                {
+                    Coords[Coords.Length - CoordStartIndex - 1] = OpCode.Gpr8();
+                    Coords[Coords.Length - CoordStartIndex - 1].Index += Coords.Length - CoordStartIndex - 1;
+                    OperBIndex--;
+                }
+            }
+            else
+            {
+                int IndexExtraCoord = 0;
+
+                if (IsArray)
+                {
+                    IndexExtraCoord++;
+
+                    Coords[Coords.Length - 1] = OpCode.Gpr8();
+                }
+
+                for (int Index = 0; Index < Coords.Length - IndexExtraCoord; Index++)
+                {
+                    Coords[Index] = OpCode.Gpr8();
+
+                    Coords[Index].Index += Index;
+
+                    Coords[Index].Index += IndexExtraCoord;
+
+                    if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex)
+                    {
+                        Coords[Index].Index = ShaderIrOperGpr.ZRIndex;
+                    }
+                }
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0)
+            {
+                Offset = OpCode.Gpr20();
+                Offset.Index += OperBIndex;
+                OperBIndex++;
+            }
+
+            if ((TextureInstructionSuffix & TextureInstructionSuffix.DC) != 0)
+            {
+                DepthCompare = OpCode.Gpr20();
+                DepthCompare.Index += OperBIndex;
+                OperBIndex++;
+            }
+
+            Coords = CoordsRegistersToTempRegisters(Block, Coords);
+
+            int RegInc = 0;
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                if (!IsChannelUsed(ChMask, Ch))
+                {
+                    continue;
+                }
+
+                ShaderIrOperGpr Dst = OpCode.Gpr0();
+
+                Dst.Index += RegInc++;
+
+                if (!Dst.IsValidRegister || Dst.IsConst)
+                {
+                    continue;
+                }
+
+                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch, TextureType, TextureInstructionSuffix, Coords)
+                {
+                    Component = Component,
+                    Offset = Offset,
+                    DepthCompare = DepthCompare
+                };
+
+                ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Tld4, OperA, OperB, OperC, Meta);
+
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
+            }
+        }
+
         private static bool IsChannelUsed(int ChMask, int Ch)
         {
             return (ChMask & (1 << Ch)) != 0;
         }
+
+        private static ShaderIrOperGpr[] CoordsRegistersToTempRegisters(ShaderIrBlock Block, params ShaderIrOperGpr[] Registers)
+        {
+            ShaderIrOperGpr[] Res = new ShaderIrOperGpr[Registers.Length];
+
+            for (int Index = 0; Index < Res.Length; Index++)
+            {
+                Res[Index] = ShaderIrOperGpr.MakeTemporary(Index);
+                Block.AddNode(new ShaderIrAsg(Res[Index], Registers[Index]));
+            }
+
+            return Res;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs
index f0f92148e6..e241e1ca58 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs
@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gal.Shader
             return ((int)(OpCode >> 20) << 8) >> 8;
         }
 
+        private static bool HasArray(this long OpCode)
+        {
+            return OpCode.Read(0x1c);
+        }
+
         private static ShaderIrOperAbuf[] Abuf20(this long OpCode)
         {
             int Abuf = OpCode.Read(20, 0x3ff);
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
index 35dea61216..68ff214e4e 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
@@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Gal.Shader
         Ipa,
         Texb,
         Texs,
+        Tld4,
         Trunc,
         F_End,
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
index 82f3bb774a..72ea221ad3 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
@@ -1,12 +1,24 @@
+using Ryujinx.Graphics.Texture;
+
 namespace Ryujinx.Graphics.Gal.Shader
 {
     class ShaderIrMetaTex : ShaderIrMeta
     {
-        public int Elem { get; private set; }
+        public int                      Elem { get; private set; }
+        public GalTextureTarget         TextureTarget { get; private set; }
+        public ShaderIrNode[]           Coordinates { get; private set; }
+        public TextureInstructionSuffix TextureInstructionSuffix { get; private set; }
+        public ShaderIrOperGpr          LevelOfDetail;
+        public ShaderIrOperGpr          Offset;
+        public ShaderIrOperGpr          DepthCompare;
+        public int                      Component; // for TLD4(S)
 
-        public ShaderIrMetaTex(int Elem)
+        public ShaderIrMetaTex(int Elem, GalTextureTarget TextureTarget, TextureInstructionSuffix TextureInstructionSuffix, params ShaderIrNode[] Coordinates)
         {
-            this.Elem = Elem;
+            this.Elem                     = Elem;
+            this.TextureTarget            = TextureTarget;
+            this.TextureInstructionSuffix = TextureInstructionSuffix;
+            this.Coordinates              = Coordinates;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
index 177e36c3e1..d2bbd38c6b 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -122,6 +122,8 @@ namespace Ryujinx.Graphics.Gal.Shader
             Set("1101111101001x", ShaderDecode.Texq);
             Set("1101x00xxxxxxx", ShaderDecode.Texs);
             Set("1101101xxxxxxx", ShaderDecode.Tlds);
+            Set("110010xxxx111x", ShaderDecode.Tld4);
+            Set("1101111100xxxx", ShaderDecode.Tld4s);
             Set("01011111xxxxxx", ShaderDecode.Vmad);
             Set("0100111xxxxxxx", ShaderDecode.Xmad_CR);
             Set("0011011x00xxxx", ShaderDecode.Xmad_I);
diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
index ef47ca2e1b..ed1955cdbb 100644
--- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
+++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Graphics.Texture;
+
 namespace Ryujinx.Graphics.Gal
 {
     public class ShaderDeclInfo
@@ -9,18 +11,27 @@ namespace Ryujinx.Graphics.Gal
         public int  Cbuf  { get; private set; }
         public int  Size  { get; private set; }
 
+        public GalTextureTarget TextureTarget { get; private set; }
+
+        public TextureInstructionSuffix TextureSuffix { get; private set; }
+
         public ShaderDeclInfo(
             string Name,
             int    Index,
             bool   IsCb = false,
             int    Cbuf = 0,
-            int    Size = 1)
+            int    Size = 1,
+            GalTextureTarget         TextureTarget = GalTextureTarget.TwoD,
+            TextureInstructionSuffix TextureSuffix = TextureInstructionSuffix.None)
         {
-            this.Name  = Name;
-            this.Index = Index;
-            this.IsCb  = IsCb;
-            this.Cbuf  = Cbuf;
-            this.Size  = Size;
+            this.Name        = Name;
+            this.Index       = Index;
+            this.IsCb        = IsCb;
+            this.Cbuf        = Cbuf;
+            this.Size        = Size;
+
+            this.TextureTarget = TextureTarget;
+            this.TextureSuffix = TextureSuffix;
         }
 
         internal void Enlarge(int NewSize)
diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs
index d46129516d..4f2d92b03a 100644
--- a/Ryujinx.Graphics/GpuResourceManager.cs
+++ b/Ryujinx.Graphics/GpuResourceManager.cs
@@ -1,6 +1,8 @@
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
+using System;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics
@@ -11,6 +13,7 @@ namespace Ryujinx.Graphics
         {
             None,
             Texture,
+            TextureArrayLayer,
             ColorBuffer,
             ZetaBuffer
         }
@@ -20,6 +23,7 @@ namespace Ryujinx.Graphics
         private HashSet<long>[] UploadedKeys;
 
         private Dictionary<long, ImageType> ImageTypes;
+        private Dictionary<long, int>      MirroredTextures;
 
         public GpuResourceManager(NvGpu Gpu)
         {
@@ -33,6 +37,7 @@ namespace Ryujinx.Graphics
             }
 
             ImageTypes = new Dictionary<long, ImageType>();
+            MirroredTextures = new Dictionary<long, int>();
         }
 
         public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage)
@@ -70,6 +75,32 @@ namespace Ryujinx.Graphics
             ImageTypes[Position] = ImageType.Texture;
         }
 
+        public bool TryGetTextureLayer(long Position, out int LayerIndex)
+        {
+            if (MirroredTextures.TryGetValue(Position, out LayerIndex))
+            {
+                ImageType Type = ImageTypes[Position];
+
+                // FIXME(thog): I'm actually unsure if we should deny all other image type, gpu testing needs to be done here.
+                if (Type != ImageType.Texture && Type != ImageType.TextureArrayLayer)
+                {
+                    LayerIndex = -1;
+                    return false;
+                }
+
+                return true;
+            }
+
+            LayerIndex = -1;
+            return false;
+        }
+
+        public void SetTextureArrayLayer(long Position, int LayerIndex)
+        {
+            ImageTypes[Position] = ImageType.TextureArrayLayer;
+            MirroredTextures[Position] = LayerIndex;
+        }
+
         private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage)
         {
             long Size = ImageUtils.GetSize(NewImage);
@@ -102,7 +133,7 @@ namespace Ryujinx.Graphics
 
         private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage)
         {
-            if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage))
+            if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.TextureTarget == NewImage.TextureTarget && CachedImage.SizeMatches(NewImage))
             {
                 Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);
 
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs
index 55e3ebd4c4..3295f6da05 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
@@ -46,6 +47,8 @@ namespace Ryujinx.Graphics.Graphics3d
             bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
             int  DstWidth  = ReadRegister(NvGpuEngine2dReg.DstWidth);
             int  DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
+            int  DstDepth  = ReadRegister(NvGpuEngine2dReg.DstDepth);
+            int  DstLayer  = ReadRegister(NvGpuEngine2dReg.DstLayer);
             int  DstPitch  = ReadRegister(NvGpuEngine2dReg.DstPitch);
             int  DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
 
@@ -53,6 +56,8 @@ namespace Ryujinx.Graphics.Graphics3d
             bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
             int  SrcWidth  = ReadRegister(NvGpuEngine2dReg.SrcWidth);
             int  SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight);
+            int  SrcDepth  = ReadRegister(NvGpuEngine2dReg.SrcDepth);
+            int  SrcLayer  = ReadRegister(NvGpuEngine2dReg.SrcLayer);
             int  SrcPitch  = ReadRegister(NvGpuEngine2dReg.SrcPitch);
             int  SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions);
 
@@ -82,26 +87,99 @@ namespace Ryujinx.Graphics.Graphics3d
             long SrcKey = Vmm.GetPhysicalAddress(SrcAddress);
             long DstKey = Vmm.GetPhysicalAddress(DstAddress);
 
+            bool IsSrcLayered = false;
+            bool IsDstLayered = false;
+
+            GalTextureTarget SrcTarget = GalTextureTarget.TwoD;
+
+            if (SrcDepth != 0)
+            {
+                SrcTarget = GalTextureTarget.TwoDArray;
+                SrcDepth++;
+                IsSrcLayered = true;
+            }
+            else
+            {
+                SrcDepth = 1;
+            }
+
+            GalTextureTarget DstTarget = GalTextureTarget.TwoD;
+
+            if (DstDepth != 0)
+            {
+                DstTarget = GalTextureTarget.TwoDArray;
+                DstDepth++;
+                IsDstLayered = true;
+            }
+            else
+            {
+                DstDepth = 1;
+            }
+
             GalImage SrcTexture = new GalImage(
                 SrcWidth,
-                SrcHeight, 1,
-                SrcBlockHeight,
+                SrcHeight,
+                1, SrcDepth, 1,
+                SrcBlockHeight, 1,
                 SrcLayout,
-                SrcImgFormat);
+                SrcImgFormat,
+                SrcTarget);
 
             GalImage DstTexture = new GalImage(
                 DstWidth,
-                DstHeight, 1,
-                DstBlockHeight,
+                DstHeight,
+                1, DstDepth, 1,
+                DstBlockHeight, 1,
                 DstLayout,
-                DstImgFormat);
+                DstImgFormat,
+                DstTarget);
 
             SrcTexture.Pitch = SrcPitch;
             DstTexture.Pitch = DstPitch;
 
+            long GetLayerOffset(GalImage Image, int Layer)
+            {
+                int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1;
+                return ImageUtils.GetLayerOffset(Image, TargetMipLevel) * Layer;
+            }
+
+            int SrcLayerIndex = -1;
+
+            if (IsSrcLayered && Gpu.ResourceManager.TryGetTextureLayer(SrcKey, out SrcLayerIndex) && SrcLayerIndex != 0)
+            {
+                SrcKey = SrcKey - GetLayerOffset(SrcTexture, SrcLayerIndex);
+            }
+
+            int DstLayerIndex = -1;
+
+            if (IsDstLayered && Gpu.ResourceManager.TryGetTextureLayer(DstKey, out DstLayerIndex) && DstLayerIndex != 0)
+            {
+                DstKey = DstKey - GetLayerOffset(DstTexture, DstLayerIndex);
+            }
+
             Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture);
             Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture);
 
+            if (IsSrcLayered && SrcLayerIndex == -1)
+            {
+                for (int Layer = 0; Layer < SrcTexture.LayerCount; Layer++)
+                {
+                    Gpu.ResourceManager.SetTextureArrayLayer(SrcKey + GetLayerOffset(SrcTexture, Layer), Layer);
+                }
+
+                SrcLayerIndex = 0;
+            }
+
+            if (IsDstLayered && DstLayerIndex == -1)
+            {
+                for (int Layer = 0; Layer < DstTexture.LayerCount; Layer++)
+                {
+                    Gpu.ResourceManager.SetTextureArrayLayer(DstKey + GetLayerOffset(DstTexture, Layer), Layer);
+                }
+
+                DstLayerIndex = 0;
+            }
+
             int SrcBlitX1 = (int)(SrcBlitX >> 32);
             int SrcBlitY1 = (int)(SrcBlitY >> 32);
 
@@ -109,8 +187,12 @@ namespace Ryujinx.Graphics.Graphics3d
             int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32);
 
             Gpu.Renderer.RenderTarget.Copy(
+                SrcTexture,
+                DstTexture,
                 SrcKey,
                 DstKey,
+                SrcLayerIndex,
+                DstLayerIndex,
                 SrcBlitX1,
                 SrcBlitY1,
                 SrcBlitX2,
@@ -124,6 +206,8 @@ namespace Ryujinx.Graphics.Graphics3d
             //the texture is modified by the guest, however it doesn't
             //work when resources that the gpu can write to are copied,
             //like framebuffers.
+
+            // FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer)
             ImageUtils.CopyTexture(
                 Vmm,
                 SrcTexture,
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs
index c1c0dba29f..7747b5a3ab 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs
@@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Graphics3d
         DstWidth           = 0x86,
         DstHeight          = 0x87,
         DstAddress         = 0x88,
+        DstAddressLow      = 0x89,
         SrcFormat          = 0x8c,
         SrcLinear          = 0x8d,
         SrcBlockDimensions = 0x8e,
@@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Graphics3d
         SrcWidth           = 0x92,
         SrcHeight          = 0x93,
         SrcAddress         = 0x94,
+        SrcAddressLow      = 0x95,
         ClipEnable         = 0xa4,
         CopyOperation      = 0xab,
         BlitControl        = 0x223,
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
index 6120053dae..eb6289fbdb 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Common;
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
@@ -190,7 +191,11 @@ namespace Ryujinx.Graphics.Graphics3d
             int Width  = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth  + FbIndex * 0x10);
             int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
 
-            int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10);
+            int ArrayMode   = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + FbIndex * 0x10);
+            int LayerCount  = ArrayMode & 0xFFFF;
+            int LayerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + FbIndex * 0x10);
+            int BaseLayer   = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + FbIndex * 0x10);
+            int BlockDim    = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10);
 
             int GobBlockHeight = 1 << ((BlockDim >> 4) & 7);
 
@@ -210,7 +215,7 @@ namespace Ryujinx.Graphics.Graphics3d
 
             GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat);
 
-            GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format);
+            GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD);
 
             Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
 
@@ -264,7 +269,8 @@ namespace Ryujinx.Graphics.Graphics3d
 
             GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat);
 
-            GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format);
+            // TODO: Support non 2D?
+            GalImage Image = new GalImage(Width, Height, 1, 1, 1, GobBlockHeight, 1, Layout, Format, GalTextureTarget.TwoD);
 
             Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image);
         }
@@ -600,7 +606,7 @@ namespace Ryujinx.Graphics.Graphics3d
                 }
 
                 Gpu.Renderer.Texture.Bind(Key, Index, Image);
-                Gpu.Renderer.Texture.SetSampler(Sampler);
+                Gpu.Renderer.Texture.SetSampler(Image, Sampler);
             }
         }
 
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
index 026b0cd198..9134646403 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
@@ -2,112 +2,115 @@ namespace Ryujinx.Graphics.Graphics3d
 {
     enum NvGpuEngine3dReg
     {
-        FrameBufferNAddress  = 0x200,
-        FrameBufferNWidth    = 0x202,
-        FrameBufferNHeight   = 0x203,
-        FrameBufferNFormat   = 0x204,
-        FrameBufferNBlockDim = 0x205,
-        ViewportNScaleX      = 0x280,
-        ViewportNScaleY      = 0x281,
-        ViewportNScaleZ      = 0x282,
-        ViewportNTranslateX  = 0x283,
-        ViewportNTranslateY  = 0x284,
-        ViewportNTranslateZ  = 0x285,
-        ViewportNHoriz       = 0x300,
-        ViewportNVert        = 0x301,
-        DepthRangeNNear      = 0x302,
-        DepthRangeNFar       = 0x303,
-        VertexArrayFirst     = 0x35d,
-        VertexArrayCount     = 0x35e,
-        ClearNColor          = 0x360,
-        ClearDepth           = 0x364,
-        ClearStencil         = 0x368,
-        ScissorEnable        = 0x380,
-        ScissorHorizontal    = 0x381,
-        ScissorVertical      = 0x382,
-        StencilBackFuncRef   = 0x3d5,
-        StencilBackMask      = 0x3d6,
-        StencilBackFuncMask  = 0x3d7,
-        ColorMaskCommon      = 0x3e4,
-        RTSeparateFragData   = 0x3eb,
-        ZetaAddress          = 0x3f8,
-        ZetaFormat           = 0x3fa,
-        ZetaBlockDimensions  = 0x3fb,
-        ZetaLayerStride      = 0x3fc,
-        VertexAttribNFormat  = 0x458,
-        RTControl            = 0x487,
-        ZetaHoriz            = 0x48a,
-        ZetaVert             = 0x48b,
-        ZetaArrayMode        = 0x48c,
-        LinkedTsc            = 0x48d,
-        DepthTestEnable      = 0x4b3,
-        BlendIndependent     = 0x4b9,
-        DepthWriteEnable     = 0x4ba,
-        DepthTestFunction    = 0x4c3,
-        BlendSeparateAlpha   = 0x4cf,
-        BlendEquationRgb     = 0x4d0,
-        BlendFuncSrcRgb      = 0x4d1,
-        BlendFuncDstRgb      = 0x4d2,
-        BlendEquationAlpha   = 0x4d3,
-        BlendFuncSrcAlpha    = 0x4d4,
-        BlendFuncDstAlpha    = 0x4d6,
-        BlendEnable          = 0x4d7,
-        IBlendNEnable        = 0x4d8,
-        StencilEnable        = 0x4e0,
-        StencilFrontOpFail   = 0x4e1,
-        StencilFrontOpZFail  = 0x4e2,
-        StencilFrontOpZPass  = 0x4e3,
-        StencilFrontFuncFunc = 0x4e4,
-        StencilFrontFuncRef  = 0x4e5,
-        StencilFrontFuncMask = 0x4e6,
-        StencilFrontMask     = 0x4e7,
-        ScreenYControl       = 0x4eb,
-        VertexArrayElemBase  = 0x50d,
-        VertexArrayInstBase  = 0x50e,
-        ZetaEnable           = 0x54e,
-        TexHeaderPoolOffset  = 0x55d,
-        TexSamplerPoolOffset = 0x557,
-        StencilTwoSideEnable = 0x565,
-        StencilBackOpFail    = 0x566,
-        StencilBackOpZFail   = 0x567,
-        StencilBackOpZPass   = 0x568,
-        StencilBackFuncFunc  = 0x569,
-        FrameBufferSrgb      = 0x56e,
-        ShaderAddress        = 0x582,
-        VertexBeginGl        = 0x586,
-        PrimRestartEnable    = 0x591,
-        PrimRestartIndex     = 0x592,
-        IndexArrayAddress    = 0x5f2,
-        IndexArrayEndAddr    = 0x5f4,
-        IndexArrayFormat     = 0x5f6,
-        IndexBatchFirst      = 0x5f7,
-        IndexBatchCount      = 0x5f8,
-        VertexArrayNInstance = 0x620,
-        CullFaceEnable       = 0x646,
-        FrontFace            = 0x647,
-        CullFace             = 0x648,
-        ColorMaskN           = 0x680,
-        QueryAddress         = 0x6c0,
-        QuerySequence        = 0x6c2,
-        QueryControl         = 0x6c3,
-        VertexArrayNControl  = 0x700,
-        VertexArrayNAddress  = 0x701,
-        VertexArrayNDivisor  = 0x703,
-        IBlendNSeparateAlpha = 0x780,
-        IBlendNEquationRgb   = 0x781,
-        IBlendNFuncSrcRgb    = 0x782,
-        IBlendNFuncDstRgb    = 0x783,
-        IBlendNEquationAlpha = 0x784,
-        IBlendNFuncSrcAlpha  = 0x785,
-        IBlendNFuncDstAlpha  = 0x786,
-        VertexArrayNEndAddr  = 0x7c0,
-        ShaderNControl       = 0x800,
-        ShaderNOffset        = 0x801,
-        ShaderNMaxGprs       = 0x803,
-        ShaderNType          = 0x804,
-        ConstBufferSize      = 0x8e0,
-        ConstBufferAddress   = 0x8e1,
-        ConstBufferOffset    = 0x8e3,
-        TextureCbIndex       = 0x982
+        FrameBufferNAddress     = 0x200,
+        FrameBufferNWidth       = 0x202,
+        FrameBufferNHeight      = 0x203,
+        FrameBufferNFormat      = 0x204,
+        FrameBufferNBlockDim    = 0x205,
+        FrameBufferNArrayMode   = 0x206,
+        FrameBufferNLayerStride = 0x207,
+        FrameBufferNBaseLayer   = 0x208,
+        ViewportNScaleX         = 0x280,
+        ViewportNScaleY         = 0x281,
+        ViewportNScaleZ         = 0x282,
+        ViewportNTranslateX     = 0x283,
+        ViewportNTranslateY     = 0x284,
+        ViewportNTranslateZ     = 0x285,
+        ViewportNHoriz          = 0x300,
+        ViewportNVert           = 0x301,
+        DepthRangeNNear         = 0x302,
+        DepthRangeNFar          = 0x303,
+        VertexArrayFirst        = 0x35d,
+        VertexArrayCount        = 0x35e,
+        ClearNColor             = 0x360,
+        ClearDepth              = 0x364,
+        ClearStencil            = 0x368,
+        ScissorEnable           = 0x380,
+        ScissorHorizontal       = 0x381,
+        ScissorVertical         = 0x382,
+        StencilBackFuncRef      = 0x3d5,
+        StencilBackMask         = 0x3d6,
+        StencilBackFuncMask     = 0x3d7,
+        ColorMaskCommon         = 0x3e4,
+        RTSeparateFragData      = 0x3eb,
+        ZetaAddress             = 0x3f8,
+        ZetaFormat              = 0x3fa,
+        ZetaBlockDimensions     = 0x3fb,
+        ZetaLayerStride         = 0x3fc,
+        VertexAttribNFormat     = 0x458,
+        RTControl               = 0x487,
+        ZetaHoriz               = 0x48a,
+        ZetaVert                = 0x48b,
+        ZetaArrayMode           = 0x48c,
+        LinkedTsc               = 0x48d,
+        DepthTestEnable         = 0x4b3,
+        BlendIndependent        = 0x4b9,
+        DepthWriteEnable        = 0x4ba,
+        DepthTestFunction       = 0x4c3,
+        BlendSeparateAlpha      = 0x4cf,
+        BlendEquationRgb        = 0x4d0,
+        BlendFuncSrcRgb         = 0x4d1,
+        BlendFuncDstRgb         = 0x4d2,
+        BlendEquationAlpha      = 0x4d3,
+        BlendFuncSrcAlpha       = 0x4d4,
+        BlendFuncDstAlpha       = 0x4d6,
+        BlendEnable             = 0x4d7,
+        IBlendNEnable           = 0x4d8,
+        StencilEnable           = 0x4e0,
+        StencilFrontOpFail      = 0x4e1,
+        StencilFrontOpZFail     = 0x4e2,
+        StencilFrontOpZPass     = 0x4e3,
+        StencilFrontFuncFunc    = 0x4e4,
+        StencilFrontFuncRef     = 0x4e5,
+        StencilFrontFuncMask    = 0x4e6,
+        StencilFrontMask        = 0x4e7,
+        ScreenYControl          = 0x4eb,
+        VertexArrayElemBase     = 0x50d,
+        VertexArrayInstBase     = 0x50e,
+        ZetaEnable              = 0x54e,
+        TexHeaderPoolOffset     = 0x55d,
+        TexSamplerPoolOffset    = 0x557,
+        StencilTwoSideEnable    = 0x565,
+        StencilBackOpFail       = 0x566,
+        StencilBackOpZFail      = 0x567,
+        StencilBackOpZPass      = 0x568,
+        StencilBackFuncFunc     = 0x569,
+        FrameBufferSrgb         = 0x56e,
+        ShaderAddress           = 0x582,
+        VertexBeginGl           = 0x586,
+        PrimRestartEnable       = 0x591,
+        PrimRestartIndex        = 0x592,
+        IndexArrayAddress       = 0x5f2,
+        IndexArrayEndAddr       = 0x5f4,
+        IndexArrayFormat        = 0x5f6,
+        IndexBatchFirst         = 0x5f7,
+        IndexBatchCount         = 0x5f8,
+        VertexArrayNInstance    = 0x620,
+        CullFaceEnable          = 0x646,
+        FrontFace               = 0x647,
+        CullFace                = 0x648,
+        ColorMaskN              = 0x680,
+        QueryAddress            = 0x6c0,
+        QuerySequence           = 0x6c2,
+        QueryControl            = 0x6c3,
+        VertexArrayNControl     = 0x700,
+        VertexArrayNAddress     = 0x701,
+        VertexArrayNDivisor     = 0x703,
+        IBlendNSeparateAlpha    = 0x780,
+        IBlendNEquationRgb      = 0x781,
+        IBlendNFuncSrcRgb       = 0x782,
+        IBlendNFuncDstRgb       = 0x783,
+        IBlendNEquationAlpha    = 0x784,
+        IBlendNFuncSrcAlpha     = 0x785,
+        IBlendNFuncDstAlpha     = 0x786,
+        VertexArrayNEndAddr     = 0x7c0,
+        ShaderNControl          = 0x800,
+        ShaderNOffset           = 0x801,
+        ShaderNMaxGprs          = 0x803,
+        ShaderNType             = 0x804,
+        ConstBufferSize         = 0x8e0,
+        ConstBufferAddress      = 0x8e1,
+        ConstBufferOffset       = 0x8e3,
+        TextureCbIndex          = 0x982
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs
index d89059c0c5..2f1df3d377 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
 using System.Collections.Generic;
@@ -125,29 +126,37 @@ namespace Ryujinx.Graphics.Graphics3d
 
                     if (SrcLinear)
                     {
-                        SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp);
+                        SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp, SrcSizeX, SrcSizeY);
                     }
                     else
                     {
-                        SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight);
+                        SrcSwizzle = new BlockLinearSwizzle(
+                            SrcSizeX,
+                            SrcSizeY, 1,
+                            SrcBlockHeight, 1,
+                            SrcCpp);
                     }
 
                     ISwizzle DstSwizzle;
 
                     if (DstLinear)
                     {
-                        DstSwizzle = new LinearSwizzle(DstPitch, DstCpp);
+                        DstSwizzle = new LinearSwizzle(DstPitch, DstCpp, SrcSizeX, SrcSizeY);
                     }
                     else
                     {
-                        DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight);
+                        DstSwizzle = new BlockLinearSwizzle(
+                            DstSizeX,
+                            DstSizeY, 1,
+                            DstBlockHeight, 1,
+                            DstCpp);
                     }
 
                     for (int Y = 0; Y < YCount; Y++)
                     for (int X = 0; X < XCount; X++)
                     {
-                        int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y);
-                        int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y);
+                        int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y, 0);
+                        int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y, 0);
 
                         long Src = SrcPA + (uint)SrcOffset;
                         long Dst = DstPA + (uint)DstOffset;
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs
index 681552556c..62872ba15c 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
 using System.Collections.Generic;
@@ -119,14 +120,17 @@ namespace Ryujinx.Graphics.Graphics3d
                 }
                 else
                 {
-                    BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight);
+                    BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(
+                        CopyWidth,
+                        CopyHeight, 1,
+                        CopyGobBlockHeight, 1, 1);
 
                     int SrcOffset = 0;
 
                     for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++)
                     for (int X = CopyStartX; X < CopyWidth  && SrcOffset < CopySize; X++)
                     {
-                        int DstOffset = Swizzle.GetSwizzleOffset(X, Y);
+                        int DstOffset = Swizzle.GetSwizzleOffset(X, Y, 0);
 
                         Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]);
                     }
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs b/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs
index 1efa025523..00158dc103 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs
@@ -72,6 +72,7 @@ namespace Ryujinx.Graphics.Texture
 
                 if (BlockZ != 1 || Z != 1)
                 {
+                    // TODO: Support 3D textures?
                     throw new ASTCDecoderException("3D compressed textures unsupported!");
                 }
 
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
index 9451291e9a..1be0644283 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
@@ -1,51 +1,178 @@
+using Ryujinx.Common;
 using System;
 
 namespace Ryujinx.Graphics.Texture
 {
     class BlockLinearSwizzle : ISwizzle
     {
-        private int BhShift;
-        private int BppShift;
+        private const int GobWidth  = 64;
+        private const int GobHeight = 8;
+
+        private const int GobSize = GobWidth * GobHeight;
+
+        private int TexWidth;
+        private int TexHeight;
+        private int TexDepth;
+        private int TexGobBlockHeight;
+        private int TexGobBlockDepth;
+        private int TexBpp;
+
         private int BhMask;
+        private int BdMask;
+
+        private int BhShift;
+        private int BdShift;
+        private int BppShift;
 
         private int XShift;
-        private int GobStride;
 
-        public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16)
+        private int RobSize;
+        private int SliceSize;
+
+        private int BaseOffset;
+
+        public BlockLinearSwizzle(
+            int Width,
+            int Height,
+            int Depth,
+            int GobBlockHeight,
+            int GobBlockDepth,
+            int Bpp)
         {
-            BhMask = (BlockHeight * 8) - 1;
+            TexWidth          = Width;
+            TexHeight         = Height;
+            TexDepth          = Depth;
+            TexGobBlockHeight = GobBlockHeight;
+            TexGobBlockDepth  = GobBlockDepth;
+            TexBpp            = Bpp;
 
-            BhShift  = CountLsbZeros(BlockHeight * 8);
-            BppShift = CountLsbZeros(Bpp);
+            BppShift = BitUtils.CountTrailingZeros32(Bpp);
 
-            int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f);
-
-            GobStride = 512 * BlockHeight * WidthInGobs;
-
-            XShift = CountLsbZeros(512 * BlockHeight);
+            SetMipLevel(0);
         }
 
-        private int CountLsbZeros(int Value)
+        public void SetMipLevel(int Level)
         {
-            int Count = 0;
+            BaseOffset = GetMipOffset(Level);
 
-            while (((Value >> Count) & 1) == 0)
+            int Width  = Math.Max(1, TexWidth  >> Level);
+            int Height = Math.Max(1, TexHeight >> Level);
+            int Depth  = Math.Max(1, TexDepth  >> Level);
+
+            GobBlockSizes GbSizes = AdjustGobBlockSizes(Height, Depth);
+
+            BhMask = GbSizes.Height - 1;
+            BdMask = GbSizes.Depth  - 1;
+
+            BhShift = BitUtils.CountTrailingZeros32(GbSizes.Height);
+            BdShift = BitUtils.CountTrailingZeros32(GbSizes.Depth);
+
+            XShift = BitUtils.CountTrailingZeros32(GobSize * GbSizes.Height * GbSizes.Depth);
+
+            RobAndSliceSizes GsSizes = GetRobAndSliceSizes(Width, Height, GbSizes);
+
+            RobSize   = GsSizes.RobSize;
+            SliceSize = GsSizes.SliceSize;
+        }
+
+        public int GetImageSize(int MipsCount)
+        {
+            int Size = GetMipOffset(MipsCount);
+
+            Size = (Size + 0x1fff) & ~0x1fff;
+
+            return Size;
+        }
+
+        public int GetMipOffset(int Level)
+        {
+            int TotalSize = 0;
+
+            for (int Index = 0; Index < Level; Index++)
             {
-                Count++;
+                int Width  = Math.Max(1, TexWidth  >> Index);
+                int Height = Math.Max(1, TexHeight >> Index);
+                int Depth  = Math.Max(1, TexDepth  >> Index);
+
+                GobBlockSizes GbSizes = AdjustGobBlockSizes(Height, Depth);
+
+                RobAndSliceSizes RsSizes = GetRobAndSliceSizes(Width, Height, GbSizes);
+
+                TotalSize += BitUtils.DivRoundUp(Depth, GbSizes.Depth) * RsSizes.SliceSize;
             }
 
-            return Count;
+            return TotalSize;
         }
 
-        public int GetSwizzleOffset(int X, int Y)
+        private struct GobBlockSizes
+        {
+            public int Height;
+            public int Depth;
+
+            public GobBlockSizes(int GobBlockHeight, int GobBlockDepth)
+            {
+                this.Height = GobBlockHeight;
+                this.Depth  = GobBlockDepth;
+            }
+        }
+
+        private GobBlockSizes AdjustGobBlockSizes(int Height, int Depth)
+        {
+            int GobBlockHeight = TexGobBlockHeight;
+            int GobBlockDepth  = TexGobBlockDepth;
+
+            int Pow2Height = BitUtils.Pow2RoundUp(Height);
+            int Pow2Depth  = BitUtils.Pow2RoundUp(Depth);
+
+            while (GobBlockHeight * GobHeight > Pow2Height && GobBlockHeight > 1)
+            {
+                GobBlockHeight >>= 1;
+            }
+
+            while (GobBlockDepth > Pow2Depth && GobBlockDepth > 1)
+            {
+                GobBlockDepth >>= 1;
+            }
+
+            return new GobBlockSizes(GobBlockHeight, GobBlockDepth);
+        }
+
+        private struct RobAndSliceSizes
+        {
+            public int RobSize;
+            public int SliceSize;
+
+            public RobAndSliceSizes(int RobSize, int SliceSize)
+            {
+                this.RobSize   = RobSize;
+                this.SliceSize = SliceSize;
+            }
+        }
+
+        private RobAndSliceSizes GetRobAndSliceSizes(int Width, int Height, GobBlockSizes GbSizes)
+        {
+            int WidthInGobs = BitUtils.DivRoundUp(Width * TexBpp, GobWidth);
+
+            int RobSize = GobSize * GbSizes.Height * GbSizes.Depth * WidthInGobs;
+
+            int SliceSize = BitUtils.DivRoundUp(Height, GbSizes.Height * GobHeight) * RobSize;
+
+            return new RobAndSliceSizes(RobSize, SliceSize);
+        }
+
+        public int GetSwizzleOffset(int X, int Y, int Z)
         {
             X <<= BppShift;
 
-            int Position = (Y >> BhShift) * GobStride;
+            int YH = Y / GobHeight;
 
-            Position += (X >> 6) << XShift;
+            int Position = (Z >> BdShift) * SliceSize + (YH >> BhShift) * RobSize;
 
-            Position += ((Y & BhMask) >> 3) << 9;
+            Position += (X / GobWidth) << XShift;
+
+            Position += (YH & BhMask) * GobSize;
+
+            Position += ((Z & BdMask) * GobSize) << BhShift;
 
             Position += ((X & 0x3f) >> 5) << 8;
             Position += ((Y & 0x07) >> 1) << 6;
@@ -53,7 +180,7 @@ namespace Ryujinx.Graphics.Texture
             Position += ((Y & 0x01) >> 0) << 4;
             Position += ((X & 0x0f) >> 0) << 0;
 
-            return Position;
+            return BaseOffset + Position;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs
index 583fc20c53..2e0e8aedd4 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs
@@ -2,6 +2,12 @@ namespace Ryujinx.Graphics.Texture
 {
     interface ISwizzle
     {
-        int GetSwizzleOffset(int X, int Y);
+        int GetSwizzleOffset(int X, int Y, int Z);
+
+        void SetMipLevel(int Level);
+
+        int GetMipOffset(int Level);
+
+        int GetImageSize(int MipsCount);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs
index f958e1de81..c4208935c3 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs
@@ -1,4 +1,6 @@
 using ChocolArm64.Memory;
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 using System;
@@ -23,14 +25,16 @@ namespace Ryujinx.Graphics.Texture
             public int BytesPerPixel { get; private set; }
             public int BlockWidth    { get; private set; }
             public int BlockHeight   { get; private set; }
+            public int BlockDepth    { get; private set; }
 
             public TargetBuffer Target { get; private set; }
 
-            public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target)
+            public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, int BlockDepth, TargetBuffer Target)
             {
                 this.BytesPerPixel = BytesPerPixel;
                 this.BlockWidth    = BlockWidth;
                 this.BlockHeight   = BlockHeight;
+                this.BlockDepth    = BlockDepth;
                 this.Target        = Target;
             }
         }
@@ -92,52 +96,52 @@ namespace Ryujinx.Graphics.Texture
         private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
                             new Dictionary<GalImageFormat, ImageDescriptor>()
         {
-            { GalImageFormat.RGBA32,      new ImageDescriptor(16, 1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGBA16,      new ImageDescriptor(8,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RG32,        new ImageDescriptor(8,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGBX8,       new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGBA8,       new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.BGRA8,       new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGB10A2,     new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.R32,         new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGBA4,       new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.BptcSfloat,  new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BptcUfloat,  new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BGR5A1,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGB5A1,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RGB565,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.BGR565,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.BptcUnorm,   new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.RG16,        new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.RG8,         new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.R16,         new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.R8,          new ImageDescriptor(1,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.R11G11B10,   new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
-            { GalImageFormat.BC1,         new ImageDescriptor(8,  4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BC2,         new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BC3,         new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BC4,         new ImageDescriptor(8,  4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.BC5,         new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D4x4,   new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D5x5,   new ImageDescriptor(16, 5,  5,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D6x6,   new ImageDescriptor(16, 6,  6,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D8x8,   new ImageDescriptor(16, 8,  8,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
-            { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
-            { GalImageFormat.Astc2D5x4,   new ImageDescriptor(16, 5,  4,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D6x5,   new ImageDescriptor(16, 6,  5,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D8x6,   new ImageDescriptor(16, 8,  6,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D10x8,  new ImageDescriptor(16, 10, 8,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
-            { GalImageFormat.Astc2D8x5,   new ImageDescriptor(16, 8,  5,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D10x5,  new ImageDescriptor(16, 10, 5,  TargetBuffer.Color) },
-            { GalImageFormat.Astc2D10x6,  new ImageDescriptor(16, 10, 6,  TargetBuffer.Color) },
+            { GalImageFormat.RGBA32,      new ImageDescriptor(16, 1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGBA16,      new ImageDescriptor(8,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RG32,        new ImageDescriptor(8,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGBX8,       new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGBA8,       new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BGRA8,       new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGB10A2,     new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R32,         new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGBA4,       new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BptcSfloat,  new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BptcUfloat,  new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BGR5A1,      new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGB5A1,      new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RGB565,      new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BGR565,      new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BptcUnorm,   new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RG16,        new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.RG8,         new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R16,         new ImageDescriptor(2,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R8,          new ImageDescriptor(1,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R11G11B10,   new ImageDescriptor(4,  1,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC1,         new ImageDescriptor(8,  4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC2,         new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC3,         new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC4,         new ImageDescriptor(8,  4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC5,         new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D4x4,   new ImageDescriptor(16, 4,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D5x5,   new ImageDescriptor(16, 5,  5,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D6x6,   new ImageDescriptor(16, 6,  6,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D8x8,   new ImageDescriptor(16, 8,  8,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, 1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, 1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D5x4,   new ImageDescriptor(16, 5,  4,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D6x5,   new ImageDescriptor(16, 6,  5,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D8x6,   new ImageDescriptor(16, 8,  6,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D10x8,  new ImageDescriptor(16, 10, 8,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, 1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D8x5,   new ImageDescriptor(16, 8,  5,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D10x5,  new ImageDescriptor(16, 10, 5,  1,  TargetBuffer.Color) },
+            { GalImageFormat.Astc2D10x6,  new ImageDescriptor(16, 10, 6,  1,  TargetBuffer.Color) },
 
-            { GalImageFormat.D16,   new ImageDescriptor(2, 1, 1, TargetBuffer.Depth)        },
-            { GalImageFormat.D24,   new ImageDescriptor(4, 1, 1, TargetBuffer.Depth)        },
-            { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
-            { GalImageFormat.D32,   new ImageDescriptor(4, 1, 1, TargetBuffer.Depth)        },
-            { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }
+            { GalImageFormat.D16,   new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Depth)        },
+            { GalImageFormat.D24,   new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth)        },
+            { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.DepthStencil) },
+            { GalImageFormat.D32,   new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth)        },
+            { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.DepthStencil) }
         };
 
         public static GalImageFormat ConvertTexture(
@@ -241,26 +245,37 @@ namespace Ryujinx.Graphics.Texture
 
             ImageDescriptor Desc = GetImageDescriptor(Image.Format);
 
-            (int Width, int Height) = GetImageSizeInBlocks(Image);
+            (int Width, int Height, int Depth) = GetImageSizeInBlocks(Image);
 
             int BytesPerPixel = Desc.BytesPerPixel;
 
             //Note: Each row of the texture needs to be aligned to 4 bytes.
             int Pitch = (Width * BytesPerPixel + 3) & ~3;
 
-            byte[] Data = new byte[Height * Pitch];
 
-            for (int Y = 0; Y < Height; Y++)
+            int DataLayerSize = Height * Pitch * Depth;
+            byte[] Data = new byte[DataLayerSize * Image.LayerCount];
+
+            int TargetMipLevel = Image.MaxMipmapLevel <= 1 ? 1 : Image.MaxMipmapLevel - 1;
+            int LayerOffset = ImageUtils.GetLayerOffset(Image, TargetMipLevel);
+
+            for (int Layer = 0; Layer < Image.LayerCount; Layer++)
             {
-                int OutOffs = Y * Pitch;
-
-                for (int X = 0; X < Width; X++)
+                for (int Z = 0; Z < Depth; Z++)
                 {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+                    for (int Y = 0; Y < Height; Y++)
+                    {
+                        int OutOffs = (DataLayerSize * Layer) + Y * Pitch + (Z * Width * Height * BytesPerPixel);
 
-                    CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
+                        for (int X = 0; X < Width; X++)
+                        {
+                            long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z);
 
-                    OutOffs += BytesPerPixel;
+                            CpuMemory.ReadBytes(Position + (LayerOffset * Layer) + Offset, Data, OutOffs, BytesPerPixel);
+
+                            OutOffs += BytesPerPixel;
+                        }
+                    }
                 }
             }
 
@@ -273,16 +288,17 @@ namespace Ryujinx.Graphics.Texture
 
             ImageDescriptor Desc = GetImageDescriptor(Image.Format);
 
-            (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image);
+            (int Width, int Height, int Depth) = ImageUtils.GetImageSizeInBlocks(Image);
 
             int BytesPerPixel = Desc.BytesPerPixel;
 
             int InOffs = 0;
 
+            for (int Z = 0; Z < Depth; Z++)
             for (int Y = 0; Y < Height; Y++)
             for (int X = 0; X < Width;  X++)
             {
-                long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+                long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y, Z);
 
                 Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel);
 
@@ -290,6 +306,7 @@ namespace Ryujinx.Graphics.Texture
             }
         }
 
+        // TODO: Support non 2D
         public static bool CopyTexture(
             NvGpuVmm Vmm,
             GalImage SrcImage,
@@ -318,8 +335,8 @@ namespace Ryujinx.Graphics.Texture
             for (int Y = 0; Y < Height; Y++)
             for (int X = 0; X < Width;  X++)
             {
-                long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y);
-                long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y);
+                long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y, 0);
+                long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y, 0);
 
                 byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel);
 
@@ -333,10 +350,41 @@ namespace Ryujinx.Graphics.Texture
         {
             ImageDescriptor Desc = GetImageDescriptor(Image.Format);
 
+            int ComponentCount = GetCoordsCountTextureTarget(Image.TextureTarget);
+
+            if (IsArray(Image.TextureTarget))
+                ComponentCount--;
+
             int Width  = DivRoundUp(Image.Width,  Desc.BlockWidth);
             int Height = DivRoundUp(Image.Height, Desc.BlockHeight);
+            int Depth  = DivRoundUp(Image.Depth,  Desc.BlockDepth);
 
-            return Desc.BytesPerPixel * Width * Height;
+            switch (ComponentCount)
+            {
+                case 1:
+                    return Desc.BytesPerPixel * Width * Image.LayerCount;
+                case 2:
+                    return Desc.BytesPerPixel * Width * Height * Image.LayerCount;
+                case 3:
+                    return Desc.BytesPerPixel * Width * Height * Depth * Image.LayerCount;
+                default:
+                    throw new InvalidOperationException($"Invalid component count: {ComponentCount}");
+            }
+        }
+
+        public static int GetGpuSize(GalImage Image, bool forcePitch = false)
+        {
+            return TextureHelper.GetSwizzle(Image).GetImageSize(Image.MaxMipmapLevel) * Image.LayerCount;
+        }
+
+        public static int GetLayerOffset(GalImage Image, int MipLevel)
+        {
+            if (MipLevel <= 0)
+            {
+                MipLevel = 1;
+            }
+
+            return TextureHelper.GetSwizzle(Image).GetMipOffset(MipLevel);
         }
 
         public static int GetPitch(GalImageFormat Format, int Width)
@@ -360,6 +408,11 @@ namespace Ryujinx.Graphics.Texture
             return GetImageDescriptor(Format).BlockHeight;
         }
 
+        public static int GetBlockDepth(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).BlockDepth;
+        }
+
         public static int GetAlignedWidth(GalImage Image)
         {
             ImageDescriptor Desc = GetImageDescriptor(Image.Format);
@@ -378,12 +431,13 @@ namespace Ryujinx.Graphics.Texture
             return (Image.Width + AlignMask) & ~AlignMask;
         }
 
-        public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image)
+        public static (int Width, int Height, int Depth) GetImageSizeInBlocks(GalImage Image)
         {
             ImageDescriptor Desc = GetImageDescriptor(Image.Format);
 
             return (DivRoundUp(Image.Width,  Desc.BlockWidth),
-                    DivRoundUp(Image.Height, Desc.BlockHeight));
+                    DivRoundUp(Image.Height, Desc.BlockHeight),
+                    DivRoundUp(Image.Depth, Desc.BlockDepth));
         }
 
         public static int GetBytesPerPixel(GalImageFormat Format)
@@ -443,5 +497,66 @@ namespace Ryujinx.Graphics.Texture
                 default: throw new NotImplementedException(((int)Type).ToString());
             }
         }
+
+        public static TextureTarget GetTextureTarget(GalTextureTarget GalTextureTarget)
+        {
+            switch (GalTextureTarget)
+            {
+                case GalTextureTarget.OneD:
+                    return TextureTarget.Texture1D;
+                case GalTextureTarget.TwoD:
+                case GalTextureTarget.TwoDNoMipMap:
+                    return TextureTarget.Texture2D;
+                case GalTextureTarget.ThreeD:
+                    return TextureTarget.Texture3D;
+                case GalTextureTarget.OneDArray:
+                    return TextureTarget.Texture1DArray;
+                case GalTextureTarget.OneDBuffer:
+                    return TextureTarget.TextureBuffer;
+                case GalTextureTarget.TwoDArray:
+                    return TextureTarget.Texture2DArray;
+                case GalTextureTarget.CubeMap:
+                    return TextureTarget.TextureCubeMap;
+                case GalTextureTarget.CubeArray:
+                    return TextureTarget.TextureCubeMapArray;
+                default:
+                    throw new NotSupportedException($"Texture target {GalTextureTarget} currently not supported!");
+            }
+        }
+
+        public static bool IsArray(GalTextureTarget TextureTarget)
+        {
+            switch (TextureTarget)
+            {
+                case GalTextureTarget.OneDArray:
+                case GalTextureTarget.TwoDArray:
+                case GalTextureTarget.CubeArray:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public static int GetCoordsCountTextureTarget(GalTextureTarget TextureTarget)
+        {
+            switch (TextureTarget)
+            {
+                case GalTextureTarget.OneD:
+                    return 1;
+                case GalTextureTarget.OneDArray:
+                case GalTextureTarget.OneDBuffer:
+                case GalTextureTarget.TwoD:
+                case GalTextureTarget.TwoDNoMipMap:
+                    return 2;
+                case GalTextureTarget.ThreeD:
+                case GalTextureTarget.TwoDArray:
+                case GalTextureTarget.CubeMap:
+                    return 3;
+                case GalTextureTarget.CubeArray:
+                    return 4;
+                default:
+                    throw new NotImplementedException($"TextureTarget.{TextureTarget} not implemented yet.");
+            }
+        }
     }
 }
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs
index ef468e27b5..e6509baa6a 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Ryujinx.Graphics.Texture
 {
     class LinearSwizzle : ISwizzle
@@ -5,15 +7,39 @@ namespace Ryujinx.Graphics.Texture
         private int Pitch;
         private int Bpp;
 
-        public LinearSwizzle(int Pitch, int Bpp)
+        private int SliceSize;
+
+        public LinearSwizzle(int Pitch, int Bpp, int Width, int Height)
         {
-            this.Pitch = Pitch;
-            this.Bpp   = Bpp;
+            this.Pitch  = Pitch;
+            this.Bpp    = Bpp;
+            SliceSize   = Width * Height * Bpp;
         }
 
-        public int GetSwizzleOffset(int X, int Y)
+        public void SetMipLevel(int Level)
         {
-            return X * Bpp + Y * Pitch;
+            throw new NotImplementedException();
+        }
+
+        public int GetMipOffset(int Level)
+        {
+            if (Level == 1)
+                return SliceSize;
+            throw new NotImplementedException();
+        }
+
+        public int GetImageSize(int MipsCount)
+        {
+            int Size = GetMipOffset(MipsCount);
+
+            Size = (Size + 0x1fff) & ~0x1fff;
+
+            return Size;
+        }
+
+        public int GetSwizzleOffset(int X, int Y, int Z)
+        {
+            return Z * SliceSize + X * Bpp + Y * Pitch;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs
index 1f2d625ec4..a2ce86f56d 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs
@@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Texture
 
             GalImageFormat Format = GetImageFormat(Tic);
 
+            GalTextureTarget TextureTarget = (GalTextureTarget)((Tic[4] >> 23) & 0xF);
+
             GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
             GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
             GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
@@ -19,6 +21,8 @@ namespace Ryujinx.Graphics.Texture
 
             TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
 
+            int MaxMipmapLevel = (Tic[3] >> 28) & 0xF + 1;
+
             GalMemoryLayout Layout;
 
             if (Swizzle == TextureSwizzle.BlockLinear ||
@@ -31,22 +35,61 @@ namespace Ryujinx.Graphics.Texture
                 Layout = GalMemoryLayout.Pitch;
             }
 
-            int BlockHeightLog2 = (Tic[3] >> 3)  & 7;
-            int TileWidthLog2   = (Tic[3] >> 10) & 7;
+            int GobBlockHeightLog2 = (Tic[3] >> 3)  & 7;
+            int GobBlockDepthLog2  = (Tic[3] >> 6)  & 7;
+            int TileWidthLog2      = (Tic[3] >> 10) & 7;
 
-            int BlockHeight = 1 << BlockHeightLog2;
-            int TileWidth   = 1 << TileWidthLog2;
+            int GobBlockHeight = 1 << GobBlockHeightLog2;
+            int GobBlockDepth  = 1 << GobBlockDepthLog2;
+            int TileWidth      = 1 << TileWidthLog2;
 
-            int Width  = (Tic[4] & 0xffff) + 1;
-            int Height = (Tic[5] & 0xffff) + 1;
+            int Width  = ((Tic[4] >> 0)  & 0xffff) + 1;
+            int Height = ((Tic[5] >> 0)  & 0xffff) + 1;
+            int Depth  = ((Tic[5] >> 16) & 0x3fff) + 1;
+
+            int LayoutCount = 1;
+
+            // TODO: check this
+            if (ImageUtils.IsArray(TextureTarget))
+            {
+                LayoutCount = Depth;
+                Depth = 1;
+            }
+
+            if (TextureTarget == GalTextureTarget.OneD)
+            {
+                Height = 1;
+            }
+
+            if (TextureTarget == GalTextureTarget.TwoD || TextureTarget == GalTextureTarget.OneD)
+            {
+                Depth = 1;
+            }
+            else if (TextureTarget == GalTextureTarget.CubeMap)
+            {
+                // FIXME: This is a bit hacky but I guess it's fine for now
+                LayoutCount = 6;
+                Depth = 1;
+            }
+            else if (TextureTarget == GalTextureTarget.CubeArray)
+            {
+                // FIXME: This is a really really hacky but I guess it's fine for now
+                LayoutCount *= 6;
+                Depth = 1;
+            }
 
             GalImage Image = new GalImage(
                 Width,
                 Height,
+                Depth,
+                LayoutCount,
                 TileWidth,
-                BlockHeight,
+                GobBlockHeight,
+                GobBlockDepth,
                 Layout,
                 Format,
+                TextureTarget,
+                MaxMipmapLevel,
                 XSource,
                 YSource,
                 ZSource,
@@ -68,6 +111,10 @@ namespace Ryujinx.Graphics.Texture
             GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
             GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
 
+            bool DepthCompare = ((Tsc[0] >> 9) & 1) == 1;
+
+            DepthCompareFunc DepthCompareFunc = (DepthCompareFunc)((Tsc[0] >> 10) & 7);
+
             GalTextureFilter    MagFilter = (GalTextureFilter)   ((Tsc[1] >> 0) & 3);
             GalTextureFilter    MinFilter = (GalTextureFilter)   ((Tsc[1] >> 4) & 3);
             GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
@@ -85,7 +132,9 @@ namespace Ryujinx.Graphics.Texture
                 MinFilter,
                 MagFilter,
                 MipFilter,
-                BorderColor);
+                BorderColor,
+                DepthCompare,
+                DepthCompareFunc);
         }
 
         private static GalImageFormat GetImageFormat(int[] Tic)
diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs
index 6ac91d8b59..33ccb0aa51 100644
--- a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs
+++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs
@@ -1,4 +1,5 @@
 using ChocolArm64.Memory;
+using Ryujinx.Common;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 
@@ -9,9 +10,13 @@ namespace Ryujinx.Graphics.Texture
         public static ISwizzle GetSwizzle(GalImage Image)
         {
             int BlockWidth    = ImageUtils.GetBlockWidth   (Image.Format);
+            int BlockHeight   = ImageUtils.GetBlockHeight  (Image.Format);
+            int BlockDepth    = ImageUtils.GetBlockDepth   (Image.Format);
             int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format);
 
-            int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth;
+            int Width  = BitUtils.DivRoundUp(Image.Width,  BlockWidth);
+            int Height = BitUtils.DivRoundUp(Image.Height, BlockHeight);
+            int Depth  = BitUtils.DivRoundUp(Image.Depth,  BlockDepth);
 
             if (Image.Layout == GalMemoryLayout.BlockLinear)
             {
@@ -19,11 +24,17 @@ namespace Ryujinx.Graphics.Texture
 
                 Width = (Width + AlignMask) & ~AlignMask;
 
-                return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight);
+                return new BlockLinearSwizzle(
+                    Width,
+                    Height,
+                    Depth,
+                    Image.GobBlockHeight,
+                    Image.GobBlockDepth,
+                    BytesPerPixel);
             }
             else
             {
-                return new LinearSwizzle(Image.Pitch, BytesPerPixel);
+                return new LinearSwizzle(Image.Pitch, BytesPerPixel, Width, Height);
             }
         }
 
diff --git a/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs b/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs
new file mode 100644
index 0000000000..bcb64af0be
--- /dev/null
+++ b/Ryujinx.Graphics/Texture/TextureInstructionSuffix.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Ryujinx.Graphics.Texture
+{
+    [Flags]
+    public enum TextureInstructionSuffix
+    {
+        None  = 0x00,  // No Modifier
+        LZ    = 0x02,  // Load LOD Zero
+        LB    = 0x08,  // Load Bias
+        LL    = 0x10,  // Load LOD
+        LBA   = 0x20,  // Load Bias with OperA? Auto?
+        LLA   = 0x40,  // Load LOD with OperA? Auto?
+        DC    = 0x80,  // Depth Compare
+        AOffI = 0x100, // Offset
+        MZ    = 0x200, // Multisample Zero?
+        PTP   = 0x400  // ???
+    }
+}
diff --git a/Ryujinx.Graphics/VDec/VideoDecoder.cs b/Ryujinx.Graphics/VDec/VideoDecoder.cs
index 847392b0d9..be53b1a02c 100644
--- a/Ryujinx.Graphics/VDec/VideoDecoder.cs
+++ b/Ryujinx.Graphics/VDec/VideoDecoder.cs
@@ -216,10 +216,11 @@ namespace Ryujinx.Graphics.VDec
 
             GalImage Image = new GalImage(
                 OutputConfig.SurfaceWidth,
-                OutputConfig.SurfaceHeight, 1,
-                OutputConfig.GobBlockHeight,
+                OutputConfig.SurfaceHeight, 1, 1, 1,
+                OutputConfig.GobBlockHeight, 1,
                 GalMemoryLayout.BlockLinear,
-                GalImageFormat.RGBA8 | GalImageFormat.Unorm);
+                GalImageFormat.RGBA8 | GalImageFormat.Unorm,
+                GalTextureTarget.TwoD);
 
             ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
index db04f47cd0..dbf255beee 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
@@ -1,6 +1,7 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
 using Ryujinx.HLE.HOS.Services.Nv.NvMap;
@@ -415,9 +416,10 @@ namespace Ryujinx.HLE.HOS.Services.Android
                 {
                     image = new GalImage(
                         fbWidth,
-                        fbHeight, 1, BlockHeight,
+                        fbHeight, 1, 1, 1, BlockHeight, 1,
                         GalMemoryLayout.BlockLinear,
-                        imageFormat);
+                        imageFormat,
+                        GalTextureTarget.TwoD);
                 }
 
                 context.Device.Gpu.ResourceManager.ClearPbCache();
diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs
index 30fa71aea2..77aba0abe6 100644
--- a/Ryujinx.ShaderTools/Program.cs
+++ b/Ryujinx.ShaderTools/Program.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.ShaderTools
         {
             if (args.Length == 2)
             {
-                GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize);
+                GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize, true);
 
                 GalShaderType ShaderType = GalShaderType.Vertex;