/ src / Ryujinx.Graphics.Vulkan / MultiFenceHolder.cs
MultiFenceHolder.cs
  1  using Ryujinx.Common.Memory;
  2  using Silk.NET.Vulkan;
  3  using System;
  4  
  5  namespace Ryujinx.Graphics.Vulkan
  6  {
  7      /// <summary>
  8      /// Holder for multiple host GPU fences.
  9      /// </summary>
 10      class MultiFenceHolder
 11      {
 12          private const int BufferUsageTrackingGranularity = 4096;
 13  
 14          private readonly FenceHolder[] _fences;
 15          private readonly BufferUsageBitmap _bufferUsageBitmap;
 16  
 17          /// <summary>
 18          /// Creates a new instance of the multiple fence holder.
 19          /// </summary>
 20          public MultiFenceHolder()
 21          {
 22              _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
 23          }
 24  
 25          /// <summary>
 26          /// Creates a new instance of the multiple fence holder, with a given buffer size in mind.
 27          /// </summary>
 28          /// <param name="size">Size of the buffer</param>
 29          public MultiFenceHolder(int size)
 30          {
 31              _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
 32              _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
 33          }
 34  
 35          /// <summary>
 36          /// Adds read/write buffer usage information to the uses list.
 37          /// </summary>
 38          /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
 39          /// <param name="offset">Offset of the buffer being used</param>
 40          /// <param name="size">Size of the buffer region being used, in bytes</param>
 41          /// <param name="write">Whether the access is a write or not</param>
 42          public void AddBufferUse(int cbIndex, int offset, int size, bool write)
 43          {
 44              _bufferUsageBitmap.Add(cbIndex, offset, size, false);
 45  
 46              if (write)
 47              {
 48                  _bufferUsageBitmap.Add(cbIndex, offset, size, true);
 49              }
 50          }
 51  
 52          /// <summary>
 53          /// Removes all buffer usage information for a given command buffer.
 54          /// </summary>
 55          /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
 56          public void RemoveBufferUses(int cbIndex)
 57          {
 58              _bufferUsageBitmap?.Clear(cbIndex);
 59          }
 60  
 61          /// <summary>
 62          /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU.
 63          /// </summary>
 64          /// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
 65          /// <param name="offset">Offset of the buffer being used</param>
 66          /// <param name="size">Size of the buffer region being used, in bytes</param>
 67          /// <returns>True if in use, false otherwise</returns>
 68          public bool IsBufferRangeInUse(int cbIndex, int offset, int size)
 69          {
 70              return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size);
 71          }
 72  
 73          /// <summary>
 74          /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU.
 75          /// </summary>
 76          /// <param name="offset">Offset of the buffer being used</param>
 77          /// <param name="size">Size of the buffer region being used, in bytes</param>
 78          /// <param name="write">True if only write usages should count</param>
 79          /// <returns>True if in use, false otherwise</returns>
 80          public bool IsBufferRangeInUse(int offset, int size, bool write)
 81          {
 82              return _bufferUsageBitmap.OverlapsWith(offset, size, write);
 83          }
 84  
 85          /// <summary>
 86          /// Adds a fence to the holder.
 87          /// </summary>
 88          /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
 89          /// <param name="fence">Fence to be added</param>
 90          /// <returns>True if the command buffer's previous fence value was null</returns>
 91          public bool AddFence(int cbIndex, FenceHolder fence)
 92          {
 93              ref FenceHolder fenceRef = ref _fences[cbIndex];
 94  
 95              if (fenceRef == null)
 96              {
 97                  fenceRef = fence;
 98                  return true;
 99              }
100  
101              return false;
102          }
103  
104          /// <summary>
105          /// Removes a fence from the holder.
106          /// </summary>
107          /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
108          public void RemoveFence(int cbIndex)
109          {
110              _fences[cbIndex] = null;
111          }
112  
113          /// <summary>
114          /// Determines if a fence referenced on the given command buffer.
115          /// </summary>
116          /// <param name="cbIndex">Index of the command buffer to check if it's used</param>
117          /// <returns>True if referenced, false otherwise</returns>
118          public bool HasFence(int cbIndex)
119          {
120              return _fences[cbIndex] != null;
121          }
122  
123          /// <summary>
124          /// Wait until all the fences on the holder are signaled.
125          /// </summary>
126          /// <param name="api">Vulkan API instance</param>
127          /// <param name="device">GPU device that the fences belongs to</param>
128          public void WaitForFences(Vk api, Device device)
129          {
130              WaitForFencesImpl(api, device, 0, 0, false, 0UL);
131          }
132  
133          /// <summary>
134          /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
135          /// </summary>
136          /// <param name="api">Vulkan API instance</param>
137          /// <param name="device">GPU device that the fences belongs to</param>
138          /// <param name="offset">Start offset of the buffer range</param>
139          /// <param name="size">Size of the buffer range in bytes</param>
140          public void WaitForFences(Vk api, Device device, int offset, int size)
141          {
142              WaitForFencesImpl(api, device, offset, size, false, 0UL);
143          }
144  
145          /// <summary>
146          /// Wait until all the fences on the holder are signaled, or the timeout expires.
147          /// </summary>
148          /// <param name="api">Vulkan API instance</param>
149          /// <param name="device">GPU device that the fences belongs to</param>
150          /// <param name="timeout">Timeout in nanoseconds</param>
151          /// <returns>True if all fences were signaled, false otherwise</returns>
152          public bool WaitForFences(Vk api, Device device, ulong timeout)
153          {
154              return WaitForFencesImpl(api, device, 0, 0, true, timeout);
155          }
156  
157          /// <summary>
158          /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
159          /// </summary>
160          /// <param name="api">Vulkan API instance</param>
161          /// <param name="device">GPU device that the fences belongs to</param>
162          /// <param name="offset">Start offset of the buffer range</param>
163          /// <param name="size">Size of the buffer range in bytes</param>
164          /// <param name="hasTimeout">Indicates if <paramref name="timeout"/> should be used</param>
165          /// <param name="timeout">Timeout in nanoseconds</param>
166          /// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
167          private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
168          {
169              using SpanOwner<FenceHolder> fenceHoldersOwner = SpanOwner<FenceHolder>.Rent(CommandBufferPool.MaxCommandBuffers);
170              Span<FenceHolder> fenceHolders = fenceHoldersOwner.Span;
171  
172              int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
173              Span<Fence> fences = stackalloc Fence[count];
174  
175              int fenceCount = 0;
176  
177              for (int i = 0; i < fences.Length; i++)
178              {
179                  if (fenceHolders[i].TryGet(out Fence fence))
180                  {
181                      fences[fenceCount] = fence;
182  
183                      if (fenceCount < i)
184                      {
185                          fenceHolders[fenceCount] = fenceHolders[i];
186                      }
187  
188                      fenceCount++;
189                  }
190              }
191  
192              if (fenceCount == 0)
193              {
194                  return true;
195              }
196  
197              bool signaled = true;
198  
199              try
200              {
201                  if (hasTimeout)
202                  {
203                      signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
204                  }
205                  else
206                  {
207                      FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
208                  }
209              }
210              finally
211              {
212                  for (int i = 0; i < fenceCount; i++)
213                  {
214                      fenceHolders[i].PutLock();
215                  }
216              }
217  
218              return signaled;
219          }
220  
221          /// <summary>
222          /// Gets fences to wait for.
223          /// </summary>
224          /// <param name="storage">Span to store fences in</param>
225          /// <returns>Number of fences placed in storage</returns>
226          private int GetFences(Span<FenceHolder> storage)
227          {
228              int count = 0;
229  
230              for (int i = 0; i < _fences.Length; i++)
231              {
232                  var fence = _fences[i];
233  
234                  if (fence != null)
235                  {
236                      storage[count++] = fence;
237                  }
238              }
239  
240              return count;
241          }
242  
243          /// <summary>
244          /// Gets fences to wait for use of a given buffer region.
245          /// </summary>
246          /// <param name="storage">Span to store overlapping fences in</param>
247          /// <param name="offset">Offset of the range</param>
248          /// <param name="size">Size of the range in bytes</param>
249          /// <returns>Number of fences for the specified region placed in storage</returns>
250          private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
251          {
252              int count = 0;
253  
254              for (int i = 0; i < _fences.Length; i++)
255              {
256                  var fence = _fences[i];
257  
258                  if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
259                  {
260                      storage[count++] = fence;
261                  }
262              }
263  
264              return count;
265          }
266      }
267  }