Browse Source

Texture/Vertex/Index data cache (#132)

* Initial implementation of the texture cache

* Cache vertex and index data aswell, some cleanup

* Improve handling of the cache by storing cached ranges on a list for each page

* Delete old data from the caches automatically, ensure that the cache is cleaned when the mapping/size changes, and some general cleanup
gdkchan 7 years ago
parent
commit
231fae1a4c

+ 61 - 2
ChocolArm64/Memory/AMemory.cs

@@ -54,7 +54,14 @@ namespace ChocolArm64.Memory
 
 
             ExAddrs = new HashSet<long>();
             ExAddrs = new HashSet<long>();
 
 
-            Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                Ram = AMemoryWin32.Allocate((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize);
+            }
+            else
+            {
+                Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize);
+            }
 
 
             RamPtr = (byte*)Ram;
             RamPtr = (byte*)Ram;
         }
         }
@@ -141,6 +148,51 @@ namespace ChocolArm64.Memory
             }
             }
         }
         }
 
 
+        public long GetHostPageSize()
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return AMemoryMgr.PageSize;
+            }
+
+            IntPtr MemAddress = new IntPtr(RamPtr);
+            IntPtr MemSize    = new IntPtr(AMemoryMgr.RamSize);
+
+            long PageSize = AMemoryWin32.IsRegionModified(MemAddress, MemSize, Reset: false);
+
+            if (PageSize < 1)
+            {
+                throw new InvalidOperationException();
+            }
+
+            return PageSize;
+        }
+
+        public bool IsRegionModified(long Position, long Size)
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return true;
+            }
+
+            long EndPos = Position + Size;
+
+            if ((ulong)EndPos < (ulong)Position)
+            {
+                return false;
+            }
+
+            if ((ulong)EndPos > AMemoryMgr.RamSize)
+            {
+                return false;
+            }
+
+            IntPtr MemAddress = new IntPtr(RamPtr + Position);
+            IntPtr MemSize    = new IntPtr(Size);
+
+            return AMemoryWin32.IsRegionModified(MemAddress, MemSize, Reset: true) != 0;
+        }
+
         public sbyte ReadSByte(long Position)
         public sbyte ReadSByte(long Position)
         {
         {
             return (sbyte)ReadByte(Position);
             return (sbyte)ReadByte(Position);
@@ -640,7 +692,14 @@ namespace ChocolArm64.Memory
         {
         {
             if (Ram != IntPtr.Zero)
             if (Ram != IntPtr.Zero)
             {
             {
-                Marshal.FreeHGlobal(Ram);
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                {
+                    AMemoryWin32.Free(Ram);
+                }
+                else
+                {
+                    Marshal.FreeHGlobal(Ram);
+                }
 
 
                 Ram = IntPtr.Zero;
                 Ram = IntPtr.Zero;
             }
             }

+ 73 - 0
ChocolArm64/Memory/AMemoryWin32.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ChocolArm64.Memory
+{
+    static class AMemoryWin32
+    {
+        private const int MEM_COMMIT      = 0x00001000;
+        private const int MEM_RESERVE     = 0x00002000;
+        private const int MEM_WRITE_WATCH = 0x00200000;
+
+        private const int PAGE_READWRITE = 0x04;
+
+        private const int MEM_RELEASE = 0x8000;
+
+        private const int WRITE_WATCH_FLAG_RESET = 1;
+
+        [DllImport("kernel32.dll")]
+        private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, int flAllocationType, int flProtect);
+
+        [DllImport("kernel32.dll")]
+        private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, int dwFreeType);
+
+        [DllImport("kernel32.dll")]
+        private unsafe static extern int GetWriteWatch(
+            int      dwFlags,
+            IntPtr   lpBaseAddress,
+            IntPtr   dwRegionSize,
+            IntPtr[] lpAddresses,
+            long*    lpdwCount,
+            long*    lpdwGranularity);
+
+        public static IntPtr Allocate(IntPtr Size)
+        {
+            const int Flags = MEM_COMMIT | MEM_RESERVE | MEM_WRITE_WATCH;
+
+            IntPtr Address = VirtualAlloc(IntPtr.Zero, Size, Flags, PAGE_READWRITE);
+
+            if (Address == IntPtr.Zero)
+            {
+                throw new InvalidOperationException();
+            }
+
+            return Address;
+        }
+
+        public static void Free(IntPtr Address)
+        {
+            VirtualFree(Address, IntPtr.Zero, MEM_RELEASE);
+        }
+
+        public unsafe static long IsRegionModified(IntPtr Address, IntPtr Size, bool Reset)
+        {
+            IntPtr[] Addresses = new IntPtr[1];
+
+            long Count = Addresses.Length;
+
+            long Granularity;
+
+            int Flags = Reset ? WRITE_WATCH_FLAG_RESET : 0;
+
+            GetWriteWatch(
+                Flags,
+                Address,
+                Size,
+                Addresses,
+                &Count,
+                &Granularity);
+
+            return Count != 0 ? Granularity : 0;
+        }
+    }
+}

+ 1 - 1
Ryujinx.Core/Gpu/NvGpu.cs

@@ -3,7 +3,7 @@ using System.Threading;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public class NvGpu
+    class NvGpu
     {
     {
         public IGalRenderer Renderer { get; private set; }
         public IGalRenderer Renderer { get; private set; }
 
 

+ 9 - 0
Ryujinx.Core/Gpu/NvGpuBufferType.cs

@@ -0,0 +1,9 @@
+namespace Ryujinx.Core.Gpu
+{
+    enum NvGpuBufferType
+    {
+        Index,
+        Vertex,
+        Texture
+    }
+}

+ 1 - 1
Ryujinx.Core/Gpu/NvGpuEngine2d.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public class NvGpuEngine2d : INvGpuEngine
+    class NvGpuEngine2d : INvGpuEngine
     {
     {
         private enum CopyOperation
         private enum CopyOperation
         {
         {

+ 59 - 29
Ryujinx.Core/Gpu/NvGpuEngine3d.cs

@@ -4,7 +4,7 @@ using System.Collections.Generic;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public class NvGpuEngine3d : INvGpuEngine
+    class NvGpuEngine3d : INvGpuEngine
     {
     {
         public int[] Registers { get; private set; }
         public int[] Registers { get; private set; }
 
 
@@ -261,6 +261,8 @@ namespace Ryujinx.Core.Gpu
 
 
             long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
             long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
 
 
+            long Tag = TextureAddress;
+
             TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
             TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
 
 
             if (IsFrameBufferPosition(TextureAddress))
             if (IsFrameBufferPosition(TextureAddress))
@@ -273,10 +275,25 @@ namespace Ryujinx.Core.Gpu
             }
             }
             else
             else
             {
             {
-                GalTexture Texture = TextureFactory.MakeTexture(Gpu, Vmm, TicPosition);
+                GalTexture NewTexture = TextureFactory.MakeTexture(Vmm, TicPosition);
+
+                long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
+
+                if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture))
+                {
+                    if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture))
+                    {
+                        Gpu.Renderer.BindTexture(Tag, TexIndex);
+
+                        return;
+                    }
+                }
+
+                byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
 
 
-                Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
-                Gpu.Renderer.BindTexture(TexIndex);
+                Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler);
+
+                Gpu.Renderer.BindTexture(Tag, TexIndex);
             }
             }
         }
         }
 
 
@@ -330,11 +347,18 @@ namespace Ryujinx.Core.Gpu
 
 
             if (IndexSize != 0)
             if (IndexSize != 0)
             {
             {
-                int BufferSize = IndexCount * IndexSize;
+                int IbSize = IndexCount * IndexSize;
+
+                bool IboCached = Gpu.Renderer.IsIboCached(IndexPosition, (uint)IbSize);
+
+                if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index))
+                {
+                    byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize);
 
 
-                byte[] Data = Vmm.ReadBytes(IndexPosition, BufferSize);
+                    Gpu.Renderer.CreateIbo(IndexPosition, Data);
+                }
 
 
-                Gpu.Renderer.SetIndexArray(Data, IndexFormat);
+                Gpu.Renderer.SetIndexArray(IndexPosition, IbSize, IndexFormat);
             }
             }
 
 
             List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
             List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@@ -359,10 +383,17 @@ namespace Ryujinx.Core.Gpu
                                          ((Packed >> 31) & 0x1) != 0));
                                          ((Packed >> 31) & 0x1) != 0));
             }
             }
 
 
