diff options
| author | mpnico <mpnico@gmail.com> | 2021-08-26 23:50:28 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-26 23:50:28 +0200 |
| commit | 8e1adb95cf7f67b976f105f4cac26d3ff2986057 (patch) | |
| tree | f56ee0f92495fc1bd1e307c3bd51a2d1240d197b /Ryujinx.Graphics.Gpu/Engine/MME | |
| parent | 5cab8ea4ad2388bd035150e79f241ae5df95ab3b (diff) | |
Add support for HLE macros and accelerate MultiDrawElementsIndirectCount #2 (#2557)
* Add support for HLE macros and accelerate MultiDrawElementsIndirectCount
* Add missing barrier
* Fix index buffer count
* Add support check for each macro hle before use
* Add missing xml doc
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine/MME')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs | 51 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 142 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs | 11 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs | 89 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs | 8 |
8 files changed, 315 insertions, 25 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs index b957de08..640687f0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs @@ -5,6 +5,33 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Engine.MME { /// <summary> + /// FIFO word. + /// </summary> + struct FifoWord + { + /// <summary> + /// GPU virtual address where the word is located in memory. + /// </summary> + public ulong GpuVa { get; } + + /// <summary> + /// Word value. + /// </summary> + public int Word { get; } + + /// <summary> + /// Creates a new FIFO word. + /// </summary> + /// <param name="gpuVa">GPU virtual address where the word is located in memory</param> + /// <param name="word">Word value</param> + public FifoWord(ulong gpuVa, int word) + { + GpuVa = gpuVa; + Word = word; + } + } + + /// <summary> /// Macro Execution Engine interface. /// </summary> interface IMacroEE @@ -12,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Arguments FIFO. /// </summary> - Queue<int> Fifo { get; } + Queue<FifoWord> Fifo { get; } /// <summary> /// Should execute the GPU Macro code being passed. diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs index 1a79afb9..9d1dbc8f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs @@ -1,4 +1,6 @@ using Ryujinx.Graphics.Device; +using Ryujinx.Graphics.Gpu.Engine.GPFifo; +using Ryujinx.Graphics.Gpu.Memory; using System; namespace Ryujinx.Graphics.Gpu.Engine.MME @@ -13,10 +15,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// </summary> public int Position { get; } + private IMacroEE _executionEngine; private bool _executionPending; private int _argument; - - private readonly IMacroEE _executionEngine; + private MacroHLEFunctionName _hleFunction; /// <summary> /// Creates a new instance of the GPU cached macro program. @@ -26,28 +28,47 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { Position = position; + _executionEngine = null; _executionPending = false; _argument = 0; - - if (GraphicsConfig.EnableMacroJit) - { - _executionEngine = new MacroJit(); - } - else - { - _executionEngine = new MacroInterpreter(); - } + _hleFunction = MacroHLEFunctionName.None; } /// <summary> /// Sets the first argument for the macro call. /// </summary> + /// <param name="context">GPU context where the macro code is being executed</param> + /// <param name="processor">GPU GP FIFO command processor</param> + /// <param name="code">Code to be executed</param> /// <param name="argument">First argument</param> - public void StartExecution(int argument) + public void StartExecution(GpuContext context, GPFifoProcessor processor, ReadOnlySpan<int> code, int argument) { _argument = argument; _executionPending = true; + + if (_executionEngine == null) + { + if (GraphicsConfig.EnableMacroHLE && MacroHLETable.TryGetMacroHLEFunction(code.Slice(Position), context.Capabilities, out _hleFunction)) + { + _executionEngine = new MacroHLE(processor, _hleFunction); + } + else if (GraphicsConfig.EnableMacroJit) + { + _executionEngine = new MacroJit(); + } + else + { + _executionEngine = new MacroInterpreter(); + } + } + + if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount) + { + // We don't consume the parameter buffer value, so we don't need to flush it. + // Doing so improves performance if the value was written by a GPU shader. + context.GPFifo.SetFlushSkips(2); + } } /// <summary> @@ -60,7 +81,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME if (_executionPending) { _executionPending = false; - _executionEngine?.Execute(code.Slice(Position), state, _argument); } } @@ -68,10 +88,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Pushes an argument to the macro call argument FIFO. /// </summary> + /// <param name="gpuVa">GPU virtual address where the command word is located</param> /// <param name="argument">Argument to be pushed</param> - public void PushArgument(int argument) + public void PushArgument(ulong gpuVa, int argument) { - _executionEngine?.Fifo.Enqueue(argument); + _executionEngine?.Fifo.Enqueue(new FifoWord(gpuVa, argument)); } } } 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); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs new file mode 100644 index 00000000..60354a9b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu.Engine.MME +{ + /// <summary> + /// Name of the High-level implementation of a Macro function. + /// </summary> + enum MacroHLEFunctionName + { + None, + MultiDrawElementsIndirectCount + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs new file mode 100644 index 00000000..77d041ad --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs @@ -0,0 +1,89 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine.MME +{ + /// <summary> + /// Table with information about High-level implementations of GPU Macro code. + /// </summary> + static class MacroHLETable + { + /// <summary> + /// Macroo High-level implementation table entry. + /// </summary> + struct TableEntry + { + /// <summary> + /// Name of the Macro function. + /// </summary> + public MacroHLEFunctionName Name { get; } + + /// <summary> + /// Hash of the original binary Macro function code. + /// </summary> + public Hash128 Hash { get; } + + /// <summary> + /// Size (in bytes) of the original binary Macro function code. + /// </summary> + public int Length { get; } + + /// <summary> + /// Creates a new table entry. + /// </summary> + /// <param name="name">Name of the Macro function</param> + /// <param name="hash">Hash of the original binary Macro function code</param> + /// <param name="length">Size (in bytes) of the original binary Macro function code</param> + public TableEntry(MacroHLEFunctionName name, Hash128 hash, int length) + { + Name = name; + Hash = hash; + Length = length; + } + } + + private static readonly TableEntry[] Table = new TableEntry[] + { + new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C) + }; + + private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name) + { + if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount) + { + return caps.SupportsIndirectParameters; + } + + return false; + } + + /// <summary> + /// Checks if there's a fast, High-level implementation of the specified Macro code available. + /// </summary> + /// <param name="code">Macro code to be checked</param> + /// <param name="caps">Renderer capabilities to check for this macro HLE support</param> + /// <param name="name">Name of the function if a implementation is available and supported, otherwise <see cref="MacroHLEFunctionName.None"/></param> + /// <returns>True if there is a implementation available and supported, false otherwise</returns> + public static bool TryGetMacroHLEFunction(ReadOnlySpan<int> code, Capabilities caps, out MacroHLEFunctionName name) + { + var mc = MemoryMarshal.Cast<int, byte>(code); + + for (int i = 0; i < Table.Length; i++) + { + ref var entry = ref Table[i]; + + var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length)); + if (hash == entry.Hash) + { + name = entry.Name; + return IsMacroHLESupported(caps, name); + } + } + + name = MacroHLEFunctionName.None; + return false; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs index 0173a7fb..df6ee040 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Arguments FIFO. /// </summary> - public Queue<int> Fifo { get; } + public Queue<FifoWord> Fifo { get; } private int[] _gprs; @@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// </summary> public MacroInterpreter() { - Fifo = new Queue<int>(); + Fifo = new Queue<FifoWord>(); _gprs = new int[8]; } @@ -364,14 +364,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <returns>The call argument, or 0 if the FIFO is empty</returns> private int FetchParam() { - if (!Fifo.TryDequeue(out int value)) + if (!Fifo.TryDequeue(out var value)) { Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); return 0; } - return value; + return value.Word; } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs index f0393dd1..4077f74e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Arguments FIFO. /// </summary> - public Queue<int> Fifo => _context.Fifo; + public Queue<FifoWord> Fifo => _context.Fifo; private MacroJitCompiler.MacroExecute _execute; diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs index aa31c9ee..52c2a11b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs @@ -12,22 +12,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// <summary> /// Arguments FIFO. /// </summary> - public Queue<int> Fifo { get; } = new Queue<int>(); + public Queue<FifoWord> Fifo { get; } = new Queue<FifoWord>(); /// <summary> /// Fetches a arguments from the arguments FIFO. /// </summary> - /// <returns></returns> + /// <returns>The call argument, or 0 if the FIFO is empty</returns> public int FetchParam() { - if (!Fifo.TryDequeue(out int value)) + if (!Fifo.TryDequeue(out var value)) { Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); return 0; } - return value; + return value.Word; } /// <summary> |
