Add spin lock to prevent waiting for fences on multiple threads at once on Adreno

This commit is contained in:
Gabriel A 2023-08-20 15:18:52 -03:00 committed by Emmanuel Hansen
parent 3e6ec4aea8
commit f1814ca542
5 changed files with 66 additions and 16 deletions

View File

@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
lock (queueLock) lock (queueLock)
{ {
_pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true); _pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, _gd.Vendor == Vendor.Qualcomm, isLight: true);
} }
} }

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device; private readonly Device _device;
private readonly Queue _queue; private readonly Queue _queue;
private readonly object _queueLock; private readonly object _queueLock;
private readonly bool _fenceNeedsLock;
private readonly CommandPool _pool; private readonly CommandPool _pool;
private readonly Thread _owner; private readonly Thread _owner;
@ -61,12 +62,13 @@ namespace Ryujinx.Graphics.Vulkan
private int _queuedCount; private int _queuedCount;
private int _inUseCount; 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 fenceNeedsLock, bool isLight = false)
{ {
_api = api; _api = api;
_device = device; _device = device;
_queue = queue; _queue = queue;
_queueLock = queueLock; _queueLock = queueLock;
_fenceNeedsLock = fenceNeedsLock;
_owner = Thread.CurrentThread; _owner = Thread.CurrentThread;
var commandPoolCreateInfo = new CommandPoolCreateInfo var commandPoolCreateInfo = new CommandPoolCreateInfo
@ -357,7 +359,7 @@ namespace Ryujinx.Graphics.Vulkan
if (refreshFence) if (refreshFence)
{ {
entry.Fence = new FenceHolder(_api, _device); entry.Fence = new FenceHolder(_api, _device, _fenceNeedsLock);
} }
else else
{ {

View File

@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device; private readonly Device _device;
private Fence _fence; private Fence _fence;
private int _referenceCount; private int _referenceCount;
private int _lock;
private readonly bool _needsLock;
private bool _disposed; private bool _disposed;
public unsafe FenceHolder(Vk api, Device device) public unsafe FenceHolder(Vk api, Device device, bool needsLock)
{ {
_api = api; _api = api;
_device = device; _device = device;
_needsLock = needsLock;
var fenceCreateInfo = new FenceCreateInfo var fenceCreateInfo = new FenceCreateInfo
{ {
@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
} }
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
if (_needsLock)
{
AcquireLock();
}
fence = _fence; fence = _fence;
return true; return true;
} }
@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
return _fence; return _fence;
} }
public void PutLock()
{
Put();
if (_needsLock)
{
ReleaseLock();
}
}
public void Put() public void Put()
{ {
if (Interlocked.Decrement(ref _referenceCount) == 0) if (Interlocked.Decrement(ref _referenceCount) == 0)
@ -66,24 +84,54 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private void AcquireLock()
{
while (!TryAcquireLock())
{
Thread.SpinWait(32);
}
}
private bool TryAcquireLock()
{
return Interlocked.Exchange(ref _lock, 1) == 0;
}
private void ReleaseLock()
{
Interlocked.Exchange(ref _lock, 0);
}
public void Wait() public void Wait()
{ {
Span<Fence> fences = stackalloc Fence[] if (_needsLock)
{ {
_fence, AcquireLock();
}; }
FenceHelper.WaitAllIndefinitely(_api, _device, fences); FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
if (_needsLock)
{
ReleaseLock();
}
} }
public bool IsSignaled() public bool IsSignaled()
{ {
Span<Fence> fences = stackalloc Fence[] if (_needsLock && !TryAcquireLock())
{ {
_fence, return false;
}; }
return FenceHelper.AllSignaled(_api, _device, fences); bool result = FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
if (_needsLock)
{
ReleaseLock();
}
return result;
} }
public void Dispose() public void Dispose()

View File

@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < fenceCount; i++) for (int i = 0; i < fenceCount; i++)
{ {
fenceHolders[i].Put(); fenceHolders[i].PutLock();
} }
return signaled; return signaled;

View File

@ -291,6 +291,8 @@ namespace Ryujinx.Graphics.Vulkan
ref var properties = ref properties2.Properties; ref var properties = ref properties2.Properties;
Vendor = VendorUtils.FromId(properties.VendorID);
ulong minResourceAlignment = Math.Max( ulong minResourceAlignment = Math.Max(
Math.Max( Math.Max(
properties.Limits.MinStorageBufferOffsetAlignment, properties.Limits.MinStorageBufferOffsetAlignment,
@ -347,7 +349,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi); Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device); HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, Vendor == Vendor.Qualcomm);
DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts); DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
@ -697,8 +699,6 @@ namespace Ryujinx.Graphics.Vulkan
string vendorName = VendorUtils.GetNameFromId(properties.VendorID); string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
Vendor = VendorUtils.FromId(properties.VendorID);
IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows(); IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
IsTBDR = IsTBDR =