diff --git a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs index 4287f0550..fa8a4a48b 100644 --- a/Ryujinx.Graphics.Gpu/MacroInterpreter.cs +++ b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu BitwiseNotAnd = 12 } - public Queue Fifo { get; private set; } + public Queue Fifo { get; } private int[] _gprs; @@ -62,6 +62,8 @@ namespace Ryujinx.Graphics.Gpu private int _pc; + private ShadowRamControl _shadowCtrl; + /// /// Creates a new instance of the macro code interpreter. /// @@ -78,8 +80,10 @@ namespace Ryujinx.Graphics.Gpu /// Code of the program to execute /// Start position to execute /// Optional argument passed to the program, 0 if not used + /// Shadow RAM control register value /// Current GPU state - public void Execute(int[] mme, int position, int param, GpuState state) + /// Shadow GPU state + 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); } /// @@ -118,8 +124,9 @@ namespace Ryujinx.Graphics.Gpu /// /// Program code to execute /// Current GPU state + /// Shadow GPU state /// True to continue execution, false if the program exited - 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. /// /// Current GPU state + /// Shadow GPU state /// Call argument - 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); diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs index 11a9e3fba..2056b3d4b 100644 --- a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs +++ b/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 /// /// Program code /// Current GPU state - 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 /// public GpuState State { get; } + /// + /// Sub-channel shadow GPU state (used as backup storage to restore MME changes). + /// + public GpuState ShadowState { get; } + /// /// Engine bound to the sub-channel. /// @@ -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(); } diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs index 89023407e..288c97d7a 100644 --- a/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs +++ b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs @@ -5,11 +5,12 @@ namespace Ryujinx.Graphics.Gpu /// 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 } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/ShadowRamControl.cs b/Ryujinx.Graphics.Gpu/ShadowRamControl.cs new file mode 100644 index 000000000..10dd39bcc --- /dev/null +++ b/Ryujinx.Graphics.Gpu/ShadowRamControl.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.Graphics.Gpu +{ + /// + /// Shadow RAM Control setting. + /// + enum ShadowRamControl + { + /// + /// Track data writes and store them on shadow RAM. + /// + Track = 0, + + /// + /// Track data writes and store them on shadow RAM, with filtering. + /// + TrackWithFilter = 1, + + /// + /// Writes data directly without storing on shadow RAM. + /// + Passthrough = 2, + + /// + /// Ignore data being written and replace with data on shadow RAM instead. + /// + Replay = 3 + } +}