Use circular queue for checking consumption on command buffers

Speeds up games that spam command buffers a little. Avoids checking multiple command buffers if multiple are active at once.
This commit is contained in:
riperiperi 2021-12-26 23:05:51 +00:00
parent 64d11d3574
commit db5ac3488a

View file

@ -26,7 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public bool InUse; public bool InUse;
public bool InConsumption; public bool InConsumption;
public bool Active;
public CommandBuffer CommandBuffer; public CommandBuffer CommandBuffer;
public FenceHolder Fence; public FenceHolder Fence;
public SemaphoreHolder Semaphore; public SemaphoreHolder Semaphore;
@ -50,14 +49,15 @@ namespace Ryujinx.Graphics.Vulkan
Dependants = new List<IAuto>(); Dependants = new List<IAuto>();
Waitables = new HashSet<MultiFenceHolder>(); Waitables = new HashSet<MultiFenceHolder>();
Dependencies = new HashSet<SemaphoreHolder>(); Dependencies = new HashSet<SemaphoreHolder>();
Active = true; // Fence should be refreshed.
} }
} }
private readonly ReservedCommandBuffer[] _commandBuffers; private readonly ReservedCommandBuffer[] _commandBuffers;
private int _cursor; private readonly int[] _queuedIndexes;
private int _queuedIndexesPtr;
private int _queuedCount;
private int _inUseCount;
public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false) public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false)
{ {
@ -82,9 +82,14 @@ namespace Ryujinx.Graphics.Vulkan
_commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers]; _commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers];
_queuedIndexes = new int[_totalCommandBuffers];
_queuedIndexesPtr = 0;
_queuedCount = 0;
for (int i = 0; i < _totalCommandBuffers; i++) for (int i = 0; i < _totalCommandBuffers; i++)
{ {
_commandBuffers[i].Initialize(api, device, _pool); _commandBuffers[i].Initialize(api, device, _pool);
WaitAndDecrementRef(i);
} }
} }
@ -168,22 +173,33 @@ namespace Ryujinx.Graphics.Vulkan
return _commandBuffers[cbIndex].Fence; return _commandBuffers[cbIndex].Fence;
} }
private void CheckConsumption(int startAt) private int FreeConsumed(bool wait)
{ {
int wrapBefore = _totalCommandBuffers + 1; int freeEntry = 0;
int index = (startAt + wrapBefore) % _totalCommandBuffers;
for (int i = 0; i < _totalCommandBuffers - 1; i++) while (_queuedCount > 0)
{ {
int index = _queuedIndexes[_queuedIndexesPtr];
ref var entry = ref _commandBuffers[index]; ref var entry = ref _commandBuffers[index];
if (!entry.InUse && entry.InConsumption && entry.Fence.IsSignaled()) if (wait || !entry.InConsumption || entry.Fence.IsSignaled())
{ {
WaitAndDecrementRef(index); WaitAndDecrementRef(index);
}
index = (index + wrapBefore) % _totalCommandBuffers; wait = false;
freeEntry = index;
_queuedCount--;
_queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers;
}
else
{
break;
}
} }
return freeEntry;
} }
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs) public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
@ -196,19 +212,17 @@ namespace Ryujinx.Graphics.Vulkan
{ {
lock (_commandBuffers) lock (_commandBuffers)
{ {
CheckConsumption(_cursor); int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers);
for (int i = 0; i < _totalCommandBuffers; i++) for (int i = 0; i < _totalCommandBuffers; i++)
{ {
int currentIndex = _cursor; ref var entry = ref _commandBuffers[cursor];
ref var entry = ref _commandBuffers[currentIndex]; if (!entry.InUse && !entry.InConsumption)
if (!entry.InUse)
{ {
WaitAndDecrementRef(currentIndex);
entry.InUse = true; entry.InUse = true;
entry.Active = true;
_inUseCount++;
var commandBufferBeginInfo = new CommandBufferBeginInfo() var commandBufferBeginInfo = new CommandBufferBeginInfo()
{ {
@ -217,10 +231,10 @@ namespace Ryujinx.Graphics.Vulkan
_api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo); _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo);
return new CommandBufferScoped(this, entry.CommandBuffer, currentIndex); return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
} }
_cursor = (currentIndex + 1) & _totalCommandBuffersMask; cursor = (cursor + 1) & _totalCommandBuffersMask;
} }
return default; return default;
@ -244,15 +258,11 @@ namespace Ryujinx.Graphics.Vulkan
ref var entry = ref _commandBuffers[cbIndex]; ref var entry = ref _commandBuffers[cbIndex];
if (_cursor == cbIndex)
{
_cursor = (cbIndex + 1) & _totalCommandBuffersMask;
}
Debug.Assert(entry.InUse); Debug.Assert(entry.InUse);
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle); Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
entry.InUse = false; entry.InUse = false;
entry.InConsumption = true; entry.InConsumption = true;
_inUseCount--;
var commandBuffer = entry.CommandBuffer; var commandBuffer = entry.CommandBuffer;
@ -280,6 +290,10 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
} }
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
_queuedIndexes[ptr] = cbIndex;
_queuedCount++;
// _api.QueueWaitIdle(_queue); // _api.QueueWaitIdle(_queue);
} }
} }
@ -288,13 +302,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
ref var entry = ref _commandBuffers[cbIndex]; ref var entry = ref _commandBuffers[cbIndex];
if (!entry.Active)
{
return;
}
entry.Active = false;
if (entry.InConsumption) if (entry.InConsumption)
{ {
entry.Fence.Wait(); entry.Fence.Wait();