GPFifoDevice.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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)).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. /// <summary>
  60. /// Creates a new instance of the GPU General Purpose FIFO device.
  61. /// </summary>
  62. /// <param name="context">GPU context that the GPFIFO belongs to</param>
  63. internal GPFifoDevice(GpuContext context)
  64. {
  65. _commandBufferQueue = new ConcurrentQueue<CommandBuffer>();
  66. _ibEnable = true;
  67. _context = context;
  68. _event = new AutoResetEvent(false);
  69. _processor = new GPFifoProcessor(context);
  70. }
  71. /// <summary>
  72. /// Signal the FIFO that there are new entries to process.
  73. /// </summary>
  74. public void SignalNewEntries()
  75. {
  76. _event.Set();
  77. }
  78. /// <summary>
  79. /// Push a GPFIFO entry in the form of a prefetched command buffer.
  80. /// It is intended to be used by nvservices to handle special cases.
  81. /// </summary>
  82. /// <param name="commandBuffer">The command buffer containing the prefetched commands</param>
  83. public void PushHostCommandBuffer(int[] commandBuffer)
  84. {
  85. _commandBufferQueue.Enqueue(new CommandBuffer
  86. {
  87. Type = CommandBufferType.Prefetch,
  88. Words = commandBuffer,
  89. EntryAddress = ulong.MaxValue,
  90. EntryCount = (uint)commandBuffer.Length
  91. });
  92. }
  93. /// <summary>
  94. /// Create a CommandBuffer from a GPFIFO entry.
  95. /// </summary>
  96. /// <param name="entry">The GPFIFO entry</param>
  97. /// <returns>A new CommandBuffer based on the GPFIFO entry</returns>
  98. private CommandBuffer CreateCommandBuffer(GPEntry entry)
  99. {
  100. CommandBufferType type = CommandBufferType.Prefetch;
  101. if (entry.Entry1Sync == Entry1Sync.Wait)
  102. {
  103. type = CommandBufferType.NoPrefetch;
  104. }
  105. ulong startAddress = ((ulong)entry.Entry0Get << 2) | ((ulong)entry.Entry1GetHi << 32);
  106. return new CommandBuffer
  107. {
  108. Type = type,
  109. Words = null,
  110. EntryAddress = startAddress,
  111. EntryCount = (uint)entry.Entry1Length
  112. };
  113. }
  114. /// <summary>
  115. /// Pushes GPFIFO entries.
  116. /// </summary>
  117. /// <param name="entries">GPFIFO entries</param>
  118. public void PushEntries(ReadOnlySpan<ulong> entries)
  119. {
  120. bool beforeBarrier = true;
  121. for (int index = 0; index < entries.Length; index++)
  122. {
  123. ulong entry = entries[index];
  124. CommandBuffer commandBuffer = CreateCommandBuffer(Unsafe.As<ulong, GPEntry>(ref entry));
  125. if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
  126. {
  127. commandBuffer.Fetch(_context);
  128. }
  129. if (commandBuffer.Type == CommandBufferType.NoPrefetch)
  130. {
  131. beforeBarrier = false;
  132. }
  133. _commandBufferQueue.Enqueue(commandBuffer);
  134. }
  135. }
  136. /// <summary>
  137. /// Waits until commands are pushed to the FIFO.
  138. /// </summary>
  139. /// <returns>True if commands were received, false if wait timed out</returns>
  140. public bool WaitForCommands()
  141. {
  142. return _event.WaitOne(8);
  143. }
  144. /// <summary>
  145. /// Processes commands pushed to the FIFO.
  146. /// </summary>
  147. public void DispatchCalls()
  148. {
  149. while (_ibEnable && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
  150. {
  151. _currentCommandBuffer = entry;
  152. _currentCommandBuffer.Fetch(_context);
  153. _processor.Process(_currentCommandBuffer.Words);
  154. }
  155. }
  156. /// <summary>
  157. /// Disposes of resources used for GPFifo command processing.
  158. /// </summary>
  159. public void Dispose() => _event.Dispose();
  160. }
  161. }