Răsfoiți Sursa

Implement MME shadow RAM (#987)

gdkchan 6 ani în urmă
părinte
comite
ff2bac9c90

+ 29 - 10
Ryujinx.Graphics.Gpu/MacroInterpreter.cs

@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu
             BitwiseNotAnd      = 12
         }
 
-        public Queue<int> Fifo { get; private set; }
+        public Queue<int> Fifo { get; }
 
         private int[] _gprs;
 
@@ -62,6 +62,8 @@ namespace Ryujinx.Graphics.Gpu
 
         private int _pc;
 
+        private ShadowRamControl _shadowCtrl;
+
         /// <summary>
         /// Creates a new instance of the macro code interpreter.
         /// </summary>
@@ -78,8 +80,10 @@ namespace Ryujinx.Graphics.Gpu
         /// <param name="mme">Code of the program to execute</param>
         /// <param name="position">Start position to execute</param>
         /// <param name="param">Optional argument passed to the program, 0 if not used</param>
+        /// <param name="shadowCtrl">Shadow RAM control register value</param>
         /// <param name="state">Current GPU state</param>
-        public void Execute(int[] mme, int position, int param, GpuState state)
+        /// <param name="shadowState">Shadow GPU state</param>
+        public void Execute(int[] mme, int position, int param, ShadowRamControl shadowCtrl, GpuState state, GpuState shadowState)
         {
             Reset();
 
@@ -87,13 +91,15 @@ namespace Ryujinx.Graphics.Gpu
 
             _pc = position;
 
+            _shadowCtrl = shadowCtrl;
+
             FetchOpCode(mme);
 
-            while (Step(mme, state));
+            while (Step(mme, state, shadowState));
 
             // Due to the delay slot, we still need to execute
             // one more instruction before we actually exit.
-            Step(mme, state);
+            Step(mme, state, shadowState);
         }
 
         /// <summary>
@@ -118,8 +124,9 @@ namespace Ryujinx.Graphics.Gpu
         /// </summary>
         /// <param name="mme">Program code to execute</param>
         /// <param name="state">Current GPU state</param>
+        /// <param name="shadowState">Shadow GPU state</param>
         /// <returns>True to continue execution, false if the program exited</returns>
-        private bool Step(int[] mme, GpuState state)
+        private bool Step(int[] mme, GpuState state, GpuState shadowState)
         {
             int baseAddr = _pc - 1;
 
@@ -165,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu
                     {
                         SetDstGpr(FetchParam());
 
-                        Send(state, result);
+                        Send(state, shadowState, result);
 
                         break;
                     }
@@ -175,7 +182,7 @@ namespace Ryujinx.Graphics.Gpu
                     {
                         SetDstGpr(result);
 
-                        Send(state, result);
+                        Send(state, shadowState, result);
 
                         break;
                     }
@@ -197,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu
 
                         SetMethAddr(result);
 
-                        Send(state, FetchParam());
+                        Send(state, shadowState, FetchParam());
 
                         break;
                     }
@@ -209,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu
 
                         SetMethAddr(result);
 
-                        Send(state, (result >> 12) & 0x3f);
+                        Send(state, shadowState,(result >> 12) & 0x3f);
 
                         break;
                     }
@@ -482,9 +489,21 @@ namespace Ryujinx.Graphics.Gpu
         /// Performs a GPU method call.
         /// </summary>
         /// <param name="state">Current GPU state</param>
+        /// <param name="shadowState">Shadow GPU state</param>
         /// <param name="value">Call argument</param>
-        private void Send(GpuState state, int value)
+        private void Send(GpuState state, GpuState shadowState, int value)
         {
+            // TODO: Figure out what TrackWithFilter does, compared to Track.
+            if (_shadowCtrl == ShadowRamControl.Track ||
+                _shadowCtrl == ShadowRamControl.TrackWithFilter)
+            {
+                shadowState.Write(_methAddr, value);
+            }
+            else if (_shadowCtrl == ShadowRamControl.Replay)
+            {
+                value = shadowState.Read(_methAddr);
+            }
+
             MethodParams meth = new MethodParams(_methAddr, value);
 
             state.CallMethod(meth);

+ 26 - 4
Ryujinx.Graphics.Gpu/NvGpuFifo.cs

@@ -1,4 +1,5 @@
 using Ryujinx.Graphics.Gpu.State;
+using System.IO;
 
 namespace Ryujinx.Graphics.Gpu
 {
@@ -61,13 +62,13 @@ namespace Ryujinx.Graphics.Gpu
             /// </summary>
             /// <param name="mme">Program code</param>
             /// <param name="state">Current GPU state</param>
-            public void Execute(int[] mme, GpuState state)
+            public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state, GpuState shadowState)
             {
                 if (_executionPending)
                 {
                     _executionPending = false;
 
-                    _interpreter?.Execute(mme, Position, _argument, state);
+                    _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state, shadowState);
                 }
             }
 
