Просмотр исходного кода

Allow "reinterpretation" of framebuffer/zeta formats (#418)

* (Re)Implement format reinterpretation, other changes

* Implement writeback to guest memory, some refactoring

* More refactoring, implement reinterpretation the old way again

* Clean up

* Some fixes on M2MF (old Dma engine), added partial support for P2MF, fix conditional ssy, add Z24S8 zeta format, other fixes

* nit: Formatting

* Address PR feedback
gdkchan 7 лет назад
Родитель
Сommit
d4187aaa9d
52 измененных файлов с 1571 добавлено и 1956 удалено
  1. 29 0
      ChocolArm64/Memory/AMemory.cs
  2. 39 9
      Ryujinx.Graphics/Gal/GalImage.cs
  3. 8 0
      Ryujinx.Graphics/Gal/GalMemoryLayout.cs
  4. 2 0
      Ryujinx.Graphics/Gal/GalPipelineState.cs
  5. 0 0
      Ryujinx.Graphics/Gal/GalSurfaceFormat.cs
  6. 1 0
      Ryujinx.Graphics/Gal/GalTextureFormat.cs
  7. 1 1
      Ryujinx.Graphics/Gal/IGalRasterizer.cs
  8. 4 14
      Ryujinx.Graphics/Gal/IGalRenderTarget.cs
  9. 4 4
      Ryujinx.Graphics/Gal/IGalTexture.cs
  10. 5 138
      Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
  11. 3 3
      Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs
  12. 2 2
      Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
  13. 11 0
      Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
  14. 3 3
      Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
  15. 141 195
      Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
  16. 75 86
      Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
  17. 1 0
      Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
  18. 64 64
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
  19. 7 17
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
  20. 1 1
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs
  21. 0 2
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
  22. 8 8
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
  23. 21 21
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
  24. 2 4
      Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs
  25. 22 22
      Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
  26. 3 3
      Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
  27. 122 0
      Ryujinx.Graphics/GpuResourceManager.cs
  28. 33 260
      Ryujinx.Graphics/Memory/NvGpuVmmCache.cs
  29. 12 7
      Ryujinx.Graphics/NvGpu.cs
  30. 1 1
      Ryujinx.Graphics/NvGpuBufferType.cs
  31. 2 2
      Ryujinx.Graphics/NvGpuEngine.cs
  32. 40 91
      Ryujinx.Graphics/NvGpuEngine2d.cs
  33. 46 77
      Ryujinx.Graphics/NvGpuEngine3d.cs
  34. 2 0
      Ryujinx.Graphics/NvGpuEngine3dReg.cs
  35. 0 143
      Ryujinx.Graphics/NvGpuEngineDma.cs
  36. 192 0
      Ryujinx.Graphics/NvGpuEngineM2mf.cs
  37. 4 1
      Ryujinx.Graphics/NvGpuEngineM2mfReg.cs
  38. 102 0
      Ryujinx.Graphics/NvGpuEngineP2mf.cs
  39. 17 0
      Ryujinx.Graphics/NvGpuEngineP2mfReg.cs
  40. 35 30
      Ryujinx.Graphics/NvGpuFifo.cs
  41. 195 169
      Ryujinx.Graphics/Texture/ImageUtils.cs
  42. 14 38
      Ryujinx.Graphics/Texture/TextureFactory.cs
  43. 13 16
      Ryujinx.Graphics/Texture/TextureHelper.cs
  44. 0 60
      Ryujinx.Graphics/Texture/TextureInfo.cs
  45. 0 398
      Ryujinx.Graphics/Texture/TextureReader.cs
  46. 0 35
      Ryujinx.Graphics/Texture/TextureWriter.cs
  47. 17 0
      Ryujinx.Graphics/ValueRange.cs
  48. 234 0
      Ryujinx.Graphics/ValueRangeSet.cs
  49. 2 1
      Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
  50. 0 1
      Ryujinx.HLE/HOS/Services/Acc/IProfile.cs
  51. 7 5
      Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs
  52. 24 24
      Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs

+ 29 - 0
ChocolArm64/Memory/AMemory.cs

@@ -287,6 +287,14 @@ namespace ChocolArm64.Memory
             return Data;
         }
 
+        public void ReadBytes(long Position, byte[] Data, int StartIndex, int Size)
+        {
+            //Note: This will be moved later.
+            EnsureRangeIsValid(Position, (uint)Size);
+
+            Marshal.Copy((IntPtr)Translate(Position), Data, StartIndex, Size);
+        }
+
         public void WriteSByte(long Position, sbyte Value)
         {
             WriteByte(Position, (byte)Value);
@@ -403,6 +411,27 @@ namespace ChocolArm64.Memory
             Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length);
         }
 
+        public void WriteBytes(long Position, byte[] Data, int StartIndex, int Size)
+        {
+            //Note: This will be moved later.
+            //Using Translate instead of TranslateWrite is on purpose.
+            EnsureRangeIsValid(Position, (uint)Size);
+
+            Marshal.Copy(Data, StartIndex, (IntPtr)Translate(Position), Size);
+        }
+
+        public void CopyBytes(long Src, long Dst, long Size)
+        {
+            //Note: This will be moved later.
+            EnsureRangeIsValid(Src, Size);
+            EnsureRangeIsValid(Dst, Size);
+
+            byte* SrcPtr = Translate(Src);
+            byte* DstPtr = TranslateWrite(Dst);
+
+            Buffer.MemoryCopy(SrcPtr, DstPtr, Size, Size);
+        }
+
         public void Map(long VA, long PA, long Size)
         {
             SetPTEntries(VA, RamPtr + PA, Size);

+ 39 - 9
Ryujinx.Graphics/Gal/GalImage.cs

@@ -1,12 +1,17 @@
+using Ryujinx.Graphics.Texture;
+
 namespace Ryujinx.Graphics.Gal
 {
     public struct GalImage
     {
         public int Width;
         public int Height;
+        public int TileWidth;
+        public int GobBlockHeight;
+        public int Pitch;
 
-        public GalImageFormat Format;
-
+        public GalImageFormat   Format;
+        public GalMemoryLayout  Layout;
         public GalTextureSource XSource;
         public GalTextureSource YSource;
         public GalTextureSource ZSource;
@@ -15,19 +20,44 @@ namespace Ryujinx.Graphics.Gal
         public GalImage(
             int              Width,
             int              Height,
+            int              TileWidth,
+            int              GobBlockHeight,
+            GalMemoryLayout  Layout,
             GalImageFormat   Format,
             GalTextureSource XSource = GalTextureSource.Red,
             GalTextureSource YSource = GalTextureSource.Green,
             GalTextureSource ZSource = GalTextureSource.Blue,
             GalTextureSource WSource = GalTextureSource.Alpha)
         {
-            this.Width   = Width;
-            this.Height  = Height;
-            this.Format  = Format;
-            this.XSource = XSource;
-            this.YSource = YSource;
-            this.ZSource = ZSource;
-            this.WSource = WSource;
+            this.Width          = Width;
+            this.Height         = Height;
+            this.TileWidth      = TileWidth;
+            this.GobBlockHeight = GobBlockHeight;
+            this.Layout         = Layout;
+            this.Format         = Format;
+            this.XSource        = XSource;
+            this.YSource        = YSource;
+            this.ZSource        = ZSource;
+            this.WSource        = WSource;
+
+            Pitch = ImageUtils.GetPitch(Format, Width);
+        }
+
+        public bool SizeMatches(GalImage Image)
+        {
+            if (ImageUtils.GetBytesPerPixel(Format) !=
+                ImageUtils.GetBytesPerPixel(Image.Format))
+            {
+                return false;
+            }
+
+            if (ImageUtils.GetAlignedWidth(this) !=
+                ImageUtils.GetAlignedWidth(Image))
+            {
+                return false;
+            }
+
+            return Height == Image.Height;
         }
     }
 }

+ 8 - 0
Ryujinx.Graphics/Gal/GalMemoryLayout.cs

@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalMemoryLayout
+    {
+        BlockLinear = 0,
+        Pitch       = 1
+    }
+}

+ 2 - 0
Ryujinx.Graphics/Gal/GalPipelineState.cs

@@ -21,6 +21,8 @@
 
         public GalVertexBinding[] VertexBindings;
 
+        public bool FramebufferSrgb;
+
         public float FlipX;
         public float FlipY;
 

+ 0 - 0
Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs → Ryujinx.Graphics/Gal/GalSurfaceFormat.cs


+ 1 - 0
Ryujinx.Graphics/Gal/GalTextureFormat.cs

@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gal
         R32G32       = 0x4,
         A8B8G8R8     = 0x8,
         A2B10G10R10  = 0x9,
+        R16G16       = 0xc,
         R32          = 0xf,
         BC6H_SF16    = 0x10,
         BC6H_UF16    = 0x11,

+ 1 - 1
Ryujinx.Graphics/Gal/IGalRasterizer.cs

@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gal
 
         void SetIndexArray(int Size, GalIndexFormat Format);
 
-        void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
+        void DrawArrays(int First, int Count, GalPrimitiveType PrimType);
 
         void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType);
     }

+ 4 - 14
Ryujinx.Graphics/Gal/IGalRenderTarget.cs

@@ -1,23 +1,17 @@
-using System;
-
 namespace Ryujinx.Graphics.Gal
 {
     public interface IGalRenderTarget
     {
-        void BindColor(long Key, int Attachment);
+        void BindColor(long Key, int Attachment, GalImage Image);
 
         void UnbindColor(int Attachment);
 
-        void BindZeta(long Key);
+        void BindZeta(long Key, GalImage Image);
 
         void UnbindZeta();
 
-        void BindTexture(long Key, int Index);
-
         void Set(long Key);
 
-        void Set(byte[] Data, int Width, int Height);
-
         void SetMap(int[] Map);
 
         void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom);
@@ -40,12 +34,8 @@ namespace Ryujinx.Graphics.Gal
             int  DstX1,
             int  DstY1);
 
-        void GetBufferData(long Key, Action<byte[]> Callback);
+        void Reinterpret(long Key, GalImage NewImage);
 
-        void SetBufferData(
-            long             Key,
-            int              Width,
-            int              Height,
-            byte[]           Buffer);
+        byte[] GetData(long Key);
     }
 }

+ 4 - 4
Ryujinx.Graphics/Gal/IGalTexture.cs

@@ -5,13 +5,13 @@ namespace Ryujinx.Graphics.Gal
         void LockCache();
         void UnlockCache();
 
-        void Create(long Key, byte[] Data, GalImage Image);
+        void Create(long Key, int Size, GalImage Image);
 
-        void CreateFb(long Key, long Size, GalImage Image);
+        void Create(long Key, byte[] Data, GalImage Image);
 
-        bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image);
+        bool TryGetImage(long Key, out GalImage Image);
 
-        void Bind(long Key, int Index);
+        void Bind(long Key, int Index, GalImage Image);
 
         void SetSampler(GalTextureSampler Sampler);
     }

+ 5 - 138
Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs

@@ -1,14 +1,9 @@
-using OpenTK.Graphics.OpenGL;
-using Ryujinx.Graphics.Texture;
-using System;
+using Ryujinx.Graphics.Texture;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
     class ImageHandler
     {
-        private static int CopyBuffer = 0;
-        private static int CopyBufferSize = 0;
-
         public GalImage Image { get; private set; }
 
         public int Width  => Image.Width;
@@ -16,144 +11,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public GalImageFormat Format => Image.Format;
 
-        public PixelInternalFormat InternalFormat { get; private set; }
-        public PixelFormat         PixelFormat    { get; private set; }
-        public PixelType           PixelType      { get; private set; }
-
         public int Handle { get; private set; }
 
-        private bool Initialized;
-
-        public ImageHandler()
-        {
-            Handle = GL.GenTexture();
-        }
+        public bool HasColor   => ImageUtils.HasColor(Image.Format);
+        public bool HasDepth   => ImageUtils.HasDepth(Image.Format);
+        public bool HasStencil => ImageUtils.HasStencil(Image.Format);
 
         public ImageHandler(int Handle, GalImage Image)
         {
             this.Handle = Handle;
-
-            this.Image = Image;
-        }
-
-        public void EnsureSetup(GalImage NewImage)
-        {
-            if (Width  == NewImage.Width  &&
-                Height == NewImage.Height &&
-                Format == NewImage.Format &&
-                Initialized)
-            {
-                return;
-            }
-
-            PixelInternalFormat InternalFmt;
-            PixelFormat         PixelFormat;
-            PixelType           PixelType;
-
-            if (ImageUtils.IsCompressed(NewImage.Format))
-            {
-                InternalFmt = (PixelInternalFormat)OGLEnumConverter.GetCompressedImageFormat(NewImage.Format);
-
-                PixelFormat = default(PixelFormat);
-                PixelType   = default(PixelType);
-            }
-            else
-            {
-                (InternalFmt, PixelFormat, PixelType) = OGLEnumConverter.GetImageFormat(NewImage.Format);
-            }
-
-            GL.BindTexture(TextureTarget.Texture2D, Handle);
-
-            if (Initialized)
-            {
-                if (CopyBuffer == 0)
-                {
-                    CopyBuffer = GL.GenBuffer();
-                }
-
-                int CurrentSize = Math.Max(ImageUtils.GetSize(NewImage),
-                                           ImageUtils.GetSize(Image));
-
-                GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
-                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
-
-                if (CopyBufferSize < CurrentSize)
-                {
-                    CopyBufferSize = CurrentSize;
-
-                    GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
-                }
-
-                if (ImageUtils.IsCompressed(Image.Format))
-                {
-                    GL.GetCompressedTexImage(TextureTarget.Texture2D, 0, IntPtr.Zero);
-                }
-                else
-                {
-                    GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);
-                }
-
-                GL.DeleteTexture(Handle);
-
-                Handle = GL.GenTexture();
-
-                GL.BindTexture(TextureTarget.Texture2D, Handle);
-            }
-
-            const int MinFilter = (int)TextureMinFilter.Linear;
-            const int MagFilter = (int)TextureMagFilter.Linear;
-
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
-
-            const int Level = 0;
-            const int Border = 0;
-
-            if (ImageUtils.IsCompressed(NewImage.Format))
-            {
-                Console.WriteLine("Hit");
-
-                GL.CompressedTexImage2D(
-                    TextureTarget.Texture2D,
-                    Level,
-                    (InternalFormat)InternalFmt,
-                    NewImage.Width,
-                    NewImage.Height,
-                    Border,
-                    ImageUtils.GetSize(NewImage),
-                    IntPtr.Zero);
-            }
-            else
-            {
-                GL.TexImage2D(
-                    TextureTarget.Texture2D,
-                    Level,
-                    InternalFmt,
-                    NewImage.Width,
-                    NewImage.Height,
-                    Border,
-                    PixelFormat,
-                    PixelType,
-                    IntPtr.Zero);
-            }
-
-            if (Initialized)
-            {
-                GL.BindBuffer(BufferTarget.PixelPackBuffer,   0);
-                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
-            }
-
-            Image = NewImage;
-
-            this.InternalFormat = InternalFmt;
-            this.PixelFormat = PixelFormat;
-            this.PixelType = PixelType;
-
-            Initialized = true;
+            this.Image  = Image;
         }
-
-        public bool HasColor   => ImageUtils.HasColor(Image.Format);
-        public bool HasDepth   => ImageUtils.HasDepth(Image.Format);
-        public bool HasStencil => ImageUtils.HasStencil(Image.Format);
     }
 }

+ 3 - 3
Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs

@@ -22,9 +22,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             public CacheBucket(T Value, long DataSize, LinkedListNode<long> Node)
             {
-                this.Value     = Value;
-                this.DataSize  = DataSize;
-                this.Node      = Node;
+                this.Value    = Value;
+                this.DataSize = DataSize;
+                this.Node     = Node;
 
                 Timestamp = Environment.TickCount;
             }

+ 2 - 2
Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs

@@ -149,8 +149,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 case GalImageFormat.R32          | GalImageFormat.Sfloat: return (PixelInternalFormat.R32f,         PixelFormat.Red,         PixelType.Float);
                 case GalImageFormat.R32          | GalImageFormat.Sint:   return (PixelInternalFormat.R32i,         PixelFormat.Red,         PixelType.Int);
                 case GalImageFormat.R32          | GalImageFormat.Uint:   return (PixelInternalFormat.R32ui,        PixelFormat.Red,         PixelType.UnsignedInt);
-                case GalImageFormat.A1R5G5B5     | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgb5A1,       PixelFormat.Rgba,        PixelType.UnsignedShort5551);
-                case GalImageFormat.B5G6R5       | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgba,         PixelFormat.Rgb,         PixelType.UnsignedShort565);
+                case GalImageFormat.A1R5G5B5     | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgb5A1,       PixelFormat.Rgba,        PixelType.UnsignedShort1555Reversed);
+                case GalImageFormat.B5G6R5       | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgba,         PixelFormat.Rgb,         PixelType.UnsignedShort565Reversed);
                 case GalImageFormat.R16G16       | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg16f,        PixelFormat.Rg,          PixelType.HalfFloat);
                 case GalImageFormat.R16G16       | GalImageFormat.Sint:   return (PixelInternalFormat.Rg16i,        PixelFormat.RgInteger,   PixelType.Short);
                 case GalImageFormat.R16G16       | GalImageFormat.Snorm:  return (PixelInternalFormat.Rg16Snorm,    PixelFormat.Rg,          PixelType.Byte);

