Convert Quads to Triangles in Vulkan (#3715)

* Add Index Buffer conversion for quads to Vulkan

Also adds a reusable repeating pattern index buffer to use for non-indexed
draws, and generalizes the conversion cache for buffers.

* Fix some issues

* End render pass before conversion

* Resume transform feedback after we ensure we're in a pass.

* Always generate UInt32 type indices for topology conversion

* No it's not.

* Remove unused code

* Rely on TopologyRemap to convert quads to tris.

* Remove double newline

* Ensure render pass ends before stride or I8 conversion
This commit is contained in:
riperiperi 2022-09-20 22:38:48 +01:00 committed by GitHub
parent da75a9a6ea
commit 4c0eb91d7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 503 additions and 65 deletions

View file

@ -51,13 +51,16 @@ namespace Ryujinx.Graphics.Vulkan
private readonly DescriptorSetUpdater _descriptorSetUpdater;
private BufferState _indexBuffer;
private IndexBufferState _indexBuffer;
private IndexBufferPattern _indexBufferPattern;
private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor;
public SupportBufferUpdater SupportBufferUpdater;
public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern;
private bool _needsIndexBufferRebind;
private bool _needsTransformFeedbackBuffersRebind;
@ -107,6 +110,9 @@ namespace Ryujinx.Graphics.Vulkan
{
SupportBufferUpdater = new SupportBufferUpdater(Gd);
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
}
public unsafe void Barrier()
@ -245,6 +251,14 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer)
{
if (_indexBuffer.BoundEquals(buffer))
{
_needsIndexBufferRebind = true;
}
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
if (!_program.IsLinked)
@ -267,24 +281,59 @@ namespace Ryujinx.Graphics.Vulkan
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads)
if (Gd.TopologyUnsupported(_topology))
{
int quadsCount = vertexCount / 4;
// Temporarily bind a conversion pattern as an index buffer.
_needsIndexBufferRebind = true;
for (int i = 0; i < quadsCount; i++)
IndexBufferPattern pattern = _topology switch
{
Gd.Api.CmdDraw(CommandBuffer, 4, (uint)instanceCount, (uint)(firstVertex + i * 4), (uint)firstInstance);
}
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false);
Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32);
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
}
else
{
ResumeTransformFeedbackInternal();
Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance);
}
}
private void UpdateIndexBufferPattern()
{
IndexBufferPattern pattern = null;
if (Gd.TopologyUnsupported(_topology))
{
pattern = _topology switch
{
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
};
}
if (_indexBufferPattern != pattern)
{
_indexBufferPattern = pattern;
_needsIndexBufferRebind = true;
}
}
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{
if (!_program.IsLinked)
@ -292,22 +341,34 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass();
ResumeTransformFeedbackInternal();
DrawCount++;
if (_topology == GAL.PrimitiveTopology.Quads)
if (_indexBufferPattern != null)
{
int quadsCount = indexCount / 4;
// Convert the index buffer into a supported topology.
IndexBufferPattern pattern = _indexBufferPattern;
for (int i = 0; i < quadsCount; i++)
int convertedCount = pattern.GetConvertedCount(indexCount);
if (_needsIndexBufferRebind)
{
Gd.Api.CmdDrawIndexed(CommandBuffer, 4, (uint)instanceCount, (uint)(firstIndex + i * 4), firstVertex, (uint)firstInstance);
_indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern);
_needsIndexBufferRebind = false;
}
BeginRenderPass(); // May have been interrupted to set buffer data.
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
}
else
{
ResumeTransformFeedbackInternal();
Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance);
}
}
@ -500,34 +561,16 @@ namespace Ryujinx.Graphics.Vulkan
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
{
_indexBuffer.Dispose();
if (buffer.Handle != BufferHandle.Null)
{
Auto<DisposableBuffer> ib = null;
int offset = buffer.Offset;
int size = buffer.Size;
if (type == GAL.IndexType.UByte && !Gd.Capabilities.SupportsIndexTypeUint8)
{
ib = Gd.BufferManager.GetBufferI8ToI16(Cbs, buffer.Handle, offset, size);
offset = 0;
size *= 2;
type = GAL.IndexType.UShort;
}
else
{
ib = Gd.BufferManager.GetBuffer(CommandBuffer, buffer.Handle, false);
}
_indexBuffer = new BufferState(ib, offset, size, type.Convert());
_indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert());
}
else
{
_indexBuffer = BufferState.Null;
_indexBuffer = IndexBufferState.Null;
}
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
_needsIndexBufferRebind = true;
}
public void SetLineParameters(float width, bool smooth)
@ -584,7 +627,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_topology = topology;
var vkTopology = topology.Convert();
var vkTopology = Gd.TopologyRemap(topology).Convert();
_newState.Topology = vkTopology;
@ -1127,9 +1170,9 @@ namespace Ryujinx.Graphics.Vulkan
// Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit();
if (_needsIndexBufferRebind)
if (_needsIndexBufferRebind && _indexBufferPattern == null)
{
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
_indexBuffer.BindIndexBuffer(Gd, Cbs);
_needsIndexBufferRebind = false;
}
@ -1265,7 +1308,6 @@ namespace Ryujinx.Graphics.Vulkan
{
_renderPass?.Dispose();
_framebuffer?.Dispose();
_indexBuffer.Dispose();
_newState.Dispose();
_descriptorSetUpdater.Dispose();