diff options
| author | Alex Barney <thealexbarney@gmail.com> | 2019-12-26 23:09:49 -0700 |
|---|---|---|
| committer | Thog <thog@protonmail.com> | 2020-01-09 02:13:00 +0100 |
| commit | d1ab9fb42c2fd9f018d4410ca619cd66294eafc9 (patch) | |
| tree | dcac71560b921f29d73ee6e21f235528184d9f84 /Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs | |
| parent | 947e14d3be0f7c5e4b6f77df204ec675b8e9e719 (diff) | |
ASTC optimizations (#845)
* ASTC optimizations
* Move code to Ryujinx.Common
* Support 3D textures
* Address feedback
* Remove ASTC logging
* Use stackalloc instead of a Buffer20 struct
* Code style and cleanup
* Respond to feedback
* Rearrange public/private property ordering
Diffstat (limited to 'Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs')
| -rw-r--r-- | Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs | 334 |
1 files changed, 205 insertions, 129 deletions
diff --git a/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index 5aa610e7..065de46b 100644 --- a/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs +++ b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -1,11 +1,14 @@ -using System.Collections; -using System.Collections.Generic; +using System; +using System.Numerics; namespace Ryujinx.Graphics.Texture.Astc { - public struct IntegerEncoded + internal struct IntegerEncoded { - public enum EIntegerEncoding + internal const int StructSize = 8; + private static readonly IntegerEncoded[] Encodings; + + public enum EIntegerEncoding : byte { JustBits, Quint, @@ -13,17 +16,27 @@ namespace Ryujinx.Graphics.Texture.Astc } EIntegerEncoding _encoding; - public int NumberBits { get; private set; } - public int BitValue { get; private set; } - public int TritValue { get; private set; } - public int QuintValue { get; private set; } + public byte NumberBits { get; private set; } + public byte TritValue { get; private set; } + public byte QuintValue { get; private set; } + public int BitValue { get; private set; } + + static IntegerEncoded() + { + Encodings = new IntegerEncoded[0x100]; + + for (int i = 0; i < Encodings.Length; i++) + { + Encodings[i] = CreateEncodingCalc(i); + } + } public IntegerEncoded(EIntegerEncoding encoding, int numBits) { - _encoding = encoding; - NumberBits = numBits; - BitValue = 0; - TritValue = 0; + _encoding = encoding; + NumberBits = (byte)numBits; + BitValue = 0; + TritValue = 0; QuintValue = 0; } @@ -53,6 +66,11 @@ namespace Ryujinx.Graphics.Texture.Astc public static IntegerEncoded CreateEncoding(int maxVal) { + return Encodings[maxVal]; + } + + private static IntegerEncoded CreateEncodingCalc(int maxVal) + { while (maxVal > 0) { int check = maxVal + 1; @@ -60,19 +78,19 @@ namespace Ryujinx.Graphics.Texture.Astc // Is maxVal a power of two? if ((check & (check - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(maxVal)); + return new IntegerEncoded(EIntegerEncoding.JustBits, BitOperations.PopCount((uint)maxVal)); } // Is maxVal of the type 3*2^n - 1? if ((check % 3 == 0) && ((check / 3) & ((check / 3) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(check / 3 - 1)); + return new IntegerEncoded(EIntegerEncoding.Trit, BitOperations.PopCount((uint)(check / 3 - 1))); } // Is maxVal of the type 5*2^n - 1? if ((check % 5 == 0) && ((check / 5) & ((check / 5) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(check / 5 - 1)); + return new IntegerEncoded(EIntegerEncoding.Quint, BitOperations.PopCount((uint)(check / 5 - 1))); } // Apparently it can't be represented with a bounded integer sequence... @@ -84,150 +102,78 @@ namespace Ryujinx.Graphics.Texture.Astc } public static void DecodeTritBlock( - BitArrayStream bitStream, - List<IntegerEncoded> listIntegerEncoded, - int numberBitsPerValue) + ref BitStream128 bitStream, + ref IntegerSequence listIntegerEncoded, + int numberBitsPerValue) { // Implement the algorithm in section C.2.12 - int[] m = new int[5]; - int[] t = new int[5]; - int T; + Span<int> m = stackalloc int[5]; - // Read the trit encoded block according to - // table C.2.14 m[0] = bitStream.ReadBits(numberBitsPerValue); - T = bitStream.ReadBits(2); + int encoded = bitStream.ReadBits(2); m[1] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(2) << 2; + encoded |= bitStream.ReadBits(2) << 2; m[2] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(1) << 4; + encoded |= bitStream.ReadBits(1) << 4; m[3] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(2) << 5; + encoded |= bitStream.ReadBits(2) << 5; m[4] = bitStream.ReadBits(numberBitsPerValue); - T |= bitStream.ReadBits(1) << 7; - - int c = 0; + encoded |= bitStream.ReadBits(1) << 7; - BitArrayStream tb = new BitArrayStream(new BitArray(new int[] { T })); - if (tb.ReadBits(2, 4) == 7) - { - c = (tb.ReadBits(5, 7) << 2) | tb.ReadBits(0, 1); - t[4] = t[3] = 2; - } - else - { - c = tb.ReadBits(0, 4); - if (tb.ReadBits(5, 6) == 3) - { - t[4] = 2; - t[3] = tb.ReadBit(7); - } - else - { - t[4] = tb.ReadBit(7); - t[3] = tb.ReadBits(5, 6); - } - } + ReadOnlySpan<byte> encodings = GetTritEncoding(encoded); - BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); - if (cb.ReadBits(0, 1) == 3) - { - t[2] = 2; - t[1] = cb.ReadBit(4); - t[0] = (cb.ReadBit(3) << 1) | (cb.ReadBit(2) & ~cb.ReadBit(3)); - } - else if (cb.ReadBits(2, 3) == 3) - { - t[2] = 2; - t[1] = 2; - t[0] = cb.ReadBits(0, 1); - } - else - { - t[2] = cb.ReadBit(4); - t[1] = cb.ReadBits(2, 3); - t[0] = (cb.ReadBit(1) << 1) | (cb.ReadBit(0) & ~cb.ReadBit(1)); - } + IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue); for (int i = 0; i < 5; i++) { - IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue) - { - BitValue = m[i], - TritValue = t[i] - }; - listIntegerEncoded.Add(intEncoded); + intEncoded.BitValue = m[i]; + intEncoded.TritValue = encodings[i]; + + listIntegerEncoded.Add(ref intEncoded); } } public static void DecodeQuintBlock( - BitArrayStream bitStream, - List<IntegerEncoded> listIntegerEncoded, - int numberBitsPerValue) + ref BitStream128 bitStream, + ref IntegerSequence listIntegerEncoded, + int numberBitsPerValue) { - // Implement the algorithm in section C.2.12 - int[] m = new int[3]; - int[] qa = new int[3]; - int q; + ReadOnlySpan<byte> interleavedBits = new byte[] { 3, 2, 2 }; - // Read the trit encoded block according to - // table C.2.15 - m[0] = bitStream.ReadBits(numberBitsPerValue); - q = bitStream.ReadBits(3); - m[1] = bitStream.ReadBits(numberBitsPerValue); - q |= bitStream.ReadBits(2) << 3; - m[2] = bitStream.ReadBits(numberBitsPerValue); - q |= bitStream.ReadBits(2) << 5; + // Implement the algorithm in section C.2.12 + Span<int> m = stackalloc int[3]; + ulong encoded = 0; + int encodedBitsRead = 0; - BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q })); - if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0) - { - qa[0] = qa[1] = 4; - qa[2] = (qb.ReadBit(0) << 2) | ((qb.ReadBit(4) & ~qb.ReadBit(0)) << 1) | (qb.ReadBit(3) & ~qb.ReadBit(0)); - } - else + for (int i = 0; i < m.Length; i++) { - int c = 0; - if (qb.ReadBits(1, 2) == 3) - { - qa[2] = 4; - c = (qb.ReadBits(3, 4) << 3) | ((~qb.ReadBits(5, 6) & 3) << 1) | qb.ReadBit(0); - } - else - { - qa[2] = qb.ReadBits(5, 6); - c = qb.ReadBits(0, 4); - } + m[i] = bitStream.ReadBits(numberBitsPerValue); - BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); - if (cb.ReadBits(0, 2) == 5) - { - qa[1] = 4; - qa[0] = cb.ReadBits(3, 4); - } - else - { - qa[1] = cb.ReadBits(3, 4); - qa[0] = cb.ReadBits(0, 2); - } + uint encodedBits = (uint)bitStream.ReadBits(interleavedBits[i]); + + encoded |= encodedBits << encodedBitsRead; + encodedBitsRead += interleavedBits[i]; } + ReadOnlySpan<byte> encodings = GetQuintEncoding((int)encoded); + for (int i = 0; i < 3; i++) { IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue) { - BitValue = m[i], - QuintValue = qa[i] + BitValue = m[i], + QuintValue = encodings[i] }; - listIntegerEncoded.Add(intEncoded); + + listIntegerEncoded.Add(ref intEncoded); } } public static void DecodeIntegerSequence( - List<IntegerEncoded> decodeIntegerSequence, - BitArrayStream bitStream, - int maxRange, - int numberValues) + ref IntegerSequence decodeIntegerSequence, + ref BitStream128 bitStream, + int maxRange, + int numberValues) { // Determine encoding parameters IntegerEncoded intEncoded = CreateEncoding(maxRange); @@ -240,7 +186,7 @@ namespace Ryujinx.Graphics.Texture.Astc { case EIntegerEncoding.Quint: { - DecodeQuintBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + DecodeQuintBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); numberValuesDecoded += 3; break; @@ -248,7 +194,7 @@ namespace Ryujinx.Graphics.Texture.Astc case EIntegerEncoding.Trit: { - DecodeTritBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + DecodeTritBlock(ref bitStream, ref decodeIntegerSequence, intEncoded.NumberBits); numberValuesDecoded += 5; break; @@ -257,7 +203,7 @@ namespace Ryujinx.Graphics.Texture.Astc case EIntegerEncoding.JustBits: { intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); - decodeIntegerSequence.Add(intEncoded); + decodeIntegerSequence.Add(ref intEncoded); numberValuesDecoded++; break; @@ -265,5 +211,135 @@ namespace Ryujinx.Graphics.Texture.Astc } } } + + private static ReadOnlySpan<byte> GetTritEncoding(int index) + { + return TritEncodings.Slice(index * 5, 5); + } + + private static ReadOnlySpan<byte> GetQuintEncoding(int index) + { + return QuintEncodings.Slice(index * 3, 3); + } + + private static ReadOnlySpan<byte> TritEncodings => new byte[] + { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, + 2, 1, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2, 0, 0, 0, + 1, 2, 0, 0, 0, 2, 2, 0, 0, 0, 2, 0, 2, 0, 0, + 0, 2, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, + 2, 0, 1, 0, 0, 0, 1, 2, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 2, 1, 1, 0, 0, 1, 1, 2, 0, 0, + 0, 2, 1, 0, 0, 1, 2, 1, 0, 0, 2, 2, 1, 0, 0, + 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 1, 0, 0, 2, 2, + 2, 0, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0, 2, 1, 0, + 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 2, 1, 0, 1, 0, + 1, 0, 2, 1, 0, 0, 2, 0, 1, 0, 1, 2, 0, 1, 0, + 2, 2, 0, 1, 0, 2, 0, 2, 1, 0, 0, 2, 2, 1, 0, + 1, 2, 2, 1, 0, 2, 2, 2, 1, 0, 2, 0, 2, 1, 0, + 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 2, 0, 1, 1, 0, + 0, 1, 2, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, + 2, 1, 1, 1, 0, 1, 1, 2, 1, 0, 0, 2, 1, 1, 0, + 1, 2, 1, 1, 0, 2, 2, 1, 1, 0, 2, 1, 2, 1, 0, + 0, 1, 0, 2, 2, 1, 1, 0, 2, 2, 2, 1, 0, 2, 2, + 1, 0, 2, 2, 2, 0, 0, 0, 2, 0, 1, 0, 0, 2, 0, + 2, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 1, 0, 2, 0, + 1, 1, 0, 2, 0, 2, 1, 0, 2, 0, 1, 0, 2, 2, 0, + 0, 2, 0, 2, 0, 1, 2, 0, 2, 0, 2, 2, 0, 2, 0, + 2, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 2, 2, 0, + 2, 2, 2, 2, 0, 2, 0, 2, 2, 0, 0, 0, 1, 2, 0, + 1, 0, 1, 2, 0, 2, 0, 1, 2, 0, 0, 1, 2, 2, 0, + 0, 1, 1, 2, 0, 1, 1, 1, 2, 0, 2, 1, 1, 2, 0, + 1, 1, 2, 2, 0, 0, 2, 1, 2, 0, 1, 2, 1, 2, 0, + 2, 2, 1, 2, 0, 2, 1, 2, 2, 0, 0, 2, 0, 2, 2, + 1, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 2, 0, 0, 0, 2, + 0, 0, 2, 0, 2, 0, 1, 0, 0, 2, 1, 1, 0, 0, 2, + 2, 1, 0, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 0, 2, + 1, 2, 0, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 0, 2, + 0, 2, 2, 0, 2, 1, 2, 2, 0, 2, 2, 2, 2, 0, 2, + 2, 0, 2, 0, 2, 0, 0, 1, 0, 2, 1, 0, 1, 0, 2, + 2, 0, 1, 0, 2, 0, 1, 2, 0, 2, 0, 1, 1, 0, 2, + 1, 1, 1, 0, 2, 2, 1, 1, 0, 2, 1, 1, 2, 0, 2, + 0, 2, 1, 0, 2, 1, 2, 1, 0, 2, 2, 2, 1, 0, 2, + 2, 1, 2, 0, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0, 0, 2, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, + 1, 0, 2, 0, 1, 0, 2, 0, 0, 1, 1, 2, 0, 0, 1, + 2, 2, 0, 0, 1, 2, 0, 2, 0, 1, 0, 2, 2, 0, 1, + 1, 2, 2, 0, 1, 2, 2, 2, 0, 1, 2, 0, 2, 0, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 2, 0, 1, 0, 1, + 0, 1, 2, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 2, 1, 1, 0, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, + 1, 2, 1, 0, 1, 2, 2, 1, 0, 1, 2, 1, 2, 0, 1, + 0, 0, 1, 2, 2, 1, 0, 1, 2, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, + 2, 0, 0, 1, 1, 0, 0, 2, 1, 1, 0, 1, 0, 1, 1, + 1, 1, 0, 1, 1, 2, 1, 0, 1, 1, 1, 0, 2, 1, 1, + 0, 2, 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 1, + 2, 0, 2, 1, 1, 0, 2, 2, 1, 1, 1, 2, 2, 1, 1, + 2, 2, 2, 1, 1, 2, 0, 2, 1, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 1, + 2, 2, 1, 1, 1, 2, 1, 2, 1, 1, 0, 1, 1, 2, 2, + 1, 1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, + 0, 0, 0, 2, 1, 1, 0, 0, 2, 1, 2, 0, 0, 2, 1, + 0, 0, 2, 2, 1, 0, 1, 0, 2, 1, 1, 1, 0, 2, 1, + 2, 1, 0, 2, 1, 1, 0, 2, 2, 1, 0, 2, 0, 2, 1, + 1, 2, 0, 2, 1, 2, 2, 0, 2, 1, 2, 0, 2, 2, 1, + 0, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2, 2, 1, + 2, 0, 2, 2, 1, 0, 0, 1, 2, 1, 1, 0, 1, 2, 1, + 2, 0, 1, 2, 1, 0, 1, 2, 2, 1, 0, 1, 1, 2, 1, + 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, + 0, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, + 2, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1, 2, 1, 2, 2, + 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 1, 2, + 1, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 0, 2, 1, 2, + 0, 1, 0, 1, 2, 1, 1, 0, 1, 2, 2, 1, 0, 1, 2, + 1, 0, 2, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 2, + 2, 2, 0, 1, 2, 2, 0, 2, 1, 2, 0, 2, 2, 1, 2, + 1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 0, 2, 1, 2, + 0, 0, 1, 1, 2, 1, 0, 1, 1, 2, 2, 0, 1, 1, 2, + 0, 1, 2, 1, 2, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, + 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 0, 2, 1, 1, 2, + 1, 2, 1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, + 0, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 2, 2, 2 + }; + + private static ReadOnlySpan<byte> QuintEncodings => new byte[] + { + 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, + 0, 4, 0, 4, 4, 0, 4, 4, 4, 0, 1, 0, 1, 1, 0, + 2, 1, 0, 3, 1, 0, 4, 1, 0, 1, 4, 0, 4, 4, 1, + 4, 4, 4, 0, 2, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, + 4, 2, 0, 2, 4, 0, 4, 4, 2, 4, 4, 4, 0, 3, 0, + 1, 3, 0, 2, 3, 0, 3, 3, 0, 4, 3, 0, 3, 4, 0, + 4, 4, 3, 4, 4, 4, 0, 0, 1, 1, 0, 1, 2, 0, 1, + 3, 0, 1, 4, 0, 1, 0, 4, 1, 4, 0, 4, 0, 4, 4, + 0, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 4, 1, 1, + 1, 4, 1, 4, 1, 4, 1, 4, 4, 0, 2, 1, 1, 2, 1, + 2, 2, 1, 3, 2, 1, 4, 2, 1, 2, 4, 1, 4, 2, 4, + 2, 4, 4, 0, 3, 1, 1, 3, 1, 2, 3, 1, 3, 3, 1, + 4, 3, 1, 3, 4, 1, 4, 3, 4, 3, 4, 4, 0, 0, 2, + 1, 0, 2, 2, 0, 2, 3, 0, 2, 4, 0, 2, 0, 4, 2, + 2, 0, 4, 3, 0, 4, 0, 1, 2, 1, 1, 2, 2, 1, 2, + 3, 1, 2, 4, 1, 2, 1, 4, 2, 2, 1, 4, 3, 1, 4, + 0, 2, 2, 1, 2, 2, 2, 2, 2, 3, 2, 2, 4, 2, 2, + 2, 4, 2, 2, 2, 4, 3, 2, 4, 0, 3, 2, 1, 3, 2, + 2, 3, 2, 3, 3, 2, 4, 3, 2, 3, 4, 2, 2, 3, 4, + 3, 3, 4, 0, 0, 3, 1, 0, 3, 2, 0, 3, 3, 0, 3, + 4, 0, 3, 0, 4, 3, 0, 0, 4, 1, 0, 4, 0, 1, 3, + 1, 1, 3, 2, 1, 3, 3, 1, 3, 4, 1, 3, 1, 4, 3, + 0, 1, 4, 1, 1, 4, 0, 2, 3, 1, 2, 3, 2, 2, 3, + 3, 2, 3, 4, 2, 3, 2, 4, 3, 0, 2, 4, 1, 2, 4, + 0, 3, 3, 1, 3, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, + 3, 4, 3, 0, 3, 4, 1, 3, 4 + }; } } |
