NvHostChannelDeviceFile.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Gpu.Memory;
  3. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
  4. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
  6. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
  7. using Ryujinx.HLE.HOS.Services.Nv.Types;
  8. using System;
  9. using System.Runtime.CompilerServices;
  10. using System.Runtime.InteropServices;
  11. namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
  12. {
  13. class NvHostChannelDeviceFile : NvDeviceFile
  14. {
  15. private const uint MaxModuleSyncpoint = 16;
  16. private uint _timeout;
  17. private uint _submitTimeout;
  18. private uint _timeslice;
  19. private Switch _device;
  20. private Cpu.MemoryManager _memory;
  21. public enum ResourcePolicy
  22. {
  23. Device,
  24. Channel
  25. }
  26. protected static uint[] DeviceSyncpoints = new uint[MaxModuleSyncpoint];
  27. protected uint[] ChannelSyncpoints;
  28. protected static ResourcePolicy ChannelResourcePolicy = ResourcePolicy.Device;
  29. private NvFence _channelSyncpoint;
  30. public NvHostChannelDeviceFile(ServiceCtx context) : base(context)
  31. {
  32. _device = context.Device;
  33. _memory = context.Memory;
  34. _timeout = 3000;
  35. _submitTimeout = 0;
  36. _timeslice = 0;
  37. ChannelSyncpoints = new uint[MaxModuleSyncpoint];
  38. _channelSyncpoint.Id = _device.System.HostSyncpoint.AllocateSyncpoint(false);
  39. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  40. }
  41. public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
  42. {
  43. NvInternalResult result = NvInternalResult.NotImplemented;
  44. if (command.Type == NvIoctl.NvHostCustomMagic)
  45. {
  46. switch (command.Number)
  47. {
  48. case 0x01:
  49. result = Submit(arguments);
  50. break;
  51. case 0x02:
  52. result = CallIoctlMethod<GetParameterArguments>(GetSyncpoint, arguments);
  53. break;
  54. case 0x03:
  55. result = CallIoctlMethod<GetParameterArguments>(GetWaitBase, arguments);
  56. break;
  57. case 0x07:
  58. result = CallIoctlMethod<uint>(SetSubmitTimeout, arguments);
  59. break;
  60. case 0x09:
  61. result = MapCommandBuffer(arguments);
  62. break;
  63. case 0x0a:
  64. result = UnmapCommandBuffer(arguments);
  65. break;
  66. }
  67. }
  68. else if (command.Type == NvIoctl.NvHostMagic)
  69. {
  70. switch (command.Number)
  71. {
  72. case 0x01:
  73. result = CallIoctlMethod<int>(SetNvMapFd, arguments);
  74. break;
  75. case 0x03:
  76. result = CallIoctlMethod<uint>(SetTimeout, arguments);
  77. break;
  78. case 0x08:
  79. result = SubmitGpfifo(arguments);
  80. break;
  81. case 0x09:
  82. result = CallIoctlMethod<AllocObjCtxArguments>(AllocObjCtx, arguments);
  83. break;
  84. case 0x0b:
  85. result = CallIoctlMethod<ZcullBindArguments>(ZcullBind, arguments);
  86. break;
  87. case 0x0c:
  88. result = CallIoctlMethod<SetErrorNotifierArguments>(SetErrorNotifier, arguments);
  89. break;
  90. case 0x0d:
  91. result = CallIoctlMethod<NvChannelPriority>(SetPriority, arguments);
  92. break;
  93. case 0x18:
  94. result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx, arguments);
  95. break;
  96. case 0x1a:
  97. result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx2, arguments);
  98. break;
  99. case 0x1d:
  100. result = CallIoctlMethod<uint>(SetTimeslice, arguments);
  101. break;
  102. }
  103. }
  104. else if (command.Type == NvIoctl.NvGpuMagic)
  105. {
  106. switch (command.Number)
  107. {
  108. case 0x14:
  109. result = CallIoctlMethod<ulong>(SetUserData, arguments);
  110. break;
  111. }
  112. }
  113. return result;
  114. }
  115. private NvInternalResult Submit(Span<byte> arguments)
  116. {
  117. SubmitArguments submitHeader = GetSpanAndSkip<SubmitArguments>(ref arguments, 1)[0];
  118. Span<CommandBuffer> commandBuffers = GetSpanAndSkip<CommandBuffer>(ref arguments, submitHeader.CmdBufsCount);
  119. Span<Reloc> relocs = GetSpanAndSkip<Reloc>(ref arguments, submitHeader.RelocsCount);
  120. Span<uint> relocShifts = GetSpanAndSkip<uint>(ref arguments, submitHeader.RelocsCount);
  121. Span<SyncptIncr> syncptIncrs = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount);
  122. Span<SyncptIncr> waitChecks = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount); // ?
  123. Span<Fence> fences = GetSpanAndSkip<Fence>(ref arguments, submitHeader.FencesCount);
  124. lock (_device)
  125. {
  126. for (int i = 0; i < syncptIncrs.Length; i++)
  127. {
  128. SyncptIncr syncptIncr = syncptIncrs[i];
  129. uint id = syncptIncr.Id;
  130. fences[i].Id = id;
  131. fences[i].Thresh = Context.Device.System.HostSyncpoint.IncrementSyncpointMax(id, syncptIncr.Incrs);
  132. }
  133. foreach (CommandBuffer commandBuffer in commandBuffers)
  134. {
  135. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem);
  136. var data = _memory.GetSpan((ulong)map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
  137. _device.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data));
  138. }
  139. }
  140. fences[0].Thresh = Context.Device.System.HostSyncpoint.IncrementSyncpointMax(fences[0].Id, 1);
  141. Span<int> tmpCmdBuff = stackalloc int[1];
  142. tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
  143. _device.Host1x.Submit(tmpCmdBuff);
  144. return NvInternalResult.Success;
  145. }
  146. private Span<T> GetSpanAndSkip<T>(ref Span<byte> arguments, int count) where T : unmanaged
  147. {
  148. Span<T> output = MemoryMarshal.Cast<byte, T>(arguments).Slice(0, count);
  149. arguments = arguments.Slice(Unsafe.SizeOf<T>() * count);
  150. return output;
  151. }
  152. private NvInternalResult GetSyncpoint(ref GetParameterArguments arguments)
  153. {
  154. if (arguments.Parameter >= MaxModuleSyncpoint)
  155. {
  156. return NvInternalResult.InvalidInput;
  157. }
  158. if (ChannelResourcePolicy == ResourcePolicy.Device)
  159. {
  160. arguments.Value = GetSyncpointDevice(_device.System.HostSyncpoint, arguments.Parameter, false);
  161. }
  162. else
  163. {
  164. arguments.Value = GetSyncpointChannel(arguments.Parameter, false);
  165. }
  166. if (arguments.Value == 0)
  167. {
  168. return NvInternalResult.TryAgain;
  169. }
  170. return NvInternalResult.Success;
  171. }
  172. private NvInternalResult GetWaitBase(ref GetParameterArguments arguments)
  173. {
  174. arguments.Value = 0;
  175. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  176. return NvInternalResult.Success;
  177. }
  178. private NvInternalResult SetSubmitTimeout(ref uint submitTimeout)
  179. {
  180. _submitTimeout = submitTimeout;
  181. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  182. return NvInternalResult.Success;
  183. }
  184. private NvInternalResult MapCommandBuffer(Span<byte> arguments)
  185. {
  186. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  187. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  188. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  189. MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
  190. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  191. {
  192. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  193. if (map == null)
  194. {
  195. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  196. return NvInternalResult.InvalidInput;
  197. }
  198. lock (map)
  199. {
  200. if (map.DmaMapAddress == 0)
  201. {
  202. map.DmaMapAddress = (long)gmm.MapLow((ulong)map.Address, (uint)map.Size);
  203. }
  204. commandBufferEntry.MapAddress = (int)map.DmaMapAddress;
  205. }
  206. }
  207. return NvInternalResult.Success;
  208. }
  209. private NvInternalResult UnmapCommandBuffer(Span<byte> arguments)
  210. {
  211. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  212. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  213. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  214. MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
  215. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  216. {
  217. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  218. if (map == null)
  219. {
  220. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  221. return NvInternalResult.InvalidInput;
  222. }
  223. lock (map)
  224. {
  225. if (map.DmaMapAddress != 0)
  226. {
  227. // FIXME:
  228. // To make unmapping work, we need separate address space per channel.
  229. // Right now NVDEC and VIC share the GPU address space which is not correct at all.
  230. // gmm.Free((ulong)map.DmaMapAddress, (uint)map.Size);
  231. // map.DmaMapAddress = 0;
  232. }
  233. }
  234. }
  235. return NvInternalResult.Success;
  236. }
  237. private NvInternalResult SetNvMapFd(ref int nvMapFd)
  238. {
  239. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  240. return NvInternalResult.Success;
  241. }
  242. private NvInternalResult SetTimeout(ref uint timeout)
  243. {
  244. _timeout = timeout;
  245. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  246. return NvInternalResult.Success;
  247. }
  248. private NvInternalResult SubmitGpfifo(Span<byte> arguments)
  249. {
  250. int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>();
  251. SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0];
  252. Span<ulong> gpfifoEntries = MemoryMarshal.Cast<byte, ulong>(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries);
  253. return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries);
  254. }
  255. private NvInternalResult AllocObjCtx(ref AllocObjCtxArguments arguments)
  256. {
  257. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  258. return NvInternalResult.Success;
  259. }
  260. private NvInternalResult ZcullBind(ref ZcullBindArguments arguments)
  261. {
  262. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  263. return NvInternalResult.Success;
  264. }
  265. private NvInternalResult SetErrorNotifier(ref SetErrorNotifierArguments arguments)
  266. {
  267. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  268. return NvInternalResult.Success;
  269. }
  270. private NvInternalResult SetPriority(ref NvChannelPriority priority)
  271. {
  272. switch (priority)
  273. {
  274. case NvChannelPriority.Low:
  275. _timeslice = 1300; // Timeslice low priority in micro-seconds
  276. break;
  277. case NvChannelPriority.Medium:
  278. _timeslice = 2600; // Timeslice medium priority in micro-seconds
  279. break;
  280. case NvChannelPriority.High:
  281. _timeslice = 5200; // Timeslice high priority in micro-seconds
  282. break;
  283. default:
  284. return NvInternalResult.InvalidInput;
  285. }
  286. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  287. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  288. return NvInternalResult.Success;
  289. }
  290. private NvInternalResult AllocGpfifoEx(ref AllocGpfifoExArguments arguments)
  291. {
  292. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  293. arguments.Fence = _channelSyncpoint;
  294. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  295. return NvInternalResult.Success;
  296. }
  297. private NvInternalResult AllocGpfifoEx2(ref AllocGpfifoExArguments arguments)
  298. {
  299. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  300. arguments.Fence = _channelSyncpoint;
  301. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  302. return NvInternalResult.Success;
  303. }
  304. private NvInternalResult SetTimeslice(ref uint timeslice)
  305. {
  306. if (timeslice < 1000 || timeslice > 50000)
  307. {
  308. return NvInternalResult.InvalidInput;
  309. }
  310. _timeslice = timeslice; // in micro-seconds
  311. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  312. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  313. return NvInternalResult.Success;
  314. }
  315. private NvInternalResult SetUserData(ref ulong userData)
  316. {
  317. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  318. return NvInternalResult.Success;
  319. }
  320. protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span<ulong> entries)
  321. {
  322. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  323. {
  324. return NvInternalResult.InvalidInput;
  325. }
  326. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
  327. {
  328. _device.Gpu.GPFifo.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
  329. }
  330. _device.Gpu.GPFifo.PushEntries(entries);
  331. header.Fence.Id = _channelSyncpoint.Id;
  332. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  333. {
  334. uint incrementCount = header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) ? 2u : 0u;
  335. if (header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  336. {
  337. incrementCount += header.Fence.Value;
  338. }
  339. header.Fence.Value = _device.System.HostSyncpoint.IncrementSyncpointMaxExt(header.Fence.Id, (int)incrementCount);
  340. }
  341. else
  342. {
  343. header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id);
  344. }
  345. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
  346. {
  347. _device.Gpu.GPFifo.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
  348. }
  349. header.Flags = SubmitGpfifoFlags.None;
  350. _device.Gpu.GPFifo.SignalNewEntries();
  351. return NvInternalResult.Success;
  352. }
  353. public uint GetSyncpointChannel(uint index, bool isClientManaged)
  354. {
  355. if (ChannelSyncpoints[index] != 0)
  356. {
  357. return ChannelSyncpoints[index];
  358. }
  359. ChannelSyncpoints[index] = _device.System.HostSyncpoint.AllocateSyncpoint(isClientManaged);
  360. return ChannelSyncpoints[index];
  361. }
  362. public static uint GetSyncpointDevice(NvHostSyncpt syncpointManager, uint index, bool isClientManaged)
  363. {
  364. if (DeviceSyncpoints[index] != 0)
  365. {
  366. return DeviceSyncpoints[index];
  367. }
  368. DeviceSyncpoints[index] = syncpointManager.AllocateSyncpoint(isClientManaged);
  369. return DeviceSyncpoints[index];
  370. }
  371. private static int[] CreateWaitCommandBuffer(NvFence fence)
  372. {
  373. int[] commandBuffer = new int[4];
  374. // SyncpointValue = fence.Value;
  375. commandBuffer[0] = 0x2001001C;
  376. commandBuffer[1] = (int)fence.Value;
  377. // SyncpointAction(fence.id, increment: false, switch_en: true);
  378. commandBuffer[2] = 0x2001001D;
  379. commandBuffer[3] = (((int)fence.Id << 8) | (0 << 0) | (1 << 4));
  380. return commandBuffer;
  381. }
  382. private int[] CreateIncrementCommandBuffer(ref NvFence fence, SubmitGpfifoFlags flags)
  383. {
  384. bool hasWfi = !flags.HasFlag(SubmitGpfifoFlags.SuppressWfi);
  385. int[] commandBuffer;
  386. int offset = 0;
  387. if (hasWfi)
  388. {
  389. commandBuffer = new int[8];
  390. // WaitForInterrupt(handle)
  391. commandBuffer[offset++] = 0x2001001E;
  392. commandBuffer[offset++] = 0x0;
  393. }
  394. else
  395. {
  396. commandBuffer = new int[6];
  397. }
  398. // SyncpointValue = 0x0;
  399. commandBuffer[offset++] = 0x2001001C;
  400. commandBuffer[offset++] = 0x0;
  401. // Increment the syncpoint 2 times. (mitigate a hardware bug)
  402. // SyncpointAction(fence.id, increment: true, switch_en: false);
  403. commandBuffer[offset++] = 0x2001001D;
  404. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  405. // SyncpointAction(fence.id, increment: true, switch_en: false);
  406. commandBuffer[offset++] = 0x2001001D;
  407. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  408. return commandBuffer;
  409. }
  410. public override void Close() { }
  411. }
  412. }