MemoryManager.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using Ryujinx.Memory;
  2. using System;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. namespace Ryujinx.Graphics.Gpu.Memory
  6. {
  7. /// <summary>
  8. /// GPU memory manager.
  9. /// </summary>
  10. public class MemoryManager
  11. {
  12. private const int PtLvl0Bits = 14;
  13. private const int PtLvl1Bits = 14;
  14. public const int PtPageBits = 12;
  15. private const ulong PtLvl0Size = 1UL << PtLvl0Bits;
  16. private const ulong PtLvl1Size = 1UL << PtLvl1Bits;
  17. public const ulong PageSize = 1UL << PtPageBits;
  18. private const ulong PtLvl0Mask = PtLvl0Size - 1;
  19. private const ulong PtLvl1Mask = PtLvl1Size - 1;
  20. public const ulong PageMask = PageSize - 1;
  21. private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
  22. private const int PtLvl1Bit = PtPageBits;
  23. public const ulong PteUnmapped = 0xffffffff_ffffffff;
  24. private readonly ulong[][] _pageTable;
  25. public event EventHandler<UnmapEventArgs> MemoryUnmapped;
  26. private GpuContext _context;
  27. /// <summary>
  28. /// Creates a new instance of the GPU memory manager.
  29. /// </summary>
  30. public MemoryManager(GpuContext context)
  31. {
  32. _context = context;
  33. _pageTable = new ulong[PtLvl0Size][];
  34. }
  35. /// <summary>
  36. /// Reads data from GPU mapped memory.
  37. /// </summary>
  38. /// <typeparam name="T">Type of the data</typeparam>
  39. /// <param name="gpuVa">GPU virtual address where the data is located</param>
  40. /// <returns>The data at the specified memory location</returns>
  41. public T Read<T>(ulong gpuVa) where T : unmanaged
  42. {
  43. ulong processVa = Translate(gpuVa);
  44. return MemoryMarshal.Cast<byte, T>(_context.PhysicalMemory.GetSpan(processVa, Unsafe.SizeOf<T>()))[0];
  45. }
  46. /// <summary>
  47. /// Gets a read-only span of data from GPU mapped memory.
  48. /// </summary>
  49. /// <param name="gpuVa">GPU virtual address where the data is located</param>
  50. /// <param name="size">Size of the data</param>
  51. /// <returns>The span of the data at the specified memory location</returns>
  52. public ReadOnlySpan<byte> GetSpan(ulong gpuVa, int size)
  53. {
  54. ulong processVa = Translate(gpuVa);
  55. return _context.PhysicalMemory.GetSpan(processVa, size);
  56. }
  57. /// <summary>
  58. /// Gets a writable region from GPU mapped memory.
  59. /// </summary>
  60. /// <param name="address">Start address of the range</param>
  61. /// <param name="size">Size in bytes to be range</param>
  62. /// <returns>A writable region with the data at the specified memory location</returns>
  63. public WritableRegion GetWritableRegion(ulong gpuVa, int size)
  64. {
  65. ulong processVa = Translate(gpuVa);
  66. return _context.PhysicalMemory.GetWritableRegion(processVa, size);
  67. }
  68. /// <summary>
  69. /// Writes data to GPU mapped memory.
  70. /// </summary>
  71. /// <typeparam name="T">Type of the data</typeparam>
  72. /// <param name="gpuVa">GPU virtual address to write the value into</param>
  73. /// <param name="value">The value to be written</param>
  74. public void Write<T>(ulong gpuVa, T value) where T : unmanaged
  75. {
  76. ulong processVa = Translate(gpuVa);
  77. _context.PhysicalMemory.Write(processVa, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
  78. }
  79. /// <summary>
  80. /// Writes data to GPU mapped memory.
  81. /// </summary>
  82. /// <param name="gpuVa">GPU virtual address to write the data into</param>
  83. /// <param name="data">The data to be written</param>
  84. public void Write(ulong gpuVa, ReadOnlySpan<byte> data)
  85. {
  86. ulong processVa = Translate(gpuVa);
  87. _context.PhysicalMemory.Write(processVa, data);
  88. }
  89. /// <summary>
  90. /// Maps a given range of pages to the specified CPU virtual address.
  91. /// </summary>
  92. /// <remarks>
  93. /// All addresses and sizes must be page aligned.
  94. /// </remarks>
  95. /// <param name="pa">CPU virtual address to map into</param>
  96. /// <param name="va">GPU virtual address to be mapped</param>
  97. /// <param name="size">Size in bytes of the mapping</param>
  98. public void Map(ulong pa, ulong va, ulong size)
  99. {
  100. lock (_pageTable)
  101. {
  102. MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
  103. for (ulong offset = 0; offset < size; offset += PageSize)
  104. {
  105. SetPte(va + offset, pa + offset);
  106. }
  107. }
  108. }
  109. /// <summary>
  110. /// Unmaps a given range of pages at the specified GPU virtual memory region.
  111. /// </summary>
  112. /// <param name="va">GPU virtual address to unmap</param>
  113. /// <param name="size">Size in bytes of the region being unmapped</param>
  114. public void Unmap(ulong va, ulong size)
  115. {
  116. lock (_pageTable)
  117. {
  118. // Event handlers are not expected to be thread safe.
  119. MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
  120. for (ulong offset = 0; offset < size; offset += PageSize)
  121. {
  122. SetPte(va + offset, PteUnmapped);
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Checks if a given page is mapped.
  128. /// </summary>
  129. /// <param name="gpuVa">GPU virtual address of the page to check</param>
  130. /// <returns>True if the page is mapped, false otherwise</returns>
  131. public bool IsMapped(ulong gpuVa)
  132. {
  133. return Translate(gpuVa) != PteUnmapped;
  134. }
  135. /// <summary>
  136. /// Translates a GPU virtual address to a CPU virtual address.
  137. /// </summary>
  138. /// <param name="gpuVa">GPU virtual address to be translated</param>
  139. /// <returns>CPU virtual address</returns>
  140. public ulong Translate(ulong gpuVa)
  141. {
  142. ulong baseAddress = GetPte(gpuVa);
  143. if (baseAddress == PteUnmapped)
  144. {
  145. return PteUnmapped;
  146. }
  147. return baseAddress + (gpuVa & PageMask);
  148. }
  149. /// <summary>
  150. /// Gets the Page Table entry for a given GPU virtual address.
  151. /// </summary>
  152. /// <param name="gpuVa">GPU virtual address</param>
  153. /// <returns>Page table entry (CPU virtual address)</returns>
  154. private ulong GetPte(ulong gpuVa)
  155. {
  156. ulong l0 = (gpuVa >> PtLvl0Bit) & PtLvl0Mask;
  157. ulong l1 = (gpuVa >> PtLvl1Bit) & PtLvl1Mask;
  158. if (_pageTable[l0] == null)
  159. {
  160. return PteUnmapped;
  161. }
  162. return _pageTable[l0][l1];
  163. }
  164. /// <summary>
  165. /// Sets a Page Table entry at a given GPU virtual address.
  166. /// </summary>
  167. /// <param name="gpuVa">GPU virtual address</param>
  168. /// <param name="pte">Page table entry (CPU virtual address)</param>
  169. private void SetPte(ulong gpuVa, ulong pte)
  170. {
  171. ulong l0 = (gpuVa >> PtLvl0Bit) & PtLvl0Mask;
  172. ulong l1 = (gpuVa >> PtLvl1Bit) & PtLvl1Mask;
  173. if (_pageTable[l0] == null)
  174. {
  175. _pageTable[l0] = new ulong[PtLvl1Size];
  176. for (ulong index = 0; index < PtLvl1Size; index++)
  177. {
  178. _pageTable[l0][index] = PteUnmapped;
  179. }
  180. }
  181. _pageTable[l0][l1] = pte;
  182. }
  183. }
  184. }