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