diff options
author | gdkchan <gab.dark.100@gmail.com> | 2022-11-16 14:53:04 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-16 14:53:04 -0300 |
commit | f1d1670b0b1b5c08064df95dabd295f3cf5dcf7f (patch) | |
tree | 082139cb80ee9776f3ea9083991fb3ed6618f7f4 /Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | |
parent | b8de72de8f25f0bb7f994bc07a0387c1c247b6fe (diff) |
Implement HLE macro for DrawElementsIndirect (#3748)1.1.345
* 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/MacroHLE.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 177 |
1 files changed, 158 insertions, 19 deletions
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); } } } |