+ 11 - 0
Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs

@@ -126,6 +126,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             BindVertexLayout(New);
 
+            if (New.FramebufferSrgb != Old.FramebufferSrgb)
+            {
+                Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb);
+            }
+
             if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance)
             {
                 Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
@@ -337,6 +342,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
                 foreach (GalVertexAttrib Attrib in Binding.Attribs)
                 {
+                    //Skip uninitialized attributes.
+                    if (Attrib.Size == 0)
+                    {
+                        continue;
+                    }
+
                     GL.EnableVertexAttribArray(Attrib.Index);
 
                     GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);

+ 3 - 3
Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs

@@ -113,14 +113,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             IndexBuffer.ElemSizeLog2 = (int)Format;
         }
 
-        public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType)
+        public void DrawArrays(int First, int Count, GalPrimitiveType PrimType)
         {
-            if (PrimCount == 0)
+            if (Count == 0)
             {
                 return;
             }
 
-            GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
+            GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
         }
 
         public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)

+ 141 - 195
Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs

@@ -15,9 +15,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             public Rect(int X, int Y, int Width, int Height)
             {
-                this.X = X;
-                this.Y = Y;
-                this.Width = Width;
+                this.X      = X;
+                this.Y      = Y;
+                this.Width  = Width;
                 this.Height = Height;
             }
         }
@@ -29,7 +29,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         private OGLTexture Texture;
 
-        private ImageHandler RawTex;
         private ImageHandler ReadTex;
 
         private Rect Viewport;
@@ -64,13 +63,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             this.Texture = Texture;
         }
 
-        public void BindColor(long Key, int Attachment)
+        public void BindColor(long Key, int Attachment, GalImage Image)
         {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
+            if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
             {
                 EnsureFrameBuffer();
 
-                Attach(ref ColorAttachments[Attachment], Tex.Handle, FramebufferAttachment.ColorAttachment0 + Attachment);
+                Attach(ref ColorAttachments[Attachment], CachedImage.Handle, FramebufferAttachment.ColorAttachment0 + Attachment);
             }
             else
             {
@@ -84,40 +83,39 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment);
         }
-        
-        public void BindZeta(long Key)
+
+        public void BindZeta(long Key, GalImage Image)
         {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
+            if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
             {
                 EnsureFrameBuffer();
 
-                if (Tex.HasDepth && Tex.HasStencil)
+                if (CachedImage.HasDepth && CachedImage.HasStencil)
                 {
-                    if (DepthAttachment   != Tex.Handle ||
-                        StencilAttachment != Tex.Handle)
+                    if (DepthAttachment   != CachedImage.Handle ||
+                        StencilAttachment != CachedImage.Handle)
                     {
                         GL.FramebufferTexture(
                             FramebufferTarget.DrawFramebuffer,
                             FramebufferAttachment.DepthStencilAttachment,
-                            Tex.Handle,
+                            CachedImage.Handle,
                             0);
 
-                        DepthAttachment = Tex.Handle;
-
-                        StencilAttachment = Tex.Handle;
+                        DepthAttachment   = CachedImage.Handle;
+                        StencilAttachment = CachedImage.Handle;
                     }
                 }
-                else if (Tex.HasDepth)
+                else if (CachedImage.HasDepth)
                 {
-                    Attach(ref DepthAttachment, Tex.Handle, FramebufferAttachment.DepthAttachment);
+                    Attach(ref DepthAttachment, CachedImage.Handle, FramebufferAttachment.DepthAttachment);
 
                     Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment);
                 }
-                else if (Tex.HasStencil)
+                else if (CachedImage.HasStencil)
                 {
                     Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment);
 
-                    Attach(ref StencilAttachment, Tex.Handle, FramebufferAttachment.StencilAttachment);
+                    Attach(ref StencilAttachment, CachedImage.Handle, FramebufferAttachment.StencilAttachment);
                 }
                 else
                 {
@@ -130,57 +128,40 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
         }
 
-        public void UnbindZeta()
+        private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment)
         {
-            EnsureFrameBuffer();
-
-            if (DepthAttachment   != 0 ||
-                StencilAttachment != 0)
+            if (OldHandle != NewHandle)
             {
                 GL.FramebufferTexture(
                     FramebufferTarget.DrawFramebuffer,
-                    FramebufferAttachment.DepthStencilAttachment,
-                    0,
+                    FbAttachment,
+                    NewHandle,
                     0);
 
-                DepthAttachment = 0;
-
-                StencilAttachment = 0;
+                OldHandle = NewHandle;
             }
         }
 
-        public void BindTexture(long Key, int Index)
+        public void UnbindZeta()
         {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
+            EnsureFrameBuffer();
+
+            if (DepthAttachment != 0 || StencilAttachment != 0)
             {
-                GL.ActiveTexture(TextureUnit.Texture0 + Index);
+                GL.FramebufferTexture(
+                    FramebufferTarget.DrawFramebuffer,
+                    FramebufferAttachment.DepthStencilAttachment,
+                    0,
+                    0);
 
-                GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
+                DepthAttachment   = 0;
+                StencilAttachment = 0;
             }
         }
 
         public void Set(long Key)
         {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
-            {
-                ReadTex = Tex;
-            }
-        }
-
-        public void Set(byte[] Data, int Width, int Height)
-        {
-            if (RawTex == null)
-            {
-                RawTex = new ImageHandler();
-            }
-
-            RawTex.EnsureSetup(new GalImage(Width, Height, RawFormat));
-
-            GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle);
-
-            GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, RawTex.PixelFormat, RawTex.PixelType, Data);
-
-            ReadTex = RawTex;
+            Texture.TryGetImageHandler(Key, out ReadTex);
         }
 
         public void SetMap(int[] Map)
@@ -280,13 +261,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
             int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
 
-            if (SrcFb == 0) SrcFb = GL.GenFramebuffer();
-
-            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
-
             GL.Viewport(0, 0, Window.Width, Window.Height);
 
+            if (SrcFb == 0)
+            {
+                SrcFb = GL.GenFramebuffer();
+            }
+
             GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
+            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
 
             GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
 
@@ -298,7 +281,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BlitFramebuffer(
                 SrcX0, SrcY0, SrcX1, SrcY1,
                 DstX0, DstY0, DstX1, DstY1,
-                ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear);
+                ClearBufferMask.ColorBufferBit,
+                BlitFramebufferFilter.Linear);
 
             EnsureFrameBuffer();
         }
@@ -315,183 +299,145 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             int  DstX1,
             int  DstY1)
         {
-            if (Texture.TryGetImage(SrcKey, out ImageHandler SrcTex) &&
-                Texture.TryGetImage(DstKey, out ImageHandler DstTex))
+            if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
+                Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
             {
-                if (SrcTex.HasColor != DstTex.HasColor ||
-                    SrcTex.HasDepth != DstTex.HasDepth ||
+                if (SrcTex.HasColor   != DstTex.HasColor ||
+                    SrcTex.HasDepth   != DstTex.HasDepth ||
                     SrcTex.HasStencil != DstTex.HasStencil)
                 {
                     throw new NotImplementedException();
                 }
 
-                if (SrcTex.HasColor)
+                if (SrcFb == 0)
                 {
-                    CopyTextures(
-                        SrcX0, SrcY0, SrcX1, SrcY1,
-                        DstX0, DstY0, DstX1, DstY1,
-                        SrcTex.Handle,
-                        DstTex.Handle,
-                        FramebufferAttachment.ColorAttachment0,
-                        ClearBufferMask.ColorBufferBit,
-                        true);
+                    SrcFb = GL.GenFramebuffer();
                 }
-                else if (SrcTex.HasDepth && SrcTex.HasStencil)
-                {
-                    CopyTextures(
-                        SrcX0, SrcY0, SrcX1, SrcY1,
-                        DstX0, DstY0, DstX1, DstY1,
-                        SrcTex.Handle,
-                        DstTex.Handle,
-                        FramebufferAttachment.DepthStencilAttachment,
-                        ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit,
-                        false);
-                }
-                else if (SrcTex.HasDepth)
-                {
-                    CopyTextures(
-                        SrcX0, SrcY0, SrcX1, SrcY1,
-                        DstX0, DstY0, DstX1, DstY1,
-                        SrcTex.Handle,
-                        DstTex.Handle,
-                        FramebufferAttachment.DepthAttachment,
-                        ClearBufferMask.DepthBufferBit,
-                        false);
-                }
-                else if (SrcTex.HasStencil)
+
+                if (DstFb == 0)
                 {
-                    CopyTextures(
-                        SrcX0, SrcY0, SrcX1, SrcY1,
-                        DstX0, DstY0, DstX1, DstY1,
-                        SrcTex.Handle,
-                        DstTex.Handle,
-                        FramebufferAttachment.StencilAttachment,
-                        ClearBufferMask.StencilBufferBit,
-                        false);
+                    DstFb = GL.GenFramebuffer();
                 }
-                else
+
+                GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
+                GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
+
+                FramebufferAttachment Attachment = GetAttachment(SrcTex);
+
+                GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
+                GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
+
+                BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
+
+                if (SrcTex.HasColor)
                 {
-                    throw new InvalidOperationException();
+                    GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
+
+                    Filter = BlitFramebufferFilter.Linear;
                 }
-            }
-        }
 
-        public void GetBufferData(long Key, Action<byte[]> Callback)
-        {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
-            {
-                byte[] Data = new byte[ImageUtils.GetSize(Tex.Image)];
+                ClearBufferMask Mask = GetClearMask(SrcTex);
 
-                GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
+                GL.Clear(Mask);
 
-                GL.GetTexImage(
-                    TextureTarget.Texture2D,
-                    0,
-                    Tex.PixelFormat,
-                    Tex.PixelType,
-                    Data);
+                GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
 
-                Callback(Data);
+                EnsureFrameBuffer();
             }
         }
 
-        public void SetBufferData(
-            long             Key,
-            int              Width,
-            int              Height,
-            byte[]           Buffer)
+        public void Reinterpret(long Key, GalImage NewImage)
         {
-            if (Texture.TryGetImage(Key, out ImageHandler Tex))
+            if (!Texture.TryGetImage(Key, out GalImage OldImage))
             {
-                GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
-
-                const int Level  = 0;
-                const int Border = 0;
-
-                GL.TexImage2D(
-                    TextureTarget.Texture2D,
-                    Level,
-                    Tex.InternalFormat,
-                    Width,
-                    Height,
-                    Border,
-                    Tex.PixelFormat,
-                    Tex.PixelType,
-                    Buffer);
+                return;
             }
-        }
 
-        private void EnsureFrameBuffer()
-        {
-            if (DummyFrameBuffer == 0)
+            if (NewImage.Format == OldImage.Format)
             {
-                DummyFrameBuffer = GL.GenFramebuffer();
+                return;
             }
 
-            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
+            byte[] Data = GetData(Key);
+
+            GL.PixelStore(PixelStoreParameter.UnpackRowLength, OldImage.Width);
+
+            Texture.Create(Key, Data, NewImage);
+
+            GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
         }
 
-        private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment)
+        public byte[] GetData(long Key)
         {
-            if (OldHandle != NewHandle)
+            if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
             {
-                GL.FramebufferTexture(
-                    FramebufferTarget.DrawFramebuffer,
-                    FbAttachment,
-                    NewHandle,
-                    0);
-
-                OldHandle = NewHandle;
+                return null;
             }
-        }
 
-        private void CopyTextures(
-            int SrcX0,
-            int SrcY0,
-            int SrcX1,
-            int SrcY1,
-            int DstX0,
-            int DstY0,
-            int DstX1,
-            int DstY1,
-            int SrcTexture,
-            int DstTexture,
-            FramebufferAttachment Attachment,
-            ClearBufferMask Mask,
-            bool Color)
-        {
-            if (SrcFb == 0) SrcFb = GL.GenFramebuffer();
-            if (DstFb == 0) DstFb = GL.GenFramebuffer();
+            if (SrcFb == 0)
+            {
+                SrcFb = GL.GenFramebuffer();
+            }
 
             GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
-            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
 
-            GL.FramebufferTexture(
-                FramebufferTarget.ReadFramebuffer,
-                Attachment,
-                SrcTexture,
-                0);
+            FramebufferAttachment Attachment = GetAttachment(CachedImage);
+
+            GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, CachedImage.Handle, 0);
+
+            int Size = ImageUtils.GetSize(CachedImage.Image);
+
+            byte[] Data = new byte[Size];
 
-            GL.FramebufferTexture(
-                FramebufferTarget.DrawFramebuffer,
-                Attachment,
-                DstTexture,
-                0);
+            int Width  = CachedImage.Width;
+            int Height = CachedImage.Height;
 
-            if (Color)
+            (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
+
+            GL.ReadPixels(0, 0, Width, Height, Format, Type, Data);
+
+            return Data;
+        }
+
+        private static FramebufferAttachment GetAttachment(ImageHandler CachedImage)
+        {
+            if (CachedImage.HasColor)
             {
-                GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
+                return FramebufferAttachment.ColorAttachment0;
+            }
+            else if (CachedImage.HasDepth && CachedImage.HasStencil)
+            {
+                return FramebufferAttachment.DepthStencilAttachment;
+            }
+            else if (CachedImage.HasDepth)
+            {
+                return FramebufferAttachment.DepthAttachment;
+            }
+            else if (CachedImage.HasStencil)
+            {
+                return FramebufferAttachment.StencilAttachment;
+            }
+            else
+            {
+                throw new InvalidOperationException();
             }
+        }
 
-            GL.Clear(Mask);
+        private static ClearBufferMask GetClearMask(ImageHandler CachedImage)
+        {
+            return (CachedImage.HasColor   ? ClearBufferMask.ColorBufferBit   : 0) |
+                   (CachedImage.HasDepth   ? ClearBufferMask.DepthBufferBit   : 0) |
+                   (CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
+        }
 
-            GL.BlitFramebuffer(
-                SrcX0, SrcY0, SrcX1, SrcY1,
-                DstX0, DstY0, DstX1, DstY1,
-                Mask,
-                Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
+        private void EnsureFrameBuffer()
+        {
+            if (DummyFrameBuffer == 0)
+            {
+                DummyFrameBuffer = GL.GenFramebuffer();
+            }
 
-            EnsureFrameBuffer();
+            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
         }
     }
 }

+ 75 - 86
Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs

@@ -28,17 +28,65 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.DeleteTexture(CachedImage.Handle);
         }
 
-        public void Create(long Key, byte[] Data, GalImage Image)
+        public void Create(long Key, int Size, GalImage Image)
         {
             int Handle = GL.GenTexture();
 
-            TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
+
+            const int Level  = 0; //TODO: Support mipmap textures.
+            const int Border = 0;
+
+            TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size);
+
+            GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask;
+
+            bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END;
+
+            if (ImageUtils.IsCompressed(Image.Format) && !IsASTC)
+            {
+                InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
+
+                GL.CompressedTexImage2D(
+                    TextureTarget.Texture2D,
+                    Level,
+                    InternalFmt,
+                    Image.Width,
+                    Image.Height,
+                    Border,
+                    Size,
+                    IntPtr.Zero);
+            }
+            else
+            {
+                (PixelInternalFormat InternalFmt,
+                 PixelFormat         Format,
+                 PixelType           Type) = OGLEnumConverter.GetImageFormat(Image.Format);
+
+                GL.TexImage2D(
+                    TextureTarget.Texture2D,
+                    Level,
+                    InternalFmt,
+                    Image.Width,
+                    Image.Height,
+                    Border,
+                    Format,
+                    Type,
+                    IntPtr.Zero);
+            }
+        }
+
+        public void Create(long Key, byte[] Data, GalImage Image)
+        {
+            int Handle = GL.GenTexture();
 
             GL.BindTexture(TextureTarget.Texture2D, Handle);
 
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
 
+            TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
+
             GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask;
 
             bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END;
@@ -62,8 +110,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 //TODO: Use KHR_texture_compression_astc_hdr when available
                 if (IsASTC)
                 {
-                    int TextureBlockWidth  = GetAstcBlockWidth(Image.Format);
-                    int TextureBlockHeight = GetAstcBlockHeight(Image.Format);
+                    int TextureBlockWidth  = ImageUtils.GetBlockWidth(Image.Format);
+                    int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
 
                     Data = ASTCDecoder.DecodeToRGBA8888(
                         Data,
@@ -85,12 +133,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Image.Format = GalImageFormat.R8G8 | (Image.Format & GalImageFormat.TypeMask);
                 }
 
-                (PixelInternalFormat InternalFormat, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
+                (PixelInternalFormat InternalFmt,
+                 PixelFormat         Format,
+                 PixelType           Type) = OGLEnumConverter.GetImageFormat(Image.Format);
 
                 GL.TexImage2D(
                     TextureTarget.Texture2D,
                     Level,
-                    InternalFormat,
+                    InternalFmt,
                     Image.Width,
                     Image.Height,
                     Border,
@@ -98,112 +148,51 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Type,
                     Data);
             }
-
-            int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource);
-            int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource);
-            int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource);
-            int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource);
-
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, SwizzleB);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
         }
 
