aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Common/Utilities/Buffers.cs59
-rw-r--r--Ryujinx.Common/Utilities/SpanHelpers.cs61
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs3
-rw-r--r--Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs699
-rw-r--r--Ryujinx.Graphics.Texture/Astc/AstcPixel.cs112
-rw-r--r--Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs121
-rw-r--r--Ryujinx.Graphics.Texture/Astc/BitStream128.cs72
-rw-r--r--Ryujinx.Graphics.Texture/Astc/Bits.cs66
-rw-r--r--Ryujinx.Graphics.Texture/Astc/EndPointSet.cs23
-rw-r--r--Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs334
-rw-r--r--Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs31
-rw-r--r--Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj1
12 files changed, 996 insertions, 586 deletions
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<byte> 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<byte>(in Buffer16 value)
+ {
+ return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator ReadOnlySpan<byte>(in Buffer16 value)
+ {
+ return SpanHelpers.AsReadOnlyByteSpan(ref Unsafe.AsRef(in value));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T As<T>() where T : unmanaged
+ {
+ if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>())
+ {
+ throw new ArgumentException();
+ }
+
+ return ref MemoryMarshal.GetReference(AsSpan<T>());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> AsSpan<T>() where T : unmanaged
+ {
+ return SpanHelpers.AsSpan<Buffer16, T>(ref this);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
+ {
+ return SpanHelpers.AsReadOnlySpan<Buffer16, T>(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<T> CreateSpan<T>(ref T reference, int length)
+ {
+ return MemoryMarshal.CreateSpan(ref reference, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
+ {
+ return CreateSpan(ref reference, 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
+ where TStruct : unmanaged where TSpan : unmanaged
+ {
+ return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
+ Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
+ {
+ return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
+ {
+ return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged
+ {
+ return CreateReadOnlySpan(ref reference, 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(ref TStruct reference)
+ where TStruct : unmanaged where TSpan : unmanaged
+ {
+ return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
+ Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(ref T reference) where T : unmanaged
+ {
+ return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
+ }
+ }
+}
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<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;
}
}
}
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<byte> BitDepth => MemoryMarshal.CreateSpan(ref Unsafe.As<uint, byte>(ref _bitDepthInt), 4);
+ private Span<short> 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<T>(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<int>() & mask;
+
+ Span<ulong> span = _data.AsSpan<ulong>();
+
+ 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<ulong> span = _data.AsSpan<ulong>();
+
+ 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<AstcPixel> 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;
}
@@ -53,6 +66,11 @@ namespace Ryujinx.Graphics.Texture.Astc
public static IntegerEncoded CreateEncoding(int maxVal)
{
+ return Encodings[maxVal];
+ }
+
+ private static IntegerEncoded CreateEncodingCalc(int maxVal)
+ {
while (maxVal > 0)
{
int check = maxVal + 1;
@@ -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<IntegerEncoded> 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<int> 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;
-
- int c = 0;
+ encoded |= bitStream.ReadBits(1) << 7;
- 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);
- }
- }
+ ReadOnlySpan<byte> encodings = GetTritEncoding(encoded);
- 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<IntegerEncoded> listIntegerEncoded,
- int numberBitsPerValue)
+ ref BitStream128 bitStream,
+ ref IntegerSequence listIntegerEncoded,
+ int numberBitsPerValue)
{
- // Implement the algorithm in section C.2.12
- int[] m = new int[3];
- int[] qa = new int[3];
- int q;
+ ReadOnlySpan<byte> interleavedBits = new byte[] { 3, 2, 2 };
- // 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;
+ // Implement the algorithm in section C.2.12
+ Span<int> m = stackalloc int[3];
+ ulong encoded = 0;
+ int encodedBitsRead = 0;
- BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q }));
- if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0)
- {
- 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
+ for (int i = 0; i < m.Length; i++)
{
- 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<byte> 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<IntegerEncoded> 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<byte> GetTritEncoding(int index)
+ {
+ return TritEncodings.Slice(index * 5, 5);
+ }
+
+ private static ReadOnlySpan<byte> GetQuintEncoding(int index)
+ {
+ return QuintEncodings.Slice(index * 3, 3);
+ }
+
+ private static ReadOnlySpan<byte> 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<byte> 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<IntegerEncoded> 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 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>