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 a2fc0c398..f449c1026 100644
--- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -370,6 +370,7 @@ namespace Ryujinx.Graphics.Vulkan
370 { 370 {
371 holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3); 371 holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
372 372
373 _gd.PipelineInternal.EndRenderPass();
373 _gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size); 374 _gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
374 375
375 _cachedConvertedBuffers.Add(offset, size, key, holder); 376 _cachedConvertedBuffers.Add(offset, size, key, holder);
@@ -388,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
388 389
389 holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride); 390 holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
390 391
392 _gd.PipelineInternal.EndRenderPass();
391 _gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride); 393 _gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
392 394
393 key.SetBuffer(holder.GetBuffer()); 395 key.SetBuffer(holder.GetBuffer());
@@ -398,6 +400,29 @@ namespace Ryujinx.Graphics.Vulkan
398 return holder.GetBuffer(); 400 return holder.GetBuffer();
399 } 401 }
400 402
403 public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
404 {
405 var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
406
407 if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
408 {
409 // The destination index size is always I32.
410
411 int indexCount = size / indexSize;
412
413 int convertedCount = pattern.GetConvertedCount(indexCount);
414
415 holder = _gd.BufferManager.Create(_gd, convertedCount * 4);
416
417 _gd.PipelineInternal.EndRenderPass();
418 _gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
419
420 _cachedConvertedBuffers.Add(offset, size, key, holder);
421 }
422
423 return holder.GetBuffer();
424 }
425
401 public void Dispose() 426 public void Dispose()
402 { 427 {
403 _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); 428 _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 43bd9877d..65883fd0d 100644
--- a/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Vulkan
140 return null; 140 return null;
141 } 141 }
142 142
143 public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
144 {
145 if (TryGetBuffer(handle, out var holder))
146 {
147 return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
148 }
149
150 return null;
151 }
152
143 public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size) 153 public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
144 { 154 {
145 if (TryGetBuffer(handle, out var holder)) 155 if (TryGetBuffer(handle, out var holder))
diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/Ryujinx.Graphics.Vulkan/BufferState.cs
index 1790017a3..88858e863 100644
--- a/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -1,5 +1,4 @@
1using Silk.NET.Vulkan; 1using System;
2using System;
3 2
4namespace Ryujinx.Graphics.Vulkan 3namespace Ryujinx.Graphics.Vulkan
5{ 4{
@@ -9,38 +8,17 @@ namespace Ryujinx.Graphics.Vulkan
9 8
10 private readonly int _offset; 9 private readonly int _offset;
11 private readonly int _size; 10 private readonly int _size;
12 private readonly IndexType _type;
13 11
14 private readonly Auto<DisposableBuffer> _buffer; 12 private readonly Auto<DisposableBuffer> _buffer;
15 13
16 public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
17 {
18 _buffer = buffer;
19
20 _offset = offset;
21 _size = size;
22 _type = type;
23 buffer?.IncrementReferenceCount();
24 }
25
26 public BufferState(Auto<DisposableBuffer> buffer, int offset, int size) 14 public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
27 { 15 {
28 _buffer = buffer; 16 _buffer = buffer;
29
30 _offset = offset; 17 _offset = offset;
31 _size = size; 18 _size = size;
32 _type = IndexType.Uint16;
33 buffer?.IncrementReferenceCount(); 19 buffer?.IncrementReferenceCount();
34 } 20 }
35 21
36 public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
37 {
38 if (_buffer != null)
39 {
40 api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
41 }
42 }
43
44 public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding) 22 public void BindTransformFeedbackBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
45 { 23 {
46 if (_buffer != null) 24 if (_buffer != null)
diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
index f9edca8a2..4c47e1c17 100644
--- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs
+++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
@@ -10,14 +10,25 @@ namespace Ryujinx.Graphics.Vulkan
10 10
11 struct I8ToI16CacheKey : ICacheKey 11 struct I8ToI16CacheKey : ICacheKey
12 { 12 {
13 public I8ToI16CacheKey() { } 13 // Used to notify the pipeline that bindings have invalidated on dispose.
14 private readonly VulkanRenderer _gd;
15 private Auto<DisposableBuffer> _buffer;
16
17 public I8ToI16CacheKey(VulkanRenderer gd)
18 {
19 _gd = gd;
20 _buffer = null;
21 }
14 22
15 public bool KeyEqual(ICacheKey other) 23 public bool KeyEqual(ICacheKey other)
16 { 24 {
17 return other is I8ToI16CacheKey; 25 return other is I8ToI16CacheKey;
18 } 26 }
19 27
20 public void Dispose() { } 28 public void Dispose()
29 {
30 _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
31 }
21 } 32 }
22 33
23 struct AlignedVertexBufferCacheKey : ICacheKey 34 struct AlignedVertexBufferCacheKey : ICacheKey
@@ -55,6 +66,41 @@ namespace Ryujinx.Graphics.Vulkan
55 } 66 }
56 } 67 }
57 68
69 struct TopologyConversionCacheKey : ICacheKey
70 {
71 private IndexBufferPattern _pattern;
72 private int _indexSize;
73
74 // Used to notify the pipeline that bindings have invalidated on dispose.
75 private readonly VulkanRenderer _gd;
76 private Auto<DisposableBuffer> _buffer;
77
78 public TopologyConversionCacheKey(VulkanRenderer gd, IndexBufferPattern pattern, int indexSize)
79 {
80 _gd = gd;
81 _pattern = pattern;
82 _indexSize = indexSize;
83 _buffer = null;
84 }
85
86 public bool KeyEqual(ICacheKey other)
87 {
88 return other is TopologyConversionCacheKey entry &&
89 entry._pattern == _pattern &&
90 entry._indexSize == _indexSize;
91 }
92
93 public void SetBuffer(Auto<DisposableBuffer> buffer)
94 {
95 _buffer = buffer;
96 }
97
98 public void Dispose()
99 {
100 _gd.PipelineInternal.DirtyIndexBuffer(_buffer);
101 }
102 }
103
58 struct CacheByRange<T> where T : IDisposable 104 struct CacheByRange<T> where T : IDisposable
59 { 105 {
60 private struct Entry 106 private struct Entry
diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs
index ab40cb10e..804cd70c4 100644
--- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs
+++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs
@@ -2,6 +2,7 @@
2using Ryujinx.Graphics.GAL; 2using Ryujinx.Graphics.GAL;
3using Ryujinx.Graphics.Shader; 3using Ryujinx.Graphics.Shader;
4using Silk.NET.Vulkan; 4using Silk.NET.Vulkan;
5using System;
5 6
6namespace Ryujinx.Graphics.Vulkan 7namespace Ryujinx.Graphics.Vulkan
7{ 8{
@@ -179,8 +180,8 @@ namespace Ryujinx.Graphics.Vulkan
179 GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency, 180 GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
180 GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency, 181 GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
181 GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList, 182 GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
182 GAL.PrimitiveTopology.Quads => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan, // Emulated with triangle fans 183 GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
183 GAL.PrimitiveTopology.QuadStrip => Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip, // Emulated with triangle strips 184 GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
184 _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList) 185 _ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
185 }; 186 };
186 } 187 }
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 2eec92f03..0201de0ad 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.Translation;
4using Ryujinx.Graphics.Vulkan.Shaders; 4using Ryujinx.Graphics.Vulkan.Shaders;
5using Silk.NET.Vulkan; 5using Silk.NET.Vulkan;
6using System; 6using System;
7using System.Collections.Generic;
7using VkFormat = Silk.NET.Vulkan.Format; 8using VkFormat = Silk.NET.Vulkan.Format;
8 9
9namespace Ryujinx.Graphics.Vulkan 10namespace Ryujinx.Graphics.Vulkan
@@ -399,6 +400,86 @@ namespace Ryujinx.Graphics.Vulkan
399 newSize); 400 newSize);
400 } 401 }
401 402
403 public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
404 CommandBufferScoped cbs,
405 BufferHolder src,
406 BufferHolder dst,
407 IndexBufferPattern pattern,
408 int indexSize,
409 int srcOffset,
410 int indexCount)
411 {
412 int convertedCount = pattern.GetConvertedCount(indexCount);
413 int outputIndexSize = 4;
414
415 // TODO: Do this with a compute shader?
416 var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
417 var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
418
419 gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
420
421 var bufferCopy = new List<BufferCopy>();
422 int outputOffset = 0;
423
424 // Try to merge copies of adjacent indices to reduce copy count.
425 int sequenceStart = 0;
426 int sequenceLength = 0;
427
428 foreach (var index in pattern.GetIndexMapping(indexCount))
429 {
430 if (sequenceLength > 0)
431 {
432 if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
433 {
434 sequenceLength++;
435 continue;
436 }
437
438 // Commit the copy so far.
439 bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
440 outputOffset += outputIndexSize * sequenceLength;
441 }
442
443 sequenceStart = index;
444 sequenceLength = 1;
445 }
446
447 if (sequenceLength > 0)
448 {
449 // Commit final pending copy.
450 bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
451 }
452
453 var bufferCopyArray = bufferCopy.ToArray();
454
455 BufferHolder.InsertBufferBarrier(
456 gd,
457 cbs.CommandBuffer,
458 dstBuffer,
459 BufferHolder.DefaultAccessFlags,
460 AccessFlags.AccessTransferWriteBit,
461 PipelineStageFlags.PipelineStageAllCommandsBit,
462 PipelineStageFlags.PipelineStageTransferBit,
463 0,
464 convertedCount * outputIndexSize);
465
466 fixed (BufferCopy* pBufferCopy = bufferCopyArray)
467 {
468 gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
469 }
470
471 BufferHolder.InsertBufferBarrier(
472 gd,
473 cbs.CommandBuffer,
474 dstBuffer,
475 AccessFlags.AccessTransferWriteBit,
476 BufferHolder.DefaultAccessFlags,
477 PipelineStageFlags.PipelineStageTransferBit,
478 PipelineStageFlags.PipelineStageAllCommandsBit,
479 0,
480 convertedCount * outputIndexSize);
481 }
482
402 protected virtual void Dispose(bool disposing) 483 protected virtual void Dispose(bool disposing)
403 { 484 {
404 if (disposing) 485 if (disposing)
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
new file mode 100644
index 000000000..8439e79db
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
@@ -0,0 +1,139 @@
1using Ryujinx.Graphics.GAL;
2using System;
3using System.Collections.Generic;
4using System.Runtime.InteropServices;
5
6namespace Ryujinx.Graphics.Vulkan
7{
8 internal class IndexBufferPattern : IDisposable
9 {
10 public int PrimitiveVertices { get; }
11 public int PrimitiveVerticesOut { get; }
12 public int BaseIndex { get; }
13 public int[] OffsetIndex { get; }
14 public int IndexStride { get; }
15 public bool RepeatStart { get; }
16
17 private VulkanRenderer _gd;
18 private int _currentSize;
19 private BufferHandle _repeatingBuffer;
20
21 public IndexBufferPattern(VulkanRenderer gd,
22 int primitiveVertices,
23 int primitiveVerticesOut,
24 int baseIndex,
25 int[] offsetIndex,
26 int indexStride,
27 bool repeatStart)
28 {
29 PrimitiveVertices = primitiveVertices;
30 PrimitiveVerticesOut = primitiveVerticesOut;
31 BaseIndex = baseIndex;
32 OffsetIndex = offsetIndex;
33 IndexStride = indexStride;
34 RepeatStart = repeatStart;
35
36 _gd = gd;
37 }
38
39 public int GetPrimitiveCount(int vertexCount)
40 {
41 return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
42 }
43
44 public int GetConvertedCount(int indexCount)
45 {
46 int primitiveCount = GetPrimitiveCount(indexCount);
47 return primitiveCount * OffsetIndex.Length;
48 }
49
50 public IEnumerable<int> GetIndexMapping(int indexCount)
51 {
52 int primitiveCount = GetPrimitiveCount(indexCount);
53 int index = BaseIndex;
54
55 for (int i = 0; i < primitiveCount; i++)
56 {
57 if (RepeatStart)
58 {
59 // Used for triangle fan
60 yield return 0;
61 }
62
63 for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
64 {
65 yield return index + OffsetIndex[j];
66 }
67
68 index += IndexStride;
69 }
70 }
71
72 public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
73 {
74 int primitiveCount = GetPrimitiveCount(vertexCount);
75 indexCount = primitiveCount * PrimitiveVerticesOut;
76
77 int expectedSize = primitiveCount * OffsetIndex.Length;
78
79 if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
80 {
81 return _repeatingBuffer;
82 }
83
84 // Expand the repeating pattern to the number of requested primitives.
85 BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
86
87 // Copy the old data to the new one.
88 if (_repeatingBuffer != BufferHandle.Null)
89 {
90 _gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
91 _gd.DeleteBuffer(_repeatingBuffer);
92 }
93
94 _repeatingBuffer = newBuffer;
95
96 // Add the additional repeats on top.
97 int newPrimitives = primitiveCount;
98 int oldPrimitives = (_currentSize) / OffsetIndex.Length;
99
100 int[] newData;
101
102 newPrimitives -= oldPrimitives;
103 newData = new int[expectedSize - _currentSize];
104
105 int outOffset = 0;
106 int index = oldPrimitives * IndexStride + BaseIndex;
107
108 for (int i = 0; i < newPrimitives; i++)
109 {
110 if (RepeatStart)
111 {
112 // Used for triangle fan
113 newData[outOffset++] = 0;
114 }
115
116 for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
117 {
118 newData[outOffset++] = index + OffsetIndex[j];
119 }
120
121 index += IndexStride;
122 }
123
124 _gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
125 _currentSize = expectedSize;
126
127 return newBuffer;
128 }
129
130 public void Dispose()
131 {
132 if (_repeatingBuffer != BufferHandle.Null)
133 {
134 _gd.DeleteBuffer(_repeatingBuffer);
135 _repeatingBuffer = BufferHandle.Null;
136 }
137 }
138 }
139}
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
new file mode 100644
index 000000000..1a112d4d7
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
@@ -0,0 +1,97 @@
1using Silk.NET.Vulkan;
2using System;
3
4namespace Ryujinx.Graphics.Vulkan
5{
6 internal struct IndexBufferState
7 {
8 public static IndexBufferState Null => new IndexBufferState(GAL.BufferHandle.Null, 0, 0);
9
10 private readonly int _offset;
11 private readonly int _size;
12 private readonly IndexType _type;
13
14 private readonly GAL.BufferHandle _handle;
15 private Auto<DisposableBuffer> _buffer;
16
17 public IndexBufferState(GAL.BufferHandle handle, int offset, int size, IndexType type)
18 {
19 _handle = handle;
20 _offset = offset;
21 _size = size;
22 _type = type;
23 _buffer = null;
24 }
25
26 public IndexBufferState(GAL.BufferHandle handle, int offset, int size)
27 {
28 _handle = handle;
29 _offset = offset;
30 _size = size;
31 _type = IndexType.Uint16;
32 _buffer = null;
33 }
34
35 public void BindIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs)
36 {
37 Auto<DisposableBuffer> autoBuffer;
38 int offset, size;
39 IndexType type = _type;
40
41 if (_type == IndexType.Uint8Ext && !gd.Capabilities.SupportsIndexTypeUint8)
42 {
43 // Index type is not supported. Convert to I16.
44 autoBuffer = gd.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
45
46 type = IndexType.Uint16;
47 offset = 0;
48 size = _size * 2;
49 }
50 else
51 {
52 autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
53
54 offset = _offset;
55 size = _size;
56 }
57
58 _buffer = autoBuffer;
59
60 if (autoBuffer != null)
61 {
62 gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, offset, size).Value, (ulong)offset, type);
63 }
64 }
65
66 public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
67 {
68 Auto<DisposableBuffer> autoBuffer;
69
70 // Convert the index buffer using the given pattern.
71 int indexSize = _type switch
72 {
73 IndexType.Uint32 => 4,
74 IndexType.Uint16 => 2,
75 _ => 1,
76 };
77
78 int firstIndexOffset = firstIndex * indexSize;
79
80 autoBuffer = gd.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
81
82 int size = convertedCount * 4;
83
84 _buffer = autoBuffer;
85
86 if (autoBuffer != null)
87 {
88 gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, autoBuffer.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
89 }
90 }
91
92 public bool BoundEquals(Auto<DisposableBuffer> buffer)
93 {
94 return _buffer == buffer;
95 }
96 }
97}
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 5c666b09d..39acc5d9e 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -51,13 +51,16 @@ namespace Ryujinx.Graphics.Vulkan
51 51
52 private readonly DescriptorSetUpdater _descriptorSetUpdater; 52 private readonly DescriptorSetUpdater _descriptorSetUpdater;
53 53
54 private BufferState _indexBuffer; 54 private IndexBufferState _indexBuffer;
55 private IndexBufferPattern _indexBufferPattern;
55 private readonly BufferState[] _transformFeedbackBuffers; 56 private readonly BufferState[] _transformFeedbackBuffers;
56 private readonly VertexBufferState[] _vertexBuffers; 57 private readonly VertexBufferState[] _vertexBuffers;
57 private ulong _vertexBuffersDirty; 58 private ulong _vertexBuffersDirty;
58 protected Rectangle<int> ClearScissor; 59 protected Rectangle<int> ClearScissor;
59 60
60 public SupportBufferUpdater SupportBufferUpdater; 61 public SupportBufferUpdater SupportBufferUpdater;
62 public IndexBufferPattern QuadsToTrisPattern;
63 public IndexBufferPattern TriFanToTrisPattern;
61 64
62 private bool _needsIndexBufferRebind; 65 private bool _needsIndexBufferRebind;
63 private bool _needsTransformFeedbackBuffersRebind; 66 private bool _needsTransformFeedbackBuffersRebind;
@@ -107,6 +110,9 @@ namespace Ryujinx.Graphics.Vulkan
107 { 110 {
108 SupportBufferUpdater = new SupportBufferUpdater(Gd); 111 SupportBufferUpdater = new SupportBufferUpdater(Gd);
109 SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount); 112 SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
113
114 QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
115 TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
110 } 116 }
111 117
112 public unsafe void Barrier() 118 public unsafe void Barrier()
@@ -245,6 +251,14 @@ namespace Ryujinx.Graphics.Vulkan
245 } 251 }
246 } 252 }
247 253
254 public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer)
255 {
256 if (_indexBuffer.BoundEquals(buffer))
257 {
258 _needsIndexBufferRebind = true;
259 }
260 }
261
248 public void DispatchCompute(int groupsX, int groupsY, int groupsZ) 262 public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
249 { 263 {
250 if (!_program.IsLinked) 264 if (!_program.IsLinked)
@@ -267,24 +281,59 @@ namespace Ryujinx.Graphics.Vulkan
267 281
268 RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); 282 RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
269 BeginRenderPass(); 283 BeginRenderPass();
270 ResumeTransformFeedbackInternal();
271 DrawCount++; 284 DrawCount++;
272 285
273 if (_topology == GAL.PrimitiveTopology.Quads) 286 if (Gd.TopologyUnsupported(_topology))
274 { 287 {
275 int quadsCount = vertexCount / 4; 288 // Temporarily bind a conversion pattern as an index buffer.
289 _needsIndexBufferRebind = true;
276 290
277 for (int i = 0; i < quadsCount; i++) 291 IndexBufferPattern pattern = _topology switch
278 { 292 {
279 Gd.Api.CmdDraw(CommandBuffer, 4, (uint)instanceCount, (uint)(firstVertex + i * 4), (uint)firstInstance); 293 GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
280 } 294 GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
295 _ => throw new NotSupportedException($"Unsupported topology: {_topology}")
296 };
297
298 BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
299 var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false);
300
301 Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32);
302
303 BeginRenderPass(); // May have been interrupted to set buffer data.
304 ResumeTransformFeedbackInternal();
305
306 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
281 } 307 }
282 else 308 else
283 { 309 {
310 ResumeTransformFeedbackInternal();
311
284 Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance); 312 Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance);
285 } 313 }
286 } 314 }
287 315
316 private void UpdateIndexBufferPattern()
317 {
318 IndexBufferPattern pattern = null;
319
320 if (Gd.TopologyUnsupported(_topology))
321 {
322 pattern = _topology switch
323 {
324 GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
325 GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
326 _ => throw new NotSupportedException($"Unsupported topology: {_topology}")
327 };
328 }
329
330 if (_indexBufferPattern != pattern)
331 {
332 _indexBufferPattern = pattern;
333 _needsIndexBufferRebind = true;
334 }
335 }
336
288 public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) 337 public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
289 { 338 {
290 if (!_program.IsLinked) 339 if (!_program.IsLinked)
@@ -292,22 +341,34 @@ namespace Ryujinx.Graphics.Vulkan
292 return; 341 return;
293 } 342 }
294 343
344 UpdateIndexBufferPattern();
295 RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); 345 RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
296 BeginRenderPass(); 346 BeginRenderPass();
297 ResumeTransformFeedbackInternal();
298 DrawCount++; 347 DrawCount++;
299 348
300 if (_topology == GAL.PrimitiveTopology.Quads) 349 if (_indexBufferPattern != null)
301 { 350 {
302 int quadsCount = indexCount / 4; 351 // Convert the index buffer into a supported topology.
352 IndexBufferPattern pattern = _indexBufferPattern;
353
354 int convertedCount = pattern.GetConvertedCount(indexCount);
303 355
304 for (int i = 0; i < quadsCount; i++) 356 if (_needsIndexBufferRebind)
305 { 357 {
306 Gd.Api.CmdDrawIndexed(CommandBuffer, 4, (uint)instanceCount, (uint)(firstIndex + i * 4), firstVertex, (uint)firstInstance); 358 _indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern);
359
360 _needsIndexBufferRebind = false;
307 } 361 }
362
363 BeginRenderPass(); // May have been interrupted to set buffer data.
364 ResumeTransformFeedbackInternal();
365
366 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance);
308 } 367 }
309 else 368 else
310 { 369 {
370 ResumeTransformFeedbackInternal();
371
311 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance); 372 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance);
312 } 373 }
313 } 374 }
@@ -500,34 +561,16 @@ namespace Ryujinx.Graphics.Vulkan
500 561
501 public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type) 562 public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
502 { 563 {
503 _indexBuffer.Dispose();
504
505 if (buffer.Handle != BufferHandle.Null) 564 if (buffer.Handle != BufferHandle.Null)
506 { 565 {
507 Auto<DisposableBuffer> ib = null; 566 _indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert());
508 int offset = buffer.Offset;
509 int size = buffer.Size;
510
511 if (type == GAL.IndexType.UByte && !Gd.Capabilities.SupportsIndexTypeUint8)
512 {
513 ib = Gd.BufferManager.GetBufferI8ToI16(Cbs, buffer.Handle, offset, size);
514 offset = 0;
515 size *= 2;
516 type = GAL.IndexType.UShort;
517 }
518 else
519 {
520 ib = Gd.BufferManager.GetBuffer(CommandBuffer, buffer.Handle, false);
521 }
522
523 _indexBuffer = new BufferState(ib, offset, size, type.Convert());
524 } 567 }
525 else 568 else
526 { 569 {
527 _indexBuffer = BufferState.Null; 570 _indexBuffer = IndexBufferState.Null;
528 } 571 }
529 572
530 _indexBuffer.BindIndexBuffer(Gd.Api, Cbs); 573 _needsIndexBufferRebind = true;
531 } 574 }
532 575
533 public void SetLineParameters(float width, bool smooth) 576 public void SetLineParameters(float width, bool smooth)
@@ -584,7 +627,7 @@ namespace Ryujinx.Graphics.Vulkan
584 { 627 {
585 _topology = topology; 628 _topology = topology;
586 629
587 var vkTopology = topology.Convert(); 630 var vkTopology = Gd.TopologyRemap(topology).Convert();
588 631
589 _newState.Topology = vkTopology; 632 _newState.Topology = vkTopology;
590 633
@@ -1127,9 +1170,9 @@ namespace Ryujinx.Graphics.Vulkan
1127 // Commit changes to the support buffer before drawing. 1170 // Commit changes to the support buffer before drawing.
1128 SupportBufferUpdater.Commit(); 1171 SupportBufferUpdater.Commit();
1129 1172
1130 if (_needsIndexBufferRebind) 1173 if (_needsIndexBufferRebind && _indexBufferPattern == null)
1131 { 1174 {
1132 _indexBuffer.BindIndexBuffer(Gd.Api, Cbs); 1175 _indexBuffer.BindIndexBuffer(Gd, Cbs);
1133 _needsIndexBufferRebind = false; 1176 _needsIndexBufferRebind = false;
1134 } 1177 }
1135 1178
@@ -1265,7 +1308,6 @@ namespace Ryujinx.Graphics.Vulkan
1265 { 1308 {
1266 _renderPass?.Dispose(); 1309 _renderPass?.Dispose();
1267 _framebuffer?.Dispose(); 1310 _framebuffer?.Dispose();
1268 _indexBuffer.Dispose();
1269 _newState.Dispose(); 1311 _newState.Dispose();
1270 _descriptorSetUpdater.Dispose(); 1312 _descriptorSetUpdater.Dispose();
1271 1313
diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index c09303514..3ff111e87 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Vulkan
199 199
200 pipeline.StencilTestEnable = state.StencilTest.TestEnable; 200 pipeline.StencilTestEnable = state.StencilTest.TestEnable;
201 201
202 pipeline.Topology = state.Topology.Convert(); 202 pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
203 203
204 int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); 204 int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
205 int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); 205 int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 3776be9ee..d92fff49e 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -471,6 +471,25 @@ namespace Ryujinx.Graphics.Vulkan
471 Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); 471 Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
472 } 472 }
473 473
474 public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
475 {
476 return topology switch
477 {
478 GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
479 GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
480 _ => topology
481 };
482 }
483
484 public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
485 {
486 return topology switch
487 {
488 GAL.PrimitiveTopology.Quads => true,
489 _ => false
490 };
491 }
492
474 public void Initialize(GraphicsDebugLevel logLevel) 493 public void Initialize(GraphicsDebugLevel logLevel)
475 { 494 {
476 SetupContext(logLevel); 495 SetupContext(logLevel);