aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs')
-rw-r--r--Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs1190
1 files changed, 1190 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs b/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs
new file mode 100644
index 00000000..81c187e1
--- /dev/null
+++ b/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs
@@ -0,0 +1,1190 @@
+using Ryujinx.Common.Memory;
+using System;
+using System.Buffers.Binary;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Ryujinx.Graphics.Nvdec.Vp9.Common;
+using Ryujinx.Graphics.Nvdec.Vp9.Dsp;
+using Ryujinx.Graphics.Nvdec.Vp9.Types;
+using Ryujinx.Graphics.Video;
+using Mv = Ryujinx.Graphics.Nvdec.Vp9.Types.Mv;
+
+namespace Ryujinx.Graphics.Nvdec.Vp9
+{
+ static class DecodeFrame
+ {
+ private static bool ReadIsValid(ArrayPtr<byte> start, int len)
+ {
+ return len != 0 && len <= start.Length;
+ }
+
+ private static void InverseTransformBlockInter(ref MacroBlockD xd, int plane, TxSize txSize, Span<byte> dst, int stride, int eob)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ ArrayPtr<int> dqcoeff = pd.DqCoeff;
+ Debug.Assert(eob > 0);
+ if (xd.CurBuf.HighBd)
+ {
+ Span<ushort> dst16 = MemoryMarshal.Cast<byte, ushort>(dst);
+ if (xd.Lossless)
+ {
+ Idct.HighbdIwht4x4Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ }
+ else
+ {
+ switch (txSize)
+ {
+ case TxSize.Tx4x4:
+ Idct.HighbdIdct4x4Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx8x8:
+ Idct.HighbdIdct8x8Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx16x16:
+ Idct.HighbdIdct16x16Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx32x32:
+ Idct.HighbdIdct32x32Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ default: Debug.Assert(false, "Invalid transform size"); break;
+ }
+ }
+ }
+ else
+ {
+ if (xd.Lossless)
+ {
+ Idct.Iwht4x4Add(dqcoeff.ToSpan(), dst, stride, eob);
+ }
+ else
+ {
+ switch (txSize)
+ {
+ case TxSize.Tx4x4: Idct.Idct4x4Add(dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx8x8: Idct.Idct8x8Add(dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx16x16: Idct.Idct16x16Add(dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx32x32: Idct.Idct32x32Add(dqcoeff.ToSpan(), dst, stride, eob); break;
+ default: Debug.Assert(false, "Invalid transform size"); return;
+ }
+ }
+ }
+
+ if (eob == 1)
+ {
+ dqcoeff.ToSpan()[0] = 0;
+ }
+ else
+ {
+ if (txSize <= TxSize.Tx16x16 && eob <= 10)
+ {
+ dqcoeff.ToSpan().Slice(0, 4 * (4 << (int)txSize)).Fill(0);
+ }
+ else if (txSize == TxSize.Tx32x32 && eob <= 34)
+ {
+ dqcoeff.ToSpan().Slice(0, 256).Fill(0);
+ }
+ else
+ {
+ dqcoeff.ToSpan().Slice(0, 16 << ((int)txSize << 1)).Fill(0);
+ }
+ }
+ }
+
+ private static void InverseTransformBlockIntra(
+ ref MacroBlockD xd,
+ int plane,
+ TxType txType,
+ TxSize txSize,
+ Span<byte> dst,
+ int stride,
+ int eob)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ ArrayPtr<int> dqcoeff = pd.DqCoeff;
+ Debug.Assert(eob > 0);
+ if (xd.CurBuf.HighBd)
+ {
+ Span<ushort> dst16 = MemoryMarshal.Cast<byte, ushort>(dst);
+ if (xd.Lossless)
+ {
+ Idct.HighbdIwht4x4Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ }
+ else
+ {
+ switch (txSize)
+ {
+ case TxSize.Tx4x4:
+ Idct.HighbdIht4x4Add(txType, dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx8x8:
+ Idct.HighbdIht8x8Add(txType, dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx16x16:
+ Idct.HighbdIht16x16Add(txType, dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ case TxSize.Tx32x32:
+ Idct.HighbdIdct32x32Add(dqcoeff.ToSpan(), dst16, stride, eob, xd.Bd);
+ break;
+ default: Debug.Assert(false, "Invalid transform size"); break;
+ }
+ }
+ }
+ else
+ {
+ if (xd.Lossless)
+ {
+ Idct.Iwht4x4Add(dqcoeff.ToSpan(), dst, stride, eob);
+ }
+ else
+ {
+ switch (txSize)
+ {
+ case TxSize.Tx4x4: Idct.Iht4x4Add(txType, dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx8x8: Idct.Iht8x8Add(txType, dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx16x16: Idct.Iht16x16Add(txType, dqcoeff.ToSpan(), dst, stride, eob); break;
+ case TxSize.Tx32x32: Idct.Idct32x32Add(dqcoeff.ToSpan(), dst, stride, eob); break;
+ default: Debug.Assert(false, "Invalid transform size"); return;
+ }
+ }
+ }
+
+ if (eob == 1)
+ {
+ dqcoeff.ToSpan()[0] = 0;
+ }
+ else
+ {
+ if (txType == TxType.DctDct && txSize <= TxSize.Tx16x16 && eob <= 10)
+ {
+ dqcoeff.ToSpan().Slice(0, 4 * (4 << (int)txSize)).Fill(0);
+ }
+ else if (txSize == TxSize.Tx32x32 && eob <= 34)
+ {
+ dqcoeff.ToSpan().Slice(0, 256).Fill(0);
+ }
+ else
+ {
+ dqcoeff.ToSpan().Slice(0, 16 << ((int)txSize << 1)).Fill(0);
+ }
+ }
+ }
+
+ private static unsafe void PredictAndReconstructIntraBlock(
+ ref TileWorkerData twd,
+ ref ModeInfo mi,
+ int plane,
+ int row,
+ int col,
+ TxSize txSize)
+ {
+ ref MacroBlockD xd = ref twd.Xd;
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ PredictionMode mode = (plane == 0) ? mi.Mode : mi.UvMode;
+ int dstOffset = 4 * row * pd.Dst.Stride + 4 * col;
+ byte* dst = &pd.Dst.Buf.ToPointer()[dstOffset];
+ Span<byte> dstSpan = pd.Dst.Buf.ToSpan().Slice(dstOffset);
+
+ if (mi.SbType < BlockSize.Block8x8)
+ {
+ if (plane == 0)
+ {
+ mode = xd.Mi[0].Value.Bmi[(row << 1) + col].Mode;
+ }
+ }
+
+ ReconIntra.PredictIntraBlock(ref xd, pd.N4Wl, txSize, mode, dst, pd.Dst.Stride, dst, pd.Dst.Stride, col, row, plane);
+
+ if (mi.Skip == 0)
+ {
+ TxType txType =
+ (plane != 0 || xd.Lossless) ? TxType.DctDct : ReconIntra.IntraModeToTxTypeLookup[(int)mode];
+ var sc = (plane != 0 || xd.Lossless)
+ ? Luts.Vp9DefaultScanOrders[(int)txSize]
+ : Luts.Vp9ScanOrders[(int)txSize][(int)txType];
+ int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId);
+ if (eob > 0)
+ {
+ InverseTransformBlockIntra(ref xd, plane, txType, txSize, dstSpan, pd.Dst.Stride, eob);
+ }
+ }
+ }
+
+ private static int ReconstructInterBlock(
+ ref TileWorkerData twd,
+ ref ModeInfo mi,
+ int plane,
+ int row,
+ int col,
+ TxSize txSize)
+ {
+ ref MacroBlockD xd = ref twd.Xd;
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ var sc = Luts.Vp9DefaultScanOrders[(int)txSize];
+ int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId);
+ Span<byte> dst = pd.Dst.Buf.ToSpan().Slice(4 * row * pd.Dst.Stride + 4 * col);
+
+ if (eob > 0)
+ {
+ InverseTransformBlockInter(ref xd, plane, txSize, dst, pd.Dst.Stride, eob);
+ }
+ return eob;
+ }
+
+ private static unsafe void BuildMcBorder(
+ byte* src,
+ int srcStride,
+ byte* dst,
+ int dstStride,
+ int x,
+ int y,
+ int bW,
+ int bH,
+ int w,
+ int h)
+ {
+ // Get a pointer to the start of the real data for this row.
+ byte* refRow = src - x - y * srcStride;
+
+ if (y >= h)
+ {
+ refRow += (h - 1) * srcStride;
+ }
+ else if (y > 0)
+ {
+ refRow += y * srcStride;
+ }
+
+ do
+ {
+ int right = 0, copy;
+ int left = x < 0 ? -x : 0;
+
+ if (left > bW)
+ {
+ left = bW;
+ }
+
+ if (x + bW > w)
+ {
+ right = x + bW - w;
+ }
+
+ if (right > bW)
+ {
+ right = bW;
+ }
+
+ copy = bW - left - right;
+
+ if (left != 0)
+ {
+ MemoryUtil.Fill(dst, refRow[0], left);
+ }
+
+ if (copy != 0)
+ {
+ MemoryUtil.Copy(dst + left, refRow + x + left, copy);
+ }
+
+ if (right != 0)
+ {
+ MemoryUtil.Fill(dst + left + copy, refRow[w - 1], right);
+ }
+
+ dst += dstStride;
+ ++y;
+
+ if (y > 0 && y < h)
+ {
+ refRow += srcStride;
+ }
+ } while (--bH != 0);
+ }
+
+ private static unsafe void HighBuildMcBorder(
+ byte* src8,
+ int srcStride,
+ ushort* dst,
+ int dstStride,
+ int x,
+ int y,
+ int bW,
+ int bH,
+ int w,
+ int h)
+ {
+ // Get a pointer to the start of the real data for this row.
+ ushort* src = (ushort*)src8;
+ ushort* refRow = src - x - y * srcStride;
+
+ if (y >= h)
+ {
+ refRow += (h - 1) * srcStride;
+ }
+ else if (y > 0)
+ {
+ refRow += y * srcStride;
+ }
+
+ do
+ {
+ int right = 0, copy;
+ int left = x < 0 ? -x : 0;
+
+ if (left > bW)
+ {
+ left = bW;
+ }
+
+ if (x + bW > w)
+ {
+ right = x + bW - w;
+ }
+
+ if (right > bW)
+ {
+ right = bW;
+ }
+
+ copy = bW - left - right;
+
+ if (left != 0)
+ {
+ MemoryUtil.Fill(dst, refRow[0], left);
+ }
+
+ if (copy != 0)
+ {
+ MemoryUtil.Copy(dst + left, refRow + x + left, copy);
+ }
+
+ if (right != 0)
+ {
+ MemoryUtil.Fill(dst + left + copy, refRow[w - 1], right);
+ }
+
+ dst += dstStride;
+ ++y;
+
+ if (y > 0 && y < h)
+ {
+ refRow += srcStride;
+ }
+ } while (--bH != 0);
+ }
+
+ [StructLayout(LayoutKind.Sequential, Size = 80 * 2 * 80 * 2)]
+ struct McBufHigh
+ {
+ }
+
+ private static unsafe void ExtendAndPredict(
+ byte* bufPtr1,
+ int preBufStride,
+ int x0,
+ int y0,
+ int bW,
+ int bH,
+ int frameWidth,
+ int frameHeight,
+ int borderOffset,
+ byte* dst,
+ int dstBufStride,
+ int subpelX,
+ int subpelY,
+ Array8<short>[] kernel,
+ ref ScaleFactors sf,
+ ref MacroBlockD xd,
+ int w,
+ int h,
+ int refr,
+ int xs,
+ int ys)
+ {
+ McBufHigh mcBufHighStruct;
+ ushort* mcBufHigh = (ushort*)Unsafe.AsPointer(ref mcBufHighStruct); // Avoid zero initialization.
+ if (xd.CurBuf.HighBd)
+ {
+ HighBuildMcBorder(bufPtr1, preBufStride, mcBufHigh, bW, x0, y0, bW, bH, frameWidth, frameHeight);
+ ReconInter.HighbdInterPredictor(
+ mcBufHigh + borderOffset,
+ bW,
+ (ushort*)dst,
+ dstBufStride,
+ subpelX,
+ subpelY,
+ ref sf,
+ w,
+ h,
+ refr,
+ kernel,
+ xs,
+ ys,
+ xd.Bd);
+ }
+ else
+ {
+ BuildMcBorder(bufPtr1, preBufStride, (byte*)mcBufHigh, bW, x0, y0, bW, bH, frameWidth, frameHeight);
+ ReconInter.InterPredictor(
+ (byte*)mcBufHigh + borderOffset,
+ bW,
+ dst,
+ dstBufStride,
+ subpelX,
+ subpelY,
+ ref sf,
+ w,
+ h,
+ refr,
+ kernel,
+ xs,
+ ys);
+ }
+ }
+
+ private static unsafe void DecBuildInterPredictors(
+ ref MacroBlockD xd,
+ int plane,
+ int bw,
+ int bh,
+ int x,
+ int y,
+ int w,
+ int h,
+ int miX,
+ int miY,
+ Array8<short>[] kernel,
+ ref ScaleFactors sf,
+ ref Buf2D preBuf,
+ ref Buf2D dstBuf,
+ ref Mv mv,
+ ref Surface refFrameBuf,
+ bool isScaled,
+ int refr)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ byte* dst = dstBuf.Buf.ToPointer() + dstBuf.Stride * y + x;
+ Mv32 scaledMv;
+ int xs, ys, x0, y0, x0_16, y0_16, frameWidth, frameHeight, bufStride, subpelX, subpelY;
+ byte* refFrame;
+ byte* bufPtr;
+
+ // Get reference frame pointer, width and height.
+ if (plane == 0)
+ {
+ frameWidth = refFrameBuf.Width;
+ frameHeight = refFrameBuf.Height;
+ refFrame = refFrameBuf.YBuffer.ToPointer();
+ }
+ else
+ {
+ frameWidth = refFrameBuf.UvWidth;
+ frameHeight = refFrameBuf.UvHeight;
+ refFrame = plane == 1 ? refFrameBuf.UBuffer.ToPointer() : refFrameBuf.VBuffer.ToPointer();
+ }
+
+ if (isScaled)
+ {
+ Mv mvQ4 = ReconInter.ClampMvToUmvBorderSb(ref xd, ref mv, bw, bh, pd.SubsamplingX, pd.SubsamplingY);
+ // Co-ordinate of containing block to pixel precision.
+ int xStart = (-xd.MbToLeftEdge >> (3 + pd.SubsamplingX));
+ int yStart = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY));
+ // Co-ordinate of the block to 1/16th pixel precision.
+ x0_16 = (xStart + x) << Filter.SubpelBits;
+ y0_16 = (yStart + y) << Filter.SubpelBits;
+
+ // Co-ordinate of current block in reference frame
+ // to 1/16th pixel precision.
+ x0_16 = sf.ScaleValueX(x0_16);
+ y0_16 = sf.ScaleValueY(y0_16);
+
+ // Map the top left corner of the block into the reference frame.
+ x0 = sf.ScaleValueX(xStart + x);
+ y0 = sf.ScaleValueY(yStart + y);
+
+ // Scale the MV and incorporate the sub-pixel offset of the block
+ // in the reference frame.
+ scaledMv = sf.ScaleMv(ref mvQ4, miX + x, miY + y);
+ xs = sf.XStepQ4;
+ ys = sf.YStepQ4;
+ }
+ else
+ {
+ // Co-ordinate of containing block to pixel precision.
+ x0 = (-xd.MbToLeftEdge >> (3 + pd.SubsamplingX)) + x;
+ y0 = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY)) + y;
+
+ // Co-ordinate of the block to 1/16th pixel precision.
+ x0_16 = x0 << Filter.SubpelBits;
+ y0_16 = y0 << Filter.SubpelBits;
+
+ scaledMv.Row = mv.Row * (1 << (1 - pd.SubsamplingY));
+ scaledMv.Col = mv.Col * (1 << (1 - pd.SubsamplingX));
+ xs = ys = 16;
+ }
+ subpelX = scaledMv.Col & Filter.SubpelMask;
+ subpelY = scaledMv.Row & Filter.SubpelMask;
+
+ // Calculate the top left corner of the best matching block in the
+ // reference frame.
+ x0 += scaledMv.Col >> Filter.SubpelBits;
+ y0 += scaledMv.Row >> Filter.SubpelBits;
+ x0_16 += scaledMv.Col;
+ y0_16 += scaledMv.Row;
+
+ // Get reference block pointer.
+ bufPtr = refFrame + y0 * preBuf.Stride + x0;
+ bufStride = preBuf.Stride;
+
+ // Do border extension if there is motion or the
+ // width/height is not a multiple of 8 pixels.
+ if (isScaled || scaledMv.Col != 0 || scaledMv.Row != 0 || (frameWidth & 0x7) != 0 || (frameHeight & 0x7) != 0)
+ {
+ int y1 = ((y0_16 + (h - 1) * ys) >> Filter.SubpelBits) + 1;
+
+ // Get reference block bottom right horizontal coordinate.
+ int x1 = ((x0_16 + (w - 1) * xs) >> Filter.SubpelBits) + 1;
+ int xPad = 0, yPad = 0;
+
+ if (subpelX != 0 || (sf.XStepQ4 != Filter.SubpelShifts))
+ {
+ x0 -= Constants.Vp9InterpExtend - 1;
+ x1 += Constants.Vp9InterpExtend;
+ xPad = 1;
+ }
+
+ if (subpelY != 0 || (sf.YStepQ4 != Filter.SubpelShifts))
+ {
+ y0 -= Constants.Vp9InterpExtend - 1;
+ y1 += Constants.Vp9InterpExtend;
+ yPad = 1;
+ }
+
+ // Skip border extension if block is inside the frame.
+ if (x0 < 0 || x0 > frameWidth - 1 || x1 < 0 || x1 > frameWidth - 1 ||
+ y0 < 0 || y0 > frameHeight - 1 || y1 < 0 || y1 > frameHeight - 1)
+ {
+ // Extend the border.
+ byte* bufPtr1 = refFrame + y0 * bufStride + x0;
+ int bW = x1 - x0 + 1;
+ int bH = y1 - y0 + 1;
+ int borderOffset = yPad * 3 * bW + xPad * 3;
+
+ ExtendAndPredict(
+ bufPtr1,
+ bufStride,
+ x0,
+ y0,
+ bW,
+ bH,
+ frameWidth,
+ frameHeight,
+ borderOffset,
+ dst,
+ dstBuf.Stride,
+ subpelX,
+ subpelY,
+ kernel,
+ ref sf,
+ ref xd,
+ w,
+ h,
+ refr,
+ xs,
+ ys);
+ return;
+ }
+ }
+ if (xd.CurBuf.HighBd)
+ {
+ ReconInter.HighbdInterPredictor(
+ (ushort*)bufPtr,
+ bufStride,
+ (ushort*)dst,
+ dstBuf.Stride,
+ subpelX,
+ subpelY,
+ ref sf,
+ w,
+ h,
+ refr,
+ kernel,
+ xs,
+ ys,
+ xd.Bd);
+ }
+ else
+ {
+ ReconInter.InterPredictor(
+ bufPtr,
+ bufStride,
+ dst,
+ dstBuf.Stride,
+ subpelX,
+ subpelY,
+ ref sf,
+ w,
+ h,
+ refr,
+ kernel,
+ xs,
+ ys);
+ }
+ }
+
+ private static void DecBuildInterPredictorsSb(ref Vp9Common cm, ref MacroBlockD xd, int miRow, int miCol)
+ {
+ int plane;
+ int miX = miCol * Constants.MiSize;
+ int miY = miRow * Constants.MiSize;
+ ref ModeInfo mi = ref xd.Mi[0].Value;
+ Array8<short>[] kernel = Luts.Vp9FilterKernels[mi.InterpFilter];
+ BlockSize sbType = mi.SbType;
+ int isCompound = mi.HasSecondRef() ? 1 : 0;
+ int refr;
+ bool isScaled;
+
+ for (refr = 0; refr < 1 + isCompound; ++refr)
+ {
+ int frame = mi.RefFrame[refr];
+ ref RefBuffer refBuf = ref cm.FrameRefs[frame - Constants.LastFrame];
+ ref ScaleFactors sf = ref refBuf.Sf;
+ ref Surface refFrameBuf = ref refBuf.Buf;
+
+ if (!sf.IsValidScale())
+ {
+ xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Reference frame has invalid dimensions");
+ }
+
+ isScaled = sf.IsScaled();
+ ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, isScaled ? new Ptr<ScaleFactors>(ref sf) : Ptr<ScaleFactors>.Null);
+ xd.BlockRefs[refr] = new Ptr<RefBuffer>(ref refBuf);
+
+ if (sbType < BlockSize.Block8x8)
+ {
+ for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ ref Buf2D dstBuf = ref pd.Dst;
+ int num4x4W = pd.N4W;
+ int num4x4H = pd.N4H;
+ int n4Wx4 = 4 * num4x4W;
+ int n4Hx4 = 4 * num4x4H;
+ ref Buf2D preBuf = ref pd.Pre[refr];
+ int i = 0, x, y;
+ for (y = 0; y < num4x4H; ++y)
+ {
+ for (x = 0; x < num4x4W; ++x)
+ {
+ Mv mv = ReconInter.AverageSplitMvs(ref pd, ref mi, refr, i++);
+ DecBuildInterPredictors(
+ ref xd,
+ plane,
+ n4Wx4,
+ n4Hx4,
+ 4 * x,
+ 4 * y,
+ 4,
+ 4,
+ miX,
+ miY,
+ kernel,
+ ref sf,
+ ref preBuf,
+ ref dstBuf,
+ ref mv,
+ ref refFrameBuf,
+ isScaled,
+ refr);
+ }
+ }
+ }
+ }
+ else
+ {
+ Mv mv = mi.Mv[refr];
+ for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ ref Buf2D dstBuf = ref pd.Dst;
+ int num4x4W = pd.N4W;
+ int num4x4H = pd.N4H;
+ int n4Wx4 = 4 * num4x4W;
+ int n4Hx4 = 4 * num4x4H;
+ ref Buf2D preBuf = ref pd.Pre[refr];
+ DecBuildInterPredictors(
+ ref xd,
+ plane,
+ n4Wx4,
+ n4Hx4,
+ 0,
+ 0,
+ n4Wx4,
+ n4Hx4,
+ miX,
+ miY,
+ kernel,
+ ref sf,
+ ref preBuf,
+ ref dstBuf,
+ ref mv,
+ ref refFrameBuf,
+ isScaled,
+ refr);
+ }
+ }
+ }
+ }
+
+ private static unsafe void DecResetSkipContext(ref MacroBlockD xd)
+ {
+ int i;
+ for (i = 0; i < Constants.MaxMbPlane; i++)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[i];
+ MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W);
+ MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H);
+ }
+ }
+
+ private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl)
+ {
+ int i;
+ for (i = 0; i < Constants.MaxMbPlane; i++)
+ {
+ xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX);
+ xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY);
+ xd.Plane[i].N4Wl = (byte)(bwl - xd.Plane[i].SubsamplingX);
+ xd.Plane[i].N4Hl = (byte)(bhl - xd.Plane[i].SubsamplingY);
+ }
+ }
+
+ private static ref ModeInfo SetOffsets(
+ ref Vp9Common cm,
+ ref MacroBlockD xd,
+ BlockSize bsize,
+ int miRow,
+ int miCol,
+ int bw,
+ int bh,
+ int xMis,
+ int yMis,
+ int bwl,
+ int bhl)
+ {
+ int offset = miRow * cm.MiStride + miCol;
+ int x, y;
+ ref TileInfo tile = ref xd.Tile;
+
+ xd.Mi = cm.MiGridVisible.Slice(offset);
+ xd.Mi[0] = new Ptr<ModeInfo>(ref cm.Mi[offset]);
+ xd.Mi[0].Value.SbType = bsize;
+ for (y = 0; y < yMis; ++y)
+ {
+ for (x = y == 0 ? 1 : 0; x < xMis; ++x)
+ {
+ xd.Mi[y * cm.MiStride + x] = xd.Mi[0];
+ }
+ }
+
+ SetPlaneN4(ref xd, bw, bh, bwl, bhl);
+
+ xd.SetSkipContext(miRow, miCol);
+
+ // Distance of Mb to the various image edges. These are specified to 8th pel
+ // as they are always compared to values that are in 1/8th pel units
+ xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols);
+
+ ReconInter.SetupDstPlanes(ref xd.Plane, ref xd.CurBuf, miRow, miCol);
+ return ref xd.Mi[0].Value;
+ }
+
+ private static void DecodeBlock(
+ ref TileWorkerData twd,
+ ref Vp9Common cm,
+ int miRow,
+ int miCol,
+ BlockSize bsize,
+ int bwl,
+ int bhl)
+ {
+ bool less8x8 = bsize < BlockSize.Block8x8;
+ int bw = 1 << (bwl - 1);
+ int bh = 1 << (bhl - 1);
+ int xMis = Math.Min(bw, cm.MiCols - miCol);
+ int yMis = Math.Min(bh, cm.MiRows - miRow);
+ ref Reader r = ref twd.BitReader;
+ ref MacroBlockD xd = ref twd.Xd;
+
+ ref ModeInfo mi = ref SetOffsets(ref cm, ref xd, bsize, miRow, miCol, bw, bh, xMis, yMis, bwl, bhl);
+
+ if (bsize >= BlockSize.Block8x8 && (cm.SubsamplingX != 0 || cm.SubsamplingY != 0))
+ {
+ BlockSize uvSubsize = Luts.SsSizeLookup[(int)bsize][cm.SubsamplingX][cm.SubsamplingY];
+ if (uvSubsize == BlockSize.BlockInvalid)
+ {
+ xd.ErrorInfo.Value.InternalError(CodecErr.CodecCorruptFrame, "Invalid block size.");
+ }
+ }
+
+ DecodeMv.ReadModeInfo(ref twd, ref cm, miRow, miCol, xMis, yMis);
+
+ if (mi.Skip != 0)
+ {
+ DecResetSkipContext(ref xd);
+ }
+
+ if (!mi.IsInterBlock())
+ {
+ int plane;
+ for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize;
+ int num4x4W = pd.N4W;
+ int num4x4H = pd.N4H;
+ int step = 1 << (int)txSize;
+ int row, col;
+ int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX));
+ int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY));
+
+ xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide);
+ xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh);
+
+ for (row = 0; row < maxBlocksHigh; row += step)
+ {
+ for (col = 0; col < maxBlocksWide; col += step)
+ {
+ PredictAndReconstructIntraBlock(ref twd, ref mi, plane, row, col, txSize);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Prediction
+ DecBuildInterPredictorsSb(ref cm, ref xd, miRow, miCol);
+
+ // Reconstruction
+ if (mi.Skip == 0)
+ {
+ int eobtotal = 0;
+ int plane;
+
+ for (plane = 0; plane < Constants.MaxMbPlane; ++plane)
+ {
+ ref MacroBlockDPlane pd = ref xd.Plane[plane];
+ TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize;
+ int num4x4W = pd.N4W;
+ int num4x4H = pd.N4H;
+ int step = 1 << (int)txSize;
+ int row, col;
+ int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX));
+ int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY));
+
+ xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide);
+ xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh);
+
+ for (row = 0; row < maxBlocksHigh; row += step)
+ {
+ for (col = 0; col < maxBlocksWide; col += step)
+ {
+ eobtotal += ReconstructInterBlock(ref twd, ref mi, plane, row, col, txSize);
+ }
+ }
+ }
+
+ if (!less8x8 && eobtotal == 0)
+ {
+ mi.Skip = 1; // Skip loopfilter
+ }
+ }
+ }
+
+ xd.Corrupted |= r.HasError();
+
+ if (cm.Lf.FilterLevel != 0)
+ {
+ LoopFilter.BuildMask(ref cm, ref mi, miRow, miCol, bw, bh);
+ }
+ }
+
+ private static int DecPartitionPlaneContext(ref TileWorkerData twd, int miRow, int miCol, int bsl)
+ {
+ ref sbyte aboveCtx = ref twd.Xd.AboveSegContext[miCol];
+ ref sbyte leftCtx = ref twd.Xd.LeftSegContext[miRow & Constants.MiMask];
+ int above = (aboveCtx >> bsl) & 1, left = (leftCtx >> bsl) & 1;
+
+ return (left * 2 + above) + bsl * Constants.PartitionPloffset;
+ }
+
+ private static void DecUpdatePartitionContext(
+ ref TileWorkerData twd,
+ int miRow,
+ int miCol,
+ BlockSize subsize,
+ int bw)
+ {
+ Span<sbyte> aboveCtx = twd.Xd.AboveSegContext.Slice(miCol).ToSpan();
+ Span<sbyte> leftCtx = MemoryMarshal.CreateSpan(ref twd.Xd.LeftSegContext[miRow & Constants.MiMask], 8 - (miRow & Constants.MiMask));
+
+ // Update the partition context at the end notes. Set partition bits
+ // of block sizes larger than the current one to be one, and partition
+ // bits of smaller block sizes to be zero.
+ aboveCtx.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Above);
+ leftCtx.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Left);
+ }
+
+ private static PartitionType ReadPartition(
+ ref TileWorkerData twd,
+ int miRow,
+ int miCol,
+ int hasRows,
+ int hasCols,
+ int bsl)
+ {
+ int ctx = DecPartitionPlaneContext(ref twd, miRow, miCol, bsl);
+ ReadOnlySpan<byte> probs = MemoryMarshal.CreateReadOnlySpan(ref twd.Xd.PartitionProbs[ctx][0], 3);
+ PartitionType p;
+ ref Reader r = ref twd.BitReader;
+
+ if (hasRows != 0 && hasCols != 0)
+ {
+ p = (PartitionType)r.ReadTree(Luts.Vp9PartitionTree, probs);
+ }
+ else if (hasRows == 0 && hasCols != 0)
+ {
+ p = r.Read(probs[1]) != 0 ? PartitionType.PartitionSplit : PartitionType.PartitionHorz;
+ }
+ else if (hasRows != 0 && hasCols == 0)
+ {
+ p = r.Read(probs[2]) != 0 ? PartitionType.PartitionSplit : PartitionType.PartitionVert;
+ }
+ else
+ {
+ p = PartitionType.PartitionSplit;
+ }
+
+ if (!twd.Xd.Counts.IsNull)
+ {
+ ++twd.Xd.Counts.Value.Partition[ctx][(int)p];
+ }
+
+ return p;
+ }
+
+ private static void DecodePartition(
+ ref TileWorkerData twd,
+ ref Vp9Common cm,
+ int miRow,
+ int miCol,
+ BlockSize bsize,
+ int n4x4L2)
+ {
+ int n8x8L2 = n4x4L2 - 1;
+ int num8x8Wh = 1 << n8x8L2;
+ int hbs = num8x8Wh >> 1;
+ PartitionType partition;
+ BlockSize subsize;
+ bool hasRows = (miRow + hbs) < cm.MiRows;
+ bool hasCols = (miCol + hbs) < cm.MiCols;
+ ref MacroBlockD xd = ref twd.Xd;
+
+ if (miRow >= cm.MiRows || miCol >= cm.MiCols)
+ {
+ return;
+ }
+
+ partition = ReadPartition(ref twd, miRow, miCol, hasRows ? 1 : 0, hasCols ? 1 : 0, n8x8L2);
+ subsize = Luts.SubsizeLookup[(int)partition][(int)bsize];
+ if (hbs == 0)
+ {
+ // Calculate bmode block dimensions (log 2)
+ xd.BmodeBlocksWl = (byte)(1 >> ((partition & PartitionType.PartitionVert) != 0 ? 1 : 0));
+ xd.BmodeBlocksHl = (byte)(1 >> ((partition & PartitionType.PartitionHorz) != 0 ? 1 : 0));
+ DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, 1, 1);
+ }
+ else
+ {
+ switch (partition)
+ {
+ case PartitionType.PartitionNone:
+ DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n4x4L2);
+ break;
+ case PartitionType.PartitionHorz:
+ DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n8x8L2);
+ if (hasRows)
+ {
+ DecodeBlock(ref twd, ref cm, miRow + hbs, miCol, subsize, n4x4L2, n8x8L2);
+ }
+
+ break;
+ case PartitionType.PartitionVert:
+ DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n8x8L2, n4x4L2);
+ if (hasCols)
+ {
+ DecodeBlock(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2, n4x4L2);
+ }
+
+ break;
+ case PartitionType.PartitionSplit:
+ DecodePartition(ref twd, ref cm, miRow, miCol, subsize, n8x8L2);
+ DecodePartition(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2);
+ DecodePartition(ref twd, ref cm, miRow + hbs, miCol, subsize, n8x8L2);
+ DecodePartition(ref twd, ref cm, miRow + hbs, miCol + hbs, subsize, n8x8L2);
+ break;
+ default: Debug.Assert(false, "Invalid partition type"); break;
+ }
+ }
+
+ // Update partition context
+ if (bsize >= BlockSize.Block8x8 && (bsize == BlockSize.Block8x8 || partition != PartitionType.PartitionSplit))
+ {
+ DecUpdatePartitionContext(ref twd, miRow, miCol, subsize, num8x8Wh);
+ }
+ }
+
+ private static void SetupTokenDecoder(
+ ArrayPtr<byte> data,
+ int readSize,
+ ref InternalErrorInfo errorInfo,
+ ref Reader r)
+ {
+ // Validate the calculated partition length. If the buffer described by the
+ // partition can't be fully read then throw an error.
+ if (!ReadIsValid(data, readSize))
+ {
+ errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length");
+ }
+
+ if (r.Init(data, readSize))
+ {
+ errorInfo.InternalError(CodecErr.CodecMemError, "Failed to allocate bool decoder 1");
+ }
+ }
+
+ // Reads the next tile returning its size and adjusting '*data' accordingly
+ // based on 'isLast'.
+ private static void GetTileBuffer(
+ bool isLast,
+ ref InternalErrorInfo errorInfo,
+ ref ArrayPtr<byte> data,
+ ref TileBuffer buf)
+ {
+ int size;
+
+ if (!isLast)
+ {
+ if (!ReadIsValid(data, 4))
+ {
+ errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length");
+ }
+
+ size = BinaryPrimitives.ReadInt32BigEndian(data.ToSpan());
+ data = data.Slice(4);
+
+ if (size > data.Length)
+ {
+ errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile size");
+ }
+ }
+ else
+ {
+ size = data.Length;
+ }
+
+ buf.Data = data;
+ buf.Size = size;
+
+ data = data.Slice(size);
+ }
+
+ private static void GetTileBuffers(
+ ref Vp9Common cm,
+ ArrayPtr<byte> data,
+ int tileCols,
+ int tileRows,
+ ref Array4<Array64<TileBuffer>> tileBuffers)
+ {
+ int r, c;
+
+ for (r = 0; r < tileRows; ++r)
+ {
+ for (c = 0; c < tileCols; ++c)
+ {
+ bool isLast = (r == tileRows - 1) && (c == tileCols - 1);
+ ref TileBuffer buf = ref tileBuffers[r][c];
+ GetTileBuffer(isLast, ref cm.Error, ref data, ref buf);
+ }
+ }
+ }
+
+ public static unsafe ArrayPtr<byte> DecodeTiles(ref Vp9Common cm, ArrayPtr<byte> data)
+ {
+ int alignedCols = TileInfo.MiColsAlignedToSb(cm.MiCols);
+ int tileCols = 1 << cm.Log2TileCols;
+ int tileRows = 1 << cm.Log2TileRows;
+ Array4<Array64<TileBuffer>> tileBuffers = new Array4<Array64<TileBuffer>>();
+ int tileRow, tileCol;
+ int miRow, miCol;
+
+ Debug.Assert(tileRows <= 4);
+ Debug.Assert(tileCols <= (1 << 6));
+
+ // Note: this memset assumes above_context[0], [1] and [2]
+ // are allocated as part of the same buffer.
+ MemoryUtil.Fill(cm.AboveContext.ToPointer(), (sbyte)0, Constants.MaxMbPlane * 2 * alignedCols);
+ MemoryUtil.Fill(cm.AboveSegContext.ToPointer(), (sbyte)0, alignedCols);
+
+ LoopFilter.ResetLfm(ref cm);
+
+ GetTileBuffers(ref cm, data, tileCols, tileRows, ref tileBuffers);
+ // Load all tile information into tile_data.
+ for (tileRow = 0; tileRow < tileRows; ++tileRow)
+ {
+ for (tileCol = 0; tileCol < tileCols; ++tileCol)
+ {
+ ref TileBuffer buf = ref tileBuffers[tileRow][tileCol];
+ ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + tileCol];
+ tileData.Xd = cm.Mb;
+ tileData.Xd.Corrupted = false;
+ tileData.Xd.Counts = cm.FrameParallelDecodingMode ? Ptr<Vp9BackwardUpdates>.Null : cm.Counts;
+ tileData.Dqcoeff = new Array32<Array32<int>>();
+ tileData.Xd.Tile.Init(ref cm, tileRow, tileCol);
+ SetupTokenDecoder(buf.Data, buf.Size, ref cm.Error, ref tileData.BitReader);
+ cm.InitMacroBlockD(ref tileData.Xd, new ArrayPtr<int>(ref tileData.Dqcoeff[0][0], 32 * 32));
+ }
+ }
+
+ for (tileRow = 0; tileRow < tileRows; ++tileRow)
+ {
+ TileInfo tile = new TileInfo();
+ tile.SetRow(ref cm, tileRow);
+ for (miRow = tile.MiRowStart; miRow < tile.MiRowEnd; miRow += Constants.MiBlockSize)
+ {
+ for (tileCol = 0; tileCol < tileCols; ++tileCol)
+ {
+ int col = tileCol;
+ ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + col];
+ tile.SetCol(ref cm, col);
+ tileData.Xd.LeftContext = new Array3<Array16<sbyte>>();
+ tileData.Xd.LeftSegContext = new Array8<sbyte>();
+ for (miCol = tile.MiColStart; miCol < tile.MiColEnd; miCol += Constants.MiBlockSize)
+ {
+ DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64x64, 4);
+ }
+ cm.Mb.Corrupted |= tileData.Xd.Corrupted;
+ if (cm.Mb.Corrupted)
+ {
+ cm.Error.InternalError(CodecErr.CodecCorruptFrame, "Failed to decode tile data");
+ };
+ }
+ }
+ }
+
+ // Get last tile data.
+ return cm.TileWorkerData[tileCols * tileRows - 1].BitReader.FindEnd();
+ }
+ }
+}