using System; using System.Diagnostics; using System.Threading; namespace Ryujinx.Graphics.Vulkan { interface IAuto { bool HasCommandBufferDependency(CommandBufferScoped cbs); void IncrementReferenceCount(); void DecrementReferenceCount(int cbIndex); void DecrementReferenceCount(); } interface IAutoPrivate : IAuto { void AddCommandBufferDependencies(CommandBufferScoped cbs); } class Auto : IAutoPrivate, IDisposable where T : IDisposable { private int _referenceCount; private T _value; private readonly BitMap _cbOwnership; private readonly MultiFenceHolder _waitable; private readonly IAutoPrivate[] _referencedObjs; private bool _disposed; private bool _destroyed; public Auto(T value) { _referenceCount = 1; _value = value; _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); } public Auto(T value, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value) { _waitable = waitable; _referencedObjs = referencedObjs; for (int i = 0; i < referencedObjs.Length; i++) { referencedObjs[i].IncrementReferenceCount(); } } public T Get(CommandBufferScoped cbs, int offset, int size) { _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size); return Get(cbs); } public T GetUnsafe() { return _value; } public T Get(CommandBufferScoped cbs) { if (!_destroyed) { AddCommandBufferDependencies(cbs); } return _value; } public bool HasCommandBufferDependency(CommandBufferScoped cbs) { return _cbOwnership.IsSet(cbs.CommandBufferIndex); } public bool HasRentedCommandBufferDependency(CommandBufferPool cbp) { return _cbOwnership.AnySet(); } public void AddCommandBufferDependencies(CommandBufferScoped cbs) { // We don't want to add a reference to this object to the command buffer // more than once, so if we detect that the command buffer already has ownership // of this object, then we can just return without doing anything else. if (_cbOwnership.Set(cbs.CommandBufferIndex)) { if (_waitable != null) { cbs.AddWaitable(_waitable); } cbs.AddDependant(this); // We need to add a dependency on the command buffer to all objects this object // references aswell. if (_referencedObjs != null) { for (int i = 0; i < _referencedObjs.Length; i++) { _referencedObjs[i].AddCommandBufferDependencies(cbs); } } } } public void IncrementReferenceCount() { if (Interlocked.Increment(ref _referenceCount) == 1) { Interlocked.Decrement(ref _referenceCount); throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed."); } } public void DecrementReferenceCount(int cbIndex) { _cbOwnership.Clear(cbIndex); DecrementReferenceCount(); } public void DecrementReferenceCount() { if (Interlocked.Decrement(ref _referenceCount) == 0) { _value.Dispose(); _value = default; _destroyed = true; // Value is no longer in use by the GPU, dispose all other // resources that it references. if (_referencedObjs != null) { for (int i = 0; i < _referencedObjs.Length; i++) { _referencedObjs[i].DecrementReferenceCount(); } } } Debug.Assert(_referenceCount >= 0); } public void Dispose() { if (!_disposed) { DecrementReferenceCount(); _disposed = true; } } } }