+            int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
+            int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
+
+            int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
+
             for (int Index = 0; Index < 32; Index++)
             for (int Index = 0; Index < 32; Index++)
             {
             {
-                int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
-                int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
+                if (Attribs[Index] == null)
+                {
+                    continue;
+                }
 
 
                 int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
                 int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
 
 
@@ -378,39 +409,38 @@ namespace Ryujinx.Core.Gpu
 
 
                 int Stride = Control & 0xfff;
                 int Stride = Control & 0xfff;
 
 
-                long Size = 0;
+                long VbSize = 0;
 
 
                 if (IndexCount != 0)
                 if (IndexCount != 0)
                 {
                 {
-                    Size = (VertexEndPos - VertexPosition) + 1;
+                    VbSize = (VertexEndPos - VertexPosition) + 1;
                 }
                 }
                 else
                 else
                 {
                 {
-                    Size = VertexCount;
+                    VbSize = VertexCount * Stride;
                 }
                 }
 
 
-                //TODO: Support cases where the Stride is 0.
-                //In this case, we need to use the size of the attribute.
-                Size *= Stride;
+                bool VboCached = Gpu.Renderer.IsVboCached(VertexPosition, VbSize);
 
 
-                byte[] Data = Vmm.ReadBytes(VertexPosition, Size);
-
-                GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
+                if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex))
+                {
+                    byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize);
 
 
-                Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray);
+                    Gpu.Renderer.CreateVbo(VertexPosition, Data);
+                }
 
 
-                int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
+                Gpu.Renderer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray());
+            }
 
 
-                GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
+            GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
 
 
-                if (IndexCount != 0)
-                {
-                    Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType);
-                }
-                else
-                {
-                    Gpu.Renderer.DrawArrays(Index, VertexFirst, VertexCount, PrimType);
-                }
+            if (IndexCount != 0)
+            {
+                Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType);
+            }
+            else
+            {
+                Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType);
             }
             }
         }
         }
 
 

+ 1 - 1
Ryujinx.Core/Gpu/NvGpuFifo.cs

