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  }