Auto.cs
1 using System; 2 using System.Diagnostics; 3 using System.Threading; 4 5 namespace Ryujinx.Graphics.Vulkan 6 { 7 interface IAuto 8 { 9 bool HasCommandBufferDependency(CommandBufferScoped cbs); 10 11 void IncrementReferenceCount(); 12 void DecrementReferenceCount(int cbIndex); 13 void DecrementReferenceCount(); 14 } 15 16 interface IAutoPrivate : IAuto 17 { 18 void AddCommandBufferDependencies(CommandBufferScoped cbs); 19 } 20 21 interface IMirrorable<T> where T : IDisposable 22 { 23 Auto<T> GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored); 24 void ClearMirrors(CommandBufferScoped cbs, int offset, int size); 25 } 26 27 class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable 28 { 29 private int _referenceCount; 30 private T _value; 31 32 private readonly BitMap _cbOwnership; 33 private readonly MultiFenceHolder _waitable; 34 private readonly IAutoPrivate[] _referencedObjs; 35 private readonly IMirrorable<T> _mirrorable; 36 37 private bool _disposed; 38 private bool _destroyed; 39 40 public Auto(T value) 41 { 42 _referenceCount = 1; 43 _value = value; 44 _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); 45 } 46 47 public Auto(T value, IMirrorable<T> mirrorable, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value, waitable, referencedObjs) 48 { 49 _mirrorable = mirrorable; 50 } 51 52 public Auto(T value, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value) 53 { 54 _waitable = waitable; 55 _referencedObjs = referencedObjs; 56 57 for (int i = 0; i < referencedObjs.Length; i++) 58 { 59 referencedObjs[i].IncrementReferenceCount(); 60 } 61 } 62 63 public T GetMirrorable(CommandBufferScoped cbs, ref int offset, int size, out bool mirrored) 64 { 65 var mirror = _mirrorable.GetMirrorable(cbs, ref offset, size, out mirrored); 66 mirror._waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, false); 67 return mirror.Get(cbs); 68 } 69 70 public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false) 71 { 72 _mirrorable?.ClearMirrors(cbs, offset, size); 73 _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write); 74 return Get(cbs); 75 } 76 77 public T GetUnsafe() 78 { 79 return _value; 80 } 81 82 public T Get(CommandBufferScoped cbs) 83 { 84 if (!_destroyed) 85 { 86 AddCommandBufferDependencies(cbs); 87 } 88 89 return _value; 90 } 91 92 public bool HasCommandBufferDependency(CommandBufferScoped cbs) 93 { 94 return _cbOwnership.IsSet(cbs.CommandBufferIndex); 95 } 96 97 public bool HasRentedCommandBufferDependency(CommandBufferPool cbp) 98 { 99 return _cbOwnership.AnySet(); 100 } 101 102 public void AddCommandBufferDependencies(CommandBufferScoped cbs) 103 { 104 // We don't want to add a reference to this object to the command buffer 105 // more than once, so if we detect that the command buffer already has ownership 106 // of this object, then we can just return without doing anything else. 107 if (_cbOwnership.Set(cbs.CommandBufferIndex)) 108 { 109 if (_waitable != null) 110 { 111 cbs.AddWaitable(_waitable); 112 } 113 114 cbs.AddDependant(this); 115 116 // We need to add a dependency on the command buffer to all objects this object 117 // references aswell. 118 if (_referencedObjs != null) 119 { 120 for (int i = 0; i < _referencedObjs.Length; i++) 121 { 122 _referencedObjs[i].AddCommandBufferDependencies(cbs); 123 } 124 } 125 } 126 } 127 128 public bool TryIncrementReferenceCount() 129 { 130 int lastValue; 131 do 132 { 133 lastValue = _referenceCount; 134 135 if (lastValue == 0) 136 { 137 return false; 138 } 139 } 140 while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); 141 142 return true; 143 } 144 145 public void IncrementReferenceCount() 146 { 147 if (Interlocked.Increment(ref _referenceCount) == 1) 148 { 149 Interlocked.Decrement(ref _referenceCount); 150 throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed."); 151 } 152 } 153 154 public void DecrementReferenceCount(int cbIndex) 155 { 156 _cbOwnership.Clear(cbIndex); 157 DecrementReferenceCount(); 158 } 159 160 public void DecrementReferenceCount() 161 { 162 if (Interlocked.Decrement(ref _referenceCount) == 0) 163 { 164 _value.Dispose(); 165 _value = default; 166 _destroyed = true; 167 168 // Value is no longer in use by the GPU, dispose all other 169 // resources that it references. 170 if (_referencedObjs != null) 171 { 172 for (int i = 0; i < _referencedObjs.Length; i++) 173 { 174 _referencedObjs[i].DecrementReferenceCount(); 175 } 176 } 177 } 178 179 Debug.Assert(_referenceCount >= 0); 180 } 181 182 public void Dispose() 183 { 184 if (!_disposed) 185 { 186 DecrementReferenceCount(); 187 _disposed = true; 188 } 189 } 190 } 191 }