diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-06-08 21:15:56 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-06-08 21:15:56 -0300 |
| commit | 231fae1a4c97d7588655e9775f37c1dc9bd55fb0 (patch) | |
| tree | 1c0e7b298ec33d5bf5b6a5693dd69a8c7e0bd23b /Ryujinx.Graphics | |
| parent | 6fe51f970501fe732276c17ed0dacb564b92a73d (diff) | |
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
Diffstat (limited to 'Ryujinx.Graphics')
| -rw-r--r-- | Ryujinx.Graphics/Gal/GalTexture.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/IGalRenderer.cs | 22 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs | 147 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 105 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 103 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 65 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Texture/BCn.cs | 468 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs | 144 | ||||
| -rw-r--r-- | Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs | 19 |
10 files changed, 330 insertions, 751 deletions
diff --git a/Ryujinx.Graphics/Gal/GalTexture.cs b/Ryujinx.Graphics/Gal/GalTexture.cs index 75aef307..2c1be65b 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 79c20e0e..b8f83469 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -49,13 +49,21 @@ namespace Ryujinx.Graphics.Gal //Rasterizer 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 void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type); @@ -73,8 +81,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); + + bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture); - void BindTexture(int Index); + void BindTexture(long Tag, int Index); } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs b/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs new file mode 100644 index 00000000..acd8d72f --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Gal.OpenGL +{ + delegate void DeleteValue<T>(T Value); +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs new file mode 100644 index 00000000..06d76b8b --- /dev/null +++ b/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; + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 5b115446..b63c8b35 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -44,24 +44,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? }; + private int VaoHandle; + + private int[] VertexBuffers; + + private OGLCachedResource<int> VboCache; + private OGLCachedResource<int> IboCache; + private struct IbInfo { - public int IboHandle; public int Count; public DrawElementsType Type; } - private int VaoHandle; - - private int[] VertexBuffers; - private IbInfo IndexBuffer; public OGLRasterizer() { VertexBuffers = new int[32]; + VboCache = new OGLCachedResource<int>(GL.DeleteBuffer); + IboCache = new OGLCachedResource<int>(GL.DeleteBuffer); + IndexBuffer = new IbInfo(); } @@ -92,15 +97,53 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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); - GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]); + GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); 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); @@ -108,7 +151,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.EnableVertexAttribArray(Attrib.Index); - GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBuffers[VbIndex]); + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); bool Unsigned = Attrib.Type == GalVertexAttribType.Unorm || @@ -139,22 +182,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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.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) { @@ -166,36 +201,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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); GL.BindVertexArray(VaoHandle); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle); 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(); - } - } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 2cbe6913..540e4735 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -6,18 +6,38 @@ 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 OGLCachedResource<TCE> TextureCache; 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 Border = 0; @@ -33,14 +53,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 +86,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Border, Format, Type, - Texture.Data); + Data); } int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource); @@ -70,23 +100,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 +146,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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) @@ -179,17 +212,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 69e344c7..4c4bd2ca 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -156,46 +156,54 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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) { 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) @@ -253,19 +261,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, 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)); } } }
\ 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 f23a86c2..00000000 --- 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 b67b841b..00000000 --- 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 4e50db51..00000000 --- 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 |
