aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
new file mode 100644
index 00000000..e80d98a1
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
@@ -0,0 +1,248 @@
+using Ryujinx.Graphics.Device;
+using Ryujinx.Graphics.Gpu.Engine.MME;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
+{
+ /// <summary>
+ /// Represents a GPU General Purpose FIFO class.
+ /// </summary>
+ class GPFifoClass : IDeviceState
+ {
+ private readonly GpuContext _context;
+ private readonly GPFifoProcessor _parent;
+ private readonly DeviceState<GPFifoClassState> _state;
+
+ private int _previousSubChannel;
+ private bool _createSyncPending;
+
+ private const int MacrosCount = 0x80;
+
+ // Note: The size of the macro memory is unknown, we just make
+ // a guess here and use 256kb as the size. Increase if needed.
+ private const int MacroCodeSize = 256 * 256;
+
+ private readonly Macro[] _macros;
+ private readonly int[] _macroCode;
+
+ /// <summary>
+ /// Creates a new instance of the GPU General Purpose FIFO class.
+ /// </summary>
+ /// <param name="context">GPU context</param>
+ /// <param name="parent">Parent GPU General Purpose FIFO processor</param>
+ public GPFifoClass(GpuContext context, GPFifoProcessor parent)
+ {
+ _context = context;
+ _parent = parent;
+ _state = new DeviceState<GPFifoClassState>(new Dictionary<string, RwCallback>
+ {
+ { nameof(GPFifoClassState.Semaphored), new RwCallback(Semaphored, null) },
+ { nameof(GPFifoClassState.Syncpointb), new RwCallback(Syncpointb, null) },
+ { nameof(GPFifoClassState.WaitForIdle), new RwCallback(WaitForIdle, null) },
+ { nameof(GPFifoClassState.SetReference), new RwCallback(SetReference, null) },
+ { nameof(GPFifoClassState.LoadMmeInstructionRam), new RwCallback(LoadMmeInstructionRam, null) },
+ { nameof(GPFifoClassState.LoadMmeStartAddressRam), new RwCallback(LoadMmeStartAddressRam, null) },
+ { nameof(GPFifoClassState.SetMmeShadowRamControl), new RwCallback(SetMmeShadowRamControl, null) }
+ });
+
+ _macros = new Macro[MacrosCount];
+ _macroCode = new int[MacroCodeSize];
+ }
+
+ /// <summary>
+ /// Create any syncs from WaitForIdle command that are currently pending.
+ /// </summary>
+ public void CreatePendingSyncs()
+ {
+ if (_createSyncPending)
+ {
+ _createSyncPending = false;
+ _context.CreateHostSyncIfNeeded(false, false);
+ }
+ }
+
+ /// <summary>
+ /// Reads data from the class registers.
+ /// </summary>
+ /// <param name="offset">Register byte offset</param>
+ /// <returns>Data at the specified offset</returns>
+ public int Read(int offset) => _state.Read(offset);
+
+ /// <summary>
+ /// Writes data to the class registers.
+ /// </summary>
+ /// <param name="offset">Register byte offset</param>
+ /// <param name="data">Data to be written</param>
+ public void Write(int offset, int data) => _state.Write(offset, data);
+
+ /// <summary>
+ /// Writes a GPU counter to guest memory.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void Semaphored(int argument)
+ {
+ ulong address = ((ulong)_state.State.SemaphorebOffsetLower << 2) |
+ ((ulong)_state.State.SemaphoreaOffsetUpper << 32);
+
+ int value = _state.State.SemaphorecPayload;
+
+ SemaphoredOperation operation = _state.State.SemaphoredOperation;
+
+ if (_state.State.SemaphoredReleaseSize == SemaphoredReleaseSize.SixteenBytes)
+ {
+ _parent.MemoryManager.Write(address + 4, 0);
+ _parent.MemoryManager.Write(address + 8, _context.GetTimestamp());
+ }
+
+ // TODO: Acquire operations (Wait), interrupts for invalid combinations.
+ if (operation == SemaphoredOperation.Release)
+ {
+ _parent.MemoryManager.Write(address, value);
+ }
+ else if (operation == SemaphoredOperation.Reduction)
+ {
+ bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed;
+
+ int mem = _parent.MemoryManager.Read<int>(address);
+
+ switch (_state.State.SemaphoredReduction)
+ {
+ case SemaphoredReduction.Min:
+ value = signed ? Math.Min(mem, value) : (int)Math.Min((uint)mem, (uint)value);
+ break;
+ case SemaphoredReduction.Max:
+ value = signed ? Math.Max(mem, value) : (int)Math.Max((uint)mem, (uint)value);
+ break;
+ case SemaphoredReduction.Xor:
+ value ^= mem;
+ break;
+ case SemaphoredReduction.And:
+ value &= mem;
+ break;
+ case SemaphoredReduction.Or:
+ value |= mem;
+ break;
+ case SemaphoredReduction.Add:
+ value += mem;
+ break;
+ case SemaphoredReduction.Inc:
+ value = (uint)mem < (uint)value ? mem + 1 : 0;
+ break;
+ case SemaphoredReduction.Dec:
+ value = (uint)mem > 0 && (uint)mem <= (uint)value ? mem - 1 : value;
+ break;
+ }
+
+ _parent.MemoryManager.Write(address, value);
+ }
+ }
+
+ /// <summary>
+ /// Apply a fence operation on a syncpoint.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void Syncpointb(int argument)
+ {
+ SyncpointbOperation operation = _state.State.SyncpointbOperation;
+
+ uint syncpointId = (uint)_state.State.SyncpointbSyncptIndex;
+
+ if (operation == SyncpointbOperation.Wait)
+ {
+ uint threshold = (uint)_state.State.SyncpointaPayload;
+
+ _context.Synchronization.WaitOnSyncpoint(syncpointId, threshold, Timeout.InfiniteTimeSpan);
+ }
+ else if (operation == SyncpointbOperation.Incr)
+ {
+ _context.CreateHostSyncIfNeeded(true, true);
+ _context.Synchronization.IncrementSyncpoint(syncpointId);
+ }
+
+ _context.AdvanceSequence();
+ }
+
+ /// <summary>
+ /// Waits for the GPU to be idle.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void WaitForIdle(int argument)
+ {
+ _parent.PerformDeferredDraws();
+ _context.Renderer.Pipeline.Barrier();
+
+ _createSyncPending = true;
+ }
+
+ /// <summary>
+ /// Used as an indirect data barrier on NVN. When used, access to previously written data must be coherent.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void SetReference(int argument)
+ {
+ _context.Renderer.Pipeline.CommandBufferBarrier();
+
+ _context.CreateHostSyncIfNeeded(false, true);
+ }
+
+ /// <summary>
+ /// Sends macro code/data to the MME.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void LoadMmeInstructionRam(int argument)
+ {
+ _macroCode[_state.State.LoadMmeInstructionRamPointer++] = argument;
+ }
+
+ /// <summary>
+ /// Binds a macro index to a position for the MME
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void LoadMmeStartAddressRam(int argument)
+ {
+ _macros[_state.State.LoadMmeStartAddressRamPointer++] = new Macro(argument);
+ }
+
+ /// <summary>
+ /// Changes the shadow RAM control.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void SetMmeShadowRamControl(int argument)
+ {
+ _parent.SetShadowRamControl(argument);
+ }
+
+ /// <summary>
+ /// Pushes an argument to a macro.
+ /// </summary>
+ /// <param name="index">Index of the macro</param>
+ /// <param name="gpuVa">GPU virtual address where the command word is located</param>
+ /// <param name="argument">Argument to be pushed to the macro</param>
+ public void MmePushArgument(int index, ulong gpuVa, int argument)
+ {
+ _macros[index].PushArgument(gpuVa, argument);
+ }
+
+ /// <summary>
+ /// Prepares a macro for execution.
+ /// </summary>
+ /// <param name="index">Index of the macro</param>
+ /// <param name="argument">Initial argument passed to the macro</param>
+ public void MmeStart(int index, int argument)
+ {
+ _macros[index].StartExecution(_context, _parent, _macroCode, argument);
+ }
+
+ /// <summary>
+ /// Executes a macro.
+ /// </summary>
+ /// <param name="index">Index of the macro</param>
+ /// <param name="state">Current GPU state</param>
+ public void CallMme(int index, IDeviceState state)
+ {
+ _macros[index].Execute(_macroCode, state);
+ }
+ }
+}