aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/MME
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2022-11-16 14:53:04 -0300
committerGitHub <noreply@github.com>2022-11-16 14:53:04 -0300
commitf1d1670b0b1b5c08064df95dabd295f3cf5dcf7f (patch)
tree082139cb80ee9776f3ea9083991fb3ed6618f7f4 /Ryujinx.Graphics.Gpu/Engine/MME
parentb8de72de8f25f0bb7f994bc07a0387c1c247b6fe (diff)
Implement HLE macro for DrawElementsIndirect (#3748)
* Implement HLE macro for DrawElementsIndirect * Shader cache version bump * Use GL_ARB_shader_draw_parameters extension on OpenGL * Fix DrawIndexedIndirectCount on Vulkan when extension is not supported * Implement DrawIndex * Alignment * Fix some validation errors * Rename BaseIds to DrawParameters * Fix incorrect index buffer and vertex buffer size in some cases * Add HLE macros for DrawArraysInstanced and DrawElementsInstanced * Perform a regular draw when indirect data is not modified * Use non-indirect draw methods if indirect buffer was not GPU modified * Only check if draw parameters match if the shader actually uses them * Expose Macro HLE setting on GUI * Reset FirstVertex and FirstInstance after draw * Update shader cache version again since some people already tested this * PR feedback Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine/MME')
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs10
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs177
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs29
4 files changed, 191 insertions, 28 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
index 1d054969..12a3ac02 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
@@ -62,10 +62,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- 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.
+ if (_hleFunction == MacroHLEFunctionName.DrawElementsIndirect)
+ {
+ context.GPFifo.SetFlushSkips(1);
+ }
+ else 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);
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
index 5f238a71..8630bbc4 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
@@ -16,6 +16,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private const int ColorStructSize = 0x40;
private const int ZetaLayerCountOffset = 0x1230;
+ private const int IndirectDataEntrySize = 0x10;
+ private const int IndirectIndexedDataEntrySize = 0x14;
+
private readonly GPFifoProcessor _processor;
private readonly MacroHLEFunctionName _functionName;
@@ -27,9 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <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="processor">GPU GP FIFO command processor</param>
/// <param name="functionName">Name of the HLE macro function to be called</param>
public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
{
@@ -55,12 +56,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
case MacroHLEFunctionName.ClearDepthStencil:
ClearDepthStencil(state, arg0);
break;
+ case MacroHLEFunctionName.DrawArraysInstanced:
+ DrawArraysInstanced(state, arg0);
+ break;
+ case MacroHLEFunctionName.DrawElementsInstanced:
+ DrawElementsInstanced(state, arg0);
+ break;
+ case MacroHLEFunctionName.DrawElementsIndirect:
+ DrawElementsIndirect(state, arg0);
+ break;
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
MultiDrawElementsIndirectCount(state, arg0);
break;
default:
throw new NotImplementedException(_functionName.ToString());
}
+
+ // It should be empty at this point, but clear it just to be safe.
+ Fifo.Clear();
}
/// <summary>
@@ -89,7 +102,118 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
/// <summary>
- /// Performs a indirect multi-draw, with parameters from a GPU buffer.
+ /// Performs a draw.
+ /// </summary>
+ /// <param name="state">GPU state at the time of the call</param>
+ /// <param name="arg0">First argument of the call</param>
+ private void DrawArraysInstanced(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ 0,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: false);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw.
+ /// </summary>
+ /// <param name="state">GPU state at the time of the call</param>
+ /// <param name="arg0">First argument of the call</param>
+ private void DrawElementsInstanced(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstIndex = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ firstIndex.Word,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: true);
+ }
+
+ /// <summary>
+ /// Performs a indirect indexed 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 DrawElementsIndirect(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstIndex = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ ulong indirectBufferGpuVa = count.GpuVa;
+
+ var bufferCache = _processor.MemoryManager.Physical.BufferCache;
+
+ bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
+
+ if (useBuffer)
+ {
+ int indexCount = firstIndex.Word + count.Word;
+
+ _processor.ThreedClass.DrawIndirect(
+ topology,
+ indirectBufferAddress,
+ 0,
+ 1,
+ IndirectIndexedDataEntrySize,
+ indexCount,
+ Threed.IndirectDrawType.DrawIndexedIndirect);
+ }
+ else
+ {
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ firstIndex.Word,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: true);
+ }
+ }
+
+ /// <summary>
+ /// Performs a indirect indexed 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>
@@ -132,8 +256,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
return;
}
- int indirectBufferSize = maxDrawCount * stride;
-
ulong indirectBufferGpuVa = 0;
int indexCount = 0;
@@ -142,8 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
var count = FetchParam();
var instanceCount = FetchParam();
var firstIndex = FetchParam();
- var baseVertex = FetchParam();
- var baseInstance = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
if (i == 0)
{
@@ -161,15 +283,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- // It should be empty at this point, but clear it just to be safe.
- Fifo.Clear();
-
var bufferCache = _processor.MemoryManager.Physical.BufferCache;
- var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4);
- var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize);
+ ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
+
+ ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
+ ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
+
+ _processor.ThreedClass.DrawIndirect(
+ topology,
+ indirectBufferAddress,
+ parameterBufferAddress,
+ maxDrawCount,
+ stride,
+ indexCount,
+ Threed.IndirectDrawType.DrawIndexedIndirectCount);
+ }
- _processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ /// <summary>
+ /// Checks if the draw should be skipped, because the masked instance count is zero.
+ /// </summary>
+ /// <param name="state">Current GPU state</param>
+ /// <param name="instanceCount">Draw instance count</param>
+ /// <returns>True if the draw should be skipped, false otherwise</returns>
+ private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
+ {
+ return (Read(state, 0xd1b) & instanceCount) == 0;
}
/// <summary>
@@ -189,14 +328,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
/// <summary>
- /// Performs a GPU method call.
+ /// Reads data from a GPU register.
/// </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)
+ /// <param name="reg">Register offset to read</param>
+ /// <returns>GPU register value</returns>
+ private static int Read(IDeviceState state, int reg)
{
- state.Write(methAddr * 4, value);
+ return state.Read(reg * 4);
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
index 4cce07fa..751867fc 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
@@ -8,6 +8,9 @@
None,
ClearColor,
ClearDepthStencil,
+ DrawArraysInstanced,
+ DrawElementsInstanced,
+ DrawElementsIndirect,
MultiDrawElementsIndirectCount
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
index c5d98848..ab6f54ef 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
@@ -44,17 +44,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- private static readonly TableEntry[] Table = new TableEntry[]
+ private static readonly TableEntry[] _table = new TableEntry[]
{
new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
+ new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
+ new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
+ new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C)
};
+ /// <summary>
+ /// Checks if the host supports all features required by the HLE macro.
+ /// </summary>
+ /// <param name="caps">Host capabilities</param>
+ /// <param name="name">Name of the HLE macro to be checked</param>
+ /// <returns>True if the host supports the HLE macro, false otherwise</returns>
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
{
if (name == MacroHLEFunctionName.ClearColor ||
- name == MacroHLEFunctionName.ClearDepthStencil)
+ name == MacroHLEFunctionName.ClearDepthStencil ||
+ name == MacroHLEFunctionName.DrawArraysInstanced ||
+ name == MacroHLEFunctionName.DrawElementsInstanced ||
+ name == MacroHLEFunctionName.DrawElementsIndirect)
{
return true;
}
@@ -77,15 +89,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
{
var mc = MemoryMarshal.Cast<int, byte>(code);
- for (int i = 0; i < Table.Length; i++)
+ for (int i = 0; i < _table.Length; i++)
{
- ref var entry = ref Table[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);
+ if (IsMacroHLESupported(caps, entry.Name))
+ {
+ name = entry.Name;
+ return true;
+ }
+
+ break;
}
}