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
This commit is contained in:
Alex Barney 2019-12-26 23:09:49 -07:00 committed by Thog
parent 947e14d3be
commit d1ab9fb42c
12 changed files with 1009 additions and 599 deletions

View File

@ -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));
}
}
}

View File

@ -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>());
}
}
}

View File

@ -7,6 +7,7 @@ using Ryujinx.Graphics.Texture.Astc;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using Ryujinx.Common.Logging;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
@ -246,7 +247,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc()) if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc())
{ {
if (!AstcDecoder.TryDecodeToRgba8( if (!AstcDecoder.TryDecodeToRgba8(
data, data.ToArray(),
_info.FormatInfo.BlockWidth, _info.FormatInfo.BlockWidth,
_info.FormatInfo.BlockHeight, _info.FormatInfo.BlockHeight,
_info.Width, _info.Width,

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,23 @@
using System; using System;
using System.Diagnostics; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Texture.Astc namespace Ryujinx.Graphics.Texture.Astc
{ {
class AstcPixel [StructLayout(LayoutKind.Sequential)]
struct AstcPixel
{ {
public short R { get; set; } internal const int StructSize = 12;
public short G { get; set; }
public short B { get; set; }
public short A { get; set; }
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) public AstcPixel(short a, short r, short g, short b)
{ {
@ -19,8 +26,7 @@ namespace Ryujinx.Graphics.Texture.Astc
G = g; G = g;
B = b; B = b;
for (int i = 0; i < 4; i++) _bitDepthInt = 0x08080808;
_bitDepth[i] = 8;
} }
public void ClampByte() public void ClampByte()
@ -33,96 +39,20 @@ namespace Ryujinx.Graphics.Texture.Astc
public short GetComponent(int index) public short GetComponent(int index)
{ {
switch(index) return Components[index];
{
case 0: return A;
case 1: return R;
case 2: return G;
case 3: return B;
}
return 0;
} }
public void SetComponent(int index, int value) public void SetComponent(int index, int value)
{ {
switch (index) Components[index] = (short)value;
{
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);
}
}
} }
public int Pack() public int Pack()
{ {
AstcPixel newPixel = new AstcPixel(A, R, G, B); return A << 24 |
byte[] eightBitDepth = { 8, 8, 8, 8 }; B << 16 |
G << 8 |
newPixel.ChangeBitDepth(eightBitDepth); R << 0;
return (byte)newPixel.A << 24 |
(byte)newPixel.B << 16 |
(byte)newPixel.G << 8 |
(byte)newPixel.R << 0;
} }
// Adds more precision to the blue channel as described // Adds more precision to the blue channel as described

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -1,11 +1,14 @@
using System.Collections; using System;
using System.Collections.Generic; using System.Numerics;
namespace Ryujinx.Graphics.Texture.Astc 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, JustBits,
Quint, Quint,
@ -13,17 +16,27 @@ namespace Ryujinx.Graphics.Texture.Astc
} }
EIntegerEncoding _encoding; EIntegerEncoding _encoding;
public int NumberBits { get; private set; } public byte NumberBits { get; private set; }
public int BitValue { get; private set; } public byte TritValue { get; private set; }
public int TritValue { get; private set; } public byte QuintValue { get; private set; }
public int 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) public IntegerEncoded(EIntegerEncoding encoding, int numBits)
{ {
_encoding = encoding; _encoding = encoding;
NumberBits = numBits; NumberBits = (byte)numBits;
BitValue = 0; BitValue = 0;
TritValue = 0; TritValue = 0;
QuintValue = 0; QuintValue = 0;
} }
@ -52,6 +65,11 @@ namespace Ryujinx.Graphics.Texture.Astc
} }
public static IntegerEncoded CreateEncoding(int maxVal) public static IntegerEncoded CreateEncoding(int maxVal)
{
return Encodings[maxVal];
}
private static IntegerEncoded CreateEncodingCalc(int maxVal)
{ {
while (maxVal > 0) while (maxVal > 0)
{ {
@ -60,19 +78,19 @@ namespace Ryujinx.Graphics.Texture.Astc
// Is maxVal a power of two? // Is maxVal a power of two?
if ((check & (check - 1)) == 0) 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? // Is maxVal of the type 3*2^n - 1?
if ((check % 3 == 0) && ((check / 3) & ((check / 3) - 1)) == 0) 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? // Is maxVal of the type 5*2^n - 1?
if ((check % 5 == 0) && ((check / 5) & ((check / 5) - 1)) == 0) 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... // Apparently it can't be represented with a bounded integer sequence...
@ -84,150 +102,78 @@ namespace Ryujinx.Graphics.Texture.Astc
} }
public static void DecodeTritBlock( public static void DecodeTritBlock(
BitArrayStream bitStream, ref BitStream128 bitStream,
List<IntegerEncoded> listIntegerEncoded, ref IntegerSequence listIntegerEncoded,
int numberBitsPerValue) int numberBitsPerValue)
{ {
// Implement the algorithm in section C.2.12 // Implement the algorithm in section C.2.12
int[] m = new int[5]; Span<int> m = stackalloc int[5];
int[] t = new int[5];
int T;
// Read the trit encoded block according to
// table C.2.14
m[0] = bitStream.ReadBits(numberBitsPerValue); m[0] = bitStream.ReadBits(numberBitsPerValue);
T = bitStream.ReadBits(2); int encoded = bitStream.ReadBits(2);
m[1] = bitStream.ReadBits(numberBitsPerValue); m[1] = bitStream.ReadBits(numberBitsPerValue);
T |= bitStream.ReadBits(2) << 2; encoded |= bitStream.ReadBits(2) << 2;
m[2] = bitStream.ReadBits(numberBitsPerValue); m[2] = bitStream.ReadBits(numberBitsPerValue);
T |= bitStream.ReadBits(1) << 4; encoded |= bitStream.ReadBits(1) << 4;
m[3] = bitStream.ReadBits(numberBitsPerValue); m[3] = bitStream.ReadBits(numberBitsPerValue);
T |= bitStream.ReadBits(2) << 5; encoded |= bitStream.ReadBits(2) << 5;
m[4] = bitStream.ReadBits(numberBitsPerValue); m[4] = bitStream.ReadBits(numberBitsPerValue);
T |= bitStream.ReadBits(1) << 7; encoded |= bitStream.ReadBits(1) << 7;
int c = 0; ReadOnlySpan<byte> encodings = GetTritEncoding(encoded);
BitArrayStream tb = new BitArrayStream(new BitArray(new int[] { T })); IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue);
if (tb.ReadBits(2, 4) == 7)
{
c = (tb.ReadBits(5, 7) << 2) | tb.ReadBits(0, 1);
t[4] = t[3] = 2;
}
else
{
c = tb.ReadBits(0, 4);
if (tb.ReadBits(5, 6) == 3)
{
t[4] = 2;
t[3] = tb.ReadBit(7);
}
else
{
t[4] = tb.ReadBit(7);
t[3] = tb.ReadBits(5, 6);
}
}
BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c }));
if (cb.ReadBits(0, 1) == 3)
{
t[2] = 2;
t[1] = cb.ReadBit(4);
t[0] = (cb.ReadBit(3) << 1) | (cb.ReadBit(2) & ~cb.ReadBit(3));
}
else if (cb.ReadBits(2, 3) == 3)
{
t[2] = 2;
t[1] = 2;
t[0] = cb.ReadBits(0, 1);
}
else
{
t[2] = cb.ReadBit(4);
t[1] = cb.ReadBits(2, 3);
t[0] = (cb.ReadBit(1) << 1) | (cb.ReadBit(0) & ~cb.ReadBit(1));
}
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue) intEncoded.BitValue = m[i];
{ intEncoded.TritValue = encodings[i];
BitValue = m[i],
TritValue = t[i] listIntegerEncoded.Add(ref intEncoded);
};
listIntegerEncoded.Add(intEncoded);
} }
} }
public static void DecodeQuintBlock( public static void DecodeQuintBlock(
BitArrayStream bitStream, ref BitStream128 bitStream,
List<IntegerEncoded> listIntegerEncoded, ref IntegerSequence listIntegerEncoded,
int numberBitsPerValue) int numberBitsPerValue)
{ {
ReadOnlySpan<byte> interleavedBits = new byte[] { 3, 2, 2 };
// Implement the algorithm in section C.2.12 // Implement the algorithm in section C.2.12
int[] m = new int[3]; Span<int> m = stackalloc int[3];
int[] qa = new int[3]; ulong encoded = 0;
int q; int encodedBitsRead = 0;
// Read the trit encoded block according to for (int i = 0; i < m.Length; i++)
// table C.2.15
m[0] = bitStream.ReadBits(numberBitsPerValue);
q = bitStream.ReadBits(3);
m[1] = bitStream.ReadBits(numberBitsPerValue);
q |= bitStream.ReadBits(2) << 3;
m[2] = bitStream.ReadBits(numberBitsPerValue);
q |= bitStream.ReadBits(2) << 5;
BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q }));
if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0)
{ {
qa[0] = qa[1] = 4; m[i] = bitStream.ReadBits(numberBitsPerValue);
qa[2] = (qb.ReadBit(0) << 2) | ((qb.ReadBit(4) & ~qb.ReadBit(0)) << 1) | (qb.ReadBit(3) & ~qb.ReadBit(0));
}
else
{
int c = 0;
if (qb.ReadBits(1, 2) == 3)
{
qa[2] = 4;
c = (qb.ReadBits(3, 4) << 3) | ((~qb.ReadBits(5, 6) & 3) << 1) | qb.ReadBit(0);
}
else
{
qa[2] = qb.ReadBits(5, 6);
c = qb.ReadBits(0, 4);
}
BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); uint encodedBits = (uint)bitStream.ReadBits(interleavedBits[i]);
if (cb.ReadBits(0, 2) == 5)
{ encoded |= encodedBits << encodedBitsRead;
qa[1] = 4; encodedBitsRead += interleavedBits[i];
qa[0] = cb.ReadBits(3, 4);
}
else
{
qa[1] = cb.ReadBits(3, 4);
qa[0] = cb.ReadBits(0, 2);
}
} }
ReadOnlySpan<byte> encodings = GetQuintEncoding((int)encoded);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue) IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue)
{ {
BitValue = m[i], BitValue = m[i],
QuintValue = qa[i] QuintValue = encodings[i]
}; };
listIntegerEncoded.Add(intEncoded);
listIntegerEncoded.Add(ref intEncoded);
} }
} }
public static void DecodeIntegerSequence( public static void DecodeIntegerSequence(
List<IntegerEncoded> decodeIntegerSequence, ref IntegerSequence decodeIntegerSequence,
BitArrayStream bitStream, ref BitStream128 bitStream,
int maxRange, int maxRange,
int numberValues) int numberValues)
{ {
// Determine encoding parameters // Determine encoding parameters
IntegerEncoded intEncoded = CreateEncoding(maxRange); IntegerEncoded intEncoded = CreateEncoding(maxRange);
@ -240,7 +186,7 @@ namespace Ryujinx.Graphics.Texture.Astc
{ {
case EIntegerEncoding.Quint: case EIntegerEncoding.Quint:
{ {
DecodeQuintBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); DecodeQuintBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits);
numberValuesDecoded += 3; numberValuesDecoded += 3;
break; break;
@ -248,7 +194,7 @@ namespace Ryujinx.Graphics.Texture.Astc
case EIntegerEncoding.Trit: case EIntegerEncoding.Trit:
{ {
DecodeTritBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); DecodeTritBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits);
numberValuesDecoded += 5; numberValuesDecoded += 5;
break; break;
@ -257,7 +203,7 @@ namespace Ryujinx.Graphics.Texture.Astc
case EIntegerEncoding.JustBits: case EIntegerEncoding.JustBits:
{ {
intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits);
decodeIntegerSequence.Add(intEncoded); decodeIntegerSequence.Add(ref intEncoded);
numberValuesDecoded++; numberValuesDecoded++;
break; 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
};
} }
} }

View File

@ -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;
}
}
}

View File

@ -6,6 +6,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
</Project> </Project>