diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index c74e11b2c5..8939040d2b 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -53,7 +53,14 @@ namespace ChocolArm64.Memory ExAddrs = new HashSet(); - 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; } @@ -140,6 +147,31 @@ namespace ChocolArm64.Memory } } + 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); + } + public sbyte ReadSByte(long Position) { return (sbyte)ReadByte(Position); @@ -610,7 +642,14 @@ namespace ChocolArm64.Memory { if (Ram != IntPtr.Zero) { - Marshal.FreeHGlobal(Ram); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + AMemoryWin32.Free(Ram); + } + else + { + Marshal.FreeHGlobal(Ram); + } Ram = IntPtr.Zero; } diff --git a/ChocolArm64/Memory/AMemoryWin32.cs b/ChocolArm64/Memory/AMemoryWin32.cs new file mode 100644 index 0000000000..c21c808d37 --- /dev/null +++ b/ChocolArm64/Memory/AMemoryWin32.cs @@ -0,0 +1,79 @@ +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; + + [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); + + [DllImport("kernel32.dll")] + private unsafe static extern int ResetWriteWatch(IntPtr lpBaseAddress, IntPtr dwRegionSize); + + 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 bool IsRegionModified(IntPtr Address, IntPtr Size) + { + IntPtr[] Addresses = new IntPtr[1]; + + long Count = Addresses.Length; + + long Granularity; + + GetWriteWatch( + 0, + Address, + Size, + Addresses, + &Count, + &Granularity); + + if (Count != 0) + { + ResetWriteWatch(Address, Size); + + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs index b827debe23..ff2ce10364 100644 --- a/Ryujinx.Core/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Core/Gpu/NvGpuEngine3d.cs @@ -23,6 +23,8 @@ namespace Ryujinx.Core.Gpu private HashSet FrameBuffers; + private HashSet ModifiedPages; + public NvGpuEngine3d(NvGpu Gpu) { this.Gpu = Gpu; @@ -55,6 +57,8 @@ namespace Ryujinx.Core.Gpu } FrameBuffers = new HashSet(); + + ModifiedPages = new HashSet(); } public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) @@ -261,6 +265,8 @@ namespace Ryujinx.Core.Gpu long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; + long Tag = TextureAddress; + TextureAddress = Vmm.GetPhysicalAddress(TextureAddress); if (IsFrameBufferPosition(TextureAddress)) @@ -273,10 +279,27 @@ namespace Ryujinx.Core.Gpu } else { - GalTexture Texture = TextureFactory.MakeTexture(Gpu, Vmm, TicPosition); + GalTexture NewTexture = TextureFactory.MakeTexture(Vmm, TicPosition); - Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); - Gpu.Renderer.BindTexture(TexIndex); + if (Gpu.Renderer.TryGetCachedTexture(Tag, out GalTexture Texture)) + { + long Size = (uint)TextureHelper.GetTextureSize(NewTexture); + + if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size)) + { + //System.Console.WriteLine("skip upload texture!"); + + Gpu.Renderer.BindTexture(Tag, TexIndex); + + return; + } + } + + byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); + + Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler); + + Gpu.Renderer.BindTexture(Tag, TexIndex); } } @@ -386,13 +409,9 @@ namespace Ryujinx.Core.Gpu } else { - Size = VertexCount; + Size = VertexCount * Stride; } - //TODO: Support cases where the Stride is 0. - //In this case, we need to use the size of the attribute. - Size *= Stride; - byte[] Data = Vmm.ReadBytes(VertexPosition, Size); GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; diff --git a/Ryujinx.Core/Gpu/NvGpuVmm.cs b/Ryujinx.Core/Gpu/NvGpuVmm.cs index 1c408964fa..c044d06d69 100644 --- a/Ryujinx.Core/Gpu/NvGpuVmm.cs +++ b/Ryujinx.Core/Gpu/NvGpuVmm.cs @@ -270,6 +270,13 @@ namespace Ryujinx.Core.Gpu PageTable[L0][L1] = TgtAddr; } + public bool IsRegionModified(long Position, long Size) + { + Position = GetPhysicalAddress(Position); + + return Memory.IsRegionModified(Position, Size); + } + public byte ReadByte(long Position) { Position = GetPhysicalAddress(Position); diff --git a/Ryujinx.Core/Gpu/TextureFactory.cs b/Ryujinx.Core/Gpu/TextureFactory.cs index 5206c125b8..9262b94780 100644 --- a/Ryujinx.Core/Gpu/TextureFactory.cs +++ b/Ryujinx.Core/Gpu/TextureFactory.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Core.Gpu { 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); @@ -16,6 +16,25 @@ namespace Ryujinx.Core.Gpu 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 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]; TextureAddress |= (long)((ushort)Tic[2]) << 32; @@ -40,17 +59,7 @@ namespace Ryujinx.Core.Gpu Swizzle, 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) diff --git a/Ryujinx.Core/Gpu/TextureHelper.cs b/Ryujinx.Core/Gpu/TextureHelper.cs index f0ebc1f043..1863bb7770 100644 --- a/Ryujinx.Core/Gpu/TextureHelper.cs +++ b/Ryujinx.Core/Gpu/TextureHelper.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; using System; namespace Ryujinx.Core.Gpu @@ -21,6 +22,43 @@ namespace Ryujinx.Core.Gpu 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( IAMemory Memory, long Position) diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs index 8aa26dd3f7..b6346f4e87 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -1,6 +1,7 @@ using ChocolArm64.State; using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; +using System; using System.Threading; using static Ryujinx.Core.OsHle.ErrorCode; diff --git a/Ryujinx.Graphics/Gal/GalTexture.cs b/Ryujinx.Graphics/Gal/GalTexture.cs index 75aef30725..2c1be65b21 100644 --- a/Ryujinx.Graphics/Gal/GalTexture.cs +++ b/Ryujinx.Graphics/Gal/GalTexture.cs @@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal { public struct GalTexture { - public byte[] Data; - public int Width; public int Height; @@ -15,7 +13,6 @@ namespace Ryujinx.Graphics.Gal public GalTextureSource WSource; public GalTexture( - byte[] Data, int Width, int Height, GalTextureFormat Format, @@ -24,7 +21,6 @@ namespace Ryujinx.Graphics.Gal GalTextureSource ZSource, GalTextureSource WSource) { - this.Data = Data; this.Width = Width; this.Height = Height; this.Format = Format; diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 79c20e0e1b..3535888983 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -73,8 +73,10 @@ namespace Ryujinx.Graphics.Gal void BindProgram(); //Texture - void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler); + void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler); - void BindTexture(int Index); + bool TryGetCachedTexture(long Tag, out GalTexture Texture); + + void BindTexture(long Tag, int Index); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 2cbe6913d7..816e274bac 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -1,23 +1,48 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.Gal.Texture; using System; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { 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 Dictionary TextureCache; public OGLTexture() { - Textures = new int[80]; + TextureCache = new Dictionary(); } - public void Set(int Index, GalTexture Texture) + public void Create(long Tag, byte[] Data, GalTexture Texture) { - GL.ActiveTexture(TextureUnit.Texture0 + Index); + if (!TextureCache.TryGetValue(Tag, out TCE CachedTexture)) + { + int Handle = GL.GenTexture(); - Bind(Index); + CachedTexture = new TCE(Handle, Texture); + + TextureCache.Add(Tag, CachedTexture); + } + else + { + CachedTexture.Texture = Texture; + } + + GL.BindTexture(TextureTarget.Texture2D, CachedTexture.Handle); const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; @@ -33,14 +58,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL Texture.Width, Texture.Height, Border, - Texture.Data.Length, - Texture.Data); + Data.Length, + Data); } else { 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; @@ -56,7 +91,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Border, Format, Type, - Texture.Data); + Data); } int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource); @@ -70,23 +105,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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) { switch (Format) @@ -133,11 +151,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentException(nameof(Format)); } - public void Bind(int Index) + public bool TryGetCachedTexture(long Tag, out GalTexture Texture) { - int Handle = EnsureTextureInitialized(Index); + if (TextureCache.TryGetValue(Tag, out TCE CachedTexture)) + { + Texture = CachedTexture.Texture; - GL.BindTexture(TextureTarget.Texture2D, Handle); + return true; + } + + Texture = default(GalTexture); + + return false; + } + + public void Bind(long Tag, int Index) + { + if (TextureCache.TryGetValue(Tag, out TCE CachedTexture)) + { + GL.ActiveTexture(TextureUnit.Texture0 + Index); + + GL.BindTexture(TextureTarget.Texture2D, CachedTexture.Handle); + } } public static void Set(GalTextureSampler Sampler) @@ -179,17 +214,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL return false; } - - private int EnsureTextureInitialized(int TexIndex) - { - int Handle = Textures[TexIndex]; - - if (Handle == 0) - { - Handle = Textures[TexIndex] = GL.GenTexture(); - } - - return Handle; - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 69e344c74c..e1260bf97b 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -253,19 +253,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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(() => { - this.Texture.Set(Index, Texture); + this.Texture.Create(Tag, Data, Texture); OGLTexture.Set(Sampler); }); } - public void BindTexture(int Index) + public bool TryGetCachedTexture(long Tag, out GalTexture Texture) { - ActionsQueue.Enqueue(() => Texture.Bind(Index)); + return this.Texture.TryGetCachedTexture(Tag, out Texture); + } + + public void BindTexture(long Tag, int Index) + { + ActionsQueue.Enqueue(() => Texture.Bind(Tag, Index)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Texture/BCn.cs b/Ryujinx.Graphics/Gal/Texture/BCn.cs deleted file mode 100644 index f23a86c2c6..0000000000 --- a/Ryujinx.Graphics/Gal/Texture/BCn.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs b/Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs deleted file mode 100644 index b67b841bcc..0000000000 --- a/Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs b/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs deleted file mode 100644 index 4e50db51dd..0000000000 --- a/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs +++ /dev/null @@ -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()); - } - } -} \ No newline at end of file