aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs')
-rw-r--r--Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs237
1 files changed, 237 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs
new file mode 100644
index 00000000..94aa6979
--- /dev/null
+++ b/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Buffers.Binary;
+using Ryujinx.Common.Memory;
+
+namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
+{
+ internal struct Reader
+ {
+ private static readonly byte[] Norm = new byte[]
+ {
+ 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ private const int BdValueSize = sizeof(ulong) * 8;
+
+ // This is meant to be a large, positive constant that can still be efficiently
+ // loaded as an immediate (on platforms like ARM, for example).
+ // Even relatively modest values like 100 would work fine.
+ private const int LotsOfBits = 0x40000000;
+
+ public ulong Value;
+ public uint Range;
+ public int Count;
+ private ArrayPtr<byte> _buffer;
+
+ public bool Init(ArrayPtr<byte> buffer, int size)
+ {
+ if (size != 0 && buffer.IsNull)
+ {
+ return true;
+ }
+ else
+ {
+ _buffer = new ArrayPtr<byte>(ref buffer[0], size);
+ Value = 0;
+ Count = -8;
+ Range = 255;
+ Fill();
+ return ReadBit() != 0; // Marker bit
+ }
+ }
+
+ private void Fill()
+ {
+ ReadOnlySpan<byte> buffer = _buffer.ToSpan();
+ ReadOnlySpan<byte> bufferStart = buffer;
+ ulong value = Value;
+ int count = Count;
+ ulong bytesLeft = (ulong)buffer.Length;
+ ulong bitsLeft = bytesLeft * 8;
+ int shift = BdValueSize - 8 - (count + 8);
+
+ if (bitsLeft > BdValueSize)
+ {
+ int bits = (shift & unchecked((int)0xfffffff8)) + 8;
+ ulong nv;
+ ulong bigEndianValues = BinaryPrimitives.ReadUInt64BigEndian(buffer);
+ nv = bigEndianValues >> (BdValueSize - bits);
+ count += bits;
+ buffer = buffer.Slice(bits >> 3);
+ value = Value | (nv << (shift & 0x7));
+ }
+ else
+ {
+ int bitsOver = shift + 8 - (int)bitsLeft;
+ int loopEnd = 0;
+ if (bitsOver >= 0)
+ {
+ count += LotsOfBits;
+ loopEnd = bitsOver;
+ }
+
+ if (bitsOver < 0 || bitsLeft != 0)
+ {
+ while (shift >= loopEnd)
+ {
+ count += 8;
+ value |= (ulong)buffer[0] << shift;
+ buffer = buffer.Slice(1);
+ shift -= 8;
+ }
+ }
+ }
+
+ // NOTE: Variable 'buffer' may not relate to '_buffer' after decryption,
+ // so we increase '_buffer' by the amount that 'buffer' moved, rather than
+ // assign 'buffer' to '_buffer'.
+ _buffer = _buffer.Slice(bufferStart.Length - buffer.Length);
+ Value = value;
+ Count = count;
+ }
+
+ public bool HasError()
+ {
+ // Check if we have reached the end of the buffer.
+ //
+ // Variable 'count' stores the number of bits in the 'value' buffer, minus
+ // 8. The top byte is part of the algorithm, and the remainder is buffered
+ // to be shifted into it. So if count == 8, the top 16 bits of 'value' are
+ // occupied, 8 for the algorithm and 8 in the buffer.
+ //
+ // When reading a byte from the user's buffer, count is filled with 8 and
+ // one byte is filled into the value buffer. When we reach the end of the
+ // data, count is additionally filled with LotsOfBits. So when
+ // count == LotsOfBits - 1, the user's data has been exhausted.
+ //
+ // 1 if we have tried to decode bits after the end of stream was encountered.
+ // 0 No error.
+ return Count > BdValueSize && Count < LotsOfBits;
+ }
+
+ public int Read(int prob)
+ {
+ uint bit = 0;
+ ulong value;
+ ulong bigsplit;
+ int count;
+ uint range;
+ uint split = (Range * (uint)prob + (256 - (uint)prob)) >> 8;
+
+ if (Count < 0)
+ {
+ Fill();
+ }
+
+ value = Value;
+ count = Count;
+
+ bigsplit = (ulong)split << (BdValueSize - 8);
+
+ range = split;
+
+ if (value >= bigsplit)
+ {
+ range = Range - split;
+ value -= bigsplit;
+ bit = 1;
+ }
+
+ {
+ int shift = Norm[range];
+ range <<= shift;
+ value <<= shift;
+ count -= shift;
+ }
+ Value = value;
+ Count = count;
+ Range = range;
+
+ return (int)bit;
+ }
+
+ public int ReadBit()
+ {
+ return Read(128); // vpx_prob_half
+ }
+
+ public int ReadLiteral(int bits)
+ {
+ int literal = 0, bit;
+
+ for (bit = bits - 1; bit >= 0; bit--)
+ {
+ literal |= ReadBit() << bit;
+ }
+
+ return literal;
+ }
+
+ public int ReadTree(ReadOnlySpan<sbyte> tree, ReadOnlySpan<byte> probs)
+ {
+ sbyte i = 0;
+
+ while ((i = tree[i + Read(probs[i >> 1])]) > 0)
+ {
+ continue;
+ }
+
+ return -i;
+ }
+
+ public int ReadBool(int prob, ref ulong value, ref int count, ref uint range)
+ {
+ uint split = (range * (uint)prob + (256 - (uint)prob)) >> 8;
+ ulong bigsplit = (ulong)split << (BdValueSize - 8);
+
+ if (count < 0)
+ {
+ Value = value;
+ Count = count;
+ Fill();
+ value = Value;
+ count = Count;
+ }
+
+ if (value >= bigsplit)
+ {
+ range = range - split;
+ value = value - bigsplit;
+ {
+ int shift = Norm[range];
+ range <<= shift;
+ value <<= shift;
+ count -= shift;
+ }
+ return 1;
+ }
+ range = split;
+ {
+ int shift = Norm[range];
+ range <<= shift;
+ value <<= shift;
+ count -= shift;
+ }
+ return 0;
+ }
+
+ public ArrayPtr<byte> FindEnd()
+ {
+ // Find the end of the coded buffer
+ while (Count > 8 && Count < BdValueSize)
+ {
+ Count -= 8;
+ _buffer = _buffer.Slice(-1);
+ }
+ return _buffer;
+ }
+ }
+}