-        public void CreateFb(long Key, long Size, GalImage Image)
+        public bool TryGetImage(long Key, out GalImage Image)
         {
-            if (!TryGetImage(Key, out ImageHandler CachedImage))
+            if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
             {
-                CachedImage = new ImageHandler();
-
-                TextureCache.AddOrUpdate(Key, CachedImage, Size);
-            }
+                Image = CachedImage.Image;
 
-            CachedImage.EnsureSetup(Image);
-        }
-
-        public bool TryGetImage(long Key, out ImageHandler CachedImage)
-        {
-            if (TextureCache.TryGetValue(Key, out CachedImage))
-            {
                 return true;
             }
 
-            CachedImage = null;
+            Image = default(GalImage);
 
             return false;
         }
 
-        private static int GetAstcBlockWidth(GalImageFormat Format)
+        public bool TryGetImageHandler(long Key, out ImageHandler CachedImage)
         {
-            switch (Format)
-            {
-                case GalImageFormat.ASTC_4x4   | GalImageFormat.Unorm: return 4;
-                case GalImageFormat.ASTC_5x5   | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_6x6   | GalImageFormat.Unorm: return 6;
-                case GalImageFormat.ASTC_8x8   | GalImageFormat.Unorm: return 8;
-                case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10;
-                case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12;
-                case GalImageFormat.ASTC_5x4   | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_6x5   | GalImageFormat.Unorm: return 6;
-                case GalImageFormat.ASTC_8x6   | GalImageFormat.Unorm: return 8;
-                case GalImageFormat.ASTC_10x8  | GalImageFormat.Unorm: return 10;
-                case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 12;
-                case GalImageFormat.ASTC_8x5   | GalImageFormat.Unorm: return 8;
-                case GalImageFormat.ASTC_10x5  | GalImageFormat.Unorm: return 10;
-                case GalImageFormat.ASTC_10x6  | GalImageFormat.Unorm: return 10;
-            }
-
-            throw new ArgumentException(nameof(Format));
-        }
-
-        private static int GetAstcBlockHeight(GalImageFormat Format)
-        {
-            switch (Format)
-            {
-                case GalImageFormat.ASTC_4x4   | GalImageFormat.Unorm: return 4;
-                case GalImageFormat.ASTC_5x5   | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_6x6   | GalImageFormat.Unorm: return 6;
-                case GalImageFormat.ASTC_8x8   | GalImageFormat.Unorm: return 8;
-                case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10;
-                case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12;
-                case GalImageFormat.ASTC_5x4   | GalImageFormat.Unorm: return 4;
-                case GalImageFormat.ASTC_6x5   | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_8x6   | GalImageFormat.Unorm: return 6;
-                case GalImageFormat.ASTC_10x8  | GalImageFormat.Unorm: return 8;
-                case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 10;
-                case GalImageFormat.ASTC_8x5   | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_10x5  | GalImageFormat.Unorm: return 5;
-                case GalImageFormat.ASTC_10x6  | GalImageFormat.Unorm: return 6;
-            }
-
-            throw new ArgumentException(nameof(Format));
-        }
-
-        public bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image)
-        {
-            if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize)
+            if (TextureCache.TryGetValue(Key, out CachedImage))
             {
-                if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
-                {
-                    Image = CachedImage.Image;
-
-                    return true;
-                }
+                return true;
             }
 
-            Image = default(GalImage);
+            CachedImage = null;
 
             return false;
         }
 
-        public void Bind(long Key, int Index)
+        public void Bind(long Key, int Index, GalImage Image)
         {
             if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
             {
                 GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
                 GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
+
+                int[] SwizzleRgba = new int[]
+                {
+                    (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource),
+                    (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource),
+                    (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource),
+                    (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
+                };
+
+                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
             }
         }
 

+ 1 - 0
Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs

@@ -641,6 +641,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                     default:
                         SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
+
                         break;
                 }
             }

+ 64 - 64
Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs

