/ src / Ryujinx.Graphics.Gpu / Engine / GPFifo / GPFifoClass.cs
GPFifoClass.cs
  1  using Ryujinx.Graphics.Device;
  2  using Ryujinx.Graphics.Gpu.Engine.MME;
  3  using Ryujinx.Graphics.Gpu.Synchronization;
  4  using System;
  5  using System.Collections.Generic;
  6  using System.Threading;
  7  
  8  namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
  9  {
 10      /// <summary>
 11      /// Represents a GPU General Purpose FIFO class.
 12      /// </summary>
 13      class GPFifoClass : IDeviceState
 14      {
 15          private readonly GpuContext _context;
 16          private readonly GPFifoProcessor _parent;
 17          private readonly DeviceState<GPFifoClassState> _state;
 18  
 19          private bool _createSyncPending;
 20  
 21          private const int MacrosCount = 0x80;
 22  
 23          // Note: The size of the macro memory is unknown, we just make
 24          // a guess here and use 256kb as the size. Increase if needed.
 25          private const int MacroCodeSize = 256 * 256;
 26  
 27          private readonly Macro[] _macros;
 28          private readonly int[] _macroCode;
 29  
 30          /// <summary>
 31          /// Creates a new instance of the GPU General Purpose FIFO class.
 32          /// </summary>
 33          /// <param name="context">GPU context</param>
 34          /// <param name="parent">Parent GPU General Purpose FIFO processor</param>
 35          public GPFifoClass(GpuContext context, GPFifoProcessor parent)
 36          {
 37              _context = context;
 38              _parent = parent;
 39              _state = new DeviceState<GPFifoClassState>(new Dictionary<string, RwCallback>
 40              {
 41                  { nameof(GPFifoClassState.Semaphored), new RwCallback(Semaphored, null) },
 42                  { nameof(GPFifoClassState.Syncpointb), new RwCallback(Syncpointb, null) },
 43                  { nameof(GPFifoClassState.WaitForIdle), new RwCallback(WaitForIdle, null) },
 44                  { nameof(GPFifoClassState.SetReference), new RwCallback(SetReference, null) },
 45                  { nameof(GPFifoClassState.LoadMmeInstructionRam), new RwCallback(LoadMmeInstructionRam, null) },
 46                  { nameof(GPFifoClassState.LoadMmeStartAddressRam), new RwCallback(LoadMmeStartAddressRam, null) },
 47                  { nameof(GPFifoClassState.SetMmeShadowRamControl), new RwCallback(SetMmeShadowRamControl, null) },
 48              });
 49  
 50              _macros = new Macro[MacrosCount];
 51              _macroCode = new int[MacroCodeSize];
 52          }
 53  
 54          /// <summary>
 55          /// Create any syncs from WaitForIdle command that are currently pending.
 56          /// </summary>
 57          public void CreatePendingSyncs()
 58          {
 59              if (_createSyncPending)
 60              {
 61                  _createSyncPending = false;
 62                  _context.CreateHostSyncIfNeeded(HostSyncFlags.None);
 63              }
 64          }
 65  
 66          /// <summary>
 67          /// Reads data from the class registers.
 68          /// </summary>
 69          /// <param name="offset">Register byte offset</param>
 70          /// <returns>Data at the specified offset</returns>
 71          public int Read(int offset) => _state.Read(offset);
 72  
 73          /// <summary>
 74          /// Writes data to the class registers.
 75          /// </summary>
 76          /// <param name="offset">Register byte offset</param>
 77          /// <param name="data">Data to be written</param>
 78          public void Write(int offset, int data) => _state.Write(offset, data);
 79  
 80          /// <summary>
 81          /// Writes a GPU counter to guest memory.
 82          /// </summary>
 83          /// <param name="argument">Method call argument</param>
 84          public void Semaphored(int argument)
 85          {
 86              ulong address = ((ulong)_state.State.SemaphorebOffsetLower << 2) |
 87                              ((ulong)_state.State.SemaphoreaOffsetUpper << 32);
 88  
 89              int value = _state.State.SemaphorecPayload;
 90  
 91              SemaphoredOperation operation = _state.State.SemaphoredOperation;
 92  
 93              if (_state.State.SemaphoredReleaseSize == SemaphoredReleaseSize.SixteenBytes)
 94              {
 95                  _parent.MemoryManager.Write(address + 4, 0);
 96                  _parent.MemoryManager.Write(address + 8, _context.GetTimestamp());
 97              }
 98  
 99              // TODO: Acquire operations (Wait), interrupts for invalid combinations.
100              if (operation == SemaphoredOperation.Release)
101              {
102                  _parent.MemoryManager.Write(address, value);
103              }
104              else if (operation == SemaphoredOperation.Reduction)
105              {
106                  bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed;
107  
108                  int mem = _parent.MemoryManager.Read<int>(address);
109  
110                  switch (_state.State.SemaphoredReduction)
111                  {
112                      case SemaphoredReduction.Min:
113                          value = signed ? Math.Min(mem, value) : (int)Math.Min((uint)mem, (uint)value);
114                          break;
115                      case SemaphoredReduction.Max:
116                          value = signed ? Math.Max(mem, value) : (int)Math.Max((uint)mem, (uint)value);
117                          break;
118                      case SemaphoredReduction.Xor:
119                          value ^= mem;
120                          break;
121                      case SemaphoredReduction.And:
122                          value &= mem;
123                          break;
124                      case SemaphoredReduction.Or:
125                          value |= mem;
126                          break;
127                      case SemaphoredReduction.Add:
128                          value += mem;
129                          break;
130                      case SemaphoredReduction.Inc:
131                          value = (uint)mem < (uint)value ? mem + 1 : 0;
132                          break;
133                      case SemaphoredReduction.Dec:
134                          value = (uint)mem > 0 && (uint)mem <= (uint)value ? mem - 1 : value;
135                          break;
136                  }
137  
138                  _parent.MemoryManager.Write(address, value);
139              }
140          }
141  
142          /// <summary>
143          /// Apply a fence operation on a syncpoint.
144          /// </summary>
145          /// <param name="argument">Method call argument</param>
146          public void Syncpointb(int argument)
147          {
148              SyncpointbOperation operation = _state.State.SyncpointbOperation;
149  
150              uint syncpointId = (uint)_state.State.SyncpointbSyncptIndex;
151  
152              if (operation == SyncpointbOperation.Wait)
153              {
154                  uint threshold = (uint)_state.State.SyncpointaPayload;
155  
156                  _context.Synchronization.WaitOnSyncpoint(syncpointId, threshold, Timeout.InfiniteTimeSpan);
157              }
158              else if (operation == SyncpointbOperation.Incr)
159              {
160                  // "Unbind" render targets since a syncpoint increment might indicate future CPU access for the textures.
161                  _parent.TextureManager.RefreshModifiedTextures();
162  
163                  _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
164                  _context.Synchronization.IncrementSyncpoint(syncpointId);
165              }
166  
167              _context.AdvanceSequence();
168          }
169  
170          /// <summary>
171          /// Waits for the GPU to be idle.
172          /// </summary>
173          /// <param name="argument">Method call argument</param>
174          public void WaitForIdle(int argument)
175          {
176              _parent.PerformDeferredDraws();
177              _context.Renderer.Pipeline.Barrier();
178  
179              _createSyncPending = true;
180          }
181  
182          /// <summary>
183          /// Used as an indirect data barrier on NVN. When used, access to previously written data must be coherent.
184          /// </summary>
185          /// <param name="argument">Method call argument</param>
186          public void SetReference(int argument)
187          {
188              _context.Renderer.Pipeline.CommandBufferBarrier();
189  
190              _context.CreateHostSyncIfNeeded(HostSyncFlags.Strict);
191          }
192  
193          /// <summary>
194          /// Sends macro code/data to the MME.
195          /// </summary>
196          /// <param name="argument">Method call argument</param>
197          public void LoadMmeInstructionRam(int argument)
198          {
199              _macroCode[_state.State.LoadMmeInstructionRamPointer++] = argument;
200          }
201  
202          /// <summary>
203          /// Binds a macro index to a position for the MME
204          /// </summary>
205          /// <param name="argument">Method call argument</param>
206          public void LoadMmeStartAddressRam(int argument)
207          {
208              _macros[_state.State.LoadMmeStartAddressRamPointer++] = new Macro(argument);
209          }
210  
211          /// <summary>
212          /// Changes the shadow RAM control.
213          /// </summary>
214          /// <param name="argument">Method call argument</param>
215          public void SetMmeShadowRamControl(int argument)
216          {
217              _parent.SetShadowRamControl(argument);
218          }
219  
220          /// <summary>
221          /// Pushes an argument to a macro.
222          /// </summary>
223          /// <param name="index">Index of the macro</param>
224          /// <param name="gpuVa">GPU virtual address where the command word is located</param>
225          /// <param name="argument">Argument to be pushed to the macro</param>
226          public void MmePushArgument(int index, ulong gpuVa, int argument)
227          {
228              _macros[index].PushArgument(gpuVa, argument);
229          }
230  
231          /// <summary>
232          /// Prepares a macro for execution.
233          /// </summary>
234          /// <param name="index">Index of the macro</param>
235          /// <param name="argument">Initial argument passed to the macro</param>
236          public void MmeStart(int index, int argument)
237          {
238              _macros[index].StartExecution(_context, _parent, _macroCode, argument);
239          }
240  
241          /// <summary>
242          /// Executes a macro.
243          /// </summary>
244          /// <param name="index">Index of the macro</param>
245          /// <param name="state">Current GPU state</param>
246          public void CallMme(int index, IDeviceState state)
247          {
248              _macros[index].Execute(_macroCode, state);
249          }
250      }
251  }