MultiRegionHandle.cs 14 KB

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