GPFifoDevice.cs 8.4 KB

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