@@ -2,7 +2,7 @@ using System.Collections.Concurrent;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public class NvGpuFifo
+    class NvGpuFifo
     {
     {
         private const int MacrosCount    = 0x80;
         private const int MacrosCount    = 0x80;
         private const int MacroIndexMask = MacrosCount - 1;
         private const int MacroIndexMask = MacrosCount - 1;

+ 1 - 1
Ryujinx.Core/Gpu/NvGpuPBEntry.cs

@@ -3,7 +3,7 @@ using System.Collections.ObjectModel;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public struct NvGpuPBEntry
+    struct NvGpuPBEntry
     {
     {
         public int Method { get; private set; }
         public int Method { get; private set; }
 
 

+ 1 - 1
Ryujinx.Core/Gpu/NvGpuPushBuffer.cs

@@ -3,7 +3,7 @@ using System.IO;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public static class NvGpuPushBuffer
+    static class NvGpuPushBuffer
     {
     {
         private enum SubmissionMode
         private enum SubmissionMode
         {
         {

+ 23 - 12
Ryujinx.Core/Gpu/NvGpuVmm.cs

@@ -4,24 +4,24 @@ using System.Collections.Concurrent;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public class NvGpuVmm : IAMemory, IGalMemory
+    class NvGpuVmm : IAMemory, IGalMemory
     {
     {
         public const long AddrSize = 1L << 40;
         public const long AddrSize = 1L << 40;
 
 
-        private const int  PTLvl0Bits = 14;
-        private const int  PTLvl1Bits = 14;
-        private const int  PTPageBits = 12;
+        private const int PTLvl0Bits = 14;
+        private const int PTLvl1Bits = 14;
+        private const int PTPageBits = 12;
 
 
-        private const int  PTLvl0Size = 1 << PTLvl0Bits;
-        private const int  PTLvl1Size = 1 << PTLvl1Bits;
-        public  const int  PageSize   = 1 << PTPageBits;
+        private const int PTLvl0Size = 1 << PTLvl0Bits;
+        private const int PTLvl1Size = 1 << PTLvl1Bits;
+        public  const int PageSize   = 1 << PTPageBits;
 
 
-        private const int  PTLvl0Mask = PTLvl0Size - 1;
-        private const int  PTLvl1Mask = PTLvl1Size - 1;
-        public  const int  PageMask   = PageSize   - 1;
+        private const int PTLvl0Mask = PTLvl0Size - 1;
+        private const int PTLvl1Mask = PTLvl1Size - 1;
+        public  const int PageMask   = PageSize   - 1;
 
 
-        private const int  PTLvl0Bit = PTPageBits + PTLvl1Bits;
-        private const int  PTLvl1Bit = PTPageBits;
+        private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
+        private const int PTLvl1Bit = PTPageBits;
 
 
         public AMemory Memory { get; private set; }
         public AMemory Memory { get; private set; }
 
 
@@ -37,6 +37,8 @@ namespace Ryujinx.Core.Gpu
 
 
         private ConcurrentDictionary<long, MappedMemory> Maps;
         private ConcurrentDictionary<long, MappedMemory> Maps;
 
 
+        private NvGpuVmmCache Cache;
+
         private const long PteUnmapped = -1;
         private const long PteUnmapped = -1;
         private const long PteReserved = -2;
         private const long PteReserved = -2;
 
 
@@ -48,6 +50,8 @@ namespace Ryujinx.Core.Gpu
 
 
             Maps = new ConcurrentDictionary<long, MappedMemory>();
             Maps = new ConcurrentDictionary<long, MappedMemory>();
 
 
+            Cache = new NvGpuVmmCache();
+
             PageTable = new long[PTLvl0Size][];
             PageTable = new long[PTLvl0Size][];
         }
         }
 
 
@@ -270,6 +274,13 @@ namespace Ryujinx.Core.Gpu
             PageTable[L0][L1] = TgtAddr;
             PageTable[L0][L1] = TgtAddr;
         }
         }
 
 
+        public bool IsRegionModified(long Position, long Size, NvGpuBufferType BufferType)
+        {
+            long PA = GetPhysicalAddress(Position);
+
+            return Cache.IsRegionModified(Memory, BufferType, Position, PA, Size);
+        }
+
         public byte ReadByte(long Position)
         public byte ReadByte(long Position)
         {
         {
             Position = GetPhysicalAddress(Position);
             Position = GetPhysicalAddress(Position);

+ 209 - 0
Ryujinx.Core/Gpu/NvGpuVmmCache.cs

@@ -0,0 +1,209 @@
+using ChocolArm64.Memory;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.Gpu
+{
+    class NvGpuVmmCache
+    {
+        private const int MaxCpCount     = 10000;
+        private const int MaxCpTimeDelta = 60000;
+
+        private class CachedPage
+        {
+            private List<(long Start, long End)> Regions;
+
+            public LinkedListNode<long> Node { get; set; }
+
+            public int Count => Regions.Count;
+
+            public int Timestamp { get; private set; }
+
+            public long PABase { get; private set; }
+
+            public NvGpuBufferType BufferType { get; private set; }
+
+            public CachedPage(long PABase, NvGpuBufferType BufferType)
+            {
+                this.PABase     = PABase;
+                this.BufferType = BufferType;
+
+                Regions = new List<(long, long)>();
+            }
+
+            public bool AddRange(long Start, long End)
+            {
+                for (int Index = 0; Index < Regions.Count; Index++)
+                {
+                    (long RgStart, long RgEnd) = Regions[Index];
+
+                    if (Start >= RgStart && End <= RgEnd)
+                    {
+                        return false;
+                    }
+
+                    if (Start <= RgEnd && RgStart <= End)
+                    {
+                        long MinStart = Math.Min(RgStart, Start);
+                        long MaxEnd   = Math.Max(RgEnd,   End);
+
+                        Regions[Index] = (MinStart, MaxEnd);
+
+                        Timestamp = Environment.TickCount;
+
+                        return true;
+                    }
+                }
+
+                Regions.Add((Start, End));
+
+                Timestamp = Environment.TickCount;
+
+                return true;
+            }
+        }
+
+        private Dictionary<long, CachedPage> Cache;
+
+        private LinkedList<long> SortedCache;
+
+        private int CpCount;
+
+        public NvGpuVmmCache()
+        {
+            Cache = new Dictionary<long, CachedPage>();
+
+            SortedCache = new LinkedList<long>();
+        }
+
+        public bool IsRegionModified(
+            AMemory         Memory,
+            NvGpuBufferType BufferType,
+            long            VA,
+            long            PA,
+            long            Size)
+        {
+            ClearCachedPagesIfNeeded();
+
+            long PageSize = Memory.GetHostPageSize();
+
+            long Mask = PageSize - 1;
+
+            long VAEnd = VA + Size;
+            long PAEnd = PA + Size;
+
+            bool RegMod = false;
+
+            while (VA < VAEnd)
+            {
+                long Key    = VA & ~Mask;
+                long PABase = PA & ~Mask;
+
+                long VAPgEnd = Math.Min((VA + PageSize) & ~Mask, VAEnd);
+                long PAPgEnd = Math.Min((PA + PageSize) & ~Mask, PAEnd);
+
+                bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
+
+                bool PgReset = false;
+
+                if (!IsCached)
+                {
+                    Cp = new CachedPage(PABase, BufferType);
+
+                    Cache.Add(Key, Cp);
+                }
+                else
+                {
+                    CpCount -= Cp.Count;
+
+                    SortedCache.Remove(Cp.Node);
+
+                    if (Cp.PABase     != PABase ||
+                        Cp.BufferType != BufferType)
+                    {
+                        PgReset = true;
+                    }
+                }
+
+                PgReset |= Memory.IsRegionModified(PA, PAPgEnd - PA) && IsCached;
+
+                if (PgReset)
+                {
+                    Cp = new CachedPage(PABase, BufferType);
+
+                    Cache[Key] = Cp;
+                }
+
+                Cp.Node = SortedCache.AddLast(Key);
+
+                RegMod |= Cp.AddRange(VA, VAPgEnd);
+
+                CpCount += Cp.Count;
+
+                VA = VAPgEnd;
+                PA = PAPgEnd;
+            }
+
+            return RegMod;
+        }
+
+        private void ClearCachedPagesIfNeeded()
+        {
+            if (CpCount <= MaxCpCount)
+            {
+                return;
+            }
+
+            int Timestamp = Environment.TickCount;
+
+            int TimeDelta;
+
+            do
+            {
+                if (!TryPopOldestCachedPageKey(Timestamp, out long Key))
+                {
+                    break;
+                }
+
+                CachedPage Cp = Cache[Key];
+
+                Cache.Remove(Key);
+
+                CpCount -= Cp.Count;
+
+                TimeDelta = RingDelta(Cp.Timestamp, Timestamp);
+            }
+            while (CpCount > (MaxCpCount >> 1) || (uint)TimeDelta > (uint)MaxCpTimeDelta);
+        }
+
+        private bool TryPopOldestCachedPageKey(int Timestamp, out long Key)
+        {
+            LinkedListNode<long> Node = SortedCache.First;
+
+            if (Node == null)
+            {
+                Key = 0;
+
+                return false;
+            }
+
+            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;
+            }
+        }
+    }
+}

+ 1 - 1
Ryujinx.Core/Gpu/Texture.cs

@@ -2,7 +2,7 @@ using Ryujinx.Graphics.Gal;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public struct Texture
+    struct Texture
     {
     {
         public long Position { get; private set; }
         public long Position { get; private set; }
 
 

+ 21 - 12
Ryujinx.Core/Gpu/TextureFactory.cs

@@ -5,7 +5,7 @@ namespace Ryujinx.Core.Gpu
 {
 {
     static class TextureFactory
     static class TextureFactory
     {
     {
-        public static GalTexture MakeTexture(NvGpu Gpu, NvGpuVmm Vmm, long TicPosition)
+        public static GalTexture MakeTexture(NvGpuVmm Vmm, long TicPosition)
         {
         {
             int[] Tic = ReadWords(Vmm, TicPosition, 8);
             int[] Tic = ReadWords(Vmm, TicPosition, 8);
 
 
@@ -16,6 +16,25 @@ namespace Ryujinx.Core.Gpu
             GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
             GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
             GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
             GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
 
 
+            int Width  = (Tic[4] & 0xffff) + 1;
+            int Height = (Tic[5] & 0xffff) + 1;
+
+            return new GalTexture(
+                Width,
+                Height,
+                Format,
+                XSource,
+                YSource,
+                ZSource,
+                WSource);
+        }
+
+        public static byte[] GetTextureData(NvGpuVmm Vmm, long TicPosition)
+        {
+            int[] Tic = ReadWords(Vmm, TicPosition, 8);
+
+            GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
+
             long TextureAddress = (uint)Tic[1];
             long TextureAddress = (uint)Tic[1];
 
 
             TextureAddress |= (long)((ushort)Tic[2]) << 32;
             TextureAddress |= (long)((ushort)Tic[2]) << 32;
@@ -40,17 +59,7 @@ namespace Ryujinx.Core.Gpu
                 Swizzle,
                 Swizzle,
                 Format);
                 Format);
 
 
-            byte[] Data = TextureReader.Read(Vmm, Texture);
-
-            return new GalTexture(
-                Data,
-                Width,
-                Height,
-                Format,
-                XSource,
-                YSource,
-                ZSource,
-                WSource);
+            return TextureReader.Read(Vmm, Texture);
         }
         }
 
 
         public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
         public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)

+ 38 - 0
Ryujinx.Core/Gpu/TextureHelper.cs

@@ -1,4 +1,5 @@
 using ChocolArm64.Memory;
 using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
 using System;
 using System;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
@@ -21,6 +22,43 @@ namespace Ryujinx.Core.Gpu
             throw new NotImplementedException(Texture.Swizzle.ToString());
             throw new NotImplementedException(Texture.Swizzle.ToString());
         }
         }
 
 
+        public static int GetTextureSize(GalTexture Texture)
+        {
+            switch (Texture.Format)
+            {
+                case GalTextureFormat.R32G32B32A32: return Texture.Width * Texture.Height * 16;
+                case GalTextureFormat.R16G16B16A16: return Texture.Width * Texture.Height * 8;
+                case GalTextureFormat.A8B8G8R8:     return Texture.Width * Texture.Height * 4;
+                case GalTextureFormat.R32:          return Texture.Width * Texture.Height * 4;
+                case GalTextureFormat.A1B5G5R5:     return Texture.Width * Texture.Height * 2;
+                case GalTextureFormat.B5G6R5:       return Texture.Width * Texture.Height * 2;
+                case GalTextureFormat.G8R8:         return Texture.Width * Texture.Height * 2;
+                case GalTextureFormat.R8:           return Texture.Width * Texture.Height;
+
+                case GalTextureFormat.BC1:
+                case GalTextureFormat.BC4:
+                {
+                    int W = (Texture.Width  + 3) / 4;
+                    int H = (Texture.Height + 3) / 4;
+
+                    return W * H * 8;
+                }
+
+                case GalTextureFormat.BC2:
+                case GalTextureFormat.BC3:
+                case GalTextureFormat.BC5:
+                case GalTextureFormat.Astc2D4x4:
+                {
+                    int W = (Texture.Width  + 3) / 4;
+                    int H = (Texture.Height + 3) / 4;
+
+                    return W * H * 16;
+                }
+            }
+
+            throw new NotImplementedException(Texture.Format.ToString());
+        }
+
         public static (AMemory Memory, long Position) GetMemoryAndPosition(
         public static (AMemory Memory, long Position) GetMemoryAndPosition(
             IAMemory Memory,
             IAMemory Memory,
             long     Position)
             long     Position)

+ 1 - 1
Ryujinx.Core/Gpu/TextureReader.cs

@@ -4,7 +4,7 @@ using System;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public static class TextureReader
+    static class TextureReader
     {
     {
         public static byte[] Read(IAMemory Memory, Texture Texture)
         public static byte[] Read(IAMemory Memory, Texture Texture)
         {
         {

+ 1 - 1
Ryujinx.Core/Gpu/TextureSwizzle.cs

@@ -1,6 +1,6 @@
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public enum TextureSwizzle
+    enum TextureSwizzle
     {
     {
         _1dBuffer           = 0,
         _1dBuffer           = 0,
         PitchColorKey       = 1,
         PitchColorKey       = 1,

+ 1 - 1
Ryujinx.Core/Gpu/TextureWriter.cs

@@ -4,7 +4,7 @@ using System;
 
 
 namespace Ryujinx.Core.Gpu
 namespace Ryujinx.Core.Gpu
 {
 {
-    public static class TextureWriter
+    static class TextureWriter
     {
     {
         public static void Write(IAMemory Memory, Texture Texture, byte[] Data)
         public static void Write(IAMemory Memory, Texture Texture, byte[] Data)
         {
         {

+ 1 - 0
Ryujinx.Core/OsHle/Kernel/SvcThread.cs

@@ -1,6 +1,7 @@
 using ChocolArm64.State;
 using ChocolArm64.State;
 using Ryujinx.Core.Logging;
 using Ryujinx.Core.Logging;
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Handles;
+using System;
 using System.Threading;
 using System.Threading;
 
 
 using static Ryujinx.Core.OsHle.ErrorCode;
 using static Ryujinx.Core.OsHle.ErrorCode;

+ 0 - 4
Ryujinx.Graphics/Gal/GalTexture.cs

@@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal
 {
 {
     public struct GalTexture
     public struct GalTexture
     {
     {
-        public byte[] Data;
-
         public int Width;
         public int Width;
         public int Height;
         public int Height;
 
 
@@ -15,7 +13,6 @@ namespace Ryujinx.Graphics.Gal
         public GalTextureSource WSource;
         public GalTextureSource WSource;
 
 
         public GalTexture(
         public GalTexture(
-            byte[]           Data,
             int              Width,
             int              Width,
             int              Height,
             int              Height,
             GalTextureFormat Format,
             GalTextureFormat Format,
@@ -24,7 +21,6 @@ namespace Ryujinx.Graphics.Gal
             GalTextureSource ZSource,
             GalTextureSource ZSource,
             GalTextureSource WSource)
             GalTextureSource WSource)
         {
         {
-            this.Data    = Data;
             this.Width   = Width;
             this.Width   = Width;
             this.Height  = Height;
             this.Height  = Height;
             this.Format  = Format;
             this.Format  = Format;

+ 16 - 6
Ryujinx.Graphics/Gal/IGalRenderer.cs

@@ -49,13 +49,21 @@ namespace Ryujinx.Graphics.Gal
         //Rasterizer
         //Rasterizer
         void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
         void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
 
 
-        void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
+        bool IsVboCached(long Tag, long DataSize);
 
 
-        void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
+        bool IsIboCached(long Tag, long DataSize);
 
 
-        void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType);
+        void CreateVbo(long Tag, byte[] Buffer);
 
 
-        void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
+        void CreateIbo(long Tag, byte[] Buffer);
+
+        void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs);
+
+        void SetIndexArray(long Tag, int Size, GalIndexFormat Format);
+
+        void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
+
+        void DrawElements(long IboTag, int First, GalPrimitiveType PrimType);
 
 
         //Shader
         //Shader
         void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type);
         void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type);
@@ -73,8 +81,10 @@ namespace Ryujinx.Graphics.Gal
         void BindProgram();
         void BindProgram();
 
 
         //Texture
         //Texture
-        void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
+        void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler);
+
+        bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture);
 
 
-        void BindTexture(int Index);
+        void BindTexture(long Tag, int Index);
     }
     }
 }
 }

+ 4 - 0
Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs

@@ -0,0 +1,4 @@
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    delegate void DeleteValue<T>(T Value);
+}

+ 147 - 0
Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs

@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLCachedResource<T>
+    {
+        public delegate void DeleteValue(T Value);
+
+        private const int MaxTimeDelta      = 5 * 60000;
+        private const int MaxRemovalsPerRun = 10;
+
+        private struct CacheBucket
+        {
+            public T Value { get; private set; }
+
+            public LinkedListNode<long> Node { get; private set; }
+
+            public long DataSize { get; private set; }
+
+            public int Timestamp { get; private set; }
+
+            public CacheBucket(T Value, long DataSize, LinkedListNode<long> Node)
+            {
+                this.Value     = Value;
+                this.DataSize  = DataSize;
+                this.Node      = Node;
+
+                Timestamp = Environment.TickCount;
+            }
+        }
+
+        private Dictionary<long, CacheBucket> Cache;
+
+        private LinkedList<long> SortedCache;
+
+        private DeleteValue DeleteValueCallback;
+
+        public OGLCachedResource(DeleteValue DeleteValueCallback)
+        {
+            if (DeleteValueCallback == null)
+            {
+                throw new ArgumentNullException(nameof(DeleteValueCallback));
+            }
+
+            this.DeleteValueCallback = DeleteValueCallback;
+
+            Cache = new Dictionary<long, CacheBucket>();
+
+            SortedCache = new LinkedList<long>();
+        }
+
+        public void AddOrUpdate(long Key, T Value, long Size)
+        {
+            ClearCacheIfNeeded();
+
+            LinkedListNode<long> Node = SortedCache.AddLast(Key);
+
+            CacheBucket NewBucket = new CacheBucket(Value, Size, Node);
+
+            if (Cache.TryGetValue(Key, out CacheBucket Bucket))
+            {
+                DeleteValueCallback(Bucket.Value);
+
+                SortedCache.Remove(Bucket.Node);
+
+                Cache[Key] = NewBucket;
+            }
+            else
+            {
+                Cache.Add(Key, NewBucket);
+            }
+        }
+
+        public bool TryGetValue(long Key, out T Value)
+        {
+            if (Cache.TryGetValue(Key, out CacheBucket Bucket))
+            {
+                Value = Bucket.Value;
+
+                return true;
+            }
+
+            Value = default(T);
+
+            return false;
+        }
+
+        public bool TryGetSize(long Key, out long Size)
+        {
+            if (Cache.TryGetValue(Key, out CacheBucket Bucket))
+            {
+                Size = Bucket.DataSize;
+
+                return true;
+            }
+
+            Size = 0;
+
+            return false;
+        }
+
+        private void ClearCacheIfNeeded()
+        {
+            int Timestamp = Environment.TickCount;
+
+            int Count = 0;
+
+            while (Count++ < MaxRemovalsPerRun)
+            {
+                LinkedListNode<long> Node = SortedCache.First;
+
+                if (Node == null)
+                {
+                    break;
+                }
+
+                CacheBucket Bucket = Cache[Node.Value];
+
+                int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp);
+
+                if ((uint)TimeDelta <= (uint)MaxTimeDelta)
+                {
+                    break;
+                }
+
+                SortedCache.Remove(Node);
+
+                Cache.Remove(Node.Value);
+
+                DeleteValueCallback(Bucket.Value);
+            }
+        }
+
+        private int RingDelta(int Old, int New)
+        {
+            if ((uint)New < (uint)Old)
+            {
+                return New + (~Old + 1);
+            }
+            else
+            {
+                return New - Old;
+            }
+        }
+    }
+}

+ 62 - 43
Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs

@@ -44,24 +44,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             { GalVertexAttribSize._11_11_10,    VertexAttribPointerType.Int   }  //?
             { GalVertexAttribSize._11_11_10,    VertexAttribPointerType.Int   }  //?
         };
         };
 
 
+        private int VaoHandle;
+
+        private int[] VertexBuffers;
+
+        private OGLCachedResource<int> VboCache;
+        private OGLCachedResource<int> IboCache;
+
         private struct IbInfo
         private struct IbInfo
         {
         {
-            public int IboHandle;
             public int Count;
             public int Count;
 
 
             public DrawElementsType Type;
             public DrawElementsType Type;
         }
         }
 
 
-        private int VaoHandle;
-
-        private int[] VertexBuffers;
-
         private IbInfo IndexBuffer;
         private IbInfo IndexBuffer;
 
 
         public OGLRasterizer()
         public OGLRasterizer()
         {
         {
             VertexBuffers = new int[32];
             VertexBuffers = new int[32];
 
 
+            VboCache = new OGLCachedResource<int>(GL.DeleteBuffer);
+            IboCache = new OGLCachedResource<int>(GL.DeleteBuffer);
+
             IndexBuffer = new IbInfo();
             IndexBuffer = new IbInfo();
         }
         }
 
 
@@ -92,15 +97,53 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.Clear(Mask);
             GL.Clear(Mask);
         }
         }
 
 
-        public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
+        public bool IsVboCached(long Tag, long DataSize)
+        {
+            return VboCache.TryGetSize(Tag, out long Size) && Size == DataSize;
+        }
+
+        public bool IsIboCached(long Tag, long DataSize)
+        {
+            return IboCache.TryGetSize(Tag, out long Size) && Size == DataSize;
+        }
+
+        public void CreateVbo(long Tag, byte[] Buffer)
         {
         {
-            EnsureVbInitialized(VbIndex);
+            int Handle = GL.GenBuffer();
+
+            VboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length);
 
 
             IntPtr Length = new IntPtr(Buffer.Length);
             IntPtr Length = new IntPtr(Buffer.Length);
 
 
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
             GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
             GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
             GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
             GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
+        }
+
+        public void CreateIbo(long Tag, byte[] Buffer)
+        {
+            int Handle = GL.GenBuffer();
+
+            IboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length);
+
+            IntPtr Length = new IntPtr(Buffer.Length);
+
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
+            GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
+        }
+
+        public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs)
+        {
+            if (!VboCache.TryGetValue(VboTag, out int VboHandle))
+            {
+                return;
+            }
+
+            if (VaoHandle == 0)
+            {
+                VaoHandle = GL.GenVertexArray();
+            }
 
 
             GL.BindVertexArray(VaoHandle);
             GL.BindVertexArray(VaoHandle);
 
 
@@ -108,7 +151,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
             {
                 GL.EnableVertexAttribArray(Attrib.Index);
                 GL.EnableVertexAttribArray(Attrib.Index);
 
 
-                GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]);
+                GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
 
 
                 bool Unsigned =
                 bool Unsigned =
                     Attrib.Type == GalVertexAttribType.Unorm ||
                     Attrib.Type == GalVertexAttribType.Unorm ||
@@ -139,22 +182,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.BindVertexArray(0);
             GL.BindVertexArray(0);
         }
         }
 
 
-        public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
+        public void SetIndexArray(long Tag, int Size, GalIndexFormat Format)
         {
         {
-            EnsureIbInitialized();
-
             IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
             IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
 
 
-            IndexBuffer.Count = Buffer.Length >> (int)Format;
-
-            IntPtr Length = new IntPtr(Buffer.Length);
-
-            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
-            GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
+            IndexBuffer.Count = Size >> (int)Format;
         }
         }
 
 
-        public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType)
+        public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType)
         {
         {
             if (PrimCount == 0)
             if (PrimCount == 0)
             {
             {
@@ -166,36 +201,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
             GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
         }
         }
 
 
-        public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
+        public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType)
         {
         {
+            if (!IboCache.TryGetValue(IboTag, out int IboHandle))
+            {
+                return;
+            }
+
             PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
             PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
 
 
             GL.BindVertexArray(VaoHandle);
             GL.BindVertexArray(VaoHandle);
 
 
-            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle);
 
 
             GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
             GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
         }
         }
-
-        private void EnsureVbInitialized(int VbIndex)
-        {
-            if (VaoHandle == 0)
-            {
-                VaoHandle = GL.GenVertexArray();
-            }
-
-            if (VertexBuffers[VbIndex] == 0)
-            {
-                VertexBuffers[VbIndex] = GL.GenBuffer();
-            }
-        }
-
-        private void EnsureIbInitialized()
-        {
-            if (IndexBuffer.IboHandle == 0)
-            {
-                IndexBuffer.IboHandle = GL.GenBuffer();
-            }
-        }
     }
     }
 }
 }

