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[] { new ConvolveFn[] { ConvolveCopy, ConvolveAvg, }, new ConvolveFn[] { Convolve8Vert, Convolve8AvgVert, }, }, new[] { new ConvolveFn[] { Convolve8Horiz, Convolve8AvgHoriz, }, new ConvolveFn[] { Convolve8, Convolve8Avg, }, }, }; private static readonly unsafe ConvolveFn[][][] _predictX16 = { new[] { new ConvolveFn[] { ScaledVert, ScaledAvgVert, }, new ConvolveFn[] { ScaledVert, ScaledAvgVert, }, }, new[] { new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, }, }; private static readonly unsafe ConvolveFn[][][] _predictY16 = { new[] { new ConvolveFn[] { ScaledHoriz, ScaledAvgHoriz, }, new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, }, new[] { new ConvolveFn[] { ScaledHoriz, ScaledAvgHoriz, }, new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, }, }; private static readonly unsafe ConvolveFn[][][] _predict = { new[] { new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, }, new[] { new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, new ConvolveFn[] { Scaled2D, ScaledAvg2D, }, }, }; private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16Y16 = { new[] { new HighbdConvolveFn[] { HighbdConvolveCopy, HighbdConvolveAvg, }, new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert, }, }, new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, }, }; private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16 = { new[] { new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert, }, new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert, }, }, new[] { new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, }, }; private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictY16 = { new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, }, new[] { new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz, }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, }, }; private static readonly unsafe HighbdConvolveFn[][][] _highbdPredict = { new[] { new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg, }, }, new[] { 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 readonly int ScaleValueX(int val) { return IsScaled() ? ScaledX(val) : val; } public readonly int ScaleValueY(int val) { return IsScaled() ? ScaledY(val) : val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly 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 readonly 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 readonly int ScaledX(int val) { return (int)((long)val * XScaleFP >> RefScaleShift); } private readonly 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() { Row = ScaledY(mv.Row) + yOffQ4, Col = ScaledX(mv.Col) + xOffQ4, }; return res; } public readonly bool IsValidScale() { return XScaleFP != RefInvalidScale && YScaleFP != RefInvalidScale; } public readonly 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); } } }