Ryujinx/Ryujinx.Graphics/Gpu/NvGpuFifo.cs

177 lines
4.9 KiB
C#

using ChocolArm64.Memory;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gpu
{
public class NvGpuFifo
{
private const int MacrosCount = 0x80;
private const int MacroIndexMask = MacrosCount - 1;
private NsGpu Gpu;
private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue;
private NvGpuEngine[] SubChannels;
private struct CachedMacro
{
public long Position { get; private set; }
private MacroInterpreter Interpreter;
public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, long Position)
{
this.Position = Position;
Interpreter = new MacroInterpreter(PFifo, Engine);
}
public void PushParam(int Param)
{
Interpreter?.Fifo.Enqueue(Param);
}
public void Execute(AMemory Memory, int Param)
{
Interpreter?.Execute(Memory, Position, Param);
}
}
private long CurrMacroPosition;
private int CurrMacroBindIndex;
private CachedMacro[] Macros;
public NvGpuFifo(NsGpu Gpu)
{
this.Gpu = Gpu;
BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>();
SubChannels = new NvGpuEngine[8];
Macros = new CachedMacro[MacrosCount];
}
public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer)
{
foreach (NsGpuPBEntry PBEntry in Buffer)
{
BufferQueue.Enqueue((Memory, PBEntry));
}
}
public void DispatchCalls()
{
while (Step());
}
public bool Step()
{
if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple))
{
CallMethod(Tuple.Memory, Tuple.PBEntry);
return true;
}
return false;
}
private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
{
if (PBEntry.Method < 0x80)
{
switch ((NvGpuFifoMeth)PBEntry.Method)
{
case NvGpuFifoMeth.BindChannel:
{
NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
SubChannels[PBEntry.SubChannel] = Engine;
break;
}
case NvGpuFifoMeth.SetMacroUploadAddress:
{
CurrMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2);
break;
}
case NvGpuFifoMeth.SendMacroCodeData:
{
long Position = Gpu.GetCpuAddr(CurrMacroPosition);
foreach (int Arg in PBEntry.Arguments)
{
Memory.WriteInt32(Position, Arg);
CurrMacroPosition += 4;
Position += 4;
}
break;
}
case NvGpuFifoMeth.SetMacroBindingIndex:
{
CurrMacroBindIndex = PBEntry.Arguments[0];
break;
}
case NvGpuFifoMeth.BindMacro:
{
long Position = (long)((ulong)PBEntry.Arguments[0] << 2);
Position = Gpu.GetCpuAddr(Position);
Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
break;
}
}
}
else
{
switch (SubChannels[PBEntry.SubChannel])
{
case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break;
case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break;
}
}
}
private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
{
Gpu.Engine2d.CallMethod(Memory, PBEntry);
}
private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
{
if (PBEntry.Method < 0xe00)
{
Gpu.Engine3d.CallMethod(Memory, PBEntry);
}
else
{
int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask;
if ((PBEntry.Method & 1) != 0)
{
foreach (int Arg in PBEntry.Arguments)
{
Macros[MacroIndex].PushParam(Arg);
}
}
else
{
Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]);
}
}
}
}
}