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:
gdkchan 2022-11-16 14:53:04 -03:00 committed by GitHub
parent b8de72de8f
commit f1d1670b0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 2336 additions and 277 deletions

View file

@ -294,6 +294,19 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ);
}
public void DispatchComputeIndirect(Auto<DisposableBuffer> indirectBuffer, int indirectBufferOffset)
{
if (!_program.IsLinked)
{
return;
}
EndRenderPass();
RecreatePipelineIfNeeded(PipelineBindPoint.Compute);
Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset);
}
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{
if (!_program.IsLinked)
@ -395,6 +408,204 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void DrawIndexedIndirect(BufferRange indirectBuffer)
{
if (!_program.IsLinked)
{
return;
}
UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
DrawCount++;
if (_indexBufferPattern != null)
{
// Convert the index buffer into a supported topology.
IndexBufferPattern pattern = _indexBufferPattern;
Auto<DisposableBuffer> indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect(
Gd,
Cbs,
indirectBuffer,
BufferRange.Empty,
pattern,
false,
1,
indirectBuffer.Size);
_needsIndexBufferRebind = false;
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 0, 1, (uint)indirectBuffer.Size);
}
else
{
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size);
}
}
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!_program.IsLinked)
{
return;
}
UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
DrawCount++;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
if (_indexBufferPattern != null)
{
// Convert the index buffer into a supported topology.
IndexBufferPattern pattern = _indexBufferPattern;
Auto<DisposableBuffer> indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect(
Gd,
Cbs,
indirectBuffer,
parameterBuffer,
pattern,
true,
maxDrawCount,
stride);
_needsIndexBufferRebind = false;
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
if (Gd.Capabilities.SupportsIndirectParameters)
{
Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
CommandBuffer,
indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value,
0,
countBuffer,
(ulong)parameterBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
else
{
// This is also fine because the indirect data conversion always zeros
// the entries that are past the current draw count.
Gd.Api.CmdDrawIndexedIndirect(
CommandBuffer,
indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value,
0,
(uint)maxDrawCount,
(uint)stride);
}
}
else
{
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
ResumeTransformFeedbackInternal();
if (Gd.Capabilities.SupportsIndirectParameters)
{
Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
CommandBuffer,
buffer,
(ulong)indirectBuffer.Offset,
countBuffer,
(ulong)parameterBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
else
{
// Not fully correct, but we can't do much better if the host does not support indirect count.
Gd.Api.CmdDrawIndexedIndirect(
CommandBuffer,
buffer,
(ulong)indirectBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
}
}
public void DrawIndirect(BufferRange indirectBuffer)
{
if (!_program.IsLinked)
{
return;
}
// TODO: Support quads and other unsupported topologies.
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
Gd.Api.CmdDrawIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size);
}
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!Gd.Capabilities.SupportsIndirectParameters)
{
// TODO: Fallback for when this is not supported.
throw new NotSupportedException();
}
if (!_program.IsLinked)
{
return;
}
// TODO: Support quads and other unsupported topologies.
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
Gd.DrawIndirectCountApi.CmdDrawIndirectCount(
CommandBuffer,
buffer,
(ulong)indirectBuffer.Offset,
countBuffer,
(ulong)parameterBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{
if (texture is TextureView srcTexture)
@ -449,76 +660,6 @@ namespace Ryujinx.Graphics.Vulkan
return CommandBuffer.Handle == cb.Handle;
}
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!Gd.Capabilities.SupportsIndirectParameters)
{
throw new NotSupportedException();
}
if (_program.LinkStatus != ProgramLinkStatus.Success)
{
return;
}
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, true)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
Gd.DrawIndirectCountApi.CmdDrawIndirectCount(
CommandBuffer,
buffer,
(ulong)indirectBuffer.Offset,
countBuffer,
(ulong)parameterBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!Gd.Capabilities.SupportsIndirectParameters)
{
throw new NotSupportedException();
}
if (_program.LinkStatus != ProgramLinkStatus.Success)
{
return;
}
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
CommandBuffer,
buffer,
(ulong)indirectBuffer.Offset,
countBuffer,
(ulong)parameterBuffer.Offset,
(uint)maxDrawCount,
(uint)stride);
}
public void SetAlphaTest(bool enable, float reference, GAL.CompareOp op)
{
// This is currently handled using shader specialization, as Vulkan does not support alpha test.
@ -706,7 +847,7 @@ namespace Ryujinx.Graphics.Vulkan
if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span))
{
_newState.SpecializationData = new SpecData(dataSpan);
SignalStateChange();
}
}