MultiRegionHandle.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Runtime.CompilerServices;
  5. using System.Threading;
  6. namespace Ryujinx.Memory.Tracking
  7. {
  8. /// <summary>
  9. /// A region handle that tracks a large region using many smaller handles, to provide
  10. /// granular tracking that can be used to track partial updates. Backed by a bitmap
  11. /// to improve performance when scanning large regions.
  12. /// </summary>
  13. public class MultiRegionHandle : IMultiRegionHandle
  14. {
  15. /// <summary>
  16. /// A list of region handles for each granularity sized chunk of the whole region.
  17. /// </summary>
  18. private readonly RegionHandle[] _handles;
  19. private readonly ulong Address;
  20. private readonly ulong Granularity;
  21. private readonly ulong Size;
  22. private ConcurrentBitmap _dirtyBitmap;
  23. private int _sequenceNumber;
  24. private BitMap _sequenceNumberBitmap;
  25. private int _uncheckedHandles;
  26. public bool Dirty { get; private set; } = true;
  27. internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
  28. {
  29. _handles = new RegionHandle[size / granularity];
  30. Granularity = granularity;
  31. _dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
  32. _sequenceNumberBitmap = new BitMap(_handles.Length);
  33. int i = 0;
  34. if (handles != null)
  35. {
  36. // Inherit from the handles we were given. Any gaps must be filled with new handles,
  37. // and old handles larger than our granularity must copy their state onto new granular handles and dispose.
  38. // It is assumed that the provided handles do not overlap, in order, are on page boundaries,
  39. // and don't extend past the requested range.
  40. foreach (RegionHandle handle in handles)
  41. {
  42. int startIndex = (int)((handle.Address - address) / granularity);
  43. // Fill any gap left before this handle.
  44. while (i < startIndex)
  45. {
  46. RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
  47. fillHandle.Parent = this;
  48. _handles[i++] = fillHandle;
  49. }
  50. lock (tracking.TrackingLock)
  51. {
  52. if (handle is RegionHandle bitHandle && handle.Size == granularity)
  53. {
  54. handle.Parent = this;
  55. bitHandle.ReplaceBitmap(_dirtyBitmap, i);
  56. _handles[i++] = bitHandle;
  57. }
  58. else
  59. {
  60. int endIndex = (int)((handle.EndAddress - address) / granularity);
  61. while (i < endIndex)
  62. {
  63. RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
  64. splitHandle.Parent = this;
  65. splitHandle.Reprotect(handle.Dirty);
  66. RegionSignal signal = handle.PreAction;
  67. if (signal != null)
  68. {
  69. splitHandle.RegisterAction(signal);
  70. }
  71. _handles[i++] = splitHandle;
  72. }
  73. handle.Dispose();
  74. }
  75. }
  76. }
  77. }
  78. // Fill any remaining space with new handles.
  79. while (i < _handles.Length)
  80. {
  81. RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
  82. handle.Parent = this;
  83. _handles[i++] = handle;
  84. }
  85. _uncheckedHandles = _handles.Length;
  86. Address = address;
  87. Size = size;
  88. }
  89. public void SignalWrite()
  90. {
  91. Dirty = true;
  92. }
  93. public IEnumerable<RegionHandle> GetHandles()
  94. {
  95. return _handles;
  96. }
  97. public void ForceDirty(ulong address, ulong size)
  98. {
  99. Dirty = true;
  100. int startHandle = (int)((address - Address) / Granularity);
  101. int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
  102. for (int i = startHandle; i <= lastHandle; i++)
  103. {
  104. if (_sequenceNumberBitmap.Clear(i))
  105. {
  106. _uncheckedHandles++;
  107. }
  108. _handles[i].ForceDirty();
  109. }
  110. }
  111. public void QueryModified(Action<ulong, ulong> modifiedAction)
  112. {
  113. if (!Dirty)
  114. {
  115. return;
  116. }
  117. Dirty = false;
  118. QueryModified(Address, Size, modifiedAction);
  119. }
  120. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  121. private void ParseDirtyBits(long dirtyBits, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
  122. {
  123. while (dirtyBits != 0)
  124. {
  125. int bit = BitOperations.TrailingZeroCount(dirtyBits);
  126. dirtyBits &= ~(1L << bit);
  127. int handleIndex = baseBit + bit;
  128. RegionHandle handle = _handles[handleIndex];
  129. if (handleIndex != prevHandle + 1)
  130. {
  131. // Submit handles scanned until the gap as dirty
  132. if (rgSize != 0)
  133. {
  134. modifiedAction(rgStart, rgSize);
  135. rgSize = 0;
  136. }
  137. rgStart = handle.Address;
  138. }
  139. if (handle.Dirty)
  140. {
  141. rgSize += handle.Size;
  142. handle.Reprotect();
  143. }
  144. prevHandle = handleIndex;
  145. }
  146. baseBit += ConcurrentBitmap.IntSize;
  147. }
  148. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
  149. {
  150. int startHandle = (int)((address - Address) / Granularity);
  151. int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
  152. ulong rgStart = _handles[startHandle].Address;
  153. if (startHandle == lastHandle)
  154. {
  155. RegionHandle handle = _handles[startHandle];
  156. if (handle.Dirty)
  157. {
  158. handle.Reprotect();
  159. modifiedAction(rgStart, handle.Size);
  160. }
  161. return;
  162. }
  163. ulong rgSize = 0;
  164. long[] masks = _dirtyBitmap.Masks;
  165. int startIndex = startHandle >> ConcurrentBitmap.IntShift;
  166. int startBit = startHandle & ConcurrentBitmap.IntMask;
  167. long startMask = -1L << startBit;
  168. int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
  169. int endBit = lastHandle & ConcurrentBitmap.IntMask;
  170. long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
  171. long startValue = Volatile.Read(ref masks[startIndex]);
  172. int baseBit = startIndex << ConcurrentBitmap.IntShift;
  173. int prevHandle = startHandle - 1;
  174. if (startIndex == endIndex)
  175. {
  176. ParseDirtyBits(startValue & startMask & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  177. }
  178. else
  179. {
  180. ParseDirtyBits(startValue & startMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  181. for (int i = startIndex + 1; i < endIndex; i++)
  182. {
  183. ParseDirtyBits(Volatile.Read(ref masks[i]), ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  184. }
  185. long endValue = Volatile.Read(ref masks[endIndex]);
  186. ParseDirtyBits(endValue & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  187. }
  188. if (rgSize != 0)
  189. {
  190. modifiedAction(rgStart, rgSize);
  191. }
  192. }
  193. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  194. private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
  195. {
  196. long seqMask = mask & ~seqMasks[index];
  197. dirtyBits &= seqMask;
  198. while (dirtyBits != 0)
  199. {
  200. int bit = BitOperations.TrailingZeroCount(dirtyBits);
  201. dirtyBits &= ~(1L << bit);
  202. int handleIndex = baseBit + bit;
  203. RegionHandle handle = _handles[handleIndex];
  204. if (handleIndex != prevHandle + 1)
  205. {
  206. // Submit handles scanned until the gap as dirty
  207. if (rgSize != 0)
  208. {
  209. modifiedAction(rgStart, rgSize);
  210. rgSize = 0;
  211. }
  212. rgStart = handle.Address;
  213. }
  214. rgSize += handle.Size;
  215. handle.Reprotect();
  216. prevHandle = handleIndex;
  217. }
  218. seqMasks[index] |= mask;
  219. _uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
  220. baseBit += ConcurrentBitmap.IntSize;
  221. }
  222. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
  223. {
  224. int startHandle = (int)((address - Address) / Granularity);
  225. int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
  226. ulong rgStart = Address + (ulong)startHandle * Granularity;
  227. if (sequenceNumber != _sequenceNumber)
  228. {
  229. if (_uncheckedHandles != _handles.Length)
  230. {
  231. _sequenceNumberBitmap.Clear();
  232. _uncheckedHandles = _handles.Length;
  233. }
  234. _sequenceNumber = sequenceNumber;
  235. }
  236. if (startHandle == lastHandle)
  237. {
  238. var handle = _handles[startHandle];
  239. if (_sequenceNumberBitmap.Set(startHandle))
  240. {
  241. _uncheckedHandles--;
  242. if (handle.DirtyOrVolatile())
  243. {
  244. handle.Reprotect();
  245. modifiedAction(rgStart, handle.Size);
  246. }
  247. }
  248. return;
  249. }
  250. if (_uncheckedHandles == 0)
  251. {
  252. return;
  253. }
  254. ulong rgSize = 0;
  255. long[] seqMasks = _sequenceNumberBitmap.Masks;
  256. long[] masks = _dirtyBitmap.Masks;
  257. int startIndex = startHandle >> ConcurrentBitmap.IntShift;
  258. int startBit = startHandle & ConcurrentBitmap.IntMask;
  259. long startMask = -1L << startBit;
  260. int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
  261. int endBit = lastHandle & ConcurrentBitmap.IntMask;
  262. long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
  263. long startValue = Volatile.Read(ref masks[startIndex]);
  264. int baseBit = startIndex << ConcurrentBitmap.IntShift;
  265. int prevHandle = startHandle - 1;
  266. if (startIndex == endIndex)
  267. {
  268. ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  269. }
  270. else
  271. {
  272. ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  273. for (int i = startIndex + 1; i < endIndex; i++)
  274. {
  275. ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  276. }
  277. long endValue = Volatile.Read(ref masks[endIndex]);
  278. ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
  279. }
  280. if (rgSize != 0)
  281. {
  282. modifiedAction(rgStart, rgSize);
  283. }
  284. }
  285. public void RegisterAction(ulong address, ulong size, RegionSignal action)
  286. {
  287. int startHandle = (int)((address - Address) / Granularity);
  288. int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
  289. for (int i = startHandle; i <= lastHandle; i++)
  290. {
  291. _handles[i].RegisterAction(action);
  292. }
  293. }
  294. public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action)
  295. {
  296. int startHandle = (int)((address - Address) / Granularity);
  297. int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
  298. for (int i = startHandle; i <= lastHandle; i++)
  299. {
  300. _handles[i].RegisterPreciseAction(action);
  301. }
  302. }
  303. public void Dispose()
  304. {
  305. foreach (var handle in _handles)
  306. {
  307. handle.Dispose();
  308. }
  309. }
  310. }
  311. }