@@ -6,32 +6,32 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
-        public static void Bfe_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitBfe(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Bfe_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Bfe_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitBfe(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Bfe_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Bfe_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitBfe(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fadd_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fadd_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFadd(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Fadd_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fadd_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFadd(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Fadd_I32(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fadd_I32(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperA = OpCode.Gpr8();
             ShaderIrNode OperB = OpCode.Immf32_20();
@@ -49,47 +49,47 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
-        public static void Fadd_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fadd_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFadd(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Ffma_CR(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ffma_CR(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFfma(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Ffma_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ffma_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFfma(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Ffma_RC(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ffma_RC(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFfma(Block, OpCode, ShaderOper.RC);
         }
 
-        public static void Ffma_RR(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ffma_RR(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFfma(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmnmx(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmnmx(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmnmx(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fmul_I32(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmul_I32(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperA = OpCode.Gpr8();
             ShaderIrNode OperB = OpCode.Immf32_20();
@@ -99,62 +99,62 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
-        public static void Fmul_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmul_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmul(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Fmul_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmul_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmul(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Fmul_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fmul_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFmul(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fset_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fset_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFset(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Fset_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fset_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFset(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Fset_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fset_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFset(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fsetp_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fsetp_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFsetp(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Fsetp_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fsetp_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFsetp(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Fsetp_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Fsetp_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitFsetp(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Iadd_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Iadd_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Iadd_I32(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd_I32(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperA = OpCode.Gpr8();
             ShaderIrNode OperB = OpCode.Imm32_20();
@@ -168,42 +168,42 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
-        public static void Iadd_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Iadd3_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd3_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd3(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Iadd3_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd3_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd3(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Iadd3_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iadd3_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIadd3(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Imnmx_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Imnmx_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitImnmx(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Imnmx_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Imnmx_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitImnmx(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Imnmx_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Imnmx_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitImnmx(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Ipa(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ipa(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperA = OpCode.Abuf28();
             ShaderIrNode OperB = OpCode.Gpr20();
@@ -217,52 +217,52 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
-        public static void Iscadd_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iscadd_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIscadd(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Iscadd_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iscadd_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIscadd(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Iscadd_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iscadd_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIscadd(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Iset_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iset_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIset(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Iset_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iset_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIset(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Iset_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Iset_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIset(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Isetp_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Isetp_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIsetp(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Isetp_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Isetp_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIsetp(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Isetp_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Isetp_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitIsetp(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Lop_I32(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Lop_I32(ShaderIrBlock Block, long OpCode, int Position)
         {
             int SubOp = OpCode.Read(53, 3);
 
@@ -296,22 +296,22 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        public static void Lop_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Lop_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitLop(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Lop_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Lop_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitLop(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Lop_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Lop_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitLop(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Mufu(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mufu(ShaderIrBlock Block, long OpCode, int Position)
         {
             int SubOp = OpCode.Read(20, 0xf);
 
@@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
-        public static void Psetp(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Psetp(ShaderIrBlock Block, long OpCode, int Position)
         {
             bool NegA = OpCode.Read(15);
             bool NegB = OpCode.Read(32);
@@ -394,47 +394,47 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
         }
 
-        public static void Rro_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Rro_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitRro(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Rro_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Rro_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitRro(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void Rro_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Rro_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitRro(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Shl_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shl_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl);
         }
 
-        public static void Shl_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shl_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl);
         }
 
-        public static void Shl_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shl_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl);
         }
 
-        public static void Shr_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shr_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
         }
 
-        public static void Shr_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shr_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
         }
 
-        public static void Shr_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Shr_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
         }
@@ -446,7 +446,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
         }
 
-        public static void Vmad(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Vmad(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperA = OpCode.Gpr8();
 
@@ -481,22 +481,22 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Final)));
         }
 
-        public static void Xmad_CR(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Xmad_CR(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitXmad(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Xmad_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Xmad_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitXmad(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Xmad_RC(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Xmad_RC(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitXmad(Block, OpCode, ShaderOper.RC);
         }
 
-        public static void Xmad_RR(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Xmad_RR(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitXmad(Block, OpCode, ShaderOper.RR);
         }

+ 7 - 17
Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs

@@ -1,12 +1,10 @@
 using System;
 
-using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
-
 namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
-        public static void Bra(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Bra(ShaderIrBlock Block, long OpCode, int Position)
         {
             if ((OpCode & 0x20) != 0)
             {
@@ -15,14 +13,12 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Target = OpCode.Branch();
-
-            ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
+            ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch());
 
             Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm)));
         }
 
-        public static void Exit(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Exit(ShaderIrBlock Block, long OpCode, int Position)
         {
             int CCode = (int)OpCode & 0x1f;
 
@@ -31,15 +27,14 @@ namespace Ryujinx.Graphics.Gal.Shader
             {
                 Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Exit)));
             }
-
         }
 
-        public static void Kil(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Kil(ShaderIrBlock Block, long OpCode, int Position)
         {
             Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Kil)));
         }
 
-        public static void Ssy(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ssy(ShaderIrBlock Block, long OpCode, int Position)
         {
             if ((OpCode & 0x20) != 0)
             {
@@ -48,19 +43,14 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Offset = OpCode.Branch();
-
-            int Target = (int)(Position + Offset);
-
-            ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
+            ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch());
 
             Block.AddNode(new ShaderIrOp(ShaderIrInst.Ssy, Imm));
         }
 
-        public static void Sync(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Sync(ShaderIrBlock Block, long OpCode, int Position)
         {
             //TODO: Implement Sync condition codes
-
             Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Sync)));
         }
     }

+ 1 - 1
Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs → Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs

@@ -1,4 +1,4 @@
 namespace Ryujinx.Graphics.Gal.Shader
 {
-    delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, long Position);
+    delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, int Position);
 }

+ 0 - 2
Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs

@@ -1,5 +1,3 @@
-using System;
-
 namespace Ryujinx.Graphics.Gal.Shader
 {
     static class ShaderDecodeHelper

+ 8 - 8
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs

@@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
         };
 
-        public static void Ld_A(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode[] Opers = OpCode.Abuf20();
 
@@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        public static void Ld_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Ld_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             int CbufPos   = OpCode.Read(22, 0x3fff);
             int CbufIndex = OpCode.Read(36, 0x1f);
@@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        public static void St_A(ShaderIrBlock Block, long OpCode, long Position)
+        public static void St_A(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode[] Opers = OpCode.Abuf20();
 
@@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        public static void Texq(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Texq(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrNode OperD = OpCode.Gpr0();
             ShaderIrNode OperA = OpCode.Gpr8();
@@ -132,12 +132,12 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, Op1))); //Is this right?
         }
 
-        public static void Tex(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Tex(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitTex(Block, OpCode, GprHandle: false);
         }
 
-        public static void Tex_B(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitTex(Block, OpCode, GprHandle: true);
         }
@@ -202,12 +202,12 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
-        public static void Texs(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Texs(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitTexs(Block, OpCode, ShaderIrInst.Texs);
         }
 
-        public static void Tlds(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Tlds(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitTexs(Block, OpCode, ShaderIrInst.Txlf);
         }

+ 21 - 21
Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs

@@ -25,67 +25,67 @@ namespace Ryujinx.Graphics.Gal.Shader
             F64 = 3
         }
 
-        public static void F2f_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2f_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2f(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void F2f_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2f_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2f(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void F2f_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2f_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2f(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void F2i_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2i_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2i(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void F2i_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2i_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2i(Block, OpCode, ShaderOper.Immf);
         }
 
-        public static void F2i_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void F2i_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitF2i(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void I2f_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2f_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2f(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void I2f_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2f_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2f(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void I2f_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2f_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2f(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void I2i_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2i_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2i(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void I2i_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2i_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2i(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void I2i_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void I2i_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitI2i(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Isberd(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Isberd(ShaderIrBlock Block, long OpCode, int Position)
         {
             //This instruction seems to be used to translate from an address to a vertex index in a GS
             //Stub it as such
@@ -95,50 +95,50 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OpCode.Gpr8())));
         }
 
-        public static void Mov_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mov_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrOperCbuf Cbuf = OpCode.Cbuf34();
 
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Cbuf)));
         }
 
-        public static void Mov_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mov_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrOperImm Imm = OpCode.Imm19_20();
 
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm)));
         }
 
-        public static void Mov_I32(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mov_I32(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrOperImm Imm = OpCode.Imm32_20();
 
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm)));
         }
 
-        public static void Mov_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mov_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             ShaderIrOperGpr Gpr = OpCode.Gpr20();
 
             Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Gpr)));
         }
 
-        public static void Sel_C(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Sel_C(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitSel(Block, OpCode, ShaderOper.CR);
         }
 
-        public static void Sel_I(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Sel_I(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitSel(Block, OpCode, ShaderOper.Imm);
         }
 
-        public static void Sel_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Sel_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             EmitSel(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Mov_S(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Mov_S(ShaderIrBlock Block, long OpCode, int Position)
         {
             Block.AddNode(new ShaderIrCmnt("Stubbed."));
 

+ 2 - 4
Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs

@@ -1,10 +1,8 @@
-using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
-
-namespace Ryujinx.Graphics.Gal.Shader
+namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
-        public static void Out_R(ShaderIrBlock Block, long OpCode, long Position)
+        public static void Out_R(ShaderIrBlock Block, long OpCode, int Position)
         {
             //TODO: Those registers have to be used for something
             ShaderIrOperGpr Gpr0  = OpCode.Gpr0();

+ 22 - 22
Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs

@@ -10,12 +10,14 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start)
         {
-            Dictionary<long, ShaderIrBlock> Visited    = new Dictionary<long, ShaderIrBlock>();
-            Dictionary<long, ShaderIrBlock> VisitedEnd = new Dictionary<long, ShaderIrBlock>();
+            Dictionary<int, ShaderIrBlock> Visited    = new Dictionary<int, ShaderIrBlock>();
+            Dictionary<int, ShaderIrBlock> VisitedEnd = new Dictionary<int, ShaderIrBlock>();
 
             Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>();
 
-            ShaderIrBlock Enqueue(long Position, ShaderIrBlock Source = null)
+            long Beginning = Start + HeaderSize;
+
+            ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null)
             {
                 if (!Visited.TryGetValue(Position, out ShaderIrBlock Output))
                 {
@@ -34,13 +36,13 @@ namespace Ryujinx.Graphics.Gal.Shader
                 return Output;
             }
 
-            ShaderIrBlock Entry = Enqueue(Start + HeaderSize);
+            ShaderIrBlock Entry = Enqueue(0);
 
             while (Blocks.Count > 0)
             {
                 ShaderIrBlock Current = Blocks.Dequeue();
 
-                FillBlock(Memory, Current, Start + HeaderSize);
+                FillBlock(Memory, Current, Beginning);
 
                 //Set child blocks. "Branch" is the block the branch instruction
                 //points to (when taken), "Next" is the block at the next address,
@@ -54,20 +56,18 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                     if (InnerOp?.Inst == ShaderIrInst.Bra)
                     {
-                        int Offset = ((ShaderIrOperImm)InnerOp.OperandA).Value;
-
-                        long Target = Current.EndPosition + Offset;
+                        int Target = ((ShaderIrOperImm)InnerOp.OperandA).Value;
 
                         Current.Branch = Enqueue(Target, Current);
                     }
 
                     foreach (ShaderIrNode Node in Current.Nodes)
                     {
-                        if (Node is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy)
-                        {
-                            int Offset = ((ShaderIrOperImm)CurrOp.OperandA).Value;
+                        InnerOp = GetInnermostOp(Node);
 
-                            long Target = Offset;
+                        if (InnerOp is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy)
+                        {
+                            int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value;
 
                             Current.Branch = Enqueue(Target, Current);
                         }
@@ -112,15 +112,15 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             while (Visited.Count > 0)
             {
-                ulong FirstPos = ulong.MaxValue;
+                uint FirstPos = uint.MaxValue;
 
                 foreach (ShaderIrBlock Block in Visited.Values)
                 {
-                    if (FirstPos > (ulong)Block.Position)
-                        FirstPos = (ulong)Block.Position;
+                    if (FirstPos > (uint)Block.Position)
+                        FirstPos = (uint)Block.Position;
                 }
 
-                ShaderIrBlock Current = Visited[(long)FirstPos];
+                ShaderIrBlock Current = Visited[(int)FirstPos];
 
                 do
                 {
@@ -138,20 +138,20 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning)
         {
-            long Position = Block.Position;
+            int Position = Block.Position;
 
             do
             {
                 //Ignore scheduling instructions, which are written every 32 bytes.
-                if (((Position - Beginning) & 0x1f) == 0)
+                if ((Position & 0x1f) == 0)
                 {
                     Position += 8;
 
                     continue;
                 }
 
-                uint Word0 = (uint)Memory.ReadInt32(Position + 0);
-                uint Word1 = (uint)Memory.ReadInt32(Position + 4);
+                uint Word0 = (uint)Memory.ReadInt32(Position + Beginning + 0);
+                uint Word1 = (uint)Memory.ReadInt32(Position + Beginning + 4);
 
                 Position += 8;
 
@@ -161,7 +161,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 if (AddDbgComments)
                 {
-                    string DbgOpCode = $"0x{(Position - Beginning - 8):x16}: 0x{OpCode:x16} ";
+                    string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} ";
 
                     DbgOpCode += (Decode?.Method.Name ?? "???");
 
@@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                     {
                         int Offset = ((int)(OpCode >> 20) << 8) >> 8;
 
-                        long Target = Position + Offset - Beginning;
+                        long Target = Position + Offset;
 
                         DbgOpCode += " (0x" + Target.ToString("x16") + ")";
                     }

+ 3 - 3
Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs

@@ -4,8 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     class ShaderIrBlock
     {
-        public long Position    { get; set; }
-        public long EndPosition { get; set; }
+        public int Position    { get; set; }
+        public int EndPosition { get; set; }
 
         public ShaderIrBlock Next   { get; set; }
         public ShaderIrBlock Branch { get; set; }
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public List<ShaderIrNode> Nodes { get; private set; }
 
-        public ShaderIrBlock(long Position)
+        public ShaderIrBlock(int Position)
         {
             this.Position = Position;
 

+ 122 - 0
Ryujinx.Graphics/GpuResourceManager.cs

@@ -0,0 +1,122 @@
+using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Memory;
+using Ryujinx.Graphics.Texture;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics
+{
+    public class GpuResourceManager
+    {
+        private NvGpu Gpu;
+
+        private HashSet<long>[] UploadedKeys;
+
+        public GpuResourceManager(NvGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            UploadedKeys = new HashSet<long>[(int)NvGpuBufferType.Count];
+
+            for (int Index = 0; Index < UploadedKeys.Length; Index++)
+            {
+                UploadedKeys[Index] = new HashSet<long>();
+            }
+        }
+
+        public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage)
+        {
+            long Size = (uint)ImageUtils.GetSize(NewImage);
+
+            MarkAsCached(Vmm, Position, Size, NvGpuBufferType.Texture);
+
+            bool IsCached = Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage);
+
+            if (IsCached && CachedImage.SizeMatches(NewImage))
+            {
+                Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);
+                Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage);
+
+                return;
+            }
+
+            Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
+
+            Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage);
+        }
+
+        public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage)
+        {
+            long Size = (uint)ImageUtils.GetSize(NewImage);
+
+            MarkAsCached(Vmm, Position, Size, NvGpuBufferType.Texture);
+
+            bool IsCached = Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage);
+
+            if (IsCached && CachedImage.SizeMatches(NewImage))
+            {
+                Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);
+                Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage);
+
+                return;
+            }
+
+            Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
+
+            Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage);
+        }
+
+        public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1)
+        {
+            long Size = (uint)ImageUtils.GetSize(NewImage);
+
+            if (!MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture))
+            {
+                if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage))
+                {
+                    Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);
+
+                    if (TexIndex >= 0)
+                    {
+                        Gpu.Renderer.Texture.Bind(Position, TexIndex, NewImage);
+                    }
+
+                    return;
+                }
+            }
+
+            byte[] Data = ImageUtils.ReadTexture(Vmm, NewImage, Position);
+
+            Gpu.Renderer.Texture.Create(Position, Data, NewImage);
+
+            if (TexIndex >= 0)
+            {
+                Gpu.Renderer.Texture.Bind(Position, TexIndex, NewImage);
+            }
+        }
+
+        private void MarkAsCached(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type)
+        {
+            Vmm.IsRegionModified(Position, Size, Type);
+        }
+
+        private bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type)
+        {
+            HashSet<long> Uploaded = UploadedKeys[(int)Type];
+
+            if (!Uploaded.Add(Position))
+            {
+                return false;
+            }
+
+            return Vmm.IsRegionModified(Position, Size, Type);
+        }
+
+        public void ClearPbCache()
+        {
+            for (int Index = 0; Index < UploadedKeys.Length; Index++)
+            {
+                UploadedKeys[Index].Clear();
+            }
+        }
+    }
+}

+ 33 - 260
Ryujinx.Graphics/Memory/NvGpuVmmCache.cs

@@ -1,309 +1,82 @@
 using ChocolArm64.Memory;
 using System;
-using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Memory
 {
     class NvGpuVmmCache
     {
-        private const long RamSize = 4L * 1024 * 1024 * 1024;
-
-        private const int MaxCpCount     = 10000;
-        private const int MaxCpTimeDelta = 60000;
-
-        private class CachedPage
-        {
-            private struct Range
-            {
-                public long Start;
-                public long End;
-
-                public Range(long Start, long End)
-                {
-                    this.Start = Start;
-                    this.End = End;
-                }
-            }
-
-            private List<Range>[] Regions;
-
-            private HashSet<long> ResidencyKeys;
-
-            public LinkedListNode<long> Node { get; set; }
-
-            public int Timestamp { get; private set; }
-
-            public CachedPage()
-            {
-                Regions = new List<Range>[(int)NvGpuBufferType.Count];
-
-                for (int Index = 0; Index < Regions.Length; Index++)
-                {
-                    Regions[Index] = new List<Range>();
-                }
-
-                ResidencyKeys = new HashSet<long>();
-            }
-
-            public void AddResidency(long Key)
-            {
-                ResidencyKeys.Add(Key);
-            }
-
-            public void RemoveResidency(HashSet<long>[] Residency, long PageSize)
-            {
-                for (int i = 0; i < (int)NvGpuBufferType.Count; i++)
-                {
-                    foreach (Range Region in Regions[i])
-                    {
-                        foreach (long Key in ResidencyKeys)
-                        {
-                            Residency[Region.Start / PageSize].Remove(Key);
-                        }
-                    }
-                }
-            }
-
-            public bool AddRange(long Start, long End, NvGpuBufferType BufferType)
-            {
-                List<Range> BtRegions = Regions[(int)BufferType];
-
-                for (int Index = 0; Index < BtRegions.Count; Index++)
-                {
-                    Range Rg = BtRegions[Index];
-
-                    if (Start >= Rg.Start && End <= Rg.End)
-                    {
-                        return false;
-                    }
-
-                    if (Start <= Rg.End && Rg.Start <= End)
-                    {
-                        long MinStart = Math.Min(Rg.Start, Start);
-                        long MaxEnd   = Math.Max(Rg.End,   End);
-
-                        BtRegions[Index] = new Range(MinStart, MaxEnd);
-
-                        Timestamp = Environment.TickCount;
-
-                        return true;
-                    }
-                }
-
-                BtRegions.Add(new Range(Start, End));
-
-                Timestamp = Environment.TickCount;
-
-                return true;
-            }
-
-            public int GetTotalCount()
-            {
-                int Count = 0;
-
-                for (int Index = 0; Index < Regions.Length; Index++)
-                {
-                    Count += Regions[Index].Count;
-                }
-
-                return Count;
-            }
-        }
-
-        private Dictionary<long, CachedPage> Cache;
-
-        private LinkedList<long> SortedCache;
-
-        private HashSet<long>[] Residency;
-
-        private long ResidencyPageSize;
-
-        private int CpCount;
+        private ValueRangeSet<int> CachedRanges;
 
         public NvGpuVmmCache()
         {
-            Cache = new Dictionary<long, CachedPage>();
-
-            SortedCache = new LinkedList<long>();
+            CachedRanges = new ValueRangeSet<int>();
         }
 
         public bool IsRegionModified(AMemory Memory, NvGpuBufferType BufferType, long PA, long Size)
         {
             (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size);
 
-            PA = Memory.GetPhysicalAddress(PA);
-
-            ClearCachedPagesIfNeeded();
-
-            long PageSize = AMemory.PageSize;
-
-            EnsureResidencyInitialized(PageSize);
-
-            bool HasResidents = AddResidency(PA, Size);
-
-            if (!HasResidents && ModifiedCount == 0)
-            {
-                return false;
-            }
-
-            long Mask = PageSize - 1;
-
-            long ResidencyKey = PA;
-
-            long PAEnd = PA + Size;
-
-            bool RegMod = false;
-
+            //Remove all modified ranges.
             int Index = 0;
 
-            while (PA < PAEnd)
-            {
-                long Key = PA & ~AMemory.PageMask;
-
-                long PAPgEnd = Math.Min((PA + AMemory.PageSize) & ~AMemory.PageMask, PAEnd);
-
-                bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
-
-                if (IsCached)
-                {
-                    CpCount -= Cp.GetTotalCount();
-
-                    SortedCache.Remove(Cp.Node);
-                }
-                else
-                {
-                    Cp = new CachedPage();
-
-                    Cache.Add(Key, Cp);
-                }
+            long Position = PA & ~NvGpuVmm.PageMask;
 
-                if (Modified[Index++] && IsCached)
+            while (ModifiedCount > 0)
+            {
+                if (Modified[Index++])
                 {
-                    Cp = new CachedPage();
+                    CachedRanges.Remove(new ValueRange<int>(Position, Position + NvGpuVmm.PageSize));
 
-                    Cache[Key] = Cp;
+                    ModifiedCount--;
                 }
 
-                Cp.AddResidency(ResidencyKey);
-
-                Cp.Node = SortedCache.AddLast(Key);
-
-                RegMod |= Cp.AddRange(PA, PAPgEnd, BufferType);
-
-                CpCount += Cp.GetTotalCount();
-
-                PA = PAPgEnd;
+                Position += NvGpuVmm.PageSize;
             }
 
-            return RegMod;
-        }
+            //Mask has the bit set for the current resource type.
+            //If the region is not yet present on the list, then a new ValueRange
+            //is directly added with the current resource type as the only bit set.
+            //Otherwise, it just sets the bit for this new resource type on the current mask.
+            int Mask = 1 << (int)BufferType;
 
-        private bool AddResidency(long PA, long Size)
-        {
-            long PageSize = ResidencyPageSize;
+            ValueRange<int> NewCached = new ValueRange<int>(PA, PA + Size);
 
-            long Mask = PageSize - 1;
+            ValueRange<int>[] Ranges = CachedRanges.GetAllIntersections(NewCached);
 
-            long Key = PA;
+            long LastEnd = NewCached.Start;
 
-            bool ResidentFound = false;
+            long Coverage = 0;
 
-            for (long Cursor = PA & ~Mask; Cursor < ((PA + Size + PageSize - 1) & ~Mask); Cursor += PageSize)
+            for (Index = 0; Index < Ranges.Length; Index++)
             {
-                long PageIndex = Cursor / PageSize;
-
-                Residency[PageIndex].Add(Key);
+                ValueRange<int> Current = Ranges[Index];
 
-                if (Residency[PageIndex].Count > 1)
-                {
-                    ResidentFound = true;
-                }
-            }
-
-            return ResidentFound;
-        }
-
-        private void EnsureResidencyInitialized(long PageSize)
-        {
-            if (Residency == null)
-            {
-                Residency = new HashSet<long>[RamSize / PageSize];
+                long RgStart = Math.Max(Current.Start, NewCached.Start);
+                long RgEnd   = Math.Min(Current.End,   NewCached.End);
 
-                for (int i = 0; i < Residency.Length; i++)
+                if ((Current.Value & Mask) == 0)
                 {
-                    Residency[i] = new HashSet<long>();
+                    CachedRanges.Add(new ValueRange<int>(RgStart, RgEnd, Current.Value | Mask));
                 }
-
-                ResidencyPageSize = PageSize;
-            }
-            else
-            {
-                if (ResidencyPageSize != PageSize)
+                else
                 {
-                    throw new InvalidOperationException("Tried to change residency page size");
+                    Coverage += RgEnd - RgStart;
                 }
-            }
-        }
-
-        private void ClearCachedPagesIfNeeded()
-        {
-            if (CpCount <= MaxCpCount)
-            {
-                return;
-            }
-
-            int Timestamp = Environment.TickCount;
 
-            int TimeDelta;
-
-            do
-            {
-                if (!TryPopOldestCachedPageKey(Timestamp, out long Key))
+                if (RgStart > LastEnd)
                 {
-                    break;
+                    CachedRanges.Add(new ValueRange<int>(LastEnd, RgStart, Mask));
                 }
 
-                CachedPage Cp = Cache[Key];
-
-                Cp.RemoveResidency(Residency, ResidencyPageSize);
-
-                Cache.Remove(Key);
-
-                CpCount -= Cp.GetTotalCount();
-
-                TimeDelta = RingDelta(Cp.Timestamp, Timestamp);
+                LastEnd = RgEnd;
             }
-            while (CpCount > (MaxCpCount >> 1) || (uint)TimeDelta > (uint)MaxCpTimeDelta);
-        }
-
-        private bool TryPopOldestCachedPageKey(int Timestamp, out long Key)
-        {
-            LinkedListNode<long> Node = SortedCache.First;
 
-            if (Node == null)
+            if (LastEnd < NewCached.End)
             {
-                Key = 0;
-
-                return false;
+                CachedRanges.Add(new ValueRange<int>(LastEnd, NewCached.End, Mask));
             }
 
-            SortedCache.Remove(Node);
-
-            Key = Node.Value;
-
-            return true;
-        }
-
-        private int RingDelta(int Old, int New)
-        {
-            if ((uint)New < (uint)Old)
-            {
-                return New + (~Old + 1);
-            }
-            else
-            {
-                return New - Old;
-            }
+            return Coverage != Size;
         }
     }
 }

+ 12 - 7
Ryujinx.Graphics/NvGpu.cs

@@ -1,5 +1,4 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.Graphics;
 
 namespace Ryujinx.Graphics
 {
@@ -7,21 +6,27 @@ namespace Ryujinx.Graphics
     {
         public IGalRenderer Renderer { get; private set; }
 
+        public GpuResourceManager ResourceManager { get; private set; }
+
         public NvGpuFifo Fifo { get; private set; }
 
-        public NvGpuEngine2d  Engine2d  { get; private set; }
-        public NvGpuEngine3d  Engine3d  { get; private set; }
-        public NvGpuEngineDma EngineDma { get; private set; }
+        internal NvGpuEngine2d   Engine2d   { get; private set; }
+        internal NvGpuEngine3d   Engine3d   { get; private set; }
+        internal NvGpuEngineM2mf EngineM2mf { get; private set; }
+        internal NvGpuEngineP2mf EngineP2mf { get; private set; }
 
         public NvGpu(IGalRenderer Renderer)
         {
             this.Renderer = Renderer;
 
+            ResourceManager = new GpuResourceManager(this);
+
             Fifo = new NvGpuFifo(this);
 
-            Engine2d  = new NvGpuEngine2d(this);
-            Engine3d  = new NvGpuEngine3d(this);
-            EngineDma = new NvGpuEngineDma(this);
+            Engine2d   = new NvGpuEngine2d(this);
+            Engine3d   = new NvGpuEngine3d(this);
+            EngineM2mf = new NvGpuEngineM2mf(this);
+            EngineP2mf = new NvGpuEngineP2mf(this);
         }
     }
 }

+ 1 - 1
Ryujinx.Graphics/Memory/NvGpuBufferType.cs → Ryujinx.Graphics/NvGpuBufferType.cs

@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Memory
+namespace Ryujinx.Graphics
 {
     public enum NvGpuBufferType
     {

+ 2 - 2
Ryujinx.Graphics/NvGpuEngine.cs

@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics
         _2d     = 0x902d,
         _3d     = 0xb197,
         Compute = 0xb1c0,
-        Kepler  = 0xa140,
-        Dma     = 0xb0b5
+        P2mf    = 0xa140,
+        M2mf    = 0xb0b5
     }
 }

+ 40 - 91
Ryujinx.Graphics/NvGpuEngine2d.cs

@@ -1,7 +1,6 @@
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
 using Ryujinx.Graphics.Texture;
-using System;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics
@@ -62,25 +61,25 @@ namespace Ryujinx.Graphics
         {
             CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
 
+            int  SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat);
             bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
             int  SrcWidth  = ReadRegister(NvGpuEngine2dReg.SrcWidth);
             int  SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight);
             int  SrcPitch  = ReadRegister(NvGpuEngine2dReg.SrcPitch);
             int  SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions);
 
+            int  DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat);
             bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
             int  DstWidth  = ReadRegister(NvGpuEngine2dReg.DstWidth);
             int  DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
             int  DstPitch  = ReadRegister(NvGpuEngine2dReg.DstPitch);
             int  DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
 
-            TextureSwizzle SrcSwizzle = SrcLinear
-                ? TextureSwizzle.Pitch
-                : TextureSwizzle.BlockLinear;
+            GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat);
+            GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat);
 
-            TextureSwizzle DstSwizzle = DstLinear
-                ? TextureSwizzle.Pitch
-                : TextureSwizzle.BlockLinear;
+            GalMemoryLayout SrcLayout = GetLayout(SrcLinear);
+            GalMemoryLayout DstLayout = GetLayout(DstLinear);
 
             int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf);
             int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
@@ -91,91 +90,41 @@ namespace Ryujinx.Graphics
             long SrcKey = Vmm.GetPhysicalAddress(SrcAddress);
             long DstKey = Vmm.GetPhysicalAddress(DstAddress);
 
-            bool IsSrcFb = Gpu.Engine3d.IsFrameBufferPosition(SrcKey);
-            bool IsDstFb = Gpu.Engine3d.IsFrameBufferPosition(DstKey);
-
-            TextureInfo SrcTexture()
-            {
-                return new TextureInfo(
-                    SrcAddress,
-                    SrcWidth,
-                    SrcHeight,
-                    SrcPitch,
-                    SrcBlockHeight, 1,
-                    SrcSwizzle,
-                    GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
-            }
-
-            TextureInfo DstTexture()
-            {
-                return new TextureInfo(
-                    DstAddress,
-                    DstWidth,
-                    DstHeight,
-                    DstPitch,
-                    DstBlockHeight, 1,
-                    DstSwizzle,
-                    GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
-            }
-
-            //TODO: fb -> fb copies, tex -> fb copies, formats other than RGBA8,
-            //make it throw for unimpl stuff (like the copy mode)...
-            if (IsSrcFb && IsDstFb)
-            {
-                //Frame Buffer -> Frame Buffer copy.
-                Gpu.Renderer.RenderTarget.Copy(
-                    SrcKey,
-                    DstKey,
-                    0,
-                    0,
-                    SrcWidth,
-                    SrcHeight,
-                    0,
-                    0,
-                    DstWidth,
-                    DstHeight);
-            }
-            if (IsSrcFb)
-            {
-                //Frame Buffer -> Texture copy.
-                Gpu.Renderer.RenderTarget.GetBufferData(SrcKey, (byte[] Buffer) =>
-                {
-                    TextureInfo Src = SrcTexture();
-                    TextureInfo Dst = DstTexture();
-
-                    if (Src.Width  != Dst.Width ||
-                        Src.Height != Dst.Height)
-                    {
-                        throw new NotImplementedException("Texture resizing is not supported");
-                    }
-
-                    TextureWriter.Write(Vmm, Dst, Buffer);
-                });
-            }
-            else if (IsDstFb)
-            {
-                byte[] Buffer = TextureReader.Read(Vmm, SrcTexture());
-
-                Gpu.Renderer.RenderTarget.SetBufferData(
-                    DstKey,
-                    DstWidth,
-                    DstHeight,
-                    Buffer);
-            }
-            else
-            {
-                //Texture -> Texture copy.
-                TextureInfo Src = SrcTexture();
-                TextureInfo Dst = DstTexture();
-
-                if (Src.Width  != Dst.Width ||
-                    Src.Height != Dst.Height)
-                {
-                    throw new NotImplementedException("Texture resizing is not supported");
-                }
+            GalImage SrcTexture = new GalImage(
+                SrcWidth,
+                SrcHeight, 1,
+                SrcBlockHeight,
+                SrcLayout,
+                SrcImgFormat);
+
+            GalImage DstTexture = new GalImage(
+                DstWidth,
+                DstHeight, 1,
+                DstBlockHeight,
+                DstLayout,
+                DstImgFormat);
+
+            Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture);
+            Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture);
+
+            Gpu.Renderer.RenderTarget.Copy(
+                SrcKey,
+                DstKey,
+                0,
+                0,
+                SrcWidth,
+                SrcHeight,
+                0,
+                0,
+                DstWidth,
+                DstHeight);
+        }
 
-                TextureWriter.Write(Vmm, Dst, TextureReader.Read(Vmm, Src));
-            }
+        private static GalMemoryLayout GetLayout(bool Linear)
+        {
+            return Linear
+                ? GalMemoryLayout.Pitch
+                : GalMemoryLayout.BlockLinear;
         }
 
         private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg)

+ 46 - 77
Ryujinx.Graphics/NvGpuEngine3d.cs

@@ -23,8 +23,6 @@ namespace Ryujinx.Graphics
 
         private ConstBuffer[][] ConstBuffers;
 
-        private HashSet<long> FrameBuffers;
-
         private List<long>[] UploadedKeys;
 
         private int CurrentInstance = 0;
@@ -60,8 +58,6 @@ namespace Ryujinx.Graphics
                 ConstBuffers[Index] = new ConstBuffer[18];
             }
 
-            FrameBuffers = new HashSet<long>();
-
             UploadedKeys = new List<long>[(int)NvGpuBufferType.Count];
 
             for (int i = 0; i < UploadedKeys.Length; i++)
@@ -96,7 +92,7 @@ namespace Ryujinx.Graphics
 
             GalPipelineState State = new GalPipelineState();
 
-            SetFlip(State);
+            SetFrameBuffer(State);
             SetFrontFace(State);
             SetCullFace(State);
             SetDepth(State);
@@ -104,10 +100,7 @@ namespace Ryujinx.Graphics
             SetAlphaBlending(State);
             SetPrimitiveRestart(State);
 
-            for (int FbIndex = 0; FbIndex < 8; FbIndex++)
-            {
-                SetFrameBuffer(Vmm, 0);
-            }
+            SetFrameBuffer(Vmm, 0);
 
             SetZeta(Vmm);
 
@@ -173,9 +166,9 @@ namespace Ryujinx.Graphics
         {
             long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
 
-            int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10);
+            int SurfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10);
 
-            if (VA == 0 || Format == 0)
+            if (VA == 0 || SurfFormat == 0)
             {
                 Gpu.Renderer.RenderTarget.UnbindColor(FbIndex);
 
@@ -184,11 +177,15 @@ namespace Ryujinx.Graphics
 
             long Key = Vmm.GetPhysicalAddress(VA);
 
-            FrameBuffers.Add(Key);
-
             int Width  = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth  + FbIndex * 0x10);
             int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
 
+            int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10);
+
+            int GobBlockHeight = 1 << ((BlockDim >> 4) & 7);
+
+            GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1);
+
             float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8);
             float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8);
 
@@ -201,48 +198,54 @@ namespace Ryujinx.Graphics
             int VpW = (int)(TX + MathF.Abs(SX)) - VpX;
             int VpH = (int)(TY + MathF.Abs(SY)) - VpY;
 
-            GalImageFormat ImageFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)Format);
+            GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat);
 
-            GalImage Image = new GalImage(Width, Height, ImageFormat);
+            GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format);
 
-            long Size = ImageUtils.GetSize(Image);
+            Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
 
-            Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
+            Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH);
+        }
 
-            Gpu.Renderer.RenderTarget.BindColor(Key, FbIndex);
+        private void SetFrameBuffer(GalPipelineState State)
+        {
+            State.FramebufferSrgb = (ReadRegister(NvGpuEngine3dReg.FrameBufferSrgb) & 1) != 0;
 
-            Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH);
+            State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX);
+            State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY);
         }
 
         private void SetZeta(NvGpuVmm Vmm)
         {
-            long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress);
+            long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress);
+
+            int ZetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat);
+
+            int BlockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions);
+
+            int GobBlockHeight = 1 << ((BlockDim >> 4) & 7);
 
-            int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat);
+            GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); //?
 
             bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0;
 
-            if (ZA == 0 || Format == 0 || !ZetaEnable)
+            if (VA == 0 || ZetaFormat == 0 || !ZetaEnable)
             {
                 Gpu.Renderer.RenderTarget.UnbindZeta();
 
                 return;
             }
 
-            long Key = Vmm.GetPhysicalAddress(ZA);
+            long Key = Vmm.GetPhysicalAddress(VA);
 
             int Width  = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
             int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
 
-            GalImageFormat ImageFormat = ImageUtils.ConvertZeta((GalZetaFormat)Format);
-
-            GalImage Image = new GalImage(Width, Height, ImageFormat);
-
-            long Size = ImageUtils.GetSize(Image);
+            GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat);
 
-            Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
+            GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format);
 
-            Gpu.Renderer.RenderTarget.BindZeta(Key);
+            Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image);
         }
 
         private long[] UploadShaders(NvGpuVmm Vmm)
@@ -322,12 +325,6 @@ namespace Ryujinx.Graphics
             throw new ArgumentOutOfRangeException(nameof(Program));
         }
 
-        private void SetFlip(GalPipelineState State)
-        {
-            State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX);
-            State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY);
-        }
-
         private void SetFrontFace(GalPipelineState State)
         {
             float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX);
@@ -504,54 +501,31 @@ namespace Ryujinx.Graphics
             TicPosition += TicIndex * 0x20;
             TscPosition += TscIndex * 0x20;
 
+            GalImage Image = TextureFactory.MakeTexture(Vmm, TicPosition);
+
             GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition);
 
             long Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
 
-            Key = Vmm.GetPhysicalAddress(Key);
-
-            if (Key == -1)
+            if (Image.Layout == GalMemoryLayout.BlockLinear)
             {
-                //FIXME: Should'nt ignore invalid addresses.
-                return;
+                Key &= ~0x1ffL;
             }
-
-            if (IsFrameBufferPosition(Key))
+            else if (Image.Layout == GalMemoryLayout.Pitch)
             {
-                //This texture is a frame buffer texture,
-                //we shouldn't read anything from memory and bind
-                //the frame buffer texture instead, since we're not
-                //really writing anything to memory.
-                Gpu.Renderer.RenderTarget.BindTexture(Key, TexIndex);
+                Key &= ~0x1fL;
             }
-            else
-            {
-                GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition);
-
-                long Size = (uint)ImageUtils.GetSize(NewImage);
-
-                bool HasCachedTexture = false;
-
-                if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Image))
-                {
-                    if (NewImage.Equals(Image) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture))
-                    {
-                        Gpu.Renderer.Texture.Bind(Key, TexIndex);
-
-                        HasCachedTexture = true;
-                    }
-                }
-
-                if (!HasCachedTexture)
-                {
-                    byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
 
-                    Gpu.Renderer.Texture.Create(Key, Data, NewImage);
-                }
+            Key = Vmm.GetPhysicalAddress(Key);
 