@@ -84,6 +85,8 @@ namespace Ryujinx.Graphics.Gpu
         private int _currMacroPosition;
         private int _currMacroBindIndex;
 
+        private ShadowRamControl _shadowCtrl;
+
         private CachedMacro[] _macros;
 
         private int[] _mme;
@@ -98,6 +101,11 @@ namespace Ryujinx.Graphics.Gpu
             /// </summary>
             public GpuState State { get; }
 
+            /// <summary>
+            /// Sub-channel shadow GPU state (used as backup storage to restore MME changes).
+            /// </summary>
+            public GpuState ShadowState { get; }
+
             /// <summary>
             /// Engine bound to the sub-channel.
             /// </summary>
@@ -109,6 +117,7 @@ namespace Ryujinx.Graphics.Gpu
             public SubChannel()
             {
                 State = new GpuState();
+                ShadowState = new GpuState();
             }
         }
 
@@ -188,11 +197,22 @@ namespace Ryujinx.Graphics.Gpu
 
                         break;
                     }
+
+                    case NvGpuFifoMeth.SetMmeShadowRamControl:
+                    {
+                        _shadowCtrl = (ShadowRamControl)meth.Argument;
+
+                        break;
+                    }
                 }
             }
             else if (meth.Method < 0xe00)
             {
-                _subChannels[meth.SubChannel].State.CallMethod(meth);
+                SubChannel sc = _subChannels[meth.SubChannel];
+
+                sc.ShadowState.Write(meth.Method, meth.Argument);
+
+                sc.State.CallMethod(meth);
             }
             else
             {
@@ -209,7 +229,9 @@ namespace Ryujinx.Graphics.Gpu
 
                 if (meth.IsLastCall)
                 {
-                    _macros[macroIndex].Execute(_mme, _subChannels[meth.SubChannel].State);
+                    SubChannel sc = _subChannels[meth.SubChannel];
+
+                    _macros[macroIndex].Execute(_mme, _shadowCtrl, sc.State, sc.ShadowState);
 
                     _context.Methods.PerformDeferredDraws();
                 }

+ 7 - 6
Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs

@@ -5,11 +5,12 @@ namespace Ryujinx.Graphics.Gpu
     /// </summary>
     enum NvGpuFifoMeth
     {
-        BindChannel           = 0,
-        WaitForIdle           = 0x44,
-        SetMacroUploadAddress = 0x45,
-        SendMacroCodeData     = 0x46,
-        SetMacroBindingIndex  = 0x47,
-        BindMacro             = 0x48
+        BindChannel            = 0,
+        WaitForIdle            = 0x44,
+        SetMacroUploadAddress  = 0x45,
+        SendMacroCodeData      = 0x46,
+        SetMacroBindingIndex   = 0x47,
+        BindMacro              = 0x48,
+        SetMmeShadowRamControl = 0x49
     }
 }

+ 28 - 0
Ryujinx.Graphics.Gpu/ShadowRamControl.cs

@@ -0,0 +1,28 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    /// <summary>
+    /// Shadow RAM Control setting.
+    /// </summary>
+    enum ShadowRamControl
+    {
+        /// <summary>
+        /// Track data writes and store them on shadow RAM.
+        /// </summary>
+        Track = 0,
+
+        /// <summary>
+        /// Track data writes and store them on shadow RAM, with filtering.
+        /// </summary>
+        TrackWithFilter = 1,
+
+        /// <summary>
+        /// Writes data directly without storing on shadow RAM.
+        /// </summary>
+        Passthrough = 2,
+
+        /// <summary>
+        /// Ignore data being written and replace with data on shadow RAM instead.
+        /// </summary>
+        Replay = 3
+    }
+}