Disallow concurrent fence waits on Adreno (#7001)
* Disallow concurrent fence waits on Adreno * Ensure locks are released if exceptions are thrown
This commit is contained in:
parent
cfc75d7e78
commit
a830eb666b
5 changed files with 108 additions and 22 deletions
|
@ -29,7 +29,14 @@ 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.IsConcurrentFenceWaitUnsupported,
|
||||||
|
isLight: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 _concurrentFenceWaitUnsupported;
|
||||||
private readonly CommandPool _pool;
|
private readonly CommandPool _pool;
|
||||||
private readonly Thread _owner;
|
private readonly Thread _owner;
|
||||||
|
|
||||||
|
@ -61,12 +62,20 @@ 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 concurrentFenceWaitUnsupported,
|
||||||
|
bool isLight = false)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
_queueLock = queueLock;
|
_queueLock = queueLock;
|
||||||
|
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
|
||||||
_owner = Thread.CurrentThread;
|
_owner = Thread.CurrentThread;
|
||||||
|
|
||||||
var commandPoolCreateInfo = new CommandPoolCreateInfo
|
var commandPoolCreateInfo = new CommandPoolCreateInfo
|
||||||
|
@ -357,7 +366,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
if (refreshFence)
|
if (refreshFence)
|
||||||
{
|
{
|
||||||
entry.Fence = new FenceHolder(_api, _device);
|
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 _concurrentWaitUnsupported;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public unsafe FenceHolder(Vk api, Device device)
|
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
|
_concurrentWaitUnsupported = concurrentWaitUnsupported;
|
||||||
|
|
||||||
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 (_concurrentWaitUnsupported)
|
||||||
|
{
|
||||||
|
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 (_concurrentWaitUnsupported)
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Put()
|
public void Put()
|
||||||
{
|
{
|
||||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||||
|
@ -66,24 +84,67 @@ 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 (_concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_fence,
|
AcquireLock();
|
||||||
};
|
|
||||||
|
|
||||||
FenceHelper.WaitAllIndefinitely(_api, _device, fences);
|
try
|
||||||
|
{
|
||||||
|
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsSignaled()
|
public bool IsSignaled()
|
||||||
{
|
{
|
||||||
Span<Fence> fences = stackalloc Fence[]
|
if (_concurrentWaitUnsupported)
|
||||||
{
|
{
|
||||||
_fence,
|
if (!TryAcquireLock())
|
||||||
};
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return FenceHelper.AllSignaled(_api, _device, fences);
|
try
|
||||||
|
{
|
||||||
|
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -196,6 +196,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
bool signaled = true;
|
bool signaled = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
if (hasTimeout)
|
if (hasTimeout)
|
||||||
{
|
{
|
||||||
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
||||||
|
@ -204,10 +206,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
for (int i = 0; i < fenceCount; i++)
|
for (int i = 0; i < fenceCount; i++)
|
||||||
{
|
{
|
||||||
fenceHolders[i].Put();
|
fenceHolders[i].PutLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return signaled;
|
return signaled;
|
||||||
|
|
|
@ -90,6 +90,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
internal bool IsMoltenVk { get; private set; }
|
internal bool IsMoltenVk { get; private set; }
|
||||||
internal bool IsTBDR { get; private set; }
|
internal bool IsTBDR { get; private set; }
|
||||||
internal bool IsSharedMemory { get; private set; }
|
internal bool IsSharedMemory { get; private set; }
|
||||||
|
internal bool IsConcurrentFenceWaitUnsupported { get; private set; }
|
||||||
|
|
||||||
public string GpuVendor { get; private set; }
|
public string GpuVendor { get; private set; }
|
||||||
public string GpuDriver { get; private set; }
|
public string GpuDriver { get; private set; }
|
||||||
public string GpuRenderer { get; private set; }
|
public string GpuRenderer { get; private set; }
|
||||||
|
@ -323,6 +325,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Vendor == Vendor.Broadcom ||
|
Vendor == Vendor.Broadcom ||
|
||||||
Vendor == Vendor.ImgTec;
|
Vendor == Vendor.ImgTec;
|
||||||
|
|
||||||
|
IsConcurrentFenceWaitUnsupported = Vendor == Vendor.Qualcomm;
|
||||||
|
|
||||||
GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
|
GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
|
||||||
GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
|
GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
|
||||||
VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
|
VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
|
||||||
|
@ -411,7 +415,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, IsConcurrentFenceWaitUnsupported);
|
||||||
|
|
||||||
PipelineLayoutCache = new PipelineLayoutCache();
|
PipelineLayoutCache = new PipelineLayoutCache();
|
||||||
|
|
||||||
|
|
Reference in a new issue