From 11a7c99764ed4e6c575c877c69ca627645702a42 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 1 Nov 2020 15:32:53 -0300 Subject: Support 3D BC4 and BC5 compressed textures (#1655) * Support 3D BC4 and BC5 compressed textures * PR feedback * Fix some typos --- Ryujinx.Graphics.Texture/BCnDecoder.cs | 242 +++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 Ryujinx.Graphics.Texture/BCnDecoder.cs (limited to 'Ryujinx.Graphics.Texture/BCnDecoder.cs') diff --git a/Ryujinx.Graphics.Texture/BCnDecoder.cs b/Ryujinx.Graphics.Texture/BCnDecoder.cs new file mode 100644 index 00000000..398e8358 --- /dev/null +++ b/Ryujinx.Graphics.Texture/BCnDecoder.cs @@ -0,0 +1,242 @@ +using Ryujinx.Common; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Texture +{ + public static class BCnDecoder + { + private const int BlockWidth = 4; + private const int BlockHeight = 4; + + public static byte[] DecodeBC4(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + { + int size = 0; + + for (int l = 0; l < levels; l++) + { + size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers; + } + + byte[] output = new byte[size]; + + ReadOnlySpan data64 = MemoryMarshal.Cast(data); + + Span rPal = stackalloc byte[8]; + + int baseOOffs = 0; + + for (int l = 0; l < levels; l++) + { + int w = BitUtils.DivRoundUp(width, BlockWidth); + int h = BitUtils.DivRoundUp(height, BlockHeight); + + for (int l2 = 0; l2 < layers; l2++) + { + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < h; y++) + { + int baseY = y * BlockHeight; + + for (int x = 0; x < w; x++) + { + int baseX = x * BlockWidth; + int lineBaseOOffs = baseOOffs + baseX; + + ulong block = data64[0]; + + rPal[0] = (byte)block; + rPal[1] = (byte)(block >> 8); + + if (signed) + { + CalculateBC3AlphaS(rPal); + } + else + { + CalculateBC3Alpha(rPal); + } + + ulong rI = block >> 16; + + for (int texel = 0; texel < BlockWidth * BlockHeight; texel++) + { + int tX = texel & 3; + int tY = texel >> 2; + + if (baseX + tX >= width || baseY + tY >= height) + { + continue; + } + + int shift = texel * 3; + + byte r = rPal[(int)((rI >> shift) & 7)]; + + int oOffs = lineBaseOOffs + tY * width + tX; + + output[oOffs] = r; + } + + data64 = data64.Slice(1); + } + + baseOOffs += width * (baseY + BlockHeight > height ? (height & (BlockHeight - 1)) : BlockHeight); + } + } + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + depth = Math.Max(1, depth >> 1); + } + + return output; + } + + public static byte[] DecodeBC5(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + { + int size = 0; + + for (int l = 0; l < levels; l++) + { + size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 2; + } + + byte[] output = new byte[size]; + + ReadOnlySpan data64 = MemoryMarshal.Cast(data); + + Span rPal = stackalloc byte[8]; + Span gPal = stackalloc byte[8]; + + int baseOOffs = 0; + + for (int l = 0; l < levels; l++) + { + int w = BitUtils.DivRoundUp(width, BlockWidth); + int h = BitUtils.DivRoundUp(height, BlockHeight); + + for (int l2 = 0; l2 < layers; l2++) + { + for (int z = 0; z < depth; z++) + { + for (int y = 0; y < h; y++) + { + int baseY = y * BlockHeight; + + for (int x = 0; x < w; x++) + { + int baseX = x * BlockWidth; + int lineBaseOOffs = baseOOffs + baseX; + + ulong blockL = data64[0]; + ulong blockH = data64[1]; + + rPal[0] = (byte)blockL; + rPal[1] = (byte)(blockL >> 8); + gPal[0] = (byte)blockH; + gPal[1] = (byte)(blockH >> 8); + + if (signed) + { + CalculateBC3AlphaS(rPal); + CalculateBC3AlphaS(gPal); + } + else + { + CalculateBC3Alpha(rPal); + CalculateBC3Alpha(gPal); + } + + ulong rI = blockL >> 16; + ulong gI = blockH >> 16; + + for (int texel = 0; texel < BlockWidth * BlockHeight; texel++) + { + int tX = texel & 3; + int tY = texel >> 2; + + if (baseX + tX >= width || baseY + tY >= height) + { + continue; + } + + int shift = texel * 3; + + byte r = rPal[(int)((rI >> shift) & 7)]; + byte g = gPal[(int)((gI >> shift) & 7)]; + + int oOffs = (lineBaseOOffs + tY * width + tX) * 2; + + output[oOffs + 0] = r; + output[oOffs + 1] = g; + } + + data64 = data64.Slice(2); + } + + baseOOffs += width * (baseY + BlockHeight > height ? (height & (BlockHeight - 1)) : BlockHeight); + } + } + } + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + depth = Math.Max(1, depth >> 1); + } + + return output; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CalculateBC3Alpha(Span alpha) + { + for (int i = 2; i < 8; i++) + { + if (alpha[0] > alpha[1]) + { + alpha[i] = (byte)(((8 - i) * alpha[0] + (i - 1) * alpha[1]) / 7); + } + else if (i < 6) + { + alpha[i] = (byte)(((6 - i) * alpha[0] + (i - 1) * alpha[1]) / 7); + } + else if (i == 6) + { + alpha[i] = 0; + } + else /* i == 7 */ + { + alpha[i] = 0xff; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CalculateBC3AlphaS(Span alpha) + { + for (int i = 2; i < 8; i++) + { + if ((sbyte)alpha[0] > (sbyte)alpha[1]) + { + alpha[i] = (byte)(((8 - i) * (sbyte)alpha[0] + (i - 1) * (sbyte)alpha[1]) / 7); + } + else if (i < 6) + { + alpha[i] = (byte)(((6 - i) * (sbyte)alpha[0] + (i - 1) * (sbyte)alpha[1]) / 7); + } + else if (i == 6) + { + alpha[i] = 0x80; + } + else /* i == 7 */ + { + alpha[i] = 0x7f; + } + } + } + } +} \ No newline at end of file -- cgit v1.2.3