aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine
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
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')
-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
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs6
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs165
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs10
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs38
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs29
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs58
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);
}
}
}