PipelineLayoutCacheEntry.cs 14 KB

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