+ 62 - 41
Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs

@@ -6,18 +6,38 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 {
 {
     class OGLTexture
     class OGLTexture
     {
     {
-        private int[] Textures;
+        private class TCE
+        {
+            public int Handle;
+
+            public GalTexture Texture;
+
+            public TCE(int Handle, GalTexture Texture)
+            {
+                this.Handle  = Handle;
+                this.Texture = Texture;
+            }
+        }
+
+        private OGLCachedResource<TCE> TextureCache;
 
 
         public OGLTexture()
         public OGLTexture()
         {
         {
-            Textures = new int[80];
+            TextureCache = new OGLCachedResource<TCE>(DeleteTexture);
         }
         }
 
 
-        public void Set(int Index, GalTexture Texture)
+        private static void DeleteTexture(TCE CachedTexture)
         {
         {
-            GL.ActiveTexture(TextureUnit.Texture0 + Index);
+            GL.DeleteTexture(CachedTexture.Handle);
+        }
 
 
-            Bind(Index);
+        public void Create(long Tag, byte[] Data, GalTexture Texture)
+        {
+            int Handle = GL.GenTexture();
+
+            TextureCache.AddOrUpdate(Tag, new TCE(Handle, Texture), (uint)Data.Length);
+
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
 
 
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
             const int Border = 0;
@@ -33,14 +53,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Texture.Width,
                     Texture.Width,
                     Texture.Height,
                     Texture.Height,
                     Border,
                     Border,
-                    Texture.Data.Length,
-                    Texture.Data);
+                    Data.Length,
+                    Data);
             }
             }
             else
             else
             {
             {
                 if (Texture.Format >= GalTextureFormat.Astc2D4x4)
                 if (Texture.Format >= GalTextureFormat.Astc2D4x4)
                 {
                 {
-                    Texture = ConvertAstcTextureToRgba(Texture);
+                    int TextureBlockWidth  = GetAstcBlockWidth(Texture.Format);
+                    int TextureBlockHeight = GetAstcBlockHeight(Texture.Format);
+
+                    Data = ASTCDecoder.DecodeToRGBA8888(
+                        Data,
+                        TextureBlockWidth,
+                        TextureBlockHeight, 1,
+                        Texture.Width,
+                        Texture.Height, 1);
+
+                    Texture.Format = GalTextureFormat.A8B8G8R8;
                 }
                 }
 
 
                 const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
                 const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
@@ -56,7 +86,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Border,
                     Border,
                     Format,
                     Format,
                     Type,
                     Type,
-                    Texture.Data);
+                    Data);
             }
             }
 
 
             int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource);
             int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource);
