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:
>
![](https://user-images.githubusercontent.com/110204265/216882414-ec88dbda-7544-4490-8a47-37f074056ae3.png)
> 
> After:
>
![](https://user-images.githubusercontent.com/110204265/216882478-4e81fead-1033-4877-b282-f9cac6d6aa3b.png)

---------

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:
Keaton 2025-02-18 20:59:36 -06:00 committed by GitHub
parent 920933bc9f
commit f91cd05260
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 11343 additions and 3036 deletions

View file

@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public PredictionMode Mode;
public Array2<Mv> Mv; // First, second inter predictor motion vectors
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
public enum BitstreamProfile
{
Profile0,
Profile1,
Profile2,
Profile3,
MaxProfiles
}
}

View file

@ -1,21 +1,21 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum BlockSize
{
Block4x4 = 0,
Block4x8 = 1,
Block8x4 = 2,
Block8x8 = 3,
Block8x16 = 4,
Block16x8 = 5,
Block16x16 = 6,
Block16x32 = 7,
Block32x16 = 8,
Block32x32 = 9,
Block32x64 = 10,
Block64x32 = 11,
Block64x64 = 12,
BlockSizes = 13,
BlockInvalid = BlockSizes,
Block4x4,
Block4x8,
Block8x4,
Block8x8,
Block8x16,
Block16x8,
Block16x16,
Block16x32,
Block32x16,
Block32x32,
Block32x64,
Block64x32,
Block64x64,
BlockSizes,
BlockInvalid = BlockSizes
}
}
}

View file

@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public ArrayPtr<byte> Buf;
public int Stride;
}
}
}

View file

@ -0,0 +1,18 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct BufferPool
{
// Private data associated with the frame buffer callbacks.
public Ptr<InternalFrameBufferList> CbPriv;
// vpx_get_frame_buffer_cb_fn_t get_fb_cb;
// vpx_release_frame_buffer_cb_fn_t release_fb_cb;
public Array12<RefCntBuffer> FrameBufs;
// Frame buffers allocated internally by the codec.
public InternalFrameBufferList IntFrameBuffers;
}
}

View file

@ -5,4 +5,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
KeyFrame = 0,
InterFrame = 1,
}
}
}

View file

@ -23,5 +23,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public ArrayPtr<LoopFilterMask> Lfm;
public int LfmStride;
public void SetDefaultLfDeltas()
{
ModeRefDeltaEnabled = true;
ModeRefDeltaUpdate = true;
RefDeltas[Constants.IntraFrame] = 1;
RefDeltas[Constants.LastFrame] = 0;
RefDeltas[Constants.GoldenFrame] = -1;
RefDeltas[Constants.AltRefFrame] = -1;
ModeDeltas[0] = 0;
ModeDeltas[1] = 0;
}
}
}
}

View file

@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Array64<LoopFilterThresh> Lfthr;
public Array8<Array4<Array2<byte>>> Lvl;
}
}
}

View file

@ -21,4 +21,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public ushort Int4x4Uv;
public Array64<byte> LflY;
}
}
}

View file

@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Array16<byte> HevThr;
#pragma warning restore CS0649
}
}
}

View file

@ -1,4 +1,5 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Video;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
@ -54,7 +55,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Ptr<InternalErrorInfo> ErrorInfo;
public readonly int GetPredContextSegId()
public int GetPredContextSegId()
{
sbyte aboveSip = !AboveMi.IsNull ? AboveMi.Value.SegIdPredicted : (sbyte)0;
sbyte leftSip = !LeftMi.IsNull ? LeftMi.Value.SegIdPredicted : (sbyte)0;
@ -62,15 +63,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return aboveSip + leftSip;
}
public readonly int GetSkipContext()
public int GetSkipContext()
{
int aboveSkip = !AboveMi.IsNull ? AboveMi.Value.Skip : 0;
int leftSkip = !LeftMi.IsNull ? LeftMi.Value.Skip : 0;
return aboveSkip + leftSkip;
}
public readonly int GetPredContextSwitchableInterp()
public int GetPredContextSwitchableInterp()
{
// Note:
// The mode info data structure has a one element border above and to the
@ -83,18 +83,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
return leftType;
}
else if (leftType == Constants.SwitchableFilters)
if (leftType == Constants.SwitchableFilters)
{
return aboveType;
}
else if (aboveType == Constants.SwitchableFilters)
if (aboveType == Constants.SwitchableFilters)
{
return leftType;
}
else
{
return Constants.SwitchableFilters;
}
return Constants.SwitchableFilters;
}
// The mode info data structure has a one element border above and to the
@ -104,20 +104,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// 1 - intra/inter, inter/intra
// 2 - intra/--, --/intra
// 3 - intra/intra
public readonly int GetIntraInterContext()
public int GetIntraInterContext()
{
if (!AboveMi.IsNull && !LeftMi.IsNull)
{ // Both edges available
{
// Both edges available
bool aboveIntra = !AboveMi.Value.IsInterBlock();
bool leftIntra = !LeftMi.Value.IsInterBlock();
return leftIntra && aboveIntra ? 3 : (leftIntra || aboveIntra ? 1 : 0);
return leftIntra && aboveIntra ? 3 : leftIntra || aboveIntra ? 1 : 0;
}
if (!AboveMi.IsNull || !LeftMi.IsNull)
{ // One edge available
{
// One edge available
return 2 * (!(!AboveMi.IsNull ? AboveMi.Value : LeftMi.Value).IsInterBlock() ? 1 : 0);
}
return 0;
}
@ -125,11 +127,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// The mode info data structure has a one element border above and to the
// left of the entries corresponding to real blocks.
// The prediction flags in these dummy entries are initialized to 0.
public readonly int GetTxSizeContext()
public int GetTxSizeContext()
{
int maxTxSize = (int)Luts.MaxTxSizeLookup[(int)Mi[0].Value.SbType];
int aboveCtx = (!AboveMi.IsNull && AboveMi.Value.Skip == 0) ? (int)AboveMi.Value.TxSize : maxTxSize;
int leftCtx = (!LeftMi.IsNull && LeftMi.Value.Skip == 0) ? (int)LeftMi.Value.TxSize : maxTxSize;
int aboveCtx = !AboveMi.IsNull && AboveMi.Value.Skip == 0 ? (int)AboveMi.Value.TxSize : maxTxSize;
int leftCtx = !LeftMi.IsNull && LeftMi.Value.Skip == 0 ? (int)LeftMi.Value.TxSize : maxTxSize;
if (LeftMi.IsNull)
{
leftCtx = aboveCtx;
@ -140,14 +142,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
aboveCtx = leftCtx;
}
return (aboveCtx + leftCtx) > maxTxSize ? 1 : 0;
return aboveCtx + leftCtx > maxTxSize ? 1 : 0;
}
public void SetupBlockPlanes(int ssX, int ssY)
{
int i;
for (i = 0; i < Constants.MaxMbPlane; i++)
for (int i = 0; i < Constants.MaxMbPlane; i++)
{
Plane[i].SubsamplingX = i != 0 ? ssX : 0;
Plane[i].SubsamplingY = i != 0 ? ssY : 0;
@ -158,25 +158,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
int aboveIdx = miCol * 2;
int leftIdx = (miRow * 2) & 15;
int i;
for (i = 0; i < Constants.MaxMbPlane; ++i)
for (int i = 0; i < Constants.MaxMbPlane; ++i)
{
ref MacroBlockDPlane pd = ref Plane[i];
pd.AboveContext = AboveContext[i].Slice(aboveIdx >> pd.SubsamplingX);
pd.LeftContext = new ArrayPtr<sbyte>(ref LeftContext[i][leftIdx >> pd.SubsamplingY], 16 - (leftIdx >> pd.SubsamplingY));
pd.LeftContext = new ArrayPtr<sbyte>(ref LeftContext[i][leftIdx >> pd.SubsamplingY],
16 - (leftIdx >> pd.SubsamplingY));
}
}
internal void SetMiRowCol(ref TileInfo tile, int miRow, int bh, int miCol, int bw, int miRows, int miCols)
{
MbToTopEdge = -((miRow * Constants.MiSize) * 8);
MbToBottomEdge = ((miRows - bh - miRow) * Constants.MiSize) * 8;
MbToLeftEdge = -((miCol * Constants.MiSize) * 8);
MbToRightEdge = ((miCols - bw - miCol) * Constants.MiSize) * 8;
MbToTopEdge = -(miRow * Constants.MiSize * 8);
MbToBottomEdge = (miRows - bh - miRow) * Constants.MiSize * 8;
MbToLeftEdge = -(miCol * Constants.MiSize * 8);
MbToRightEdge = (miCols - bw - miCol) * Constants.MiSize * 8;
// Are edges available for intra prediction?
AboveMi = (miRow != 0) ? Mi[-MiStride] : Ptr<ModeInfo>.Null;
LeftMi = (miCol > tile.MiColStart) ? Mi[-1] : Ptr<ModeInfo>.Null;
AboveMi = miRow != 0 ? Mi[-MiStride] : Ptr<ModeInfo>.Null;
LeftMi = miCol > tile.MiColStart ? Mi[-1] : Ptr<ModeInfo>.Null;
}
public unsafe void DecResetSkipContext()
{
for (int i = 0; i < Constants.MaxMbPlane; i++)
{
ref MacroBlockDPlane pd = ref Plane[i];
MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W);
MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H);
}
}
}
}
}

View file

@ -15,7 +15,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
// Number of 4x4s in current block
public ushort N4W, N4H;
// Log2 of N4W, N4H
public byte N4Wl, N4Hl;
}
}
}

View file

@ -1,4 +1,4 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using System.Diagnostics;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
@ -32,11 +32,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return SbType < BlockSize.Block8x8 ? Bmi[block].Mode : Mode;
}
public readonly TxSize GetUvTxSize(ref MacroBlockDPlane pd)
public TxSize GetUvTxSize(ref MacroBlockDPlane pd)
{
Debug.Assert(SbType < BlockSize.Block8x8 ||
Luts.SsSizeLookup[(int)SbType][pd.SubsamplingX][pd.SubsamplingY] != BlockSize.BlockInvalid);
Luts.SsSizeLookup[(int)SbType][pd.SubsamplingX][pd.SubsamplingY] != BlockSize.BlockInvalid);
return Luts.UvTxsizeLookup[(int)SbType][(int)TxSize][pd.SubsamplingX][pd.SubsamplingY];
}
@ -50,8 +49,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return RefFrame[1] > Constants.IntraFrame;
}
private static readonly int[][] _idxNColumnToSubblock = {
new[] { 1, 2 }, new[] { 1, 3 }, new[] { 3, 2 }, new[] { 3, 3 },
private static readonly int[][] IdxNColumnToSubblock =
{
new[] { 1, 2 }, new[] { 1, 3 }, new[] { 3, 2 }, new[] { 3, 3 }
};
// This function returns either the appropriate sub block or block's mv
@ -59,8 +59,49 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Mv GetSubBlockMv(int whichMv, int searchCol, int blockIdx)
{
return blockIdx >= 0 && SbType < BlockSize.Block8x8
? Bmi[_idxNColumnToSubblock[blockIdx][searchCol == 0 ? 1 : 0]].Mv[whichMv]
? Bmi[IdxNColumnToSubblock[blockIdx][searchCol == 0 ? 1 : 0]].Mv[whichMv]
: Mv[whichMv];
}
public Mv MvPredQ4(int idx)
{
Mv res = new()
{
Row = (short)ReconInter.RoundMvCompQ4(
Bmi[0].Mv[idx].Row + Bmi[1].Mv[idx].Row +
Bmi[2].Mv[idx].Row + Bmi[3].Mv[idx].Row),
Col = (short)ReconInter.RoundMvCompQ4(
Bmi[0].Mv[idx].Col + Bmi[1].Mv[idx].Col +
Bmi[2].Mv[idx].Col + Bmi[3].Mv[idx].Col)
};
return res;
}
public Mv MvPredQ2(int idx, int block0, int block1)
{
Mv res = new()
{
Row = (short)ReconInter.RoundMvCompQ2(
Bmi[block0].Mv[idx].Row +
Bmi[block1].Mv[idx].Row),
Col = (short)ReconInter.RoundMvCompQ2(
Bmi[block0].Mv[idx].Col +
Bmi[block1].Mv[idx].Col)
};
return res;
}
// Performs mv sign inversion if indicated by the reference frame combination.
public Mv ScaleMv(int refr, sbyte thisRefFrame, ref Array4<sbyte> refSignBias)
{
Mv mv = Mv[refr];
if (refSignBias[RefFrame[refr]] != refSignBias[thisRefFrame])
{
mv.Row *= -1;
mv.Col *= -1;
}
return mv;
}
}
}
}

