using Ryujinx.Graphics.Gpu.State; using System.IO; namespace Ryujinx.Graphics.Gpu { /// /// GPU commands FIFO. /// class NvGpuFifo { private const int MacrosCount = 0x80; private const int MacroIndexMask = MacrosCount - 1; // 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 MmeWords = 256 * 256; private GpuContext _context; /// /// Cached GPU macro program. /// private struct CachedMacro { /// /// Word offset of the code on the code memory. /// public int Position { get; } private bool _executionPending; private int _argument; private MacroInterpreter _interpreter; /// /// Creates a new instance of the GPU cached macro program. /// /// Macro code start position public CachedMacro(int position) { Position = position; _executionPending = false; _argument = 0; _interpreter = new MacroInterpreter(); } /// /// Sets the first argument for the macro call. /// /// First argument public void StartExecution(int argument) { _argument = argument; _executionPending = true; } /// /// Starts executing the macro program code. /// /// Program code /// Current GPU state public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state, GpuState shadowState) { if (_executionPending) { _executionPending = false; _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state, shadowState); } } /// /// Pushes an argument to the macro call argument FIFO. /// /// Argument to be pushed public void PushArgument(int argument) { _interpreter?.Fifo.Enqueue(argument); } } private int _currMacroPosition; private int _currMacroBindIndex; private ShadowRamControl _shadowCtrl; private CachedMacro[] _macros; private int[] _mme; /// /// GPU sub-channel information. /// private class SubChannel { /// /// Sub-channel GPU state. /// 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. /// public ClassId Class { get; set; } /// /// Creates a new instance of the GPU sub-channel. /// public SubChannel() { State = new GpuState(); ShadowState = new GpuState(); } } private SubChannel[] _subChannels; private SubChannel _fifoChannel; /// /// Creates a new instance of the GPU commands FIFO. /// /// GPU emulation context public NvGpuFifo(GpuContext context) { _context = context; _macros = new CachedMacro[MacrosCount]; _mme = new int[MmeWords]; _fifoChannel = new SubChannel(); _context.Methods.RegisterCallbacksForFifo(_fifoChannel.State); _subChannels = new SubChannel[8]; for (int index = 0; index < _subChannels.Length; index++) { _subChannels[index] = new SubChannel(); _context.Methods.RegisterCallbacks(_subChannels[index].State); } } /// /// Send macro code/data to the MME /// /// The index in the MME /// The data to use public void SendMacroCodeData(int index, int data) { _mme[index] = data; } /// /// Bind a macro index to a position for the MME /// /// The macro index /// The position of the macro public void BindMacro(int index, int position) { _macros[index] = new CachedMacro(position); } /// /// Change the shadow RAM setting /// /// The new Shadow RAM setting public void SetMmeShadowRamControl(ShadowRamControl shadowCtrl) { _shadowCtrl = shadowCtrl; } /// /// Calls a GPU method. /// /// GPU method call parameters public void CallMethod(MethodParams meth) { if ((MethodOffset)meth.Method == MethodOffset.BindChannel) { _subChannels[meth.SubChannel] = new SubChannel { Class = (ClassId)meth.Argument }; _context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel].State); } else if (meth.Method < 0x60) { // TODO: check if macros are shared between subchannels or not. For now let's assume they are. _fifoChannel.State.CallMethod(meth); } else if (meth.Method < 0xe00) { SubChannel sc = _subChannels[meth.SubChannel]; sc.ShadowState.Write(meth.Method, meth.Argument); sc.State.CallMethod(meth); } else { int macroIndex = (meth.Method >> 1) & MacroIndexMask; if ((meth.Method & 1) != 0) { _macros[macroIndex].PushArgument(meth.Argument); } else { _macros[macroIndex].StartExecution(meth.Argument); } if (meth.IsLastCall) { SubChannel sc = _subChannels[meth.SubChannel]; _macros[macroIndex].Execute(_mme, _shadowCtrl, sc.State, sc.ShadowState); _context.Methods.PerformDeferredDraws(); } } } } }