aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/Threed
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/Threed
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/Threed')
-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
6 files changed, 263 insertions, 43 deletions
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);
}
}
}