GPFifoDevice.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. /// The type of the command buffer.
  28. /// </summary>
  29. public CommandBufferType Type;
  30. /// <summary>
  31. /// Fetched data.
  32. /// </summary>
  33. public int[] Words;
  34. /// <summary>
  35. /// The GPFIFO entry address (used in <see cref="CommandBufferType.NoPrefetch"/> mode).
  36. /// </summary>
  37. public ulong EntryAddress;
  38. /// <summary>
  39. /// The count of entries inside this GPFIFO entry.
  40. /// </summary>
  41. public uint EntryCount;
  42. /// <summary>
  43. /// Fetch the command buffer.
  44. /// </summary>
  45. public void Fetch(GpuContext context)
  46. {
  47. if (Words == null)
  48. {
  49. Words = MemoryMarshal.Cast<byte, int>(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
  50. }
  51. }
  52. }
  53. private readonly ConcurrentQueue<CommandBuffer> _commandBufferQueue;
  54. private CommandBuffer _currentCommandBuffer;
  55. private readonly bool _ibEnable;
  56. private readonly GpuContext _context;
  57. private readonly AutoResetEvent _event;
  58. private readonly GPFifoProcessor _processor;
  59. private bool _interrupt;
  60. /// <summary>
  61. /// Creates a new instance of the GPU General Purpose FIFO device.
  62. /// </summary>
  63. /// <param name="context">GPU context that the GPFIFO belongs to</param>
  64. internal GPFifoDevice(GpuContext context)
  65. {
  66. _commandBufferQueue = new ConcurrentQueue<CommandBuffer>();
  67. _ibEnable = true;
  68. _context = context;
  69. _event = new AutoResetEvent(false);
  70. _processor = new GPFifoProcessor(context);
  71. }
  72. /// <summary>
  73. /// Signal the FIFO that there are new entries to process.
  74. /// </summary>
  75. public void SignalNewEntries()
  76. {
  77. _event.Set();
  78. }
  79. /// <summary>
  80. /// Push a GPFIFO entry in the form of a prefetched command buffer.
  81. /// It is intended to be used by nvservices to handle special cases.
  82. /// </summary>
  83. /// <param name="commandBuffer">The command buffer containing the prefetched commands</param>
  84. public void PushHostCommandBuffer(int[] commandBuffer)
  85. {
  86. _commandBufferQueue.Enqueue(new CommandBuffer
  87. {
  88. Type = CommandBufferType.Prefetch,
  89. Words = commandBuffer,
  90. EntryAddress = ulong.MaxValue,
  91. EntryCount = (uint)commandBuffer.Length
  92. });
  93. }
  94. /// <summary>
  95. /// Create a CommandBuffer from a GPFIFO entry.
  96. /// </summary>
  97. /// <param name="entry">The GPFIFO entry</param>
  98. /// <returns>A new CommandBuffer based on the GPFIFO entry</returns>
  99. private CommandBuffer CreateCommandBuffer(GPEntry entry)
  100. {
  101. CommandBufferType type = CommandBufferType.Prefetch;
  102. if (entry.Entry1Sync == Entry1Sync.Wait)
  103. {
  104. type = CommandBufferType.NoPrefetch;
  105. }
  106. ulong startAddress = ((ulong)entry.Entry0Get << 2) | ((ulong)entry.Entry1GetHi << 32);
  107. return new CommandBuffer
  108. {
  109. Type = type,
  110. Words = null,
  111. EntryAddress = startAddress,
  112. EntryCount = (uint)entry.Entry1Length
  113. };
  114. }
  115. /// <summary>
  116. /// Pushes GPFIFO entries.
  117. /// </summary>
  118. /// <param name="entries">GPFIFO entries</param>
  119. public void PushEntries(ReadOnlySpan<ulong> entries)
  120. {
  121. bool beforeBarrier = true;
  122. for (int index = 0; index < entries.Length; index++)
  123. {
  124. ulong entry = entries[index];
  125. CommandBuffer commandBuffer = CreateCommandBuffer(Unsafe.As<ulong, GPEntry>(ref entry));
  126. if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
  127. {
  128. commandBuffer.Fetch(_context);
  129. }
  130. if (commandBuffer.Type == CommandBufferType.NoPrefetch)
  131. {
  132. beforeBarrier = false;
  133. }
  134. _commandBufferQueue.Enqueue(commandBuffer);
  135. }
  136. }
  137. /// <summary>
  138. /// Waits until commands are pushed to the FIFO.
  139. /// </summary>
  140. /// <returns>True if commands were received, false if wait timed out</returns>
  141. public bool WaitForCommands()
  142. {
  143. return !_commandBufferQueue.IsEmpty || (_event.WaitOne(8) && !_commandBufferQueue.IsEmpty);
  144. }
  145. /// <summary>
  146. /// Processes commands pushed to the FIFO.
  147. /// </summary>
  148. public void DispatchCalls()
  149. {
  150. while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
  151. {
  152. _currentCommandBuffer = entry;
  153. _currentCommandBuffer.Fetch(_context);
  154. _processor.Process(_currentCommandBuffer.Words);
  155. }
  156. _interrupt = false;
  157. }
  158. /// <summary>
  159. /// Interrupts command processing. This will break out of the DispatchCalls loop.
  160. /// </summary>
  161. public void Interrupt()
  162. {
  163. _interrupt = true;
  164. }
  165. /// <summary>
  166. /// Disposes of resources used for GPFifo command processing.
  167. /// </summary>
  168. public void Dispose() => _event.Dispose();
  169. }
  170. }