@@ -70,23 +100,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
         }
         }
 
 
-        private static GalTexture ConvertAstcTextureToRgba(GalTexture Texture)
-        {
-            int TextureBlockWidth  = GetAstcBlockWidth(Texture.Format);
-            int TextureBlockHeight = GetAstcBlockHeight(Texture.Format);
-
-            Texture.Data = ASTCDecoder.DecodeToRGBA8888(
-                Texture.Data,
-                TextureBlockWidth,
-                TextureBlockHeight, 1,
-                Texture.Width,
-                Texture.Height, 1);
-
-            Texture.Format = GalTextureFormat.A8B8G8R8;
-
-            return Texture;
-        }
-
         private static int GetAstcBlockWidth(GalTextureFormat Format)
         private static int GetAstcBlockWidth(GalTextureFormat Format)
         {
         {
             switch (Format)
             switch (Format)
@@ -133,11 +146,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             throw new ArgumentException(nameof(Format));
             throw new ArgumentException(nameof(Format));
         }
         }
 
 
-        public void Bind(int Index)
+        public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture)
+        {
+            if (TextureCache.TryGetSize(Tag, out long Size) && Size == DataSize)
+            {
+                if (TextureCache.TryGetValue(Tag, out TCE CachedTexture))
+                {
+                    Texture = CachedTexture.Texture;
+
+                    return true;
+                }
+            }
+
+            Texture = default(GalTexture);
+
+            return false;
+        }
+
+        public void Bind(long Tag, int Index)
         {
         {
-            int Handle = EnsureTextureInitialized(Index);
+            if (TextureCache.TryGetValue(Tag, out TCE CachedTexture))
+            {
+                GL.ActiveTexture(TextureUnit.Texture0 + Index);
 
 
-            GL.BindTexture(TextureTarget.Texture2D, Handle);
+                GL.BindTexture(TextureTarget.Texture2D, CachedTexture.Handle);
+            }
         }
         }
 
 
         public static void Set(GalTextureSampler Sampler)
         public static void Set(GalTextureSampler Sampler)
