aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Host1x
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Graphics.Host1x
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Graphics.Host1x')
-rw-r--r--src/Ryujinx.Graphics.Host1x/ClassId.cs20
-rw-r--r--src/Ryujinx.Graphics.Host1x/Devices.cs32
-rw-r--r--src/Ryujinx.Graphics.Host1x/Host1xClass.cs33
-rw-r--r--src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs43
-rw-r--r--src/Ryujinx.Graphics.Host1x/Host1xDevice.cs174
-rw-r--r--src/Ryujinx.Graphics.Host1x/OpCode.cs21
-rw-r--r--src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj12
-rw-r--r--src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs99
-rw-r--r--src/Ryujinx.Graphics.Host1x/ThiDevice.cs137
-rw-r--r--src/Ryujinx.Graphics.Host1x/ThiRegisters.cs24
10 files changed, 595 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Host1x/ClassId.cs b/src/Ryujinx.Graphics.Host1x/ClassId.cs
new file mode 100644
index 00000000..dfeadd4c
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/ClassId.cs
@@ -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
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/Devices.cs b/src/Ryujinx.Graphics.Host1x/Devices.cs
new file mode 100644
index 00000000..5b3bed6b
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/Devices.cs
@@ -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();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/Host1xClass.cs b/src/Ryujinx.Graphics.Host1x/Host1xClass.cs
new file mode 100644
index 00000000..1a1297f9
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/Host1xClass.cs
@@ -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);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs b/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs
new file mode 100644
index 00000000..f9f4889b
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/Host1xClassRegisters.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Common.Memory;
+
+namespace Ryujinx.Graphics.Host1x
+{
+ struct Host1xClassRegisters
+ {
+#pragma warning disable CS0649
+ 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;
+#pragma warning restore CS0649
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs b/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs
new file mode 100644
index 00000000..90dd4fa0
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/Host1xDevice.cs
@@ -0,0 +1,174 @@
+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 struct Command
+ {
+ public int[] Buffer { get; }
+ public long ContextId { get; }
+
+ public Command(int[] buffer, long contextId)
+ {
+ Buffer = buffer;
+ ContextId = contextId;
+ }
+ }
+
+ private readonly SyncptIncrManager _syncptIncrMgr;
+ private readonly AsyncWorkQueue<Command> _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<Command>(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 long CreateContext()
+ {
+ if (_devices.GetDevice(ClassId.Nvdec) is IDeviceStateWithContext nvdec)
+ {
+ return nvdec.CreateContext();
+ }
+
+ return -1;
+ }
+
+ public void DestroyContext(long id)
+ {
+ if (id == -1)
+ {
+ return;
+ }
+
+ if (_devices.GetDevice(ClassId.Nvdec) is IDeviceStateWithContext nvdec)
+ {
+ nvdec.DestroyContext(id);
+ }
+ }
+
+ private void SetNvdecContext(long id)
+ {
+ if (id == -1)
+ {
+ return;
+ }
+
+ if (_devices.GetDevice(ClassId.Nvdec) is IDeviceStateWithContext nvdec)
+ {
+ nvdec.BindContext(id);
+ }
+ }
+
+ public void Submit(ReadOnlySpan<int> commandBuffer, long contextId)
+ {
+ _commandQueue.Add(new Command(commandBuffer.ToArray(), contextId));
+ }
+
+ private void Process(Command command)
+ {
+ SetNvdecContext(command.ContextId);
+ int[] commandBuffer = command.Buffer;
+
+ 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.Error?.Print(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();
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/OpCode.cs b/src/Ryujinx.Graphics.Host1x/OpCode.cs
new file mode 100644
index 00000000..2ec6034b
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/OpCode.cs
@@ -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
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj b/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
new file mode 100644
index 00000000..3cff4061
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net7.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
+ <ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs b/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
new file mode 100644
index 00000000..62c49917
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
@@ -0,0 +1,99 @@
+using Ryujinx.Graphics.Gpu.Synchronization;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Host1x
+{
+ class SyncptIncrManager
+ {
+ private readonly SynchronizationManager _syncMgr;
+
+ private readonly 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);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/ThiDevice.cs b/src/Ryujinx.Graphics.Host1x/ThiDevice.cs
new file mode 100644
index 00000000..259c8836
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/ThiDevice.cs
@@ -0,0 +1,137 @@
+using Ryujinx.Common;
+using Ryujinx.Graphics.Device;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Host1x
+{
+ class ThiDevice : IDeviceStateWithContext, IDisposable
+ {
+ private readonly ClassId _classId;
+ private readonly IDeviceState _device;
+
+ private readonly SyncptIncrManager _syncptIncrMgr;
+
+ private long _currentContextId;
+ private long _previousContextId;
+
+ private class CommandAction
+ {
+ public long ContextId { get; }
+ public int Data { get; }
+
+ public CommandAction(long contextId, int data)
+ {
+ ContextId = contextId;
+ Data = data;
+ }
+ }
+
+ private class MethodCallAction : CommandAction
+ {
+ public int Method { get; }
+
+ public MethodCallAction(long contextId, int method, int data) : base(contextId, data)
+ {
+ Method = method;
+ }
+ }
+
+ private class SyncptIncrAction : CommandAction
+ {
+ public SyncptIncrAction(long contextId, uint syncptIncrHandle) : base(contextId, (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) }
+ });
+
+ _previousContextId = -1;
+ }
+
+ public long CreateContext()
+ {
+ if (_device is IDeviceStateWithContext deviceWithContext)
+ {
+ return deviceWithContext.CreateContext();
+ }
+
+ return -1;
+ }
+
+ public void DestroyContext(long id)
+ {
+ if (_device is IDeviceStateWithContext deviceWithContext)
+ {
+ deviceWithContext.DestroyContext(id);
+ }
+ }
+
+ public void BindContext(long id)
+ {
+ _currentContextId = id;
+ }
+
+ 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(_currentContextId, _syncptIncrMgr.IncrementWhenDone(_classId, syncpointId)));
+ }
+ }
+
+ private void Method1(int data)
+ {
+ _commandQueue.Add(new MethodCallAction(_currentContextId, (int)_state.State.Method0 * sizeof(uint), data));
+ }
+
+ private void Process(CommandAction cmdAction)
+ {
+ long contextId = cmdAction.ContextId;
+ if (contextId != _previousContextId)
+ {
+ _previousContextId = contextId;
+
+ if (_device is IDeviceStateWithContext deviceWithContext)
+ {
+ deviceWithContext.BindContext(contextId);
+ }
+ }
+
+ 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();
+ }
+}
diff --git a/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs b/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs
new file mode 100644
index 00000000..71c48511
--- /dev/null
+++ b/src/Ryujinx.Graphics.Host1x/ThiRegisters.cs
@@ -0,0 +1,24 @@
+using Ryujinx.Common.Memory;
+
+namespace Ryujinx.Graphics.Host1x
+{
+ struct ThiRegisters
+ {
+#pragma warning disable CS0649
+ 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;
+#pragma warning restore CS0649
+ }
+}