using Ryujinx.Common.Memory; using System.Runtime.CompilerServices; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Convolve; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Filter; namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal struct ScaleFactors { private const int RefScaleShift = 14; private const int RefNoScale = (1 << RefScaleShift); private const int RefInvalidScale = -1; private unsafe delegate void ConvolveFn( byte* src, int srcStride, byte* dst, int dstStride, Array8[] filter, int x0Q4, int xStepQ4, int y0Q4, int yStepQ4, int w, int h); private unsafe delegate void HighbdConvolveFn( ushort* src, int srcStride, ushort* dst, int dstStride, Array8[] filter, int x0Q4, int xStepQ4, int y0Q4, int yStepQ4, int w, int h, int bd); private static readonly unsafe ConvolveFn[][][] PredictX16Y16 = new ConvolveFn[][][] { new ConvolveFn[][] { new ConvolveFn[] { ConvolveCopy, ConvolveAvg }, new ConvolveFn[] { Convolve8Vert, Convolve8AvgVert } }, new ConvolveFn[][] { new ConvolveFn[] { Convolve8Horiz, Convolve8AvgHoriz }, new ConvolveFn[] { Convolve8, Convolve8Avg } } }; private static readonly unsafe ConvolveFn[][][] PredictX16 = new ConvolveFn[][][] { new ConvolveFn[][] { new ConvolveFn[] { ScaledVert, ScaledAvgVert }, new ConvolveFn[] { ScaledVert, ScaledAvgVert } }, new ConvolveFn[][] { new ConvolveFn[] { Scaled2D, ScaledAvg2D }, new ConvolveFn[] { Scaled2D, ScaledAvg2D } } }; private static readonly unsafe ConvolveFn[][][] PredictY16 = new ConvolveFn[][][] { new ConvolveFn[][] { new ConvolveFn[] { ScaledHoriz, ScaledAvgHoriz }, new ConvolveFn[] { Scaled2D, ScaledAvg2D } }, new ConvolveFn[][] { new ConvolveFn[] { ScaledHoriz, ScaledAvgHoriz }, new ConvolveFn[] { Scaled2D, ScaledAvg2D } } }; private static readonly unsafe ConvolveFn[][][] Predict = new ConvolveFn[][][] { new ConvolveFn[][] { new ConvolveFn[] { Scaled2D, ScaledAvg2D }, new ConvolveFn[] { Scaled2D, ScaledAvg2D } }, new ConvolveFn[][] { new ConvolveFn[] { Scaled2D, ScaledAvg2D }, new ConvolveFn[] { Scaled2D, ScaledAvg2D } } }; private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16Y16 = new HighbdConvolveFn[][][] { new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolveCopy, HighbdConvolveAvg }, new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert } }, new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } } }; private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16 = new HighbdConvolveFn[][][] { new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert }, new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert } }, new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } } }; private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictY16 = new HighbdConvolveFn[][][] { new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } }, new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } } }; private static readonly unsafe HighbdConvolveFn[][][] HighbdPredict = new HighbdConvolveFn[][][] { new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } }, new HighbdConvolveFn[][] { new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg } } }; public int XScaleFP; // Horizontal fixed point scale factor public int YScaleFP; // Vertical fixed point scale factor public int XStepQ4; public int YStepQ4; public int ScaleValueX(int val) { return IsScaled() ? ScaledX(val) : val; } public int ScaleValueY(int val) { return IsScaled() ? ScaledY(val) : val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void InterPredict( int horiz, int vert, int avg, byte* src, int srcStride, byte* dst, int dstStride, int subpelX, int subpelY, int w, int h, Array8[] kernel, int xs, int ys) { if (XStepQ4 == 16) { if (YStepQ4 == 16) { // No scaling in either direction. PredictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); } else { // No scaling in x direction. Must always scale in the y direction. PredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); } } else { if (YStepQ4 == 16) { // No scaling in the y direction. Must always scale in the x direction. PredictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); } else { // Must always scale in both directions. Predict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void HighbdInterPredict( int horiz, int vert, int avg, ushort* src, int srcStride, ushort* dst, int dstStride, int subpelX, int subpelY, int w, int h, Array8[] kernel, int xs, int ys, int bd) { if (XStepQ4 == 16) { if (YStepQ4 == 16) { // No scaling in either direction. HighbdPredictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); } else { // No scaling in x direction. Must always scale in the y direction. HighbdPredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); } } else { if (YStepQ4 == 16) { // No scaling in the y direction. Must always scale in the x direction. HighbdPredictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); } else { // Must always scale in both directions. HighbdPredict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); } } } private int ScaledX(int val) { return (int)((long)val * XScaleFP >> RefScaleShift); } private int ScaledY(int val) { return (int)((long)val * YScaleFP >> RefScaleShift); } private static int GetFixedPointScaleFactor(int otherSize, int thisSize) { // Calculate scaling factor once for each reference frame // and use fixed point scaling factors in decoding and encoding routines. // Hardware implementations can calculate scale factor in device driver // and use multiplication and shifting on hardware instead of division. return (otherSize << RefScaleShift) / thisSize; } public Mv32 ScaleMv(ref Mv mv, int x, int y) { int xOffQ4 = ScaledX(x << SubpelBits) & SubpelMask; int yOffQ4 = ScaledY(y << SubpelBits) & SubpelMask; Mv32 res = new Mv32() { Row = ScaledY(mv.Row) + yOffQ4, Col = ScaledX(mv.Col) + xOffQ4 }; return res; } public bool IsValidScale() { return XScaleFP != RefInvalidScale && YScaleFP != RefInvalidScale; } public bool IsScaled() { return IsValidScale() && (XScaleFP != RefNoScale || YScaleFP != RefNoScale); } public static bool ValidRefFrameSize(int refWidth, int refHeight, int thisWidth, int thisHeight) { return 2 * thisWidth >= refWidth && 2 * thisHeight >= refHeight && thisWidth <= 16 * refWidth && thisHeight <= 16 * refHeight; } public void SetupScaleFactorsForFrame(int otherW, int otherH, int thisW, int thisH) { if (!ValidRefFrameSize(otherW, otherH, thisW, thisH)) { XScaleFP = RefInvalidScale; YScaleFP = RefInvalidScale; return; } XScaleFP = GetFixedPointScaleFactor(otherW, thisW); YScaleFP = GetFixedPointScaleFactor(otherH, thisH); XStepQ4 = ScaledX(16); YStepQ4 = ScaledY(16); } } }