HostMemoryAllocator.cs 7.1 KB

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