DecodeMv.cs
   1  using Ryujinx.Common.Memory;
   2  using Ryujinx.Graphics.Nvdec.Vp9.Dsp;
   3  using Ryujinx.Graphics.Nvdec.Vp9.Types;
   4  using Ryujinx.Graphics.Video;
   5  using System;
   6  using System.Diagnostics;
   7  using System.Runtime.CompilerServices;
   8  
   9  namespace Ryujinx.Graphics.Nvdec.Vp9
  10  {
  11      internal static class DecodeMv
  12      {
  13          private const int MvrefNeighbours = 8;
  14  
  15          private static PredictionMode ReadIntraMode(ref Reader r, ReadOnlySpan<byte> p)
  16          {
  17              return (PredictionMode)r.ReadTree(Luts.Vp9IntraModeTree, p);
  18          }
  19  
  20          private static PredictionMode ReadIntraModeY(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r, int sizeGroup)
  21          {
  22              PredictionMode yMode = ReadIntraMode(ref r, cm.Fc.Value.YModeProb[sizeGroup].AsSpan());
  23              if (!xd.Counts.IsNull)
  24              {
  25                  ++xd.Counts.Value.YMode[sizeGroup][(int)yMode];
  26              }
  27  
  28              return yMode;
  29          }
  30  
  31          private static PredictionMode ReadIntraModeUv(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r, byte yMode)
  32          {
  33              PredictionMode uvMode = ReadIntraMode(ref r, cm.Fc.Value.UvModeProb[yMode].AsSpan());
  34              if (!xd.Counts.IsNull)
  35              {
  36                  ++xd.Counts.Value.UvMode[yMode][(int)uvMode];
  37              }
  38  
  39              return uvMode;
  40          }
  41  
  42          private static PredictionMode ReadInterMode(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r, int ctx)
  43          {
  44              int mode = r.ReadTree(Luts.Vp9InterModeTree, cm.Fc.Value.InterModeProb[ctx].AsSpan());
  45              if (!xd.Counts.IsNull)
  46              {
  47                  ++xd.Counts.Value.InterMode[ctx][mode];
  48              }
  49  
  50              return PredictionMode.NearestMv + mode;
  51          }
  52  
  53          private static int ReadSegmentId(ref Reader r, ref Array7<byte> segTreeProbs)
  54          {
  55              return r.ReadTree(Luts.Vp9SegmentTree, segTreeProbs.AsSpan());
  56          }
  57  
  58          private static ReadOnlySpan<byte> GetTxProbs(ref Vp9EntropyProbs fc, TxSize maxTxSize, int ctx)
  59          {
  60              switch (maxTxSize)
  61              {
  62                  case TxSize.Tx8x8:
  63                      return fc.Tx8x8Prob[ctx].AsSpan();
  64                  case TxSize.Tx16x16:
  65                      return fc.Tx16x16Prob[ctx].AsSpan();
  66                  case TxSize.Tx32x32:
  67                      return fc.Tx32x32Prob[ctx].AsSpan();
  68                  default:
  69                      Debug.Assert(false, "Invalid maxTxSize.");
  70  
  71                      return ReadOnlySpan<byte>.Empty;
  72              }
  73          }
  74  
  75          private static Span<uint> GetTxCounts(ref Vp9BackwardUpdates counts, TxSize maxTxSize, int ctx)
  76          {
  77              switch (maxTxSize)
  78              {
  79                  case TxSize.Tx8x8:
  80                      return counts.Tx8x8[ctx].AsSpan();
  81                  case TxSize.Tx16x16:
  82                      return counts.Tx16x16[ctx].AsSpan();
  83                  case TxSize.Tx32x32:
  84                      return counts.Tx32x32[ctx].AsSpan();
  85                  default:
  86                      Debug.Assert(false, "Invalid maxTxSize.");
  87  
  88                      return Span<uint>.Empty;
  89              }
  90          }
  91  
  92          private static TxSize ReadSelectedTxSize(ref Vp9Common cm, ref MacroBlockD xd, TxSize maxTxSize, ref Reader r)
  93          {
  94              int ctx = xd.GetTxSizeContext();
  95              ReadOnlySpan<byte> txProbs = GetTxProbs(ref cm.Fc.Value, maxTxSize, ctx);
  96              TxSize txSize = (TxSize)r.Read(txProbs[0]);
  97              if (txSize != TxSize.Tx4x4 && maxTxSize >= TxSize.Tx16x16)
  98              {
  99                  txSize += r.Read(txProbs[1]);
 100                  if (txSize != TxSize.Tx8x8 && maxTxSize >= TxSize.Tx32x32)
 101                  {
 102                      txSize += r.Read(txProbs[2]);
 103                  }
 104              }
 105  
 106              if (!xd.Counts.IsNull)
 107              {
 108                  ++GetTxCounts(ref xd.Counts.Value, maxTxSize, ctx)[(int)txSize];
 109              }
 110  
 111              return txSize;
 112          }
 113  
 114          private static TxSize ReadTxSize(ref Vp9Common cm, ref MacroBlockD xd, bool allowSelect, ref Reader r)
 115          {
 116              TxMode txMode = cm.TxMode;
 117              BlockSize bsize = xd.Mi[0].Value.SbType;
 118              TxSize maxTxSize = Luts.MaxTxSizeLookup[(int)bsize];
 119              if (allowSelect && txMode == TxMode.TxModeSelect && bsize >= BlockSize.Block8x8)
 120              {
 121                  return ReadSelectedTxSize(ref cm, ref xd, maxTxSize, ref r);
 122              }
 123  
 124              return (TxSize)Math.Min((int)maxTxSize, (int)Luts.TxModeToBiggestTxSize[(int)txMode]);
 125          }
 126  
 127          private static int DecGetSegmentId(ref Vp9Common cm, ArrayPtr<byte> segmentIds, int miOffset, int xMis, int yMis)
 128          {
 129              int x, y, segmentId = int.MaxValue;
 130  
 131              for (y = 0; y < yMis; y++)
 132              {
 133                  for (x = 0; x < xMis; x++)
 134                  {
 135                      segmentId = Math.Min(segmentId, segmentIds[miOffset + y * cm.MiCols + x]);
 136                  }
 137              }
 138  
 139              Debug.Assert(segmentId >= 0 && segmentId < Constants.MaxSegments);
 140  
 141              return segmentId;
 142          }
 143  
 144          private static void SetSegmentId(ref Vp9Common cm, int miOffset, int xMis, int yMis, int segmentId)
 145          {
 146              int x, y;
 147  
 148              Debug.Assert(segmentId >= 0 && segmentId < Constants.MaxSegments);
 149  
 150              for (y = 0; y < yMis; y++)
 151              {
 152                  for (x = 0; x < xMis; x++)
 153                  {
 154                      cm.CurrentFrameSegMap[miOffset + y * cm.MiCols + x] = (byte)segmentId;
 155                  }
 156              }
 157          }
 158  
 159          private static void CopySegmentId(
 160              ref Vp9Common cm,
 161              ArrayPtr<byte> lastSegmentIds,
 162              ArrayPtr<byte> currentSegmentIds,
 163              int miOffset,
 164              int xMis,
 165              int yMis)
 166          {
 167              int x, y;
 168  
 169              for (y = 0; y < yMis; y++)
 170              {
 171                  for (x = 0; x < xMis; x++)
 172                  {
 173                      currentSegmentIds[miOffset + y * cm.MiCols + x] = (byte)(!lastSegmentIds.IsNull ? lastSegmentIds[miOffset + y * cm.MiCols + x] : 0);
 174                  }
 175              }
 176          }
 177  
 178          private static int ReadIntraSegmentId(ref Vp9Common cm, int miOffset, int xMis, int yMis, ref Reader r)
 179          {
 180              ref Segmentation seg = ref cm.Seg;
 181              int segmentId;
 182  
 183              if (!seg.Enabled)
 184              {
 185                  return 0; // Default for disabled segmentation
 186              }
 187  
 188              if (!seg.UpdateMap)
 189              {
 190                  CopySegmentId(ref cm, cm.LastFrameSegMap, cm.CurrentFrameSegMap, miOffset, xMis, yMis);
 191  
 192                  return 0;
 193              }
 194  
 195              segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
 196              SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId);
 197  
 198              return segmentId;
 199          }
 200  
 201          private static int ReadInterSegmentId(
 202              ref Vp9Common cm,
 203              ref MacroBlockD xd,
 204              int miRow,
 205              int miCol,
 206              ref Reader r,
 207              int xMis,
 208              int yMis)
 209          {
 210              ref Segmentation seg = ref cm.Seg;
 211              ref ModeInfo mi = ref xd.Mi[0].Value;
 212              int predictedSegmentId, segmentId;
 213              int miOffset = miRow * cm.MiCols + miCol;
 214  
 215              if (!seg.Enabled)
 216              {
 217                  return 0; // Default for disabled segmentation
 218              }
 219  
 220              predictedSegmentId = !cm.LastFrameSegMap.IsNull
 221                  ? DecGetSegmentId(ref cm, cm.LastFrameSegMap, miOffset, xMis, yMis)
 222                  : 0;
 223  
 224              if (!seg.UpdateMap)
 225              {
 226                  CopySegmentId(ref cm, cm.LastFrameSegMap, cm.CurrentFrameSegMap, miOffset, xMis, yMis);
 227  
 228                  return predictedSegmentId;
 229              }
 230  
 231              if (seg.TemporalUpdate)
 232              {
 233                  byte predProb = Segmentation.GetPredProbSegId(ref cm.Fc.Value.SegPredProb, ref xd);
 234                  mi.SegIdPredicted = (sbyte)r.Read(predProb);
 235                  segmentId = mi.SegIdPredicted != 0 ? predictedSegmentId : ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
 236              }
 237              else
 238              {
 239                  segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb);
 240              }
 241              SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId);
 242  
 243              return segmentId;
 244          }
 245  
 246          private static int ReadSkip(ref Vp9Common cm, ref MacroBlockD xd, int segmentId, ref Reader r)
 247          {
 248              if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlSkip) != 0)
 249              {
 250                  return 1;
 251              }
 252  
 253              int ctx = xd.GetSkipContext();
 254              int skip = r.Read(cm.Fc.Value.SkipProb[ctx]);
 255              if (!xd.Counts.IsNull)
 256              {
 257                  ++xd.Counts.Value.Skip[ctx][skip];
 258              }
 259  
 260              return skip;
 261          }
 262  
 263          private static int ReadMvComponent(ref Reader r, ref Vp9EntropyProbs fc, int mvcomp, bool usehp)
 264          {
 265              int mag, d, fr, hp;
 266              bool sign = r.Read(fc.Sign[mvcomp]) != 0;
 267              MvClassType mvClass = (MvClassType)r.ReadTree(Luts.Vp9MvClassTree, fc.Classes[mvcomp].AsSpan());
 268              bool class0 = mvClass == MvClassType.MvClass0;
 269  
 270              // Integer part
 271              if (class0)
 272              {
 273                  d = r.Read(fc.Class0[mvcomp][0]);
 274                  mag = 0;
 275              }
 276              else
 277              {
 278                  int i;
 279                  int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits
 280  
 281                  d = 0;
 282                  for (i = 0; i < n; ++i)
 283                  {
 284                      d |= r.Read(fc.Bits[mvcomp][i]) << i;
 285                  }
 286  
 287                  mag = Constants.Class0Size << ((int)mvClass + 2);
 288              }
 289  
 290              // Fractional part
 291              fr = r.ReadTree(Luts.Vp9MvFPTree, class0 ? fc.Class0Fp[mvcomp][d].AsSpan() : fc.Fp[mvcomp].AsSpan());
 292  
 293              // High precision part (if hp is not used, the default value of the hp is 1)
 294              hp = usehp ? r.Read(class0 ? fc.Class0Hp[mvcomp] : fc.Hp[mvcomp]) : 1;
 295  
 296              // Result
 297              mag += ((d << 3) | (fr << 1) | hp) + 1;
 298  
 299              return sign ? -mag : mag;
 300          }
 301  
 302          private static void ReadMv(
 303              ref Reader r,
 304              ref Mv mv,
 305              ref Mv refr,
 306              ref Vp9EntropyProbs fc,
 307              Ptr<Vp9BackwardUpdates> counts,
 308              bool allowHP)
 309          {
 310              MvJointType jointType = (MvJointType)r.ReadTree(Luts.Vp9MvJointTree, fc.Joints.AsSpan());
 311              bool useHP = allowHP && refr.UseMvHp();
 312              Mv diff = new();
 313  
 314              if (Mv.MvJointVertical(jointType))
 315              {
 316                  diff.Row = (short)ReadMvComponent(ref r, ref fc, 0, useHP);
 317              }
 318  
 319              if (Mv.MvJointHorizontal(jointType))
 320              {
 321                  diff.Col = (short)ReadMvComponent(ref r, ref fc, 1, useHP);
 322              }
 323  
 324              diff.IncMv(counts);
 325  
 326              mv.Row = (short)(refr.Row + diff.Row);
 327              mv.Col = (short)(refr.Col + diff.Col);
 328          }
 329  
 330          private static ReferenceMode ReadBlockReferenceMode(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r)
 331          {
 332              if (cm.ReferenceMode == ReferenceMode.ReferenceModeSelect)
 333              {
 334                  int ctx = PredCommon.GetReferenceModeContext(ref cm, ref xd);
 335                  ReferenceMode mode = (ReferenceMode)r.Read(cm.Fc.Value.CompInterProb[ctx]);
 336                  if (!xd.Counts.IsNull)
 337                  {
 338                      ++xd.Counts.Value.CompInter[ctx][(int)mode];
 339                  }
 340  
 341                  return mode; // SingleReference or CompoundReference
 342              }
 343  
 344              return cm.ReferenceMode;
 345          }
 346  
 347          // Read the referncence frame
 348          private static void ReadRefFrames(
 349              ref Vp9Common cm,
 350              ref MacroBlockD xd,
 351              ref Reader r,
 352              int segmentId,
 353              ref Array2<sbyte> refFrame)
 354          {
 355              ref Vp9EntropyProbs fc = ref cm.Fc.Value;
 356  
 357              if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlRefFrame) != 0)
 358              {
 359                  refFrame[0] = (sbyte)cm.Seg.GetSegData(segmentId, SegLvlFeatures.SegLvlRefFrame);
 360                  refFrame[1] = Constants.None;
 361              }
 362              else
 363              {
 364                  ReferenceMode mode = ReadBlockReferenceMode(ref cm, ref xd, ref r);
 365                  if (mode == ReferenceMode.CompoundReference)
 366                  {
 367                      int idx = cm.RefFrameSignBias[cm.CompFixedRef];
 368                      int ctx = PredCommon.GetPredContextCompRefP(ref cm, ref xd);
 369                      int bit = r.Read(fc.CompRefProb[ctx]);
 370                      if (!xd.Counts.IsNull)
 371                      {
 372                          ++xd.Counts.Value.CompRef[ctx][bit];
 373                      }
 374  
 375                      refFrame[idx] = cm.CompFixedRef;
 376                      refFrame[idx == 0 ? 1 : 0] = cm.CompVarRef[bit];
 377                  }
 378                  else if (mode == ReferenceMode.SingleReference)
 379                  {
 380                      int ctx0 = PredCommon.GetPredContextSingleRefP1(ref xd);
 381                      int bit0 = r.Read(fc.SingleRefProb[ctx0][0]);
 382                      if (!xd.Counts.IsNull)
 383                      {
 384                          ++xd.Counts.Value.SingleRef[ctx0][0][bit0];
 385                      }
 386  
 387                      if (bit0 != 0)
 388                      {
 389                          int ctx1 = PredCommon.GetPredContextSingleRefP2(ref xd);
 390                          int bit1 = r.Read(fc.SingleRefProb[ctx1][1]);
 391                          if (!xd.Counts.IsNull)
 392                          {
 393                              ++xd.Counts.Value.SingleRef[ctx1][1][bit1];
 394                          }
 395  
 396                          refFrame[0] = (sbyte)(bit1 != 0 ? Constants.AltRefFrame : Constants.GoldenFrame);
 397                      }
 398                      else
 399                      {
 400                          refFrame[0] = Constants.LastFrame;
 401                      }
 402  
 403                      refFrame[1] = Constants.None;
 404                  }
 405                  else
 406                  {
 407                      Debug.Assert(false, "Invalid prediction mode.");
 408                  }
 409              }
 410          }
 411  
 412          private static byte ReadSwitchableInterpFilter(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r)
 413          {
 414              int ctx = xd.GetPredContextSwitchableInterp();
 415              byte type = (byte)r.ReadTree(Luts.Vp9SwitchableInterpTree, cm.Fc.Value.SwitchableInterpProb[ctx].AsSpan());
 416              if (!xd.Counts.IsNull)
 417              {
 418                  ++xd.Counts.Value.SwitchableInterp[ctx][type];
 419              }
 420  
 421              return type;
 422          }
 423  
 424          private static void ReadIntraBlockModeInfo(ref Vp9Common cm, ref MacroBlockD xd, ref ModeInfo mi, ref Reader r)
 425          {
 426              BlockSize bsize = mi.SbType;
 427              int i;
 428  
 429              switch (bsize)
 430              {
 431                  case BlockSize.Block4x4:
 432                      for (i = 0; i < 4; ++i)
 433                      {
 434                          mi.Bmi[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
 435                      }
 436  
 437                      mi.Mode = mi.Bmi[3].Mode;
 438                      break;
 439                  case BlockSize.Block4x8:
 440                      mi.Bmi[0].Mode = mi.Bmi[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
 441                      mi.Bmi[1].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
 442                      break;
 443                  case BlockSize.Block8x4:
 444                      mi.Bmi[0].Mode = mi.Bmi[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
 445                      mi.Bmi[2].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0);
 446                      break;
 447                  default:
 448                      mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]);
 449                      break;
 450              }
 451  
 452              mi.UvMode = ReadIntraModeUv(ref cm, ref xd, ref r, (byte)mi.Mode);
 453  
 454              // Initialize interp_filter here so we do not have to check for inter block
 455              // modes in GetPredContextSwitchableInterp()
 456              mi.InterpFilter = Constants.SwitchableFilters;
 457  
 458              mi.RefFrame[0] = Constants.IntraFrame;
 459              mi.RefFrame[1] = Constants.None;
 460          }
 461  
 462          private static bool IsMvValid(ref Mv mv)
 463          {
 464              return mv.Row > Constants.MvLow &&
 465                     mv.Row < Constants.MvUpp &&
 466                     mv.Col > Constants.MvLow &&
 467                     mv.Col < Constants.MvUpp;
 468          }
 469  
 470          private static void CopyMvPair(ref Array2<Mv> dst, ref Array2<Mv> src)
 471          {
 472              dst[0] = src[0];
 473              dst[1] = src[1];
 474          }
 475  
 476          private static void ZeroMvPair(ref Array2<Mv> dst)
 477          {
 478              dst[0] = new Mv();
 479              dst[1] = new Mv();
 480          }
 481  
 482          private static bool AssignMv(
 483              ref Vp9Common cm,
 484              ref MacroBlockD xd,
 485              PredictionMode mode,
 486              ref Array2<Mv> mv,
 487              ref Array2<Mv> refMv,
 488              ref Array2<Mv> nearNearestMv,
 489              int isCompound,
 490              bool allowHP,
 491              ref Reader r)
 492          {
 493              int i;
 494              bool ret = true;
 495  
 496              switch (mode)
 497              {
 498                  case PredictionMode.NewMv:
 499                      {
 500                          for (i = 0; i < 1 + isCompound; ++i)
 501                          {
 502                              ReadMv(ref r, ref mv[i], ref refMv[i], ref cm.Fc.Value, xd.Counts, allowHP);
 503                              ret = ret && IsMvValid(ref mv[i]);
 504                          }
 505                          break;
 506                      }
 507                  case PredictionMode.NearMv:
 508                  case PredictionMode.NearestMv:
 509                      {
 510                          CopyMvPair(ref mv, ref nearNearestMv);
 511                          break;
 512                      }
 513                  case PredictionMode.ZeroMv:
 514                      {
 515                          ZeroMvPair(ref mv);
 516                          break;
 517                      }
 518                  default:
 519                      return false;
 520              }
 521              return ret;
 522          }
 523  
 524          private static bool ReadIsInterBlock(ref Vp9Common cm, ref MacroBlockD xd, int segmentId, ref Reader r)
 525          {
 526              if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlRefFrame) != 0)
 527              {
 528                  return cm.Seg.GetSegData(segmentId, SegLvlFeatures.SegLvlRefFrame) != Constants.IntraFrame;
 529              }
 530  
 531              int ctx = xd.GetIntraInterContext();
 532              bool isInter = r.Read(cm.Fc.Value.IntraInterProb[ctx]) != 0;
 533              if (!xd.Counts.IsNull)
 534              {
 535                  ++xd.Counts.Value.IntraInter[ctx][isInter ? 1 : 0];
 536              }
 537  
 538              return isInter;
 539          }
 540  
 541          private static void DecFindBestRefMvs(bool allowHP, Span<Mv> mvlist, ref Mv bestMv, int refmvCount)
 542          {
 543              int i;
 544  
 545              // Make sure all the candidates are properly clamped etc
 546              for (i = 0; i < refmvCount; ++i)
 547              {
 548                  mvlist[i].LowerMvPrecision(allowHP);
 549                  bestMv = mvlist[i];
 550              }
 551          }
 552  
 553          private static bool AddMvRefListEb(Mv mv, ref int refMvCount, Span<Mv> mvRefList, bool earlyBreak)
 554          {
 555              if (refMvCount != 0)
 556              {
 557                  if (Unsafe.As<Mv, int>(ref mv) != Unsafe.As<Mv, int>(ref mvRefList[0]))
 558                  {
 559                      mvRefList[refMvCount] = mv;
 560                      refMvCount++;
 561  
 562                      return true;
 563                  }
 564              }
 565              else
 566              {
 567                  mvRefList[refMvCount++] = mv;
 568                  if (earlyBreak)
 569                  {
 570                      return true;
 571                  }
 572              }
 573  
 574              return false;
 575          }
 576  
 577          // Performs mv sign inversion if indicated by the reference frame combination.
 578          private static Mv ScaleMv(ref ModeInfo mi, int refr, sbyte thisRefFrame, ref Array4<sbyte> refSignBias)
 579          {
 580              Mv mv = mi.Mv[refr];
 581              if (refSignBias[mi.RefFrame[refr]] != refSignBias[thisRefFrame])
 582              {
 583                  mv.Row *= -1;
 584                  mv.Col *= -1;
 585              }
 586              return mv;
 587          }
 588  
 589          private static bool IsDiffRefFrameAddMvEb(
 590              ref ModeInfo mbmi,
 591              sbyte refFrame,
 592              ref Array4<sbyte> refSignBias,
 593              ref int refmvCount,
 594              Span<Mv> mvRefList,
 595              bool earlyBreak)
 596          {
 597              if (mbmi.IsInterBlock())
 598              {
 599                  if (mbmi.RefFrame[0] != refFrame)
 600                  {
 601                      if (AddMvRefListEb(ScaleMv(ref mbmi, 0, refFrame, ref refSignBias), ref refmvCount, mvRefList, earlyBreak))
 602                      {
 603                          return true;
 604                      }
 605                  }
 606                  if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame && Unsafe.As<Mv, int>(ref mbmi.Mv[1]) != Unsafe.As<Mv, int>(ref mbmi.Mv[0]))
 607                  {
 608                      if (AddMvRefListEb(ScaleMv(ref mbmi, 1, refFrame, ref refSignBias), ref refmvCount, mvRefList, earlyBreak))
 609                      {
 610                          return true;
 611                      }
 612                  }
 613  
 614              }
 615              return false;
 616          }
 617  
 618          // This function searches the neighborhood of a given MB/SB
 619          // to try and find candidate reference vectors.
 620          private static int DecFindMvRefs(
 621              ref Vp9Common cm,
 622              ref MacroBlockD xd,
 623              PredictionMode mode,
 624              sbyte refFrame,
 625              Span<Position> mvRefSearch,
 626              Span<Mv> mvRefList,
 627              int miRow,
 628              int miCol,
 629              int block,
 630              int isSub8X8)
 631          {
 632              ref Array4<sbyte> refSignBias = ref cm.RefFrameSignBias;
 633              int i, refmvCount = 0;
 634              bool differentRefFound = false;
 635              Ptr<MvRef> prevFrameMvs = cm.UsePrevFrameMvs ? new Ptr<MvRef>(ref cm.PrevFrameMvs[miRow * cm.MiCols + miCol]) : Ptr<MvRef>.Null;
 636              ref TileInfo tile = ref xd.Tile;
 637              // If mode is nearestmv or newmv (uses nearestmv as a reference) then stop
 638              // searching after the first mv is found.
 639              bool earlyBreak = mode != PredictionMode.NearMv;
 640  
 641              // Blank the reference vector list
 642              mvRefList[..Constants.MaxMvRefCandidates].Clear();
 643  
 644              i = 0;
 645              if (isSub8X8 != 0)
 646              {
 647                  // If the size < 8x8 we get the mv from the bmi substructure for the
 648                  // nearest two blocks.
 649                  for (i = 0; i < 2; ++i)
 650                  {
 651                      ref Position mvRef = ref mvRefSearch[i];
 652                      if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef))
 653                      {
 654                          ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value;
 655                          differentRefFound = true;
 656  
 657                          if (candidateMi.RefFrame[0] == refFrame)
 658                          {
 659                              if (AddMvRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak))
 660                              {
 661                                  goto Done;
 662                              }
 663                          }
 664                          else if (candidateMi.RefFrame[1] == refFrame)
 665                          {
 666                              if (AddMvRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak))
 667                              {
 668                                  goto Done;
 669                              }
 670                          }
 671                      }
 672                  }
 673              }
 674  
 675              // Check the rest of the neighbors in much the same way
 676              // as before except we don't need to keep track of sub blocks or
 677              // mode counts.
 678              for (; i < MvrefNeighbours; ++i)
 679              {
 680                  ref Position mvRef = ref mvRefSearch[i];
 681                  if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef))
 682                  {
 683                      ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value;
 684                      differentRefFound = true;
 685  
 686                      if (candidate.RefFrame[0] == refFrame)
 687                      {
 688                          if (AddMvRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak))
 689                          {
 690                              goto Done;
 691                          }
 692                      }
 693                      else if (candidate.RefFrame[1] == refFrame)
 694                      {
 695                          if (AddMvRefListEb(candidate.Mv[1], ref refmvCount, mvRefList, earlyBreak))
 696                          {
 697                              goto Done;
 698                          }
 699                      }
 700                  }
 701              }
 702  
 703              // Check the last frame's mode and mv info.
 704              if (!prevFrameMvs.IsNull)
 705              {
 706                  if (prevFrameMvs.Value.RefFrame[0] == refFrame)
 707                  {
 708                      if (AddMvRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak))
 709                      {
 710                          goto Done;
 711                      }
 712                  }
 713                  else if (prevFrameMvs.Value.RefFrame[1] == refFrame)
 714                  {
 715                      if (AddMvRefListEb(prevFrameMvs.Value.Mv[1], ref refmvCount, mvRefList, earlyBreak))
 716                      {
 717                          goto Done;
 718                      }
 719                  }
 720              }
 721  
 722              // Since we couldn't find 2 mvs from the same reference frame
 723              // go back through the neighbors and find motion vectors from
 724              // different reference frames.
 725              if (differentRefFound)
 726              {
 727                  for (i = 0; i < MvrefNeighbours; ++i)
 728                  {
 729                      ref Position mvRef = ref mvRefSearch[i];
 730                      if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef))
 731                      {
 732                          ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value;
 733  
 734                          // If the candidate is Intra we don't want to consider its mv.
 735                          if (IsDiffRefFrameAddMvEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList, earlyBreak))
 736                          {
 737                              goto Done;
 738                          }
 739                      }
 740                  }
 741              }
 742  
 743              // Since we still don't have a candidate we'll try the last frame.
 744              if (!prevFrameMvs.IsNull)
 745              {
 746                  if (prevFrameMvs.Value.RefFrame[0] != refFrame && prevFrameMvs.Value.RefFrame[0] > Constants.IntraFrame)
 747                  {
 748                      Mv mv = prevFrameMvs.Value.Mv[0];
 749                      if (refSignBias[prevFrameMvs.Value.RefFrame[0]] != refSignBias[refFrame])
 750                      {
 751                          mv.Row *= -1;
 752                          mv.Col *= -1;
 753                      }
 754                      if (AddMvRefListEb(mv, ref refmvCount, mvRefList, earlyBreak))
 755                      {
 756                          goto Done;
 757                      }
 758                  }
 759  
 760                  if (prevFrameMvs.Value.RefFrame[1] > Constants.IntraFrame &&
 761                      prevFrameMvs.Value.RefFrame[1] != refFrame &&
 762                      Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[1]) != Unsafe.As<Mv, int>(ref prevFrameMvs.Value.Mv[0]))
 763                  {
 764                      Mv mv = prevFrameMvs.Value.Mv[1];
 765                      if (refSignBias[prevFrameMvs.Value.RefFrame[1]] != refSignBias[refFrame])
 766                      {
 767                          mv.Row *= -1;
 768                          mv.Col *= -1;
 769                      }
 770                      if (AddMvRefListEb(mv, ref refmvCount, mvRefList, earlyBreak))
 771                      {
 772                          goto Done;
 773                      }
 774                  }
 775              }
 776  
 777              if (mode == PredictionMode.NearMv)
 778              {
 779                  refmvCount = Constants.MaxMvRefCandidates;
 780              }
 781              else
 782              {
 783                  // We only care about the nearestmv for the remaining modes
 784                  refmvCount = 1;
 785              }
 786  
 787          Done:
 788              // Clamp vectors
 789              for (i = 0; i < refmvCount; ++i)
 790              {
 791                  mvRefList[i].ClampMvRef(ref xd);
 792              }
 793  
 794              return refmvCount;
 795          }
 796  
 797          private static void AppendSub8x8MvsForIdx(
 798              ref Vp9Common cm,
 799              ref MacroBlockD xd,
 800              Span<Position> mvRefSearch,
 801              PredictionMode bMode,
 802              int block,
 803              int refr,
 804              int miRow,
 805              int miCol,
 806              ref Mv bestSub8x8)
 807          {
 808              Span<Mv> mvList = stackalloc Mv[Constants.MaxMvRefCandidates];
 809              ref ModeInfo mi = ref xd.Mi[0].Value;
 810              ref Array4<BModeInfo> bmi = ref mi.Bmi;
 811              int n;
 812              int refmvCount;
 813  
 814              Debug.Assert(Constants.MaxMvRefCandidates == 2);
 815  
 816              refmvCount = DecFindMvRefs(ref cm, ref xd, bMode, mi.RefFrame[refr], mvRefSearch, mvList, miRow, miCol, block, 1);
 817  
 818              switch (block)
 819              {
 820                  case 0:
 821                      bestSub8x8 = mvList[refmvCount - 1];
 822                      break;
 823                  case 1:
 824                  case 2:
 825                      if (bMode == PredictionMode.NearestMv)
 826                      {
 827                          bestSub8x8 = bmi[0].Mv[refr];
 828                      }
 829                      else
 830                      {
 831                          bestSub8x8 = new Mv();
 832                          for (n = 0; n < refmvCount; ++n)
 833                          {
 834                              if (Unsafe.As<Mv, int>(ref bmi[0].Mv[refr]) != Unsafe.As<Mv, int>(ref mvList[n]))
 835                              {
 836                                  bestSub8x8 = mvList[n];
 837                                  break;
 838                              }
 839                          }
 840                      }
 841                      break;
 842                  case 3:
 843                      if (bMode == PredictionMode.NearestMv)
 844                      {
 845                          bestSub8x8 = bmi[2].Mv[refr];
 846                      }
 847                      else
 848                      {
 849                          Span<Mv> candidates = stackalloc Mv[2 + Constants.MaxMvRefCandidates];
 850                          candidates[0] = bmi[1].Mv[refr];
 851                          candidates[1] = bmi[0].Mv[refr];
 852                          candidates[2] = mvList[0];
 853                          candidates[3] = mvList[1];
 854                          bestSub8x8 = new Mv();
 855                          for (n = 0; n < 2 + Constants.MaxMvRefCandidates; ++n)
 856                          {
 857                              if (Unsafe.As<Mv, int>(ref bmi[2].Mv[refr]) != Unsafe.As<Mv, int>(ref candidates[n]))
 858                              {
 859                                  bestSub8x8 = candidates[n];
 860                                  break;
 861                              }
 862                          }
 863                      }
 864                      break;
 865                  default:
 866                      Debug.Assert(false, "Invalid block index.");
 867                      break;
 868              }
 869          }
 870  
 871          private static byte GetModeContext(ref Vp9Common cm, ref MacroBlockD xd, Span<Position> mvRefSearch, int miRow, int miCol)
 872          {
 873              int i;
 874              int contextCounter = 0;
 875              ref TileInfo tile = ref xd.Tile;
 876  
 877              // Get mode count from nearest 2 blocks
 878              for (i = 0; i < 2; ++i)
 879              {
 880                  ref Position mvRef = ref mvRefSearch[i];
 881                  if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef))
 882                  {
 883                      ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value;
 884                      // Keep counts for entropy encoding.
 885                      contextCounter += Luts.Mode2Counter[(int)candidate.Mode];
 886                  }
 887              }
 888  
 889              return (byte)Luts.CounterToContext[contextCounter];
 890          }
 891  
 892          private static void ReadInterBlockModeInfo(
 893              ref Vp9Common cm,
 894              ref MacroBlockD xd,
 895              ref ModeInfo mi,
 896              int miRow,
 897              int miCol,
 898              ref Reader r)
 899          {
 900              BlockSize bsize = mi.SbType;
 901              bool allowHP = cm.AllowHighPrecisionMv;
 902              Array2<Mv> bestRefMvs = new();
 903              int refr, isCompound;
 904              byte interModeCtx;
 905              Span<Position> mvRefSearch = Luts.MvRefBlocks[(int)bsize];
 906  
 907              ReadRefFrames(ref cm, ref xd, ref r, mi.SegmentId, ref mi.RefFrame);
 908              isCompound = mi.HasSecondRef() ? 1 : 0;
 909              interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol);
 910  
 911              if (cm.Seg.IsSegFeatureActive(mi.SegmentId, SegLvlFeatures.SegLvlSkip) != 0)
 912              {
 913                  mi.Mode = PredictionMode.ZeroMv;
 914                  if (bsize < BlockSize.Block8x8)
 915                  {
 916                      xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Invalid usage of segement feature on small blocks");
 917  
 918                      return;
 919                  }
 920              }
 921              else
 922              {
 923                  if (bsize >= BlockSize.Block8x8)
 924                  {
 925                      mi.Mode = ReadInterMode(ref cm, ref xd, ref r, interModeCtx);
 926                  }
 927                  else
 928                  {
 929                      // Sub 8x8 blocks use the nearestmv as a ref_mv if the bMode is NewMv.
 930                      // Setting mode to NearestMv forces the search to stop after the nearestmv
 931                      // has been found. After bModes have been read, mode will be overwritten
 932                      // by the last bMode.
 933                      mi.Mode = PredictionMode.NearestMv;
 934                  }
 935  
 936                  if (mi.Mode != PredictionMode.ZeroMv)
 937                  {
 938                      Span<Mv> tmpMvs = stackalloc Mv[Constants.MaxMvRefCandidates];
 939  
 940                      for (refr = 0; refr < 1 + isCompound; ++refr)
 941                      {
 942                          sbyte frame = mi.RefFrame[refr];
 943                          int refmvCount;
 944  
 945                          refmvCount = DecFindMvRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol, -1, 0);
 946  
 947                          DecFindBestRefMvs(allowHP, tmpMvs, ref bestRefMvs[refr], refmvCount);
 948                      }
 949                  }
 950              }
 951  
 952              mi.InterpFilter = (cm.InterpFilter == Constants.Switchable) ? ReadSwitchableInterpFilter(ref cm, ref xd, ref r) : cm.InterpFilter;
 953  
 954              if (bsize < BlockSize.Block8x8)
 955              {
 956                  int num4X4W = 1 << xd.BmodeBlocksWl;
 957                  int num4X4H = 1 << xd.BmodeBlocksHl;
 958                  int idx, idy;
 959                  PredictionMode bMode = 0;
 960                  Array2<Mv> bestSub8x8 = new();
 961                  const uint InvalidMv = 0x80008000;
 962                  // Initialize the 2nd element as even though it won't be used meaningfully
 963                  // if isCompound is false.
 964                  Unsafe.As<Mv, uint>(ref bestSub8x8[1]) = InvalidMv;
 965                  for (idy = 0; idy < 2; idy += num4X4H)
 966                  {
 967                      for (idx = 0; idx < 2; idx += num4X4W)
 968                      {
 969                          int j = idy * 2 + idx;
 970                          bMode = ReadInterMode(ref cm, ref xd, ref r, interModeCtx);
 971  
 972                          if (bMode == PredictionMode.NearestMv || bMode == PredictionMode.NearMv)
 973                          {
 974                              for (refr = 0; refr < 1 + isCompound; ++refr)
 975                              {
 976                                  AppendSub8x8MvsForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol, ref bestSub8x8[refr]);
 977                              }
 978                          }
 979  
 980                          if (!AssignMv(ref cm, ref xd, bMode, ref mi.Bmi[j].Mv, ref bestRefMvs, ref bestSub8x8, isCompound, allowHP, ref r))
 981                          {
 982                              xd.Corrupted |= true;
 983                              break;
 984                          }
 985  
 986                          if (num4X4H == 2)
 987                          {
 988                              mi.Bmi[j + 2] = mi.Bmi[j];
 989                          }
 990  
 991                          if (num4X4W == 2)
 992                          {
 993                              mi.Bmi[j + 1] = mi.Bmi[j];
 994                          }
 995                      }
 996                  }
 997  
 998                  mi.Mode = bMode;
 999  
