GPFifoDevice.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. using System.Threading;
  6. namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
  7. {
  8. /// <summary>
  9. /// Represents a GPU General Purpose FIFO device.
  10. /// </summary>
  11. public sealed class GPFifoDevice : IDisposable
  12. {
  13. /// <summary>
  14. /// Indicates if the command buffer has pre-fetch enabled.
  15. /// </summary>
  16. private enum CommandBufferType
  17. {
  18. Prefetch,
  19. NoPrefetch
  20. }
  21. /// <summary>
  22. /// Command buffer data.
  23. /// </summary>
  24. private struct CommandBuffer
  25. {
  26. /// <summary>
  27. /// Processor used to process the command buffer. Contains channel state.
  28. /// </summary>
  29. public GPFifoProcessor Processor;
  30. /// <summary>
  31. /// The type of the command buffer.
  32. /// </summary>
  33. public CommandBufferType Type;
  34. /// <summary>
  35. /// Fetched data.
  36. /// </summary>
  37. public int[] Words;
  38. /// <summary>
  39. /// The GPFIFO entry address (used in <see cref="CommandBufferType.NoPrefetch"/> mode).
  40. /// </summary>
  41. public ulong EntryAddress;
  42. /// <summary>
  43. /// The count of entries inside this GPFIFO entry.
  44. /// </summary>
  45. public uint EntryCount;
  46. /// <summary>
  47. /// Fetch the command buffer.
  48. /// </summary>
  49. public void Fetch(GpuContext context)
  50. {
  51. if (Words == null)
  52. {
  53. Words = MemoryMarshal.Cast<byte, int>(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
  54. }
  55. }
  56. }
  57. private readonly ConcurrentQueue<CommandBuffer> _commandBufferQueue;
  58. private CommandBuffer _currentCommandBuffer;
  59. private GPFifoProcessor _prevChannelProcessor;
  60. private readonly bool _ibEnable;
  61. private readonly GpuContext _context;
  62. private readonly AutoResetEvent _event;
  63. private bool _interrupt;
  64. /// <summary>
  65. /// Creates a new instance of the GPU General Purpose FIFO device.
  66. /// </summary>
  67. /// <param name="context">GPU context that the GPFIFO belongs to</param>
  68. internal GPFifoDevice(GpuContext context)
  69. {
  70. _commandBufferQueue = new ConcurrentQueue<CommandBuffer>();
  71. _ibEnable = true;
  72. _context = context;
  73. _event = new AutoResetEvent(false);
  74. }
  75. /// <summary>
  76. /// Signal the FIFO that there are new entries to process.
  77. /// </summary>
  78. public void SignalNewEntries()
  79. {
  80. _event.Set();
  81. }
  82. /// <summary>
  83. /// Push a GPFIFO entry in the form of a prefetched command buffer.
  84. /// It is intended to be used by nvservices to handle special cases.
  85. /// </summary>
  86. /// <param name="processor">Processor used to process <paramref name="commandBuffer"/></param>
  87. /// <param name="commandBuffer">The command buffer containing the prefetched commands</param>
  88. internal void PushHostCommandBuffer(GPFifoProcessor processor, int[] commandBuffer)
  89. {
  90. _commandBufferQueue.Enqueue(new CommandBuffer
  91. {
  92. Processor = processor,
  93. Type = CommandBufferType.Prefetch,
  94. Words = commandBuffer,
  95. EntryAddress = ulong.MaxValue,
  96. EntryCount = (uint)commandBuffer.Length
  97. });
  98. }
  99. /// <summary>
  100. /// Create a CommandBuffer from a GPFIFO entry.
  101. /// </summary>
  102. /// <param name="processor">Processor used to process the command buffer pointed to by <paramref name="entry"/></param>
  103. /// <param name="entry">The GPFIFO entry</param>
  104. /// <returns>A new CommandBuffer based on the GPFIFO entry</returns>
  105. private static CommandBuffer CreateCommandBuffer(GPFifoProcessor processor, GPEntry entry)
  106. {
  107. CommandBufferType type = CommandBufferType.Prefetch;
  108. if (entry.Entry1Sync == Entry1Sync.Wait)
  109. {
  110. type = CommandBufferType.NoPrefetch;
  111. }
  112. ulong startAddress = ((ulong)entry.Entry0Get << 2) | ((ulong)entry.Entry1GetHi << 32);
  113. return new CommandBuffer
  114. {
  115. Processor = processor,
  116. Type = type,
  117. Words = null,
  118. EntryAddress = startAddress,
  119. EntryCount = (uint)entry.Entry1Length
  120. };
  121. }
  122. /// <summary>
  123. /// Pushes GPFIFO entries.
  124. /// </summary>
  125. /// <param name="processor">Processor used to process the command buffers pointed to by <paramref name="entries"/></param>
  126. /// <param name="entries">GPFIFO entries</param>
  127. internal void PushEntries(GPFifoProcessor processor, ReadOnlySpan<ulong> entries)
  128. {
  129. bool beforeBarrier = true;
  130. for (int index = 0; index < entries.Length; index++)
  131. {
  132. ulong entry = entries[index];
  133. CommandBuffer commandBuffer = CreateCommandBuffer(processor, Unsafe.As<ulong, GPEntry>(ref entry));
  134. if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
  135. {
  136. commandBuffer.Fetch(_context);
  137. }
  138. if (commandBuffer.Type == CommandBufferType.NoPrefetch)
  139. {
  140. beforeBarrier = false;
  141. }
  142. _commandBufferQueue.Enqueue(commandBuffer);
  143. }
  144. }
  145. /// <summary>
  146. /// Waits until commands are pushed to the FIFO.
  147. /// </summary>
  148. /// <returns>True if commands were received, false if wait timed out</returns>
  149. public bool WaitForCommands()
  150. {
  151. return !_commandBufferQueue.IsEmpty || (_event.WaitOne(8) && !_commandBufferQueue.IsEmpty);
  152. }
  153. /// <summary>
  154. /// Processes commands pushed to the FIFO.
  155. /// </summary>
  156. public void DispatchCalls()
  157. {
  158. // Use this opportunity to also dispose any pending channels that were closed.
  159. _context.DisposePendingChannels();
  160. // Process command buffers.
  161. while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
  162. {
  163. _currentCommandBuffer = entry;
  164. _currentCommandBuffer.Fetch(_context);
  165. // If we are changing the current channel,
  166. // we need to force all the host state to be updated.
  167. if (_prevChannelProcessor != entry.Processor)
  168. {
  169. _prevChannelProcessor = entry.Processor;
  170. entry.Processor.ForceAllDirty();
  171. }
  172. entry.Processor.Process(_currentCommandBuffer.Words);
  173. }
  174. _interrupt = false;
  175. }
  176. /// <summary>
  177. /// Interrupts command processing. This will break out of the DispatchCalls loop.
  178. /// </summary>
  179. public void Interrupt()
  180. {
  181. _interrupt = true;
  182. }
  183. /// <summary>
  184. /// Disposes of resources used for GPFifo command processing.
  185. /// </summary>
  186. public void Dispose() => _event.Dispose();
  187. }
  188. }