HostMemoryAllocator.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Collections;
  3. using Ryujinx.Common.Logging;
  4. using Silk.NET.Vulkan;
  5. using Silk.NET.Vulkan.Extensions.EXT;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Threading;
  9. namespace Ryujinx.Graphics.Vulkan
  10. {
  11. internal class HostMemoryAllocator
  12. {
  13. private readonly struct HostMemoryAllocation
  14. {
  15. public readonly Auto<MemoryAllocation> Allocation;
  16. public readonly nint Pointer;
  17. public readonly ulong Size;
  18. public ulong Start => (ulong)Pointer;
  19. public ulong End => (ulong)Pointer + Size;
  20. public HostMemoryAllocation(Auto<MemoryAllocation> allocation, nint pointer, ulong size)
  21. {
  22. Allocation = allocation;
  23. Pointer = pointer;
  24. Size = size;
  25. }
  26. }
  27. private readonly MemoryAllocator _allocator;
  28. private readonly Vk _api;
  29. private readonly ExtExternalMemoryHost _hostMemoryApi;
  30. private readonly Device _device;
  31. private readonly Lock _lock = new();
  32. private readonly List<HostMemoryAllocation> _allocations;
  33. private readonly IntervalTree<ulong, HostMemoryAllocation> _allocationTree;
  34. public HostMemoryAllocator(MemoryAllocator allocator, Vk api, ExtExternalMemoryHost hostMemoryApi, Device device)
  35. {
  36. _allocator = allocator;
  37. _api = api;
  38. _hostMemoryApi = hostMemoryApi;
  39. _device = device;
  40. _allocations = new List<HostMemoryAllocation>();
  41. _allocationTree = new IntervalTree<ulong, HostMemoryAllocation>();
  42. }
  43. public unsafe bool TryImport(
  44. MemoryRequirements requirements,
  45. MemoryPropertyFlags flags,
  46. nint pointer,
  47. ulong size)
  48. {
  49. lock (_lock)
  50. {
  51. // Does a compatible allocation exist in the tree?
  52. var allocations = new HostMemoryAllocation[10];
  53. ulong start = (ulong)pointer;
  54. ulong end = start + size;
  55. int count = _allocationTree.Get(start, end, ref allocations);
  56. // A compatible range is one that where the start and end completely cover the requested range.
  57. for (int i = 0; i < count; i++)
  58. {
  59. HostMemoryAllocation existing = allocations[i];
  60. if (start >= existing.Start && end <= existing.End)
  61. {
  62. try
  63. {
  64. existing.Allocation.IncrementReferenceCount();
  65. return true;
  66. }
  67. catch (InvalidOperationException)
  68. {
  69. // Can throw if the allocation has been disposed.
  70. // Just continue the search if this happens.
  71. }
  72. }
  73. }
  74. nint pageAlignedPointer = BitUtils.AlignDown(pointer, Environment.SystemPageSize);
  75. nint pageAlignedEnd = BitUtils.AlignUp((nint)((ulong)pointer + size), Environment.SystemPageSize);
  76. ulong pageAlignedSize = (ulong)(pageAlignedEnd - pageAlignedPointer);
  77. Result getResult = _hostMemoryApi.GetMemoryHostPointerProperties(_device, ExternalMemoryHandleTypeFlags.HostAllocationBitExt, (void*)pageAlignedPointer, out MemoryHostPointerPropertiesEXT properties);
  78. if (getResult < Result.Success)
  79. {
  80. return false;
  81. }
  82. int memoryTypeIndex = _allocator.FindSuitableMemoryTypeIndex(properties.MemoryTypeBits & requirements.MemoryTypeBits, flags);
  83. if (memoryTypeIndex < 0)
  84. {
  85. return false;
  86. }
  87. ImportMemoryHostPointerInfoEXT importInfo = new()
  88. {
  89. SType = StructureType.ImportMemoryHostPointerInfoExt,
  90. HandleType = ExternalMemoryHandleTypeFlags.HostAllocationBitExt,
  91. PHostPointer = (void*)pageAlignedPointer,
  92. };
  93. var memoryAllocateInfo = new MemoryAllocateInfo
  94. {
  95. SType = StructureType.MemoryAllocateInfo,
  96. AllocationSize = pageAlignedSize,
  97. MemoryTypeIndex = (uint)memoryTypeIndex,
  98. PNext = &importInfo,
  99. };
  100. Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
  101. if (result < Result.Success)
  102. {
  103. Logger.Debug?.PrintMsg(LogClass.Gpu, $"Host mapping import 0x{pageAlignedPointer:x16} 0x{pageAlignedSize:x8} failed.");
  104. return false;
  105. }
  106. var allocation = new MemoryAllocation(this, deviceMemory, pageAlignedPointer, 0, pageAlignedSize);
  107. var allocAuto = new Auto<MemoryAllocation>(allocation);
  108. var hostAlloc = new HostMemoryAllocation(allocAuto, pageAlignedPointer, pageAlignedSize);
  109. allocAuto.IncrementReferenceCount();
  110. allocAuto.Dispose(); // Kept alive by ref count only.
  111. // Register this mapping for future use.
  112. _allocationTree.Add(hostAlloc.Start, hostAlloc.End, hostAlloc);
  113. _allocations.Add(hostAlloc);
  114. }
  115. return true;
  116. }
  117. public (Auto<MemoryAllocation>, ulong) GetExistingAllocation(nint pointer, ulong size)
  118. {
  119. lock (_lock)
  120. {
  121. // Does a compatible allocation exist in the tree?
  122. var allocations = new HostMemoryAllocation[10];
  123. ulong start = (ulong)pointer;
  124. ulong end = start + size;
  125. int count = _allocationTree.Get(start, end, ref allocations);
  126. // A compatible range is one that where the start and end completely cover the requested range.
  127. for (int i = 0; i < count; i++)
  128. {
  129. HostMemoryAllocation existing = allocations[i];
  130. if (start >= existing.Start && end <= existing.End)
  131. {
  132. return (existing.Allocation, start - existing.Start);
  133. }
  134. }
  135. throw new InvalidOperationException($"No host allocation was prepared for requested range 0x{pointer:x16}:0x{size:x16}.");
  136. }
  137. }
  138. public void Free(DeviceMemory memory, ulong offset, ulong size)
  139. {
  140. lock (_lock)
  141. {
  142. _allocations.RemoveAll(allocation =>
  143. {
  144. if (allocation.Allocation.GetUnsafe().Memory.Handle == memory.Handle)
  145. {
  146. _allocationTree.Remove(allocation.Start, allocation);
  147. return true;
  148. }
  149. return false;
  150. });
  151. }
  152. _api.FreeMemory(_device, memory, ReadOnlySpan<AllocationCallbacks>.Empty);
  153. }
  154. }
  155. }