MemoryAllocator.cs
1 using Silk.NET.Vulkan; 2 using System; 3 using System.Collections.Generic; 4 using System.Threading; 5 6 namespace Ryujinx.Graphics.Vulkan 7 { 8 class MemoryAllocator : IDisposable 9 { 10 private const ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; 11 12 private readonly Vk _api; 13 private readonly VulkanPhysicalDevice _physicalDevice; 14 private readonly Device _device; 15 private readonly List<MemoryAllocatorBlockList> _blockLists; 16 private readonly int _blockAlignment; 17 private readonly ReaderWriterLockSlim _lock; 18 19 public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device) 20 { 21 _api = api; 22 _physicalDevice = physicalDevice; 23 _device = device; 24 _blockLists = new List<MemoryAllocatorBlockList>(); 25 _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount); 26 _lock = new(LockRecursionPolicy.NoRecursion); 27 } 28 29 public MemoryAllocation AllocateDeviceMemory( 30 MemoryRequirements requirements, 31 MemoryPropertyFlags flags = 0, 32 bool isBuffer = false) 33 { 34 int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags); 35 if (memoryTypeIndex < 0) 36 { 37 return default; 38 } 39 40 bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit); 41 return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer); 42 } 43 44 private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer) 45 { 46 _lock.EnterReadLock(); 47 48 try 49 { 50 for (int i = 0; i < _blockLists.Count; i++) 51 { 52 var bl = _blockLists[i]; 53 if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer) 54 { 55 return bl.Allocate(size, alignment, map); 56 } 57 } 58 } 59 finally 60 { 61 _lock.ExitReadLock(); 62 } 63 64 _lock.EnterWriteLock(); 65 66 try 67 { 68 var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer); 69 _blockLists.Add(newBl); 70 71 return newBl.Allocate(size, alignment, map); 72 } 73 finally 74 { 75 _lock.ExitWriteLock(); 76 } 77 } 78 79 internal int FindSuitableMemoryTypeIndex( 80 uint memoryTypeBits, 81 MemoryPropertyFlags flags) 82 { 83 for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++) 84 { 85 var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i]; 86 87 if ((memoryTypeBits & (1 << i)) != 0) 88 { 89 if (type.PropertyFlags.HasFlag(flags)) 90 { 91 return i; 92 } 93 } 94 } 95 96 return -1; 97 } 98 99 public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice) 100 { 101 for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++) 102 { 103 if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) 104 { 105 return false; 106 } 107 } 108 109 return true; 110 } 111 112 public void Dispose() 113 { 114 for (int i = 0; i < _blockLists.Count; i++) 115 { 116 _blockLists[i].Dispose(); 117 } 118 } 119 } 120 }