DescriptorSetManager.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using Silk.NET.Vulkan;
  2. using System;
  3. using System.Diagnostics;
  4. namespace Ryujinx.Graphics.Vulkan
  5. {
  6. class DescriptorSetManager : IDisposable
  7. {
  8. public const uint MaxSets = 8;
  9. public class DescriptorPoolHolder : IDisposable
  10. {
  11. public Vk Api { get; }
  12. public Device Device { get; }
  13. private readonly DescriptorPool _pool;
  14. private int _freeDescriptors;
  15. private int _totalSets;
  16. private int _setsInUse;
  17. private bool _done;
  18. public unsafe DescriptorPoolHolder(Vk api, Device device, ReadOnlySpan<DescriptorPoolSize> poolSizes, bool updateAfterBind)
  19. {
  20. Api = api;
  21. Device = device;
  22. foreach (var poolSize in poolSizes)
  23. {
  24. _freeDescriptors += (int)poolSize.DescriptorCount;
  25. }
  26. fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
  27. {
  28. var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo
  29. {
  30. SType = StructureType.DescriptorPoolCreateInfo,
  31. Flags = updateAfterBind ? DescriptorPoolCreateFlags.UpdateAfterBindBit : DescriptorPoolCreateFlags.None,
  32. MaxSets = MaxSets,
  33. PoolSizeCount = (uint)poolSizes.Length,
  34. PPoolSizes = pPoolsSize,
  35. };
  36. Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
  37. }
  38. }
  39. public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors)
  40. {
  41. TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: false, out var dsc);
  42. return dsc;
  43. }
  44. public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors, out DescriptorSetCollection dsc)
  45. {
  46. return TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: true, out dsc);
  47. }
  48. private unsafe bool TryAllocateDescriptorSets(
  49. ReadOnlySpan<DescriptorSetLayout> layouts,
  50. int consumedDescriptors,
  51. bool isTry,
  52. out DescriptorSetCollection dsc)
  53. {
  54. Debug.Assert(!_done);
  55. DescriptorSet[] descriptorSets = new DescriptorSet[layouts.Length];
  56. fixed (DescriptorSet* pDescriptorSets = descriptorSets)
  57. {
  58. fixed (DescriptorSetLayout* pLayouts = layouts)
  59. {
  60. var descriptorSetAllocateInfo = new DescriptorSetAllocateInfo
  61. {
  62. SType = StructureType.DescriptorSetAllocateInfo,
  63. DescriptorPool = _pool,
  64. DescriptorSetCount = (uint)layouts.Length,
  65. PSetLayouts = pLayouts,
  66. };
  67. var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
  68. if (isTry && result == Result.ErrorOutOfPoolMemory)
  69. {
  70. _totalSets = (int)MaxSets;
  71. _done = true;
  72. DestroyIfDone();
  73. dsc = default;
  74. return false;
  75. }
  76. result.ThrowOnError();
  77. }
  78. }
  79. _freeDescriptors -= consumedDescriptors;
  80. _totalSets += layouts.Length;
  81. _setsInUse += layouts.Length;
  82. dsc = new DescriptorSetCollection(this, descriptorSets);
  83. return true;
  84. }
  85. public void FreeDescriptorSets(DescriptorSetCollection dsc)
  86. {
  87. _setsInUse -= dsc.SetsCount;
  88. Debug.Assert(_setsInUse >= 0);
  89. DestroyIfDone();
  90. }
  91. public bool CanFit(int setsCount, int descriptorsCount)
  92. {
  93. // Try to determine if an allocation with the given parameters will succeed.
  94. // An allocation may fail if the sets count or descriptors count exceeds the available counts
  95. // of the pool.
  96. // Not getting that right is not fatal, it will just create a new pool and try again,
  97. // but it is less efficient.
  98. if (_totalSets + setsCount <= MaxSets && _freeDescriptors >= descriptorsCount)
  99. {
  100. return true;
  101. }
  102. _done = true;
  103. DestroyIfDone();
  104. return false;
  105. }
  106. private unsafe void DestroyIfDone()
  107. {
  108. if (_done && _setsInUse == 0)
  109. {
  110. Api.DestroyDescriptorPool(Device, _pool, null);
  111. }
  112. }
  113. protected virtual void Dispose(bool disposing)
  114. {
  115. if (disposing)
  116. {
  117. unsafe
  118. {
  119. Api.DestroyDescriptorPool(Device, _pool, null);
  120. }
  121. }
  122. }
  123. public void Dispose()
  124. {
  125. GC.SuppressFinalize(this);
  126. Dispose(true);
  127. }
  128. }
  129. private readonly Device _device;
  130. private readonly DescriptorPoolHolder[] _currentPools;
  131. public DescriptorSetManager(Device device, int poolCount)
  132. {
  133. _device = device;
  134. _currentPools = new DescriptorPoolHolder[poolCount];
  135. }
  136. public Auto<DescriptorSetCollection> AllocateDescriptorSet(
  137. Vk api,
  138. DescriptorSetLayout layout,
  139. ReadOnlySpan<DescriptorPoolSize> poolSizes,
  140. int poolIndex,
  141. int consumedDescriptors,
  142. bool updateAfterBind)
  143. {
  144. Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
  145. layouts[0] = layout;
  146. return AllocateDescriptorSets(api, layouts, poolSizes, poolIndex, consumedDescriptors, updateAfterBind);
  147. }
  148. public Auto<DescriptorSetCollection> AllocateDescriptorSets(
  149. Vk api,
  150. ReadOnlySpan<DescriptorSetLayout> layouts,
  151. ReadOnlySpan<DescriptorPoolSize> poolSizes,
  152. int poolIndex,
  153. int consumedDescriptors,
  154. bool updateAfterBind)
  155. {
  156. // If we fail the first time, just create a new pool and try again.
  157. var pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
  158. if (!pool.TryAllocateDescriptorSets(layouts, consumedDescriptors, out var dsc))
  159. {
  160. pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
  161. dsc = pool.AllocateDescriptorSets(layouts, consumedDescriptors);
  162. }
  163. return new Auto<DescriptorSetCollection>(dsc);
  164. }
  165. private DescriptorPoolHolder GetPool(
  166. Vk api,
  167. ReadOnlySpan<DescriptorPoolSize> poolSizes,
  168. int poolIndex,
  169. int setsCount,
  170. int descriptorsCount,
  171. bool updateAfterBind)
  172. {
  173. ref DescriptorPoolHolder currentPool = ref _currentPools[poolIndex];
  174. if (currentPool == null || !currentPool.CanFit(setsCount, descriptorsCount))
  175. {
  176. currentPool = new DescriptorPoolHolder(api, _device, poolSizes, updateAfterBind);
  177. }
  178. return currentPool;
  179. }
  180. protected virtual void Dispose(bool disposing)
  181. {
  182. if (disposing)
  183. {
  184. for (int index = 0; index < _currentPools.Length; index++)
  185. {
  186. _currentPools[index]?.Dispose();
  187. _currentPools[index] = null;
  188. }
  189. }
  190. }
  191. public void Dispose()
  192. {
  193. GC.SuppressFinalize(this);
  194. Dispose(true);
  195. }
  196. }
  197. }