@@ -179,17 +212,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
 
             return false;
             return false;
         }
         }
-
-        private int EnsureTextureInitialized(int TexIndex)
-        {
-            int Handle = Textures[TexIndex];
-
-            if (Handle == 0)
-            {
-                Handle = Textures[TexIndex] = GL.GenTexture();
-            }
-
-            return Handle;
-        }
     }
     }
 }
 }

+ 39 - 26
Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs

@@ -156,46 +156,54 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
             ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
         }
         }
 
 
-        public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
+        public bool IsVboCached(long Tag, long DataSize)
         {
         {
-            if ((uint)VbIndex > 31)
-            {
-                throw new ArgumentOutOfRangeException(nameof(VbIndex));
-            }
+            return Rasterizer.IsVboCached(Tag, DataSize);
+        }
 
 
-            ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride,
-                Buffer  ?? throw new ArgumentNullException(nameof(Buffer)),
-                Attribs ?? throw new ArgumentNullException(nameof(Attribs))));
+        public bool IsIboCached(long Tag, long DataSize)
+        {
+            return Rasterizer.IsIboCached(Tag, DataSize);
         }
         }
 
 
-        public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
+        public void CreateVbo(long Tag, byte[] Buffer)
         {
         {
-            if (Buffer == null)
-            {
-                throw new ArgumentNullException(nameof(Buffer));
-            }
+            ActionsQueue.Enqueue(() => Rasterizer.CreateVbo(Tag, Buffer));
+        }
 
 
-            ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
+        public void CreateIbo(long Tag, byte[] Buffer)
+        {
+            ActionsQueue.Enqueue(() => Rasterizer.CreateIbo(Tag, Buffer));
         }
         }
 
 
-        public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType)
+        public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs)
         {
         {
             if ((uint)VbIndex > 31)
             if ((uint)VbIndex > 31)
             {
             {
                 throw new ArgumentOutOfRangeException(nameof(VbIndex));
                 throw new ArgumentOutOfRangeException(nameof(VbIndex));
             }
             }
 
 
-            ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, First, PrimCount, PrimType));
+            if (Attribs == null)
+            {
+                throw new ArgumentNullException(nameof(Attribs));
+            }
+
+            ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride, VboTag, Attribs));
         }
         }
 
 
-        public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
+        public void SetIndexArray(long Tag, int Size, GalIndexFormat Format)
         {
         {
-            if ((uint)VbIndex > 31)
-            {
-                throw new ArgumentOutOfRangeException(nameof(VbIndex));
-            }
+            ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Tag, Size, Format));
+        }
+
+        public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType)
+        {
+            ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(First, PrimCount, PrimType));
+        }
 
 
-            ActionsQueue.Enqueue(() => Rasterizer.DrawElements(VbIndex, First, PrimType));
+        public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType)
+        {
+            ActionsQueue.Enqueue(() => Rasterizer.DrawElements(IboTag, First, PrimType));
         }
         }
 
 
         public void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type)
         public void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type)
@@ -253,19 +261,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             ActionsQueue.Enqueue(() => Shader.BindProgram());
             ActionsQueue.Enqueue(() => Shader.BindProgram());
         }
         }
 
 
-        public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler)
+        public void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler)
         {
         {
             ActionsQueue.Enqueue(() =>
             ActionsQueue.Enqueue(() =>
             {
             {
-                this.Texture.Set(Index, Texture);
+                this.Texture.Create(Tag, Data, Texture);
 
 
                 OGLTexture.Set(Sampler);
                 OGLTexture.Set(Sampler);
             });
             });
         }
         }
 
 
-        public void BindTexture(int Index)
+        public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture)
+        {
+            return this.Texture.TryGetCachedTexture(Tag, DataSize, out Texture);
+        }
+
+        public void BindTexture(long Tag, int Index)
         {
         {
-            ActionsQueue.Enqueue(() => Texture.Bind(Index));
+            ActionsQueue.Enqueue(() => Texture.Bind(Tag, Index));
         }
         }
     }
     }
 }
 }

+ 0 - 468
Ryujinx.Graphics/Gal/Texture/BCn.cs