View file

@ -11,4 +11,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
BothIntra = 6,
InvalidCase = 9,
}
}
}

View file

@ -1,4 +1,4 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Video;
using System;
using System.Diagnostics;
@ -12,96 +12,86 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
private static ReadOnlySpan<byte> LogInBase2 => new byte[]
{
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10,
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 10
};
public readonly bool UseMvHp()
public bool UseHp()
{
const int KMvRefThresh = 64; // Threshold for use of high-precision 1/8 mv
return Math.Abs(Row) < KMvRefThresh && Math.Abs(Col) < KMvRefThresh;
const int kMvRefThresh = 64; // Threshold for use of high-precision 1/8 mv
return Math.Abs(Row) < kMvRefThresh && Math.Abs(Col) < kMvRefThresh;
}
public static bool MvJointVertical(MvJointType type)
public static bool JointVertical(MvJointType type)
{
return type == MvJointType.MvJointHzvnz || type == MvJointType.MvJointHnzvnz;
return type == MvJointType.Hzvnz || type == MvJointType.Hnzvnz;
}
public static bool MvJointHorizontal(MvJointType type)
public static bool JointHorizontal(MvJointType type)
{
return type == MvJointType.MvJointHnzvz || type == MvJointType.MvJointHnzvnz;
return type == MvJointType.Hnzvz || type == MvJointType.Hnzvnz;
}
private static int MvClassBase(MvClassType c)
private static int ClassBase(MvClassType c)
{
return c != 0 ? Constants.Class0Size << ((int)c + 2) : 0;
}
private static MvClassType GetMvClass(int z, Ptr<int> offset)
private static MvClassType GetClass(int z, Ptr<int> offset)
{
MvClassType c = (z >= Constants.Class0Size * 4096) ? MvClassType.MvClass10 : (MvClassType)LogInBase2[z >> 3];
MvClassType c = z >= Constants.Class0Size * 4096 ? MvClassType.Class10 : (MvClassType)LogInBase2[z >> 3];
if (!offset.IsNull)
{
offset.Value = z - MvClassBase(c);
offset.Value = z - ClassBase(c);
}
return c;
}
private static void IncMvComponent(int v, ref Vp9BackwardUpdates counts, int comp, int incr, int usehp)
private static void IncComponent(int v, ref Vp9BackwardUpdates counts, int comp, int incr, int usehp)
{
int s, z, c, o = 0, d, e, f;
int o = 0;
Debug.Assert(v != 0); /* Should not be zero */
s = v < 0 ? 1 : 0;
int s = v < 0 ? 1 : 0;
counts.Sign[comp][s] += (uint)incr;
z = (s != 0 ? -v : v) - 1; /* Magnitude - 1 */
int z = (s != 0 ? -v : v) - 1 /* Magnitude - 1 */;
c = (int)GetMvClass(z, new Ptr<int>(ref o));
int c = (int)GetClass(z, new Ptr<int>(ref o));
counts.Classes[comp][c] += (uint)incr;
d = (o >> 3); /* Int mv data */
f = (o >> 1) & 3; /* Fractional pel mv data */
e = (o & 1); /* High precision mv data */
int d = o >> 3 /* Int mv data */;
int f = (o >> 1) & 3 /* Fractional pel mv data */;
int e = o & 1 /* High precision mv data */;
if (c == (int)MvClassType.MvClass0)
if (c == (int)MvClassType.Class0)
{
counts.Class0[comp][d] += (uint)incr;
counts.Class0Fp[comp][d][f] += (uint)incr;
@ -109,11 +99,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
}
else
{
int i;
int b = c + Constants.Class0Bits - 1; // Number of bits
for (i = 0; i < b; ++i)
for (int i = 0; i < b; ++i)
{
counts.Bits[comp][i][((d >> i) & 1)] += (uint)incr;
counts.Bits[comp][i][(d >> i) & 1] += (uint)incr;
}
counts.Fp[comp][f] += (uint)incr;
@ -121,56 +110,56 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
}
}
private readonly MvJointType GetMvJoint()
public MvJointType GetJoint()
{
if (Row == 0)
{
return Col == 0 ? MvJointType.MvJointZero : MvJointType.MvJointHnzvz;
return Col == 0 ? MvJointType.Zero : MvJointType.Hnzvz;
}
return Col == 0 ? MvJointType.MvJointHzvnz : MvJointType.MvJointHnzvnz;
return Col == 0 ? MvJointType.Hzvnz : MvJointType.Hnzvnz;
}
internal readonly void IncMv(Ptr<Vp9BackwardUpdates> counts)
internal void Inc(Ptr<Vp9BackwardUpdates> counts)
{
if (!counts.IsNull)
{
MvJointType j = GetMvJoint();
MvJointType j = GetJoint();
++counts.Value.Joints[(int)j];
if (MvJointVertical(j))
if (JointVertical(j))
{
IncMvComponent(Row, ref counts.Value, 0, 1, 1);
IncComponent(Row, ref counts.Value, 0, 1, 1);
}
if (MvJointHorizontal(j))
if (JointHorizontal(j))
{
IncMvComponent(Col, ref counts.Value, 1, 1, 1);
IncComponent(Col, ref counts.Value, 1, 1, 1);
}
}
}
public void ClampMv(int minCol, int maxCol, int minRow, int maxRow)
public void Clamp(int minCol, int maxCol, int minRow, int maxRow)
{
Col = (short)Math.Clamp(Col, minCol, maxCol);
Row = (short)Math.Clamp(Row, minRow, maxRow);
}
private const int MvBorder = (16 << 3); // Allow 16 pels in 1/8th pel units
private const int Border = 16 << 3; // Allow 16 pels in 1/8th pel units
public void ClampMvRef(ref MacroBlockD xd)
public void ClampRef(ref MacroBlockD xd)
{
ClampMv(
xd.MbToLeftEdge - MvBorder,
xd.MbToRightEdge + MvBorder,
xd.MbToTopEdge - MvBorder,
xd.MbToBottomEdge + MvBorder);
Clamp(
xd.MbToLeftEdge - Border,
xd.MbToRightEdge + Border,
xd.MbToTopEdge - Border,
xd.MbToBottomEdge + Border);
}
public void LowerMvPrecision(bool allowHP)
public void LowerPrecision(bool allowHp)
{
bool useHP = allowHP && UseMvHp();
if (!useHP)
bool useHp = allowHp && UseHp();
if (!useHp)
{
if ((Row & 1) != 0)
{
@ -183,5 +172,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
}
}
}
public bool IsValid()
{
return Row is > Constants.MvLow and < Constants.MvUpp &&
Col is > Constants.MvLow and < Constants.MvUpp;
}
}
}
}

View file

@ -5,4 +5,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public int Row;
public int Col;
}
}
}

View file

@ -2,16 +2,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum MvClassType
{
MvClass0 = 0, /* (0, 2] integer pel */
MvClass1 = 1, /* (2, 4] integer pel */
MvClass2 = 2, /* (4, 8] integer pel */
MvClass3 = 3, /* (8, 16] integer pel */
MvClass4 = 4, /* (16, 32] integer pel */
MvClass5 = 5, /* (32, 64] integer pel */
MvClass6 = 6, /* (64, 128] integer pel */
MvClass7 = 7, /* (128, 256] integer pel */
MvClass8 = 8, /* (256, 512] integer pel */
MvClass9 = 9, /* (512, 1024] integer pel */
MvClass10 = 10, /* (1024,2048] integer pel */
Class0, /* (0, 2] integer pel */
Class1, /* (2, 4] integer pel */
Class2, /* (4, 8] integer pel */
Class3, /* (8, 16] integer pel */
Class4, /* (16, 32] integer pel */
Class5, /* (32, 64] integer pel */
Class6, /* (64, 128] integer pel */
Class7, /* (128, 256] integer pel */
Class8, /* (256, 512] integer pel */
Class9, /* (512, 1024] integer pel */
Class10 /* (1024,2048] integer pel */
}
}
}

View file

@ -2,9 +2,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum MvJointType
{
MvJointZero = 0, /* Zero vector */
MvJointHnzvz = 1, /* Vert zero, hor nonzero */
MvJointHzvnz = 2, /* Hor zero, vert nonzero */
MvJointHnzvnz = 3, /* Both components nonzero */
Zero, /* Zero vector */
Hnzvz, /* Vert zero, hor nonzero */
Hzvnz, /* Hor zero, vert nonzero */
Hnzvnz /* Both components nonzero */
}
}
}

View file

@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public Array2<Mv> Mv;
public Array2<sbyte> RefFrame;
}
}
}

View file

@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
PartitionTypes,
PartitionInvalid = PartitionTypes,
}
}
}

View file

@ -1,9 +1,9 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum PlaneType
{
Y = 0,
Uv = 1,
PlaneTypes,
Y,
Uv,
PlaneTypes
}
}
}

View file

@ -11,4 +11,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
Col = col;
}
}
}
}

View file

@ -1,21 +1,21 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum PredictionMode
{
DcPred = 0, // Average of above and left pixels
VPred = 1, // Vertical
HPred = 2, // Horizontal
D45Pred = 3, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi)
D135Pred = 4, // Directional 135 deg = 180 - 45
D117Pred = 5, // Directional 117 deg = 180 - 63
D153Pred = 6, // Directional 153 deg = 180 - 27
D207Pred = 7, // Directional 207 deg = 180 + 27
D63Pred = 8, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi)
TmPred = 9, // True-motion
NearestMv = 10,
NearMv = 11,
ZeroMv = 12,
NewMv = 13,
MbModeCount = 14,
DcPred, // Average of above and left pixels
VPred, // Vertical
HPred, // Horizontal
D45Pred, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi)
D135Pred, // Directional 135 deg = 180 - 45
D117Pred, // Directional 117 deg = 180 - 63
D153Pred, // Directional 153 deg = 180 - 27
D207Pred, // Directional 207 deg = 180 + 27
D63Pred, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi)
TmPred, // True-motion
NearestMv,
NearMv,
ZeroMv,
NewMv,
MbModeCount
}
}
}

