New NVDEC and VIC implementation (#1384)

* Initial NVDEC and VIC implementation

* Update FFmpeg.AutoGen to 4.3.0

* Add nvdec dependencies for Windows

* Unify some VP9 structures

* Rename VP9 structure fields

* Improvements to Video API

* XML docs for Common.Memory

* Remove now unused or redundant overloads from MemoryAccessor

* NVDEC UV surface read/write scalar paths

* Add FIXME comments about hacky things/stuff that will need to be fixed in the future

* Cleaned up VP9 memory allocation

* Remove some debug logs

* Rename some VP9 structs

* Remove unused struct

* No need to compile Ryujinx.Graphics.Host1x with unsafe anymore

* Name AsyncWorkQueue threads to make debugging easier

* Make Vp9PictureInfo a ref struct

* LayoutConverter no longer needs the depth argument (broken by rebase)

* Pooling of VP9 buffers, plus fix a memory leak on VP9

* Really wish VS could rename projects properly...

* Address feedback

* Remove using

* Catch OperationCanceledException

* Add licensing informations

* Add THIRDPARTY.md to release too

Co-authored-by: Thog <me@thog.eu>
This commit is contained in:
gdkchan
2020-07-12 00:07:01 -03:00
committed by GitHub
parent 38b26cf424
commit 4d02a2d2c0
202 changed files with 20563 additions and 2567 deletions

View File

@ -0,0 +1,20 @@
namespace Ryujinx.Graphics.Host1x
{
public enum ClassId
{
Host1x = 0x1,
Mpeg = 0x20,
Nvenc = 0x21,
Vi = 0x30,
Isp = 0x32,
Ispb = 0x34,
Vii2c = 0x36,
Vic = 0x5d,
Gr3d = 0x60,
Gpu = 0x61,
Tsec = 0xe0,
Tsecb = 0xe1,
Nvjpg = 0xc0,
Nvdec = 0xf0
}
}

View File

@ -0,0 +1,32 @@
using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Host1x
{
class Devices : IDisposable
{
private readonly Dictionary<ClassId, IDeviceState> _devices = new Dictionary<ClassId, IDeviceState>();
public void RegisterDevice(ClassId classId, IDeviceState device)
{
_devices[classId] = device;
}
public IDeviceState GetDevice(ClassId classId)
{
return _devices.TryGetValue(classId, out IDeviceState device) ? device : null;
}
public void Dispose()
{
foreach (var device in _devices.Values)
{
if (device is ThiDevice thi)
{
thi.Dispose();
}
}
}
}
}

View File

@ -0,0 +1,33 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Synchronization;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Graphics.Host1x
{
public class Host1xClass : IDeviceState
{
private readonly SynchronizationManager _syncMgr;
private readonly DeviceState<Host1xClassRegisters> _state;
public Host1xClass(SynchronizationManager syncMgr)
{
_syncMgr = syncMgr;
_state = new DeviceState<Host1xClassRegisters>(new Dictionary<string, RwCallback>
{
{ nameof(Host1xClassRegisters.WaitSyncpt32), new RwCallback(WaitSyncpt32, null) }
});
}
public int Read(int offset) => _state.Read(offset);
public void Write(int offset, int data) => _state.Write(offset, data);
private void WaitSyncpt32(int data)
{
uint syncpointId = (uint)(data & 0xFF);
uint threshold = _state.State.LoadSyncptPayload32;
_syncMgr.WaitOnSyncpoint(syncpointId, threshold, Timeout.InfiniteTimeSpan);
}
}
}

View File

@ -0,0 +1,41 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.Graphics.Host1x
{
struct Host1xClassRegisters
{
public uint IncrSyncpt;
public uint IncrSyncptCntrl;
public uint IncrSyncptError;
public Array5<uint> ReservedC;
public uint WaitSyncpt;
public uint WaitSyncptBase;
public uint WaitSyncptIncr;
public uint LoadSyncptBase;
public uint IncrSyncptBase;
public uint Clear;
public uint Wait;
public uint WaitWithIntr;
public uint DelayUsec;
public uint TickcountHi;
public uint TickcountLo;
public uint Tickctrl;
public Array23<uint> Reserved50;
public uint Indctrl;
public uint Indoff2;
public uint Indoff;
public Array31<uint> Inddata;
public uint Reserved134;
public uint LoadSyncptPayload32;
public uint Stallctrl;
public uint WaitSyncpt32;
public uint WaitSyncptBase32;
public uint LoadSyncptBase32;
public uint IncrSyncptBase32;
public uint StallcountHi;
public uint StallcountLo;
public uint Xrefctrl;
public uint ChannelXrefHi;
public uint ChannelXrefLo;
}
}

View File

@ -0,0 +1,123 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Synchronization;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Host1x
{
public sealed class Host1xDevice : IDisposable
{
private readonly SyncptIncrManager _syncptIncrMgr;
private readonly AsyncWorkQueue<int[]> _commandQueue;
private readonly Devices _devices = new Devices();
public Host1xClass Class { get; }
private IDeviceState _device;
private int _count;
private int _offset;
private int _mask;
private bool _incrementing;
public Host1xDevice(SynchronizationManager syncMgr)
{
_syncptIncrMgr = new SyncptIncrManager(syncMgr);
_commandQueue = new AsyncWorkQueue<int[]>(Process, "Ryujinx.Host1xProcessor");
Class = new Host1xClass(syncMgr);
_devices.RegisterDevice(ClassId.Host1x, Class);
}
public void RegisterDevice(ClassId classId, IDeviceState device)
{
var thi = new ThiDevice(classId, device ?? throw new ArgumentNullException(nameof(device)), _syncptIncrMgr);
_devices.RegisterDevice(classId, thi);
}
public void Submit(ReadOnlySpan<int> commandBuffer)
{
_commandQueue.Add(commandBuffer.ToArray());
}
private void Process(int[] commandBuffer)
{
for (int index = 0; index < commandBuffer.Length; index++)
{
Step(commandBuffer[index]);
}
}
private void Step(int value)
{
if (_mask != 0)
{
int lbs = BitOperations.TrailingZeroCount(_mask);
_mask &= ~(1 << lbs);
DeviceWrite(_offset + lbs, value);
return;
}
else if (_count != 0)
{
_count--;
DeviceWrite(_offset, value);
if (_incrementing)
{
_offset++;
}
return;
}
OpCode opCode = (OpCode)((value >> 28) & 0xf);
switch (opCode)
{
case OpCode.SetClass:
_mask = value & 0x3f;
ClassId classId = (ClassId)((value >> 6) & 0x3ff);
_offset = (value >> 16) & 0xfff;
_device = _devices.GetDevice(classId);
break;
case OpCode.Incr:
case OpCode.NonIncr:
_count = value & 0xffff;
_offset = (value >> 16) & 0xfff;
_incrementing = opCode == OpCode.Incr;
break;
case OpCode.Mask:
_mask = value & 0xffff;
_offset = (value >> 16) & 0xfff;
break;
case OpCode.Imm:
int data = value & 0xfff;
_offset = (value >> 16) & 0xfff;
DeviceWrite(_offset, data);
break;
default:
Logger.PrintError(LogClass.Host1x, $"Unsupported opcode \"{opCode}\".");
break;
}
}
private void DeviceWrite(int offset, int data)
{
_device?.Write(offset * 4, data);
}
public void Dispose()
{
_commandQueue.Dispose();
_devices.Dispose();
}
}
}

View File

@ -0,0 +1,21 @@
namespace Ryujinx.Graphics.Host1x
{
enum OpCode
{
SetClass,
Incr,
NonIncr,
Mask,
Imm,
Restart,
Gather,
SetStrmId,
SetAppId,
SetPyld,
IncrW,
NonIncrW,
GatherW,
RestartW,
Extend
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,99 @@
using Ryujinx.Graphics.Gpu.Synchronization;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Host1x
{
class SyncptIncrManager
{
private readonly SynchronizationManager _syncMgr;
private struct SyncptIncr
{
public uint Id { get; }
public ClassId ClassId { get; }
public uint SyncptId { get; }
public bool Done { get; }
public SyncptIncr(uint id, ClassId classId, uint syncptId, bool done = false)
{
Id = id;
ClassId = classId;
SyncptId = syncptId;
Done = done;
}
}
private readonly List<SyncptIncr> _incrs = new List<SyncptIncr>();
private uint _currentId;
public SyncptIncrManager(SynchronizationManager syncMgr)
{
_syncMgr = syncMgr;
}
public void Increment(uint id)
{
lock (_incrs)
{
_incrs.Add(new SyncptIncr(0, 0, id, true));
IncrementAllDone();
}
}
public uint IncrementWhenDone(ClassId classId, uint id)
{
lock (_incrs)
{
uint handle = _currentId++;
_incrs.Add(new SyncptIncr(handle, classId, id));
return handle;
}
}
public void SignalDone(uint handle)
{
lock (_incrs)
{
// Set pending increment with the given handle to "done".
for (int i = 0; i < _incrs.Count; i++)
{
SyncptIncr incr = _incrs[i];
if (_incrs[i].Id == handle)
{
_incrs[i] = new SyncptIncr(incr.Id, incr.ClassId, incr.SyncptId, true);
break;
}
}
IncrementAllDone();
}
}
private void IncrementAllDone()
{
lock (_incrs)
{
// Increment all sequential pending increments that are already done.
int doneCount = 0;
for (; doneCount < _incrs.Count; doneCount++)
{
if (!_incrs[doneCount].Done)
{
break;
}
_syncMgr.IncrementSyncpoint(_incrs[doneCount].SyncptId);
}
_incrs.RemoveRange(0, doneCount);
}
}
}
}

View File

@ -0,0 +1,96 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Host1x
{
class ThiDevice : IDeviceState, IDisposable
{
private readonly ClassId _classId;
private readonly IDeviceState _device;
private readonly SyncptIncrManager _syncptIncrMgr;
private class CommandAction
{
public int Data { get; }
public CommandAction(int data)
{
Data = data;
}
}
private class MethodCallAction : CommandAction
{
public int Method { get; }
public MethodCallAction(int method, int data) : base(data)
{
Method = method;
}
}
private class SyncptIncrAction : CommandAction
{
public SyncptIncrAction(uint syncptIncrHandle) : base((int)syncptIncrHandle)
{
}
}
private readonly AsyncWorkQueue<CommandAction> _commandQueue;
private readonly DeviceState<ThiRegisters> _state;
public ThiDevice(ClassId classId, IDeviceState device, SyncptIncrManager syncptIncrMgr)
{
_classId = classId;
_device = device;
_syncptIncrMgr = syncptIncrMgr;
_commandQueue = new AsyncWorkQueue<CommandAction>(Process, $"Ryujinx.{classId}Processor");
_state = new DeviceState<ThiRegisters>(new Dictionary<string, RwCallback>
{
{ nameof(ThiRegisters.IncrSyncpt), new RwCallback(IncrSyncpt, null) },
{ nameof(ThiRegisters.Method1), new RwCallback(Method1, null) }
});
}
public int Read(int offset) => _state.Read(offset);
public void Write(int offset, int data) => _state.Write(offset, data);
private void IncrSyncpt(int data)
{
uint syncpointId = (uint)(data & 0xFF);
uint cond = (uint)((data >> 8) & 0xFF); // 0 = Immediate, 1 = Done
if (cond == 0)
{
_syncptIncrMgr.Increment(syncpointId);
}
else
{
_commandQueue.Add(new SyncptIncrAction(_syncptIncrMgr.IncrementWhenDone(_classId, syncpointId)));
}
}
private void Method1(int data)
{
_commandQueue.Add(new MethodCallAction((int)_state.State.Method0 * 4, data));
}
private void Process(CommandAction cmdAction)
{
if (cmdAction is SyncptIncrAction syncptIncrAction)
{
_syncptIncrMgr.SignalDone((uint)syncptIncrAction.Data);
}
else if (cmdAction is MethodCallAction methodCallAction)
{
_device.Write(methodCallAction.Method, methodCallAction.Data);
}
}
public void Dispose() => _commandQueue.Dispose();
}
}

View File

@ -0,0 +1,22 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.Graphics.Host1x
{
struct ThiRegisters
{
public uint IncrSyncpt;
public uint Reserved4;
public uint IncrSyncptErr;
public uint CtxswIncrSyncpt;
public Array4<uint> Reserved10;
public uint Ctxsw;
public uint Reserved24;
public uint ContSyncptEof;
public Array5<uint> Reserved2C;
public uint Method0;
public uint Method1;
public Array12<uint> Reserved48;
public uint IntStatus;
public uint IntMask;
}
}