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 }