diff options
| author | Alex Barney <thealexbarney@gmail.com> | 2019-12-26 23:09:49 -0700 |
|---|---|---|
| committer | Thog <thog@protonmail.com> | 2020-01-09 02:13:00 +0100 |
| commit | d1ab9fb42c2fd9f018d4410ca619cd66294eafc9 (patch) | |
| tree | dcac71560b921f29d73ee6e21f235528184d9f84 /Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs | |
| parent | 947e14d3be0f7c5e4b6f77df204ec675b8e9e719 (diff) | |
ASTC optimizations (#845)
* ASTC optimizations
* Move code to Ryujinx.Common
* Support 3D textures
* Address feedback
* Remove ASTC logging
* Use stackalloc instead of a Buffer20 struct
* Code style and cleanup
* Respond to feedback
* Rearrange public/private property ordering
Diffstat (limited to 'Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs')
| -rw-r--r-- | Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs | 699 |
1 files changed, 455 insertions, 244 deletions
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<byte> InputBuffer { get; } + private Memory<byte> 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<byte> inputBuffer, + Memory<byte> 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<byte, Buffer16>(InputBuffer.Span)[index]; + + Span<int> decompressedData = stackalloc int[144]; + + try + { + DecompressBlock(inputBlock, decompressedData, BlockSizeX, BlockSizeY); + } + catch (Exception) + { + Success = false; + } + + Span<byte> decompressedBytes = MemoryMarshal.Cast<int, byte>(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<byte> block, Span<byte> 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<byte> blockRow = block.Slice(inputOffset, outputPixelsX * 4); + Span<byte> 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<byte> data, - int blockWidth, - int blockHeight, - int width, - int height, - int depth, - int levels, + ReadOnlyMemory<byte> data, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels, out Span<byte> decoded) { - bool success = true; + byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels)]; - using (MemoryStream inputStream = new MemoryStream(data.ToArray())) - { - BinaryReader binReader = new BinaryReader(inputStream); + AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels); - 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 i = 0; i < decoder.TotalBlockCount; i++) + { + decoder.ProcessBlock(i); + } - 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]; + decoded = output; - try - { - DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockWidth, blockHeight); - } - catch (Exception) - { - success = false; - } + return decoder.Success; + } - int decompressedWidth = Math.Min(blockWidth, width - i); - int decompressedHeight = Math.Min(blockHeight, height - j); + public static bool TryDecodeToRgba8( + ReadOnlyMemory<byte> data, + Memory<byte> 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); - int baseOffset = mipOffset + k * sliceSize + (j * width + i) * 4; + for (int i = 0; i < decoder.TotalBlockCount; i++) + { + decoder.ProcessBlock(i); + } - for (int jj = 0; jj < decompressedHeight; jj++) - { - outputStream.Seek(baseOffset + jj * width * 4, SeekOrigin.Begin); + return decoder.Success; + } - byte[] outputBuffer = new byte[decompressedData.Length * sizeof(int)]; + public static bool TryDecodeToRgba8P( + ReadOnlyMemory<byte> data, + Memory<byte> 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); - Buffer.BlockCopy(decompressedData, 0, outputBuffer, 0, outputBuffer.Length); + // Lazy parallelism + Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); - outputStream.Write(outputBuffer, jj * blockWidth * 4, decompressedWidth * 4); - } + return decoder.Success; + } - blockIndex++; - } + public static bool TryDecodeToRgba8P( + ReadOnlyMemory<byte> data, + int blockWidth, + int blockHeight, + int width, + int height, + int depth, + int levels, + out Span<byte> decoded) + { + byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels)]; - mipOffset += sliceSize * depth; + AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels); - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - depth = Math.Max(1, depth >> 1); - } + Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x)); - decoded = outputStream.ToArray(); - } - } + decoded = output; - return success; + return decoder.Success; } public static bool DecompressBlock( - byte[] inputBuffer, - int[] outputBuffer, - int blockWidth, - int blockHeight) + Buffer16 inputBlock, + Span<int> 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; + + Span<uint> colorEndpointMode = stackalloc uint[4]; - BitArrayStream colorEndpointStream = new BitArrayStream(new BitArray(16 * 8)); + 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<bool> 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<byte> 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<int> 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<IntegerEncoded> texelWeightValues = new List<IntegerEncoded>(); - BitArrayStream weightBitStream = new BitArrayStream(new BitArray(texelWeightData)); + IntegerSequence texelWeightValues; + unsafe { _ = &texelWeightValues; } // Skip struct initialization + texelWeightValues.Reset(); + + BitStream128 weightBitStream = new BitStream128(texelWeightData); - IntegerEncoded.DecodeIntegerSequence(texelWeightValues, weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); + 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<int> 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<int> 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<IntegerEncoded> 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 + + Span<IntegerEncoded> weightsList = weights.List; + Span<int> unquantized0 = unquantized[0]; + Span<int> unquantized1 = unquantized[1]; - for (int i = 0; i < weights.Count; i++) + 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<int> unquantizedSpan = unquantized.Get(plane); + Span<int> 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)) - { - p00 = unquantized[plane][v0]; - } + int wxh = texelParams.Width * texelParams.Height; - if (v0 + 1 < (texelParams.Width * texelParams.Height)) + if (v0 < wxh) { - p01 = unquantized[plane][v0 + 1]; - } + weight += unquantizedSpan[v0] * (16 - fs - ft + w11); - if (v0 + texelParams.Width < (texelParams.Width * texelParams.Height)) - { - p10 = unquantized[plane][v0 + texelParams.Width]; + if (v0 + 1 < wxh) + { + weight += unquantizedSpan[v0 + 1] * (fs - w11); + } } - if (v0 + texelParams.Width + 1 < (texelParams.Width * texelParams.Height)) + if (v0 + texelParams.Width < wxh) { - p11 = unquantized[plane][v0 + texelParams.Width + 1]; + weight += unquantizedSpan[v0 + texelParams.Width] * (ft - w11); + + if (v0 + texelParams.Width + 1 < wxh) + { + weight += unquantizedSpan[v0 + texelParams.Width + 1] * w11; + } } - 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<uint> ReadUintColorValues(int number, Span<int> colorValues, ref int colorValuesPosition) { - uint[] ret = new uint[number]; + Span<int> ret = colorValues.Slice(colorValuesPosition, number); - for (int i = 0; i < number; i++) - { - ret[i] = (uint)colorValues[colorValuesPosition++]; - } + colorValuesPosition += number; - return ret; + return MemoryMarshal.Cast<int, uint>(ret); } - static int[] ReadIntColorValues(int number, int[] colorValues, ref int colorValuesPosition) + static Span<int> ReadIntColorValues(int number, Span<int> colorValues, ref int colorValuesPosition) { - int[] ret = new int[number]; + Span<int> 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<AstcPixel> endPoints, + Span<int> colorValues, + uint colorEndpointMode, + ref int colorValuesPosition) { switch (colorEndpointMode) { case 0: { - uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + Span<uint> 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<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); 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<uint> 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<int> 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<uint> 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<uint> 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<int> 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<uint> 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<uint> 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<int> 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<int> outputValues, + ref BitStream128 colorBitStream, + Span<uint> 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<IntegerEncoded> integerEncodedSequence = new List<IntegerEncoded>(); - 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<int> 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<byte> maxWeights = new byte[] { 9, 11, 15, 19, 23, 31 }; texelParams.MaxWeight = maxWeights[r - 2]; } else { - int[] maxWeights = { 1, 2, 3, 4, 5, 7 }; + ReadOnlySpan<byte> maxWeights = new byte[] { 1, 2, 3, 4, 5, 7 }; texelParams.MaxWeight = maxWeights[r - 2]; } texelParams.DualPlane = d; - - return texelParams; } } } |