@@ -1,468 +0,0 @@
-using System;
-using System.Drawing;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    static class BCn
-    {
-        public static byte[] DecodeBC1(GalTexture Texture, int Offset)
-        {
-            int W = (Texture.Width  + 3) / 4;
-            int H = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[W * H * 64];
-
-            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8);
-
-            for (int Y = 0; Y < H; Y++)
-            {
-                for (int X = 0; X < W; X++)
-                {
-                    int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8;
-
-                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs, true);
-
-                    int TOffset = 0;
-
-                    for (int TY = 0; TY < 4; TY++)
-                    {
-                        for (int TX = 0; TX < 4; TX++)
-                        {
-                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                            Output[OOffset + 0] = Tile[TOffset + 0];
-                            Output[OOffset + 1] = Tile[TOffset + 1];
-                            Output[OOffset + 2] = Tile[TOffset + 2];
-                            Output[OOffset + 3] = Tile[TOffset + 3];
-
-                            TOffset += 4;
-                        }
-                    }
-                }
-            }
-
-            return Output;
-        }
-
-        public static byte[] DecodeBC2(GalTexture Texture, int Offset)
-        {
-            int W = (Texture.Width  + 3) / 4;
-            int H = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[W * H * 64];
-
-            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
-
-            for (int Y = 0; Y < H; Y++)
-            {
-                for (int X = 0; X < W; X++)
-                {
-                    int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
-
-                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
-
-                    int AlphaLow  = Get32(Texture.Data, IOffs + 0);
-                    int AlphaHigh = Get32(Texture.Data, IOffs + 4);
-
-                    ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
-
-                    int TOffset = 0;
-
-                    for (int TY = 0; TY < 4; TY++)
-                    {
-                        for (int TX = 0; TX < 4; TX++)
-                        {
-                            ulong Alpha = (AlphaCh >> (TY * 16 + TX * 4)) & 0xf;
-
-                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                            Output[OOffset + 0] = Tile[TOffset + 0];
-                            Output[OOffset + 1] = Tile[TOffset + 1];
-                            Output[OOffset + 2] = Tile[TOffset + 2];
-                            Output[OOffset + 3] = (byte)(Alpha | (Alpha << 4));
-
-                            TOffset += 4;
-                        }
-                    }
-                }
-            }
-
-            return Output;
-        }
-
-        public static byte[] DecodeBC3(GalTexture Texture, int Offset)
-        {
-            int W = (Texture.Width  + 3) / 4;
-            int H = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[W * H * 64];
-
-            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
-
-            for (int Y = 0; Y < H; Y++)
-            {
-                for (int X = 0; X < W; X++)
-                {
-                    int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
-
-                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
-
-                    byte[] Alpha = new byte[8];
-
-                    Alpha[0] = Texture.Data[IOffs + 0];
-                    Alpha[1] = Texture.Data[IOffs + 1];
-
-                    CalculateBC3Alpha(Alpha);
-
-                    int AlphaLow  = Get32(Texture.Data, IOffs + 2);
-                    int AlphaHigh = Get16(Texture.Data, IOffs + 6);
-
-                    ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
-
-                    int TOffset = 0;
-
-                    for (int TY = 0; TY < 4; TY++)
-                    {
-                        for (int TX = 0; TX < 4; TX++)
-                        {
-                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                            byte AlphaPx = Alpha[(AlphaCh >> (TY * 12 + TX * 3)) & 7];
-
-                            Output[OOffset + 0] = Tile[TOffset + 0];
-                            Output[OOffset + 1] = Tile[TOffset + 1];
-                            Output[OOffset + 2] = Tile[TOffset + 2];
-                            Output[OOffset + 3] = AlphaPx;
-
-                            TOffset += 4;
-                        }
-                    }
-                }
-            }
-
-            return Output;
-        }
-
-        public static byte[] DecodeBC4(GalTexture Texture, int Offset)
-        {
-            int W = (Texture.Width  + 3) / 4;
-            int H = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[W * H * 64];
-
-            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 8);
-
-            for (int Y = 0; Y < H; Y++)
-            {
-                for (int X = 0; X < W; X++)
-                {
-                    int IOffs = Swizzle.GetSwizzledAddress64(X, Y) * 8;
-
-                    byte[] Red = new byte[8];
-
-                    Red[0] = Texture.Data[IOffs + 0];
-                    Red[1] = Texture.Data[IOffs + 1];
-
-                    CalculateBC3Alpha(Red);
-
-                    int RedLow  = Get32(Texture.Data, IOffs + 2);
-                    int RedHigh = Get16(Texture.Data, IOffs + 6);
-
-                    ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
-
-                    int TOffset = 0;
-
-                    for (int TY = 0; TY < 4; TY++)
-                    {
-                        for (int TX = 0; TX < 4; TX++)
-                        {
-                            int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                            byte RedPx = Red[(RedCh >> (TY * 12 + TX * 3)) & 7];
-
-                            Output[OOffset + 0] = RedPx;
-                            Output[OOffset + 1] = RedPx;
-                            Output[OOffset + 2] = RedPx;
-                            Output[OOffset + 3] = 0xff;
-
-                            TOffset += 4;
-                        }
-                    }
-                }
-            }
-
-            return Output;
-        }
-
-        public static byte[] DecodeBC5(GalTexture Texture, int Offset, bool SNorm)
-        {
-            int W = (Texture.Width  + 3) / 4;
-            int H = (Texture.Height + 3) / 4;
-
-            byte[] Output = new byte[W * H * 64];
-
-            SwizzleAddr Swizzle = new SwizzleAddr(W, H, 4);
-
-            for (int Y = 0; Y < H; Y++)
-            {
-                for (int X = 0; X < W; X++)
-                {
-                    int IOffs = Swizzle.GetSwizzledAddress128(X, Y) * 16;
-
-                    byte[] Red   = new byte[8];
-                    byte[] Green = new byte[8];
-
-                    Red[0]   = Texture.Data[IOffs + 0];
-                    Red[1]   = Texture.Data[IOffs + 1];
-
-                    Green[0] = Texture.Data[IOffs + 8];
-                    Green[1] = Texture.Data[IOffs + 9];
-
-                    if (SNorm)
-                    {
-                        CalculateBC3AlphaS(Red);
-                        CalculateBC3AlphaS(Green);
-                    }
-                    else
-                    {
-                        CalculateBC3Alpha(Red);
-                        CalculateBC3Alpha(Green);
-                    }
-
-                    int RedLow    = Get32(Texture.Data, IOffs + 2);
-                    int RedHigh   = Get16(Texture.Data, IOffs + 6);
-
-                    int GreenLow  = Get32(Texture.Data, IOffs + 10);
-                    int GreenHigh = Get16(Texture.Data, IOffs + 14);
-
-                    ulong RedCh   = (uint)RedLow   | (ulong)RedHigh   << 32;
-                    ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32;
-
-                    int TOffset = 0;
-
-                    if (SNorm)
-                    {
-                        for (int TY = 0; TY < 4; TY++)
-                        {
-                            for (int TX = 0; TX < 4; TX++)
-                            {
-                                int Shift = TY * 12 + TX * 3;
-
-                                int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                                byte RedPx   = Red  [(RedCh   >> Shift) & 7];
-                                byte GreenPx = Green[(GreenCh >> Shift) & 7];
-
-                                RedPx   += 0x80;
-                                GreenPx += 0x80;
-
-                                float NX = (RedPx   / 255f) * 2 - 1;
-                                float NY = (GreenPx / 255f) * 2 - 1;
-
-                                float NZ = (float)Math.Sqrt(1 - (NX * NX + NY * NY));
-
-                                Output[OOffset + 0] = Clamp((NZ + 1) * 0.5f);
-                                Output[OOffset + 1] = Clamp((NY + 1) * 0.5f);
-                                Output[OOffset + 2] = Clamp((NX + 1) * 0.5f);
-                                Output[OOffset + 3] = 0xff;
-
-                                TOffset += 4;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        for (int TY = 0; TY < 4; TY++)
-                        {
-                            for (int TX = 0; TX < 4; TX++)
-                            {
-                                int Shift = TY * 12 + TX * 3;
-
-                                int OOffset = (X * 4 + TX + (Y * 4 + TY) * W * 4) * 4;
-
-                                byte RedPx   = Red  [(RedCh   >> Shift) & 7];
-                                byte GreenPx = Green[(GreenCh >> Shift) & 7];
-
-                                Output[OOffset + 0] = RedPx;
-                                Output[OOffset + 1] = RedPx;
-                                Output[OOffset + 2] = RedPx;
-                                Output[OOffset + 3] = GreenPx;
-
-                                TOffset += 4;
-                            }
-                        }
-                    }
-                }
-            }
-
-            return Output;
-        }
-
-        private static byte Clamp(float Value)
-        {
-            if (Value > 1)
-            {
-                return 0xff;
-            }
-            else if (Value < 0)
-            {
-                return 0;
-            }
-            else
-            {
-                return (byte)(Value * 0xff);
-            }
-        }
-
-        private static void CalculateBC3Alpha(byte[] Alpha)
-        {
-            for (int i = 2; i < 8; i++)
-            {
-                if (Alpha[0] > Alpha[1])
-                {
-                    Alpha[i] = (byte)(((8 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7);
-                }
-                else if (i < 6)
-                {
-                    Alpha[i] = (byte)(((6 - i) * Alpha[0] + (i - 1) * Alpha[1]) / 7);
-                }
-                else if (i == 6)
-                {
-                    Alpha[i] = 0;
-                }
-                else /* i == 7 */
-                {
-                    Alpha[i] = 0xff;
-                }
-            }
-        }
-
-        private static void CalculateBC3AlphaS(byte[] Alpha)
-        {
-            for (int i = 2; i < 8; i++)
-            {
-                if ((sbyte)Alpha[0] > (sbyte)Alpha[1])
-                {
-                    Alpha[i] = (byte)(((8 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7);
-                }
-                else if (i < 6)
-                {
-                    Alpha[i] = (byte)(((6 - i) * (sbyte)Alpha[0] + (i - 1) * (sbyte)Alpha[1]) / 7);
-                }
-                else if (i == 6)
-                {
-                    Alpha[i] = 0x80;
-                }
-                else /* i == 7 */
-                {
-                    Alpha[i] = 0x7f;
-                }
-            }
-        }
-
-        private static byte[] BCnDecodeTile(
-            byte[] Input,
-            int    Offset,
-            bool   IsBC1)
-        {
-            Color[] CLUT = new Color[4];
-
-            int c0 = Get16(Input, Offset + 0);
-            int c1 = Get16(Input, Offset + 2);
-
-            CLUT[0] = DecodeRGB565(c0);
-            CLUT[1] = DecodeRGB565(c1);
-            CLUT[2] = CalculateCLUT2(CLUT[0], CLUT[1], c0, c1, IsBC1);
-            CLUT[3] = CalculateCLUT3(CLUT[0], CLUT[1], c0, c1, IsBC1);
-
-            int Indices = Get32(Input, Offset + 4);
-
-            int IdxShift = 0;
-
-            byte[] Output = new byte[4 * 4 * 4];
-
-            int OOffset = 0;
-
-            for (int TY = 0; TY < 4; TY++)
-            {
-                for (int TX = 0; TX < 4; TX++)
-                {
-                    int Idx = (Indices >> IdxShift) & 3;
-
-                    IdxShift += 2;
-
-                    Color Pixel = CLUT[Idx];
-
-                    Output[OOffset + 0] = Pixel.R;
-                    Output[OOffset + 1] = Pixel.G;
-                    Output[OOffset + 2] = Pixel.B;
-                    Output[OOffset + 3] = Pixel.A;
-
-                    OOffset += 4;
-                }
-            }
-
-            return Output;
-        }
-
-        private static Color CalculateCLUT2(Color C0, Color C1, int c0, int c1, bool IsBC1)
-        {
-            if (c0 > c1 || !IsBC1)
-            {
-                return Color.FromArgb(
-                    (2 * C0.R + C1.R) / 3,
-                    (2 * C0.G + C1.G) / 3,
-                    (2 * C0.B + C1.B) / 3);
-            }
-            else
-            {
-                return Color.FromArgb(
-                    (C0.R + C1.R) / 2,
-                    (C0.G + C1.G) / 2,
-                    (C0.B + C1.B) / 2);
-            }
-        }
-
-        private static Color CalculateCLUT3(Color C0, Color C1, int c0, int c1, bool IsBC1)
-        {
-            if (c0 > c1 || !IsBC1)
-            {
-                return
-                    Color.FromArgb(
-                        (2 * C1.R + C0.R) / 3,
-                        (2 * C1.G + C0.G) / 3,
-                        (2 * C1.B + C0.B) / 3);
-            }
-
-            return Color.Transparent;
-        }
-
-        private static Color DecodeRGB565(int Value)
-        {
-            int B = ((Value >>  0) & 0x1f) << 3;
-            int G = ((Value >>  5) & 0x3f) << 2;
-            int R = ((Value >> 11) & 0x1f) << 3;
-
-            return Color.FromArgb(
-                R | (R >> 5),
-                G | (G >> 6),
-                B | (B >> 5));
-        }
-
-        private static int Get16(byte[] Data, int Address)
-        {
-            return
-                Data[Address + 0] << 0 |
-                Data[Address + 1] << 8;
-        }
-
-        private static int Get32(byte[] Data, int Address)
-        {
-            return
-                Data[Address + 0] << 0 |
-                Data[Address + 1] << 8 |
-                Data[Address + 2] << 16 |
-                Data[Address + 3] << 24;
-        }
-    }
-}

+ 0 - 144
Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs

@@ -1,144 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    class SwizzleAddr
-    {
-        private int Width;
-
-        private int XB;
-        private int YB;
-
-        public SwizzleAddr(int Width, int Height, int Pad)
-        {
-            int W = Pow2RoundUp(Width);
-            int H = Pow2RoundUp(Height);
-
-            XB = CountZeros(W);
-            YB = CountZeros(H);
-
-            int HH = H >> 1;
-
-            if (!IsPow2(Height) && Height <= HH + HH / 3 && YB > 3)
-            {
-                YB--;
-            }
-
-            this.Width = RoundSize(Width, Pad);
-        }
-
-        private static int Pow2RoundUp(int Value)
-        {
-            Value--;
-
-            Value |= (Value >>  1);
-            Value |= (Value >>  2);
-            Value |= (Value >>  4);
-            Value |= (Value >>  8);
-            Value |= (Value >> 16);
-
-            return ++Value;
-        }
-
-        private static bool IsPow2(int Value)
-        {
-            return Value != 0 && (Value & (Value - 1)) == 0;
-        }
-
-        private static int CountZeros(int Value)
-        {
-            int Count = 0;
-
-            for (int i = 0; i < 32; i++)
-            {
-                if ((Value & (1 << i)) != 0)
-                {
-                    break;
-                }
-
-                Count++;
-            }
-
-            return Count;
-        }
-
-        private static int RoundSize(int Size, int Pad)
-        {
-            int Mask = Pad - 1;
-
-            if ((Size & Mask) != 0)
-            {
-                Size &= ~Mask;
-                Size +=  Pad;
-            }
-
-            return Size;
-        }
-
-        public int GetSwizzledAddress8(int X, int Y)
-        {
-            return GetSwizzledAddress(X, Y, 4);
-        }
-
-        public int GetSwizzledAddress16(int X, int Y)
-        {
-            return GetSwizzledAddress(X, Y, 3);
-        }
-
-        public int GetSwizzledAddress32(int X, int Y)
-        {
-            return GetSwizzledAddress(X, Y, 2);
-        }
-
-        public int GetSwizzledAddress64(int X, int Y)
-        {
-            return GetSwizzledAddress(X, Y, 1);
-        }
-
-        public int GetSwizzledAddress128(int X, int Y)
-        {
-            return GetSwizzledAddress(X, Y, 0);
-        }
-
-        private int GetSwizzledAddress(int X, int Y, int XBase)
-        {
-            /*
-             * Examples of patterns:
-             *                     x x y x y y x y 0 0 0 0 64   x 64   dxt5
-             *         x x x x x y y y y x y y x y 0 0 0 0 512  x 512  dxt5
-             *     y x x x x x x y y y y x y y x y 0 0 0 0 1024 x 1024 dxt5
-             *   y y x x x x x x y y y y x y y x y x 0 0 0 2048 x 2048 dxt1
-             * y y y x x x x x x y y y y x y y x y x x 0 0 1024 x 1024 rgba8888
-             *
-             * Read from right to left, LSB first.
-             */
-            int XCnt    = XBase;
-            int YCnt    = 1;
-            int XUsed   = 0;
-            int YUsed   = 0;
-            int Address = 0;
-
-            while (XUsed < XBase + 2 && XUsed + XCnt < XB)
-            {
-                int XMask = (1 << XCnt) - 1;
-                int YMask = (1 << YCnt) - 1;
-
-                Address |= (X & XMask) << XUsed + YUsed;
-                Address |= (Y & YMask) << XUsed + YUsed + XCnt;
-
-                X >>= XCnt;
-                Y >>= YCnt;
-
-                XUsed += XCnt;
-                YUsed += YCnt;
-
-                XCnt = Math.Min(XB - XUsed, 1);
-                YCnt = Math.Min(YB - YUsed, YCnt << 1);
-            }
-
-            Address |= (X + Y * (Width >> XUsed)) << (XUsed + YUsed);
-
-            return Address;
-        }
-    }
-}

+ 0 - 19
Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs

@@ -1,19 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    static class TextureDecoder
-    {
-        public static byte[] Decode(GalTexture Texture)
-        {
-            switch (Texture.Format)
-            {
-                case GalTextureFormat.BC1: return BCn.DecodeBC1(Texture, 0);
-                case GalTextureFormat.BC2: return BCn.DecodeBC2(Texture, 0);
-                case GalTextureFormat.BC3: return BCn.DecodeBC3(Texture, 0);
-            }
-
-            throw new NotImplementedException(Texture.Format.ToString());
-        }
-    }
-}