MemoryAllocator.cs 3.8 KB

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