aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs
diff options
context:
space:
mode:
authorAlex Barney <thealexbarney@gmail.com>2019-12-26 23:09:49 -0700
committerThog <thog@protonmail.com>2020-01-09 02:13:00 +0100
commitd1ab9fb42c2fd9f018d4410ca619cd66294eafc9 (patch)
treedcac71560b921f29d73ee6e21f235528184d9f84 /Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs
parent947e14d3be0f7c5e4b6f77df204ec675b8e9e719 (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.cs699
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;
}
}
}