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:
parent
947e14d3be
commit
d1ab9fb42c
12 changed files with 1009 additions and 599 deletions
59
Ryujinx.Common/Utilities/Buffers.cs
Normal file
59
Ryujinx.Common/Utilities/Buffers.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
Ryujinx.Common/Utilities/SpanHelpers.cs
Normal file
61
Ryujinx.Common/Utilities/SpanHelpers.cs
Normal 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>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -1,20 +1,179 @@
|
||||||
using System;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Collections;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Texture.Astc
|
namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
// https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
|
// 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
|
struct TexelWeightParams
|
||||||
{
|
{
|
||||||
public int Width;
|
public int Width;
|
||||||
public int Height;
|
public int Height;
|
||||||
public bool DualPlane;
|
|
||||||
public int MaxWeight;
|
public int MaxWeight;
|
||||||
|
public bool DualPlane;
|
||||||
public bool Error;
|
public bool Error;
|
||||||
public bool VoidExtentLdr;
|
public bool VoidExtentLdr;
|
||||||
public bool VoidExtentHdr;
|
public bool VoidExtentHdr;
|
||||||
|
@ -48,7 +207,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryDecodeToRgba8(
|
public static bool TryDecodeToRgba8(
|
||||||
Span<byte> data,
|
ReadOnlyMemory<byte> data,
|
||||||
int blockWidth,
|
int blockWidth,
|
||||||
int blockHeight,
|
int blockHeight,
|
||||||
int width,
|
int width,
|
||||||
|
@ -57,87 +216,97 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
int levels,
|
int levels,
|
||||||
out Span<byte> decoded)
|
out Span<byte> decoded)
|
||||||
{
|
{
|
||||||
bool success = true;
|
byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels)];
|
||||||
|
|
||||||
using (MemoryStream inputStream = new MemoryStream(data.ToArray()))
|
AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels);
|
||||||
{
|
|
||||||
BinaryReader binReader = new BinaryReader(inputStream);
|
|
||||||
|
|
||||||
using (MemoryStream outputStream = new MemoryStream())
|
for (int i = 0; i < decoder.TotalBlockCount; i++)
|
||||||
{
|
{
|
||||||
int blockIndex = 0;
|
decoder.ProcessBlock(i);
|
||||||
|
|
||||||
int mipOffset = 0;
|
|
||||||
|
|
||||||
for (int l = 0; l < levels; l++)
|
|
||||||
{
|
|
||||||
int sliceSize = width * height * 4;
|
|
||||||
|
|
||||||
for (int k = 0; k < depth; k++)
|
|
||||||
for (int j = 0; j < height; j += blockHeight)
|
|
||||||
for (int i = 0; i < width; i += blockWidth)
|
|
||||||
{
|
|
||||||
int[] decompressedData = new int[144];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockWidth, blockHeight);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int decompressedWidth = Math.Min(blockWidth, width - i);
|
decoded = output;
|
||||||
int decompressedHeight = Math.Min(blockHeight, height - j);
|
|
||||||
|
|
||||||
int baseOffset = mipOffset + k * sliceSize + (j * width + i) * 4;
|
return decoder.Success;
|
||||||
|
}
|
||||||
|
|
||||||
for (int jj = 0; jj < decompressedHeight; jj++)
|
public static bool TryDecodeToRgba8(
|
||||||
|
ReadOnlyMemory<byte> data,
|
||||||
|
Memory<byte> outputBuffer,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels)
|
||||||
{
|
{
|
||||||
outputStream.Seek(baseOffset + jj * width * 4, SeekOrigin.Begin);
|
AstcDecoder decoder = new AstcDecoder(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels);
|
||||||
|
|
||||||
byte[] outputBuffer = new byte[decompressedData.Length * sizeof(int)];
|
for (int i = 0; i < decoder.TotalBlockCount; i++)
|
||||||
|
{
|
||||||
Buffer.BlockCopy(decompressedData, 0, outputBuffer, 0, outputBuffer.Length);
|
decoder.ProcessBlock(i);
|
||||||
|
|
||||||
outputStream.Write(outputBuffer, jj * blockWidth * 4, decompressedWidth * 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockIndex++;
|
return decoder.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
mipOffset += sliceSize * depth;
|
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);
|
||||||
|
|
||||||
width = Math.Max(1, width >> 1);
|
// Lazy parallelism
|
||||||
height = Math.Max(1, height >> 1);
|
Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
|
||||||
depth = Math.Max(1, depth >> 1);
|
|
||||||
|
return decoder.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded = outputStream.ToArray();
|
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)];
|
||||||
|
|
||||||
return success;
|
AstcDecoder decoder = new AstcDecoder(data, output, blockWidth, blockHeight, width, height, depth, levels);
|
||||||
|
|
||||||
|
Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
|
||||||
|
|
||||||
|
decoded = output;
|
||||||
|
|
||||||
|
return decoder.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DecompressBlock(
|
public static bool DecompressBlock(
|
||||||
byte[] inputBuffer,
|
Buffer16 inputBlock,
|
||||||
int[] outputBuffer,
|
Span<int> outputBuffer,
|
||||||
int blockWidth,
|
int blockWidth,
|
||||||
int blockHeight)
|
int blockHeight)
|
||||||
{
|
{
|
||||||
BitArrayStream bitStream = new BitArrayStream(new BitArray(inputBuffer));
|
BitStream128 bitStream = new BitStream128(inputBlock);
|
||||||
TexelWeightParams texelParams = DecodeBlockInfo(bitStream);
|
|
||||||
|
DecodeBlockInfo(ref bitStream, out TexelWeightParams texelParams);
|
||||||
|
|
||||||
if (texelParams.Error)
|
if (texelParams.Error)
|
||||||
{
|
{
|
||||||
throw new AstcDecoderException("Invalid block mode.");
|
throw new AstcDecoderException("Invalid block mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texelParams.VoidExtentLdr)
|
if (texelParams.VoidExtentLdr)
|
||||||
{
|
{
|
||||||
FillVoidExtentLdr(bitStream, outputBuffer, blockWidth, blockHeight);
|
FillVoidExtentLdr(ref bitStream, outputBuffer, blockWidth, blockHeight);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -170,11 +339,12 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
// each partition.
|
// each partition.
|
||||||
|
|
||||||
// Determine partitions, partition index, and color endpoint modes
|
// Determine partitions, partition index, and color endpoint modes
|
||||||
int planeIndices = -1;
|
int planeIndices;
|
||||||
int partitionIndex;
|
int partitionIndex;
|
||||||
uint[] colorEndpointMode = { 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
BitArrayStream colorEndpointStream = new BitArrayStream(new BitArray(16 * 8));
|
Span<uint> colorEndpointMode = stackalloc uint[4];
|
||||||
|
|
||||||
|
BitStream128 colorEndpointStream = new BitStream128();
|
||||||
|
|
||||||
// Read extra config data...
|
// Read extra config data...
|
||||||
uint baseColorEndpointMode = 0;
|
uint baseColorEndpointMode = 0;
|
||||||
|
@ -194,7 +364,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
// Remaining bits are color endpoint data...
|
// Remaining bits are color endpoint data...
|
||||||
int numberWeightBits = texelParams.GetPackedBitSize();
|
int numberWeightBits = texelParams.GetPackedBitSize();
|
||||||
int remainingBits = 128 - numberWeightBits - bitStream.Position;
|
int remainingBits = bitStream.BitsLeft - numberWeightBits;
|
||||||
|
|
||||||
// Consider extra bits prior to texel data...
|
// Consider extra bits prior to texel data...
|
||||||
uint extraColorEndpointModeBits = 0;
|
uint extraColorEndpointModeBits = 0;
|
||||||
|
@ -243,7 +413,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode;
|
uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode;
|
||||||
tempColorEndpointMode >>= 2;
|
tempColorEndpointMode >>= 2;
|
||||||
|
|
||||||
bool[] c = new bool[4];
|
Span<bool> c = stackalloc bool[4];
|
||||||
|
|
||||||
for (int i = 0; i < numberPartitions; i++)
|
for (int i = 0; i < numberPartitions; i++)
|
||||||
{
|
{
|
||||||
|
@ -251,7 +421,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
tempColorEndpointMode >>= 1;
|
tempColorEndpointMode >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] m = new byte[4];
|
Span<byte> m = stackalloc byte[4];
|
||||||
|
|
||||||
for (int i = 0; i < numberPartitions; i++)
|
for (int i = 0; i < numberPartitions; i++)
|
||||||
{
|
{
|
||||||
|
@ -272,7 +442,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
uint tempColorEndpointMode = baseColorEndpointMode >> 2;
|
uint tempColorEndpointMode = baseColorEndpointMode >> 2;
|
||||||
|
|
||||||
for (uint i = 0; i < numberPartitions; i++)
|
for (int i = 0; i < numberPartitions; i++)
|
||||||
{
|
{
|
||||||
colorEndpointMode[i] = tempColorEndpointMode;
|
colorEndpointMode[i] = tempColorEndpointMode;
|
||||||
}
|
}
|
||||||
|
@ -283,27 +453,24 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
Debug.Assert(colorEndpointMode[i] < 16);
|
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
|
// Decode both color data and texel weight data
|
||||||
int[] colorValues = new int[32]; // Four values * two endpoints * four maximum partitions
|
Span<int> colorValues = stackalloc int[32]; // Four values * two endpoints * four maximum partitions
|
||||||
DecodeColorValues(colorValues, colorEndpointStream.ToByteArray(), colorEndpointMode, numberPartitions, colorDataBits);
|
DecodeColorValues(colorValues, ref colorEndpointStream, colorEndpointMode, numberPartitions, colorDataBits);
|
||||||
|
|
||||||
AstcPixel[][] endPoints = new AstcPixel[4][];
|
EndPointSet endPoints;
|
||||||
endPoints[0] = new AstcPixel[2];
|
unsafe { _ = &endPoints; } // Skip struct initialization
|
||||||
endPoints[1] = new AstcPixel[2];
|
|
||||||
endPoints[2] = new AstcPixel[2];
|
|
||||||
endPoints[3] = new AstcPixel[2];
|
|
||||||
|
|
||||||
int colorValuesPosition = 0;
|
int colorValuesPosition = 0;
|
||||||
|
|
||||||
for (int i = 0; i < numberPartitions; i++)
|
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.
|
// Read the texel weight data.
|
||||||
byte[] texelWeightData = (byte[])inputBuffer.Clone();
|
Buffer16 texelWeightData = inputBlock;
|
||||||
|
|
||||||
// Reverse everything
|
// Reverse everything
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
|
@ -322,17 +489,21 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
int cLen = 16 - clearByteStart;
|
int cLen = 16 - clearByteStart;
|
||||||
for (int i = clearByteStart; i < clearByteStart + cLen; i++) texelWeightData[i] = 0;
|
for (int i = clearByteStart; i < clearByteStart + cLen; i++) texelWeightData[i] = 0;
|
||||||
|
|
||||||
List<IntegerEncoded> texelWeightValues = new List<IntegerEncoded>();
|
IntegerSequence texelWeightValues;
|
||||||
BitArrayStream weightBitStream = new BitArrayStream(new BitArray(texelWeightData));
|
unsafe { _ = &texelWeightValues; } // Skip struct initialization
|
||||||
|
texelWeightValues.Reset();
|
||||||
|
|
||||||
IntegerEncoded.DecodeIntegerSequence(texelWeightValues, weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues());
|
BitStream128 weightBitStream = new BitStream128(texelWeightData);
|
||||||
|
|
||||||
|
IntegerEncoded.DecodeIntegerSequence(ref texelWeightValues, ref weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues());
|
||||||
|
|
||||||
// Blocks can be at most 12x12, so we can have as many as 144 weights
|
// Blocks can be at most 12x12, so we can have as many as 144 weights
|
||||||
int[][] weights = new int[2][];
|
Weights weights;
|
||||||
weights[0] = new int[144];
|
unsafe { _ = &weights; } // Skip struct initialization
|
||||||
weights[1] = new int[144];
|
|
||||||
|
|
||||||
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
|
// Now that we have endpoints and weights, we can interpolate and generate
|
||||||
// the proper decoding...
|
// the proper decoding...
|
||||||
|
@ -343,13 +514,13 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32));
|
int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32));
|
||||||
Debug.Assert(partition < numberPartitions);
|
Debug.Assert(partition < numberPartitions);
|
||||||
|
|
||||||
AstcPixel pixel = new AstcPixel(0, 0, 0, 0);
|
AstcPixel pixel = new AstcPixel();
|
||||||
for (int component = 0; component < 4; component++)
|
for (int component = 0; component < 4; component++)
|
||||||
{
|
{
|
||||||
int component0 = endPoints[partition][0].GetComponent(component);
|
int component0 = endPoints.Get(partition)[0].GetComponent(component);
|
||||||
component0 = BitArrayStream.Replicate(component0, 8, 16);
|
component0 = table[component0];
|
||||||
int component1 = endPoints[partition][1].GetComponent(component);
|
int component1 = endPoints.Get(partition)[1].GetComponent(component);
|
||||||
component1 = BitArrayStream.Replicate(component1, 8, 16);
|
component1 = table[component1];
|
||||||
|
|
||||||
int plane = 0;
|
int plane = 0;
|
||||||
|
|
||||||
|
@ -358,7 +529,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
plane = 1;
|
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;
|
int finalComponent = (component0 * (64 - weight) + component1 * weight + 32) / 64;
|
||||||
|
|
||||||
if (finalComponent == 65535)
|
if (finalComponent == 65535)
|
||||||
|
@ -379,6 +550,38 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
return true;
|
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)
|
private static int Select2dPartition(int seed, int x, int y, int partitionCount, bool isSmallBlock)
|
||||||
{
|
{
|
||||||
return SelectPartition(seed, x, y, 0, partitionCount, isSmallBlock);
|
return SelectPartition(seed, x, y, 0, partitionCount, isSmallBlock);
|
||||||
|
@ -466,43 +669,49 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UnquantizeTexelWeights(
|
static void UnquantizeTexelWeights(
|
||||||
int[][] outputBuffer,
|
ref Weights outputBuffer,
|
||||||
List<IntegerEncoded> weights,
|
ref IntegerSequence weights,
|
||||||
TexelWeightParams texelParams,
|
ref TexelWeightParams texelParams,
|
||||||
int blockWidth,
|
int blockWidth,
|
||||||
int blockHeight)
|
int blockHeight)
|
||||||
{
|
{
|
||||||
int weightIndices = 0;
|
int weightIndices = 0;
|
||||||
int[][] unquantized = new int[2][];
|
Weights unquantized;
|
||||||
unquantized[0] = new int[144];
|
unsafe { _ = &unquantized; } // Skip struct initialization
|
||||||
unquantized[1] = new int[144];
|
|
||||||
|
|
||||||
for (int i = 0; i < weights.Count; i++)
|
Span<IntegerEncoded> weightsList = weights.List;
|
||||||
|
Span<int> unquantized0 = unquantized[0];
|
||||||
|
Span<int> unquantized1 = unquantized[1];
|
||||||
|
|
||||||
|
for (int i = 0; i < weightsList.Length; i++)
|
||||||
{
|
{
|
||||||
unquantized[0][weightIndices] = UnquantizeTexelWeight(weights[i]);
|
unquantized0[weightIndices] = UnquantizeTexelWeight(weightsList[i]);
|
||||||
|
|
||||||
if (texelParams.DualPlane)
|
if (texelParams.DualPlane)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
unquantized[1][weightIndices] = UnquantizeTexelWeight(weights[i]);
|
unquantized1[weightIndices] = UnquantizeTexelWeight(weightsList[i]);
|
||||||
|
|
||||||
if (i == weights.Count)
|
if (i == weightsList.Length)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++weightIndices >= (texelParams.Width * texelParams.Height)) break;
|
if (++weightIndices >= texelParams.Width * texelParams.Height) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do infill if necessary (Section C.2.18) ...
|
// Do infill if necessary (Section C.2.18) ...
|
||||||
int ds = (1024 + (blockWidth / 2)) / (blockWidth - 1);
|
int ds = (1024 + blockWidth / 2) / (blockWidth - 1);
|
||||||
int dt = (1024 + (blockHeight / 2)) / (blockHeight - 1);
|
int dt = (1024 + blockHeight / 2) / (blockHeight - 1);
|
||||||
|
|
||||||
int planeScale = texelParams.DualPlane ? 2 : 1;
|
int planeScale = texelParams.DualPlane ? 2 : 1;
|
||||||
|
|
||||||
for (int plane = 0; plane < planeScale; plane++)
|
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 t = 0; t < blockHeight; t++)
|
||||||
{
|
{
|
||||||
for (int s = 0; s < blockWidth; s++)
|
for (int s = 0; s < blockWidth; s++)
|
||||||
|
@ -520,38 +729,34 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
int ft = gt & 0x0F;
|
int ft = gt & 0x0F;
|
||||||
|
|
||||||
int w11 = (fs * ft + 8) >> 4;
|
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 v0 = js + jt * texelParams.Width;
|
||||||
|
|
||||||
int p00 = 0;
|
int weight = 8;
|
||||||
int p01 = 0;
|
|
||||||
int p10 = 0;
|
|
||||||
int p11 = 0;
|
|
||||||
|
|
||||||
if (v0 < (texelParams.Width * texelParams.Height))
|
int wxh = texelParams.Width * texelParams.Height;
|
||||||
|
|
||||||
|
if (v0 < wxh)
|
||||||
{
|
{
|
||||||
p00 = unquantized[plane][v0];
|
weight += unquantizedSpan[v0] * (16 - fs - ft + w11);
|
||||||
|
|
||||||
|
if (v0 + 1 < wxh)
|
||||||
|
{
|
||||||
|
weight += unquantizedSpan[v0 + 1] * (fs - w11);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v0 + 1 < (texelParams.Width * texelParams.Height))
|
if (v0 + texelParams.Width < wxh)
|
||||||
{
|
{
|
||||||
p01 = unquantized[plane][v0 + 1];
|
weight += unquantizedSpan[v0 + texelParams.Width] * (ft - w11);
|
||||||
|
|
||||||
|
if (v0 + texelParams.Width + 1 < wxh)
|
||||||
|
{
|
||||||
|
weight += unquantizedSpan[v0 + texelParams.Width + 1] * w11;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v0 + texelParams.Width < (texelParams.Width * texelParams.Height))
|
outputSpan[t * blockWidth + s] = weight >> 4;
|
||||||
{
|
|
||||||
p10 = unquantized[plane][v0 + texelParams.Width];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v0 + texelParams.Width + 1 < (texelParams.Width * texelParams.Height))
|
|
||||||
{
|
|
||||||
p11 = unquantized[plane][v0 + texelParams.Width + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
outputBuffer[plane][t * blockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,7 +767,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
int bitValue = intEncoded.BitValue;
|
int bitValue = intEncoded.BitValue;
|
||||||
int bitLength = intEncoded.NumberBits;
|
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 b = 0, c = 0, d = 0;
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -570,7 +775,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
switch (intEncoded.GetEncoding())
|
switch (intEncoded.GetEncoding())
|
||||||
{
|
{
|
||||||
case IntegerEncoded.EIntegerEncoding.JustBits:
|
case IntegerEncoded.EIntegerEncoding.JustBits:
|
||||||
result = BitArrayStream.Replicate(bitValue, bitLength, 6);
|
result = Bits.Replicate(bitValue, bitLength, 6);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IntegerEncoded.EIntegerEncoding.Trit:
|
case IntegerEncoded.EIntegerEncoding.Trit:
|
||||||
|
@ -582,8 +787,13 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
int[] results = { 0, 32, 63 };
|
result = d switch
|
||||||
result = results[d];
|
{
|
||||||
|
0 => 0,
|
||||||
|
1 => 32,
|
||||||
|
2 => 63,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -628,8 +838,15 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
int[] results = { 0, 16, 32, 47, 63 };
|
result = d switch
|
||||||
result = results[d];
|
{
|
||||||
|
0 => 0,
|
||||||
|
1 => 16,
|
||||||
|
2 => 32,
|
||||||
|
3 => 47,
|
||||||
|
4 => 63,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -683,33 +900,27 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32);
|
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++)
|
colorValuesPosition += number;
|
||||||
{
|
|
||||||
ret[i] = (uint)colorValues[colorValuesPosition++];
|
return MemoryMarshal.Cast<int, uint>(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
static Span<int> ReadIntColorValues(int number, Span<int> colorValues, ref int colorValuesPosition)
|
||||||
}
|
|
||||||
|
|
||||||
static int[] ReadIntColorValues(int number, int[] colorValues, ref int colorValuesPosition)
|
|
||||||
{
|
{
|
||||||
int[] ret = new int[number];
|
Span<int> ret = colorValues.Slice(colorValuesPosition, number);
|
||||||
|
|
||||||
for (int i = 0; i < number; i++)
|
colorValuesPosition += number;
|
||||||
{
|
|
||||||
ret[i] = colorValues[colorValuesPosition++];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ComputeEndpoints(
|
static void ComputeEndpoints(
|
||||||
AstcPixel[] endPoints,
|
Span<AstcPixel> endPoints,
|
||||||
int[] colorValues,
|
Span<int> colorValues,
|
||||||
uint colorEndpointMode,
|
uint colorEndpointMode,
|
||||||
ref int colorValuesPosition)
|
ref int colorValuesPosition)
|
||||||
{
|
{
|
||||||
|
@ -717,7 +928,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
case 0:
|
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[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]);
|
endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]);
|
||||||
|
@ -728,7 +939,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition);
|
Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition);
|
||||||
int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0));
|
int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0));
|
||||||
int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU);
|
int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU);
|
||||||
|
|
||||||
|
@ -740,7 +951,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
case 4:
|
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[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]);
|
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:
|
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]);
|
Bits.BitTransferSigned(ref val[1], ref val[0]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[3], ref val[2]);
|
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[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]));
|
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:
|
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[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]);
|
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:
|
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])
|
if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4])
|
||||||
{
|
{
|
||||||
|
@ -794,11 +1005,11 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
case 9:
|
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]);
|
Bits.BitTransferSigned(ref val[1], ref val[0]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[3], ref val[2]);
|
Bits.BitTransferSigned(ref val[3], ref val[2]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[5], ref val[4]);
|
Bits.BitTransferSigned(ref val[5], ref val[4]);
|
||||||
|
|
||||||
if (val[1] + val[3] + val[5] >= 0)
|
if (val[1] + val[3] + val[5] >= 0)
|
||||||
{
|
{
|
||||||
|
@ -819,7 +1030,7 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
case 10:
|
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[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]);
|
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:
|
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])
|
if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4])
|
||||||
{
|
{
|
||||||
|
@ -847,12 +1058,12 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
case 13:
|
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]);
|
Bits.BitTransferSigned(ref val[1], ref val[0]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[3], ref val[2]);
|
Bits.BitTransferSigned(ref val[3], ref val[2]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[5], ref val[4]);
|
Bits.BitTransferSigned(ref val[5], ref val[4]);
|
||||||
BitArrayStream.BitTransferSigned(ref val[7], ref val[6]);
|
Bits.BitTransferSigned(ref val[7], ref val[6]);
|
||||||
|
|
||||||
if (val[1] + val[3] + val[5] >= 0)
|
if (val[1] + val[3] + val[5] >= 0)
|
||||||
{
|
{
|
||||||
|
@ -877,9 +1088,9 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecodeColorValues(
|
static void DecodeColorValues(
|
||||||
int[] outputValues,
|
Span<int> outputValues,
|
||||||
byte[] inputData,
|
ref BitStream128 colorBitStream,
|
||||||
uint[] modes,
|
Span<uint> modes,
|
||||||
int numberPartitions,
|
int numberPartitions,
|
||||||
int numberBitsForColorData)
|
int numberBitsForColorData)
|
||||||
{
|
{
|
||||||
|
@ -919,16 +1130,17 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
}
|
}
|
||||||
|
|
||||||
// We now have enough to decode our integer sequence.
|
// We now have enough to decode our integer sequence.
|
||||||
List<IntegerEncoded> integerEncodedSequence = new List<IntegerEncoded>();
|
IntegerSequence integerEncodedSequence;
|
||||||
BitArrayStream colorBitStream = new BitArrayStream(new BitArray(inputData));
|
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
|
// 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
|
// This procedure is outlined in ASTC spec C.2.13
|
||||||
int outputIndices = 0;
|
int outputIndices = 0;
|
||||||
|
|
||||||
foreach (IntegerEncoded intEncoded in integerEncodedSequence)
|
foreach (ref IntegerEncoded intEncoded in integerEncodedSequence.List)
|
||||||
{
|
{
|
||||||
int bitLength = intEncoded.NumberBits;
|
int bitLength = intEncoded.NumberBits;
|
||||||
int bitValue = intEncoded.BitValue;
|
int bitValue = intEncoded.BitValue;
|
||||||
|
@ -937,13 +1149,13 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
int a = 0, b = 0, c = 0, d = 0;
|
int a = 0, b = 0, c = 0, d = 0;
|
||||||
// A is just the lsb replicated 9 times.
|
// 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())
|
switch (intEncoded.GetEncoding())
|
||||||
{
|
{
|
||||||
case IntegerEncoded.EIntegerEncoding.JustBits:
|
case IntegerEncoded.EIntegerEncoding.JustBits:
|
||||||
{
|
{
|
||||||
outputValues[outputIndices++] = BitArrayStream.Replicate(bitValue, bitLength, 8);
|
outputValues[outputIndices++] = Bits.Replicate(bitValue, bitLength, 8);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -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...
|
// Don't actually care about the void extent, just read the bits...
|
||||||
for (int i = 0; i < 4; ++i)
|
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
|
// Read the entire block mode all at once
|
||||||
ushort modeBits = (ushort)bitStream.ReadBits(11);
|
ushort modeBits = (ushort)bitStream.ReadBits(11);
|
||||||
|
@ -1146,14 +1358,15 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
texelParams.Error = true;
|
texelParams.Error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return texelParams;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check if the last four bits are zero
|
// First check if the last four bits are zero
|
||||||
if ((modeBits & 0xF) == 0)
|
if ((modeBits & 0xF) == 0)
|
||||||
{
|
{
|
||||||
texelParams.Error = true;
|
texelParams.Error = true;
|
||||||
return texelParams;
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the last two bits are zero, then if bits
|
// If the last two bits are zero, then if bits
|
||||||
|
@ -1162,14 +1375,14 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
{
|
{
|
||||||
texelParams.Error = true;
|
texelParams.Error = true;
|
||||||
|
|
||||||
return texelParams;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, there is no error... Figure out the layout
|
// Otherwise, there is no error... Figure out the layout
|
||||||
// of the block mode. Layout is determined by a number
|
// of the block mode. Layout is determined by a number
|
||||||
// between 0 and 9 corresponding to table C.2.8 of the
|
// between 0 and 9 corresponding to table C.2.8 of the
|
||||||
// ASTC spec.
|
// ASTC spec.
|
||||||
int layout = 0;
|
int layout;
|
||||||
|
|
||||||
if ((modeBits & 0x1) != 0 || (modeBits & 0x2) != 0)
|
if ((modeBits & 0x1) != 0 || (modeBits & 0x2) != 0)
|
||||||
{
|
{
|
||||||
|
@ -1378,18 +1591,16 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
|
|
||||||
if (h)
|
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];
|
texelParams.MaxWeight = maxWeights[r - 2];
|
||||||
}
|
}
|
||||||
else
|
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.MaxWeight = maxWeights[r - 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
texelParams.DualPlane = d;
|
texelParams.DualPlane = d;
|
||||||
|
|
||||||
return texelParams;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
72
Ryujinx.Graphics.Texture/Astc/BitStream128.cs
Normal file
72
Ryujinx.Graphics.Texture/Astc/BitStream128.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
Ryujinx.Graphics.Texture/Astc/Bits.cs
Normal file
66
Ryujinx.Graphics.Texture/Astc/Bits.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
Ryujinx.Graphics.Texture/Astc/EndPointSet.cs
Normal file
23
Ryujinx.Graphics.Texture/Astc/EndPointSet.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,15 +16,25 @@ namespace Ryujinx.Graphics.Texture.Astc
|
||||||
}
|
}
|
||||||
|
|
||||||
EIntegerEncoding _encoding;
|
EIntegerEncoding _encoding;
|
||||||
public int NumberBits { 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; }
|
public int BitValue { get; private set; }
|
||||||
public int TritValue { get; private set; }
|
|
||||||
public int QuintValue { 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,148 +102,76 @@ 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);
|
m[i] = 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 }));
|
uint encodedBits = (uint)bitStream.ReadBits(interleavedBits[i]);
|
||||||
if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0)
|
|
||||||
{
|
encoded |= encodedBits << encodedBitsRead;
|
||||||
qa[0] = qa[1] = 4;
|
encodedBitsRead += interleavedBits[i];
|
||||||
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 }));
|
ReadOnlySpan<byte> encodings = GetQuintEncoding((int)encoded);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs
Normal file
31
Ryujinx.Graphics.Texture/Astc/IntegerSequence.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in a new issue