View file

@ -2,7 +2,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct RefBuffer
{
public const int InvalidIdx = -1; // Invalid buffer index.
public int Idx;
public Surface Buf;
public ScaleFactors Sf;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct RefCntBuffer
{
public int RefCount;
public int MiRows;
public int MiCols;
public byte Released;
public VpxCodecFrameBuffer RawFrameBuffer;
public Surface Buf;
}
}

View file

@ -1,10 +1,10 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum ReferenceMode
{
SingleReference = 0,
CompoundReference = 1,
ReferenceModeSelect = 2,
ReferenceModes = 3,
Single,
Compound,
Select,
ReferenceModes
}
}
}

View file

@ -1,4 +1,4 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using System.Runtime.CompilerServices;
using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Convolve;
using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Filter;
@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
internal struct ScaleFactors
{
private const int RefScaleShift = 14;
private const int RefNoScale = (1 << RefScaleShift);
private const int RefNoScale = 1 << RefScaleShift;
private const int RefInvalidScale = -1;
private unsafe delegate void ConvolveFn(
@ -38,255 +38,114 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
int h,
int bd);
private static readonly unsafe ConvolveFn[][][] _predictX16Y16 = {
private static readonly unsafe ConvolveFn[][][] PredictX16Y16 =
{
new[]
{
new ConvolveFn[]
{
ConvolveCopy,
ConvolveAvg,
},
new ConvolveFn[]
{
Convolve8Vert,
Convolve8AvgVert,
},
new ConvolveFn[] { ConvolveCopy, ConvolveAvg },
new ConvolveFn[] { Convolve8Vert, Convolve8AvgVert }
},
new[]
{
new ConvolveFn[]
{
Convolve8Horiz,
Convolve8AvgHoriz,
},
new ConvolveFn[]
{
Convolve8,
Convolve8Avg,
},
},
new ConvolveFn[] { Convolve8Horiz, Convolve8AvgHoriz },
new ConvolveFn[] { Convolve8, Convolve8Avg }
}
};
private static readonly unsafe ConvolveFn[][][] _predictX16 = {
private static readonly unsafe ConvolveFn[][][] PredictX16 =
{
new[]
{
new ConvolveFn[]
{
ScaledVert,
ScaledAvgVert,
},
new ConvolveFn[]
{
ScaledVert,
ScaledAvgVert,
},
},
new[]
{
new ConvolveFn[]
{
Scaled2D,
ScaledAvg2D,
},
new ConvolveFn[]
{
Scaled2D,
ScaledAvg2D,
},
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[][][] 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 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 = {
private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16Y16 =
{
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolveCopy,
HighbdConvolveAvg,
},
new HighbdConvolveFn[]
{
HighbdConvolve8Vert,
HighbdConvolve8AvgVert,
},
new HighbdConvolveFn[] { HighbdConvolveCopy, HighbdConvolveAvg },
new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert }
},
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8Horiz,
HighbdConvolve8AvgHoriz,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
},
new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz },
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }
}
};
private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16 = {
private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictX16 =
{
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8Vert,
HighbdConvolve8AvgVert,
},
new HighbdConvolveFn[]
{
HighbdConvolve8Vert,
HighbdConvolve8AvgVert,
},
new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert },
new HighbdConvolveFn[] { HighbdConvolve8Vert, HighbdConvolve8AvgVert }
},
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
},
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg },
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }
}
};
private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictY16 = {
private static readonly unsafe HighbdConvolveFn[][][] HighbdPredictY16 =
{
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8Horiz,
HighbdConvolve8AvgHoriz,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz },
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }
},
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8Horiz,
HighbdConvolve8AvgHoriz,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
},
new HighbdConvolveFn[] { HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz },
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }
}
};
private static readonly unsafe HighbdConvolveFn[][][] _highbdPredict = {
private static readonly unsafe HighbdConvolveFn[][][] HighbdPredict =
{
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg },
new HighbdConvolveFn[] { HighbdConvolve8, HighbdConvolve8Avg }
},
new[]
{
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
new HighbdConvolveFn[]
{
HighbdConvolve8,
HighbdConvolve8Avg,
},
},
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 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)
public int ScaleValueX(int val)
{
return IsScaled() ? ScaledX(val) : val;
}
public readonly int ScaleValueY(int val)
public int ScaleValueY(int val)
{
return IsScaled() ? ScaledY(val) : val;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly unsafe void InterPredict(
public unsafe void InterPredict(
int horiz,
int vert,
int avg,
@ -307,12 +166,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
if (YStepQ4 == 16)
{
// No scaling in either direction.
_predictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h);
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);
PredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w,
h);
}
}
else
@ -320,18 +181,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
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);
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);
Predict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly unsafe void HighbdInterPredict(
public unsafe void HighbdInterPredict(
int horiz,
int vert,
int avg,
@ -353,12 +215,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
if (YStepQ4 == 16)
{
// No scaling in either direction.
_highbdPredictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd);
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);
HighbdPredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys,
w, h, bd);
}
}
else
@ -366,24 +230,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
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);
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);
HighbdPredict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w,
h, bd);
}
}
}
private readonly int ScaledX(int val)
private int ScaledX(int val)
{
return (int)((long)val * XScaleFP >> RefScaleShift);
return (int)(((long)val * XScaleFp) >> RefScaleShift);
}
private readonly int ScaledY(int val)
private int ScaledY(int val)
{
return (int)((long)val * YScaleFP >> RefScaleShift);
return (int)(((long)val * YScaleFp) >> RefScaleShift);
}
private static int GetFixedPointScaleFactor(int otherSize, int thisSize)
@ -399,23 +265,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
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,
};
Mv32 res = new() { Row = ScaledY(mv.Row) + yOffQ4, Col = ScaledX(mv.Col) + xOffQ4 };
return res;
}
public readonly bool IsValidScale()
public bool IsValidScale()
{
return XScaleFP != RefInvalidScale && YScaleFP != RefInvalidScale;
return XScaleFp != RefInvalidScale && YScaleFp != RefInvalidScale;
}
public readonly bool IsScaled()
public bool IsScaled()
{
return IsValidScale() && (XScaleFP != RefNoScale || YScaleFP != RefNoScale);
return IsValidScale() && (XScaleFp != RefNoScale || YScaleFp != RefNoScale);
}
public static bool ValidRefFrameSize(int refWidth, int refHeight, int thisWidth, int thisHeight)
@ -430,16 +291,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
if (!ValidRefFrameSize(otherW, otherH, thisW, thisH))
{
XScaleFP = RefInvalidScale;
YScaleFP = RefInvalidScale;
XScaleFp = RefInvalidScale;
YScaleFp = RefInvalidScale;
return;
}
XScaleFP = GetFixedPointScaleFactor(otherW, thisW);
YScaleFP = GetFixedPointScaleFactor(otherH, thisH);
XScaleFp = GetFixedPointScaleFactor(otherW, thisW);
YScaleFp = GetFixedPointScaleFactor(otherH, thisH);
XStepQ4 = ScaledX(16);
YStepQ4 = ScaledY(16);
}
}
}
}

View file

@ -1,11 +1,11 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum SegLvlFeatures
{
SegLvlAltQ = 0, // Use alternate Quantizer ....
SegLvlAltLf = 1, // Use alternate loop filter value...
SegLvlRefFrame = 2, // Optional Segment reference frame
SegLvlSkip = 3, // Optional Segment (0,0) + skip mode
SegLvlMax = 4, // Number of features supported
AltQ, // Use alternate Quantizer ....
AltLf, // Use alternate loop filter value...
RefFrame, // Optional Segment reference frame
Skip, // Optional Segment (0,0) + skip mode
Max // Number of features supported
}
}
}

View file

@ -1,4 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Video;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
@ -6,8 +8,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct Segmentation
{
private static readonly int[] _segFeatureDataSigned = { 1, 1, 0, 0 };
private static readonly int[] _segFeatureDataMax = { QuantCommon.MaxQ, Vp9.LoopFilter.MaxLoopFilter, 3, 0 };
public const int SegmentDeltadata = 0;
public const int SegmentAbsdata = 1;
public const int MaxSegments = 8;
public const int SegTreeProbs = MaxSegments - 1;
public const int PredictionProbs = 3;
private static readonly int[] SegFeatureDataSigned = { 1, 1, 0, 0 };
private static readonly int[] SegFeatureDataMax = { QuantCommon.MaxQ, Vp9.LoopFilter.MaxLoopFilter, 3, 0 };
public bool Enabled;
public bool UpdateMap;
@ -38,21 +48,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
internal static int FeatureDataMax(SegLvlFeatures featureId)
{
return _segFeatureDataMax[(int)featureId];
return SegFeatureDataMax[(int)featureId];
}
internal static int IsSegFeatureSigned(SegLvlFeatures featureId)
{
return _segFeatureDataSigned[(int)featureId];
return SegFeatureDataSigned[(int)featureId];
}
internal void SetSegData(int segmentId, SegLvlFeatures featureId, int segData)
{
Debug.Assert(segData <= _segFeatureDataMax[(int)featureId]);
Debug.Assert(segData <= SegFeatureDataMax[(int)featureId]);
if (segData < 0)
{
Debug.Assert(_segFeatureDataSigned[(int)featureId] != 0);
Debug.Assert(-segData <= _segFeatureDataMax[(int)featureId]);
Debug.Assert(SegFeatureDataSigned[(int)featureId] != 0);
Debug.Assert(-segData <= SegFeatureDataMax[(int)featureId]);
}
FeatureData[segmentId][(int)featureId] = (short)segData;
@ -67,5 +77,88 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
return FeatureData[segmentId][(int)featureId];
}
public int GetQIndex(int segmentId, int baseQIndex)
{
if (IsSegFeatureActive(segmentId, SegLvlFeatures.AltQ) != 0)
{
int data = GetSegData(segmentId, SegLvlFeatures.AltQ);
int segQIndex = AbsDelta == Constants.SegmentAbsData ? data : baseQIndex + data;
return Math.Clamp(segQIndex, 0, QuantCommon.MaxQ);
}
return baseQIndex;
}
public void SetupSegmentation(ref Vp9EntropyProbs fc, ref ReadBitBuffer rb)
{
UpdateMap = false;
UpdateData = 0;
Enabled = rb.ReadBit() != 0;
if (!Enabled)
{
return;
}
// Segmentation map update
UpdateMap = rb.ReadBit() != 0;
if (UpdateMap)
{
for (int i = 0; i < SegTreeProbs; i++)
{
fc.SegTreeProb[i] = rb.ReadBit() != 0
? (byte)rb.ReadLiteral(8)
: (byte)Prob.MaxProb;
}
TemporalUpdate = rb.ReadBit() != 0;
if (TemporalUpdate)
{
for (int i = 0; i < PredictionProbs; i++)
{
fc.SegPredProb[i] = rb.ReadBit() != 0
? (byte)rb.ReadLiteral(8)
: (byte)Prob.MaxProb;
}
}
else
{
for (int i = 0; i < PredictionProbs; i++)
{
fc.SegPredProb[i] = Prob.MaxProb;
}
}
}
// Segmentation data update
UpdateData = (byte)rb.ReadBit();
if (UpdateData != 0)
{
AbsDelta = (byte)rb.ReadBit();
ClearAllSegFeatures();
for (int i = 0; i < Constants.MaxSegments; i++)
{
for (int j = 0; j < (int)SegLvlFeatures.Max; j++)
{
int data = 0;
int featureEnabled = rb.ReadBit();
if (featureEnabled != 0)
{
EnableSegFeature(i, (SegLvlFeatures)j);
data = rb.DecodeUnsignedMax(FeatureDataMax((SegLvlFeatures)j));
if (IsSegFeatureSigned((SegLvlFeatures)j) != 0)
{
data = rb.ReadBit() != 0 ? -data : data;
}
}
SetSegData(i, (SegLvlFeatures)j, data);
}
}
}
}
}
}
}

