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:
20
Ryujinx.Graphics.Host1x/ClassId.cs
Normal file
20
Ryujinx.Graphics.Host1x/ClassId.cs
Normal 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
|
||||
}
|
||||
}
|
32
Ryujinx.Graphics.Host1x/Devices.cs
Normal file
32
Ryujinx.Graphics.Host1x/Devices.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Graphics.Host1x/Host1xClass.cs
Normal file
33
Ryujinx.Graphics.Host1x/Host1xClass.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs
Normal file
41
Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs
Normal 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;
|
||||
}
|
||||
}
|
123
Ryujinx.Graphics.Host1x/Host1xDevice.cs
Normal file
123
Ryujinx.Graphics.Host1x/Host1xDevice.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx.Graphics.Host1x/OpCode.cs
Normal file
21
Ryujinx.Graphics.Host1x/OpCode.cs
Normal 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
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
Normal file
20
Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
Normal 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>
|
99
Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
Normal file
99
Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
Ryujinx.Graphics.Host1x/ThiDevice.cs
Normal file
96
Ryujinx.Graphics.Host1x/ThiDevice.cs
Normal 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();
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics.Host1x/ThiRegisters.cs
Normal file
22
Ryujinx.Graphics.Host1x/ThiRegisters.cs
Normal 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user