-                Gpu.Renderer.Texture.Bind(Key, TexIndex);
+            if (Key == -1)
+            {
+                //FIXME: Shouldn't ignore invalid addresses.
+                return;
             }
 
+            Gpu.ResourceManager.SendTexture(Vmm, Key, Image, TexIndex);
+
             Gpu.Renderer.Texture.SetSampler(Sampler);
         }
 
@@ -876,11 +850,6 @@ namespace Ryujinx.Graphics
             Registers[(int)Reg] = Value;
         }
 
-        public bool IsFrameBufferPosition(long Position)
-        {
-            return FrameBuffers.Contains(Position);
-        }
-
         private bool QueryKeyUpload(NvGpuVmm Vmm, long Key, long Size, NvGpuBufferType Type)
         {
             List<long> Uploaded = UploadedKeys[(int)Type];

+ 2 - 0
Ryujinx.Graphics/NvGpuEngine3dReg.cs

@@ -6,6 +6,7 @@ namespace Ryujinx.Graphics
         FrameBufferNWidth    = 0x202,
         FrameBufferNHeight   = 0x203,
         FrameBufferNFormat   = 0x204,
+        FrameBufferNBlockDim = 0x205,
         ViewportNScaleX      = 0x280,
         ViewportNScaleY      = 0x281,
         ViewportNScaleZ      = 0x282,
@@ -62,6 +63,7 @@ namespace Ryujinx.Graphics
         StencilBackOpZFail   = 0x567,
         StencilBackOpZPass   = 0x568,
         StencilBackFuncFunc  = 0x569,
+        FrameBufferSrgb      = 0x56e,
         ShaderAddress        = 0x582,
         VertexBeginGl        = 0x586,
         PrimRestartEnable    = 0x591,

+ 0 - 143
Ryujinx.Graphics/NvGpuEngineDma.cs

@@ -1,143 +0,0 @@
-using Ryujinx.Graphics.Memory;
-using Ryujinx.Graphics.Texture;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics
-{
-    public class NvGpuEngineDma : INvGpuEngine
-    {
-        public int[] Registers { get; private set; }
-
-        private NvGpu Gpu;
-
-        private Dictionary<int, NvGpuMethod> Methods;
-
-        public NvGpuEngineDma(NvGpu Gpu)
-        {
-            this.Gpu = Gpu;
-
-            Registers = new int[0x1d6];
-
-            Methods = new Dictionary<int, NvGpuMethod>();
-
-            void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
-            {
-                while (Count-- > 0)
-                {
-                    Methods.Add(Meth, Method);
-
-                    Meth += Stride;
-                }
-            }
-
-            AddMethod(0xc0, 1, 1, Execute);
-        }
-
-        public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
-        {
-            if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
-            {
-                Method(Vmm, PBEntry);
-            }
-            else
-            {
-                WriteRegister(PBEntry);
-            }
-        }
-
-        private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
-        {
-            int Control = PBEntry.Arguments[0];
-
-            bool SrcLinear = ((Control >> 7) & 1) != 0;
-            bool DstLinear = ((Control >> 8) & 1) != 0;
-
-            long SrcAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.SrcAddress);
-            long DstAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.DstAddress);
-
-            int SrcPitch = ReadRegister(NvGpuEngineDmaReg.SrcPitch);
-            int DstPitch = ReadRegister(NvGpuEngineDmaReg.DstPitch);
-
-            int DstBlkDim = ReadRegister(NvGpuEngineDmaReg.DstBlkDim);
-            int DstSizeX  = ReadRegister(NvGpuEngineDmaReg.DstSizeX);
-            int DstSizeY  = ReadRegister(NvGpuEngineDmaReg.DstSizeY);
-            int DstSizeZ  = ReadRegister(NvGpuEngineDmaReg.DstSizeZ);
-            int DstPosXY  = ReadRegister(NvGpuEngineDmaReg.DstPosXY);
-            int DstPosZ   = ReadRegister(NvGpuEngineDmaReg.DstPosZ);
-
-            int SrcBlkDim = ReadRegister(NvGpuEngineDmaReg.SrcBlkDim);
-            int SrcSizeX  = ReadRegister(NvGpuEngineDmaReg.SrcSizeX);
-            int SrcSizeY  = ReadRegister(NvGpuEngineDmaReg.SrcSizeY);
-            int SrcSizeZ  = ReadRegister(NvGpuEngineDmaReg.SrcSizeZ);
-            int SrcPosXY  = ReadRegister(NvGpuEngineDmaReg.SrcPosXY);
-            int SrcPosZ   = ReadRegister(NvGpuEngineDmaReg.SrcPosZ);
-
-            int DstPosX = (DstPosXY >>  0) & 0xffff;
-            int DstPosY = (DstPosXY >> 16) & 0xffff;
-
-            int SrcPosX = (SrcPosXY >>  0) & 0xffff;
-            int SrcPosY = (SrcPosXY >> 16) & 0xffff;
-
-            int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf);
-            int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
-
-            ISwizzle SrcSwizzle;
-
-            if (SrcLinear)
-            {
-                SrcSwizzle = new LinearSwizzle(SrcPitch, 1);
-            }
-            else
-            {
-                SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, 1, SrcBlockHeight);
-            }
-
-            ISwizzle DstSwizzle;
-
-            if (DstLinear)
-            {
-                DstSwizzle = new LinearSwizzle(DstPitch, 1);
-            }
-            else
-            {
-                DstSwizzle = new BlockLinearSwizzle(DstSizeX, 1, DstBlockHeight);
-            }
-
-            for (int Y = 0; Y < DstSizeY; Y++)
-            for (int X = 0; X < DstSizeX; X++)
-            {
-                long SrcOffset = SrcAddress + (uint)SrcSwizzle.GetSwizzleOffset(X, Y);
-                long DstOffset = DstAddress + (uint)DstSwizzle.GetSwizzleOffset(X, Y);
-
-                Vmm.WriteByte(DstOffset, Vmm.ReadByte(SrcOffset));
-            }
-        }
-
-        private long MakeInt64From2xInt32(NvGpuEngineDmaReg Reg)
-        {
-            return
-                (long)Registers[(int)Reg + 0] << 32 |
-                (uint)Registers[(int)Reg + 1];
-        }
-
-        private void WriteRegister(NvGpuPBEntry PBEntry)
-        {
-            int ArgsCount = PBEntry.Arguments.Count;
-
-            if (ArgsCount > 0)
-            {
-                Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
-            }
-        }
-
-        private int ReadRegister(NvGpuEngineDmaReg Reg)
-        {
-            return Registers[(int)Reg];
-        }
-
-        private void WriteRegister(NvGpuEngineDmaReg Reg, int Value)
-        {
-            Registers[(int)Reg] = Value;
-        }
-    }
-}

+ 192 - 0
Ryujinx.Graphics/NvGpuEngineM2mf.cs