View file

@ -1,11 +1,23 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Video;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal delegate int VpxGetFrameBufferCbFnT(MemoryAllocator allocator, Ptr<InternalFrameBufferList> cbPriv,
ulong minSize, ref VpxCodecFrameBuffer fb);
internal struct Surface : ISurface
{
public const int Innerborderinpixels = 96;
public const int InterpExtend = 4;
public const int EncBorderInPixels = 160;
public const int DecBorderInPixels = 32;
public const int Yv12FlagHighbitdepth = 8;
public ArrayPtr<byte> YBuffer;
public ArrayPtr<byte> UBuffer;
public ArrayPtr<byte> VBuffer;
@ -14,43 +26,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public readonly unsafe Plane UPlane => new((nint)UBuffer.ToPointer(), UBuffer.Length);
public readonly unsafe Plane VPlane => new((nint)VBuffer.ToPointer(), VBuffer.Length);
public readonly FrameField Field => FrameField.Progressive;
public FrameField Field => FrameField.Progressive;
public int Width { get; }
public int Height { get; }
public int AlignedWidth { get; }
public int AlignedHeight { get; }
public int Stride { get; }
public int UvWidth { get; }
public int UvHeight { get; }
public int UvAlignedWidth { get; }
public int UvAlignedHeight { get; }
public int UvStride { get; }
public int Width { get; private set; }
public int Height { get; private set; }
public int AlignedWidth { get; private set; }
public int AlignedHeight { get; private set; }
public int Stride { get; private set; }
public int UvWidth { get; private set; }
public int UvHeight { get; private set; }
public int UvAlignedWidth { get; private set; }
public int UvAlignedHeight { get; private set; }
public int UvStride { get; private set; }
public bool HighBd { get; private set; }
public bool HighBd { get; }
public int FrameSize { get; private set; }
public int Border { get; private set; }
public int YCropWidth => Width;
public int YCropHeight => Height;
public int UvCropWidth => UvWidth;
public int UvCropHeight => UvHeight;
public ArrayPtr<byte> BufferAlloc;
public int BufferAllocSz;
public int SubsamplingX;
public int SubsamplingY;
public uint BitDepth;
public VpxColorSpace ColorSpace;
public VpxColorRange ColorRange;
public int RenderWidth;
public int RenderHeight;
public int Corrupted;
public int Flags;
private readonly nint _pointer;
public Surface(int width, int height)
{
HighBd = false;
const int Border = 32;
const int SsX = 1;
const int SsY = 1;
const int border = 32;
const int ssX = 1;
const int ssY = 1;
const bool highbd = false;
int alignedWidth = (width + 7) & ~7;
int alignedHeight = (height + 7) & ~7;
int yStride = ((alignedWidth + 2 * Border) + 31) & ~31;
int yplaneSize = (alignedHeight + 2 * Border) * yStride;
int uvWidth = alignedWidth >> SsX;
int uvHeight = alignedHeight >> SsY;
int uvStride = yStride >> SsX;
int uvBorderW = Border >> SsX;
int uvBorderH = Border >> SsY;
int uvplaneSize = (uvHeight + 2 * uvBorderH) * uvStride;
int yStride = (alignedWidth + (2 * border) + 31) & ~31;
int yplaneSize = (alignedHeight + (2 * border)) * yStride;
int uvWidth = alignedWidth >> ssX;
int uvHeight = alignedHeight >> ssY;
int uvStride = yStride >> ssX;
int uvBorderW = border >> ssX;
int uvBorderH = border >> ssY;
int uvplaneSize = (uvHeight + (2 * uvBorderH)) * uvStride;
int frameSize = (HighBd ? 2 : 1) * (yplaneSize + 2 * uvplaneSize);
int frameSize = (highbd ? 2 : 1) * (yplaneSize + (2 * uvplaneSize));
nint pointer = Marshal.AllocHGlobal(frameSize);
_pointer = pointer;
@ -59,23 +90,148 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
AlignedWidth = alignedWidth;
AlignedHeight = alignedHeight;
Stride = yStride;
UvWidth = (width + SsX) >> SsX;
UvHeight = (height + SsY) >> SsY;
UvWidth = (width + ssX) >> ssX;
UvHeight = (height + ssY) >> ssY;
UvAlignedWidth = uvWidth;
UvAlignedHeight = uvHeight;
UvStride = uvStride;
ArrayPtr<byte> NewPlane(int start, int size, int planeBorder)
ArrayPtr<byte> NewPlane(int start, int size, int border)
{
return new ArrayPtr<byte>(pointer + start + planeBorder, size - planeBorder);
return new ArrayPtr<byte>(pointer + start + border, size - border);
}
YBuffer = NewPlane(0, yplaneSize, (Border * yStride) + Border);
YBuffer = NewPlane(0, yplaneSize, (border * yStride) + border);
UBuffer = NewPlane(yplaneSize, uvplaneSize, (uvBorderH * uvStride) + uvBorderW);
VBuffer = NewPlane(yplaneSize + uvplaneSize, uvplaneSize, (uvBorderH * uvStride) + uvBorderW);
}
public readonly void Dispose()
public unsafe int ReallocFrameBuffer(
MemoryAllocator allocator,
int width,
int height,
int ssX,
int ssY,
bool useHighbitdepth,
int border,
int byteAlignment,
Ptr<VpxCodecFrameBuffer> fb,
VpxGetFrameBufferCbFnT cb,
Ptr<InternalFrameBufferList> cbPriv)
{
int byteAlign = byteAlignment == 0 ? 1 : byteAlignment; // TODO: Is it safe to ignore the alignment?
int alignedWidth = (width + 7) & ~7;
int alignedHeight = (height + 7) & ~7;
int yStride = (alignedWidth + (2 * border) + 31) & ~31;
ulong yplaneSize =
((ulong)(alignedHeight + (2 * border)) * (ulong)yStride) + (ulong)byteAlignment;
int uvWidth = alignedWidth >> ssX;
int uvHeight = alignedHeight >> ssY;
int uvStride = yStride >> ssX;
int uvBorderW = border >> ssX;
int uvBorderH = border >> ssY;
ulong uvplaneSize =
((ulong)(uvHeight + (2 * uvBorderH)) * (ulong)uvStride) + (ulong)byteAlignment;
ulong frameSize = (ulong)(1 + (useHighbitdepth ? 1 : 0)) * (yplaneSize + (2 * uvplaneSize));
ArrayPtr<byte> buf = ArrayPtr<byte>.Null;
// frame_size is stored in buffer_alloc_sz, which is an int. If it won't
// fit, fail early.
if (frameSize > int.MaxValue)
{
return -1;
}
if (cb != null)
{
const int alignAddrExtraSize = 31;
ulong externalFrameSize = frameSize + alignAddrExtraSize;
Debug.Assert(!fb.IsNull);
// Allocation to hold larger frame, or first allocation.
if (cb(allocator, cbPriv, externalFrameSize, ref fb.Value) < 0)
{
return -1;
}
if (fb.Value.Data.IsNull || (ulong)fb.Value.Data.Length < externalFrameSize)
{
return -1;
}
BufferAlloc = fb.Value.Data;
}
else if (frameSize > (ulong)BufferAllocSz)
{
// Allocation to hold larger frame, or first allocation.
allocator.Free(BufferAlloc);
BufferAlloc = ArrayPtr<byte>.Null;
BufferAlloc = allocator.Allocate<byte>((int)frameSize);
if (BufferAlloc.IsNull)
{
return -1;
}
BufferAllocSz = (int)frameSize;
// This memset is needed for fixing valgrind error from C loop filter
// due to access uninitialized memory in frame border. It could be
// removed if border is totally removed.
MemoryUtil.Fill(BufferAlloc.ToPointer(), (byte)0, BufferAllocSz);
}
/* Only support allocating buffers that have a border that's a multiple
* of 32. The border restriction is required to get 16-byte alignment of
* the start of the chroma rows without introducing an arbitrary gap
* between planes, which would break the semantics of things like
* vpx_img_set_rect(). */
if ((border & 0x1f) != 0)
{
return -3;
}
Width = width;
Height = height;
AlignedWidth = alignedWidth;
AlignedHeight = alignedHeight;
Stride = yStride;
UvWidth = (width + ssX) >> ssX;
UvHeight = (height + ssY) >> ssY;
UvAlignedWidth = uvWidth;
UvAlignedHeight = uvHeight;
UvStride = uvStride;
Border = border;
FrameSize = (int)frameSize;
SubsamplingX = ssX;
SubsamplingY = ssY;
buf = BufferAlloc;
if (useHighbitdepth)
{
// Store uint16 addresses when using 16bit framebuffers
buf = BufferAlloc;
Flags = Yv12FlagHighbitdepth;
}
else
{
Flags = 0;
}
YBuffer = buf.Slice((border * yStride) + border);
UBuffer = buf.Slice((int)yplaneSize + (uvBorderH * uvStride) + uvBorderW);
VBuffer = buf.Slice((int)yplaneSize + (int)uvplaneSize + (uvBorderH * uvStride) + uvBorderW);
Corrupted = 0; /* assume not corrupted by errors */
return 0;
}
public void Dispose()
{
Marshal.FreeHGlobal(_pointer);
}

View file

@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
private static int GetMinLog2TileCols(int sb64Cols)
{
int minLog2 = 0;
while ((MaxTileWidthB64 << minLog2) < sb64Cols)
while (MaxTileWidthB64 << minLog2 < sb64Cols)
{
++minLog2;
}
@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
private static int GetMaxLog2TileCols(int sb64Cols)
{
int maxLog2 = 1;
while ((sb64Cols >> maxLog2) >= MinTileWidthB64)
while (sb64Cols >> maxLog2 >= MinTileWidthB64)
{
++maxLog2;
}
@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return maxLog2 - 1;
}
public static void GetTileNBits(int miCols, ref int minLog2TileCols, ref int maxLog2TileCols)
public static void GetTileNBits(int miCols, out int minLog2TileCols, out int maxLog2TileCols)
{
int sb64Cols = MiColsAlignedToSb(miCols) >> Constants.MiBlockSizeLog2;
minLog2TileCols = GetMinLog2TileCols(sb64Cols);
@ -83,4 +83,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
Debug.Assert(minLog2TileCols <= maxLog2TileCols);
}
}
}
}

View file

@ -1,12 +1,12 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
public enum TxMode
{
Only4X4 = 0, // Only 4x4 transform used
Allow8X8 = 1, // Allow block transform size up to 8x8
Allow16X16 = 2, // Allow block transform size up to 16x16
Allow32X32 = 3, // Allow block transform size up to 32x32
TxModeSelect = 4, // Transform specified for each block
TxModes = 5,
Only4x4, // Only 4x4 transform used
Allow8x8, // Allow block transform size up to 8x8
Allow16x16, // Allow block transform size up to 16x16
Allow32x32, // Allow block transform size up to 32x32
TxModeSelect, // Transform specified for each block
TxModes
}
}
}

