diff --git a/Ryujinx.Common/Utilities/Buffers.cs b/Ryujinx.Common/Utilities/Buffers.cs new file mode 100644 index 00000000..d614bfc4 --- /dev/null +++ b/Ryujinx.Common/Utilities/Buffers.cs @@ -0,0 +1,59 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Utilities +{ + [DebuggerDisplay("{ToString()}")] + [StructLayout(LayoutKind.Sequential, Size = 16)] + public struct Buffer16 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; + + public byte this[int i] + { + get => Bytes[i]; + set => Bytes[i] = value; + } + + public Span Bytes => SpanHelpers.AsByteSpan(ref this); + + // Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Span(in Buffer16 value) + { + return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Buffer16 value) + { + return SpanHelpers.AsReadOnlyByteSpan(ref Unsafe.AsRef(in value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T As() where T : unmanaged + { + if (Unsafe.SizeOf() > (uint)Unsafe.SizeOf()) + { + throw new ArgumentException(); + } + + return ref MemoryMarshal.GetReference(AsSpan()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AsSpan() where T : unmanaged + { + return SpanHelpers.AsSpan(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly ReadOnlySpan AsReadOnlySpan() where T : unmanaged + { + return SpanHelpers.AsReadOnlySpan(ref Unsafe.AsRef(in this)); + } + } +} diff --git a/Ryujinx.Common/Utilities/SpanHelpers.cs b/Ryujinx.Common/Utilities/SpanHelpers.cs new file mode 100644 index 00000000..84c13023 --- /dev/null +++ b/Ryujinx.Common/Utilities/SpanHelpers.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Utilities +{ + public static class SpanHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span CreateSpan(ref T reference, int length) + { + return MemoryMarshal.CreateSpan(ref reference, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(ref T reference) where T : unmanaged + { + return CreateSpan(ref reference, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(ref TStruct reference) + where TStruct : unmanaged where TSpan : unmanaged + { + return CreateSpan(ref Unsafe.As(ref reference), + Unsafe.SizeOf() / Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsByteSpan(ref T reference) where T : unmanaged + { + return CreateSpan(ref Unsafe.As(ref reference), Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) + { + return MemoryMarshal.CreateReadOnlySpan(ref reference, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(ref T reference) where T : unmanaged + { + return CreateReadOnlySpan(ref reference, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(ref TStruct reference) + where TStruct : unmanaged where TSpan : unmanaged + { + return CreateReadOnlySpan(ref Unsafe.As(ref reference), + Unsafe.SizeOf() / Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlyByteSpan(ref T reference) where T : unmanaged + { + return CreateReadOnlySpan(ref Unsafe.As(ref reference), Unsafe.SizeOf()); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 11855592..b939e0b2 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -7,6 +7,7 @@ using Ryujinx.Graphics.Texture.Astc; using System; using System.Collections.Generic; using System.Diagnostics; +using Ryujinx.Common.Logging; namespace Ryujinx.Graphics.Gpu.Image { @@ -246,7 +247,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc()) { if (!AstcDecoder.TryDecodeToRgba8( - data, + data.ToArray(), _info.FormatInfo.BlockWidth, _info.FormatInfo.BlockHeight, _info.Width, diff --git a/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 2f24fd1e..4ba332d0 100644 --- a/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -1,20 +1,179 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using Ryujinx.Common.Utilities; +using System; using System.Diagnostics; -using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Texture.Astc { // https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp - public static class AstcDecoder + public class AstcDecoder { + private ReadOnlyMemory InputBuffer { get; } + private Memory OutputBuffer { get; } + + private int BlockSizeX { get; } + private int BlockSizeY { get; } + + private AstcLevel[] Levels { get; } + + private bool Success { get; set; } + + public int TotalBlockCount { get; } + + public AstcDecoder( + ReadOnlyMemory inputBuffer, + Memory outputBuffer, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels) + { + if ((uint)blockWidth > 12 || (uint)blockHeight > 12) + { + throw new AstcDecoderException("Invalid block size."); + } + + InputBuffer = inputBuffer; + OutputBuffer = outputBuffer; + + BlockSizeX = blockWidth; + BlockSizeY = blockHeight; + + Levels = new AstcLevel[levels]; + + TotalBlockCount = 0; + + int currentInputBlock = 0; + int currentOutputOffset = 0; + + for (int i = 0; i < levels; i++) + { + ref AstcLevel level = ref Levels[i]; + + level.ImageSizeX = Math.Max(1, width >> i); + level.ImageSizeY = Math.Max(1, height >> i); + level.ImageSizeZ = Math.Max(1, depth >> i); + + level.BlockCountX = (level.ImageSizeX + blockWidth - 1) / blockWidth; + level.BlockCountY = (level.ImageSizeY + blockHeight - 1) / blockHeight; + + level.StartBlock = currentInputBlock; + level.OutputByteOffset = currentOutputOffset; + + currentInputBlock += level.TotalBlockCount; + currentOutputOffset += level.PixelCount * 4; + } + + TotalBlockCount = currentInputBlock; + } + + private struct AstcLevel + { + public int ImageSizeX { get; set; } + public int ImageSizeY { get; set; } + public int ImageSizeZ { get; set; } + + public int BlockCountX { get; set; } + public int BlockCountY { get; set; } + + public int StartBlock { get; set; } + public int OutputByteOffset { get; set; } + + public int TotalBlockCount => BlockCountX * BlockCountY * ImageSizeZ; + public int PixelCount => ImageSizeX * ImageSizeY * ImageSizeZ; + } + + public static int QueryDecompressedSize(int sizeX, int sizeY, int sizeZ, int levelCount) + { + int size = 0; + + for (int i = 0; i < levelCount; i++) + { + int levelSizeX = Math.Max(1, sizeX >> i); + int levelSizeY = Math.Max(1, sizeY >> i); + int levelSizeZ = Math.Max(1, sizeZ >> i); + + size += levelSizeX * levelSizeY * levelSizeZ; + } + + return size * 4; + } + + public void ProcessBlock(int index) + { + Buffer16 inputBlock = MemoryMarshal.Cast(InputBuffer.Span)[index]; + + Span decompressedData = stackalloc int[144]; + + try + { + DecompressBlock(inputBlock, decompressedData, BlockSizeX, BlockSizeY); + } + catch (Exception) + { + Success = false; + } + + Span decompressedBytes = MemoryMarshal.Cast(decompressedData); + + AstcLevel levelInfo = GetLevelInfo(index); + + WriteDecompressedBlock(decompressedBytes, OutputBuffer.Span.Slice(levelInfo.OutputByteOffset), + index - levelInfo.StartBlock, levelInfo); + } + + private AstcLevel GetLevelInfo(int blockIndex) + { + foreach (AstcLevel levelInfo in Levels) + { + if (blockIndex < levelInfo.StartBlock + levelInfo.TotalBlockCount) + { + return levelInfo; + } + } + + throw new AstcDecoderException("Invalid block index."); + } + + private void WriteDecompressedBlock(ReadOnlySpan block, Span outputBuffer, int blockIndex, AstcLevel level) + { + int stride = level.ImageSizeX * 4; + + int blockCordX = blockIndex % level.BlockCountX; + int blockCordY = blockIndex / level.BlockCountX; + + int pixelCordX = blockCordX * BlockSizeX; + int pixelCordY = blockCordY * BlockSizeY; + + int outputPixelsX = Math.Min(pixelCordX + BlockSizeX, level.ImageSizeX) - pixelCordX; + int outputPixelsY = Math.Min(pixelCordY + BlockSizeY, level.ImageSizeY * level.ImageSizeZ) - pixelCordY; + + int outputStart = pixelCordX * 4 + pixelCordY * stride; + int outputOffset = outputStart; + + int inputOffset = 0; + + for (int i = 0; i < outputPixelsY; i++) + { + ReadOnlySpan blockRow = block.Slice(inputOffset, outputPixelsX * 4); + Span outputRow = outputBuffer.Slice(outputOffset); + blockRow.CopyTo(outputRow); + + inputOffset += BlockSizeX * 4; + outputOffset += stride; + } + } + struct TexelWeightParams { - public int Width; - public int Height; + public int Width; + public int Height; + public int MaxWeight; public bool DualPlane; - public int MaxWeight; public bool Error; public bool VoidExtentLdr; public bool VoidExtentHdr; @@ -48,96 +207,106 @@ namespace Ryujinx.Graphics.Texture.Astc } public static bool TryDecodeToRgba8( - Span data, - int blockWidth, - int blockHeight, - int width, - int height, - int depth, - int levels, + ReadOnlyMemory data, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels, out Span decoded) { - bool success = true; + byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels)]; - using (MemoryStream inputStream = new MemoryStream(data.ToArray())) + AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels); + + for (int i = 0; i < decoder.TotalBlockCount; i++) { - BinaryReader binReader = new BinaryReader(inputStream); - - using (MemoryStream outputStream = new MemoryStream()) - { - int blockIndex = 0; - - int mipOffset = 0; - - for (int l = 0; l < levels; l++) - { - int sliceSize = width * height * 4; - - for (int k = 0; k < depth; k++) - for (int j = 0; j < height; j += blockHeight) - for (int i = 0; i < width; i += blockWidth) - { - int[] decompressedData = new int[144]; - - try - { - DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockWidth, blockHeight); - } - catch (Exception) - { - success = false; - } - - int decompressedWidth = Math.Min(blockWidth, width - i); - int decompressedHeight = Math.Min(blockHeight, height - j); - - int baseOffset = mipOffset + k * sliceSize + (j * width + i) * 4; - - for (int jj = 0; jj < decompressedHeight; jj++) - { - outputStream.Seek(baseOffset + jj * width * 4, SeekOrigin.Begin); - - byte[] outputBuffer = new byte[decompressedData.Length * sizeof(int)]; - - Buffer.BlockCopy(decompressedData, 0, outputBuffer, 0, outputBuffer.Length); - - outputStream.Write(outputBuffer, jj * blockWidth * 4, decompressedWidth * 4); - } - - blockIndex++; - } - - mipOffset += sliceSize * depth; - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - depth = Math.Max(1, depth >> 1); - } - - decoded = outputStream.ToArray(); - } + decoder.ProcessBlock(i); } - return success; + decoded = output; + + return decoder.Success; + } + + public static bool TryDecodeToRgba8( + ReadOnlyMemory data, + Memory outputBuffer, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels) + { + AstcDecoder decoder = new AstcDecoder(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels); + + for (int i = 0; i < decoder.TotalBlockCount; i++) + { + decoder.ProcessBlock(i); + } + + return decoder.Success; + } + + public static bool TryDecodeToRgba8P( + ReadOnlyMemory data, + Memory outputBuffer, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels) + { + AstcDecoder decoder = new AstcDecoder(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels); + + // Lazy parallelism + Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); + + return decoder.Success; + } + + public static bool TryDecodeToRgba8P( + ReadOnlyMemory data, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels, + out Span decoded) + { + byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels)]; + + AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels); + + Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); + + decoded = output; + + return decoder.Success; } public static bool DecompressBlock( - byte[] inputBuffer, - int[] outputBuffer, - int blockWidth, - int blockHeight) + Buffer16 inputBlock, + Span outputBuffer, + int blockWidth, + int blockHeight) { - BitArrayStream bitStream = new BitArrayStream(new BitArray(inputBuffer)); - TexelWeightParams texelParams = DecodeBlockInfo(bitStream); + BitStream128 bitStream = new BitStream128(inputBlock); + + DecodeBlockInfo(ref bitStream, out TexelWeightParams texelParams); if (texelParams.Error) { - throw new AstcDecoderException("Invalid block mode."); + throw new AstcDecoderException("Invalid block mode"); } if (texelParams.VoidExtentLdr) { - FillVoidExtentLdr(bitStream, outputBuffer, blockWidth, blockHeight); + FillVoidExtentLdr(ref bitStream, outputBuffer, blockWidth, blockHeight); return true; } @@ -170,11 +339,12 @@ namespace Ryujinx.Graphics.Texture.Astc // each partition. // Determine partitions, partition index, and color endpoint modes - int planeIndices = -1; - int partitionIndex; - uint[] colorEndpointMode = { 0, 0, 0, 0 }; + int planeIndices; + int partitionIndex; - BitArrayStream colorEndpointStream = new BitArrayStream(new BitArray(16 * 8)); + Span colorEndpointMode = stackalloc uint[4]; + + BitStream128 colorEndpointStream = new BitStream128(); // Read extra config data... uint baseColorEndpointMode = 0; @@ -182,11 +352,11 @@ namespace Ryujinx.Graphics.Texture.Astc if (numberPartitions == 1) { colorEndpointMode[0] = (uint)bitStream.ReadBits(4); - partitionIndex = 0; + partitionIndex = 0; } else { - partitionIndex = bitStream.ReadBits(10); + partitionIndex = bitStream.ReadBits(10); baseColorEndpointMode = (uint)bitStream.ReadBits(6); } @@ -194,7 +364,7 @@ namespace Ryujinx.Graphics.Texture.Astc // Remaining bits are color endpoint data... int numberWeightBits = texelParams.GetPackedBitSize(); - int remainingBits = 128 - numberWeightBits - bitStream.Position; + int remainingBits = bitStream.BitsLeft - numberWeightBits; // Consider extra bits prior to texel data... uint extraColorEndpointModeBits = 0; @@ -203,9 +373,9 @@ namespace Ryujinx.Graphics.Texture.Astc { switch (numberPartitions) { - case 2: extraColorEndpointModeBits += 2; break; - case 3: extraColorEndpointModeBits += 5; break; - case 4: extraColorEndpointModeBits += 8; break; + case 2: extraColorEndpointModeBits += 2; break; + case 3: extraColorEndpointModeBits += 5; break; + case 4: extraColorEndpointModeBits += 8; break; default: Debug.Assert(false); break; } } @@ -240,10 +410,10 @@ namespace Ryujinx.Graphics.Texture.Astc if (baseMode != 0) { uint extraColorEndpointMode = (uint)bitStream.ReadBits((int)extraColorEndpointModeBits); - uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode; - tempColorEndpointMode >>= 2; + uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode; + tempColorEndpointMode >>= 2; - bool[] c = new bool[4]; + Span c = stackalloc bool[4]; for (int i = 0; i < numberPartitions; i++) { @@ -251,7 +421,7 @@ namespace Ryujinx.Graphics.Texture.Astc tempColorEndpointMode >>= 1; } - byte[] m = new byte[4]; + Span m = stackalloc byte[4]; for (int i = 0; i < numberPartitions; i++) { @@ -272,7 +442,7 @@ namespace Ryujinx.Graphics.Texture.Astc { uint tempColorEndpointMode = baseColorEndpointMode >> 2; - for (uint i = 0; i < numberPartitions; i++) + for (int i = 0; i < numberPartitions; i++) { colorEndpointMode[i] = tempColorEndpointMode; } @@ -283,27 +453,24 @@ namespace Ryujinx.Graphics.Texture.Astc { Debug.Assert(colorEndpointMode[i] < 16); } - Debug.Assert(bitStream.Position + texelParams.GetPackedBitSize() == 128); + Debug.Assert(bitStream.BitsLeft == texelParams.GetPackedBitSize()); // Decode both color data and texel weight data - int[] colorValues = new int[32]; // Four values * two endpoints * four maximum partitions - DecodeColorValues(colorValues, colorEndpointStream.ToByteArray(), colorEndpointMode, numberPartitions, colorDataBits); + Span colorValues = stackalloc int[32]; // Four values * two endpoints * four maximum partitions + DecodeColorValues(colorValues, ref colorEndpointStream, colorEndpointMode, numberPartitions, colorDataBits); - AstcPixel[][] endPoints = new AstcPixel[4][]; - endPoints[0] = new AstcPixel[2]; - endPoints[1] = new AstcPixel[2]; - endPoints[2] = new AstcPixel[2]; - endPoints[3] = new AstcPixel[2]; + EndPointSet endPoints; + unsafe { _ = &endPoints; } // Skip struct initialization int colorValuesPosition = 0; for (int i = 0; i < numberPartitions; i++) { - ComputeEndpoints(endPoints[i], colorValues, colorEndpointMode[i], ref colorValuesPosition); + ComputeEndpoints(endPoints.Get(i), colorValues, colorEndpointMode[i], ref colorValuesPosition); } // Read the texel weight data. - byte[] texelWeightData = (byte[])inputBuffer.Clone(); + Buffer16 texelWeightData = inputBlock; // Reverse everything for (int i = 0; i < 8; i++) @@ -311,28 +478,32 @@ namespace Ryujinx.Graphics.Texture.Astc byte a = ReverseByte(texelWeightData[i]); byte b = ReverseByte(texelWeightData[15 - i]); - texelWeightData[i] = b; + texelWeightData[i] = b; texelWeightData[15 - i] = a; } // Make sure that higher non-texel bits are set to zero - int clearByteStart = (texelParams.GetPackedBitSize() >> 3) + 1; + int clearByteStart = (texelParams.GetPackedBitSize() >> 3) + 1; texelWeightData[clearByteStart - 1] &= (byte)((1 << (texelParams.GetPackedBitSize() % 8)) - 1); int cLen = 16 - clearByteStart; for (int i = clearByteStart; i < clearByteStart + cLen; i++) texelWeightData[i] = 0; - List texelWeightValues = new List(); - BitArrayStream weightBitStream = new BitArrayStream(new BitArray(texelWeightData)); + IntegerSequence texelWeightValues; + unsafe { _ = &texelWeightValues; } // Skip struct initialization + texelWeightValues.Reset(); - IntegerEncoded.DecodeIntegerSequence(texelWeightValues, weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); + BitStream128 weightBitStream = new BitStream128(texelWeightData); + + IntegerEncoded.DecodeIntegerSequence(ref texelWeightValues, ref weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); // Blocks can be at most 12x12, so we can have as many as 144 weights - int[][] weights = new int[2][]; - weights[0] = new int[144]; - weights[1] = new int[144]; + Weights weights; + unsafe { _ = &weights; } // Skip struct initialization - UnquantizeTexelWeights(weights, texelWeightValues, texelParams, blockWidth, blockHeight); + UnquantizeTexelWeights(ref weights, ref texelWeightValues, ref texelParams, blockWidth, blockHeight); + + ushort[] table = Bits.Replicate8_16Table; // Now that we have endpoints and weights, we can interpolate and generate // the proper decoding... @@ -343,13 +514,13 @@ namespace Ryujinx.Graphics.Texture.Astc int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32)); Debug.Assert(partition < numberPartitions); - AstcPixel pixel = new AstcPixel(0, 0, 0, 0); + AstcPixel pixel = new AstcPixel(); for (int component = 0; component < 4; component++) { - int component0 = endPoints[partition][0].GetComponent(component); - component0 = BitArrayStream.Replicate(component0, 8, 16); - int component1 = endPoints[partition][1].GetComponent(component); - component1 = BitArrayStream.Replicate(component1, 8, 16); + int component0 = endPoints.Get(partition)[0].GetComponent(component); + component0 = table[component0]; + int component1 = endPoints.Get(partition)[1].GetComponent(component); + component1 = table[component1]; int plane = 0; @@ -358,7 +529,7 @@ namespace Ryujinx.Graphics.Texture.Astc plane = 1; } - int weight = weights[plane][j * blockWidth + i]; + int weight = weights.Get(plane)[j * blockWidth + i]; int finalComponent = (component0 * (64 - weight) + component1 * weight + 32) / 64; if (finalComponent == 65535) @@ -379,6 +550,38 @@ namespace Ryujinx.Graphics.Texture.Astc return true; } + // Blocks can be at most 12x12, so we can have as many as 144 weights + [StructLayout(LayoutKind.Sequential, Size = 144 * sizeof(int) * Count)] + private struct Weights + { + private int _start; + + public const int Count = 2; + + public Span this[int index] + { + get + { + if ((uint)index >= Count) + { + throw new ArgumentOutOfRangeException(); + } + + ref int start = ref Unsafe.Add(ref _start, index * 144); + + return MemoryMarshal.CreateSpan(ref start, 144); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Get(int index) + { + ref int start = ref Unsafe.Add(ref _start, index * 144); + + return MemoryMarshal.CreateSpan(ref start, 144); + } + } + private static int Select2dPartition(int seed, int x, int y, int partitionCount, bool isSmallBlock) { return SelectPartition(seed, x, y, 0, partitionCount, isSmallBlock); @@ -400,19 +603,19 @@ namespace Ryujinx.Graphics.Texture.Astc seed += (partitionCount - 1) * 1024; - int rightNum = Hash52((uint)seed); - byte seed01 = (byte)(rightNum & 0xF); - byte seed02 = (byte)((rightNum >> 4) & 0xF); - byte seed03 = (byte)((rightNum >> 8) & 0xF); - byte seed04 = (byte)((rightNum >> 12) & 0xF); - byte seed05 = (byte)((rightNum >> 16) & 0xF); - byte seed06 = (byte)((rightNum >> 20) & 0xF); - byte seed07 = (byte)((rightNum >> 24) & 0xF); - byte seed08 = (byte)((rightNum >> 28) & 0xF); - byte seed09 = (byte)((rightNum >> 18) & 0xF); - byte seed10 = (byte)((rightNum >> 22) & 0xF); - byte seed11 = (byte)((rightNum >> 26) & 0xF); - byte seed12 = (byte)(((rightNum >> 30) | (rightNum << 2)) & 0xF); + int rightNum = Hash52((uint)seed); + byte seed01 = (byte)(rightNum & 0xF); + byte seed02 = (byte)((rightNum >> 4) & 0xF); + byte seed03 = (byte)((rightNum >> 8) & 0xF); + byte seed04 = (byte)((rightNum >> 12) & 0xF); + byte seed05 = (byte)((rightNum >> 16) & 0xF); + byte seed06 = (byte)((rightNum >> 20) & 0xF); + byte seed07 = (byte)((rightNum >> 24) & 0xF); + byte seed08 = (byte)((rightNum >> 28) & 0xF); + byte seed09 = (byte)((rightNum >> 18) & 0xF); + byte seed10 = (byte)((rightNum >> 22) & 0xF); + byte seed11 = (byte)((rightNum >> 26) & 0xF); + byte seed12 = (byte)(((rightNum >> 30) | (rightNum << 2)) & 0xF); seed01 *= seed01; seed02 *= seed02; seed03 *= seed03; seed04 *= seed04; @@ -459,50 +662,56 @@ namespace Ryujinx.Graphics.Texture.Astc static int Hash52(uint val) { val ^= val >> 15; val -= val << 17; val += val << 7; val += val << 4; - val ^= val >> 5; val += val << 16; val ^= val >> 7; val ^= val >> 3; - val ^= val << 6; val ^= val >> 17; + val ^= val >> 5; val += val << 16; val ^= val >> 7; val ^= val >> 3; + val ^= val << 6; val ^= val >> 17; return (int)val; } static void UnquantizeTexelWeights( - int[][] outputBuffer, - List weights, - TexelWeightParams texelParams, - int blockWidth, - int blockHeight) + ref Weights outputBuffer, + ref IntegerSequence weights, + ref TexelWeightParams texelParams, + int blockWidth, + int blockHeight) { - int weightIndices = 0; - int[][] unquantized = new int[2][]; - unquantized[0] = new int[144]; - unquantized[1] = new int[144]; + int weightIndices = 0; + Weights unquantized; + unsafe { _ = &unquantized; } // Skip struct initialization - for (int i = 0; i < weights.Count; i++) + Span weightsList = weights.List; + Span unquantized0 = unquantized[0]; + Span unquantized1 = unquantized[1]; + + for (int i = 0; i < weightsList.Length; i++) { - unquantized[0][weightIndices] = UnquantizeTexelWeight(weights[i]); + unquantized0[weightIndices] = UnquantizeTexelWeight(weightsList[i]); if (texelParams.DualPlane) { i++; - unquantized[1][weightIndices] = UnquantizeTexelWeight(weights[i]); + unquantized1[weightIndices] = UnquantizeTexelWeight(weightsList[i]); - if (i == weights.Count) + if (i == weightsList.Length) { break; } } - if (++weightIndices >= (texelParams.Width * texelParams.Height)) break; + if (++weightIndices >= texelParams.Width * texelParams.Height) break; } // Do infill if necessary (Section C.2.18) ... - int ds = (1024 + (blockWidth / 2)) / (blockWidth - 1); - int dt = (1024 + (blockHeight / 2)) / (blockHeight - 1); + int ds = (1024 + blockWidth / 2) / (blockWidth - 1); + int dt = (1024 + blockHeight / 2) / (blockHeight - 1); int planeScale = texelParams.DualPlane ? 2 : 1; for (int plane = 0; plane < planeScale; plane++) { + Span unquantizedSpan = unquantized.Get(plane); + Span outputSpan = outputBuffer.Get(plane); + for (int t = 0; t < blockHeight; t++) { for (int s = 0; s < blockWidth; s++) @@ -520,38 +729,34 @@ namespace Ryujinx.Graphics.Texture.Astc int ft = gt & 0x0F; int w11 = (fs * ft + 8) >> 4; - int w10 = ft - w11; - int w01 = fs - w11; - int w00 = 16 - fs - ft + w11; int v0 = js + jt * texelParams.Width; - int p00 = 0; - int p01 = 0; - int p10 = 0; - int p11 = 0; + int weight = 8; - if (v0 < (texelParams.Width * texelParams.Height)) + int wxh = texelParams.Width * texelParams.Height; + + if (v0 < wxh) { - p00 = unquantized[plane][v0]; + weight += unquantizedSpan[v0] * (16 - fs - ft + w11); + + if (v0 + 1 < wxh) + { + weight += unquantizedSpan[v0 + 1] * (fs - w11); + } } - if (v0 + 1 < (texelParams.Width * texelParams.Height)) + if (v0 + texelParams.Width < wxh) { - p01 = unquantized[plane][v0 + 1]; + weight += unquantizedSpan[v0 + texelParams.Width] * (ft - w11); + + if (v0 + texelParams.Width + 1 < wxh) + { + weight += unquantizedSpan[v0 + texelParams.Width + 1] * w11; + } } - if (v0 + texelParams.Width < (texelParams.Width * texelParams.Height)) - { - p10 = unquantized[plane][v0 + texelParams.Width]; - } - - if (v0 + texelParams.Width + 1 < (texelParams.Width * texelParams.Height)) - { - p11 = unquantized[plane][v0 + texelParams.Width + 1]; - } - - outputBuffer[plane][t * blockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4; + outputSpan[t * blockWidth + s] = weight >> 4; } } } @@ -559,10 +764,10 @@ namespace Ryujinx.Graphics.Texture.Astc static int UnquantizeTexelWeight(IntegerEncoded intEncoded) { - int bitValue = intEncoded.BitValue; + int bitValue = intEncoded.BitValue; int bitLength = intEncoded.NumberBits; - int a = BitArrayStream.Replicate(bitValue & 1, 1, 7); + int a = Bits.Replicate1_7(bitValue & 1); int b = 0, c = 0, d = 0; int result = 0; @@ -570,7 +775,7 @@ namespace Ryujinx.Graphics.Texture.Astc switch (intEncoded.GetEncoding()) { case IntegerEncoded.EIntegerEncoding.JustBits: - result = BitArrayStream.Replicate(bitValue, bitLength, 6); + result = Bits.Replicate(bitValue, bitLength, 6); break; case IntegerEncoded.EIntegerEncoding.Trit: @@ -582,8 +787,13 @@ namespace Ryujinx.Graphics.Texture.Astc { case 0: { - int[] results = { 0, 32, 63 }; - result = results[d]; + result = d switch + { + 0 => 0, + 1 => 32, + 2 => 63, + _ => 0 + }; break; } @@ -628,8 +838,15 @@ namespace Ryujinx.Graphics.Texture.Astc { case 0: { - int[] results = { 0, 16, 32, 47, 63 }; - result = results[d]; + result = d switch + { + 0 => 0, + 1 => 16, + 2 => 32, + 3 => 47, + 4 => 63, + _ => 0 + }; break; } @@ -661,9 +878,9 @@ namespace Ryujinx.Graphics.Texture.Astc if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && bitLength > 0) { // Decode the value... - result = d * c + b; + result = d * c + b; result ^= a; - result = (a & 0x20) | (result >> 2); + result = (a & 0x20) | (result >> 2); } Debug.Assert(result < 64); @@ -683,41 +900,35 @@ namespace Ryujinx.Graphics.Texture.Astc return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32); } - static uint[] ReadUintColorValues(int number, int[] colorValues, ref int colorValuesPosition) + static Span ReadUintColorValues(int number, Span colorValues, ref int colorValuesPosition) { - uint[] ret = new uint[number]; + Span ret = colorValues.Slice(colorValuesPosition, number); - for (int i = 0; i < number; i++) - { - ret[i] = (uint)colorValues[colorValuesPosition++]; - } + colorValuesPosition += number; - return ret; + return MemoryMarshal.Cast(ret); } - static int[] ReadIntColorValues(int number, int[] colorValues, ref int colorValuesPosition) + static Span ReadIntColorValues(int number, Span colorValues, ref int colorValuesPosition) { - int[] ret = new int[number]; + Span ret = colorValues.Slice(colorValuesPosition, number); - for (int i = 0; i < number; i++) - { - ret[i] = colorValues[colorValuesPosition++]; - } + colorValuesPosition += number; return ret; } static void ComputeEndpoints( - AstcPixel[] endPoints, - int[] colorValues, - uint colorEndpointMode, - ref int colorValuesPosition) + Span endPoints, + Span colorValues, + uint colorEndpointMode, + ref int colorValuesPosition) { switch (colorEndpointMode) { case 0: { - uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[0], (short)val[0]); endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]); @@ -728,9 +939,9 @@ namespace Ryujinx.Graphics.Texture.Astc case 1: { - uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); - int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); - int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); + Span val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); + int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); @@ -740,7 +951,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 4: { - uint[] val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); endPoints[1] = new AstcPixel((short)val[3], (short)val[1], (short)val[1], (short)val[1]); @@ -750,10 +961,10 @@ namespace Ryujinx.Graphics.Texture.Astc case 5: { - int[] val = ReadIntColorValues(4, colorValues, ref colorValuesPosition); + Span val = ReadIntColorValues(4, colorValues, ref colorValuesPosition); - BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); - BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); endPoints[1] = new AstcPixel((short)(val[2] + val[3]), (short)(val[0] + val[1]), (short)(val[0] + val[1]), (short)(val[0] + val[1])); @@ -766,7 +977,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 6: { - uint[] val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); endPoints[0] = new AstcPixel(0xFF, (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); endPoints[1] = new AstcPixel(0xFF, (short)val[0], (short)val[1], (short)val[2]); @@ -776,7 +987,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 8: { - uint[] val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) { @@ -794,11 +1005,11 @@ namespace Ryujinx.Graphics.Texture.Astc case 9: { - int[] val = ReadIntColorValues(6, colorValues, ref colorValuesPosition); + Span val = ReadIntColorValues(6, colorValues, ref colorValuesPosition); - BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); - BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); - BitArrayStream.BitTransferSigned(ref val[5], ref val[4]); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[5], ref val[4]); if (val[1] + val[3] + val[5] >= 0) { @@ -819,7 +1030,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 10: { - uint[] val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); endPoints[0] = new AstcPixel((short)val[4], (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); endPoints[1] = new AstcPixel((short)val[5], (short)val[0], (short)val[1], (short)val[2]); @@ -829,7 +1040,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 12: { - uint[] val = ReadUintColorValues(8, colorValues, ref colorValuesPosition); + Span val = ReadUintColorValues(8, colorValues, ref colorValuesPosition); if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) { @@ -847,12 +1058,12 @@ namespace Ryujinx.Graphics.Texture.Astc case 13: { - int[] val = ReadIntColorValues(8, colorValues, ref colorValuesPosition); + Span val = ReadIntColorValues(8, colorValues, ref colorValuesPosition); - BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); - BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); - BitArrayStream.BitTransferSigned(ref val[5], ref val[4]); - BitArrayStream.BitTransferSigned(ref val[7], ref val[6]); + Bits.BitTransferSigned(ref val[1], ref val[0]); + Bits.BitTransferSigned(ref val[3], ref val[2]); + Bits.BitTransferSigned(ref val[5], ref val[4]); + Bits.BitTransferSigned(ref val[7], ref val[6]); if (val[1] + val[3] + val[5] >= 0) { @@ -877,11 +1088,11 @@ namespace Ryujinx.Graphics.Texture.Astc } static void DecodeColorValues( - int[] outputValues, - byte[] inputData, - uint[] modes, - int numberPartitions, - int numberBitsForColorData) + Span outputValues, + ref BitStream128 colorBitStream, + Span modes, + int numberPartitions, + int numberBitsForColorData) { // First figure out how many color values we have int numberValues = 0; @@ -898,7 +1109,7 @@ namespace Ryujinx.Graphics.Texture.Astc while (--range > 0) { IntegerEncoded intEncoded = IntegerEncoded.CreateEncoding(range); - int bitLength = intEncoded.GetBitLength(numberValues); + int bitLength = intEncoded.GetBitLength(numberValues); if (bitLength <= numberBitsForColorData) { @@ -919,31 +1130,32 @@ namespace Ryujinx.Graphics.Texture.Astc } // We now have enough to decode our integer sequence. - List integerEncodedSequence = new List(); - BitArrayStream colorBitStream = new BitArrayStream(new BitArray(inputData)); + IntegerSequence integerEncodedSequence; + unsafe { _ = &integerEncodedSequence; } // Skip struct initialization + integerEncodedSequence.Reset(); - IntegerEncoded.DecodeIntegerSequence(integerEncodedSequence, colorBitStream, range, numberValues); + IntegerEncoded.DecodeIntegerSequence(ref integerEncodedSequence, ref colorBitStream, range, numberValues); // Once we have the decoded values, we need to dequantize them to the 0-255 range // This procedure is outlined in ASTC spec C.2.13 int outputIndices = 0; - foreach (IntegerEncoded intEncoded in integerEncodedSequence) + foreach (ref IntegerEncoded intEncoded in integerEncodedSequence.List) { int bitLength = intEncoded.NumberBits; - int bitValue = intEncoded.BitValue; + int bitValue = intEncoded.BitValue; Debug.Assert(bitLength >= 1); int a = 0, b = 0, c = 0, d = 0; // A is just the lsb replicated 9 times. - a = BitArrayStream.Replicate(bitValue & 1, 1, 9); + a = Bits.Replicate(bitValue & 1, 1, 9); switch (intEncoded.GetEncoding()) { case IntegerEncoded.EIntegerEncoding.JustBits: { - outputValues[outputIndices++] = BitArrayStream.Replicate(bitValue, bitLength, 8); + outputValues[outputIndices++] = Bits.Replicate(bitValue, bitLength, 8); break; } @@ -1082,8 +1294,8 @@ namespace Ryujinx.Graphics.Texture.Astc if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits) { int T = d * c + b; - T ^= a; - T = (a & 0x80) | (T >> 2); + T ^= a; + T = (a & 0x80) | (T >> 2); outputValues[outputIndices++] = T; } @@ -1096,7 +1308,7 @@ namespace Ryujinx.Graphics.Texture.Astc } } - static void FillVoidExtentLdr(BitArrayStream bitStream, int[] outputBuffer, int blockWidth, int blockHeight) + static void FillVoidExtentLdr(ref BitStream128 bitStream, Span outputBuffer, int blockWidth, int blockHeight) { // Don't actually care about the void extent, just read the bits... for (int i = 0; i < 4; ++i) @@ -1121,9 +1333,9 @@ namespace Ryujinx.Graphics.Texture.Astc } } - static TexelWeightParams DecodeBlockInfo(BitArrayStream bitStream) + static void DecodeBlockInfo(ref BitStream128 bitStream, out TexelWeightParams texelParams) { - TexelWeightParams texelParams = new TexelWeightParams(); + texelParams = new TexelWeightParams(); // Read the entire block mode all at once ushort modeBits = (ushort)bitStream.ReadBits(11); @@ -1146,14 +1358,15 @@ namespace Ryujinx.Graphics.Texture.Astc texelParams.Error = true; } - return texelParams; + return; } // First check if the last four bits are zero if ((modeBits & 0xF) == 0) { texelParams.Error = true; - return texelParams; + + return; } // If the last two bits are zero, then if bits @@ -1162,14 +1375,14 @@ namespace Ryujinx.Graphics.Texture.Astc { texelParams.Error = true; - return texelParams; + return; } // Otherwise, there is no error... Figure out the layout // of the block mode. Layout is determined by a number // between 0 and 9 corresponding to table C.2.8 of the // ASTC spec. - int layout = 0; + int layout; if ((modeBits & 0x1) != 0 || (modeBits & 0x2) != 0) { @@ -1269,7 +1482,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 7) & 0x3; - texelParams.Width = b + 4; + texelParams.Width = b + 4; texelParams.Height = a + 2; break; @@ -1280,7 +1493,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 7) & 0x3; - texelParams.Width = b + 8; + texelParams.Width = b + 8; texelParams.Height = a + 2; break; @@ -1291,7 +1504,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 7) & 0x3; - texelParams.Width = a + 2; + texelParams.Width = a + 2; texelParams.Height = b + 8; break; @@ -1302,7 +1515,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 7) & 0x1; - texelParams.Width = a + 2; + texelParams.Width = a + 2; texelParams.Height = b + 6; break; @@ -1313,7 +1526,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 7) & 0x1; - texelParams.Width = b + 2; + texelParams.Width = b + 2; texelParams.Height = a + 2; break; @@ -1323,7 +1536,7 @@ namespace Ryujinx.Graphics.Texture.Astc { int a = (modeBits >> 5) & 0x3; - texelParams.Width = 12; + texelParams.Width = 12; texelParams.Height = a + 2; break; @@ -1333,7 +1546,7 @@ namespace Ryujinx.Graphics.Texture.Astc { int a = (modeBits >> 5) & 0x3; - texelParams.Width = a + 2; + texelParams.Width = a + 2; texelParams.Height = 12; break; @@ -1341,7 +1554,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 7: { - texelParams.Width = 6; + texelParams.Width = 6; texelParams.Height = 10; break; @@ -1349,7 +1562,7 @@ namespace Ryujinx.Graphics.Texture.Astc case 8: { - texelParams.Width = 10; + texelParams.Width = 10; texelParams.Height = 6; break; } @@ -1359,7 +1572,7 @@ namespace Ryujinx.Graphics.Texture.Astc int a = (modeBits >> 5) & 0x3; int b = (modeBits >> 9) & 0x3; - texelParams.Width = a + 6; + texelParams.Width = a + 6; texelParams.Height = b + 6; break; @@ -1378,18 +1591,16 @@ namespace Ryujinx.Graphics.Texture.Astc if (h) { - int[] maxWeights = { 9, 11, 15, 19, 23, 31 }; + ReadOnlySpan maxWeights = new byte[] { 9, 11, 15, 19, 23, 31 }; texelParams.MaxWeight = maxWeights[r - 2]; } else { - int[] maxWeights = { 1, 2, 3, 4, 5, 7 }; + ReadOnlySpan maxWeights = new byte[] { 1, 2, 3, 4, 5, 7 }; texelParams.MaxWeight = maxWeights[r - 2]; } texelParams.DualPlane = d; - - return texelParams; } } } diff --git a/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs b/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs index 7d127878..13197714 100644 --- a/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs +++ b/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs @@ -1,16 +1,23 @@ using System; -using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Texture.Astc { - class AstcPixel + [StructLayout(LayoutKind.Sequential)] + struct AstcPixel { - public short R { get; set; } - public short G { get; set; } - public short B { get; set; } - public short A { get; set; } + internal const int StructSize = 12; - byte[] _bitDepth = new byte[4]; + public short A; + public short R; + public short G; + public short B; + + private uint _bitDepthInt; + + private Span BitDepth => MemoryMarshal.CreateSpan(ref Unsafe.As(ref _bitDepthInt), 4); + private Span Components => MemoryMarshal.CreateSpan(ref A, 4); public AstcPixel(short a, short r, short g, short b) { @@ -19,8 +26,7 @@ namespace Ryujinx.Graphics.Texture.Astc G = g; B = b; - for (int i = 0; i < 4; i++) - _bitDepth[i] = 8; + _bitDepthInt = 0x08080808; } public void ClampByte() @@ -33,96 +39,20 @@ namespace Ryujinx.Graphics.Texture.Astc public short GetComponent(int index) { - switch(index) - { - case 0: return A; - case 1: return R; - case 2: return G; - case 3: return B; - } - - return 0; + return Components[index]; } public void SetComponent(int index, int value) { - switch (index) - { - case 0: - A = (short)value; - break; - case 1: - R = (short)value; - break; - case 2: - G = (short)value; - break; - case 3: - B = (short)value; - break; - } - } - - public void ChangeBitDepth(byte[] depth) - { - for (int i = 0; i< 4; i++) - { - int value = ChangeBitDepth(GetComponent(i), _bitDepth[i], depth[i]); - - SetComponent(i, value); - _bitDepth[i] = depth[i]; - } - } - - short ChangeBitDepth(short value, byte oldDepth, byte newDepth) - { - Debug.Assert(newDepth <= 8); - Debug.Assert(oldDepth <= 8); - - if (oldDepth == newDepth) - { - // Do nothing - return value; - } - else if (oldDepth == 0 && newDepth != 0) - { - return (short)((1 << newDepth) - 1); - } - else if (newDepth > oldDepth) - { - return (short)BitArrayStream.Replicate(value, oldDepth, newDepth); - } - else - { - // oldDepth > newDepth - if (newDepth == 0) - { - return 0xFF; - } - else - { - byte bitsWasted = (byte)(oldDepth - newDepth); - short tempValue = value; - - tempValue = (short)((tempValue + (1 << (bitsWasted - 1))) >> bitsWasted); - tempValue = Math.Min(Math.Max((short)0, tempValue), (short)((1 << newDepth) - 1)); - - return (byte)(tempValue); - } - } + Components[index] = (short)value; } public int Pack() { - AstcPixel newPixel = new AstcPixel(A, R, G, B); - byte[] eightBitDepth = { 8, 8, 8, 8 }; - - newPixel.ChangeBitDepth(eightBitDepth); - - return (byte)newPixel.A << 24 | - (byte)newPixel.B << 16 | - (byte)newPixel.G << 8 | - (byte)newPixel.R << 0; + return A << 24 | + B << 16 | + G << 8 | + R << 0; } // Adds more precision to the blue channel as described diff --git a/Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs b/Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs deleted file mode 100644 index 3c472a3c..00000000 --- a/Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections; - -namespace Ryujinx.Graphics.Texture.Astc -{ - public class BitArrayStream - { - public BitArray BitsArray; - - public int Position { get; private set; } - - public BitArrayStream(BitArray bitArray) - { - BitsArray = bitArray; - Position = 0; - } - - public short ReadBits(int length) - { - int retValue = 0; - for (int i = Position; i < Position + length; i++) - { - if (BitsArray[i]) - { - retValue |= 1 << (i - Position); - } - } - - Position += length; - return (short)retValue; - } - - public int ReadBits(int start, int end) - { - int retValue = 0; - for (int i = start; i <= end; i++) - { - if (BitsArray[i]) - { - retValue |= 1 << (i - start); - } - } - - return retValue; - } - - public int ReadBit(int index) - { - return Convert.ToInt32(BitsArray[index]); - } - - public void WriteBits(int value, int length) - { - for (int i = Position; i < Position + length; i++) - { - BitsArray[i] = ((value >> (i - Position)) & 1) != 0; - } - - Position += length; - } - - public byte[] ToByteArray() - { - byte[] retArray = new byte[(BitsArray.Length + 7) / 8]; - BitsArray.CopyTo(retArray, 0); - return retArray; - } - - public static int Replicate(int value, int numberBits, int toBit) - { - if (numberBits == 0) return 0; - if (toBit == 0) return 0; - - int tempValue = value & ((1 << numberBits) - 1); - int retValue = tempValue; - int resLength = numberBits; - - while (resLength < toBit) - { - int comp = 0; - if (numberBits > toBit - resLength) - { - int newShift = toBit - resLength; - comp = numberBits - newShift; - numberBits = newShift; - } - retValue <<= numberBits; - retValue |= tempValue >> comp; - resLength += numberBits; - } - return retValue; - } - - public static int PopCnt(int number) - { - int counter; - for (counter = 0; number != 0; counter++) - { - number &= number - 1; - } - return counter; - } - - public static void Swap(ref T lhs, ref T rhs) - { - T temp = lhs; - lhs = rhs; - rhs = temp; - } - - // Transfers a bit as described in C.2.14 - public static void BitTransferSigned(ref int a, ref int b) - { - b >>= 1; - b |= a & 0x80; - a >>= 1; - a &= 0x3F; - if ((a & 0x20) != 0) a -= 0x40; - } - } -} diff --git a/Ryujinx.Graphics.Texture/Astc/BitStream128.cs b/Ryujinx.Graphics.Texture/Astc/BitStream128.cs new file mode 100644 index 00000000..3bf9769f --- /dev/null +++ b/Ryujinx.Graphics.Texture/Astc/BitStream128.cs @@ -0,0 +1,72 @@ +using Ryujinx.Common.Utilities; +using System; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Texture.Astc +{ + public struct BitStream128 + { + private Buffer16 _data; + public int BitsLeft { get; set; } + + public BitStream128(Buffer16 data) + { + _data = data; + BitsLeft = 128; + } + + public int ReadBits(int bitCount) + { + Debug.Assert(bitCount < 32); + + if (bitCount == 0) + { + return 0; + } + + int mask = (1 << bitCount) - 1; + int value = _data.As() & mask; + + Span span = _data.AsSpan(); + + ulong carry = span[1] << (64 - bitCount); + span[0] = (span[0] >> bitCount) | carry; + span[1] >>= bitCount; + + BitsLeft -= bitCount; + + return value; + } + + public void WriteBits(int value, int bitCount) + { + Debug.Assert(bitCount < 32); + + if (bitCount == 0) return; + + ulong maskedValue = (uint)(value & ((1 << bitCount) - 1)); + + Span span = _data.AsSpan(); + + if (BitsLeft < 64) + { + ulong lowMask = maskedValue << BitsLeft; + span[0] |= lowMask; + } + + if (BitsLeft + bitCount > 64) + { + if (BitsLeft > 64) + { + span[1] |= maskedValue << (BitsLeft - 64); + } + else + { + span[1] |= maskedValue >> (64 - BitsLeft); + } + } + + BitsLeft += bitCount; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/Astc/Bits.cs b/Ryujinx.Graphics.Texture/Astc/Bits.cs new file mode 100644 index 00000000..b140a20a --- /dev/null +++ b/Ryujinx.Graphics.Texture/Astc/Bits.cs @@ -0,0 +1,66 @@ +namespace Ryujinx.Graphics.Texture.Astc +{ + internal static class Bits + { + public static readonly ushort[] Replicate8_16Table; + public static readonly byte[] Replicate1_7Table; + + static Bits() + { + Replicate8_16Table = new ushort[0x200]; + Replicate1_7Table = new byte[0x200]; + + for (int i = 0; i < 0x200; i++) + { + Replicate8_16Table[i] = (ushort)Replicate(i, 8, 16); + Replicate1_7Table[i] = (byte)Replicate(i, 1, 7); + } + } + + public static int Replicate8_16(int value) + { + return Replicate8_16Table[value]; + } + + public static int Replicate1_7(int value) + { + return Replicate1_7Table[value]; + } + + public static int Replicate(int value, int numberBits, int toBit) + { + if (numberBits == 0) return 0; + if (toBit == 0) return 0; + + int tempValue = value & ((1 << numberBits) - 1); + int retValue = tempValue; + int resLength = numberBits; + + while (resLength < toBit) + { + int comp = 0; + if (numberBits > toBit - resLength) + { + int newShift = toBit - resLength; + comp = numberBits - newShift; + numberBits = newShift; + } + retValue <<= numberBits; + retValue |= tempValue >> comp; + resLength += numberBits; + } + + return retValue; + } + + // Transfers a bit as described in C.2.14 + public static void BitTransferSigned(ref int a, ref int b) + { + b >>= 1; + b |= a & 0x80; + a >>= 1; + a &= 0x3F; + if ((a & 0x20) != 0) a -= 0x40; + } + } +} diff --git a/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs b/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs new file mode 100644 index 00000000..45e61ca2 --- /dev/null +++ b/Ryujinx.Graphics.Texture/Astc/EndPointSet.cs @@ -0,0 +1,23 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Texture.Astc +{ + [StructLayout(LayoutKind.Sequential, Size = AstcPixel.StructSize * 8)] + internal struct EndPointSet + { + private AstcPixel _start; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Get(int index) + { + Debug.Assert(index < 4); + + ref AstcPixel start = ref Unsafe.Add(ref _start, index * 2); + + return MemoryMarshal.CreateSpan(ref start, 2); + } + } +} diff --git a/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index 5aa610e7..065de46b 100644 --- a/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs +++ b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -1,11 +1,14 @@ -using System.Collections; -using System.Collections.Generic; +using System; +using System.Numerics; namespace Ryujinx.Graphics.Texture.Astc { - public struct IntegerEncoded + internal struct IntegerEncoded { - public enum EIntegerEncoding + internal const int StructSize = 8; + private static readonly IntegerEncoded[] Encodings; + + public enum EIntegerEncoding : byte { JustBits, Quint, @@ -13,17 +16,27 @@ namespace Ryujinx.Graphics.Texture.Astc } EIntegerEncoding _encoding; - public int NumberBits { get; private set; } - public int BitValue { get; private set; } - public int TritValue { get; private set; } - public int QuintValue { get; private set; } + public byte NumberBits { get; private set; } + public byte TritValue { get; private set; } + public byte QuintValue { get; private set; } + public int BitValue { get; private set; } + + static IntegerEncoded() + { + Encodings = new IntegerEncoded[0x100]; + + for (int i = 0; i < Encodings.Length; i++) + { + Encodings[i] = CreateEncodingCalc(i); + } + } public IntegerEncoded(EIntegerEncoding encoding, int numBits) { - _encoding = encoding; - NumberBits = numBits; - BitValue = 0; - TritValue = 0; + _encoding = encoding; + NumberBits = (byte)numBits; + BitValue = 0; + TritValue = 0; QuintValue = 0; } @@ -52,6 +65,11 @@ namespace Ryujinx.Graphics.Texture.Astc } public static IntegerEncoded CreateEncoding(int maxVal) + { + return Encodings[maxVal]; + } + + private static IntegerEncoded CreateEncodingCalc(int maxVal) { while (maxVal > 0) { @@ -60,19 +78,19 @@ namespace Ryujinx.Graphics.Texture.Astc // Is maxVal a power of two? if ((check & (check - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(maxVal)); + return new IntegerEncoded(EIntegerEncoding.JustBits, BitOperations.PopCount((uint)maxVal)); } // Is maxVal of the type 3*2^n - 1? if ((check % 3 == 0) && ((check / 3) & ((check / 3) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(check / 3 - 1)); + return new IntegerEncoded(EIntegerEncoding.Trit, BitOperations.PopCount((uint)(check / 3 - 1))); } // Is maxVal of the type 5*2^n - 1? if ((check % 5 == 0) && ((check / 5) & ((check / 5) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(check / 5 - 1)); + return new IntegerEncoded(EIntegerEncoding.Quint, BitOperations.PopCount((uint)(check / 5 - 1))); } // Apparently it can't be represented with a bounded integer sequence... @@ -84,150 +102,78 @@ namespace Ryujinx.Graphics.Texture.Astc } public static void DecodeTritBlock( - BitArrayStream bitStream, - List listIntegerEncoded, - int numberBitsPerValue) + ref BitStream128 bitStream, + ref IntegerSequence listIntegerEncoded, + int numberBitsPerValue) { // Implement the algorithm in section C.2.12 - int[] m = new int[5]; - int[] t = new int[5]; - int T; + Span m = stackalloc int[5]; - // Read the trit encoded block according to - // table C.2.14 m[0] = bitStream.ReadBits(numberBitsPerValue); - T = bitStream.ReadBits(2); + int encoded = bitStream.ReadBits(2); m[1] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(2) << 2; + encoded |= bitStream.ReadBits(2) << 2; m[2] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(1) << 4; + encoded |= bitStream.ReadBits(1) << 4; m[3] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(2) << 5; + encoded |= bitStream.ReadBits(2) << 5; m[4] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(1) << 7; + encoded |= bitStream.ReadBits(1) << 7; - int c = 0; + ReadOnlySpan encodings = GetTritEncoding(encoded); - BitArrayStream tb = new BitArrayStream(new BitArray(new int[] { T })); - if (tb.ReadBits(2, 4) == 7) - { - c = (tb.ReadBits(5, 7) << 2) | tb.ReadBits(0, 1); - t[4] = t[3] = 2; - } - else - { - c = tb.ReadBits(0, 4); - if (tb.ReadBits(5, 6) == 3) - { - t[4] = 2; - t[3] = tb.ReadBit(7); - } - else - { - t[4] = tb.ReadBit(7); - t[3] = tb.ReadBits(5, 6); - } - } - - BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); - if (cb.ReadBits(0, 1) == 3) - { - t[2] = 2; - t[1] = cb.ReadBit(4); - t[0] = (cb.ReadBit(3) << 1) | (cb.ReadBit(2) & ~cb.ReadBit(3)); - } - else if (cb.ReadBits(2, 3) == 3) - { - t[2] = 2; - t[1] = 2; - t[0] = cb.ReadBits(0, 1); - } - else - { - t[2] = cb.ReadBit(4); - t[1] = cb.ReadBits(2, 3); - t[0] = (cb.ReadBit(1) << 1) | (cb.ReadBit(0) & ~cb.ReadBit(1)); - } + IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue); for (int i = 0; i < 5; i++) { - IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue) - { - BitValue = m[i], - TritValue = t[i] - }; - listIntegerEncoded.Add(intEncoded); + intEncoded.BitValue = m[i]; + intEncoded.TritValue = encodings[i]; + + listIntegerEncoded.Add(ref intEncoded); } } public static void DecodeQuintBlock( - BitArrayStream bitStream, - List listIntegerEncoded, - int numberBitsPerValue) + ref BitStream128 bitStream, + ref IntegerSequence listIntegerEncoded, + int numberBitsPerValue) { + ReadOnlySpan interleavedBits = new byte[] { 3, 2, 2 }; + // Implement the algorithm in section C.2.12 - int[] m = new int[3]; - int[] qa = new int[3]; - int q; + Span m = stackalloc int[3]; + ulong encoded = 0; + int encodedBitsRead = 0; - // Read the trit encoded block according to - // table C.2.15 - m[0] = bitStream.ReadBits(numberBitsPerValue); - q = bitStream.ReadBits(3); - m[1] = bitStream.ReadBits(numberBitsPerValue); - q |= bitStream.ReadBits(2) << 3; - m[2] = bitStream.ReadBits(numberBitsPerValue); - q |= bitStream.ReadBits(2) << 5; - - BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q })); - if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0) + for (int i = 0; i < m.Length; i++) { - qa[0] = qa[1] = 4; - qa[2] = (qb.ReadBit(0) << 2) | ((qb.ReadBit(4) & ~qb.ReadBit(0)) << 1) | (qb.ReadBit(3) & ~qb.ReadBit(0)); - } - else - { - int c = 0; - if (qb.ReadBits(1, 2) == 3) - { - qa[2] = 4; - c = (qb.ReadBits(3, 4) << 3) | ((~qb.ReadBits(5, 6) & 3) << 1) | qb.ReadBit(0); - } - else - { - qa[2] = qb.ReadBits(5, 6); - c = qb.ReadBits(0, 4); - } + m[i] = bitStream.ReadBits(numberBitsPerValue); - BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); - if (cb.ReadBits(0, 2) == 5) - { - qa[1] = 4; - qa[0] = cb.ReadBits(3, 4); - } - else - { - qa[1] = cb.ReadBits(3, 4); - qa[0] = cb.ReadBits(0, 2); - } + uint encodedBits = (uint)bitStream.ReadBits(interleavedBits[i]); + + encoded |= encodedBits << encodedBitsRead; + encodedBitsRead += interleavedBits[i]; } + ReadOnlySpan encodings = GetQuintEncoding((int)encoded); + for (int i = 0; i < 3; i++) { IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue) { - BitValue = m[i], - QuintValue = qa[i] + BitValue = m[i], + QuintValue = encodings[i] }; - listIntegerEncoded.Add(intEncoded); + + listIntegerEncoded.Add(ref intEncoded); } } public static void DecodeIntegerSequence( - List decodeIntegerSequence, - BitArrayStream bitStream, - int maxRange, - int numberValues) + ref IntegerSequence decodeIntegerSequence, + ref BitStream128 bitStream, + int maxRange, + int numberValues) { // Determine encoding parameters IntegerEncoded intEncoded = CreateEncoding(maxRange); @@ -240,7 +186,7 @@ namespace Ryujinx.Graphics.Texture.Astc { case EIntegerEncoding.Quint: { - DecodeQuintBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + DecodeQuintBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); numberValuesDecoded += 3; break; @@ -248,7 +194,7 @@ namespace Ryujinx.Graphics.Texture.Astc case EIntegerEncoding.Trit: { - DecodeTritBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + DecodeTritBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); numberValuesDecoded += 5; break; @@ -257,7 +203,7 @@ namespace Ryujinx.Graphics.Texture.Astc case EIntegerEncoding.JustBits: { intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); - decodeIntegerSequence.Add(intEncoded); + decodeIntegerSequence.Add(ref intEncoded); numberValuesDecoded++; break; @@ -265,5 +211,135 @@ namespace Ryujinx.Graphics.Texture.Astc } } } + + private static ReadOnlySpan GetTritEncoding(int index) + { + return TritEncodings.Slice(index * 5, 5); + } + + private static ReadOnlySpan GetQuintEncoding(int index) + { + return QuintEncodings.Slice(index * 3, 3); + } + + private static ReadOnlySpan TritEncodings => new byte[] + { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, + 2, 1, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2, 0, 0, 0, + 1, 2, 0, 0, 0, 2, 2, 0, 0, 0, 2, 0, 2, 0, 0, + 0, 2, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, + 2, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 2, 1, 1, 0, 0, 1, 1, 2, 0, 0, + 0, 2, 1, 0, 0, 1, 2, 1, 0, 0, 2, 2, 1, 0, 0, + 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 1, 0, 0, 2, 2, + 2, 0, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0, 2, 1, 0, + 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 2, 1, 0, 1, 0, + 1, 0, 2, 1, 0, 0, 2, 0, 1, 0, 1, 2, 0, 1, 0, + 2, 2, 0, 1, 0, 2, 0, 2, 1, 0, 0, 2, 2, 1, 0, + 1, 2, 2, 1, 0, 2, 2, 2, 1, 0, 2, 0, 2, 1, 0, + 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 2, 0, 1, 1, 0, + 0, 1, 2, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, + 2, 1, 1, 1, 0, 1, 1, 2, 1, 0, 0, 2, 1, 1, 0, + 1, 2, 1, 1, 0, 2, 2, 1, 1, 0, 2, 1, 2, 1, 0, + 0, 1, 0, 2, 2, 1, 1, 0, 2, 2, 2, 1, 0, 2, 2, + 1, 0, 2, 2, 2, 0, 0, 0, 2, 0, 1, 0, 0, 2, 0, + 2, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 1, 0, 2, 0, + 1, 1, 0, 2, 0, 2, 1, 0, 2, 0, 1, 0, 2, 2, 0, + 0, 2, 0, 2, 0, 1, 2, 0, 2, 0, 2, 2, 0, 2, 0, + 2, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 2, 2, 0, + 2, 2, 2, 2, 0, 2, 0, 2, 2, 0, 0, 0, 1, 2, 0, + 1, 0, 1, 2, 0, 2, 0, 1, 2, 0, 0, 1, 2, 2, 0, + 0, 1, 1, 2, 0, 1, 1, 1, 2, 0, 2, 1, 1, 2, 0, + 1, 1, 2, 2, 0, 0, 2, 1, 2, 0, 1, 2, 1, 2, 0, + 2, 2, 1, 2, 0, 2, 1, 2, 2, 0, 0, 2, 0, 2, 2, + 1, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 2, 0, 0, 0, 2, + 0, 0, 2, 0, 2, 0, 1, 0, 0, 2, 1, 1, 0, 0, 2, + 2, 1, 0, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 0, 2, + 1, 2, 0, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 0, 2, + 0, 2, 2, 0, 2, 1, 2, 2, 0, 2, 2, 2, 2, 0, 2, + 2, 0, 2, 0, 2, 0, 0, 1, 0, 2, 1, 0, 1, 0, 2, + 2, 0, 1, 0, 2, 0, 1, 2, 0, 2, 0, 1, 1, 0, 2, + 1, 1, 1, 0, 2, 2, 1, 1, 0, 2, 1, 1, 2, 0, 2, + 0, 2, 1, 0, 2, 1, 2, 1, 0, 2, 2, 2, 1, 0, 2, + 2, 1, 2, 0, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0, 0, 2, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, + 1, 0, 2, 0, 1, 0, 2, 0, 0, 1, 1, 2, 0, 0, 1, + 2, 2, 0, 0, 1, 2, 0, 2, 0, 1, 0, 2, 2, 0, 1, + 1, 2, 2, 0, 1, 2, 2, 2, 0, 1, 2, 0, 2, 0, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 2, 0, 1, 0, 1, + 0, 1, 2, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 2, 1, 1, 0, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, + 1, 2, 1, 0, 1, 2, 2, 1, 0, 1, 2, 1, 2, 0, 1, + 0, 0, 1, 2, 2, 1, 0, 1, 2, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, + 2, 0, 0, 1, 1, 0, 0, 2, 1, 1, 0, 1, 0, 1, 1, + 1, 1, 0, 1, 1, 2, 1, 0, 1, 1, 1, 0, 2, 1, 1, + 0, 2, 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 1, + 2, 0, 2, 1, 1, 0, 2, 2, 1, 1, 1, 2, 2, 1, 1, + 2, 2, 2, 1, 1, 2, 0, 2, 1, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 1, + 2, 2, 1, 1, 1, 2, 1, 2, 1, 1, 0, 1, 1, 2, 2, + 1, 1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, + 0, 0, 0, 2, 1, 1, 0, 0, 2, 1, 2, 0, 0, 2, 1, + 0, 0, 2, 2, 1, 0, 1, 0, 2, 1, 1, 1, 0, 2, 1, + 2, 1, 0, 2, 1, 1, 0, 2, 2, 1, 0, 2, 0, 2, 1, + 1, 2, 0, 2, 1, 2, 2, 0, 2, 1, 2, 0, 2, 2, 1, + 0, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2, 2, 1, + 2, 0, 2, 2, 1, 0, 0, 1, 2, 1, 1, 0, 1, 2, 1, + 2, 0, 1, 2, 1, 0, 1, 2, 2, 1, 0, 1, 1, 2, 1, + 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, + 0, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, + 2, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1, 2, 1, 2, 2, + 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 1, 2, + 1, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 0, 2, 1, 2, + 0, 1, 0, 1, 2, 1, 1, 0, 1, 2, 2, 1, 0, 1, 2, + 1, 0, 2, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 2, + 2, 2, 0, 1, 2, 2, 0, 2, 1, 2, 0, 2, 2, 1, 2, + 1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 0, 2, 1, 2, + 0, 0, 1, 1, 2, 1, 0, 1, 1, 2, 2, 0, 1, 1, 2, + 0, 1, 2, 1, 2, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, + 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 0, 2, 1, 1, 2, + 1, 2, 1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, + 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 2, 2, 2 + }; + + private static ReadOnlySpan QuintEncodings => new byte[] + { + 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, + 0, 4, 0, 4, 4, 0, 4, 4, 4, 0, 1, 0, 1, 1, 0, + 2, 1, 0, 3, 1, 0, 4, 1, 0, 1, 4, 0, 4, 4, 1, + 4, 4, 4, 0, 2, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, + 4, 2, 0, 2, 4, 0, 4, 4, 2, 4, 4, 4, 0, 3, 0, + 1, 3, 0, 2, 3, 0, 3, 3, 0, 4, 3, 0, 3, 4, 0, + 4, 4, 3, 4, 4, 4, 0, 0, 1, 1, 0, 1, 2, 0, 1, + 3, 0, 1, 4, 0, 1, 0, 4, 1, 4, 0, 4, 0, 4, 4, + 0, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 4, 1, 1, + 1, 4, 1, 4, 1, 4, 1, 4, 4, 0, 2, 1, 1, 2, 1, + 2, 2, 1, 3, 2, 1, 4, 2, 1, 2, 4, 1, 4, 2, 4, + 2, 4, 4, 0, 3, 1, 1, 3, 1, 2, 3, 1, 3, 3, 1, + 4, 3, 1, 3, 4, 1, 4, 3, 4, 3, 4, 4, 0, 0, 2, + 1, 0, 2, 2, 0, 2, 3, 0, 2, 4, 0, 2, 0, 4, 2, + 2, 0, 4, 3, 0, 4, 0, 1, 2, 1, 1, 2, 2, 1, 2, + 3, 1, 2, 4, 1, 2, 1, 4, 2, 2, 1, 4, 3, 1, 4, + 0, 2, 2, 1, 2, 2, 2, 2, 2, 3, 2, 2, 4, 2, 2, + 2, 4, 2, 2, 2, 4, 3, 2, 4, 0, 3, 2, 1, 3, 2, + 2, 3, 2, 3, 3, 2, 4, 3, 2, 3, 4, 2, 2, 3, 4, + 3, 3, 4, 0, 0, 3, 1, 0, 3, 2, 0, 3, 3, 0, 3, + 4, 0, 3, 0, 4, 3, 0, 0, 4, 1, 0, 4, 0, 1, 3, + 1, 1, 3, 2, 1, 3, 3, 1, 3, 4, 1, 3, 1, 4, 3, + 0, 1, 4, 1, 1, 4, 0, 2, 3, 1, 2, 3, 2, 2, 3, + 3, 2, 3, 4, 2, 3, 2, 4, 3, 0, 2, 4, 1, 2, 4, + 0, 3, 3, 1, 3, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, + 3, 4, 3, 0, 3, 4, 1, 3, 4 + }; } } diff --git a/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs b/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs new file mode 100644 index 00000000..367b6809 --- /dev/null +++ b/Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Texture.Astc +{ + [StructLayout(LayoutKind.Sequential, Size = IntegerEncoded.StructSize * Capacity + sizeof(int))] + internal struct IntegerSequence + { + private const int Capacity = 100; + + private int _length; + private IntegerEncoded _start; + + public Span List => MemoryMarshal.CreateSpan(ref _start, _length); + + public void Reset() => _length = 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(ref IntegerEncoded item) + { + Debug.Assert(_length < Capacity); + + int oldLength = _length; + _length++; + + List[oldLength] = item; + } + } +} diff --git a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj index f446f16c..6ec98a8d 100644 --- a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj +++ b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj @@ -6,6 +6,7 @@ netcoreapp3.0 + true