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 | |
| parent | b8de72de8f25f0bb7f994bc07a0387c1c247b6fe (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')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs | 10 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 177 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs | 3 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs | 6 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs | 165 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs | 10 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs | 38 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs | 58 |
10 files changed, 454 insertions, 71 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; } } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs index 85f66985..a6b62a4a 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed else { evt.Flush(); - return (memoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; + return (memoryManager.Read<ulong>(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } } @@ -108,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed evt?.Flush(); evt2?.Flush(); - ulong x = memoryManager.Read<ulong>(gpuVa); - ulong y = memoryManager.Read<ulong>(gpuVa + 16); + ulong x = memoryManager.Read<ulong>(gpuVa, true); + ulong y = memoryManager.Read<ulong>(gpuVa + 16, true); return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index e02c8cdc..a7acb469 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Memory; using System; namespace Ryujinx.Graphics.Gpu.Engine.Threed @@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// </summary> class DrawManager { + // Since we don't know the index buffer size for indirect draws, + // we must assume a minimum and maximum size and use that for buffer data update purposes. + private const int MinIndirectIndexCount = 0x10000; + private const int MaxIndirectIndexCount = 0x4000000; + private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly DeviceStateWithShadow<ThreedClassState> _state; @@ -28,6 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private int _instanceIndex; + private const int VertexBufferFirstMethodOffset = 0x35d; private const int IndexBufferCountMethodOffset = 0x5f8; /// <summary> @@ -237,6 +244,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _instanceIndex = 0; } + UpdateTopology(topology); + } + + /// <summary> + /// Updates the current primitive topology if needed. + /// </summary> + /// <param name="topology">New primitive topology</param> + private void UpdateTopology(PrimitiveTopology topology) + { if (_drawState.Topology != topology || !_topologySet) { _context.Renderer.Pipeline.SetPrimitiveTopology(topology); @@ -383,28 +399,104 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } /// <summary> - /// Performs a indirect multi-draw, with parameters from a GPU buffer. + /// Performs a indexed or non-indexed draw. + /// </summary> + /// <param name="engine">3D engine where this method is being called</param> + /// <param name="topology">Primitive topology</param> + /// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param> + /// <param name="instanceCount">Instance count</param> + /// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param> + /// <param name="firstVertex">First vertex on the vertex buffer</param> + /// <param name="firstInstance">First instance</param> + /// <param name="indexed">True if the draw is indexed, false otherwise</param> + public void Draw( + ThreedClass engine, + PrimitiveTopology topology, + int count, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance, + bool indexed) + { + UpdateTopology(topology); + + ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( + _context, + _channel.MemoryManager, + _state.State.RenderEnableAddress, + _state.State.RenderEnableCondition); + + if (renderEnable == ConditionalRenderEnabled.False) + { + _drawState.DrawIndexed = false; + return; + } + + if (indexed) + { + _drawState.FirstIndex = firstIndex; + _drawState.IndexCount = count; + _state.State.FirstVertex = (uint)firstVertex; + engine.ForceStateDirty(IndexBufferCountMethodOffset * 4); + } + else + { + _state.State.VertexBufferDrawState.First = firstVertex; + _state.State.VertexBufferDrawState.Count = count; + engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4); + } + + _state.State.FirstInstance = (uint)firstInstance; + + _drawState.DrawIndexed = indexed; + _drawState.HasConstantBufferDrawParameters = true; + + engine.UpdateState(); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance); + _state.State.FirstVertex = 0; + } + else + { + _context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance); + } + + _state.State.FirstInstance = 0; + + _drawState.DrawIndexed = false; + _drawState.HasConstantBufferDrawParameters = false; + + if (renderEnable == ConditionalRenderEnabled.Host) + { + _context.Renderer.Pipeline.EndHostConditionalRendering(); + } + } + + /// <summary> + /// Performs a indirect draw, with parameters from a GPU buffer. /// </summary> /// <param name="engine">3D engine where this method is being called</param> /// <param name="topology">Primitive topology</param> - /// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param> - /// <param name="parameterBuffer">GPU buffer with the draw count</param> + /// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param> + /// <param name="parameterBufferAddress">Address of the buffer with the draw count</param> /// <param name="maxDrawCount">Maximum number of draws that can be made</param> - /// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param> - public void MultiDrawIndirectCount( + /// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param> + /// <param name="indexCount">Maximum number of indices that the draw can consume</param> + /// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param> + public void DrawIndirect( ThreedClass engine, - int indexCount, PrimitiveTopology topology, - BufferRange indirectBuffer, - BufferRange parameterBuffer, + ulong indirectBufferAddress, + ulong parameterBufferAddress, int maxDrawCount, - int stride) + int stride, + int indexCount, + IndirectDrawType drawType) { - engine.Write(IndexBufferCountMethodOffset * 4, indexCount); - - _context.Renderer.Pipeline.SetPrimitiveTopology(topology); - _drawState.Topology = topology; - _topologySet = true; + UpdateTopology(topology); ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( _context, @@ -418,21 +510,56 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return; } - _drawState.FirstIndex = _state.State.IndexBufferState.First; - _drawState.IndexCount = indexCount; + PhysicalMemory memory = _channel.MemoryManager.Physical; + + bool hasCount = (drawType & IndirectDrawType.Count) != 0; + bool indexed = (drawType & IndirectDrawType.Indexed) != 0; + + if (indexed) + { + indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount); + _drawState.FirstIndex = 0; + _drawState.IndexCount = indexCount; + engine.ForceStateDirty(IndexBufferCountMethodOffset * 4); + } + + _drawState.DrawIndexed = indexed; + _drawState.DrawIndirect = true; + _drawState.HasConstantBufferDrawParameters = true; engine.UpdateState(); - if (_drawState.DrawIndexed) + if (hasCount) { - _context.Renderer.Pipeline.MultiDrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride); + var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + } + else + { + _context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + } } else { - _context.Renderer.Pipeline.MultiDrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); + var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride); + + if (indexed) + { + _context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer); + } + else + { + _context.Renderer.Pipeline.DrawIndirect(indirectBuffer); + } } _drawState.DrawIndexed = false; + _drawState.DrawIndirect = false; + _drawState.HasConstantBufferDrawParameters = false; if (renderEnable == ConditionalRenderEnabled.Host) { diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs index ff186acc..fd1cb0ea 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs @@ -23,6 +23,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public bool DrawIndexed; /// <summary> + /// Indicates if the next draw will be a indirect draw. + /// </summary> + public bool DrawIndirect; + + /// <summary> /// Indicates if any of the currently used vertex shaders reads the instance ID. /// </summary> public bool VsUsesInstanceId; @@ -33,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public bool IsAnyVbInstanced; /// <summary> + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. + /// </summary> + public bool HasConstantBufferDrawParameters; + + /// <summary> /// Primitive topology for the next draw. /// </summary> public PrimitiveTopology Topology; diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs new file mode 100644 index 00000000..d78aa498 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs @@ -0,0 +1,38 @@ +namespace Ryujinx.Graphics.Gpu.Engine.Threed +{ + /// <summary> + /// Indirect draw type, which can be indexed or non-indexed, with or without a draw count. + /// </summary> + enum IndirectDrawType + { + /// <summary> + /// Non-indexed draw without draw count. + /// </summary> + DrawIndirect = 0, + + /// <summary> + /// Indexed draw without draw count. + /// </summary> + DrawIndexedIndirect = Indexed, + + /// <summary> + /// Non-indexed draw with draw count. + /// </summary> + DrawIndirectCount = Count, + + /// <summary> + /// Indexed draw with draw count. + /// </summary> + DrawIndexedIndirectCount = Indexed | Count, + + /// <summary> + /// Indexed flag. + /// </summary> + Indexed = 1 << 0, + + /// <summary> + /// Draw count flag. + /// </summary> + Count = 1 << 1 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index fdf8f822..3f71172c 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -34,10 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ProgramPipelineState _pipeline; + private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; private bool _prevDrawIndexed; + private bool _prevDrawIndirect; private IndexType _prevIndexType; private uint _prevFirstVertex; private bool _prevTfEnable; @@ -210,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // of the shader for the new state. if (_shaderSpecState != null) { - if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false)) + if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false)) { ForceShaderUpdate(); } @@ -237,6 +239,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _prevDrawIndexed = _drawState.DrawIndexed; } + // Some draw parameters are used to restrict the vertex buffer size, + // but they can't be used on indirect draws because their values are unknown in this case. + // When switching between indirect and non-indirect draw, we need to + // make sure the vertex buffer sizes are still correct. + if (_drawState.DrawIndirect != _prevDrawIndirect) + { + _updateTracker.ForceDirty(VertexBufferStateIndex); + } + // In some cases, the index type is also used to guess the // vertex buffer size, so we must update it if the type changed too. if (_drawState.DrawIndexed && @@ -938,6 +949,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _drawState.IsAnyVbInstanced = false; + bool drawIndexed = _drawState.DrawIndexed; + bool drawIndirect = _drawState.DrawIndirect; + for (int index = 0; index < Constants.TotalVertexBuffers; index++) { var vertexBuffer = _state.State.VertexBufferState[index]; @@ -965,14 +979,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong vbSize = endAddress.Pack() - address + 1; ulong size; - if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced) + if (_drawState.IbStreamer.HasInlineIndexData || drawIndexed || stride == 0 || instanced) { // This size may be (much) larger than the real vertex buffer size. // Avoid calculating it this way, unless we don't have any other option. size = vbSize; - if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced) + if (stride > 0 && indexTypeSmall && drawIndexed && !drawIndirect && !instanced) { // If the index type is a small integer type, then we might be still able // to reduce the vertex buffer size based on the maximum possible index value. @@ -1207,6 +1221,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed byte oldVsClipDistancesWritten = _vsClipDistancesWritten; _drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false; + _vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false; _vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0; if (oldVsClipDistancesWritten != _vsClipDistancesWritten) @@ -1222,6 +1237,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.SetProgram(gs.HostProgram); } + /// <summary> + /// Updates bindings consumed by the shader stage on the texture and buffer managers. + /// </summary> + /// <param name="stage">Shader stage to have the bindings updated</param> + /// <param name="info">Shader stage bindings info</param> private void UpdateStageBindings(int stage, ShaderProgramInfo info) { _currentProgramInfo[stage] = info; @@ -1340,7 +1360,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.AlphaTestEnable, _state.State.AlphaTestFunc, _state.State.AlphaTestRef, - ref attributeTypes); + ref attributeTypes, + _drawState.HasConstantBufferDrawParameters); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 8e222e71..106a6f3f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -498,34 +498,58 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } /// <summary> - /// Clears the current color and depth-stencil buffers. - /// Which buffers should be cleared can also specified with the arguments. + /// Performs a indexed or non-indexed draw. /// </summary> - /// <param name="argument">Method call argument</param> - /// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param> - public void Clear(int argument, int layerCount) + /// <param name="topology">Primitive topology</param> + /// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param> + /// <param name="instanceCount">Instance count</param> + /// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param> + /// <param name="firstVertex">First vertex on the vertex buffer</param> + /// <param name="firstInstance">First instance</param> + /// <param name="indexed">True if the draw is indexed, false otherwise</param> + public void Draw( + PrimitiveTopology topology, + int count, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance, + bool indexed) { - _drawManager.Clear(this, argument, layerCount); + _drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed); } /// <summary> - /// Performs a indirect multi-draw, with parameters from a GPU buffer. + /// Performs a indirect draw, with parameters from a GPU buffer. /// </summary> - /// <param name="indexCount">Index Buffer Count</param> /// <param name="topology">Primitive topology</param> - /// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param> - /// <param name="parameterBuffer">GPU buffer with the draw count</param> + /// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param> + /// <param name="parameterBufferAddress">Address of the buffer with the draw count</param> /// <param name="maxDrawCount">Maximum number of draws that can be made</param> - /// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param> - public void MultiDrawIndirectCount( - int indexCount, + /// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param> + /// <param name="indexCount">Maximum number of indices that the draw can consume</param> + /// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param> + public void DrawIndirect( PrimitiveTopology topology, - BufferRange indirectBuffer, - BufferRange parameterBuffer, + ulong indirectBufferAddress, + ulong parameterBufferAddress, int maxDrawCount, - int stride) + int stride, + int indexCount, + IndirectDrawType drawType) { - _drawManager.MultiDrawIndirectCount(this, indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride); + _drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType); + } + + /// <summary> + /// Clears the current color and depth-stencil buffers. + /// Which buffers should be cleared can also specified with the arguments. + /// </summary> + /// <param name="argument">Method call argument</param> + /// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param> + public void Clear(int argument, int layerCount) + { + _drawManager.Clear(this, argument, layerCount); } } } |
