From 9261ec6bc858948c7ba73e1ff1301e19623be933 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 25 Apr 2020 10:56:56 -0300
Subject: [PATCH] Fix MME shadow RAM implementation (#1051)

---
 Ryujinx.Graphics.Gpu/MacroInterpreter.cs | 34 +++++----------
 Ryujinx.Graphics.Gpu/NvGpuFifo.cs        | 20 ++-------
 Ryujinx.Graphics.Gpu/State/GpuState.cs   | 55 ++++++++++++++++--------
 3 files changed, 50 insertions(+), 59 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs
index fa8a4a48b5..8d2d62023e 100644
--- a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs
+++ b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs
@@ -82,8 +82,7 @@ namespace Ryujinx.Graphics.Gpu
         /// <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>
-        /// <param name="shadowState">Shadow GPU state</param>
-        public void Execute(int[] mme, int position, int param, ShadowRamControl shadowCtrl, GpuState state, GpuState shadowState)
+        public void Execute(int[] mme, int position, int param, ShadowRamControl shadowCtrl, GpuState state)
         {
             Reset();
 
@@ -95,11 +94,11 @@ namespace Ryujinx.Graphics.Gpu
 
             FetchOpCode(mme);
 
-            while (Step(mme, state, shadowState));
+            while (Step(mme, state));
 
             // Due to the delay slot, we still need to execute
             // one more instruction before we actually exit.
-            Step(mme, state, shadowState);
+            Step(mme, state);
         }
 
         /// <summary>
@@ -124,9 +123,8 @@ 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, GpuState shadowState)
+        private bool Step(int[] mme, GpuState state)
         {
             int baseAddr = _pc - 1;
 
@@ -172,7 +170,7 @@ namespace Ryujinx.Graphics.Gpu
                     {
                         SetDstGpr(FetchParam());
 
-                        Send(state, shadowState, result);
+                        Send(state, result);
 
                         break;
                     }
@@ -182,7 +180,7 @@ namespace Ryujinx.Graphics.Gpu
                     {
                         SetDstGpr(result);
 
-                        Send(state, shadowState, result);
+                        Send(state, result);
 
                         break;
                     }
@@ -204,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu
 
                         SetMethAddr(result);
 
-                        Send(state, shadowState, FetchParam());
+                        Send(state, FetchParam());
 
                         break;
                     }
@@ -216,7 +214,7 @@ namespace Ryujinx.Graphics.Gpu
 
                         SetMethAddr(result);
 
-                        Send(state, shadowState,(result >> 12) & 0x3f);
+                        Send(state, (result >> 12) & 0x3f);
 
                         break;
                     }
@@ -489,24 +487,12 @@ 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, GpuState shadowState, int value)
+        private void Send(GpuState state, 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);
+            state.CallMethod(meth, _shadowCtrl);
 
             _methAddr += _methIncr;
         }
diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs
index 5936fa53af..7628fe6bf6 100644
--- a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs
+++ b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs
@@ -62,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, ShadowRamControl shadowCtrl, GpuState state, GpuState shadowState)
+            public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state)
             {
                 if (_executionPending)
                 {
                     _executionPending = false;
 
-                    _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state, shadowState);
+                    _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state);
                 }
             }
 
@@ -101,11 +101,6 @@ 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>
@@ -117,7 +112,6 @@ namespace Ryujinx.Graphics.Gpu
             public SubChannel()
             {
                 State = new GpuState();
-                ShadowState = new GpuState();
             }
         }
 
@@ -202,11 +196,7 @@ namespace Ryujinx.Graphics.Gpu
             }
             else if (meth.Method < 0xe00)
             {
-                SubChannel sc = _subChannels[meth.SubChannel];
-
-                sc.ShadowState.Write(meth.Method, meth.Argument);
-
-                sc.State.CallMethod(meth);
+                _subChannels[meth.SubChannel].State.CallMethod(meth, _shadowCtrl);
             }
             else
             {
@@ -223,9 +213,7 @@ namespace Ryujinx.Graphics.Gpu
 
                 if (meth.IsLastCall)
                 {
-                    SubChannel sc = _subChannels[meth.SubChannel];
-
-                    _macros[macroIndex].Execute(_mme, _shadowCtrl, sc.State, sc.ShadowState);
+                    _macros[macroIndex].Execute(_mme, _shadowCtrl, _subChannels[meth.SubChannel].State);
 
                     _context.Methods.PerformDeferredDraws();
                 }
diff --git a/Ryujinx.Graphics.Gpu/State/GpuState.cs b/Ryujinx.Graphics.Gpu/State/GpuState.cs
index a6671fe841..fb495eff01 100644
--- a/Ryujinx.Graphics.Gpu/State/GpuState.cs
+++ b/Ryujinx.Graphics.Gpu/State/GpuState.cs
@@ -12,7 +12,8 @@ namespace Ryujinx.Graphics.Gpu.State
 
         public delegate void MethodCallback(GpuState state, int argument);
 
-        private int[] _backingMemory;
+        private readonly int[] _memory;
+        private readonly int[] _shadow;
 
         /// <summary>
         /// GPU register information.
@@ -29,14 +30,15 @@ namespace Ryujinx.Graphics.Gpu.State
             public bool Modified;
         }
 