View file

@ -1,11 +1,11 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
public enum TxSize
{
Tx4x4 = 0, // 4x4 transform
Tx8x8 = 1, // 8x8 transform
Tx16x16 = 2, // 16x16 transform
Tx32x32 = 3, // 32x32 transform
TxSizes = 4,
Tx4x4, // 4x4 transform
Tx8x8, // 8x8 transform
Tx16x16, // 16x16 transform
Tx32x32, // 32x32 transform
TxSizes
}
}
}

View file

@ -1,11 +1,11 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum TxType
{
DctDct = 0, // DCT in both horizontal and vertical
AdstDct = 1, // ADST in vertical, DCT in horizontal
DctAdst = 2, // DCT in vertical, ADST in horizontal
AdstAdst = 3, // ADST in both directions
TxTypes = 4,
DctDct, // DCT in both horizontal and vertical
AdstDct, // ADST in vertical, DCT in horizontal
DctAdst, // DCT in vertical, ADST in horizontal
AdstAdst, // ADST in both directions
TxTypes
}
}
}

View file

@ -1,6 +1,8 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Nvdec.Vp9.Dsp;
using Ryujinx.Graphics.Video;
using System;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
@ -9,27 +11,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public MacroBlockD Mb;
public ArrayPtr<TileWorkerData> TileWorkerData;
public int TotalTiles;
public InternalErrorInfo Error;
public VpxColorSpace ColorSpace;
public VpxColorRange ColorRange;
public int Width;
public int Height;
public int RenderWidth;
public int RenderHeight;
public int LastWidth;
public int LastHeight;
public int SubsamplingX;
public int SubsamplingY;
public bool UseHighBitDepth;
public ArrayPtr<MvRef> PrevFrameMvs;
public ArrayPtr<MvRef> CurFrameMvs;
public Ptr<Surface> FrameToShow;
public Ptr<RefCntBuffer> PrevFrame;
public Ptr<RefCntBuffer> CurFrame;
public Array8<int> RefFrameMap; /* maps fb_idx to reference slot */
// Prepare ref_frame_map for the next frame.
// Only used in frame parallel decode.
public Array8<int> NextRefFrameMap;
public Array3<RefBuffer> FrameRefs;
public int NewFbIdx;
public int CurShowFrameFbIdx;
public FrameType LastFrameType;
public FrameType FrameType;
public int ShowFrame;
public int LastShowFrame;
public int ShowExistingFrame;
// Flag signaling that the frame is encoded using only Intra modes.
public bool IntraOnly;
public bool LastIntraOnly;
public bool AllowHighPrecisionMv;
public int ResetFrameContext;
// MBs, MbRows/Cols is in 16-pixel units; MiRows/Cols is in
// ModeInfo (8-pixel) units.
public int MBs;
@ -49,8 +86,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
/* We allocate a ModeInfo struct for each macroblock, together with
an extra row on top and column on the left to simplify prediction. */
public int MiAllocSize;
public ArrayPtr<ModeInfo> Mip; /* Base of allocated array */
public ArrayPtr<ModeInfo> Mi; /* Corresponds to upper left visible macroblock */
public ArrayPtr<ModeInfo> Mi; /* Corresponds to upper left visible macroblock */
// prev_mip and prev_mi will only be allocated in VP9 encoder.
public Ptr<ModeInfo> PrevMip; /* MODE_INFO array 'mip' from last decoded frame */
public Ptr<ModeInfo> PrevMi; /* 'mi' from last frame (points into prev_mip) */
public ArrayPtr<Ptr<ModeInfo>> MiGridBase;
public ArrayPtr<Ptr<ModeInfo>> MiGridVisible;
@ -70,6 +112,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public LoopFilterInfoN LfInfo;
public int RefreshFrameContext; /* Two state 0 = NO, 1 = YES */
public Array4<sbyte> RefFrameSignBias; /* Two state 0, 1 */
public LoopFilter Lf;
@ -81,22 +125,37 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public ReferenceMode ReferenceMode;
public Ptr<Vp9EntropyProbs> Fc;
public ArrayPtr<Vp9EntropyProbs> FrameContexts; // FRAME_CONTEXTS
public uint FrameContextIdx; /* Context to use/update */
public Ptr<Vp9BackwardUpdates> Counts;
public uint CurrentVideoFrame;
public BitstreamProfile Profile;
public BitDepth BitDepth;
public BitDepth DequantBitDepth; // bit_depth of current dequantizer
public int ErrorResilientMode;
public int FrameParallelDecodingMode;
public int Log2TileCols, Log2TileRows;
public int ByteAlignment;
public int SkipLoopFilter;
public Ptr<BufferPool> BufferPool;
public ArrayPtr<sbyte> AboveSegContext;
public ArrayPtr<sbyte> AboveContext;
public readonly bool FrameIsIntraOnly()
public bool FrameIsIntraOnly()
{
return FrameType == FrameType.KeyFrame || IntraOnly;
}
public bool CompoundReferenceAllowed()
{
int i;
for (i = 1; i < Constants.RefsPerFrame; ++i)
for (int i = 1; i < Constants.RefsPerFrame; ++i)
{
if (RefFrameSignBias[i + 1] != RefFrameSignBias[1])
{
@ -107,6 +166,47 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
return false;
}
public ref Surface GetFrameNewBuffer()
{
return ref BufferPool.Value.FrameBufs[NewFbIdx].Buf;
}
public int GetFreeFb()
{
ref Array12<RefCntBuffer> frameBufs = ref BufferPool.Value.FrameBufs;
int i;
for (i = 0; i < Constants.FrameBuffers; ++i)
{
if (frameBufs[i].RefCount == 0)
{
break;
}
}
if (i != Constants.FrameBuffers)
{
frameBufs[i].RefCount = 1;
}
else
{
// Reset i to be INVALID_IDX to indicate no free buffer found.
i = RefBuffer.InvalidIdx;
}
return i;
}
public void SwapCurrentAndLastSegMap()
{
// Swap indices.
(SegMapIdx, PrevSegMapIdx) = (PrevSegMapIdx, SegMapIdx);
CurrentFrameSegMap = SegMapArray[SegMapIdx];
LastFrameSegMap = SegMapArray[PrevSegMapIdx];
}
private static int CalcMiSize(int len)
{
// Len is in mi units.
@ -129,19 +229,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public void AllocTileWorkerData(MemoryAllocator allocator, int tileCols, int tileRows, int maxThreads)
{
TileWorkerData = allocator.Allocate<TileWorkerData>(tileCols * tileRows + (maxThreads > 1 ? maxThreads : 0));
TileWorkerData =
allocator.Allocate<TileWorkerData>((tileCols * tileRows) + (maxThreads > 1 ? maxThreads : 0));
}
public readonly void FreeTileWorkerData(MemoryAllocator allocator)
public void FreeTileWorkerData(MemoryAllocator allocator)
{
allocator.Free(TileWorkerData);
}
private void AllocSegMap(MemoryAllocator allocator, int segMapSize)
{
int i;
for (i = 0; i < Constants.NumPingPongBuffers; ++i)
for (int i = 0; i < Constants.NumPingPongBuffers; ++i)
{
SegMapArray[i] = allocator.Allocate<byte>(segMapSize);
}
@ -156,9 +255,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
private void FreeSegMap(MemoryAllocator allocator)
{
int i;
for (i = 0; i < Constants.NumPingPongBuffers; ++i)
for (int i = 0; i < Constants.NumPingPongBuffers; ++i)
{
allocator.Free(SegMapArray[i]);
SegMapArray[i] = ArrayPtr<byte>.Null;
@ -194,6 +291,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
Lf.Lfm = ArrayPtr<LoopFilterMask>.Null;
allocator.Free(CurFrameMvs);
CurFrameMvs = ArrayPtr<MvRef>.Null;
if (UsePrevFrameMvs)
{
allocator.Free(PrevFrameMvs);
@ -209,7 +307,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
Lf.Lfm = allocator.Allocate<LoopFilterMask>(((MiRows + (Constants.MiBlockSize - 1)) >> 3) * Lf.LfmStride);
}
public void AllocContextBuffers(MemoryAllocator allocator, int width, int height)
public bool AllocContextBuffers(MemoryAllocator allocator, int width, int height)
{
SetMbMi(width, height);
int newMiSize = MiStride * CalcMiSize(MiRows);
@ -239,6 +337,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
PrevFrameMvs = allocator.Allocate<MvRef>(MiRows * MiCols);
}
return false;
}
private unsafe void DecSetupMi()
@ -257,7 +357,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
}
}
private readonly void SetPartitionProbs(ref MacroBlockD xd)
private void SetPartitionProbs(ref MacroBlockD xd)
{
xd.PartitionProbs = FrameIsIntraOnly()
? new ArrayPtr<Array3<byte>>(ref Fc.Value.KfPartitionProb[0], 16)
@ -266,9 +366,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
internal void InitMacroBlockD(ref MacroBlockD xd, ArrayPtr<int> dqcoeff)
{
int i;
for (i = 0; i < Constants.MaxMbPlane; ++i)
for (int i = 0; i < Constants.MaxMbPlane; ++i)
{
xd.Plane[i].DqCoeff = dqcoeff;
xd.AboveContext[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols));
@ -281,6 +379,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref UvDequant);
}
xd.Fc = new Ptr<Vp9EntropyProbs>(ref Fc.Value);
}
@ -293,29 +392,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public void SetupSegmentationDequant()
{
const BitDepth BitDepth = BitDepth.Bits8; // TODO: Configurable
// Build y/uv dequant values based on segmentation.
if (Seg.Enabled)
{
int i;
for (i = 0; i < Constants.MaxSegments; ++i)
for (int i = 0; i < Constants.MaxSegments; ++i)
{
int qIndex = QuantCommon.GetQIndex(ref Seg, i, BaseQindex);
YDequant[i][0] = QuantCommon.DcQuant(qIndex, YDcDeltaQ, BitDepth);
YDequant[i][1] = QuantCommon.AcQuant(qIndex, 0, BitDepth);
UvDequant[i][0] = QuantCommon.DcQuant(qIndex, UvDcDeltaQ, BitDepth);
UvDequant[i][1] = QuantCommon.AcQuant(qIndex, UvAcDeltaQ, BitDepth);
int qindex = Seg.GetQIndex(i, BaseQindex);
YDequant[i][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth);
YDequant[i][1] = QuantCommon.AcQuant(qindex, 0, BitDepth);
UvDequant[i][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth);
UvDequant[i][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth);
}
}
else
{
int qIndex = BaseQindex;
int qindex = BaseQindex;
// When segmentation is disabled, only the first value is used. The
// remaining are don't cares.
YDequant[0][0] = QuantCommon.DcQuant(qIndex, YDcDeltaQ, BitDepth);
YDequant[0][1] = QuantCommon.AcQuant(qIndex, 0, BitDepth);
UvDequant[0][0] = QuantCommon.DcQuant(qIndex, UvDcDeltaQ, BitDepth);
UvDequant[0][1] = QuantCommon.AcQuant(qIndex, UvAcDeltaQ, BitDepth);
YDequant[0][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth);
YDequant[0][1] = QuantCommon.AcQuant(qindex, 0, BitDepth);
UvDequant[0][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth);
UvDequant[0][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth);
}
}
@ -327,5 +424,576 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
refBuf.Sf.SetupScaleFactorsForFrame(refBuf.Buf.Width, refBuf.Buf.Height, Width, Height);
}
}
public void ReadFrameReferenceModeProbs(ref Reader r)
{
ref Vp9EntropyProbs fc = ref Fc.Value;
if (ReferenceMode == ReferenceMode.Select)
{
for (int i = 0; i < Constants.CompInterContexts; ++i)
{
r.DiffUpdateProb(ref fc.CompInterProb[i]);
}
}
if (ReferenceMode != ReferenceMode.Compound)
{
for (int i = 0; i < Constants.RefContexts; ++i)
{
r.DiffUpdateProb(ref fc.SingleRefProb[i][0]);
r.DiffUpdateProb(ref fc.SingleRefProb[i][1]);
}
}
if (ReferenceMode != ReferenceMode.Single)
{
for (int i = 0; i < Constants.RefContexts; ++i)
{
r.DiffUpdateProb(ref fc.CompRefProb[i]);
}
}
}
public ReferenceMode ReadFrameReferenceMode(ref Reader r)
{
if (CompoundReferenceAllowed())
{
return r.ReadBit() != 0
? r.ReadBit() != 0 ? ReferenceMode.Select : ReferenceMode.Compound
: ReferenceMode.Single;
}
return ReferenceMode.Single;
}
public void SetupCompoundReferenceMode()
{
if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.GoldenFrame])
{
CompFixedRef = Constants.AltRefFrame;
CompVarRef[0] = Constants.LastFrame;
CompVarRef[1] = Constants.GoldenFrame;
}
else if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.AltRefFrame])
{
CompFixedRef = Constants.GoldenFrame;
CompVarRef[0] = Constants.LastFrame;
CompVarRef[1] = Constants.AltRefFrame;
}
else
{
CompFixedRef = Constants.LastFrame;
CompVarRef[0] = Constants.GoldenFrame;
CompVarRef[1] = Constants.AltRefFrame;
}
}
public void InitMvProbs()
{
Fc.Value.Joints[0] = 32;
Fc.Value.Joints[1] = 64;
Fc.Value.Joints[2] = 96;
Fc.Value.Sign[0] = 128;
Fc.Value.Classes[0][0] = 224;
Fc.Value.Classes[0][1] = 144;
Fc.Value.Classes[0][2] = 192;
Fc.Value.Classes[0][3] = 168;
Fc.Value.Classes[0][4] = 192;
Fc.Value.Classes[0][5] = 176;
Fc.Value.Classes[0][6] = 192;
Fc.Value.Classes[0][7] = 198;
Fc.Value.Classes[0][8] = 198;
Fc.Value.Classes[0][9] = 245;
Fc.Value.Class0[0][0] = 216;
Fc.Value.Bits[0][0] = 136;
Fc.Value.Bits[0][1] = 140;
Fc.Value.Bits[0][2] = 148;
Fc.Value.Bits[0][3] = 160;
Fc.Value.Bits[0][4] = 176;
Fc.Value.Bits[0][5] = 192;
Fc.Value.Bits[0][6] = 224;
Fc.Value.Bits[0][7] = 234;
Fc.Value.Bits[0][8] = 234;
Fc.Value.Bits[0][9] = 240;
Fc.Value.Class0Fp[0][0][0] = 128;
Fc.Value.Class0Fp[0][0][1] = 128;
Fc.Value.Class0Fp[0][0][2] = 64;
Fc.Value.Class0Fp[0][1][0] = 96;
Fc.Value.Class0Fp[0][1][1] = 112;
Fc.Value.Class0Fp[0][1][2] = 64;
Fc.Value.Fp[0][0] = 64;
Fc.Value.Fp[0][1] = 96;
Fc.Value.Fp[0][2] = 64;
Fc.Value.Class0Hp[0] = 160;
Fc.Value.Hp[0] = 128;
Fc.Value.Sign[1] = 128;
Fc.Value.Classes[1][0] = 216;
Fc.Value.Classes[1][1] = 128;
Fc.Value.Classes[1][2] = 176;
Fc.Value.Classes[1][3] = 160;
Fc.Value.Classes[1][4] = 176;
Fc.Value.Classes[1][5] = 176;
Fc.Value.Classes[1][6] = 192;
Fc.Value.Classes[1][7] = 198;
Fc.Value.Classes[1][8] = 198;
Fc.Value.Classes[1][9] = 208;
Fc.Value.Class0[1][0] = 208;
Fc.Value.Bits[1][0] = 136;
Fc.Value.Bits[1][1] = 140;
Fc.Value.Bits[1][2] = 148;
Fc.Value.Bits[1][3] = 160;
Fc.Value.Bits[1][4] = 176;
Fc.Value.Bits[1][5] = 192;
Fc.Value.Bits[1][6] = 224;
Fc.Value.Bits[1][7] = 234;
Fc.Value.Bits[1][8] = 234;
Fc.Value.Bits[1][9] = 240;
Fc.Value.Class0Fp[1][0][0] = 128;
Fc.Value.Class0Fp[1][0][1] = 128;
Fc.Value.Class0Fp[1][0][2] = 64;
Fc.Value.Class0Fp[1][1][0] = 96;
Fc.Value.Class0Fp[1][1][1] = 112;
Fc.Value.Class0Fp[1][1][2] = 64;
Fc.Value.Fp[1][0] = 64;
Fc.Value.Fp[1][1] = 96;
Fc.Value.Fp[1][2] = 64;
Fc.Value.Class0Hp[1] = 160;
Fc.Value.Hp[1] = 128;
}
public void AdaptMvProbs(bool allowHp)
{
ref Vp9EntropyProbs fc = ref Fc.Value;
ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx];
ref Vp9BackwardUpdates counts = ref Counts.Value;
Prob.VpxTreeMergeProbs(
EntropyMv.JointTree,
preFc.Joints.AsSpan(),
counts.Joints.AsSpan(),
fc.Joints.AsSpan());
for (int i = 0; i < 2; ++i)
{
fc.Sign[i] = Prob.ModeMvMergeProbs(preFc.Sign[i], ref counts.Sign[i]);
Prob.VpxTreeMergeProbs(
EntropyMv.ClassTree,
preFc.Classes[i].AsSpan(),
counts.Classes[i].AsSpan(),
fc.Classes[i].AsSpan());
Prob.VpxTreeMergeProbs(
EntropyMv.Class0Tree,
preFc.Class0[i].AsSpan(),
counts.Class0[i].AsSpan(),
fc.Class0[i].AsSpan());
for (int j = 0; j < EntropyMv.OffsetBits; ++j)
{
fc.Bits[i][j] = Prob.ModeMvMergeProbs(preFc.Bits[i][j], ref counts.Bits[i][j]);
}
for (int j = 0; j < EntropyMv.Class0Size; ++j)
{
Prob.VpxTreeMergeProbs(
EntropyMv.FpTree,
preFc.Class0Fp[i][j].AsSpan(),
counts.Class0Fp[i][j].AsSpan(),
fc.Class0Fp[i][j].AsSpan());
}
Prob.VpxTreeMergeProbs(EntropyMv.FpTree, preFc.Fp[i].AsSpan(), counts.Fp[i].AsSpan(),
fc.Fp[i].AsSpan());
if (allowHp)
{
fc.Class0Hp[i] = Prob.ModeMvMergeProbs(preFc.Class0Hp[i], ref counts.Class0Hp[i]);
fc.Hp[i] = Prob.ModeMvMergeProbs(preFc.Hp[i], ref counts.Hp[i]);
}
}
}
public void ResizeContextBuffers(MemoryAllocator allocator, int width, int height)
{
if (Width != width || Height != height)
{
int newMiRows = BitUtils.AlignPowerOfTwo(height, Constants.MiSizeLog2) >> Constants.MiSizeLog2;
int newMiCols = BitUtils.AlignPowerOfTwo(width, Constants.MiSizeLog2) >> Constants.MiSizeLog2;
// Allocations in AllocContextBuffers() depend on individual
// dimensions as well as the overall size.
if (newMiCols > MiCols || newMiRows > MiRows)
{
if (AllocContextBuffers(allocator, width, height))
{
// The Mi* values have been cleared and any existing context
// buffers have been freed. Clear Width and Height to be
// consistent and to force a realloc next time.
Width = 0;
Height = 0;
Error.InternalError(CodecErr.MemError, "Failed to allocate context buffers");
}
}
else
{
SetMbMi(width, height);
}
InitContextBuffers();
Width = width;
Height = height;
}
if (CurFrameMvs.IsNull ||
MiRows > CurFrame.Value.MiRows ||
MiCols > CurFrame.Value.MiCols)
{
ResizeMvBuffer(allocator);
}
}
public void CheckMemError<T>(ref ArrayPtr<T> lval, ArrayPtr<T> expr)
where T : unmanaged
{
lval = expr;
if (lval.IsNull)
{
Error.InternalError(CodecErr.MemError, "Failed to allocate");
}
}
private void ResizeMvBuffer(MemoryAllocator allocator)
{
allocator.Free(CurFrameMvs);
CurFrame.Value.MiRows = MiRows;
CurFrame.Value.MiCols = MiCols;
CheckMemError(ref CurFrameMvs, allocator.Allocate<MvRef>(MiRows * MiCols));
}
public void CheckMemError<T>(ref Ptr<T> lval, Ptr<T> expr) where T : unmanaged
{
lval = expr;
if (lval.IsNull)
{
Error.InternalError(CodecErr.MemError, "Failed to allocate");
}
}
public void SetupTileInfo(ref ReadBitBuffer rb)
{
int minLog2TileCols = 0, maxLog2TileCols = 0, maxOnes;
TileInfo.GetTileNBits(MiCols, out minLog2TileCols, out maxLog2TileCols);
// columns
maxOnes = maxLog2TileCols - minLog2TileCols;
Log2TileCols = minLog2TileCols;
while (maxOnes-- != 0 && rb.ReadBit() != 0)
{
Log2TileCols++;
}
if (Log2TileCols > 6)
{
Error.InternalError(CodecErr.CorruptFrame, "Invalid number of tile columns");
}
// rows
Log2TileRows = rb.ReadBit();
if (Log2TileRows != 0)
{
Log2TileRows += rb.ReadBit();
}
}
public void ReadBitdepthColorspaceSampling(ref ReadBitBuffer rb)
{
if (Profile >= BitstreamProfile.Profile2)
{
BitDepth = rb.ReadBit() != 0 ? BitDepth.Bits12 : BitDepth.Bits10;
UseHighBitDepth = true;
}
else
{
BitDepth = BitDepth.Bits8;
UseHighBitDepth = false;
}
ColorSpace = (VpxColorSpace)rb.ReadLiteral(3);
if (ColorSpace != VpxColorSpace.Srgb)
{
ColorRange = (VpxColorRange)rb.ReadBit();
if (Profile == BitstreamProfile.Profile1 || Profile == BitstreamProfile.Profile3)
{
SubsamplingX = rb.ReadBit();
SubsamplingY = rb.ReadBit();
if (SubsamplingX == 1 && SubsamplingY == 1)
{
Error.InternalError(CodecErr.UnsupBitstream,
"4:2:0 color not supported in profile 1 or 3");
}
if (rb.ReadBit() != 0)
{
Error.InternalError(CodecErr.UnsupBitstream, "Reserved bit set");
}
}
else
{
SubsamplingY = SubsamplingX = 1;
}
}
else
{
ColorRange = VpxColorRange.Full;
if (Profile == BitstreamProfile.Profile1 || Profile == BitstreamProfile.Profile3)
{
// Note if colorspace is SRGB then 4:4:4 chroma sampling is assumed.
// 4:2:2 or 4:4:0 chroma sampling is not allowed.
SubsamplingY = SubsamplingX = 0;
if (rb.ReadBit() != 0)
{
Error.InternalError(CodecErr.UnsupBitstream, "Reserved bit set");
}
}
else
{
Error.InternalError(CodecErr.UnsupBitstream, "4:4:4 color not supported in profile 0 or 2");
}
}
}
public void AdaptModeProbs()
{
ref Vp9EntropyProbs fc = ref Fc.Value;
ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx];
ref Vp9BackwardUpdates counts = ref Counts.Value;
for (int i = 0; i < Constants.IntraInterContexts; i++)
{
fc.IntraInterProb[i] = Prob.ModeMvMergeProbs(preFc.IntraInterProb[i], ref counts.IntraInter[i]);
}
for (int i = 0; i < Constants.CompInterContexts; i++)
{
fc.CompInterProb[i] = Prob.ModeMvMergeProbs(preFc.CompInterProb[i], ref counts.CompInter[i]);
}
for (int i = 0; i < Constants.RefContexts; i++)
{
fc.CompRefProb[i] = Prob.ModeMvMergeProbs(preFc.CompRefProb[i], ref counts.CompRef[i]);
}
for (int i = 0; i < Constants.RefContexts; i++)
{
for (int j = 0; j < 2; j++)
{
fc.SingleRefProb[i][j] =
Prob.ModeMvMergeProbs(preFc.SingleRefProb[i][j], ref counts.SingleRef[i][j]);
}
}
for (int i = 0; i < Constants.InterModeContexts; i++)
{
Prob.VpxTreeMergeProbs(
EntropyMode.InterModeTree,
preFc.InterModeProb[i].AsSpan(),
counts.InterMode[i].AsSpan(),
fc.InterModeProb[i].AsSpan());
}
for (int i = 0; i < EntropyMode.BlockSizeGroups; i++)
{
Prob.VpxTreeMergeProbs(
EntropyMode.IntraModeTree,
preFc.YModeProb[i].AsSpan(),
counts.YMode[i].AsSpan(),
fc.YModeProb[i].AsSpan());
}
for (int i = 0; i < Constants.IntraModes; ++i)
{
Prob.VpxTreeMergeProbs(
EntropyMode.IntraModeTree,
preFc.UvModeProb[i].AsSpan(),
counts.UvMode[i].AsSpan(),
fc.UvModeProb[i].AsSpan());
}
for (int i = 0; i < Constants.PartitionContexts; i++)
{
Prob.VpxTreeMergeProbs(
EntropyMode.PartitionTree,
preFc.PartitionProb[i].AsSpan(),
counts.Partition[i].AsSpan(),
fc.PartitionProb[i].AsSpan());
}
if (InterpFilter == Constants.Switchable)
{
for (int i = 0; i < Constants.SwitchableFilterContexts; i++)
{
Prob.VpxTreeMergeProbs(
EntropyMode.SwitchableInterpTree,
preFc.SwitchableInterpProb[i].AsSpan(),
counts.SwitchableInterp[i].AsSpan(),
fc.SwitchableInterpProb[i].AsSpan());
}
}
if (TxMode == TxMode.TxModeSelect)
{
Array1<Array2<uint>> branchCt8x8P = new();
Array2<Array2<uint>> branchCt16x16P = new();
Array3<Array2<uint>> branchCt32x32P = new();
for (int i = 0; i < EntropyMode.TxSizeContexts; ++i)
{
EntropyMode.TxCountsToBranchCounts8x8(counts.Tx8x8[i].AsSpan(), ref branchCt8x8P);
for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j)
{
fc.Tx8x8Prob[i][j] = Prob.ModeMvMergeProbs(preFc.Tx8x8Prob[i][j], ref branchCt8x8P[j]);
}
EntropyMode.TxCountsToBranchCounts16x16(counts.Tx16x16[i].AsSpan(), ref branchCt16x16P);
for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j)
{
fc.Tx16x16Prob[i][j] =
Prob.ModeMvMergeProbs(preFc.Tx16x16Prob[i][j], ref branchCt16x16P[j]);
}
EntropyMode.TxCountsToBranchCounts32x32(counts.Tx32x32[i].AsSpan(), ref branchCt32x32P);
for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j)
{
fc.Tx32x32Prob[i][j] =
Prob.ModeMvMergeProbs(preFc.Tx32x32Prob[i][j], ref branchCt32x32P[j]);
}
}
}
for (int i = 0; i < Constants.SkipContexts; ++i)
{
fc.SkipProb[i] = Prob.ModeMvMergeProbs(preFc.SkipProb[i], ref counts.Skip[i]);
}
}
public void AdaptCoefProbs()
{
byte t;
uint countSat, updateFactor;
if (FrameIsIntraOnly())
{
updateFactor = Entropy.CoefMaxUpdateFactorKey;
countSat = Entropy.CoefCountSatKey;
}
else if (LastFrameType == FrameType.KeyFrame)
{
updateFactor = Entropy.CoefMaxUpdateFactorAfterKey; /* adapt quickly */
countSat = Entropy.CoefCountSatAfterKey;
}
else
{
updateFactor = Entropy.CoefMaxUpdateFactor;
countSat = Entropy.CoefCountSat;
}
for (t = (int)TxSize.Tx4x4; t <= (int)TxSize.Tx32x32; t++)
{
AdaptCoefProbs(t, countSat, updateFactor);
}
}
public void SetMvs(ReadOnlySpan<Vp9MvRef> mvs)
{
if (mvs.Length > PrevFrameMvs.Length)
{
throw new ArgumentException(
$"Size mismatch, expected: {PrevFrameMvs.Length}, but got: {mvs.Length}.");
}
for (int i = 0; i < mvs.Length; i++)
{
ref MvRef mv = ref PrevFrameMvs[i];
mv.Mv[0].Row = mvs[i].Mvs[0].Row;
mv.Mv[0].Col = mvs[i].Mvs[0].Col;
mv.Mv[1].Row = mvs[i].Mvs[1].Row;
mv.Mv[1].Col = mvs[i].Mvs[1].Col;
mv.RefFrame[0] = (sbyte)mvs[i].RefFrames[0];
mv.RefFrame[1] = (sbyte)mvs[i].RefFrames[1];
}
}
public void GetMvs(Span<Vp9MvRef> mvs)
{
if (mvs.Length > CurFrameMvs.Length)
{
throw new ArgumentException(
$"Size mismatch, expected: {CurFrameMvs.Length}, but got: {mvs.Length}.");
}
for (int i = 0; i < mvs.Length; i++)
{
ref MvRef mv = ref CurFrameMvs[i];
mvs[i].Mvs[0].Row = mv.Mv[0].Row;
mvs[i].Mvs[0].Col = mv.Mv[0].Col;
mvs[i].Mvs[1].Row = mv.Mv[1].Row;
mvs[i].Mvs[1].Col = mv.Mv[1].Col;
mvs[i].RefFrames[0] = mv.RefFrame[0];
mvs[i].RefFrames[1] = mv.RefFrame[1];
}
}
private void AdaptCoefProbs(byte txSize, uint countSat, uint updateFactor)
{
ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx];
ref Array2<Array2<Array6<Array6<Array3<byte>>>>> probs = ref Fc.Value.CoefProbs[txSize];
ref Array2<Array2<Array6<Array6<Array3<byte>>>>> preProbs = ref preFc.CoefProbs[txSize];
ref Array2<Array2<Array6<Array6<Array4<uint>>>>> counts = ref Counts.Value.Coef[txSize];
ref Array2<Array2<Array6<Array6<uint>>>> eobCounts = ref Counts.Value.EobBranch[txSize];
for (int i = 0; i < Constants.PlaneTypes; ++i)
{
for (int j = 0; j < Entropy.RefTypes; ++j)
{
for (int k = 0; k < Entropy.CoefBands; ++k)
{
for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l)
{
int n0 = (int)counts[i][j][k][l][Entropy.ZeroToken];
int n1 = (int)counts[i][j][k][l][Entropy.OneToken];
int n2 = (int)counts[i][j][k][l][Entropy.TwoToken];
int neob = (int)counts[i][j][k][l][Entropy.EobModelToken];
Array3<Array2<uint>> branchCt = new();
branchCt[0][0] = (uint)neob;
branchCt[0][1] = (uint)(eobCounts[i][j][k][l] - neob);
branchCt[1][0] = (uint)n0;
branchCt[1][1] = (uint)(n1 + n2);
branchCt[2][0] = (uint)n1;
branchCt[2][1] = (uint)n2;
for (int m = 0; m < Entropy.UnconstrainedNodes; ++m)
{
probs[i][j][k][l][m] = Prob.MergeProbs(preProbs[i][j][k][l][m], ref branchCt[m],
countSat, updateFactor);
}
}
}
}
}
}
public void DefaultCoefProbs()
{
Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx4x4], Entropy.DefaultCoefProbs4x4);
Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx8x8], Entropy.DefaultCoefProbs8x8);
Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx16x16], Entropy.DefaultCoefProbs16x16);
Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx32x32], Entropy.DefaultCoefProbs32x32);
}
}
}
}

