diff options
-rw-r--r-- | Ryujinx.Graphics.Vulkan/BufferHolder.cs | 25 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/BufferManager.cs | 10 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/BufferState.cs | 24 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/CacheByRange.cs | 50 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/EnumConversion.cs | 5 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/HelperShader.cs | 81 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs | 139 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/IndexBufferState.cs | 97 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/PipelineBase.cs | 116 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/PipelineConverter.cs | 2 | ||||
-rw-r--r-- | Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 19 |
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 @@ | |||
1 | using Silk.NET.Vulkan; | 1 | using System; |
2 | using System; | ||
3 | 2 | ||
4 | namespace Ryujinx.Graphics.Vulkan | 3 | namespace 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 @@ | |||
2 | using Ryujinx.Graphics.GAL; | 2 | using Ryujinx.Graphics.GAL; |
3 | using Ryujinx.Graphics.Shader; | 3 | using Ryujinx.Graphics.Shader; |
4 | using Silk.NET.Vulkan; | 4 | using Silk.NET.Vulkan; |
5 | using System; | ||
5 | 6 | ||
6 | namespace Ryujinx.Graphics.Vulkan | 7 | namespace 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; | |||
4 | using Ryujinx.Graphics.Vulkan.Shaders; | 4 | using Ryujinx.Graphics.Vulkan.Shaders; |
5 | using Silk.NET.Vulkan; | 5 | using Silk.NET.Vulkan; |
6 | using System; | 6 | using System; |
7 | using System.Collections.Generic; | ||
7 | using VkFormat = Silk.NET.Vulkan.Format; | 8 | using VkFormat = Silk.NET.Vulkan.Format; |
8 | 9 | ||
9 | namespace Ryujinx.Graphics.Vulkan | 10 | namespace 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 @@ | |||
1 | using Ryujinx.Graphics.GAL; | ||
2 | using System; | ||
3 | using System.Collections.Generic; | ||
4 | using System.Runtime.InteropServices; | ||
5 | |||
6 | namespace 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 @@ | |||
1 | using Silk.NET.Vulkan; | ||
2 | using System; | ||
3 | |||
4 | namespace 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); |