mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-19 11:56:30 +02:00
Implement VP9 loop filtering (#550)
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>
This commit is contained in:
parent
920933bc9f
commit
f91cd05260
79 changed files with 11343 additions and 3036 deletions
|
@ -1,4 +1,4 @@
|
|||
using Ryujinx.Graphics.Nvdec.Vp9.Types;
|
||||
using Ryujinx.Graphics.Nvdec.Vp9.Types;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||
|
@ -13,7 +13,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
// left of the entries corresponding to real macroblocks.
|
||||
// The prediction flags in these dummy entries are initialized to 0.
|
||||
if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull)
|
||||
{ // both edges available
|
||||
{
|
||||
// both edges available
|
||||
if (!xd.AboveMi.Value.HasSecondRef() && !xd.LeftMi.Value.HasSecondRef())
|
||||
{
|
||||
// Neither edge uses comp pred (0/1)
|
||||
|
@ -23,12 +24,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
else if (!xd.AboveMi.Value.HasSecondRef())
|
||||
{
|
||||
// One of two edges uses comp pred (2/3)
|
||||
ctx = 2 + (xd.AboveMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.AboveMi.Value.IsInterBlock() ? 1 : 0);
|
||||
ctx = 2 + (xd.AboveMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.AboveMi.Value.IsInterBlock()
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
else if (!xd.LeftMi.Value.HasSecondRef())
|
||||
{
|
||||
// One of two edges uses comp pred (2/3)
|
||||
ctx = 2 + (xd.LeftMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.LeftMi.Value.IsInterBlock() ? 1 : 0);
|
||||
ctx = 2 +
|
||||
(xd.LeftMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.LeftMi.Value.IsInterBlock() ? 1 : 0);
|
||||
}
|
||||
else // Both edges use comp pred (4)
|
||||
{
|
||||
|
@ -36,7 +40,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull)
|
||||
{ // One edge available
|
||||
{
|
||||
// One edge available
|
||||
ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value;
|
||||
|
||||
if (!edgeMi.HasSecondRef())
|
||||
|
@ -51,11 +56,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else
|
||||
{ // No edges available (1)
|
||||
{
|
||||
// No edges available (1)
|
||||
ctx = 1;
|
||||
}
|
||||
Debug.Assert(ctx >= 0 && ctx < Constants.CompInterContexts);
|
||||
|
||||
Debug.Assert(ctx >= 0 && ctx < Constants.CompInterContexts);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
@ -71,29 +77,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
int varRefIdx = fixRefIdx == 0 ? 1 : 0;
|
||||
|
||||
if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull)
|
||||
{ // Both edges available
|
||||
{
|
||||
// Both edges available
|
||||
bool aboveIntra = !xd.AboveMi.Value.IsInterBlock();
|
||||
bool leftIntra = !xd.LeftMi.Value.IsInterBlock();
|
||||
|
||||
if (aboveIntra && leftIntra)
|
||||
{ // Intra/Intra (2)
|
||||
{
|
||||
// Intra/Intra (2)
|
||||
predContext = 2;
|
||||
}
|
||||
else if (aboveIntra || leftIntra)
|
||||
{ // Intra/Inter
|
||||
{
|
||||
// Intra/Inter
|
||||
ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value;
|
||||
|
||||
if (!edgeMi.HasSecondRef()) // single pred (1/3)
|
||||
{
|
||||
predContext = 1 + 2 * (edgeMi.RefFrame[0] != cm.CompVarRef[1] ? 1 : 0);
|
||||
predContext = 1 + (2 * (edgeMi.RefFrame[0] != cm.CompVarRef[1] ? 1 : 0));
|
||||
}
|
||||
else // Comp pred (1/3)
|
||||
{
|
||||
predContext = 1 + 2 * (edgeMi.RefFrame[varRefIdx] != cm.CompVarRef[1] ? 1 : 0);
|
||||
predContext = 1 + (2 * (edgeMi.RefFrame[varRefIdx] != cm.CompVarRef[1] ? 1 : 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Inter/Inter
|
||||
{
|
||||
// Inter/Inter
|
||||
bool lSg = !xd.LeftMi.Value.HasSecondRef();
|
||||
bool aSg = !xd.AboveMi.Value.HasSecondRef();
|
||||
sbyte vrfa = aSg ? xd.AboveMi.Value.RefFrame[0] : xd.AboveMi.Value.RefFrame[varRefIdx];
|
||||
|
@ -104,7 +114,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
predContext = 0;
|
||||
}
|
||||
else if (lSg && aSg)
|
||||
{ // Single/Single
|
||||
{
|
||||
// Single/Single
|
||||
if ((vrfa == cm.CompFixedRef && vrfl == cm.CompVarRef[0]) ||
|
||||
(vrfl == cm.CompFixedRef && vrfa == cm.CompVarRef[0]))
|
||||
{
|
||||
|
@ -120,7 +131,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else if (lSg || aSg)
|
||||
{ // Single/Comp
|
||||
{
|
||||
// Single/Comp
|
||||
sbyte vrfc = lSg ? vrfa : vrfl;
|
||||
sbyte rfs = aSg ? vrfa : vrfl;
|
||||
if (vrfc == cm.CompVarRef[1] && rfs != cm.CompVarRef[1])
|
||||
|
@ -137,7 +149,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else if (vrfa == vrfl)
|
||||
{ // Comp/Comp
|
||||
{
|
||||
// Comp/Comp
|
||||
predContext = 4;
|
||||
}
|
||||
else
|
||||
|
@ -147,7 +160,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull)
|
||||
{ // One edge available
|
||||
{
|
||||
// One edge available
|
||||
ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value;
|
||||
|
||||
if (!edgeMi.IsInterBlock())
|
||||
|
@ -167,11 +181,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
}
|
||||
else
|
||||
{ // No edges available (2)
|
||||
{
|
||||
// No edges available (2)
|
||||
predContext = 2;
|
||||
}
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
return predContext;
|
||||
}
|
||||
|
||||
|
@ -183,16 +198,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
// left of the entries corresponding to real macroblocks.
|
||||
// The prediction flags in these dummy entries are initialized to 0.
|
||||
if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull)
|
||||
{ // Both edges available
|
||||
{
|
||||
// Both edges available
|
||||
bool aboveIntra = !xd.AboveMi.Value.IsInterBlock();
|
||||
bool leftIntra = !xd.LeftMi.Value.IsInterBlock();
|
||||
|
||||
if (aboveIntra && leftIntra)
|
||||
{ // Intra/Intra
|
||||
{
|
||||
// Intra/Intra
|
||||
predContext = 2;
|
||||
}
|
||||
else if (aboveIntra || leftIntra)
|
||||
{ // Intra/Inter or Inter/Intra
|
||||
{
|
||||
// Intra/Inter or Inter/Intra
|
||||
ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value;
|
||||
if (!edgeMi.HasSecondRef())
|
||||
{
|
||||
|
@ -201,11 +219,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
else
|
||||
{
|
||||
predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame ||
|
||||
edgeMi.RefFrame[1] == Constants.LastFrame ? 1 : 0);
|
||||
edgeMi.RefFrame[1] == Constants.LastFrame
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Inter/Inter
|
||||
{
|
||||
// Inter/Inter
|
||||
bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef();
|
||||
bool leftHasSecond = xd.LeftMi.Value.HasSecondRef();
|
||||
sbyte above0 = xd.AboveMi.Value.RefFrame[0];
|
||||
|
@ -216,7 +237,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
if (aboveHasSecond && leftHasSecond)
|
||||
{
|
||||
predContext = 1 + (above0 == Constants.LastFrame || above1 == Constants.LastFrame ||
|
||||
left0 == Constants.LastFrame || left1 == Constants.LastFrame ? 1 : 0);
|
||||
left0 == Constants.LastFrame || left1 == Constants.LastFrame
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
else if (aboveHasSecond || leftHasSecond)
|
||||
{
|
||||
|
@ -230,24 +253,28 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
else
|
||||
{
|
||||
predContext = (crf1 == Constants.LastFrame || crf2 == Constants.LastFrame ? 1 : 0);
|
||||
predContext = crf1 == Constants.LastFrame || crf2 == Constants.LastFrame ? 1 : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
predContext = 2 * (above0 == Constants.LastFrame ? 1 : 0) + 2 * (left0 == Constants.LastFrame ? 1 : 0);
|
||||
predContext = (2 * (above0 == Constants.LastFrame ? 1 : 0)) +
|
||||
(2 * (left0 == Constants.LastFrame ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull)
|
||||
{ // One edge available
|
||||
{
|
||||
// One edge available
|
||||
ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value;
|
||||
if (!edgeMi.IsInterBlock())
|
||||
{ // Intra
|
||||
{
|
||||
// Intra
|
||||
predContext = 2;
|
||||
}
|
||||
else
|
||||
{ // Inter
|
||||
{
|
||||
// Inter
|
||||
if (!edgeMi.HasSecondRef())
|
||||
{
|
||||
predContext = 4 * (edgeMi.RefFrame[0] == Constants.LastFrame ? 1 : 0);
|
||||
|
@ -255,16 +282,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
else
|
||||
{
|
||||
predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame ||
|
||||
edgeMi.RefFrame[1] == Constants.LastFrame ? 1 : 0);
|
||||
edgeMi.RefFrame[1] == Constants.LastFrame
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // No edges available
|
||||
{
|
||||
// No edges available
|
||||
predContext = 2;
|
||||
}
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
return predContext;
|
||||
}
|
||||
|
||||
|
@ -277,16 +307,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
// left of the entries corresponding to real macroblocks.
|
||||
// The prediction flags in these dummy entries are initialized to 0.
|
||||
if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull)
|
||||
{ // Both edges available
|
||||
{
|
||||
// Both edges available
|
||||
bool aboveIntra = !xd.AboveMi.Value.IsInterBlock();
|
||||
bool leftIntra = !xd.LeftMi.Value.IsInterBlock();
|
||||
|
||||
if (aboveIntra && leftIntra)
|
||||
{ // Intra/Intra
|
||||
{
|
||||
// Intra/Intra
|
||||
predContext = 2;
|
||||
}
|
||||
else if (aboveIntra || leftIntra)
|
||||
{ // Intra/Inter or Inter/Intra
|
||||
{
|
||||
// Intra/Inter or Inter/Intra
|
||||
ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value;
|
||||
if (!edgeMi.HasSecondRef())
|
||||
{
|
||||
|
@ -301,12 +334,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
else
|
||||
{
|
||||
predContext = 1 + 2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ||
|
||||
edgeMi.RefFrame[1] == Constants.GoldenFrame ? 1 : 0);
|
||||
predContext = 1 + (2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ||
|
||||
edgeMi.RefFrame[1] == Constants.GoldenFrame
|
||||
? 1
|
||||
: 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Inter/Inter
|
||||
{
|
||||
// Inter/Inter
|
||||
bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef();
|
||||
bool leftHasSecond = xd.LeftMi.Value.HasSecondRef();
|
||||
sbyte above0 = xd.AboveMi.Value.RefFrame[0];
|
||||
|
@ -319,7 +355,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
if (above0 == left0 && above1 == left1)
|
||||
{
|
||||
predContext = 3 * (above0 == Constants.GoldenFrame || above1 == Constants.GoldenFrame ||
|
||||
left0 == Constants.GoldenFrame || left1 == Constants.GoldenFrame ? 1 : 0);
|
||||
left0 == Constants.GoldenFrame || left1 == Constants.GoldenFrame
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -342,7 +380,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
else
|
||||
{
|
||||
predContext = 1 + 2 * (crf1 == Constants.GoldenFrame || crf2 == Constants.GoldenFrame ? 1 : 0);
|
||||
predContext =
|
||||
1 + (2 * (crf1 == Constants.GoldenFrame || crf2 == Constants.GoldenFrame ? 1 : 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -353,18 +392,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
}
|
||||
else if (above0 == Constants.LastFrame || left0 == Constants.LastFrame)
|
||||
{
|
||||
sbyte edge0 = (above0 == Constants.LastFrame) ? left0 : above0;
|
||||
sbyte edge0 = above0 == Constants.LastFrame ? left0 : above0;
|
||||
predContext = 4 * (edge0 == Constants.GoldenFrame ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
predContext = 2 * (above0 == Constants.GoldenFrame ? 1 : 0) + 2 * (left0 == Constants.GoldenFrame ? 1 : 0);
|
||||
predContext = (2 * (above0 == Constants.GoldenFrame ? 1 : 0)) +
|
||||
(2 * (left0 == Constants.GoldenFrame ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull)
|
||||
{ // One edge available
|
||||
{
|
||||
// One edge available
|
||||
ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value;
|
||||
|
||||
if (!edgeMi.IsInterBlock() || (edgeMi.RefFrame[0] == Constants.LastFrame && !edgeMi.HasSecondRef()))
|
||||
|
@ -378,16 +419,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||
else
|
||||
{
|
||||
predContext = 3 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ||
|
||||
edgeMi.RefFrame[1] == Constants.GoldenFrame ? 1 : 0);
|
||||
edgeMi.RefFrame[1] == Constants.GoldenFrame
|
||||
? 1
|
||||
: 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // No edges available (2)
|
||||
{
|
||||
// No edges available (2)
|
||||
predContext = 2;
|
||||
}
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
|
||||
Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts);
|
||||
return predContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue