/ src / Ryujinx.Graphics.Vulkan / FenceHolder.cs
FenceHolder.cs
  1  using Silk.NET.Vulkan;
  2  using System;
  3  using System.Threading;
  4  
  5  namespace Ryujinx.Graphics.Vulkan
  6  {
  7      class FenceHolder : IDisposable
  8      {
  9          private readonly Vk _api;
 10          private readonly Device _device;
 11          private Fence _fence;
 12          private int _referenceCount;
 13          private int _lock;
 14          private readonly bool _concurrentWaitUnsupported;
 15          private bool _disposed;
 16  
 17          public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
 18          {
 19              _api = api;
 20              _device = device;
 21              _concurrentWaitUnsupported = concurrentWaitUnsupported;
 22  
 23              var fenceCreateInfo = new FenceCreateInfo
 24              {
 25                  SType = StructureType.FenceCreateInfo,
 26              };
 27  
 28              api.CreateFence(device, in fenceCreateInfo, null, out _fence).ThrowOnError();
 29  
 30              _referenceCount = 1;
 31          }
 32  
 33          public Fence GetUnsafe()
 34          {
 35              return _fence;
 36          }
 37  
 38          public bool TryGet(out Fence fence)
 39          {
 40              int lastValue;
 41              do
 42              {
 43                  lastValue = _referenceCount;
 44  
 45                  if (lastValue == 0)
 46                  {
 47                      fence = default;
 48                      return false;
 49                  }
 50              }
 51              while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
 52  
 53              if (_concurrentWaitUnsupported)
 54              {
 55                  AcquireLock();
 56              }
 57  
 58              fence = _fence;
 59              return true;
 60          }
 61  
 62          public Fence Get()
 63          {
 64              Interlocked.Increment(ref _referenceCount);
 65              return _fence;
 66          }
 67  
 68          public void PutLock()
 69          {
 70              Put();
 71  
 72              if (_concurrentWaitUnsupported)
 73              {
 74                  ReleaseLock();
 75              }
 76          }
 77  
 78          public void Put()
 79          {
 80              if (Interlocked.Decrement(ref _referenceCount) == 0)
 81              {
 82                  _api.DestroyFence(_device, _fence, Span<AllocationCallbacks>.Empty);
 83                  _fence = default;
 84              }
 85          }
 86  
 87          private void AcquireLock()
 88          {
 89              while (!TryAcquireLock())
 90              {
 91                  Thread.SpinWait(32);
 92              }
 93          }
 94  
 95          private bool TryAcquireLock()
 96          {
 97              return Interlocked.Exchange(ref _lock, 1) == 0;
 98          }
 99  
100          private void ReleaseLock()
101          {
102              Interlocked.Exchange(ref _lock, 0);
103          }
104  
105          public void Wait()
106          {
107              if (_concurrentWaitUnsupported)
108              {
109                  AcquireLock();
110  
111                  try
112                  {
113                      FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
114                  }
115                  finally
116                  {
117                      ReleaseLock();
118                  }
119              }
120              else
121              {
122                  FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
123              }
124          }
125  
126          public bool IsSignaled()
127          {
128              if (_concurrentWaitUnsupported)
129              {
130                  if (!TryAcquireLock())
131                  {
132                      return false;
133                  }
134  
135                  try
136                  {
137                      return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
138                  }
139                  finally
140                  {
141                      ReleaseLock();
142                  }
143              }
144              else
145              {
146                  return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
147              }
148          }
149  
150          public void Dispose()
151          {
152              if (!_disposed)
153              {
154                  Put();
155                  _disposed = true;
156              }
157          }
158      }
159  }