Ryujinx/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs
gdkchan a830eb666b
Disallow concurrent fence waits on Adreno (#7001)
* Disallow concurrent fence waits on Adreno

* Ensure locks are released if exceptions are thrown
2024-07-07 19:33:28 -03:00

159 lines
3.8 KiB
C#

using Silk.NET.Vulkan;
using System;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan
{
class FenceHolder : IDisposable
{
private readonly Vk _api;
private readonly Device _device;
private Fence _fence;
private int _referenceCount;
private int _lock;
private readonly bool _concurrentWaitUnsupported;
private bool _disposed;
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
{
_api = api;
_device = device;
_concurrentWaitUnsupported = concurrentWaitUnsupported;
var fenceCreateInfo = new FenceCreateInfo
{
SType = StructureType.FenceCreateInfo,
};
api.CreateFence(device, in fenceCreateInfo, null, out _fence).ThrowOnError();
_referenceCount = 1;
}
public Fence GetUnsafe()
{
return _fence;
}
public bool TryGet(out Fence fence)
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
fence = default;
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
if (_concurrentWaitUnsupported)
{
AcquireLock();
}
fence = _fence;
return true;
}
public Fence Get()
{
Interlocked.Increment(ref _referenceCount);
return _fence;
}
public void PutLock()
{
Put();
if (_concurrentWaitUnsupported)
{
ReleaseLock();
}
}
public void Put()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
{
_api.DestroyFence(_device, _fence, Span<AllocationCallbacks>.Empty);
_fence = default;
}
}
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()
{
if (_concurrentWaitUnsupported)
{
AcquireLock();
try
{
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
}
finally
{
ReleaseLock();
}
}
else
{
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
}
}
public bool IsSignaled()
{
if (_concurrentWaitUnsupported)
{
if (!TryAcquireLock())
{
return false;
}
try
{
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
}
finally
{
ReleaseLock();
}
}
else
{
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
}
}
public void Dispose()
{
if (!_disposed)
{
Put();
_disposed = true;
}
}
}
}