aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferHolder.cs25
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferManager.cs10
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferState.cs24
-rw-r--r--Ryujinx.Graphics.Vulkan/CacheByRange.cs50
-rw-r--r--Ryujinx.Graphics.Vulkan/EnumConversion.cs5
-rw-r--r--Ryujinx.Graphics.Vulkan/HelperShader.cs81
-rw-r--r--Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs139
-rw-r--r--Ryujinx.Graphics.Vulkan/IndexBufferState.cs97
-rw-r--r--Ryujinx.Graphics.Vulkan/PipelineBase.cs116
-rw-r--r--Ryujinx.Graphics.Vulkan/PipelineConverter.cs2
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs19
11 files changed, 503 insertions, 65 deletions
diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index a2fc0c39..f449c102 100644
--- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -370,6 +370,7 @@ namespace Ryujinx.Graphics.Vulkan
{
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
+ _gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
_cachedConvertedBuffers.Add(offset, size, key, holder);
@@ -388,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
+ _gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
key.SetBuffer(holder.GetBuffer());
@@ -398,6 +400,29 @@ namespace Ryujinx.Graphics.Vulkan
return holder.GetBuffer();
}
+ public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
+ {
+ var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
+
+ if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
+ {
+ // The destination index size is always I32.
+
+ int indexCount = size / indexSize;
+
+ int convertedCount = pattern.GetConvertedCount(indexCount);
+
+ holder = _gd.BufferManager.Create(_gd, convertedCount * 4);
+
+ _gd.PipelineInternal.EndRenderPass();
+ _gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
+
+ _cachedConvertedBuffers.Add(offset, size, key, holder);
+ }
+
+ return holder.GetBuffer();
+ }
+
public void Dispose()
{
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 43bd9877..65883fd0 100644
--- a/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
+ public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
+ {
+ if (TryGetBuffer(handle, out var holder))
+ {
+ return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
+ }
+
+ return null;
+ }
+
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out var holder))
diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/Ryujinx.Graphics.Vulkan/BufferState.cs
index 1790017a..88858e86 100644
--- a/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -1,5 +1,4 @@
-using Silk.NET.Vulkan;
-using System;
+using System;
namespace Ryujinx.Graphics.Vulkan
{
@@ -9,38 +8,17 @@ namespace Ryujinx.Graphics.Vulkan
private readonly int _offset;
private readonly int _size;
- private readonly IndexType _type;
private readonly Auto<DisposableBuffer> _buffer;
- public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
- {
- _buffer = buffer;
-
- _offset = offset;
- _size = size;
- _type = type;
- buffer?.IncrementReferenceCount();
- }
-
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{
_buffer = buffer;
-
_offset = offset;
_size = size;
- _type = IndexType.Uint16;
buffer?.IncrementReferenceCount();
}
- public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
- {
- if (_buffer != null)
- {
- api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
- }
- }
-
public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
{
if (_buffer != null)
diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
index f9edca8a..4c47e1c1 100644
--- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs
+++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
@@ -10,14 +10,25 @@ namespace Ryujinx.Graphics.Vulkan
struct I8ToI16CacheKey : ICacheKey
{
- public I8ToI16CacheKey() { }
+ // Used to notify the pipeline that bindings have invalidated on dispose.
+ private readonly VulkanRenderer _gd;
+ private Auto<DisposableBuffer> _buffer;
+
+ public I8ToI16CacheKey(VulkanRenderer gd)
+ {
+ _gd = gd;
+ _buffer = null;
+ }
public bool KeyEqual(ICacheKey other)
{
return other is I8ToI16CacheKey;
}
- public void Dispose() { }
+ public void Dispose()
+ {
+ _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
+ }
}
struct AlignedVertexBufferCacheKey : ICacheKey
@@ -55,6 +66,41 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ struct TopologyConversionCacheKey : ICacheKey
+ {
+ private IndexBufferPattern _pattern;
+ private int _indexSize;
+
+ // Used to notify the pipeline that bindings have invalidated on dispose.
+ private readonly VulkanRenderer _gd;
+ private Auto<DisposableBuffer> _buffer;
+
+ public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
+ {
+ _gd = gd;
+ _pattern = pattern;
+ _indexSize = indexSize;
+ _buffer = null;
+ }
+
+ public bool KeyEqual(ICacheKey other)
+ {
+ return other is TopologyConversionCacheKey entry &&
+ entry._pattern == _pattern &&
+ entry._indexSize == _indexSize;
+ }
+
+ public void SetBuffer(Auto<DisposableBuffer> buffer)
+ {
+ _buffer = buffer;
+ }
+
+ public void Dispose()
+ {
+ _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
+ }
+ }
+
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs
index ab40cb10..804cd70c 100644
--- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs
+++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs
@@ -2,6 +2,7 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
+using System;
namespace Ryujinx.Graphics.Vulkan
{
@@ -179,8 +180,8 @@ namespace Ryujinx.Graphics.Vulkan
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
- GAL.PrimitiveTopology.Quads => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, // Emulated with triangle fans
- GAL.PrimitiveTopology.QuadStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, // Emulated with triangle strips
+ GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
+ GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
};
}
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 2eec92f0..0201de0a 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
using Ryujinx.Graphics.Vulkan.Shaders;
using Silk.NET.Vulkan;
using System;
+using System.Collections.Generic;
using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan
@@ -399,6 +400,86 @@ namespace Ryujinx.Graphics.Vulkan
newSize);
}
+ public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ BufferHolder src,
+ BufferHolder dst,
+ IndexBufferPattern pattern,
+ int indexSize,
+ int srcOffset,
+ int indexCount)
+ {
+ int convertedCount = pattern.GetConvertedCount(indexCount);
+ int outputIndexSize = 4;
+
+ // TODO: Do this with a compute shader?
+ var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
+ var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
+
+ gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
+
+ var bufferCopy = new List<BufferCopy>();
+ int outputOffset = 0;
+
+ // Try to merge copies of adjacent indices to reduce copy count.
+ int sequenceStart = 0;
+ int sequenceLength = 0;
+
+ foreach (var index in pattern.GetIndexMapping(indexCount))
+ {
+ if (sequenceLength > 0)
+ {
+ if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
+ {
+ sequenceLength++;
+ continue;
+ }
+
+ // Commit the copy so far.
+ bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
+ outputOffset += outputIndexSize * sequenceLength;
+ }
+
+ sequenceStart = index;
+ sequenceLength = 1;
+ }
+
+ if (sequenceLength > 0)
+ {
+ // Commit final pending copy.
+ bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
+ }
+
+ var bufferCopyArray = bufferCopy.ToArray();
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ dstBuffer,
+ BufferHolder.DefaultAccessFlags,
+ AccessFlags.AccessTransferWriteBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageTransferBit,
+ 0,
+ convertedCount * outputIndexSize);
+
+ fixed (BufferCopy* pBufferCopy = bufferCopyArray)
+ {
+ gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
+ }
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ dstBuffer,
+ AccessFlags.AccessTransferWriteBit,
+ BufferHolder.DefaultAccessFlags,
+ PipelineStageFlags.PipelineStageTransferBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ 0,
+ convertedCount * outputIndexSize);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
new file mode 100644
index 00000000..8439e79d
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
@@ -0,0 +1,139 @@
+using Ryujinx.Graphics.GAL;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal class IndexBufferPattern : IDisposable
+ {
+ public int PrimitiveVertices { get; }
+ public int PrimitiveVerticesOut { get; }
+ public int BaseIndex { get; }
+ public int[] OffsetIndex { get; }
+ public int IndexStride { get; }
+ public bool RepeatStart { get; }
+
+ private VulkanRenderer _gd;
+ private int _currentSize;
+ private BufferHandle _repeatingBuffer;
+
+ public IndexBufferPattern(VulkanRenderer gd,
+ int primitiveVertices,
+ int primitiveVerticesOut,
+ int baseIndex,
+ int[] offsetIndex,
+ int indexStride,
+ bool repeatStart)
+ {
+ PrimitiveVertices = primitiveVertices;
+ PrimitiveVerticesOut = primitiveVerticesOut;
+ BaseIndex = baseIndex;
+ OffsetIndex = offsetIndex;
+ IndexStride = indexStride;
+ RepeatStart = repeatStart;
+
+ _gd = gd;
+ }
+
+ public int GetPrimitiveCount(int vertexCount)
+ {
+ return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
+ }
+
+ public int GetConvertedCount(int indexCount)
+ {
+ int primitiveCount = GetPrimitiveCount(indexCount);
+ return primitiveCount * OffsetIndex.Length;
+ }
+
+ public IEnumerable<int> GetIndexMapping(int indexCount)
+ {
+ int primitiveCount = GetPrimitiveCount(indexCount);
+ int index = BaseIndex;
+
+ for (int i = 0; i < primitiveCount; i++)
+ {
+ if (RepeatStart)
+ {
+ // Used for triangle fan
+ yield return 0;
+ }
+
+ for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
+ {
+ yield return index + OffsetIndex[j];
+ }
+
+ index += IndexStride;
+ }
+ }
+
+ public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
+ {
+ int primitiveCount = GetPrimitiveCount(vertexCount);
+ indexCount = primitiveCount * PrimitiveVerticesOut;
+
+ int expectedSize = primitiveCount * OffsetIndex.Length;
+
+ if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
+ {
+ return _repeatingBuffer;
+ }
+
+ // Expand the repeating pattern to the number of requested primitives.
+ BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
+
+ // Copy the old data to the new one.
+ if (_repeatingBuffer != BufferHandle.Null)
+ {
+ _gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
+ _gd.DeleteBuffer(_repeatingBuffer);
+ }
+
+ _repeatingBuffer = newBuffer;
+
+ // Add the additional repeats on top.
+ int newPrimitives = primitiveCount;
+ int oldPrimitives = (_currentSize) / OffsetIndex.Length;
+
+ int[] newData;
+
+ newPrimitives -= oldPrimitives;
+ newData = new int[expectedSize - _currentSize];
+
+ int outOffset = 0;
+ int index = oldPrimitives * IndexStride + BaseIndex;
+
+ for (int i = 0; i < newPrimitives; i++)
+ {
+ if (RepeatStart)
+ {
+ // Used for triangle fan
+ newData[outOffset++] = 0;
+ }
+
+ for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
+ {
+ newData[outOffset++] = index + OffsetIndex[j];
+ }
+
+ index += IndexStride;
+ }
+
+ _gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
+ _currentSize = expectedSize;
+
+ return newBuffer;
+ }
+
+ public void Dispose()
+ {
+ if (_repeatingBuffer != BufferHandle.Null)
+ {
+ _gd.DeleteBuffer(_repeatingBuffer);
+ _repeatingBuffer = BufferHandle.Null;
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
new file mode 100644
index 00000000..1a112d4d
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
@@ -0,0 +1,97 @@
+using Silk.NET.Vulkan;
+using System;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal struct IndexBufferState
+ {
+ public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0);
+
+ private readonly int _offset;
+ private readonly int _size;
+ private readonly IndexType _type;
+
+ private readonly GAL.BufferHandle _handle;
+ private Auto<DisposableBuffer> _buffer;
+
+ public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type)
+ {
+ _handle = handle;
+ _offset = offset;
+ _size = size;
+ _type = type;
+ _buffer = null;
+ }
+
+ public IndexBufferState(GAL.BufferHandle handle, int offset, int size)
+ {
+ _handle = handle;
+ _offset = offset;
+ _size = size;
+ _type = IndexType.Uint16;
+ _buffer = null;
+ }
+
+ public void BindIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs)
+ {
+ Auto<DisposableBuffer> autoBuffer;
+ int offset, size;
+ IndexType type = _type;
+
+ if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8)
+ {
+ // Index type is not supported. Convert to I16.
+ autoBuffer = gd.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
+
+ type = IndexType.Uint16;
+ offset = 0;
+ size = _size * 2;
+ }
+ else
+ {
+ autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
+
+ offset = _offset;
+ size = _size;
+ }
+
+ _buffer = autoBuffer;
+
+ if (autoBuffer != null)
+ {
+ gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type);
+ }
+ }
+
+ public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
+ {
+ Auto<DisposableBuffer> autoBuffer;
+
+ // Convert the index buffer using the given pattern.
+ int indexSize = _type switch
+ {
+ IndexType.Uint32 => 4,
+ IndexType.Uint16 => 2,
+ _ => 1,
+ };
+
+ int firstIndexOffset = firstIndex * indexSize;
+
+ autoBuffer = gd.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
+
+ int size = convertedCount * 4;
+
+ _buffer = autoBuffer;
+
+ if (autoBuffer != null)
+ {
+ gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
+ }
+ }
+
+ public bool BoundEquals(Auto<DisposableBuffer> buffer)
+ {
+ return _buffer == buffer;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 5c666b09..39acc5d9 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -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;
+
+ int convertedCount = pattern.GetConvertedCount(indexCount);
- for (int i = 0; i < quadsCount; i++)
+ 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();
diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index c0930351..3ff111e8 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
- pipeline.Topology = state.Topology.Convert();
+ pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 3776be9e..d92fff49 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -471,6 +471,25 @@ namespace Ryujinx.Graphics.Vulkan
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
}
+ public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
+ {
+ return topology switch
+ {
+ GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
+ GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
+ _ => topology
+ };
+ }
+
+ public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
+ {
+ return topology switch
+ {
+ GAL.PrimitiveTopology.Quads => true,
+ _ => false
+ };
+ }
+
public void Initialize(GraphicsDebugLevel logLevel)
{
SetupContext(logLevel);