View file

@ -0,0 +1,410 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using Ryujinx.Graphics.Video;
using System.Diagnostics;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct Vp9Decoder
{
public Vp9Common Common;
public int ReadyForNewData;
public int RefreshFrameFlags;
public int NeedResync; // Wait for key/intra-only frame.
public int HoldRefBuf; // Hold the reference buffer.
private static void DecreaseRefCount(int idx, ref Array12<RefCntBuffer> frameBufs, ref BufferPool pool)
{
if (idx >= 0 && frameBufs[idx].RefCount > 0)
{
--frameBufs[idx].RefCount;
// A worker may only get a free framebuffer index when calling GetFreeFb.
// But the private buffer is not set up until finish decoding header.
// So any error happens during decoding header, the frame_bufs will not
// have valid priv buffer.
if (frameBufs[idx].Released == 0 && frameBufs[idx].RefCount == 0 &&
!frameBufs[idx].RawFrameBuffer.Priv.IsNull)
{
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[idx].RawFrameBuffer);
frameBufs[idx].Released = 1;
}
}
}
public void Create(MemoryAllocator allocator, ref BufferPool pool)
{
ref Vp9Common cm = ref Common;
cm.CheckMemError(ref cm.Fc,
new Ptr<Vp9EntropyProbs>(ref allocator.Allocate<Vp9EntropyProbs>(1)[0]));
cm.CheckMemError(ref cm.FrameContexts,
allocator.Allocate<Vp9EntropyProbs>(Constants.FrameContexts));
for (int i = 0; i < EntropyMode.KfYModeProb.Length; i++)
{
for (int j = 0; j < EntropyMode.KfYModeProb[i].Length; j++)
{
for (int k = 0; k < EntropyMode.KfYModeProb[i][j].Length; k++)
{
cm.Fc.Value.KfYModeProb[i][j][k] = EntropyMode.KfYModeProb[i][j][k];
}
}
}
for (int i = 0; i < EntropyMode.KfUvModeProb.Length; i++)
{
for (int j = 0; j < EntropyMode.KfUvModeProb[i].Length; j++)
{
cm.Fc.Value.KfUvModeProb[i][j] = EntropyMode.KfUvModeProb[i][j];
}
}
byte[][] KfPartitionProbs =
{
// 8x8 . 4x4
new byte[] { 158, 97, 94 }, // a/l both not split
new byte[] { 93, 24, 99 }, // a split, l not split
new byte[] { 85, 119, 44 }, // l split, a not split
new byte[] { 62, 59, 67 }, // a/l both split
// 16x16 . 8x8
new byte[] { 149, 53, 53 }, // a/l both not split
new byte[] { 94, 20, 48 }, // a split, l not split
new byte[] { 83, 53, 24 }, // l split, a not split
new byte[] { 52, 18, 18 }, // a/l both split
// 32x32 . 16x16
new byte[] { 150, 40, 39 }, // a/l both not split
new byte[] { 78, 12, 26 }, // a split, l not split
new byte[] { 67, 33, 11 }, // l split, a not split
new byte[] { 24, 7, 5 }, // a/l both split
// 64x64 . 32x32
new byte[] { 174, 35, 49 }, // a/l both not split
new byte[] { 68, 11, 27 }, // a split, l not split
new byte[] { 57, 15, 9 }, // l split, a not split
new byte[] { 12, 3, 3 } // a/l both split
};
for (int i = 0; i < KfPartitionProbs.Length; i++)
{
for (int j = 0; j < KfPartitionProbs[i].Length; j++)
{
cm.Fc.Value.KfPartitionProb[i][j] = KfPartitionProbs[i][j];
}
}
cm.Counts = new Ptr<Vp9BackwardUpdates>(ref allocator.Allocate<Vp9BackwardUpdates>(1)[0]);
NeedResync = 1;
// Initialize the references to not point to any frame buffers.
for (int i = 0; i < 8; i++)
{
cm.RefFrameMap[i] = -1;
cm.NextRefFrameMap[i] = -1;
}
cm.CurrentVideoFrame = 0;
ReadyForNewData = 1;
Common.BufferPool = new Ptr<BufferPool>(ref pool);
cm.BitDepth = BitDepth.Bits8;
cm.DequantBitDepth = BitDepth.Bits8;
// vp9_loop_filter_init(ref cm);
}
/* If any buffer updating is signaled it should be done here. */
private void SwapFrameBuffers()
{
int refIndex = 0, mask;
ref Vp9Common cm = ref Common;
ref BufferPool pool = ref cm.BufferPool.Value;
ref Array12<RefCntBuffer> frameBufs = ref cm.BufferPool.Value.FrameBufs;
for (mask = RefreshFrameFlags; mask != 0; mask >>= 1)
{
int oldIdx = cm.RefFrameMap[refIndex];
// Current thread releases the holding of reference frame.
DecreaseRefCount(oldIdx, ref frameBufs, ref pool);
// Release the reference frame in reference map.
if ((mask & 1) != 0)
{
DecreaseRefCount(oldIdx, ref frameBufs, ref pool);
}
cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex];
++refIndex;
}
// Current thread releases the holding of reference frame.
for (; refIndex < Constants.RefFrames && cm.ShowExistingFrame == 0; ++refIndex)
{
int oldIdx = cm.RefFrameMap[refIndex];
DecreaseRefCount(oldIdx, ref frameBufs, ref pool);
cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex];
}
HoldRefBuf = 0;
cm.FrameToShow = new Ptr<Surface>(ref cm.GetFrameNewBuffer());
--frameBufs[cm.NewFbIdx].RefCount;
// Invalidate these references until the next frame starts.
for (refIndex = 0; refIndex < 3; refIndex++)
{
cm.FrameRefs[refIndex].Idx = RefBuffer.InvalidIdx;
}
}
public CodecErr ReceiveCompressedData(MemoryAllocator allocator, ulong size, ref ArrayPtr<byte> psource)
{
ref Vp9Common cm = ref Common;
ref BufferPool pool = ref cm.BufferPool.Value;
ref Array12<RefCntBuffer> frameBufs = ref cm.BufferPool.Value.FrameBufs;
ArrayPtr<byte> source = psource;
CodecErr retcode = 0;
cm.Error.ErrorCode = CodecErr.Ok;
if (size == 0)
{
// This is used to signal that we are missing frames.
// We do not know if the missing frame(s) was supposed to update
// any of the reference buffers, but we act conservative and
// mark only the last buffer as corrupted.
if (cm.FrameRefs[0].Idx > 0)
{
cm.FrameRefs[0].Buf.Corrupted = 1;
}
}
ReadyForNewData = 0;
// Check if the previous frame was a frame without any references to it.
if (cm.NewFbIdx >= 0 && frameBufs[cm.NewFbIdx].RefCount == 0 &&
frameBufs[cm.NewFbIdx].Released == 0)
{
FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[cm.NewFbIdx].RawFrameBuffer);
frameBufs[cm.NewFbIdx].Released = 1;
}
// Find a free frame buffer. Return error if can not find any.
cm.NewFbIdx = cm.GetFreeFb();
if (cm.NewFbIdx == RefBuffer.InvalidIdx)
{
ReadyForNewData = 1;
cm.Error.InternalError(CodecErr.MemError, "Unable to find free frame buffer");
return cm.Error.ErrorCode;
}
// Assign a MV array to the frame buffer.
cm.CurFrame = new Ptr<RefCntBuffer>(ref pool.FrameBufs[cm.NewFbIdx]);
HoldRefBuf = 0;
DecodeFrame.Decode(allocator, ref this, new ArrayPtr<byte>(ref source[0], (int)size), out psource);
SwapFrameBuffers();
// vpx_clear_system_state();
if (cm.ShowExistingFrame == 0)
{
cm.LastShowFrame = cm.ShowFrame;
cm.PrevFrame = cm.CurFrame;
if (cm.PrevFrameMvs.IsNull || cm.PrevFrameMvs.Length != cm.CurFrameMvs.Length)
{
allocator.Free(cm.PrevFrameMvs);
cm.PrevFrameMvs = allocator.Allocate<MvRef>(cm.CurFrameMvs.Length);
}
cm.CurFrameMvs.AsSpan().CopyTo(cm.PrevFrameMvs.AsSpan());
if (cm.Seg.Enabled)
{
cm.SwapCurrentAndLastSegMap();
}
}
if (cm.ShowFrame != 0)
{
cm.CurShowFrameFbIdx = cm.NewFbIdx;
}
// Update progress in frame parallel decode.
cm.LastWidth = cm.Width;
cm.LastHeight = cm.Height;
if (cm.ShowFrame != 0)
{
cm.CurrentVideoFrame++;
}
return retcode;
}
public int GetRawFrame(ref Surface sd)
{
ref Vp9Common cm = ref Common;
int ret = -1;
if (ReadyForNewData == 1)
{
return ret;
}
ReadyForNewData = 1;
if (cm.ShowFrame == 0)
{
return ret;
}
ReadyForNewData = 1;
sd = cm.FrameToShow.Value;
ret = 0;
return ret;
}
public CodecErr Decode(MemoryAllocator allocator, ArrayPtr<byte> data)
{
ArrayPtr<byte> dataStart = data;
CodecErr res;
Array8<uint> frameSizes = new();
int frameCount = 0;
res = Types.Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount);
if (res != CodecErr.Ok)
{
return res;
}
// Decode in serial mode.
if (frameCount > 0)
{
for (int i = 0; i < frameCount; ++i)
{
ArrayPtr<byte> dataStartCopy = dataStart;
uint frameSize = frameSizes[i];
if (frameSize > (uint)dataStart.Length)
{
return CodecErr.CorruptFrame;
}
res = ReceiveCompressedData(allocator, frameSize, ref dataStartCopy);
if (res != CodecErr.Ok)
{
return res;
}
dataStart = dataStart.Slice((int)frameSize);
}
}
else
{
while (dataStart.Length != 0)
{
uint frameSize = (uint)dataStart.Length;
res = ReceiveCompressedData(allocator, frameSize, ref dataStart);
if (res != CodecErr.Ok)
{
return res;
}
// Account for suboptimal termination by the encoder.
while (dataStart.Length != 0)
{
byte marker = Types.Decoder.ReadMarker(dataStart);
if (marker != 0)
{
break;
}
dataStart = dataStart.Slice(1);
}
}
}
return res;
}
}
internal static class Decoder
{
public static byte ReadMarker(ArrayPtr<byte> data)
{
return data[0];
}
public static CodecErr ParseSuperframeIndex(ArrayPtr<byte> data, ulong dataSz, ref Array8<uint> sizes, out int count)
{
// A chunk ending with a byte matching 0xc0 is an invalid chunk unless
// it is a super frame index. If the last byte of real video compression
// data is 0xc0 the encoder must add a 0 byte. If we have the marker but
// not the associated matching marker byte at the front of the index we have
// an invalid bitstream and need to return an error.
byte marker;
Debug.Assert(dataSz != 0);
marker = ReadMarker(data.Slice((int)dataSz - 1));
count = 0;
if ((marker & 0xe0) == 0xc0)
{
uint frames = (uint)(marker & 0x7) + 1;
uint mag = (uint)((marker >> 3) & 0x3) + 1;
ulong indexSz = 2 + (mag * frames);
// This chunk is marked as having a superframe index but doesn't have
// enough data for it, thus it's an invalid superframe index.
if (dataSz < indexSz)
{
return CodecErr.CorruptFrame;
}
{
byte marker2 = ReadMarker(data.Slice((int)(dataSz - indexSz)));
// This chunk is marked as having a superframe index but doesn't have
// the matching marker byte at the front of the index therefore it's an
// invalid chunk.
if (marker != marker2)
{
return CodecErr.CorruptFrame;
}
}
{
// Found a valid superframe index.
ArrayPtr<byte> x = data.Slice((int)(dataSz - indexSz + 1));
for (int i = 0; i < frames; ++i)
{
uint thisSz = 0;
for (int j = 0; j < mag; ++j)
{
thisSz |= (uint)x[0] << j * 8;
x = x.Slice(1);
}
sizes[i] = thisSz;
}
count = (int)frames;
}
}
return CodecErr.Ok;
}
}
}

View file

@ -0,0 +1,10 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal struct VpxCodecFrameBuffer
{
public ArrayPtr<byte> Data;
public Ptr<InternalFrameBuffer> Priv;
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum VpxColorRange
{
// Y [16..235], UV [16..240]
Studio,
// YUV/RGB [0..255]
Full
}
}

View file

@ -0,0 +1,29 @@
namespace Ryujinx.Graphics.Nvdec.Vp9.Types
{
internal enum VpxColorSpace
{
// Unknown
Unknown,
// BT.601
Bt601,
// BT.709
Bt709,
// SMPTE.170
Smpte170,
// SMPTE.240
Smpte240,
// BT.2020
Bt2020,
// Reserved
Reserved,
// sRGB
Srgb
}
}