DecodeFrame.cs
1 using Ryujinx.Common.Memory; 2 using Ryujinx.Graphics.Nvdec.Vp9.Common; 3 using Ryujinx.Graphics.Nvdec.Vp9.Dsp; 4 using Ryujinx.Graphics.Nvdec.Vp9.Types; 5 using Ryujinx.Graphics.Video; 6 using System; 7 using System.Buffers.Binary; 8 using System.Diagnostics; 9 using System.Runtime.CompilerServices; 10 using System.Runtime.InteropServices; 11 using System.Threading.Tasks; 12 13 namespace Ryujinx.Graphics.Nvdec.Vp9 14 { 15 static class DecodeFrame 16 { 17 private static bool ReadIsValid(ArrayPtr<byte> start, int len) 18 { 19 return len != 0 && len <= start.Length; 20 } 21 22 private static void InverseTransformBlockInter(ref MacroBlockD xd, int plane, TxSize txSize, Span<byte> dst, int stride, int eob) 23 { 24 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 25 ArrayPtr<int> dqcoeff = pd.DqCoeff; 26 Debug.Assert(eob > 0); 27 if (xd.CurBuf.HighBd) 28 { 29 Span<ushort> dst16 = MemoryMarshal.Cast<byte, ushort>(dst); 30 if (xd.Lossless) 31 { 32 Idct.HighbdIwht4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 33 } 34 else 35 { 36 switch (txSize) 37 { 38 case TxSize.Tx4x4: 39 Idct.HighbdIdct4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 40 break; 41 case TxSize.Tx8x8: 42 Idct.HighbdIdct8x8Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 43 break; 44 case TxSize.Tx16x16: 45 Idct.HighbdIdct16x16Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 46 break; 47 case TxSize.Tx32x32: 48 Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 49 break; 50 default: 51 Debug.Assert(false, "Invalid transform size"); 52 break; 53 } 54 } 55 } 56 else 57 { 58 if (xd.Lossless) 59 { 60 Idct.Iwht4x4Add(dqcoeff.AsSpan(), dst, stride, eob); 61 } 62 else 63 { 64 switch (txSize) 65 { 66 case TxSize.Tx4x4: 67 Idct.Idct4x4Add(dqcoeff.AsSpan(), dst, stride, eob); 68 break; 69 case TxSize.Tx8x8: 70 Idct.Idct8x8Add(dqcoeff.AsSpan(), dst, stride, eob); 71 break; 72 case TxSize.Tx16x16: 73 Idct.Idct16x16Add(dqcoeff.AsSpan(), dst, stride, eob); 74 break; 75 case TxSize.Tx32x32: 76 Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); 77 break; 78 default: 79 Debug.Assert(false, "Invalid transform size"); 80 return; 81 } 82 } 83 } 84 85 if (eob == 1) 86 { 87 dqcoeff.AsSpan()[0] = 0; 88 } 89 else 90 { 91 if (txSize <= TxSize.Tx16x16 && eob <= 10) 92 { 93 dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); 94 } 95 else if (txSize == TxSize.Tx32x32 && eob <= 34) 96 { 97 dqcoeff.AsSpan()[..256].Clear(); 98 } 99 else 100 { 101 dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); 102 } 103 } 104 } 105 106 private static void InverseTransformBlockIntra( 107 ref MacroBlockD xd, 108 int plane, 109 TxType txType, 110 TxSize txSize, 111 Span<byte> dst, 112 int stride, 113 int eob) 114 { 115 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 116 ArrayPtr<int> dqcoeff = pd.DqCoeff; 117 Debug.Assert(eob > 0); 118 if (xd.CurBuf.HighBd) 119 { 120 Span<ushort> dst16 = MemoryMarshal.Cast<byte, ushort>(dst); 121 if (xd.Lossless) 122 { 123 Idct.HighbdIwht4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 124 } 125 else 126 { 127 switch (txSize) 128 { 129 case TxSize.Tx4x4: 130 Idct.HighbdIht4x4Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 131 break; 132 case TxSize.Tx8x8: 133 Idct.HighbdIht8x8Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 134 break; 135 case TxSize.Tx16x16: 136 Idct.HighbdIht16x16Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 137 break; 138 case TxSize.Tx32x32: 139 Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); 140 break; 141 default: 142 Debug.Assert(false, "Invalid transform size"); 143 break; 144 } 145 } 146 } 147 else 148 { 149 if (xd.Lossless) 150 { 151 Idct.Iwht4x4Add(dqcoeff.AsSpan(), dst, stride, eob); 152 } 153 else 154 { 155 switch (txSize) 156 { 157 case TxSize.Tx4x4: 158 Idct.Iht4x4Add(txType, dqcoeff.AsSpan(), dst, stride, eob); 159 break; 160 case TxSize.Tx8x8: 161 Idct.Iht8x8Add(txType, dqcoeff.AsSpan(), dst, stride, eob); 162 break; 163 case TxSize.Tx16x16: 164 Idct.Iht16x16Add(txType, dqcoeff.AsSpan(), dst, stride, eob); 165 break; 166 case TxSize.Tx32x32: 167 Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); 168 break; 169 default: 170 Debug.Assert(false, "Invalid transform size"); 171 return; 172 } 173 } 174 } 175 176 if (eob == 1) 177 { 178 dqcoeff.AsSpan()[0] = 0; 179 } 180 else 181 { 182 if (txType == TxType.DctDct && txSize <= TxSize.Tx16x16 && eob <= 10) 183 { 184 dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); 185 } 186 else if (txSize == TxSize.Tx32x32 && eob <= 34) 187 { 188 dqcoeff.AsSpan()[..256].Clear(); 189 } 190 else 191 { 192 dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); 193 } 194 } 195 } 196 197 private static unsafe void PredictAndReconstructIntraBlock( 198 ref TileWorkerData twd, 199 ref ModeInfo mi, 200 int plane, 201 int row, 202 int col, 203 TxSize txSize) 204 { 205 ref MacroBlockD xd = ref twd.Xd; 206 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 207 PredictionMode mode = (plane == 0) ? mi.Mode : mi.UvMode; 208 int dstOffset = 4 * row * pd.Dst.Stride + 4 * col; 209 byte* dst = &pd.Dst.Buf.ToPointer()[dstOffset]; 210 Span<byte> dstSpan = pd.Dst.Buf.AsSpan()[dstOffset..]; 211 212 if (mi.SbType < BlockSize.Block8x8) 213 { 214 if (plane == 0) 215 { 216 mode = xd.Mi[0].Value.Bmi[(row << 1) + col].Mode; 217 } 218 } 219 220 ReconIntra.PredictIntraBlock(ref xd, pd.N4Wl, txSize, mode, dst, pd.Dst.Stride, dst, pd.Dst.Stride, col, row, plane); 221 222 if (mi.Skip == 0) 223 { 224 TxType txType = 225 (plane != 0 || xd.Lossless) ? TxType.DctDct : ReconIntra.IntraModeToTxTypeLookup[(int)mode]; 226 var sc = (plane != 0 || xd.Lossless) 227 ? Luts.Vp9DefaultScanOrders[(int)txSize] 228 : Luts.Vp9ScanOrders[(int)txSize][(int)txType]; 229 int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId); 230 if (eob > 0) 231 { 232 InverseTransformBlockIntra(ref xd, plane, txType, txSize, dstSpan, pd.Dst.Stride, eob); 233 } 234 } 235 } 236 237 private static int ReconstructInterBlock( 238 ref TileWorkerData twd, 239 ref ModeInfo mi, 240 int plane, 241 int row, 242 int col, 243 TxSize txSize) 244 { 245 ref MacroBlockD xd = ref twd.Xd; 246 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 247 var sc = Luts.Vp9DefaultScanOrders[(int)txSize]; 248 int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId); 249 Span<byte> dst = pd.Dst.Buf.AsSpan()[(4 * row * pd.Dst.Stride + 4 * col)..]; 250 251 if (eob > 0) 252 { 253 InverseTransformBlockInter(ref xd, plane, txSize, dst, pd.Dst.Stride, eob); 254 } 255 return eob; 256 } 257 258 private static unsafe void BuildMcBorder( 259 byte* src, 260 int srcStride, 261 byte* dst, 262 int dstStride, 263 int x, 264 int y, 265 int bW, 266 int bH, 267 int w, 268 int h) 269 { 270 // Get a pointer to the start of the real data for this row. 271 byte* refRow = src - x - y * srcStride; 272 273 if (y >= h) 274 { 275 refRow += (h - 1) * srcStride; 276 } 277 else if (y > 0) 278 { 279 refRow += y * srcStride; 280 } 281 282 do 283 { 284 int right = 0, copy; 285 int left = x < 0 ? -x : 0; 286 287 if (left > bW) 288 { 289 left = bW; 290 } 291 292 if (x + bW > w) 293 { 294 right = x + bW - w; 295 } 296 297 if (right > bW) 298 { 299 right = bW; 300 } 301 302 copy = bW - left - right; 303 304 if (left != 0) 305 { 306 MemoryUtil.Fill(dst, refRow[0], left); 307 } 308 309 if (copy != 0) 310 { 311 MemoryUtil.Copy(dst + left, refRow + x + left, copy); 312 } 313 314 if (right != 0) 315 { 316 MemoryUtil.Fill(dst + left + copy, refRow[w - 1], right); 317 } 318 319 dst += dstStride; 320 ++y; 321 322 if (y > 0 && y < h) 323 { 324 refRow += srcStride; 325 } 326 } while (--bH != 0); 327 } 328 329 private static unsafe void HighBuildMcBorder( 330 byte* src8, 331 int srcStride, 332 ushort* dst, 333 int dstStride, 334 int x, 335 int y, 336 int bW, 337 int bH, 338 int w, 339 int h) 340 { 341 // Get a pointer to the start of the real data for this row. 342 ushort* src = (ushort*)src8; 343 ushort* refRow = src - x - y * srcStride; 344 345 if (y >= h) 346 { 347 refRow += (h - 1) * srcStride; 348 } 349 else if (y > 0) 350 { 351 refRow += y * srcStride; 352 } 353 354 do 355 { 356 int right = 0, copy; 357 int left = x < 0 ? -x : 0; 358 359 if (left > bW) 360 { 361 left = bW; 362 } 363 364 if (x + bW > w) 365 { 366 right = x + bW - w; 367 } 368 369 if (right > bW) 370 { 371 right = bW; 372 } 373 374 copy = bW - left - right; 375 376 if (left != 0) 377 { 378 MemoryUtil.Fill(dst, refRow[0], left); 379 } 380 381 if (copy != 0) 382 { 383 MemoryUtil.Copy(dst + left, refRow + x + left, copy); 384 } 385 386 if (right != 0) 387 { 388 MemoryUtil.Fill(dst + left + copy, refRow[w - 1], right); 389 } 390 391 dst += dstStride; 392 ++y; 393 394 if (y > 0 && y < h) 395 { 396 refRow += srcStride; 397 } 398 } while (--bH != 0); 399 } 400 401 [SkipLocalsInit] 402 private static unsafe void ExtendAndPredict( 403 byte* bufPtr1, 404 int preBufStride, 405 int x0, 406 int y0, 407 int bW, 408 int bH, 409 int frameWidth, 410 int frameHeight, 411 int borderOffset, 412 byte* dst, 413 int dstBufStride, 414 int subpelX, 415 int subpelY, 416 Array8<short>[] kernel, 417 ref ScaleFactors sf, 418 ref MacroBlockD xd, 419 int w, 420 int h, 421 int refr, 422 int xs, 423 int ys) 424 { 425 ushort* mcBufHigh = stackalloc ushort[80 * 2 * 80 * 2]; 426 if (xd.CurBuf.HighBd) 427 { 428 HighBuildMcBorder(bufPtr1, preBufStride, mcBufHigh, bW, x0, y0, bW, bH, frameWidth, frameHeight); 429 ReconInter.HighbdInterPredictor( 430 mcBufHigh + borderOffset, 431 bW, 432 (ushort*)dst, 433 dstBufStride, 434 subpelX, 435 subpelY, 436 ref sf, 437 w, 438 h, 439 refr, 440 kernel, 441 xs, 442 ys, 443 xd.Bd); 444 } 445 else 446 { 447 BuildMcBorder(bufPtr1, preBufStride, (byte*)mcBufHigh, bW, x0, y0, bW, bH, frameWidth, frameHeight); 448 ReconInter.InterPredictor( 449 (byte*)mcBufHigh + borderOffset, 450 bW, 451 dst, 452 dstBufStride, 453 subpelX, 454 subpelY, 455 ref sf, 456 w, 457 h, 458 refr, 459 kernel, 460 xs, 461 ys); 462 } 463 } 464 465 private static unsafe void DecBuildInterPredictors( 466 ref MacroBlockD xd, 467 int plane, 468 int bw, 469 int bh, 470 int x, 471 int y, 472 int w, 473 int h, 474 int miX, 475 int miY, 476 Array8<short>[] kernel, 477 ref ScaleFactors sf, 478 ref Buf2D preBuf, 479 ref Buf2D dstBuf, 480 ref Mv mv, 481 ref Surface refFrameBuf, 482 bool isScaled, 483 int refr) 484 { 485 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 486 byte* dst = dstBuf.Buf.ToPointer() + dstBuf.Stride * y + x; 487 Mv32 scaledMv; 488 int xs, ys, x0, y0, x0_16, y0_16, frameWidth, frameHeight, bufStride, subpelX, subpelY; 489 byte* refFrame; 490 byte* bufPtr; 491 492 // Get reference frame pointer, width and height. 493 if (plane == 0) 494 { 495 frameWidth = refFrameBuf.Width; 496 frameHeight = refFrameBuf.Height; 497 refFrame = refFrameBuf.YBuffer.ToPointer(); 498 } 499 else 500 { 501 frameWidth = refFrameBuf.UvWidth; 502 frameHeight = refFrameBuf.UvHeight; 503 refFrame = plane == 1 ? refFrameBuf.UBuffer.ToPointer() : refFrameBuf.VBuffer.ToPointer(); 504 } 505 506 if (isScaled) 507 { 508 Mv mvQ4 = ReconInter.ClampMvToUmvBorderSb(ref xd, ref mv, bw, bh, pd.SubsamplingX, pd.SubsamplingY); 509 // Co-ordinate of containing block to pixel precision. 510 int xStart = (-xd.MbToLeftEdge >> (3 + pd.SubsamplingX)); 511 int yStart = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY)); 512 // Co-ordinate of the block to 1/16th pixel precision. 513 x0_16 = (xStart + x) << Filter.SubpelBits; 514 y0_16 = (yStart + y) << Filter.SubpelBits; 515 516 // Co-ordinate of current block in reference frame 517 // to 1/16th pixel precision. 518 x0_16 = sf.ScaleValueX(x0_16); 519 y0_16 = sf.ScaleValueY(y0_16); 520 521 // Map the top left corner of the block into the reference frame. 522 x0 = sf.ScaleValueX(xStart + x); 523 y0 = sf.ScaleValueY(yStart + y); 524 525 // Scale the MV and incorporate the sub-pixel offset of the block 526 // in the reference frame. 527 scaledMv = sf.ScaleMv(ref mvQ4, miX + x, miY + y); 528 xs = sf.XStepQ4; 529 ys = sf.YStepQ4; 530 } 531 else 532 { 533 // Co-ordinate of containing block to pixel precision. 534 x0 = (-xd.MbToLeftEdge >> (3 + pd.SubsamplingX)) + x; 535 y0 = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY)) + y; 536 537 // Co-ordinate of the block to 1/16th pixel precision. 538 x0_16 = x0 << Filter.SubpelBits; 539 y0_16 = y0 << Filter.SubpelBits; 540 541 scaledMv.Row = mv.Row * (1 << (1 - pd.SubsamplingY)); 542 scaledMv.Col = mv.Col * (1 << (1 - pd.SubsamplingX)); 543 xs = ys = 16; 544 } 545 subpelX = scaledMv.Col & Filter.SubpelMask; 546 subpelY = scaledMv.Row & Filter.SubpelMask; 547 548 // Calculate the top left corner of the best matching block in the 549 // reference frame. 550 x0 += scaledMv.Col >> Filter.SubpelBits; 551 y0 += scaledMv.Row >> Filter.SubpelBits; 552 x0_16 += scaledMv.Col; 553 y0_16 += scaledMv.Row; 554 555 // Get reference block pointer. 556 bufPtr = refFrame + y0 * preBuf.Stride + x0; 557 bufStride = preBuf.Stride; 558 559 // Do border extension if there is motion or the 560 // width/height is not a multiple of 8 pixels. 561 if (isScaled || scaledMv.Col != 0 || scaledMv.Row != 0 || (frameWidth & 0x7) != 0 || (frameHeight & 0x7) != 0) 562 { 563 int y1 = ((y0_16 + (h - 1) * ys) >> Filter.SubpelBits) + 1; 564 565 // Get reference block bottom right horizontal coordinate. 566 int x1 = ((x0_16 + (w - 1) * xs) >> Filter.SubpelBits) + 1; 567 int xPad = 0, yPad = 0; 568 569 if (subpelX != 0 || (sf.XStepQ4 != Filter.SubpelShifts)) 570 { 571 x0 -= Constants.Vp9InterpExtend - 1; 572 x1 += Constants.Vp9InterpExtend; 573 xPad = 1; 574 } 575 576 if (subpelY != 0 || (sf.YStepQ4 != Filter.SubpelShifts)) 577 { 578 y0 -= Constants.Vp9InterpExtend - 1; 579 y1 += Constants.Vp9InterpExtend; 580 yPad = 1; 581 } 582 583 // Skip border extension if block is inside the frame. 584 if (x0 < 0 || x0 > frameWidth - 1 || x1 < 0 || x1 > frameWidth - 1 || 585 y0 < 0 || y0 > frameHeight - 1 || y1 < 0 || y1 > frameHeight - 1) 586 { 587 // Extend the border. 588 byte* bufPtr1 = refFrame + y0 * bufStride + x0; 589 int bW = x1 - x0 + 1; 590 int bH = y1 - y0 + 1; 591 int borderOffset = yPad * 3 * bW + xPad * 3; 592 593 ExtendAndPredict( 594 bufPtr1, 595 bufStride, 596 x0, 597 y0, 598 bW, 599 bH, 600 frameWidth, 601 frameHeight, 602 borderOffset, 603 dst, 604 dstBuf.Stride, 605 subpelX, 606 subpelY, 607 kernel, 608 ref sf, 609 ref xd, 610 w, 611 h, 612 refr, 613 xs, 614 ys); 615 616 return; 617 } 618 } 619 620 if (xd.CurBuf.HighBd) 621 { 622 ReconInter.HighbdInterPredictor( 623 (ushort*)bufPtr, 624 bufStride, 625 (ushort*)dst, 626 dstBuf.Stride, 627 subpelX, 628 subpelY, 629 ref sf, 630 w, 631 h, 632 refr, 633 kernel, 634 xs, 635 ys, 636 xd.Bd); 637 } 638 else 639 { 640 ReconInter.InterPredictor( 641 bufPtr, 642 bufStride, 643 dst, 644 dstBuf.Stride, 645 subpelX, 646 subpelY, 647 ref sf, 648 w, 649 h, 650 refr, 651 kernel, 652 xs, 653 ys); 654 } 655 } 656 657 private static void DecBuildInterPredictorsSb(ref Vp9Common cm, ref MacroBlockD xd, int miRow, int miCol) 658 { 659 int plane; 660 int miX = miCol * Constants.MiSize; 661 int miY = miRow * Constants.MiSize; 662 ref ModeInfo mi = ref xd.Mi[0].Value; 663 Array8<short>[] kernel = Luts.Vp9FilterKernels[mi.InterpFilter]; 664 BlockSize sbType = mi.SbType; 665 int isCompound = mi.HasSecondRef() ? 1 : 0; 666 int refr; 667 bool isScaled; 668 669 for (refr = 0; refr < 1 + isCompound; ++refr) 670 { 671 int frame = mi.RefFrame[refr]; 672 ref RefBuffer refBuf = ref cm.FrameRefs[frame - Constants.LastFrame]; 673 ref ScaleFactors sf = ref refBuf.Sf; 674 ref Surface refFrameBuf = ref refBuf.Buf; 675 676 if (!sf.IsValidScale()) 677 { 678 xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Reference frame has invalid dimensions"); 679 } 680 681 isScaled = sf.IsScaled(); 682 ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, isScaled ? new Ptr<ScaleFactors>(ref sf) : Ptr<ScaleFactors>.Null); 683 xd.BlockRefs[refr] = new Ptr<RefBuffer>(ref refBuf); 684 685 if (sbType < BlockSize.Block8x8) 686 { 687 for (plane = 0; plane < Constants.MaxMbPlane; ++plane) 688 { 689 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 690 ref Buf2D dstBuf = ref pd.Dst; 691 int num4x4W = pd.N4W; 692 int num4x4H = pd.N4H; 693 int n4Wx4 = 4 * num4x4W; 694 int n4Hx4 = 4 * num4x4H; 695 ref Buf2D preBuf = ref pd.Pre[refr]; 696 int i = 0, x, y; 697 for (y = 0; y < num4x4H; ++y) 698 { 699 for (x = 0; x < num4x4W; ++x) 700 { 701 Mv mv = ReconInter.AverageSplitMvs(ref pd, ref mi, refr, i++); 702 DecBuildInterPredictors( 703 ref xd, 704 plane, 705 n4Wx4, 706 n4Hx4, 707 4 * x, 708 4 * y, 709 4, 710 4, 711 miX, 712 miY, 713 kernel, 714 ref sf, 715 ref preBuf, 716 ref dstBuf, 717 ref mv, 718 ref refFrameBuf, 719 isScaled, 720 refr); 721 } 722 } 723 } 724 } 725 else 726 { 727 Mv mv = mi.Mv[refr]; 728 for (plane = 0; plane < Constants.MaxMbPlane; ++plane) 729 { 730 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 731 ref Buf2D dstBuf = ref pd.Dst; 732 int num4x4W = pd.N4W; 733 int num4x4H = pd.N4H; 734 int n4Wx4 = 4 * num4x4W; 735 int n4Hx4 = 4 * num4x4H; 736 ref Buf2D preBuf = ref pd.Pre[refr]; 737 DecBuildInterPredictors( 738 ref xd, 739 plane, 740 n4Wx4, 741 n4Hx4, 742 0, 743 0, 744 n4Wx4, 745 n4Hx4, 746 miX, 747 miY, 748 kernel, 749 ref sf, 750 ref preBuf, 751 ref dstBuf, 752 ref mv, 753 ref refFrameBuf, 754 isScaled, 755 refr); 756 } 757 } 758 } 759 } 760 761 private static unsafe void DecResetSkipContext(ref MacroBlockD xd) 762 { 763 int i; 764 for (i = 0; i < Constants.MaxMbPlane; i++) 765 { 766 ref MacroBlockDPlane pd = ref xd.Plane[i]; 767 MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W); 768 MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H); 769 } 770 } 771 772 private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl) 773 { 774 int i; 775 for (i = 0; i < Constants.MaxMbPlane; i++) 776 { 777 xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX); 778 xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY); 779 xd.Plane[i].N4Wl = (byte)(bwl - xd.Plane[i].SubsamplingX); 780 xd.Plane[i].N4Hl = (byte)(bhl - xd.Plane[i].SubsamplingY); 781 } 782 } 783 784 private static ref ModeInfo SetOffsets( 785 ref Vp9Common cm, 786 ref MacroBlockD xd, 787 BlockSize bsize, 788 int miRow, 789 int miCol, 790 int bw, 791 int bh, 792 int xMis, 793 int yMis, 794 int bwl, 795 int bhl) 796 { 797 int offset = miRow * cm.MiStride + miCol; 798 int x, y; 799 ref TileInfo tile = ref xd.Tile; 800 801 xd.Mi = cm.MiGridVisible.Slice(offset); 802 xd.Mi[0] = new Ptr<ModeInfo>(ref cm.Mi[offset]); 803 xd.Mi[0].Value.SbType = bsize; 804 for (y = 0; y < yMis; ++y) 805 { 806 for (x = y == 0 ? 1 : 0; x < xMis; ++x) 807 { 808 xd.Mi[y * cm.MiStride + x] = xd.Mi[0]; 809 } 810 } 811 812 SetPlaneN4(ref xd, bw, bh, bwl, bhl); 813 814 xd.SetSkipContext(miRow, miCol); 815 816 // Distance of Mb to the various image edges. These are specified to 8th pel 817 // as they are always compared to values that are in 1/8th pel units 818 xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols); 819 820 ReconInter.SetupDstPlanes(ref xd.Plane, ref xd.CurBuf, miRow, miCol); 821 822 return ref xd.Mi[0].Value; 823 } 824 825 private static void DecodeBlock( 826 ref TileWorkerData twd, 827 ref Vp9Common cm, 828 int miRow, 829 int miCol, 830 BlockSize bsize, 831 int bwl, 832 int bhl) 833 { 834 bool less8x8 = bsize < BlockSize.Block8x8; 835 int bw = 1 << (bwl - 1); 836 int bh = 1 << (bhl - 1); 837 int xMis = Math.Min(bw, cm.MiCols - miCol); 838 int yMis = Math.Min(bh, cm.MiRows - miRow); 839 ref Reader r = ref twd.BitReader; 840 ref MacroBlockD xd = ref twd.Xd; 841 842 ref ModeInfo mi = ref SetOffsets(ref cm, ref xd, bsize, miRow, miCol, bw, bh, xMis, yMis, bwl, bhl); 843 844 if (bsize >= BlockSize.Block8x8 && (cm.SubsamplingX != 0 || cm.SubsamplingY != 0)) 845 { 846 BlockSize uvSubsize = Luts.SsSizeLookup[(int)bsize][cm.SubsamplingX][cm.SubsamplingY]; 847 if (uvSubsize == BlockSize.BlockInvalid) 848 { 849 xd.ErrorInfo.Value.InternalError(CodecErr.CodecCorruptFrame, "Invalid block size."); 850 } 851 } 852 853 DecodeMv.ReadModeInfo(ref twd, ref cm, miRow, miCol, xMis, yMis); 854 855 if (mi.Skip != 0) 856 { 857 DecResetSkipContext(ref xd); 858 } 859 860 if (!mi.IsInterBlock()) 861 { 862 int plane; 863 for (plane = 0; plane < Constants.MaxMbPlane; ++plane) 864 { 865 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 866 TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; 867 int num4x4W = pd.N4W; 868 int num4x4H = pd.N4H; 869 int step = 1 << (int)txSize; 870 int row, col; 871 int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); 872 int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); 873 874 xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide); 875 xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh); 876 877 for (row = 0; row < maxBlocksHigh; row += step) 878 { 879 for (col = 0; col < maxBlocksWide; col += step) 880 { 881 PredictAndReconstructIntraBlock(ref twd, ref mi, plane, row, col, txSize); 882 } 883 } 884 } 885 } 886 else 887 { 888 // Prediction 889 DecBuildInterPredictorsSb(ref cm, ref xd, miRow, miCol); 890 891 // Reconstruction 892 if (mi.Skip == 0) 893 { 894 int eobtotal = 0; 895 int plane; 896 897 for (plane = 0; plane < Constants.MaxMbPlane; ++plane) 898 { 899 ref MacroBlockDPlane pd = ref xd.Plane[plane]; 900 TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; 901 int num4x4W = pd.N4W; 902 int num4x4H = pd.N4H; 903 int step = 1 << (int)txSize; 904 int row, col; 905 int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); 906 int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); 907 908 xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide); 909 xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh); 910 911 for (row = 0; row < maxBlocksHigh; row += step) 912 { 913 for (col = 0; col < maxBlocksWide; col += step) 914 { 915 eobtotal += ReconstructInterBlock(ref twd, ref mi, plane, row, col, txSize); 916 } 917 } 918 } 919 920 if (!less8x8 && eobtotal == 0) 921 { 922 mi.Skip = 1; // Skip loopfilter 923 } 924 } 925 } 926 927 xd.Corrupted |= r.HasError(); 928 929 if (cm.Lf.FilterLevel != 0) 930 { 931 LoopFilter.BuildMask(ref cm, ref mi, miRow, miCol, bw, bh); 932 } 933 } 934 935 private static int DecPartitionPlaneContext(ref TileWorkerData twd, int miRow, int miCol, int bsl) 936 { 937 ref sbyte aboveCtx = ref twd.Xd.AboveSegContext[miCol]; 938 ref sbyte leftCtx = ref twd.Xd.LeftSegContext[miRow & Constants.MiMask]; 939 int above = (aboveCtx >> bsl) & 1, left = (leftCtx >> bsl) & 1; 940 941 return (left * 2 + above) + bsl * Constants.PartitionPloffset; 942 } 943 944 private static void DecUpdatePartitionContext( 945 ref TileWorkerData twd, 946 int miRow, 947 int miCol, 948 BlockSize subsize, 949 int bw) 950 { 951 Span<sbyte> aboveCtx = twd.Xd.AboveSegContext.Slice(miCol).AsSpan(); 952 Span<sbyte> leftCtx = MemoryMarshal.CreateSpan(ref twd.Xd.LeftSegContext[miRow & Constants.MiMask], 8 - (miRow & Constants.MiMask)); 953 954 // Update the partition context at the end notes. Set partition bits 955 // of block sizes larger than the current one to be one, and partition 956 // bits of smaller block sizes to be zero. 957 aboveCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Above); 958 leftCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Left); 959 } 960 961 private static PartitionType ReadPartition( 962 ref TileWorkerData twd, 963 int miRow, 964 int miCol, 965 int hasRows, 966 int hasCols, 967 int bsl) 968 { 969 int ctx = DecPartitionPlaneContext(ref twd, miRow, miCol, bsl); 970 ReadOnlySpan<byte> probs = MemoryMarshal.CreateReadOnlySpan(ref twd.Xd.PartitionProbs[ctx][0], 3); 971 PartitionType p; 972 ref Reader r = ref twd.BitReader; 973 974 if (hasRows != 0 && hasCols != 0) 975 { 976 p = (PartitionType)r.ReadTree(Luts.Vp9PartitionTree, probs); 977 } 978 else if (hasRows == 0 && hasCols != 0) 979 { 980 p = r.Read(probs[1]) != 0 ? PartitionType.PartitionSplit : PartitionType.PartitionHorz; 981 } 982 else if (hasRows != 0 && hasCols == 0) 983 { 984 p = r.Read(probs[2]) != 0 ? PartitionType.PartitionSplit : PartitionType.PartitionVert; 985 } 986 else 987 { 988 p = PartitionType.PartitionSplit; 989 } 990 991 if (!twd.Xd.Counts.IsNull) 992 { 993 ++twd.Xd.Counts.Value.Partition[ctx][(int)p]; 994 } 995 996 return p; 997 } 998 999 private static void DecodePartition( 1000 ref TileWorkerData twd, 1001 ref Vp9Common cm, 1002 int miRow, 1003 int miCol, 1004 BlockSize bsize, 1005 int n4x4L2) 1006 { 1007 int n8x8L2 = n4x4L2 - 1; 1008 int num8x8Wh = 1 << n8x8L2; 1009 int hbs = num8x8Wh >> 1; 1010 PartitionType partition; 1011 BlockSize subsize; 1012 bool hasRows = (miRow + hbs) < cm.MiRows; 1013 bool hasCols = (miCol + hbs) < cm.MiCols; 1014 ref MacroBlockD xd = ref twd.Xd; 1015 1016 if (miRow >= cm.MiRows || miCol >= cm.MiCols) 1017 { 1018 return; 1019 } 1020 1021 partition = ReadPartition(ref twd, miRow, miCol, hasRows ? 1 : 0, hasCols ? 1 : 0, n8x8L2); 1022 subsize = Luts.SubsizeLookup[(int)partition][(int)bsize]; 1023 if (hbs == 0) 1024 { 1025 // Calculate bmode block dimensions (log 2) 1026 xd.BmodeBlocksWl = (byte)(1 >> ((partition & PartitionType.PartitionVert) != 0 ? 1 : 0)); 1027 xd.BmodeBlocksHl = (byte)(1 >> ((partition & PartitionType.PartitionHorz) != 0 ? 1 : 0)); 1028 DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, 1, 1); 1029 } 1030 else 1031 { 1032 switch (partition) 1033 { 1034 case PartitionType.PartitionNone: 1035 DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n4x4L2); 1036 break; 1037 case PartitionType.PartitionHorz: 1038 DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n8x8L2); 1039 if (hasRows) 1040 { 1041 DecodeBlock(ref twd, ref cm, miRow + hbs, miCol, subsize, n4x4L2, n8x8L2); 1042 } 1043 1044 break; 1045 case PartitionType.PartitionVert: 1046 DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n8x8L2, n4x4L2); 1047 if (hasCols) 1048 { 1049 DecodeBlock(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2, n4x4L2); 1050 } 1051 1052 break; 1053 case PartitionType.PartitionSplit: 1054 DecodePartition(ref twd, ref cm, miRow, miCol, subsize, n8x8L2); 1055 DecodePartition(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2); 1056 DecodePartition(ref twd, ref cm, miRow + hbs, miCol, subsize, n8x8L2); 1057 DecodePartition(ref twd, ref cm, miRow + hbs, miCol + hbs, subsize, n8x8L2); 1058 break; 1059 default: 1060 Debug.Assert(false, "Invalid partition type"); 1061 break; 1062 } 1063 } 1064 1065 // Update partition context 1066 if (bsize >= BlockSize.Block8x8 && (bsize == BlockSize.Block8x8 || partition != PartitionType.PartitionSplit)) 1067 { 1068 DecUpdatePartitionContext(ref twd, miRow, miCol, subsize, num8x8Wh); 1069 } 1070 } 1071 1072 private static void SetupTokenDecoder( 1073 ArrayPtr<byte> data, 1074 int readSize, 1075 ref InternalErrorInfo errorInfo, 1076 ref Reader r) 1077 { 1078 // Validate the calculated partition length. If the buffer described by the 1079 // partition can't be fully read then throw an error. 1080 if (!ReadIsValid(data, readSize)) 1081 { 1082 errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length"); 1083 } 1084 1085 if (r.Init(data, readSize)) 1086 { 1087 errorInfo.InternalError(CodecErr.CodecMemError, "Failed to allocate bool decoder 1"); 1088 } 1089 } 1090 1091 // Reads the next tile returning its size and adjusting '*data' accordingly 1092 // based on 'isLast'. 1093 private static void GetTileBuffer( 1094 bool isLast, 1095 ref InternalErrorInfo errorInfo, 1096 ref ArrayPtr<byte> data, 1097 ref TileBuffer buf) 1098 { 1099 int size; 1100 1101 if (!isLast) 1102 { 1103 if (!ReadIsValid(data, 4)) 1104 { 1105 errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length"); 1106 } 1107 1108 size = BinaryPrimitives.ReadInt32BigEndian(data.AsSpan()); 1109 data = data.Slice(4); 1110 1111 if (size > data.Length) 1112 { 1113 errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile size"); 1114 } 1115 } 1116 else 1117 { 1118 size = data.Length; 1119 } 1120 1121 buf.Data = data; 1122 buf.Size = size; 1123 1124 data = data.Slice(size); 1125 } 1126 1127 private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr<byte> data, int tileCols, ref Array64<TileBuffer> tileBuffers) 1128 { 1129 int c; 1130 1131 for (c = 0; c < tileCols; ++c) 1132 { 1133 bool isLast = c == tileCols - 1; 1134 ref TileBuffer buf = ref tileBuffers[c]; 1135 buf.Col = c; 1136 GetTileBuffer(isLast, ref cm.Error, ref data, ref buf); 1137 } 1138 } 1139 1140 private static void GetTileBuffers( 1141 ref Vp9Common cm, 1142 ArrayPtr<byte> data, 1143 int tileCols, 1144 int tileRows, 1145 ref Array4<Array64<TileBuffer>> tileBuffers) 1146 { 1147 int r, c; 1148 1149 for (r = 0; r < tileRows; ++r) 1150 { 1151 for (c = 0; c < tileCols; ++c) 1152 { 1153 bool isLast = (r == tileRows - 1) && (c == tileCols - 1); 1154 ref TileBuffer buf = ref tileBuffers[r][c]; 1155 GetTileBuffer(isLast, ref cm.Error, ref data, ref buf); 1156 } 1157 } 1158 } 1159 1160 public static unsafe ArrayPtr<byte> DecodeTiles(ref Vp9Common cm, ArrayPtr<byte> data) 1161 { 1162 int alignedCols = TileInfo.MiColsAlignedToSb(cm.MiCols); 1163 int tileCols = 1 << cm.Log2TileCols; 1164 int tileRows = 1 << cm.Log2TileRows; 1165 Array4<Array64<TileBuffer>> tileBuffers = new(); 1166 int tileRow, tileCol; 1167 int miRow, miCol; 1168 1169 Debug.Assert(tileRows <= 4); 1170 Debug.Assert(tileCols <= (1 << 6)); 1171 1172 // Note: this memset assumes above_context[0], [1] and [2] 1173 // are allocated as part of the same buffer. 1174 MemoryUtil.Fill(cm.AboveContext.ToPointer(), (sbyte)0, Constants.MaxMbPlane * 2 * alignedCols); 1175 MemoryUtil.Fill(cm.AboveSegContext.ToPointer(), (sbyte)0, alignedCols); 1176 1177 LoopFilter.ResetLfm(ref cm); 1178 1179 GetTileBuffers(ref cm, data, tileCols, tileRows, ref tileBuffers); 1180 // Load all tile information into tile_data. 1181 for (tileRow = 0; tileRow < tileRows; ++tileRow) 1182 { 1183 for (tileCol = 0; tileCol < tileCols; ++tileCol) 1184 { 1185 ref TileBuffer buf = ref tileBuffers[tileRow][tileCol]; 1186 ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + tileCol]; 1187 tileData.Xd = cm.Mb; 1188 tileData.Xd.Corrupted = false; 1189 tileData.Xd.Counts = cm.Counts; 1190 tileData.Dqcoeff = new Array32<Array32<int>>(); 1191 tileData.Xd.Tile.Init(ref cm, tileRow, tileCol); 1192 SetupTokenDecoder(buf.Data, buf.Size, ref cm.Error, ref tileData.BitReader); 1193 cm.InitMacroBlockD(ref tileData.Xd, new ArrayPtr<int>(ref tileData.Dqcoeff[0][0], 32 * 32)); 1194 } 1195 } 1196 1197 for (tileRow = 0; tileRow < tileRows; ++tileRow) 1198 { 1199 TileInfo tile = new(); 1200 tile.SetRow(ref cm, tileRow); 1201 for (miRow = tile.MiRowStart; miRow < tile.MiRowEnd; miRow += Constants.MiBlockSize) 1202 { 1203 for (tileCol = 0; tileCol < tileCols; ++tileCol) 1204 { 1205 int col = tileCol; 1206 ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + col]; 1207 tile.SetCol(ref cm, col); 1208 tileData.Xd.LeftContext = new Array3<Array16<sbyte>>(); 1209 tileData.Xd.LeftSegContext = new Array8<sbyte>(); 1210 for (miCol = tile.MiColStart; miCol < tile.MiColEnd; miCol += Constants.MiBlockSize) 1211 { 1212 DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64x64, 4); 1213 } 1214 cm.Mb.Corrupted |= tileData.Xd.Corrupted; 1215 if (cm.Mb.Corrupted) 1216 { 1217 cm.Error.InternalError(CodecErr.CodecCorruptFrame, "Failed to decode tile data"); 1218 } 1219 } 1220 } 1221 } 1222 1223 // Get last tile data. 1224 return cm.TileWorkerData[tileCols * tileRows - 1].BitReader.FindEnd(); 1225 } 1226 1227 private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm, ref Array64<TileBuffer> tileBuffers) 1228 { 1229 ref TileInfo tile = ref tileData.Xd.Tile; 1230 int finalCol = (1 << cm.Log2TileCols) - 1; 1231 ArrayPtr<byte> bitReaderEnd = ArrayPtr<byte>.Null; 1232 1233 int n = tileData.BufStart; 1234 1235 tileData.Xd.Corrupted = false; 1236 1237 do 1238 { 1239 ref TileBuffer buf = ref tileBuffers[n]; 1240 1241 Debug.Assert(cm.Log2TileRows == 0); 1242 tileData.Dqcoeff = new Array32<Array32<int>>(); 1243 tile.Init(ref cm, 0, buf.Col); 1244 SetupTokenDecoder(buf.Data, buf.Size, ref tileData.ErrorInfo, ref tileData.BitReader); 1245 cm.InitMacroBlockD(ref tileData.Xd, new ArrayPtr<int>(ref tileData.Dqcoeff[0][0], 32 * 32)); 1246 tileData.Xd.ErrorInfo = new Ptr<InternalErrorInfo>(ref tileData.ErrorInfo); 1247 1248 for (int miRow = tile.MiRowStart; miRow < tile.MiRowEnd; miRow += Constants.MiBlockSize) 1249 { 1250 tileData.Xd.LeftContext = new Array3<Array16<sbyte>>(); 1251 tileData.Xd.LeftSegContext = new Array8<sbyte>(); 1252 for (int miCol = tile.MiColStart; miCol < tile.MiColEnd; miCol += Constants.MiBlockSize) 1253 { 1254 DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64x64, 4); 1255 } 1256 } 1257 1258 if (buf.Col == finalCol) 1259 { 1260 bitReaderEnd = tileData.BitReader.FindEnd(); 1261 } 1262 } while (!tileData.Xd.Corrupted && ++n <= tileData.BufEnd); 1263 1264 tileData.DataEnd = bitReaderEnd; 1265 1266 return !tileData.Xd.Corrupted; 1267 } 1268 1269 public static ArrayPtr<byte> DecodeTilesMt(ref Vp9Common cm, ArrayPtr<byte> data, int maxThreads) 1270 { 1271 ArrayPtr<byte> bitReaderEnd = ArrayPtr<byte>.Null; 1272 1273 int tileCols = 1 << cm.Log2TileCols; 1274 int tileRows = 1 << cm.Log2TileRows; 1275 int totalTiles = tileCols * tileRows; 1276 int numWorkers = Math.Min(maxThreads, tileCols); 1277 int n; 1278 1279 Debug.Assert(tileCols <= (1 << 6)); 1280 Debug.Assert(tileRows == 1); 1281 1282 cm.AboveContext.AsSpan().Clear(); 1283 cm.AboveSegContext.AsSpan().Clear(); 1284 1285 for (n = 0; n < numWorkers; ++n) 1286 { 1287 ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; 1288 1289 tileData.Xd = cm.Mb; 1290 tileData.Xd.Counts = new Ptr<Vp9BackwardUpdates>(ref tileData.Counts); 1291 tileData.Counts = new Vp9BackwardUpdates(); 1292 } 1293 1294 Array64<TileBuffer> tileBuffers = new(); 1295 1296 GetTileBuffers(ref cm, data, tileCols, ref tileBuffers); 1297 1298 tileBuffers.AsSpan()[..tileCols].Sort(CompareTileBuffers); 1299 1300 if (numWorkers == tileCols) 1301 { 1302 TileBuffer largest = tileBuffers[0]; 1303 Span<TileBuffer> buffers = tileBuffers.AsSpan(); 1304 buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]); 1305 tileBuffers[tileCols - 1] = largest; 1306 } 1307 else 1308 { 1309 int start = 0, end = tileCols - 2; 1310 TileBuffer tmp; 1311 1312 // Interleave the tiles to distribute the load between threads, assuming a 1313 // larger tile implies it is more difficult to decode. 1314 while (start < end) 1315 { 1316 tmp = tileBuffers[start]; 1317 tileBuffers[start] = tileBuffers[end]; 1318 tileBuffers[end] = tmp; 1319 start += 2; 1320 end -= 2; 1321 } 1322 } 1323 1324 int baseVal = tileCols / numWorkers; 1325 int remain = tileCols % numWorkers; 1326 int bufStart = 0; 1327 1328 for (n = 0; n < numWorkers; ++n) 1329 { 1330 int count = baseVal + (remain + n) / numWorkers; 1331 ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; 1332 1333 tileData.BufStart = bufStart; 1334 tileData.BufEnd = bufStart + count - 1; 1335 tileData.DataEnd = data.Slice(data.Length); 1336 bufStart += count; 1337 } 1338 1339 Ptr<Vp9Common> cmPtr = new(ref cm); 1340 1341 Parallel.For(0, numWorkers, n => 1342 { 1343 ref TileWorkerData tileData = ref cmPtr.Value.TileWorkerData[n + totalTiles]; 1344 1345 if (!DecodeTileCol(ref tileData, ref cmPtr.Value, ref tileBuffers)) 1346 { 1347 cmPtr.Value.Mb.Corrupted = true; 1348 } 1349 }); 1350 1351 for (; n > 0; --n) 1352 { 1353 if (bitReaderEnd.IsNull) 1354 { 1355 ref TileWorkerData tileData = ref cm.TileWorkerData[n - 1 + totalTiles]; 1356 bitReaderEnd = tileData.DataEnd; 1357 } 1358 } 1359 1360 for (n = 0; n < numWorkers; ++n) 1361 { 1362 ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; 1363 AccumulateFrameCounts(ref cm.Counts.Value, ref tileData.Counts); 1364 } 1365 1366 Debug.Assert(!bitReaderEnd.IsNull || cm.Mb.Corrupted); 1367 1368 return bitReaderEnd; 1369 } 1370 1371 private static int CompareTileBuffers(TileBuffer bufA, TileBuffer bufB) 1372 { 1373 return (bufA.Size < bufB.Size ? 1 : 0) - (bufA.Size > bufB.Size ? 1 : 0); 1374 } 1375 1376 private static void AccumulateFrameCounts(ref Vp9BackwardUpdates accum, ref Vp9BackwardUpdates counts) 1377 { 1378 Span<uint> a = MemoryMarshal.Cast<Vp9BackwardUpdates, uint>(MemoryMarshal.CreateSpan(ref accum, 1)); 1379 Span<uint> c = MemoryMarshal.Cast<Vp9BackwardUpdates, uint>(MemoryMarshal.CreateSpan(ref counts, 1)); 1380 1381 for (int i = 0; i < a.Length; i++) 1382 { 1383 a[i] += c[i]; 1384 } 1385 } 1386 } 1387 }