Unmerged PR from OG Ryujinx (#4367). From @gdkchan: > The main goal of this change is porting the loop filtering from libvpx, which should fix the block artifacts on some VP9 videos on games using NVDEC to decode them. In addition to that, there are two other changes: > > - The remaining decoder code required to decode a VP9 video (with headers included) has been added. That was done because it's much better to test the decoder standalone with a video file. I decided to keep that code on the emulator, even if some of it is unused, since it makes standalone testing easier in the future too, and we can include unit tests with video files. > - Large refactoring of both new and existing code to conform with our conding [sic] styles, done by @TSRBerry (thanks!) Some of it has been automated. > > Since we had no loop filtering before, this change will make video decoding slower. That may cause frame drop etc if the decoder is not fast enough in some games. I plan to optimize the decoder more in the future to make up for that, but if possible I'd prefer to not do it as part of this PR, but if the perf loss is too severe I might consider. > > This will need to be tested on games that had the block artifacts, it would be nice to confirm if they match hardware now, and get some before/after screenshots etc. Comment from @Bjorn29512: > Significantly improves the block artifacts in FE: Engage. > > Before: >  > > After: >  --------- Co-authored-by: gdkchan <gab.dark.100@gmail.com> Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
94 lines
3.1 KiB
C#
94 lines
3.1 KiB
C#
using Ryujinx.Common.Memory;
|
|
using Ryujinx.Graphics.Nvdec.Vp9.Common;
|
|
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Ryujinx.Graphics.Nvdec.Vp9
|
|
{
|
|
public static class Prob
|
|
{
|
|
public const int MaxProb = 255;
|
|
|
|
private static byte GetProb(uint num, uint den)
|
|
{
|
|
Debug.Assert(den != 0);
|
|
{
|
|
int p = (int)((((ulong)num * 256) + (den >> 1)) / den);
|
|
// (p > 255) ? 255 : (p < 1) ? 1 : p;
|
|
int clippedProb = p | ((255 - p) >> 23) | (p == 0 ? 1 : 0);
|
|
return (byte)clippedProb;
|
|
}
|
|
}
|
|
|
|
private static byte GetBinaryProb(uint n0, uint n1)
|
|
{
|
|
uint den = n0 + n1;
|
|
if (den == 0)
|
|
{
|
|
return 128;
|
|
}
|
|
|
|
return GetProb(n0, den);
|
|
}
|
|
|
|
/* This function assumes prob1 and prob2 are already within [1,255] range. */
|
|
public static byte WeightedProb(int prob1, int prob2, int factor)
|
|
{
|
|
return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8);
|
|
}
|
|
|
|
public static byte MergeProbs(byte preProb, ref Array2<uint> ct, uint countSat, uint maxUpdateFactor)
|
|
{
|
|
byte prob = GetBinaryProb(ct[0], ct[1]);
|
|
uint count = Math.Min(ct[0] + ct[1], countSat);
|
|
uint factor = maxUpdateFactor * count / countSat;
|
|
return WeightedProb(preProb, prob, (int)factor);
|
|
}
|
|
|
|
// MODE_MV_MAX_UPDATE_FACTOR (128) * count / MODE_MV_COUNT_SAT;
|
|
private static readonly uint[] CountToUpdateFactor =
|
|
{
|
|
0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, 70, 76, 83, 89, 96, 102, 108, 115, 121, 128
|
|
};
|
|
|
|
private const int ModeMvCountSat = 20;
|
|
|
|
public static byte ModeMvMergeProbs(byte preProb, ref Array2<uint> ct)
|
|
{
|
|
uint den = ct[0] + ct[1];
|
|
if (den == 0)
|
|
{
|
|
return preProb;
|
|
}
|
|
|
|
uint count = Math.Min(den, ModeMvCountSat);
|
|
uint factor = CountToUpdateFactor[(int)count];
|
|
byte prob = GetProb(ct[0], den);
|
|
return WeightedProb(preProb, prob, (int)factor);
|
|
}
|
|
|
|
private static uint TreeMergeProbsImpl(
|
|
uint i,
|
|
sbyte[] tree,
|
|
ReadOnlySpan<byte> preProbs,
|
|
ReadOnlySpan<uint> counts,
|
|
Span<byte> probs)
|
|
{
|
|
int l = tree[i];
|
|
uint leftCount = l <= 0 ? counts[-l] : TreeMergeProbsImpl((uint)l, tree, preProbs, counts, probs);
|
|
int r = tree[i + 1];
|
|
uint rightCount = r <= 0 ? counts[-r] : TreeMergeProbsImpl((uint)r, tree, preProbs, counts, probs);
|
|
Array2<uint> ct = new();
|
|
ct[0] = leftCount;
|
|
ct[1] = rightCount;
|
|
probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ref ct);
|
|
return leftCount + rightCount;
|
|
}
|
|
|
|
public static void VpxTreeMergeProbs(sbyte[] tree, ReadOnlySpan<byte> preProbs, ReadOnlySpan<uint> counts,
|
|
Span<byte> probs)
|
|
{
|
|
TreeMergeProbsImpl(0, tree, preProbs, counts, probs);
|
|
}
|
|
}
|
|
} |