Ryujinx/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
riperiperi 9f1cf6458c
Vulkan: Migrate buffers between memory types to improve GPU performance (#4540)
* Initial implementation of migration between memory heaps

- Missing OOM handling
- Missing `_map` data safety when remapping
  - Copy may not have completed yet (needs some kind of fence)
  - Map may be unmapped before it is done being used. (needs scoped access)
- SSBO accesses are all "writes" - maybe pass info in another way.
- Missing keeping map type when resizing buffers (should this be done?)

* Ensure migrated data is in place before flushing.

* Fix issue where old waitable would be signalled.

- There is a real issue where existing Auto<> references need to be replaced.

* Swap bound Auto<> instances when swapping buffer backing

* Fix conversion buffers

* Don't try move buffers if the host has shared memory.

* Make GPU methods return PinnedSpan with scope

* Storage Hint

* Fix stupidity

* Fix rebase

* Tweak rules

Attempt to sidestep BOTW slowdown

* Remove line

* Migrate only when command buffers flush

* Change backing swap log to debug

* Address some feedback

* Disallow backing swap when the flush lock is held by the current thread

* Make PinnedSpan from ReadOnlySpan explicitly unsafe

* Fix some small issues

- Index buffer swap fixed
- Allocate DeviceLocal buffers using a separate block list to images.

* Remove alternative flags

* Address feedback
2023-03-19 17:56:48 -03:00

139 lines
4.3 KiB
C#

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);
}
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.BufferManager.CreateWithHandle(_gd, 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;
}
}
}
}