MemoryAllocatorBlockList.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using Ryujinx.Common;
  2. using Silk.NET.Vulkan;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. namespace Ryujinx.Graphics.Vulkan
  7. {
  8. class MemoryAllocatorBlockList : IDisposable
  9. {
  10. private const ulong InvalidOffset = ulong.MaxValue;
  11. public class Block : IComparable<Block>
  12. {
  13. public DeviceMemory Memory { get; private set; }
  14. public IntPtr HostPointer { get; private set; }
  15. public ulong Size { get; }
  16. public bool Mapped => HostPointer != IntPtr.Zero;
  17. private readonly struct Range : IComparable<Range>
  18. {
  19. public ulong Offset { get; }
  20. public ulong Size { get; }
  21. public Range(ulong offset, ulong size)
  22. {
  23. Offset = offset;
  24. Size = size;
  25. }
  26. public int CompareTo(Range other)
  27. {
  28. return Offset.CompareTo(other.Offset);
  29. }
  30. }
  31. private readonly List<Range> _freeRanges;
  32. public Block(DeviceMemory memory, IntPtr hostPointer, ulong size)
  33. {
  34. Memory = memory;
  35. HostPointer = hostPointer;
  36. Size = size;
  37. _freeRanges = new List<Range>
  38. {
  39. new Range(0, size)
  40. };
  41. }
  42. public ulong Allocate(ulong size, ulong alignment)
  43. {
  44. for (int i = 0; i < _freeRanges.Count; i++)
  45. {
  46. var range = _freeRanges[i];
  47. ulong alignedOffset = BitUtils.AlignUp<ulong>(range.Offset, alignment);
  48. ulong sizeDelta = alignedOffset - range.Offset;
  49. ulong usableSize = range.Size - sizeDelta;
  50. if (sizeDelta < range.Size && usableSize >= size)
  51. {
  52. _freeRanges.RemoveAt(i);
  53. if (sizeDelta != 0)
  54. {
  55. InsertFreeRange(range.Offset, sizeDelta);
  56. }
  57. ulong endOffset = range.Offset + range.Size;
  58. ulong remainingSize = endOffset - (alignedOffset + size);
  59. if (remainingSize != 0)
  60. {
  61. InsertFreeRange(endOffset - remainingSize, remainingSize);
  62. }
  63. return alignedOffset;
  64. }
  65. }
  66. return InvalidOffset;
  67. }
  68. public void Free(ulong offset, ulong size)
  69. {
  70. InsertFreeRangeComingled(offset, size);
  71. }
  72. private void InsertFreeRange(ulong offset, ulong size)
  73. {
  74. var range = new Range(offset, size);
  75. int index = _freeRanges.BinarySearch(range);
  76. if (index < 0)
  77. {
  78. index = ~index;
  79. }
  80. _freeRanges.Insert(index, range);
  81. }
  82. private void InsertFreeRangeComingled(ulong offset, ulong size)
  83. {
  84. ulong endOffset = offset + size;
  85. var range = new Range(offset, size);
  86. int index = _freeRanges.BinarySearch(range);
  87. if (index < 0)
  88. {
  89. index = ~index;
  90. }
  91. if (index < _freeRanges.Count && _freeRanges[index].Offset == endOffset)
  92. {
  93. endOffset = _freeRanges[index].Offset + _freeRanges[index].Size;
  94. _freeRanges.RemoveAt(index);
  95. }
  96. if (index > 0 && _freeRanges[index - 1].Offset + _freeRanges[index - 1].Size == offset)
  97. {
  98. offset = _freeRanges[index - 1].Offset;
  99. _freeRanges.RemoveAt(--index);
  100. }
  101. range = new Range(offset, endOffset - offset);
  102. _freeRanges.Insert(index, range);
  103. }
  104. public bool IsTotallyFree()
  105. {
  106. if (_freeRanges.Count == 1 && _freeRanges[0].Size == Size)
  107. {
  108. Debug.Assert(_freeRanges[0].Offset == 0);
  109. return true;
  110. }
  111. return false;
  112. }
  113. public int CompareTo(Block other)
  114. {
  115. return Size.CompareTo(other.Size);
  116. }
  117. public unsafe void Destroy(Vk api, Device device)
  118. {
  119. if (Mapped)
  120. {
  121. api.UnmapMemory(device, Memory);
  122. HostPointer = IntPtr.Zero;
  123. }
  124. if (Memory.Handle != 0)
  125. {
  126. api.FreeMemory(device, Memory, null);
  127. Memory = default;
  128. }
  129. }
  130. }
  131. private readonly List<Block> _blocks;
  132. private readonly Vk _api;
  133. private readonly Device _device;
  134. public int MemoryTypeIndex { get; }
  135. public bool ForBuffer { get; }
  136. private readonly int _blockAlignment;
  137. public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
  138. {
  139. _blocks = new List<Block>();
  140. _api = api;
  141. _device = device;
  142. MemoryTypeIndex = memoryTypeIndex;
  143. ForBuffer = forBuffer;
  144. _blockAlignment = blockAlignment;
  145. }
  146. public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
  147. {
  148. // Ensure we have a sane alignment value.
  149. if ((ulong)(int)alignment != alignment || (int)alignment <= 0)
  150. {
  151. throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
  152. }
  153. for (int i = 0; i < _blocks.Count; i++)
  154. {
  155. var block = _blocks[i];
  156. if (block.Mapped == map && block.Size >= size)
  157. {
  158. ulong offset = block.Allocate(size, alignment);
  159. if (offset != InvalidOffset)
  160. {
  161. return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
  162. }
  163. }
  164. }
  165. ulong blockAlignedSize = BitUtils.AlignUp<ulong>(size, (ulong)_blockAlignment);
  166. var memoryAllocateInfo = new MemoryAllocateInfo()
  167. {
  168. SType = StructureType.MemoryAllocateInfo,
  169. AllocationSize = blockAlignedSize,
  170. MemoryTypeIndex = (uint)MemoryTypeIndex
  171. };
  172. _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
  173. IntPtr hostPointer = IntPtr.Zero;
  174. if (map)
  175. {
  176. unsafe
  177. {
  178. void* pointer = null;
  179. _api.MapMemory(_device, deviceMemory, 0, blockAlignedSize, 0, ref pointer).ThrowOnError();
  180. hostPointer = (IntPtr)pointer;
  181. }
  182. }
  183. var newBlock = new Block(deviceMemory, hostPointer, blockAlignedSize);
  184. InsertBlock(newBlock);
  185. ulong newBlockOffset = newBlock.Allocate(size, alignment);
  186. Debug.Assert(newBlockOffset != InvalidOffset);
  187. return new MemoryAllocation(this, newBlock, deviceMemory, GetHostPointer(newBlock, newBlockOffset), newBlockOffset, size);
  188. }
  189. private static IntPtr GetHostPointer(Block block, ulong offset)
  190. {
  191. if (block.HostPointer == IntPtr.Zero)
  192. {
  193. return IntPtr.Zero;
  194. }
  195. return (IntPtr)((nuint)(nint)block.HostPointer + offset);
  196. }
  197. public unsafe void Free(Block block, ulong offset, ulong size)
  198. {
  199. block.Free(offset, size);
  200. if (block.IsTotallyFree())
  201. {
  202. for (int i = 0; i < _blocks.Count; i++)
  203. {
  204. if (_blocks[i] == block)
  205. {
  206. _blocks.RemoveAt(i);
  207. break;
  208. }
  209. }
  210. block.Destroy(_api, _device);
  211. }
  212. }
  213. private void InsertBlock(Block block)
  214. {
  215. int index = _blocks.BinarySearch(block);
  216. if (index < 0)
  217. {
  218. index = ~index;
  219. }
  220. _blocks.Insert(index, block);
  221. }
  222. public unsafe void Dispose()
  223. {
  224. for (int i = 0; i < _blocks.Count; i++)
  225. {
  226. _blocks[i].Destroy(_api, _device);
  227. }
  228. }
  229. }
  230. }