1000                  CopyMvPair(ref mi.Mv, ref mi.Bmi[3].Mv);
1001              }
1002              else
1003              {
1004                  xd.Corrupted |= !AssignMv(ref cm, ref xd, mi.Mode, ref mi.Mv, ref bestRefMvs, ref bestRefMvs, isCompound, allowHP, ref r);
1005              }
1006          }
1007  
1008          private static void ReadInterFrameModeInfo(
1009              ref Vp9Common cm,
1010              ref MacroBlockD xd,
1011              int miRow,
1012              int miCol,
1013              ref Reader r,
1014              int xMis,
1015              int yMis)
1016          {
1017              ref ModeInfo mi = ref xd.Mi[0].Value;
1018              bool interBlock;
1019  
1020              mi.SegmentId = (sbyte)ReadInterSegmentId(ref cm, ref xd, miRow, miCol, ref r, xMis, yMis);
1021              mi.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.SegmentId, ref r);
1022              interBlock = ReadIsInterBlock(ref cm, ref xd, mi.SegmentId, ref r);
1023              mi.TxSize = ReadTxSize(ref cm, ref xd, mi.Skip == 0 || !interBlock, ref r);
1024  
1025              if (interBlock)
1026              {
1027                  ReadInterBlockModeInfo(ref cm, ref xd, ref mi, miRow, miCol, ref r);
1028              }
1029              else
1030              {
1031                  ReadIntraBlockModeInfo(ref cm, ref xd, ref mi, ref r);
1032              }
1033          }
1034  
1035          private static PredictionMode LeftBlockMode(Ptr<ModeInfo> curMi, Ptr<ModeInfo> leftMi, int b)
1036          {
1037              if (b == 0 || b == 2)
1038              {
1039                  if (leftMi.IsNull || leftMi.Value.IsInterBlock())
1040                  {
1041                      return PredictionMode.DcPred;
1042                  }
1043  
1044                  return leftMi.Value.GetYMode(b + 1);
1045              }
1046  
1047              Debug.Assert(b == 1 || b == 3);
1048  
1049              return curMi.Value.Bmi[b - 1].Mode;
1050          }
1051  
1052          private static PredictionMode AboveBlockMode(Ptr<ModeInfo> curMi, Ptr<ModeInfo> aboveMi, int b)
1053          {
1054              if (b == 0 || b == 1)
1055              {
1056                  if (aboveMi.IsNull || aboveMi.Value.IsInterBlock())
1057                  {
1058                      return PredictionMode.DcPred;
1059                  }
1060  
1061                  return aboveMi.Value.GetYMode(b + 2);
1062              }
1063  
1064              Debug.Assert(b == 2 || b == 3);
1065  
1066              return curMi.Value.Bmi[b - 2].Mode;
1067          }
1068  
1069          private static ReadOnlySpan<byte> GetYModeProbs(
1070              ref Vp9EntropyProbs fc,
1071              Ptr<ModeInfo> mi,
1072              Ptr<ModeInfo> aboveMi,
1073              Ptr<ModeInfo> leftMi,
1074              int block)
1075          {
1076              PredictionMode above = AboveBlockMode(mi, aboveMi, block);
1077              PredictionMode left = LeftBlockMode(mi, leftMi, block);
1078  
1079              return fc.KfYModeProb[(int)above][(int)left].AsSpan();
1080          }
1081  
1082          private static void ReadIntraFrameModeInfo(
1083              ref Vp9Common cm,
1084              ref MacroBlockD xd,
1085              int miRow,
1086              int miCol,
1087              ref Reader r,
1088              int xMis,
1089              int yMis)
1090          {
1091              Ptr<ModeInfo> mi = xd.Mi[0];
1092              Ptr<ModeInfo> aboveMi = xd.AboveMi;
1093              Ptr<ModeInfo> leftMi = xd.LeftMi;
1094              BlockSize bsize = mi.Value.SbType;
1095              int i;
1096              int miOffset = miRow * cm.MiCols + miCol;
1097  
1098              mi.Value.SegmentId = (sbyte)ReadIntraSegmentId(ref cm, miOffset, xMis, yMis, ref r);
1099              mi.Value.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r);
1100              mi.Value.TxSize = ReadTxSize(ref cm, ref xd, true, ref r);
1101              mi.Value.RefFrame[0] = Constants.IntraFrame;
1102              mi.Value.RefFrame[1] = Constants.None;
1103  
1104              switch (bsize)
1105              {
1106                  case BlockSize.Block4x4:
1107                      for (i = 0; i < 4; ++i)
1108                      {
1109                          mi.Value.Bmi[i].Mode =
1110                              ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, i));
1111                      }
1112  
1113                      mi.Value.Mode = mi.Value.Bmi[3].Mode;
1114                      break;
1115                  case BlockSize.Block4x8:
1116                      mi.Value.Bmi[0].Mode = mi.Value.Bmi[2].Mode =
1117                          ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0));
1118                      mi.Value.Bmi[1].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode =
1119                          ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 1));
1120                      break;
1121                  case BlockSize.Block8x4:
1122                      mi.Value.Bmi[0].Mode = mi.Value.Bmi[1].Mode =
1123                          ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0));
1124                      mi.Value.Bmi[2].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode =
1125                          ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 2));
1126                      break;
1127                  default:
1128                      mi.Value.Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0));
1129                      break;
1130              }
1131  
1132              mi.Value.UvMode = ReadIntraMode(ref r, cm.Fc.Value.KfUvModeProb[(int)mi.Value.Mode].AsSpan());
1133          }
1134  
1135          private static void CopyRefFramePair(ref Array2<sbyte> dst, ref Array2<sbyte> src)
1136          {
1137              dst[0] = src[0];
1138              dst[1] = src[1];
1139          }
1140  
1141          public static void ReadModeInfo(
1142              ref TileWorkerData twd,
1143              ref Vp9Common cm,
1144              int miRow,
1145              int miCol,
1146              int xMis,
1147              int yMis)
1148          {
1149              ref Reader r = ref twd.BitReader;
1150              ref MacroBlockD xd = ref twd.Xd;
1151              ref ModeInfo mi = ref xd.Mi[0].Value;
1152              ArrayPtr<MvRef> frameMvs = cm.CurFrameMvs.Slice(miRow * cm.MiCols + miCol);
1153              int w, h;
1154  
1155              if (cm.FrameIsIntraOnly())
1156              {
1157                  ReadIntraFrameModeInfo(ref cm, ref xd, miRow, miCol, ref r, xMis, yMis);
1158              }
1159              else
1160              {
1161                  ReadInterFrameModeInfo(ref cm, ref xd, miRow, miCol, ref r, xMis, yMis);
1162  
1163                  for (h = 0; h < yMis; ++h)
1164                  {
1165                      for (w = 0; w < xMis; ++w)
1166                      {
1167                          ref MvRef mv = ref frameMvs[w];
1168                          CopyRefFramePair(ref mv.RefFrame, ref mi.RefFrame);
1169                          CopyMvPair(ref mv.Mv, ref mi.Mv);
1170                      }
1171                      frameMvs = frameMvs.Slice(cm.MiCols);
1172                  }
1173              }
1174          }
1175      }
1176  }