@@ -0,0 +1,192 @@
+using Ryujinx.Graphics.Memory;
+using Ryujinx.Graphics.Texture;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics
+{
+    public class NvGpuEngineM2mf : INvGpuEngine
+    {
+        public int[] Registers { get; private set; }
+
+        private NvGpu Gpu;
+
+        private Dictionary<int, NvGpuMethod> Methods;
+
+        public NvGpuEngineM2mf(NvGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            Registers = new int[0x1d6];
+
+            Methods = new Dictionary<int, NvGpuMethod>();
+
+            void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
+            {
+                while (Count-- > 0)
+                {
+                    Methods.Add(Meth, Method);
+
+                    Meth += Stride;
+                }
+            }
+
+            AddMethod(0xc0, 1, 1, Execute);
+        }
+
+        public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
+            {
+                Method(Vmm, PBEntry);
+            }
+            else
+            {
+                WriteRegister(PBEntry);
+            }
+        }
+
+        private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            //TODO: Some registers and copy modes are still not implemented.
+            int Control = PBEntry.Arguments[0];
+
+            bool SrcLinear = ((Control >> 7) & 1) != 0;
+            bool DstLinear = ((Control >> 8) & 1) != 0;
+            bool Copy2d    = ((Control >> 9) & 1) != 0;
+
+            long SrcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress);
+            long DstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress);
+
+            int SrcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch);
+            int DstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch);
+
+            int XCount = ReadRegister(NvGpuEngineM2mfReg.XCount);
+            int YCount = ReadRegister(NvGpuEngineM2mfReg.YCount);
+
+            int Swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle);
+
+            int DstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim);
+            int DstSizeX  = ReadRegister(NvGpuEngineM2mfReg.DstSizeX);
+            int DstSizeY  = ReadRegister(NvGpuEngineM2mfReg.DstSizeY);
+            int DstSizeZ  = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ);
+            int DstPosXY  = ReadRegister(NvGpuEngineM2mfReg.DstPosXY);
+            int DstPosZ   = ReadRegister(NvGpuEngineM2mfReg.DstPosZ);
+
+            int SrcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim);
+            int SrcSizeX  = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX);
+            int SrcSizeY  = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY);
+            int SrcSizeZ  = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ);
+            int SrcPosXY  = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY);
+            int SrcPosZ   = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ);
+
+            int SrcCpp = ((Swizzle >> 20) & 7) + 1;
+            int DstCpp = ((Swizzle >> 24) & 7) + 1;
+
+            int DstPosX = (DstPosXY >>  0) & 0xffff;
+            int DstPosY = (DstPosXY >> 16) & 0xffff;
+
+            int SrcPosX = (SrcPosXY >>  0) & 0xffff;
+            int SrcPosY = (SrcPosXY >> 16) & 0xffff;
+
+            int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf);
+            int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
+
+            long SrcPA = Vmm.GetPhysicalAddress(SrcAddress);
+            long DstPA = Vmm.GetPhysicalAddress(DstAddress);
+
+            if (Copy2d)
+            {
+                if (SrcLinear)
+                {
+                    SrcPosX = SrcPosY = SrcPosZ = 0;
+                }
+
+                if (DstLinear)
+                {
+                    DstPosX = DstPosY = DstPosZ = 0;
+                }
+
+                if (SrcLinear && DstLinear)
+                {
+                    for (int Y = 0; Y < YCount; Y++)
+                    {
+                        int SrcOffset = (SrcPosY + Y) * SrcPitch + SrcPosX * SrcCpp;
+                        int DstOffset = (DstPosY + Y) * DstPitch + DstPosX * DstCpp;
+
+                        long Src = SrcPA + (uint)SrcOffset;
+                        long Dst = DstPA + (uint)DstOffset;
+
+                        Vmm.Memory.CopyBytes(Src, Dst, XCount * SrcCpp);
+                    }
+                }
+                else
+                {
+                    ISwizzle SrcSwizzle;
+
+                    if (SrcLinear)
+                    {
+                        SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp);
+                    }
+                    else
+                    {
+                        SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight);
+                    }
+
+                    ISwizzle DstSwizzle;
+
+                    if (DstLinear)
+                    {
+                        DstSwizzle = new LinearSwizzle(DstPitch, DstCpp);
+                    }
+                    else
+                    {
+                        DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight);
+                    }
+
+                    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);
+
+                        long Src = SrcPA + (uint)SrcOffset;
+                        long Dst = DstPA + (uint)DstOffset;
+
+                        Vmm.Memory.CopyBytes(Src, Dst, SrcCpp);
+                    }
+                }
+            }
+            else
+            {
+                Vmm.Memory.CopyBytes(SrcPA, DstPA, XCount);
+            }
+        }
+
+        private long MakeInt64From2xInt32(NvGpuEngineM2mfReg Reg)
+        {
+            return
+                (long)Registers[(int)Reg + 0] << 32 |
+                (uint)Registers[(int)Reg + 1];
+        }
+
+        private void WriteRegister(NvGpuPBEntry PBEntry)
+        {
+            int ArgsCount = PBEntry.Arguments.Count;
+
+            if (ArgsCount > 0)
+            {
+                Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
+            }
+        }
+
+        private int ReadRegister(NvGpuEngineM2mfReg Reg)
+        {
+            return Registers[(int)Reg];
+        }
+
+        private void WriteRegister(NvGpuEngineM2mfReg Reg, int Value)
+        {
+            Registers[(int)Reg] = Value;
+        }
+    }
+}

+ 4 - 1
Ryujinx.Graphics/NvGpuEngineDmaReg.cs → Ryujinx.Graphics/NvGpuEngineM2mfReg.cs

@@ -1,11 +1,14 @@
 namespace Ryujinx.Graphics
 {
-    enum NvGpuEngineDmaReg
+    enum NvGpuEngineM2mfReg
     {
         SrcAddress = 0x100,
         DstAddress = 0x102,
         SrcPitch   = 0x104,
         DstPitch   = 0x105,
+        XCount     = 0x106,
+        YCount     = 0x107,
+        Swizzle    = 0x1c2,
         DstBlkDim  = 0x1c3,
         DstSizeX   = 0x1c4,
         DstSizeY   = 0x1c5,

+ 102 - 0
Ryujinx.Graphics/NvGpuEngineP2mf.cs

@@ -0,0 +1,102 @@
+using Ryujinx.Graphics.Memory;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Ryujinx.Graphics
+{
+    public class NvGpuEngineP2mf : INvGpuEngine
+    {
+        public int[] Registers { get; private set; }
+
+        private NvGpu Gpu;
+
+        private Dictionary<int, NvGpuMethod> Methods;
+
+        private ReadOnlyCollection<int> DataBuffer;
+
+        public NvGpuEngineP2mf(NvGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            Registers = new int[0x80];
+
+            Methods = new Dictionary<int, NvGpuMethod>();
+
+            void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
+            {
+                while (Count-- > 0)
+                {
+                    Methods.Add(Meth, Method);
+
+                    Meth += Stride;
+                }
+            }
+
+            AddMethod(0x6c, 1, 1, Execute);
+            AddMethod(0x6d, 1, 1, PushData);
+        }
+
+        public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
+            {
+                Method(Vmm, PBEntry);
+            }
+            else
+            {
+                WriteRegister(PBEntry);
+            }
+        }
+
+        private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            //TODO: Some registers and copy modes are still not implemented.
+            int Control = PBEntry.Arguments[0];
+
+            long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress);
+
+            int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn);
+
+            DataBuffer = null;
+
+            Gpu.Fifo.Step();
+
+            for (int Offset = 0; Offset < LineLengthIn; Offset += 4)
+            {
+                Vmm.WriteInt32(DstAddress + Offset, DataBuffer[Offset >> 2]);
+            }
+        }
+
+        private void PushData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            DataBuffer = PBEntry.Arguments;
+        }
+
+        private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg)
+        {
+            return
+                (long)Registers[(int)Reg + 0] << 32 |
+                (uint)Registers[(int)Reg + 1];
+        }
+
+        private void WriteRegister(NvGpuPBEntry PBEntry)
+        {
+            int ArgsCount = PBEntry.Arguments.Count;
+
+            if (ArgsCount > 0)
+            {
+                Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
+            }
+        }
+
+        private int ReadRegister(NvGpuEngineP2mfReg Reg)
+        {
+            return Registers[(int)Reg];
+        }
+
+        private void WriteRegister(NvGpuEngineP2mfReg Reg, int Value)
+        {
+            Registers[(int)Reg] = Value;
+        }
+    }
+}

+ 17 - 0
Ryujinx.Graphics/NvGpuEngineP2mfReg.cs

@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics
+{
+    enum NvGpuEngineP2mfReg
+    {
+        LineLengthIn = 0x60,
+        LineCount    = 0x61,
+        DstAddress   = 0x62,
+        DstPitch     = 0x64,
+        DstBlockDim  = 0x65,
+        DstWidth     = 0x66,
+        DstHeight    = 0x67,
+        DstDepth     = 0x68,
+        DstZ         = 0x69,
+        DstX         = 0x6a,
+        DstY         = 0x6b
+    }
+}

+ 35 - 30
Ryujinx.Graphics/NvGpuFifo.cs

@@ -94,6 +94,8 @@ namespace Ryujinx.Graphics
 
                 Gpu.Engine3d.ResetCache();
 
+                Gpu.ResourceManager.ClearPbCache();
+
                 CurrPbEntryIndex = 0;
             }
 
@@ -104,19 +106,35 @@ namespace Ryujinx.Graphics
 
         private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
         {
-            if (PBEntry.Method < 0x80)
+            if ((NvGpuFifoMeth)PBEntry.Method == NvGpuFifoMeth.BindChannel)
             {
-                switch ((NvGpuFifoMeth)PBEntry.Method)
-                {
-                    case NvGpuFifoMeth.BindChannel:
-                    {
-                        NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
+                NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
 
-                        SubChannels[PBEntry.SubChannel] = Engine;
+                SubChannels[PBEntry.SubChannel] = Engine;
+            }
+            else
+            {
+                switch (SubChannels[PBEntry.SubChannel])
+                {
+                    case NvGpuEngine._2d:  Call2dMethod  (Vmm, PBEntry); break;
+                    case NvGpuEngine._3d:  Call3dMethod  (Vmm, PBEntry); break;
+                    case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, PBEntry); break;
+                    case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, PBEntry); break;
+                }
+            }
+        }
 
-                        break;
-                    }
+        private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            Gpu.Engine2d.CallMethod(Vmm, PBEntry);
+        }
 
+        private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            if (PBEntry.Method < 0x80)
+            {
+                switch ((NvGpuFifoMeth)PBEntry.Method)
+                {
                     case NvGpuFifoMeth.SetMacroUploadAddress:
                     {
                         CurrMacroPosition = PBEntry.Arguments[0];
@@ -150,25 +168,7 @@ namespace Ryujinx.Graphics
                     }
                 }
             }
-            else
-            {
-                switch (SubChannels[PBEntry.SubChannel])
-                {
-                    case NvGpuEngine._2d: Call2dMethod (Vmm, PBEntry); break;
-                    case NvGpuEngine._3d: Call3dMethod (Vmm, PBEntry); break;
-                    case NvGpuEngine.Dma: CallDmaMethod(Vmm, PBEntry); break;
-                }
-            }
-        }
-
-        private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
-        {
-            Gpu.Engine2d.CallMethod(Vmm, PBEntry);
-        }
-
-        private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
-        {
-            if (PBEntry.Method < 0xe00)
+            else if (PBEntry.Method < 0xe00)
             {
                 Gpu.Engine3d.CallMethod(Vmm, PBEntry);
             }
@@ -190,9 +190,14 @@ namespace Ryujinx.Graphics
             }
         }
 
-        private void CallDmaMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        private void CallP2mfMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
+        {
+            Gpu.EngineP2mf.CallMethod(Vmm, PBEntry);
+        }
+
+        private void CallM2mfMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
         {
-            Gpu.EngineDma.CallMethod(Vmm, PBEntry);
+            Gpu.EngineM2mf.CallMethod(Vmm, PBEntry);
         }
     }
 }

+ 195 - 169
Ryujinx.Graphics/Texture/ImageUtils.cs

