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>
This commit is contained in:
parent
b8de72de8f
commit
f1d1670b0b
60 changed files with 2336 additions and 277 deletions
|
@ -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;
|
||||
|
||||
_processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, 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);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue