PrivateMemoryAllocator.cs 8.1 KB

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