using Ryujinx.Graphics.Gpu.State; using System.IO; namespace Ryujinx.Graphics.Gpu { /// <summary> /// GPU commands FIFO. /// </summary> 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; /// <summary> /// Cached GPU macro program. /// </summary> private struct CachedMacro { /// <summary> /// Word offset of the code on the code memory. /// </summary> public int Position { get; } private bool _executionPending; private int _argument; private MacroInterpreter _interpreter; /// <summary> /// Creates a new instance of the GPU cached macro program. /// </summary> /// <param name="position">Macro code start position</param> public CachedMacro(int position) { Position = position; _executionPending = false; _argument = 0; _interpreter = new MacroInterpreter(); } /// <summary> /// Sets the first argument for the macro call. /// </summary> /// <param name="argument">First argument</param> public void StartExecution(int argument) { _argument = argument; _executionPending = true; } /// <summary> /// Starts executing the macro program code. /// </summary> /// <param name="mme">Program code</param> /// <param name="state">Current GPU state</param> public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state) { if (_executionPending) { _executionPending = false; _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state); } } /// <summary> /// Pushes an argument to the macro call argument FIFO. /// </summary> /// <param name="argument">Argument to be pushed</param> public void PushArgument(int argument) { _interpreter?.Fifo.Enqueue(argument); } } private int _currMacroPosition; private int _currMacroBindIndex; private ShadowRamControl _shadowCtrl; private CachedMacro[] _macros; private int[] _mme; /// <summary> /// GPU sub-channel information. /// </summary> private class SubChannel { /// <summary> /// Sub-channel GPU state. /// </summary> public GpuState State { get; } /// <summary> /// Engine bound to the sub-channel. /// </summary> public ClassId Class { get; set; } /// <summary> /// Creates a new instance of the GPU sub-channel. /// </summary> public SubChannel() { State = new GpuState(); } } private SubChannel[] _subChannels; private SubChannel _fifoChannel; /// <summary> /// Creates a new instance of the GPU commands FIFO. /// </summary> /// <param name="context">GPU emulation context</param> 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); } } /// <summary> /// Send macro code/data to the MME /// </summary> /// <param name="index">The index in the MME</param> /// <param name="data">The data to use</param> public void SendMacroCodeData(int index, int data) { _mme[index] = data; } /// <summary> /// Bind a macro index to a position for the MME /// </summary> /// <param name="index">The macro index</param> /// <param name="position">The position of the macro</param> public void BindMacro(int index, int position) { _macros[index] = new CachedMacro(position); } /// <summary> /// Change the shadow RAM setting /// </summary> /// <param name="shadowCtrl">The new Shadow RAM setting</param> public void SetMmeShadowRamControl(ShadowRamControl shadowCtrl) { _shadowCtrl = shadowCtrl; } /// <summary> /// Calls a GPU method. /// </summary> /// <param name="meth">GPU method call parameters</param> 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, _shadowCtrl); } else if (meth.Method < 0xe00) { _subChannels[meth.SubChannel].State.CallMethod(meth, _shadowCtrl); } 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) { _macros[macroIndex].Execute(_mme, _shadowCtrl, _subChannels[meth.SubChannel].State); _context.Methods.PerformDeferredDraws(); } } } } }