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 }