SmartMultiRegionHandle.cs 9.3 KB

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