SmartMultiRegionHandle.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. namespace Ryujinx.Memory.Tracking
  4. {
  5. /// <summary>
  6. /// A MultiRegionHandle that attempts to segment a region's handles into the regions requested
  7. /// to avoid iterating over granular chunks for canonically large regions.
  8. /// If minimum granularity is to be expected, use MultiRegionHandle.
  9. /// </summary>
  10. public class SmartMultiRegionHandle : IMultiRegionHandle
  11. {
  12. /// <summary>
  13. /// A list of region handles starting at each granularity size increment.
  14. /// </summary>
  15. private readonly RegionHandle[] _handles;
  16. private readonly ulong _address;
  17. private readonly ulong _granularity;
  18. private readonly ulong _size;
  19. private readonly MemoryTracking _tracking;
  20. private readonly int _id;
  21. public bool Dirty { get; private set; } = true;
  22. internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id)
  23. {
  24. // For this multi-region handle, the handle list starts empty.
  25. // As regions are queried, they are added to the _handles array at their start index.
  26. // When a region being added overlaps another, the existing region is split.
  27. // A query can therefore scan multiple regions, though with no overlaps they can cover a large area.
  28. _tracking = tracking;
  29. _handles = new RegionHandle[size / granularity];
  30. _granularity = granularity;
  31. _address = address;
  32. _size = size;
  33. _id = id;
  34. }
  35. public void SignalWrite()
  36. {
  37. Dirty = true;
  38. }
  39. public void ForceDirty(ulong address, ulong size)
  40. {
  41. foreach (var handle in _handles)
  42. {
  43. if (handle != null && handle.OverlapsWith(address, size))
  44. {
  45. handle.ForceDirty();
  46. }
  47. }
  48. }
  49. public void RegisterAction(RegionSignal action)
  50. {
  51. foreach (var handle in _handles)
  52. {
  53. if (handle != null)
  54. {
  55. handle?.RegisterAction((address, size) => action(handle.Address, handle.Size));
  56. }
  57. }
  58. }
  59. public void RegisterPreciseAction(PreciseRegionSignal action)
  60. {
  61. foreach (var handle in _handles)
  62. {
  63. if (handle != null)
  64. {
  65. handle?.RegisterPreciseAction((address, size, write) => action(handle.Address, handle.Size, write));
  66. }
  67. }
  68. }
  69. public void QueryModified(Action<ulong, ulong> modifiedAction)
  70. {
  71. if (!Dirty)
  72. {
  73. return;
  74. }
  75. Dirty = false;
  76. QueryModified(_address, _size, modifiedAction);
  77. }
  78. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  79. private ulong HandlesToBytes(int handles)
  80. {
  81. return (ulong)handles * _granularity;
  82. }
  83. private void SplitHandle(int handleIndex, int splitIndex)
  84. {
  85. RegionHandle handle = _handles[handleIndex];
  86. ulong address = _address + HandlesToBytes(handleIndex);
  87. ulong size = HandlesToBytes(splitIndex - handleIndex);
  88. // First, the target handle must be removed. Its data can still be used to determine the new handles.
  89. RegionSignal signal = handle.PreAction;
  90. handle.Dispose();
  91. RegionHandle splitLow = _tracking.BeginTracking(address, size, _id);
  92. splitLow.Parent = this;
  93. if (signal != null)
  94. {
  95. splitLow.RegisterAction(signal);
  96. }
  97. _handles[handleIndex] = splitLow;
  98. RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id);
  99. splitHigh.Parent = this;
  100. if (signal != null)
  101. {
  102. splitHigh.RegisterAction(signal);
  103. }
  104. _handles[splitIndex] = splitHigh;
  105. }
  106. private void CreateHandle(int startHandle, int lastHandle)
  107. {
  108. ulong startAddress = _address + HandlesToBytes(startHandle);
  109. // Scan for the first handle before us. If it's overlapping us, it must be split.
  110. for (int i = startHandle - 1; i >= 0; i--)
  111. {
  112. RegionHandle handle = _handles[i];
  113. if (handle != null)
  114. {
  115. if (handle.EndAddress > startAddress)
  116. {
  117. SplitHandle(i, startHandle);
  118. return; // The remainer of this handle should be filled in later on.
  119. }
  120. break;
  121. }
  122. }
  123. // Scan for handles after us. We should create a handle that goes up to this handle's start point, if present.
  124. for (int i = startHandle + 1; i <= lastHandle; i++)
  125. {
  126. RegionHandle handle = _handles[i];
  127. if (handle != null)
  128. {
  129. // Fill up to the found handle.
  130. handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id);
  131. handle.Parent = this;
  132. _handles[startHandle] = handle;
  133. return;
  134. }
  135. }
  136. // Can fill the whole range.
  137. _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id);
  138. _handles[startHandle].Parent = this;
  139. }
  140. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
  141. {
  142. int startHandle = (int)((address - _address) / _granularity);
  143. int lastHandle = (int)((address + (size - 1) - _address) / _granularity);
  144. ulong rgStart = _address + (ulong)startHandle * _granularity;
  145. ulong rgSize = 0;
  146. ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity;
  147. int i = startHandle;
  148. while (i <= lastHandle)
  149. {
  150. RegionHandle handle = _handles[i];
  151. if (handle == null)
  152. {
  153. // Missing handle. A new handle must be created.
  154. CreateHandle(i, lastHandle);
  155. handle = _handles[i];
  156. }
  157. if (handle.EndAddress > endAddress)
  158. {
  159. // End address of handle is beyond the end of the search. Force a split.
  160. SplitHandle(i, lastHandle + 1);
  161. handle = _handles[i];
  162. }
  163. if (handle.Dirty)
  164. {
  165. rgSize += handle.Size;
  166. handle.Reprotect();
  167. }
  168. else
  169. {
  170. // Submit the region scanned so far as dirty
  171. if (rgSize != 0)
  172. {
  173. modifiedAction(rgStart, rgSize);
  174. rgSize = 0;
  175. }
  176. rgStart = handle.EndAddress;
  177. }
  178. i += (int)(handle.Size / _granularity);
  179. }
  180. if (rgSize != 0)
  181. {
  182. modifiedAction(rgStart, rgSize);
  183. }
  184. }
  185. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
  186. {
  187. int startHandle = (int)((address - _address) / _granularity);
  188. int lastHandle = (int)((address + (size - 1) - _address) / _granularity);
  189. ulong rgStart = _address + (ulong)startHandle * _granularity;
  190. ulong rgSize = 0;
  191. ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity;
  192. int i = startHandle;
  193. while (i <= lastHandle)
  194. {
  195. RegionHandle handle = _handles[i];
  196. if (handle == null)
  197. {
  198. // Missing handle. A new handle must be created.
  199. CreateHandle(i, lastHandle);
  200. handle = _handles[i];
  201. }
  202. if (handle.EndAddress > endAddress)
  203. {
  204. // End address of handle is beyond the end of the search. Force a split.
  205. SplitHandle(i, lastHandle + 1);
  206. handle = _handles[i];
  207. }
  208. if (handle.Dirty && sequenceNumber != handle.SequenceNumber)
  209. {
  210. rgSize += handle.Size;
  211. handle.Reprotect();
  212. }
  213. else
  214. {
  215. // Submit the region scanned so far as dirty
  216. if (rgSize != 0)
  217. {
  218. modifiedAction(rgStart, rgSize);
  219. rgSize = 0;
  220. }
  221. rgStart = handle.EndAddress;
  222. }
  223. handle.SequenceNumber = sequenceNumber;
  224. i += (int)(handle.Size / _granularity);
  225. }
  226. if (rgSize != 0)
  227. {
  228. modifiedAction(rgStart, rgSize);
  229. }
  230. }
  231. public void Dispose()
  232. {
  233. GC.SuppressFinalize(this);
  234. foreach (var handle in _handles)
  235. {
  236. handle?.Dispose();
  237. }
  238. }
  239. }
  240. }