Buffer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using Ryujinx.Cpu.Tracking;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Memory.Range;
  4. using Ryujinx.Memory.Tracking;
  5. using System;
  6. namespace Ryujinx.Graphics.Gpu.Memory
  7. {
  8. /// <summary>
  9. /// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
  10. /// </summary>
  11. class Buffer : IRange, IDisposable
  12. {
  13. private static ulong GranularBufferThreshold = 4096;
  14. private readonly GpuContext _context;
  15. /// <summary>
  16. /// Host buffer handle.
  17. /// </summary>
  18. public BufferHandle Handle { get; }
  19. /// <summary>
  20. /// Start address of the buffer in guest memory.
  21. /// </summary>
  22. public ulong Address { get; }
  23. /// <summary>
  24. /// Size of the buffer in bytes.
  25. /// </summary>
  26. public ulong Size { get; }
  27. /// <summary>
  28. /// End address of the buffer in guest memory.
  29. /// </summary>
  30. public ulong EndAddress => Address + Size;
  31. /// <summary>
  32. /// Ranges of the buffer that have been modified on the GPU.
  33. /// Ranges defined here cannot be updated from CPU until a CPU waiting sync point is reached.
  34. /// Then, write tracking will signal, wait for GPU sync (generated at the syncpoint) and flush these regions.
  35. /// </summary>
  36. /// <remarks>
  37. /// This is null until at least one modification occurs.
  38. /// </remarks>
  39. private BufferModifiedRangeList _modifiedRanges = null;
  40. private CpuMultiRegionHandle _memoryTrackingGranular;
  41. private CpuRegionHandle _memoryTracking;
  42. private readonly RegionSignal _externalFlushDelegate;
  43. private readonly Action<ulong, ulong> _loadDelegate;
  44. private readonly Action<ulong, ulong> _modifiedDelegate;
  45. private int _sequenceNumber;
  46. private bool _useGranular;
  47. private bool _syncActionRegistered;
  48. /// <summary>
  49. /// Creates a new instance of the buffer.
  50. /// </summary>
  51. /// <param name="context">GPU context that the buffer belongs to</param>
  52. /// <param name="address">Start address of the buffer</param>
  53. /// <param name="size">Size of the buffer in bytes</param>
  54. public Buffer(GpuContext context, ulong address, ulong size)
  55. {
  56. _context = context;
  57. Address = address;
  58. Size = size;
  59. Handle = context.Renderer.CreateBuffer((int)size);
  60. _useGranular = size > GranularBufferThreshold;
  61. if (_useGranular)
  62. {
  63. _memoryTrackingGranular = context.PhysicalMemory.BeginGranularTracking(address, size);
  64. }
  65. else
  66. {
  67. _memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
  68. }
  69. _externalFlushDelegate = new RegionSignal(ExternalFlush);
  70. _loadDelegate = new Action<ulong, ulong>(LoadRegion);
  71. _modifiedDelegate = new Action<ulong, ulong>(RegionModified);
  72. }
  73. /// <summary>
  74. /// Gets a sub-range from the buffer, from a start address till the end of the buffer.
  75. /// </summary>
  76. /// <remarks>
  77. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  78. /// </remarks>
  79. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  80. /// <returns>The buffer sub-range</returns>
  81. public BufferRange GetRange(ulong address)
  82. {
  83. ulong offset = address - Address;
  84. return new BufferRange(Handle, (int)offset, (int)(Size - offset));
  85. }
  86. /// <summary>
  87. /// Gets a sub-range from the buffer.
  88. /// </summary>
  89. /// <remarks>
  90. /// This can be used to bind and use sub-ranges of the buffer on the host API.
  91. /// </remarks>
  92. /// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
  93. /// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
  94. /// <returns>The buffer sub-range</returns>
  95. public BufferRange GetRange(ulong address, ulong size)
  96. {
  97. int offset = (int)(address - Address);
  98. return new BufferRange(Handle, offset, (int)size);
  99. }
  100. /// <summary>
  101. /// Checks if a given range overlaps with the buffer.
  102. /// </summary>
  103. /// <param name="address">Start address of the range</param>
  104. /// <param name="size">Size in bytes of the range</param>
  105. /// <returns>True if the range overlaps, false otherwise</returns>
  106. public bool OverlapsWith(ulong address, ulong size)
  107. {
  108. return Address < address + size && address < EndAddress;
  109. }
  110. /// <summary>
  111. /// Performs guest to host memory synchronization of the buffer data.
  112. /// </summary>
  113. /// <remarks>
  114. /// This causes the buffer data to be overwritten if a write was detected from the CPU,
  115. /// since the last call to this method.
  116. /// </remarks>
  117. /// <param name="address">Start address of the range to synchronize</param>
  118. /// <param name="size">Size in bytes of the range to synchronize</param>
  119. public void SynchronizeMemory(ulong address, ulong size)
  120. {
  121. if (_useGranular)
  122. {
  123. _memoryTrackingGranular.QueryModified(address, size, _modifiedDelegate, _context.SequenceNumber);
  124. }
  125. else
  126. {
  127. if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
  128. {
  129. _memoryTracking.Reprotect();
  130. if (_modifiedRanges != null)
  131. {
  132. _modifiedRanges.ExcludeModifiedRegions(Address, Size, _loadDelegate);
  133. }
  134. else
  135. {
  136. _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
  137. }
  138. _sequenceNumber = _context.SequenceNumber;
  139. }
  140. }
  141. }
  142. /// <summary>
  143. /// Ensure that the modified range list exists.
  144. /// </summary>
  145. private void EnsureRangeList()
  146. {
  147. if (_modifiedRanges == null)
  148. {
  149. _modifiedRanges = new BufferModifiedRangeList(_context);
  150. }
  151. }
  152. /// <summary>
  153. /// Signal that the given region of the buffer has been modified.
  154. /// </summary>
  155. /// <param name="address">The start address of the modified region</param>
  156. /// <param name="size">The size of the modified region</param>
  157. public void SignalModified(ulong address, ulong size)
  158. {
  159. EnsureRangeList();
  160. _modifiedRanges.SignalModified(address, size);
  161. if (!_syncActionRegistered)
  162. {
  163. _context.RegisterSyncAction(SyncAction);
  164. _syncActionRegistered = true;
  165. }
  166. }
  167. /// <summary>
  168. /// Indicate that mofifications in a given region of this buffer have been overwritten.
  169. /// </summary>
  170. /// <param name="address">The start address of the region</param>
  171. /// <param name="size">The size of the region</param>
  172. public void ClearModified(ulong address, ulong size)
  173. {
  174. if (_modifiedRanges != null)
  175. {
  176. _modifiedRanges.Clear(address, size);
  177. }
  178. }
  179. /// <summary>
  180. /// Action to be performed when a syncpoint is reached after modification.
  181. /// This will register read/write tracking to flush the buffer from GPU when its memory is used.
  182. /// </summary>
  183. private void SyncAction()
  184. {
  185. _syncActionRegistered = false;
  186. if (_useGranular)
  187. {
  188. _modifiedRanges.GetRanges(Address, Size, (address, size) =>
  189. {
  190. _memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
  191. SynchronizeMemory(address, size);
  192. });
  193. }
  194. else
  195. {
  196. _memoryTracking.RegisterAction(_externalFlushDelegate);
  197. SynchronizeMemory(Address, Size);
  198. }
  199. }
  200. /// <summary>
  201. /// Inherit modified ranges from another buffer.
  202. /// </summary>
  203. /// <param name="from">The buffer to inherit from</param>
  204. public void InheritModifiedRanges(Buffer from)
  205. {
  206. if (from._modifiedRanges != null)
  207. {
  208. if (from._syncActionRegistered && !_syncActionRegistered)
  209. {
  210. _context.RegisterSyncAction(SyncAction);
  211. _syncActionRegistered = true;
  212. }
  213. EnsureRangeList();
  214. _modifiedRanges.InheritRanges(from._modifiedRanges, (ulong address, ulong size) =>
  215. {
  216. if (_useGranular)
  217. {
  218. _memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
  219. }
  220. else
  221. {
  222. _memoryTracking.RegisterAction(_externalFlushDelegate);
  223. }
  224. });
  225. }
  226. }
  227. /// <summary>
  228. /// Determine if a given region of the buffer has been modified, and must be flushed.
  229. /// </summary>
  230. /// <param name="address">The start address of the region</param>
  231. /// <param name="size">The size of the region</param>
  232. /// <returns></returns>
  233. public bool IsModified(ulong address, ulong size)
  234. {
  235. if (_modifiedRanges != null)
  236. {
  237. return _modifiedRanges.HasRange(address, size);
  238. }
  239. return false;
  240. }
  241. /// <summary>
  242. /// Indicate that a region of the buffer was modified, and must be loaded from memory.
  243. /// </summary>
  244. /// <param name="mAddress">Start address of the modified region</param>
  245. /// <param name="mSize">Size of the modified region</param>
  246. private void RegionModified(ulong mAddress, ulong mSize)
  247. {
  248. if (mAddress < Address)
  249. {
  250. mAddress = Address;
  251. }
  252. ulong maxSize = Address + Size - mAddress;
  253. if (mSize > maxSize)
  254. {
  255. mSize = maxSize;
  256. }
  257. if (_modifiedRanges != null)
  258. {
  259. _modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate);
  260. }
  261. else
  262. {
  263. LoadRegion(mAddress, mSize);
  264. }
  265. }
  266. /// <summary>
  267. /// Load a region of the buffer from memory.
  268. /// </summary>
  269. /// <param name="mAddress">Start address of the modified region</param>
  270. /// <param name="mSize">Size of the modified region</param>
  271. private void LoadRegion(ulong mAddress, ulong mSize)
  272. {
  273. int offset = (int)(mAddress - Address);
  274. _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
  275. }
  276. /// <summary>
  277. /// Performs copy of all the buffer data from one buffer to another.
  278. /// </summary>
  279. /// <param name="destination">The destination buffer to copy the data into</param>
  280. /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
  281. public void CopyTo(Buffer destination, int dstOffset)
  282. {
  283. _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
  284. }
  285. /// <summary>
  286. /// Flushes a range of the buffer.
  287. /// This writes the range data back into guest memory.
  288. /// </summary>
  289. /// <param name="address">Start address of the range</param>
  290. /// <param name="size">Size in bytes of the range</param>
  291. public void Flush(ulong address, ulong size)
  292. {
  293. int offset = (int)(address - Address);
  294. byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
  295. // TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
  296. _context.PhysicalMemory.WriteUntracked(address, data);
  297. }
  298. /// <summary>
  299. /// Align a given address and size region to page boundaries.
  300. /// </summary>
  301. /// <param name="address">The start address of the region</param>
  302. /// <param name="size">The size of the region</param>
  303. /// <returns>The page aligned address and size</returns>
  304. private static (ulong address, ulong size) PageAlign(ulong address, ulong size)
  305. {
  306. ulong pageMask = MemoryManager.PageMask;
  307. ulong rA = address & ~pageMask;
  308. ulong rS = ((address + size + pageMask) & ~pageMask) - rA;
  309. return (rA, rS);
  310. }
  311. /// <summary>
  312. /// Flush modified ranges of the buffer from another thread.
  313. /// This will flush all modifications made before the active SyncNumber was set, and may block to wait for GPU sync.
  314. /// </summary>
  315. /// <param name="address">Address of the memory action</param>
  316. /// <param name="size">Size in bytes</param>
  317. public void ExternalFlush(ulong address, ulong size)
  318. {
  319. _context.Renderer.BackgroundContextAction(() =>
  320. {
  321. var ranges = _modifiedRanges;
  322. if (ranges != null)
  323. {
  324. (address, size) = PageAlign(address, size);
  325. ranges.WaitForAndGetRanges(address, size, Flush);
  326. }
  327. });
  328. }
  329. /// <summary>
  330. /// Called when part of the memory for this buffer has been unmapped.
  331. /// Calls are from non-GPU threads.
  332. /// </summary>
  333. /// <param name="address">Start address of the unmapped region</param>
  334. /// <param name="size">Size of the unmapped region</param>
  335. public void Unmapped(ulong address, ulong size)
  336. {
  337. _modifiedRanges?.Clear(address, size);
  338. }
  339. /// <summary>
  340. /// Disposes the host buffer.
  341. /// </summary>
  342. public void Dispose()
  343. {
  344. _modifiedRanges?.Clear();
  345. _memoryTrackingGranular?.Dispose();
  346. _memoryTracking?.Dispose();
  347. _context.Renderer.DeleteBuffer(Handle);
  348. }
  349. }
  350. }