-        private Register[] _registers;
+        private readonly Register[] _registers;
 
         /// <summary>
         /// Creates a new instance of the GPU state.
         /// </summary>
         public GpuState()
         {
-            _backingMemory = new int[RegistersCount];
+            _memory = new int[RegistersCount];
+            _shadow = new int[RegistersCount];
 
             _registers = new Register[RegistersCount];
 
@@ -62,25 +64,40 @@ namespace Ryujinx.Graphics.Gpu.State
                 }
             }
 
-            InitializeDefaultState();
+            InitializeDefaultState(_memory);
+            InitializeDefaultState(_shadow);
         }
 
         /// <summary>
         /// Calls a GPU method, using this state.
         /// </summary>
         /// <param name="meth">The GPU method to be called</param>
-        public void CallMethod(MethodParams meth)
+        /// <param name="shadowCtrl">Shadow RAM control register value</param>
+        public void CallMethod(MethodParams meth, ShadowRamControl shadowCtrl)
         {
+            int value = meth.Argument;
+
+            // TODO: Figure out what TrackWithFilter does, compared to Track.
+            if (shadowCtrl == ShadowRamControl.Track ||
+                shadowCtrl == ShadowRamControl.TrackWithFilter)
+            {
+                _shadow[meth.Method] = value;
+            }
+            else if (shadowCtrl == ShadowRamControl.Replay)
+            {
+                value = _shadow[meth.Method];
+            }
+
             Register register = _registers[meth.Method];
 
-            if (_backingMemory[meth.Method] != meth.Argument)
+            if (_memory[meth.Method] != value)
             {
                 _registers[(int)register.BaseOffset].Modified = true;
             }
 
-            _backingMemory[meth.Method] = meth.Argument;
+            _memory[meth.Method] = value;
 
-            register.Callback?.Invoke(this, meth.Argument);
+            register.Callback?.Invoke(this, value);
         }
 
         /// <summary>
@@ -90,7 +107,7 @@ namespace Ryujinx.Graphics.Gpu.State
         /// <returns>Data at the register</returns>
         public int Read(int offset)
         {
-            return _backingMemory[offset];
+            return _memory[offset];
         }
 
         /// <summary>
@@ -100,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.State
         /// <param name="value">Value to be written</param>
         public void Write(int offset, int value)
         {
-            _backingMemory[offset] = value;
+            _memory[offset] = value;
         }
 
         /// <summary>
@@ -109,29 +126,29 @@ namespace Ryujinx.Graphics.Gpu.State
         /// <param name="offset">The offset to be written</param>
         public void SetUniformBufferOffset(int offset)
         {
-            _backingMemory[(int)MethodOffset.UniformBufferState + 3] = offset;
+            _memory[(int)MethodOffset.UniformBufferState + 3] = offset;
         }
 
         /// <summary>
         /// Initializes registers with the default state.
         /// </summary>
-        private void InitializeDefaultState()
+        private static void InitializeDefaultState(int[] memory)
         {
             // Enable Rasterizer
-            _backingMemory[(int)MethodOffset.RasterizeEnable] = 1;
+            memory[(int)MethodOffset.RasterizeEnable] = 1;
 
             // Depth ranges.
             for (int index = 0; index < Constants.TotalViewports; index++)
             {
-                _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0;
-                _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
+                memory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0;
+                memory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
             }
 
             // Viewport transform enable.
-            _backingMemory[(int)MethodOffset.ViewportTransformEnable] = 1;
+            memory[(int)MethodOffset.ViewportTransformEnable] = 1;
 
             // Default front stencil mask.
-            _backingMemory[0x4e7] = 0xff;
+            memory[0x4e7] = 0xff;
 
             // Conditional rendering condition.
             _backingMemory[0x556] = (int)Condition.Always;
@@ -139,7 +156,7 @@ namespace Ryujinx.Graphics.Gpu.State
             // Default color mask.
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             {
-                _backingMemory[(int)MethodOffset.RtColorMask + index] = 0x1111;
+                memory[(int)MethodOffset.RtColorMask + index] = 0x1111;
             }
 
             // Default blend states
@@ -342,7 +359,7 @@ namespace Ryujinx.Graphics.Gpu.State
         /// <returns>The data at the specified location</returns>
         public T Get<T>(MethodOffset offset) where T : struct
         {
-            return MemoryMarshal.Cast<int, T>(_backingMemory.AsSpan().Slice((int)offset))[0];
+            return MemoryMarshal.Cast<int, T>(_memory.AsSpan().Slice((int)offset))[0];
         }
 
         /// <summary>