@@ -1,33 +1,37 @@
-using Ryujinx.Graphics.Gal;
+using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Memory;
 using System;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Texture
 {
-    static class ImageUtils
+    public static class ImageUtils
     {
-        struct ImageDescriptor
+        [Flags]
+        private enum TargetBuffer
         {
-            public TextureReaderDelegate Reader;
+            Color   = 1 << 0,
+            Depth   = 1 << 1,
+            Stencil = 1 << 2,
 
-            public bool HasColor;
-            public bool HasDepth;
-            public bool HasStencil;
+            DepthStencil = Depth | Stencil
+        }
+
+        private struct ImageDescriptor
+        {
+            public int BytesPerPixel { get; private set; }
+            public int BlockWidth    { get; private set; }
+            public int BlockHeight   { get; private set; }
 
-            public bool Compressed;
+            public TargetBuffer Target { get; private set; }
 
-            public ImageDescriptor(
-                TextureReaderDelegate Reader,
-                bool                  HasColor,
-                bool                  HasDepth,
-                bool                  HasStencil,
-                bool                  Compressed)
+            public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target)
             {
-                this.Reader     = Reader;
-                this.HasColor   = HasColor;
-                this.HasDepth   = HasDepth;
-                this.HasStencil = HasStencil;
-                this.Compressed = Compressed;
+                this.BytesPerPixel = BytesPerPixel;
+                this.BlockWidth    = BlockWidth;
+                this.BlockHeight   = BlockHeight;
+                this.Target        = Target;
             }
         }
 
@@ -48,6 +52,7 @@ namespace Ryujinx.Graphics.Texture
                 { GalTextureFormat.G8R8,         GalImageFormat.G8R8         | Snorm | Unorm | Sint | Uint          },
                 { GalTextureFormat.R16,          GalImageFormat.R16          | Snorm | Unorm | Sint | Uint | Sfloat },
                 { GalTextureFormat.R8,           GalImageFormat.R8           | Snorm | Unorm | Sint | Uint          },
+                { GalTextureFormat.R16G16,       GalImageFormat.R16G16       | Snorm                                },
                 { GalTextureFormat.R32,          GalImageFormat.R32                          | Sint | Uint | Sfloat },
                 { GalTextureFormat.A4B4G4R4,     GalImageFormat.A4B4G4R4             | Unorm                        },
                 { GalTextureFormat.A1B5G5R5,     GalImageFormat.A1R5G5B5             | Unorm                        },
@@ -85,58 +90,58 @@ namespace Ryujinx.Graphics.Texture
 
         private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
                             new Dictionary<GalImageFormat, ImageDescriptor>()
-            {
-                { GalImageFormat.R32G32B32A32,  new ImageDescriptor(TextureReader.Read16Bpp,                       true, false, false, false) },
-                { GalImageFormat.R16G16B16A16,  new ImageDescriptor(TextureReader.Read8Bpp,                        true, false, false, false) },
-                { GalImageFormat.R32G32,        new ImageDescriptor(TextureReader.Read8Bpp,                        true, false, false, false) },
-                { GalImageFormat.A8B8G8R8,      new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.A2B10G10R10,   new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.R32,           new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.A4B4G4R4,      new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
-                { GalImageFormat.BC6H_SF16,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.BC6H_UF16,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.A1R5G5B5,      new ImageDescriptor(TextureReader.Read5551,                        true, false, false, false) },
-                { GalImageFormat.B5G6R5,        new ImageDescriptor(TextureReader.Read565,                         true, false, false, false) },
-                { GalImageFormat.BC7,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.R16G16,        new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.R8G8,          new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
-                { GalImageFormat.G8R8,          new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
-                { GalImageFormat.R16,           new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
-                { GalImageFormat.R8,            new ImageDescriptor(TextureReader.Read1Bpp,                        true, false, false, false) },
-                { GalImageFormat.B10G11R11,     new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
-                { GalImageFormat.BC1_RGBA,      new ImageDescriptor(TextureReader.Read8Bpt4x4,                     true, false, false, true)  },
-                { GalImageFormat.BC2,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.BC3,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.BC4,           new ImageDescriptor(TextureReader.Read8Bpt4x4,                     true, false, false, true)  },
-                { GalImageFormat.BC5,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.ASTC_4x4,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
-                { GalImageFormat.ASTC_5x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x5,   true, false, false, true)  },
-                { GalImageFormat.ASTC_6x6,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x6,   true, false, false, true)  },
-                { GalImageFormat.ASTC_8x8,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x8,   true, false, false, true)  },
-                { GalImageFormat.ASTC_10x10,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x10, true, false, false, true)  },
-                { GalImageFormat.ASTC_12x12,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x12, true, false, false, true)  },
-                { GalImageFormat.ASTC_5x4,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x4,   true, false, false, true)  },
-                { GalImageFormat.ASTC_6x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x5,   true, false, false, true)  },
-                { GalImageFormat.ASTC_8x6,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x6,   true, false, false, true)  },
-                { GalImageFormat.ASTC_10x8,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x8,  true, false, false, true)  },
-                { GalImageFormat.ASTC_12x10,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x10, true, false, false, true)  },
-                { GalImageFormat.ASTC_8x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x5,   true, false, false, true)  },
-                { GalImageFormat.ASTC_10x5,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x5,  true, false, false, true)  },
-                { GalImageFormat.ASTC_10x6,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x6,  true, false, false, true)  },
-
-                { GalImageFormat.D24_S8, new ImageDescriptor(TextureReader.Read4Bpp, false, true, true,  false)  },
-                { GalImageFormat.D32,    new ImageDescriptor(TextureReader.Read4Bpp, false, true, false, false) },
-                { GalImageFormat.D16,    new ImageDescriptor(TextureReader.Read2Bpp, false, true, false, false) },
-                { GalImageFormat.D32_S8, new ImageDescriptor(TextureReader.Read8Bpp, false, true, true,  false)  },
-            };
+        {
+            { GalImageFormat.R32G32B32A32,  new ImageDescriptor(16, 1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R16G16B16A16,  new ImageDescriptor(8,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R32G32,        new ImageDescriptor(8,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.A8B8G8R8,      new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.A2B10G10R10,   new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R32,           new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.A4B4G4R4,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC6H_SF16,     new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
+            { GalImageFormat.BC6H_UF16,     new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
+            { GalImageFormat.A1R5G5B5,      new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.B5G6R5,        new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC7,           new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
+            { GalImageFormat.R16G16,        new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.R8G8,          new ImageDescriptor(2,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.G8R8,          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.B10G11R11,     new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(4,  1,  1,  TargetBuffer.Color) },
+            { GalImageFormat.BC1_RGBA,      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.ASTC_4x4,      new ImageDescriptor(16, 4,  4,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_5x5,      new ImageDescriptor(16, 5,  5,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_6x6,      new ImageDescriptor(16, 6,  6,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_8x8,      new ImageDescriptor(16, 8,  8,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_10x10,    new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
+            { GalImageFormat.ASTC_12x12,    new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
+            { GalImageFormat.ASTC_5x4,      new ImageDescriptor(16, 5,  4,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_6x5,      new ImageDescriptor(16, 6,  5,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_8x6,      new ImageDescriptor(16, 8,  6,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_10x8,     new ImageDescriptor(16, 10, 8,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_12x10,    new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
+            { GalImageFormat.ASTC_8x5,      new ImageDescriptor(16, 8,  5,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_10x5,     new ImageDescriptor(16, 10, 5,  TargetBuffer.Color) },
+            { GalImageFormat.ASTC_10x6,     new ImageDescriptor(16, 10, 6,  TargetBuffer.Color) },
+
+            { GalImageFormat.D24_S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
+            { GalImageFormat.D32,    new ImageDescriptor(4, 1, 1, TargetBuffer.Depth)        },
+            { GalImageFormat.D16,    new ImageDescriptor(2, 1, 1, TargetBuffer.Depth)        },
+            { GalImageFormat.D32_S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) },
+        };
 
         public static GalImageFormat ConvertTexture(
             GalTextureFormat Format,
-            GalTextureType RType,
-            GalTextureType GType,
-            GalTextureType BType,
-            GalTextureType AType)
+            GalTextureType   RType,
+            GalTextureType   GType,
+            GalTextureType   BType,
+            GalTextureType   AType)
         {
             if (RType != GType || RType != BType || RType != AType)
             {
@@ -202,128 +207,157 @@ namespace Ryujinx.Graphics.Texture
                 case GalZetaFormat.Z32Float:      return GalImageFormat.D32    | Sfloat;
                 case GalZetaFormat.S8Z24Unorm:    return GalImageFormat.D24_S8 | Unorm;
                 case GalZetaFormat.Z16Unorm:      return GalImageFormat.D16    | Unorm;
-                //This one might not be Uint, change when a texture uses this format
-                case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Uint;
+                case GalZetaFormat.Z24S8Unorm:    return GalImageFormat.D24_S8 | Unorm;
+                case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Sfloat;
             }
 
             throw new NotImplementedException(Format.ToString());
         }
 
-        public static TextureReaderDelegate GetReader(GalImageFormat Format)
+        public static byte[] ReadTexture(IAMemory Memory, GalImage Image, long Position)
+        {
+            AMemory CpuMemory;
+
+            if (Memory is NvGpuVmm Vmm)
+            {
+                CpuMemory = Vmm.Memory;
+            }
+            else
+            {
+                CpuMemory = (AMemory)Memory;
+            }
+
+            ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
+
+            ImageDescriptor Desc = GetImageDescriptor(Image.Format);
+
+            (int Width, int Height) = GetImageSizeInBlocks(Image);
+
+            int BytesPerPixel = Desc.BytesPerPixel;
+
+            int OutOffs = 0;
+
+            byte[] Data = new byte[Width * Height * BytesPerPixel];
+
+            for (int Y = 0; Y < Height; Y++)
+            for (int X = 0; X < Width;  X++)
+            {
+                long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
+
+                OutOffs += BytesPerPixel;
+            }
+
+            return Data;
+        }
+
+        public static void WriteTexture(NvGpuVmm Vmm, GalImage Image, long Position, byte[] Data)
         {
-            return GetImageDescriptor(Format).Reader;
+            ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
+
+            ImageDescriptor Desc = GetImageDescriptor(Image.Format);
+
+            (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image);
+
+            int BytesPerPixel = Desc.BytesPerPixel;
+
+            int InOffs = 0;
+
+            for (int Y = 0; Y < Height; Y++)
+            for (int X = 0; X < Width;  X++)
+            {
+                long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel);
+
+                InOffs += BytesPerPixel;
+            }
         }
 
         public static int GetSize(GalImage Image)
         {
-            switch (Image.Format & GalImageFormat.FormatMask)
+            ImageDescriptor Desc = GetImageDescriptor(Image.Format);
+
+            int Width  = DivRoundUp(Image.Width,  Desc.BlockWidth);
+            int Height = DivRoundUp(Image.Height, Desc.BlockHeight);
+
+            return Desc.BytesPerPixel * Width * Height;
+        }
+
+        public static int GetPitch(GalImageFormat Format, int Width)
+        {
+            ImageDescriptor Desc = GetImageDescriptor(Format);
+
+            return Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
+        }
+
+        public static int GetBlockWidth(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).BlockWidth;
+        }
+
+        public static int GetBlockHeight(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).BlockHeight;
+        }
+
+        public static int GetAlignedWidth(GalImage Image)
+        {
+            ImageDescriptor Desc = GetImageDescriptor(Image.Format);
+
+             int AlignMask;
+
+            if (Image.Layout == GalMemoryLayout.BlockLinear)
             {
-                case GalImageFormat.R32G32B32A32:
-                    return Image.Width * Image.Height * 16;
-
-                case GalImageFormat.R16G16B16A16:
-                case GalImageFormat.D32_S8:
-                case GalImageFormat.R32G32:
-                    return Image.Width * Image.Height * 8;
-
-                case GalImageFormat.A8B8G8R8:
-                case GalImageFormat.A8B8G8R8_SRGB:
-                case GalImageFormat.A2B10G10R10:
-                case GalImageFormat.R16G16:
-                case GalImageFormat.R32:
-                case GalImageFormat.D32:
-                case GalImageFormat.B10G11R11:
-                case GalImageFormat.D24_S8:
-                    return Image.Width * Image.Height * 4;
-
-                case GalImageFormat.B4G4R4A4:
-                case GalImageFormat.A1R5G5B5:
-                case GalImageFormat.B5G6R5:
-                case GalImageFormat.R8G8:
-                case GalImageFormat.G8R8:
-                case GalImageFormat.R16:
-                case GalImageFormat.D16:
-                    return Image.Width * Image.Height * 2;
-
-                case GalImageFormat.R8:
-                    return Image.Width * Image.Height;
-
-                case GalImageFormat.BC1_RGBA:
-                case GalImageFormat.BC4:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8);
-                }
-
-                case GalImageFormat.BC6H_SF16:
-                case GalImageFormat.BC6H_UF16:
-                case GalImageFormat.BC7:
-                case GalImageFormat.BC2:
-                case GalImageFormat.BC3:
-                case GalImageFormat.BC5:
-                case GalImageFormat.ASTC_4x4:
-                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16);
-
-                case GalImageFormat.ASTC_5x5:
-                    return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16);
-
-                case GalImageFormat.ASTC_6x6:
-                    return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16);
-
-                case GalImageFormat.ASTC_8x8:
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16);
-
-                case GalImageFormat.ASTC_10x10:
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16);
-
-                case GalImageFormat.ASTC_12x12:
-                    return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16);
-
-                case GalImageFormat.ASTC_5x4:
-                    return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16);
-
-                case GalImageFormat.ASTC_6x5:
-                    return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16);
-
-                case GalImageFormat.ASTC_8x6:
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16);
-
-                case GalImageFormat.ASTC_10x8:
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16);
-
-                case GalImageFormat.ASTC_12x10:
-                    return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16);
-
-                case GalImageFormat.ASTC_8x5:
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16);
-
-                case GalImageFormat.ASTC_10x5:
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16);
-
-                case GalImageFormat.ASTC_10x6:
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16);
+                AlignMask = Image.TileWidth * (64 / Desc.BytesPerPixel) - 1;
+            }
+            else
+            {
+                AlignMask = (32 / Desc.BytesPerPixel) - 1;
             }
 
-            throw new NotImplementedException((Image.Format & GalImageFormat.FormatMask).ToString());
+            return (Image.Width + AlignMask) & ~AlignMask;
+        }
+
+        public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image)
+        {
+            ImageDescriptor Desc = GetImageDescriptor(Image.Format);
+
+            return (DivRoundUp(Image.Width,  Desc.BlockWidth),
+                    DivRoundUp(Image.Height, Desc.BlockHeight));
+        }
+
+        public static int GetBytesPerPixel(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).BytesPerPixel;
+        }
+
+        private static int DivRoundUp(int LHS, int RHS)
+        {
+            return (LHS + (RHS - 1)) / RHS;
         }
 
         public static bool HasColor(GalImageFormat Format)
         {
-            return GetImageDescriptor(Format).HasColor;
+            return (GetImageDescriptor(Format).Target & TargetBuffer.Color) != 0;
         }
 
         public static bool HasDepth(GalImageFormat Format)
         {
-            return GetImageDescriptor(Format).HasDepth;
+            return (GetImageDescriptor(Format).Target & TargetBuffer.Depth) != 0;
         }
 
         public static bool HasStencil(GalImageFormat Format)
         {
-            return GetImageDescriptor(Format).HasStencil;
+            return (GetImageDescriptor(Format).Target & TargetBuffer.Stencil) != 0;
         }
 
         public static bool IsCompressed(GalImageFormat Format)
         {
-            return GetImageDescriptor(Format).Compressed;
+            ImageDescriptor Desc = GetImageDescriptor(Format);
+
+            return (Desc.BlockWidth | Desc.BlockHeight) != 1;
         }
 
         private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
@@ -351,13 +385,5 @@ namespace Ryujinx.Graphics.Texture
                 default: throw new NotImplementedException(((int)Type).ToString());
             }
         }
-
-        private static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)
-        {
-            int W = (TextureWidth + (BlockWidth - 1)) / BlockWidth;
-            int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight;
-
-            return W * H * Bpb;
-        }
     }
 }

+ 14 - 38
Ryujinx.Graphics/Texture/TextureFactory.cs

@@ -17,44 +17,20 @@ namespace Ryujinx.Graphics.Texture
             GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
             GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
 
-            int Width  = (Tic[4] & 0xffff) + 1;
-            int Height = (Tic[5] & 0xffff) + 1;
-
-            return new GalImage(
-                Width,
-                Height,
-                Format,
-                XSource,
-                YSource,
-                ZSource,
-                WSource);
-        }
-
-        public static byte[] GetTextureData(NvGpuVmm Vmm, long TicPosition)
-        {
-            int[] Tic = ReadWords(Vmm, TicPosition, 8);
-
-            GalImageFormat Format = GetImageFormat(Tic);
-
-            long TextureAddress = (uint)Tic[1];
-
-            TextureAddress |= (long)((ushort)Tic[2]) << 32;
-
             TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
 
+            GalMemoryLayout Layout;
+
             if (Swizzle == TextureSwizzle.BlockLinear ||
                 Swizzle == TextureSwizzle.BlockLinearColorKey)
             {
-                TextureAddress &= ~0x1ffL;
+                Layout = GalMemoryLayout.BlockLinear;
             }
-            else if (Swizzle == TextureSwizzle.Pitch ||
-                     Swizzle == TextureSwizzle.PitchColorKey)
+            else
             {
-                TextureAddress &= ~0x1fL;
+                Layout = GalMemoryLayout.Pitch;
             }
 
-            int Pitch = (Tic[3] & 0xffff) << 5;
-
             int BlockHeightLog2 = (Tic[3] >> 3)  & 7;
             int TileWidthLog2   = (Tic[3] >> 10) & 7;
 
@@ -64,17 +40,17 @@ namespace Ryujinx.Graphics.Texture
             int Width  = (Tic[4] & 0xffff) + 1;
             int Height = (Tic[5] & 0xffff) + 1;
 
-            TextureInfo Texture = new TextureInfo(
-                TextureAddress,
+            return new GalImage(
                 Width,
                 Height,
-                Pitch,
-                BlockHeight,
                 TileWidth,
-                Swizzle,
-                Format);
-
-            return TextureReader.Read(Vmm, Texture);
+                BlockHeight,
+                Layout,
+                Format,
+                XSource,
+                YSource,
+                ZSource,
+                WSource);
         }
 
         public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
@@ -107,7 +83,7 @@ namespace Ryujinx.Graphics.Texture
 
         private static GalImageFormat GetImageFormat(int[] Tic)
         {
-            GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7);
+            GalTextureType RType = (GalTextureType)((Tic[0] >> 7)  & 7);
             GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
             GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
             GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);

+ 13 - 16
Ryujinx.Graphics/Texture/TextureHelper.cs

@@ -1,33 +1,30 @@
 using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Memory;
