PipelineLayoutCacheEntry.cs
1 using Ryujinx.Graphics.GAL; 2 using Silk.NET.Vulkan; 3 using System; 4 using System.Collections.Generic; 5 using System.Collections.ObjectModel; 6 using System.Diagnostics; 7 using System.Runtime.InteropServices; 8 9 namespace Ryujinx.Graphics.Vulkan 10 { 11 class PipelineLayoutCacheEntry 12 { 13 private const int MaxPoolSizesPerSet = 8; 14 15 private readonly VulkanRenderer _gd; 16 private readonly Device _device; 17 18 public DescriptorSetLayout[] DescriptorSetLayouts { get; } 19 public bool[] DescriptorSetLayoutsUpdateAfterBind { get; } 20 public PipelineLayout PipelineLayout { get; } 21 22 private readonly int[] _consumedDescriptorsPerSet; 23 private readonly DescriptorPoolSize[][] _poolSizes; 24 25 private readonly DescriptorSetManager _descriptorSetManager; 26 27 private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache; 28 private List<Auto<DescriptorSetCollection>>[] _currentDsCache; 29 private readonly int[] _dsCacheCursor; 30 private int _dsLastCbIndex; 31 private int _dsLastSubmissionCount; 32 33 private struct ManualDescriptorSetEntry 34 { 35 public Auto<DescriptorSetCollection> DescriptorSet; 36 public uint CbRefMask; 37 public bool InUse; 38 39 public ManualDescriptorSetEntry(Auto<DescriptorSetCollection> descriptorSet, int cbIndex) 40 { 41 DescriptorSet = descriptorSet; 42 CbRefMask = 1u << cbIndex; 43 InUse = true; 44 } 45 } 46 47 private readonly struct PendingManualDsConsumption 48 { 49 public FenceHolder Fence { get; } 50 public int CommandBufferIndex { get; } 51 public int SetIndex { get; } 52 public int CacheIndex { get; } 53 54 public PendingManualDsConsumption(FenceHolder fence, int commandBufferIndex, int setIndex, int cacheIndex) 55 { 56 Fence = fence; 57 CommandBufferIndex = commandBufferIndex; 58 SetIndex = setIndex; 59 CacheIndex = cacheIndex; 60 fence.Get(); 61 } 62 } 63 64 private readonly List<ManualDescriptorSetEntry>[] _manualDsCache; 65 private readonly Queue<PendingManualDsConsumption> _pendingManualDsConsumptions; 66 private readonly Queue<int>[] _freeManualDsCacheEntries; 67 68 private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates; 69 private readonly ResourceDescriptorCollection _pdDescriptors; 70 private long _lastPdUsage; 71 private DescriptorSetTemplate _lastPdTemplate; 72 73 private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount) 74 { 75 _gd = gd; 76 _device = device; 77 78 _dsCache = new List<Auto<DescriptorSetCollection>>[CommandBufferPool.MaxCommandBuffers][]; 79 80 for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) 81 { 82 _dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount]; 83 84 for (int j = 0; j < _dsCache[i].Length; j++) 85 { 86 _dsCache[i][j] = new List<Auto<DescriptorSetCollection>>(); 87 } 88 } 89 90 _dsCacheCursor = new int[setsCount]; 91 _manualDsCache = new List<ManualDescriptorSetEntry>[setsCount]; 92 _pendingManualDsConsumptions = new Queue<PendingManualDsConsumption>(); 93 _freeManualDsCacheEntries = new Queue<int>[setsCount]; 94 } 95 96 public PipelineLayoutCacheEntry( 97 VulkanRenderer gd, 98 Device device, 99 ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, 100 bool usePushDescriptors) : this(gd, device, setDescriptors.Count) 101 { 102 ResourceLayouts layouts = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors); 103 104 DescriptorSetLayouts = layouts.DescriptorSetLayouts; 105 DescriptorSetLayoutsUpdateAfterBind = layouts.DescriptorSetLayoutsUpdateAfterBind; 106 PipelineLayout = layouts.PipelineLayout; 107 108 _consumedDescriptorsPerSet = new int[setDescriptors.Count]; 109 _poolSizes = new DescriptorPoolSize[setDescriptors.Count][]; 110 111 Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet]; 112 113 for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++) 114 { 115 int count = 0; 116 117 foreach (var descriptor in setDescriptors[setIndex].Descriptors) 118 { 119 count += descriptor.Count; 120 } 121 122 _consumedDescriptorsPerSet[setIndex] = count; 123 _poolSizes[setIndex] = GetDescriptorPoolSizes(poolSizes, setDescriptors[setIndex], DescriptorSetManager.MaxSets).ToArray(); 124 } 125 126 if (usePushDescriptors) 127 { 128 _pdDescriptors = setDescriptors[0]; 129 _pdTemplates = new(); 130 } 131 132 _descriptorSetManager = new DescriptorSetManager(_device, setDescriptors.Count); 133 } 134 135 public void UpdateCommandBufferIndex(int commandBufferIndex) 136 { 137 int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex); 138 139 if (_dsLastCbIndex != commandBufferIndex || _dsLastSubmissionCount != submissionCount) 140 { 141 _dsLastCbIndex = commandBufferIndex; 142 _dsLastSubmissionCount = submissionCount; 143 Array.Clear(_dsCacheCursor); 144 } 145 146 _currentDsCache = _dsCache[commandBufferIndex]; 147 } 148 149 public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew) 150 { 151 var list = _currentDsCache[setIndex]; 152 int index = _dsCacheCursor[setIndex]++; 153 if (index == list.Count) 154 { 155 var dsc = _descriptorSetManager.AllocateDescriptorSet( 156 _gd.Api, 157 DescriptorSetLayouts[setIndex], 158 _poolSizes[setIndex], 159 setIndex, 160 _consumedDescriptorsPerSet[setIndex], 161 DescriptorSetLayoutsUpdateAfterBind[setIndex]); 162 163 list.Add(dsc); 164 isNew = true; 165 return dsc; 166 } 167 168 isNew = false; 169 return list[index]; 170 } 171 172 public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex) 173 { 174 FreeCompletedManualDescriptorSets(); 175 176 var list = _manualDsCache[setIndex] ??= new(); 177 var span = CollectionsMarshal.AsSpan(list); 178 179 Queue<int> freeQueue = _freeManualDsCacheEntries[setIndex]; 180 181 // Do we have at least one freed descriptor set? If so, just use that. 182 if (freeQueue != null && freeQueue.TryDequeue(out int freeIndex)) 183 { 184 ref ManualDescriptorSetEntry entry = ref span[freeIndex]; 185 186 Debug.Assert(!entry.InUse && entry.CbRefMask == 0); 187 188 entry.InUse = true; 189 entry.CbRefMask = 1u << cbs.CommandBufferIndex; 190 cacheIndex = freeIndex; 191 192 _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, freeIndex)); 193 194 return entry.DescriptorSet; 195 } 196 197 // Otherwise create a new descriptor set, and add to our pending queue for command buffer consumption tracking. 198 var dsc = _descriptorSetManager.AllocateDescriptorSet( 199 _gd.Api, 200 DescriptorSetLayouts[setIndex], 201 _poolSizes[setIndex], 202 setIndex, 203 _consumedDescriptorsPerSet[setIndex], 204 DescriptorSetLayoutsUpdateAfterBind[setIndex]); 205 206 cacheIndex = list.Count; 207 list.Add(new ManualDescriptorSetEntry(dsc, cbs.CommandBufferIndex)); 208 _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex)); 209 210 return dsc; 211 } 212 213 public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex) 214 { 215 FreeCompletedManualDescriptorSets(); 216 217 var list = _manualDsCache[setIndex]; 218 var span = CollectionsMarshal.AsSpan(list); 219 ref var entry = ref span[cacheIndex]; 220 221 uint cbMask = 1u << cbs.CommandBufferIndex; 222 223 if ((entry.CbRefMask & cbMask) == 0) 224 { 225 entry.CbRefMask |= cbMask; 226 227 _pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex)); 228 } 229 } 230 231 private void FreeCompletedManualDescriptorSets() 232 { 233 FenceHolder signalledFence = null; 234 while (_pendingManualDsConsumptions.TryPeek(out var pds) && (pds.Fence == signalledFence || pds.Fence.IsSignaled())) 235 { 236 signalledFence = pds.Fence; // Already checked - don't need to do it again. 237 var dequeued = _pendingManualDsConsumptions.Dequeue(); 238 Debug.Assert(dequeued.Fence == pds.Fence); 239 pds.Fence.Put(); 240 241 var span = CollectionsMarshal.AsSpan(_manualDsCache[dequeued.SetIndex]); 242 ref var entry = ref span[dequeued.CacheIndex]; 243 entry.CbRefMask &= ~(1u << dequeued.CommandBufferIndex); 244 245 if (!entry.InUse && entry.CbRefMask == 0) 246 { 247 // If not in use by any array, and not bound to any command buffer, the descriptor set can be re-used immediately. 248 (_freeManualDsCacheEntries[dequeued.SetIndex] ??= new()).Enqueue(dequeued.CacheIndex); 249 } 250 } 251 } 252 253 public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex) 254 { 255 var list = _manualDsCache[setIndex]; 256 var span = CollectionsMarshal.AsSpan(list); 257 258 span[cacheIndex].InUse = false; 259 260 if (span[cacheIndex].CbRefMask == 0) 261 { 262 // This is no longer in use by any array, so if not bound to any command buffer, the descriptor set can be re-used immediately. 263 (_freeManualDsCacheEntries[setIndex] ??= new()).Enqueue(cacheIndex); 264 } 265 } 266 267 private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier) 268 { 269 int count = 0; 270 271 for (int index = 0; index < setDescriptor.Descriptors.Count; index++) 272 { 273 ResourceDescriptor descriptor = setDescriptor.Descriptors[index]; 274 DescriptorType descriptorType = descriptor.Type.Convert(); 275 276 bool found = false; 277 278 for (int poolSizeIndex = 0; poolSizeIndex < count; poolSizeIndex++) 279 { 280 if (output[poolSizeIndex].Type == descriptorType) 281 { 282 output[poolSizeIndex].DescriptorCount += (uint)descriptor.Count * multiplier; 283 found = true; 284 break; 285 } 286 } 287 288 if (!found) 289 { 290 output[count++] = new DescriptorPoolSize() 291 { 292 Type = descriptorType, 293 DescriptorCount = (uint)descriptor.Count, 294 }; 295 } 296 } 297 298 return output[..count]; 299 } 300 301 public DescriptorSetTemplate GetPushDescriptorTemplate(PipelineBindPoint pbp, long updateMask) 302 { 303 if (_lastPdUsage == updateMask && _lastPdTemplate != null) 304 { 305 // Most likely result is that it asks to update the same buffers. 306 return _lastPdTemplate; 307 } 308 309 if (!_pdTemplates.TryGetValue(updateMask, out DescriptorSetTemplate template)) 310 { 311 template = new DescriptorSetTemplate(_gd, _device, _pdDescriptors, updateMask, this, pbp, 0); 312 313 _pdTemplates.Add(updateMask, template); 314 } 315 316 _lastPdUsage = updateMask; 317 _lastPdTemplate = template; 318 319 return template; 320 } 321 322 protected virtual unsafe void Dispose(bool disposing) 323 { 324 if (disposing) 325 { 326 if (_pdTemplates != null) 327 { 328 foreach (DescriptorSetTemplate template in _pdTemplates.Values) 329 { 330 template.Dispose(); 331 } 332 } 333 334 for (int i = 0; i < _dsCache.Length; i++) 335 { 336 for (int j = 0; j < _dsCache[i].Length; j++) 337 { 338 for (int k = 0; k < _dsCache[i][j].Count; k++) 339 { 340 _dsCache[i][j][k].Dispose(); 341 } 342 343 _dsCache[i][j].Clear(); 344 } 345 } 346 347 for (int i = 0; i < _manualDsCache.Length; i++) 348 { 349 if (_manualDsCache[i] == null) 350 { 351 continue; 352 } 353 354 for (int j = 0; j < _manualDsCache[i].Count; j++) 355 { 356 _manualDsCache[i][j].DescriptorSet.Dispose(); 357 } 358 359 _manualDsCache[i].Clear(); 360 } 361 362 _gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); 363 364 for (int i = 0; i < DescriptorSetLayouts.Length; i++) 365 { 366 _gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null); 367 } 368 369 while (_pendingManualDsConsumptions.TryDequeue(out var pds)) 370 { 371 pds.Fence.Put(); 372 } 373 374 _descriptorSetManager.Dispose(); 375 } 376 } 377 378 public void Dispose() 379 { 380 Dispose(true); 381 } 382 } 383 }