diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs new file mode 100644 index 00000000..77b44e81 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -0,0 +1,142 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Device; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Engine.GPFifo; +using Ryujinx.Graphics.Gpu.Engine.Threed; +using Ryujinx.Graphics.Gpu.Memory; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Engine.MME +{ + /// <summary> + /// Macro High-level emulation. + /// </summary> + class MacroHLE : IMacroEE + { + private readonly GPFifoProcessor _processor; + private readonly MacroHLEFunctionName _functionName; + + /// <summary> + /// Arguments FIFO. + /// </summary> + public Queue<FifoWord> Fifo { get; } + + /// <summary> + /// Creates a new instance of the HLE macro handler. + /// </summary> + /// <param name="context">GPU context the macro is being executed on</param> + /// <param name="memoryManager">GPU memory manager</param> + /// <param name="engine">3D engine where this macro is being called</param> + /// <param name="functionName">Name of the HLE macro function to be called</param> + public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName) + { + _processor = processor; + _functionName = functionName; + + Fifo = new Queue<FifoWord>(); + } + + /// <summary> + /// Executes a macro program until it exits. + /// </summary> + /// <param name="code">Code of the program to execute</param> + /// <param name="state">GPU state at the time of the call</param> + /// <param name="arg0">Optional argument passed to the program, 0 if not used</param> + public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0) + { + switch (_functionName) + { + case MacroHLEFunctionName.MultiDrawElementsIndirectCount: + MultiDrawElementsIndirectCount(state, arg0); + break; + default: + throw new NotImplementedException(_functionName.ToString()); + } + } + + /// <summary> + /// Performs a indirect multi-draw, with parameters from a GPU buffer. + /// </summary> + /// <param name="state">GPU state at the time of the call</param> + /// <param name="arg0">First argument of the call</param> + private void MultiDrawElementsIndirectCount(IDeviceState state, int arg0) + { + int arg1 = FetchParam().Word; + int arg2 = FetchParam().Word; + int arg3 = FetchParam().Word; + + int startOffset = arg0; + int endOffset = arg1; + var topology = (PrimitiveTopology)arg2; + int paddingWords = arg3; + int maxDrawCount = endOffset - startOffset; + int stride = paddingWords * 4 + 0x14; + int indirectBufferSize = maxDrawCount * stride; + + ulong parameterBufferGpuVa = FetchParam().GpuVa; + ulong indirectBufferGpuVa = 0; + + int indexCount = 0; + + for (int i = 0; i < maxDrawCount; i++) + { + var count = FetchParam(); + var instanceCount = FetchParam(); + var firstIndex = FetchParam(); + var baseVertex = FetchParam(); + var baseInstance = FetchParam(); + + if (i == 0) + { + indirectBufferGpuVa = count.GpuVa; + } + + indexCount = Math.Max(indexCount, count.Word + firstIndex.Word); + + if (i != maxDrawCount - 1) + { + for (int j = 0; j < paddingWords; j++) + { + FetchParam(); + } + } + } + + // It should be empty at this point, but clear it just to be safe. + Fifo.Clear(); + + var parameterBuffer = _processor.MemoryManager.Physical.BufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4); + var indirectBuffer = _processor.MemoryManager.Physical.BufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize); + + _processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride); + } + + /// <summary> + /// Fetches a arguments from the arguments FIFO. + /// </summary> + /// <returns>The call argument, or a 0 value with null address if the FIFO is empty</returns> + private FifoWord FetchParam() + { + if (!Fifo.TryDequeue(out var value)) + { + Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); + + return new FifoWord(0UL, 0); + } + + return value; + } + + /// <summary> + /// Performs a GPU method call. + /// </summary> + /// <param name="state">Current GPU state</param> + /// <param name="methAddr">Address, in words, of the method</param> + /// <param name="value">Call argument</param> + private static void Send(IDeviceState state, int methAddr, int value) + { + state.Write(methAddr * 4, value); + } + } +} |