-using System;
 
 namespace Ryujinx.Graphics.Texture
 {
     static class TextureHelper
     {
-        public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp)
+        public static ISwizzle GetSwizzle(GalImage Image)
         {
-            int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth;
+            int BlockWidth    = ImageUtils.GetBlockWidth   (Image.Format);
+            int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format);
 
-            int AlignMask = Texture.TileWidth * (64 / Bpp) - 1;
+            int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth;
 
-            Width = (Width + AlignMask) & ~AlignMask;
-
-            switch (Texture.Swizzle)
+            if (Image.Layout == GalMemoryLayout.BlockLinear)
             {
-                case TextureSwizzle._1dBuffer:
-                case TextureSwizzle.Pitch:
-                case TextureSwizzle.PitchColorKey:
-                     return new LinearSwizzle(Texture.Pitch, Bpp);
+                int AlignMask = Image.TileWidth * (64 / BytesPerPixel) - 1;
 
-                case TextureSwizzle.BlockLinear:
-                case TextureSwizzle.BlockLinearColorKey:
-                    return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
-            }
+                Width = (Width + AlignMask) & ~AlignMask;
 
-            throw new NotImplementedException(Texture.Swizzle.ToString());
+                return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight);
+            }
+            else
+            {
+                return new LinearSwizzle(Image.Pitch, BytesPerPixel);
+            }
         }
 
         public static (AMemory Memory, long Position) GetMemoryAndPosition(

+ 0 - 60
Ryujinx.Graphics/Texture/TextureInfo.cs

@@ -1,60 +0,0 @@
-using Ryujinx.Graphics.Gal;
-
-namespace Ryujinx.Graphics.Texture
-{
-    public struct TextureInfo
-    {
-        public long Position { get; private set; }
-
-        public int Width  { get; private set; }
-        public int Height { get; private set; }
-        public int Pitch  { get; private set; }
-
-        public int BlockHeight { get; private set; }
-        public int TileWidth   { get; private set; }
-
-        public TextureSwizzle Swizzle { get; private set; }
-
-        public GalImageFormat Format { get; private set; }
-
-        public TextureInfo(
-            long Position,
-            int  Width,
-            int  Height)
-        {
-            this.Position = Position;
-            this.Width    = Width;
-            this.Height   = Height;
-
-            Pitch = 0;
-
-            BlockHeight = 16;
-
-            TileWidth = 1;
-
-            Swizzle = TextureSwizzle.BlockLinear;
-
-            Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
-        }
-
-        public TextureInfo(
-            long             Position,
-            int              Width,
-            int              Height,
-            int              Pitch,
-            int              BlockHeight,
-            int              TileWidth,
-            TextureSwizzle   Swizzle,
-            GalImageFormat   Format)
-        {
-            this.Position     = Position;
-            this.Width        = Width;
-            this.Height       = Height;
-            this.Pitch        = Pitch;
-            this.BlockHeight  = BlockHeight;
-            this.TileWidth    = TileWidth;
-            this.Swizzle      = Swizzle;
-            this.Format       = Format;
-        }
-    }
-}

+ 0 - 398
Ryujinx.Graphics/Texture/TextureReader.cs

@@ -1,398 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Graphics.Gal;
-using System;
-
-namespace Ryujinx.Graphics.Texture
-{
-    delegate byte[] TextureReaderDelegate(IAMemory Memory, TextureInfo Texture);
-
-    public static class TextureReader
-    {
-        public static byte[] Read(IAMemory Memory, TextureInfo Texture)
-        {
-            TextureReaderDelegate Reader = ImageUtils.GetReader(Texture.Format);
-
-            return Reader(Memory, Texture);
-        }
-
-        internal unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 1);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    byte Pixel = CpuMem.ReadByte(Position + Offset);
-
-                    *(BuffPtr + OutOffs) = Pixel;
-
-                    OutOffs++;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 2];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
-
-                    Pixel = (Pixel & 0x001f) << 11 |
-                            (Pixel & 0x03e0) << 1  |
-                            (Pixel & 0x7c00) >> 9  |
-                            (Pixel & 0x8000) >> 15;
-
-                    *(short*)(BuffPtr + OutOffs) = (short)Pixel;
-
-                    OutOffs += 2;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 2];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
-
-                    Pixel = (Pixel & 0x001f) << 11 |
-                            (Pixel & 0x07e0)       |
-                            (Pixel & 0xf800) >> 11;
-
-                    *(short*)(BuffPtr + OutOffs) = (short)Pixel;
-
-                    OutOffs += 2;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 2];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    short Pixel = CpuMem.ReadInt16(Position + Offset);
-
-                    *(short*)(BuffPtr + OutOffs) = Pixel;
-
-                    OutOffs += 2;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 4];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    int Pixel = CpuMem.ReadInt32(Position + Offset);
-
-                    *(int*)(BuffPtr + OutOffs) = Pixel;
-
-                    OutOffs += 4;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 8];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 8);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    long Pixel = CpuMem.ReadInt64(Position + Offset);
-
-                    *(long*)(BuffPtr + OutOffs) = Pixel;
-
-                    OutOffs += 8;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = Texture.Width;
-            int Height = Texture.Height;
-
-            byte[] Output = new byte[Width * Height * 16];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 16);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    long PxLow  = CpuMem.ReadInt64(Position + Offset + 0);
-                    long PxHigh = CpuMem.ReadInt64(Position + Offset + 8);
-
-                    *(long*)(BuffPtr + OutOffs + 0) = PxLow;
-                    *(long*)(BuffPtr + OutOffs + 8) = PxHigh;
-
-                    OutOffs += 16;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
-        {
-            int Width  = (Texture.Width  + 3) / 4;
-            int Height = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[Width * Height * 8];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 4, 8);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    long Tile = CpuMem.ReadInt64(Position + Offset);
-
-                    *(long*)(BuffPtr + OutOffs) = Tile;
-
-                    OutOffs += 8;
-                }
-            }
-
-            return Output;
-        }
-
-        internal unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight)
-        {
-            int Width  = (Texture.Width  + (BlockWidth - 1)) / BlockWidth;
-            int Height = (Texture.Height + (BlockHeight - 1)) / BlockHeight;
-
-            byte[] Output = new byte[Width * Height * 16];
-
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, BlockWidth, 16);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Output)
-            {
-                long OutOffs = 0;
-
-                for (int Y = 0; Y < Height; Y++)
-                for (int X = 0; X < Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    long Tile0 = CpuMem.ReadInt64(Position + Offset + 0);
-                    long Tile1 = CpuMem.ReadInt64(Position + Offset + 8);
-
-                    *(long*)(BuffPtr + OutOffs + 0) = Tile0;
-                    *(long*)(BuffPtr + OutOffs + 8) = Tile1;
-
-                    OutOffs += 16;
-                }
-            }
-
-            return Output;
-        }
-
-        internal static byte[] Read16BptCompressedTexture4x4(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-        }
-
-        internal static byte[] Read16BptCompressedTexture5x5(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 5, 5);
-        }
-
-        internal static byte[] Read16BptCompressedTexture6x6(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 6, 6);
-        }
-
-        internal static byte[] Read16BptCompressedTexture8x8(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 8, 8);
-        }
-
-        internal static byte[] Read16BptCompressedTexture10x10(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 10, 10);
-        }
-
-        internal static byte[] Read16BptCompressedTexture12x12(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 12, 12);
-        }
-
-        internal static byte[] Read16BptCompressedTexture5x4(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 5, 4);
-        }
-
-        internal static byte[] Read16BptCompressedTexture6x5(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 6, 5);
-        }
-
-        internal static byte[] Read16BptCompressedTexture8x6(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 8, 6);
-        }
-
-        internal static byte[] Read16BptCompressedTexture10x8(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 10, 8);
-        }
-
-        internal static byte[] Read16BptCompressedTexture12x10(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 12, 10);
-        }
-
-        internal static byte[] Read16BptCompressedTexture8x5(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 5, 5);
-        }
-
-        internal static byte[] Read16BptCompressedTexture10x5(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 10, 5);
-        }
-
-        internal static byte[] Read16BptCompressedTexture10x6(IAMemory Memory, TextureInfo Texture)
-        {
-            return Read16BptCompressedTexture(Memory, Texture, 10, 6);
-        }
-    }
-}

+ 0 - 35
Ryujinx.Graphics/Texture/TextureWriter.cs

@@ -1,35 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Graphics.Gal;
-using Ryujinx.Graphics.Memory;
-
-namespace Ryujinx.Graphics.Texture
-{
-    static class TextureWriter
-    {
-        public unsafe static void Write(IAMemory Memory, TextureInfo Texture, byte[] Data)
-        {
-            ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4);
-
-            (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
-                Memory,
-                Texture.Position);
-
-            fixed (byte* BuffPtr = Data)
-            {
-                long InOffs = 0;
-
-                for (int Y = 0; Y < Texture.Height; Y++)
-                for (int X = 0; X < Texture.Width;  X++)
-                {
-                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
-
-                    int Pixel = *(int*)(BuffPtr + InOffs);
-
-                    CpuMem.WriteInt32(Position + Offset, Pixel);
-
-                    InOffs += 4;
-                }
-            }
-        }
-    }
-}

+ 17 - 0
Ryujinx.Graphics/ValueRange.cs

@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics
+{
+    struct ValueRange<T>
+    {
+        public long Start { get; private set; }
+        public long End   { get; private set; }
+
+        public T Value { get; set; }
+
+        public ValueRange(long Start, long End, T Value = default(T))
+        {
+            this.Start = Start;
+            this.End   = End;
+            this.Value = Value;
+        }
+    }
+}

+ 234 - 0
Ryujinx.Graphics/ValueRangeSet.cs

@@ -0,0 +1,234 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics
+{
+    class ValueRangeSet<T>
+    {
+        private List<ValueRange<T>> Ranges;
+
+        public ValueRangeSet()
+        {
+            Ranges = new List<ValueRange<T>>();
+        }
+
+        public void Add(ValueRange<T> Range)
+        {
+            if (Range.End <= Range.Start)
+            {
+                //Empty or invalid range, do nothing.
+                return;
+            }
+
+            int First = BinarySearchFirstIntersection(Range);
+
+            if (First == -1)
+            {
+                //No intersections case.
+                //Find first greater than range (after the current one).
+                //If found, add before, otherwise add to the end of the list.
+                int GtIndex = BinarySearchGt(Range);
+
+                if (GtIndex != -1)
+                {
+                    Ranges.Insert(GtIndex, Range);
+                }
+                else
+                {
+                    Ranges.Add(Range);
+                }
+
+                return;
+            }
+
+            (int Start, int End) = GetAllIntersectionRanges(Range, First);
+
+            ValueRange<T> Prev = Ranges[Start];
+            ValueRange<T> Next = Ranges[End];
+
+            Ranges.RemoveRange(Start, (End - Start) + 1);
+
+            InsertNextNeighbour(Start, Range, Next);
+
+            int NewIndex = Start;
+
+            Ranges.Insert(Start, Range);
+
+            InsertPrevNeighbour(Start, Range, Prev);
+
+            //Try merging neighbours if the value is equal.
+            if (NewIndex > 0)
+            {
+                Prev = Ranges[NewIndex - 1];
+
+                if (Prev.End == Range.Start && CompareValues(Prev, Range))
+                {
+                    Ranges.RemoveAt(--NewIndex);
+
+                    Ranges[NewIndex] = new ValueRange<T>(Prev.Start, Range.End, Range.Value);
+                }
+            }
+
+            if (NewIndex < Ranges.Count - 1)
+            {
+                Next = Ranges[NewIndex + 1];
+
+                if (Next.Start == Range.End && CompareValues(Next, Range))
+                {
+                    Ranges.RemoveAt(NewIndex + 1);
+
+                    Ranges[NewIndex] = new ValueRange<T>(Range.Start, Next.End, Range.Value);
+                }
+            }
+        }
+
+        private bool CompareValues(ValueRange<T> LHS, ValueRange<T> RHS)
+        {
+            return LHS.Value?.Equals(RHS.Value) ?? RHS.Value == null;
+        }
+
+        public void Remove(ValueRange<T> Range)
+        {
+            int First = BinarySearchFirstIntersection(Range);
+
+            if (First == -1)
+            {
+                //Nothing to remove.
+                return;
+            }
+
+            (int Start, int End) = GetAllIntersectionRanges(Range, First);
+
+            ValueRange<T> Prev = Ranges[Start];
+            ValueRange<T> Next = Ranges[End];
+
+            Ranges.RemoveRange(Start, (End - Start) + 1);
+
+            InsertNextNeighbour(Start, Range, Next);
+            InsertPrevNeighbour(Start, Range, Prev);
+        }
+
+        private void InsertNextNeighbour(int Index, ValueRange<T> Range, ValueRange<T> Next)
+        {
+            //Split last intersection (ordered by Start) if necessary.
+            if (Range.End < Next.End)
+            {
+                InsertNewRange(Index, Range.End, Next.End, Next.Value);
+            }
+        }
+
+        private void InsertPrevNeighbour(int Index, ValueRange<T> Range, ValueRange<T> Prev)
+        {
+            //Split first intersection (ordered by Start) if necessary.
+            if (Range.Start > Prev.Start)
+            {
+                InsertNewRange(Index, Prev.Start, Range.Start, Prev.Value);
+            }
+        }
+
+        private void InsertNewRange(int Index, long Start, long End, T Value)
+        {
+            Ranges.Insert(Index, new ValueRange<T>(Start, End, Value));
+        }
+
+        public ValueRange<T>[] GetAllIntersections(ValueRange<T> Range)
+        {
+            int First = BinarySearchFirstIntersection(Range);
+
+            if (First == -1)
+            {
+                return new ValueRange<T>[0];
+            }
+
+            (int Start, int End) = GetAllIntersectionRanges(Range, First);
+
+            return Ranges.GetRange(Start, (End - Start) + 1).ToArray();
+        }
+
+        private (int Start, int End) GetAllIntersectionRanges(ValueRange<T> Range, int BaseIndex)
+        {
+            int Start = BaseIndex;
+            int End   = BaseIndex;
+
+            while (Start > 0 && Intersects(Range, Ranges[Start - 1]))
+            {
+                Start--;
+            }
+
+            while (End < Ranges.Count - 1 && Intersects(Range, Ranges[End + 1]))
+            {
+                End++;
+            }
+
+            return (Start, End);
+        }
+
+        private int BinarySearchFirstIntersection(ValueRange<T> Range)
+        {
+            int Left  = 0;
+            int Right = Ranges.Count - 1;
+
+            while (Left <= Right)
+            {
+                int Size = Right - Left;
+
+                int Middle = Left + (Size >> 1);
+
+                ValueRange<T> Current = Ranges[Middle];
+
+                if (Intersects(Range, Current))
+                {
+                    return Middle;
+                }
+
+                if (Range.Start < Current.Start)
+                {
+                    Right = Middle - 1;
+                }
+                else
+                {
+                    Left = Middle + 1;
+                }
+            }
+
+            return -1;
+        }
+
+        private int BinarySearchGt(ValueRange<T> Range)
+        {
+            int GtIndex = -1;
+
+            int Left  = 0;
+            int Right = Ranges.Count - 1;
+
+            while (Left <= Right)
+            {
+                int Size = Right - Left;
+
+                int Middle = Left + (Size >> 1);
+
+                ValueRange<T> Current = Ranges[Middle];
+
+                if (Range.Start < Current.Start)
+                {
+                    Right = Middle - 1;
+
+                    if (GtIndex == -1 || Current.Start < Ranges[GtIndex].Start)
+                    {
+                        GtIndex = Middle;
+                    }
+                }
+                else
+                {
+                    Left = Middle + 1;
+                }
+            }
+
+            return GtIndex;
+        }
+
+        private bool Intersects(ValueRange<T> LHS, ValueRange<T> RHS)
+        {
+            return LHS.Start < RHS.End && RHS.Start < LHS.End;
+        }
+    }
+}

+ 2 - 1
Ryujinx.HLE/HOS/Kernel/SvcSystem.cs

@@ -288,7 +288,8 @@ namespace Ryujinx.HLE.HOS.Kernel
             //Fail for info not available on older Kernel versions.
             if (InfoType == 18 ||
                 InfoType == 19 ||
-                InfoType == 20)
+                InfoType == 20 ||
+                InfoType == 21)
             {
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
 

+ 0 - 1
Ryujinx.HLE/HOS/Services/Acc/IProfile.cs

@@ -3,7 +3,6 @@ using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.Utilities;
-using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Reflection;

+ 7 - 5
Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs

@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
 
         public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private KEvent ReleaseEvent;
+        private KEvent BinderEvent;
 
         private NvFlinger Flinger;
 
@@ -27,9 +27,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi
                 { 3, TransactParcelAuto }
             };
 
-            ReleaseEvent = new KEvent();
+            BinderEvent = new KEvent();
 
-            Flinger = new NvFlinger(Renderer, ReleaseEvent);
+            BinderEvent.WaitEvent.Set();
+
+            Flinger = new NvFlinger(Renderer, BinderEvent);
         }
 
         public long TransactParcel(ServiceCtx Context)
@@ -75,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
             int  Id  = Context.RequestData.ReadInt32();
             uint Unk = Context.RequestData.ReadUInt32();
 
-            int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent);
+            int Handle = Context.Process.HandleTable.OpenHandle(BinderEvent);
 
             Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
 
@@ -91,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
         {
             if (Disposing)
             {
-                ReleaseEvent.Dispose();
+                BinderEvent.Dispose();
 
                 Flinger.Dispose();
             }

+ 24 - 24
Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs

@@ -1,6 +1,7 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.Graphics.Texture;
+using Ryujinx.Graphics.Memory;
 using Ryujinx.HLE.HOS.Kernel;
+using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
 using Ryujinx.HLE.HOS.Services.Nv.NvMap;
 using Ryujinx.HLE.Logging;
 using System;
@@ -19,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
 
         private Dictionary<(string, int), ServiceProcessParcel> Commands;
 
-        private KEvent ReleaseEvent;
+        private KEvent BinderEvent;
 
         private IGalRenderer Renderer;
 
@@ -67,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
 
         private bool Disposed;
 
-        public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
+        public NvFlinger(IGalRenderer Renderer, KEvent BinderEvent)
         {
             Commands = new Dictionary<(string, int), ServiceProcessParcel>()
             {
@@ -82,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Services.Android
                 { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
             };
 
-            this.Renderer     = Renderer;
-            this.ReleaseEvent = ReleaseEvent;
+            this.Renderer    = Renderer;
+            this.BinderEvent = BinderEvent;
 
             BufferQueue = new BufferEntry[0x40];
 
@@ -301,42 +302,41 @@ namespace Ryujinx.HLE.HOS.Services.Android
             bool FlipX = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX);
             bool FlipY = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY);
 
-            //Rotation is being ignored
+            //Note: Rotation is being ignored.
 
             int Top    = Crop.Top;
             int Left   = Crop.Left;
             int Right  = Crop.Right;
             int Bottom = Crop.Bottom;
 
-            Renderer.QueueAction(() => Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom));
+            NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;
 
-            //TODO: Support double buffering here aswell, it is broken for GPU
-            //frame buffers because it seems to be completely out of sync.
-            if (Context.Device.Gpu.Engine3d.IsFrameBufferPosition(FbAddr))
+            Renderer.QueueAction(() =>
             {
-                //Frame buffer is rendered to by the GPU, we can just
-                //bind the frame buffer texture, it's not necessary to read anything.
-                Renderer.QueueAction(() => Renderer.RenderTarget.Set(FbAddr));
-            }
-            else
-            {
-                //Frame buffer is not set on the GPU registers, in this case
-                //assume that the app is manually writing to it.
-                TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight);
+                if (!Renderer.Texture.TryGetImage(FbAddr, out GalImage Image))
+                {
+                    Image = new GalImage(
+                        FbWidth,
+                        FbHeight, 1, 16,
+                        GalMemoryLayout.BlockLinear,
+                        GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
+                }
 
-                byte[] Data = TextureReader.Read(Context.Memory, Texture);
+                Context.Device.Gpu.ResourceManager.ClearPbCache();
+                Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image);
 
-                Renderer.QueueAction(() => Renderer.RenderTarget.Set(Data, FbWidth, FbHeight));
-            }
+                Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom);
+                Renderer.RenderTarget.Set(FbAddr);
 
-            Context.Device.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
+                ReleaseBuffer(Slot);
+            });
         }
 
         private void ReleaseBuffer(int Slot)
         {
             BufferQueue[Slot].State = BufferState.Free;
 
-            ReleaseEvent.WaitEvent.Set();
+            BinderEvent.WaitEvent.Set();
 
             lock (WaitBufferFree)
             {