SmartMultiRegionHandle.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 RegisterAction(RegionSignal action)
  38. {
  39. foreach (var handle in _handles)
  40. {
  41. if (handle != null)
  42. {
  43. handle?.RegisterAction((address, size) => action(handle.Address, handle.Size));
  44. }
  45. }
  46. }
  47. public void QueryModified(Action<ulong, ulong> modifiedAction)
  48. {
  49. if (!Dirty)
  50. {
  51. return;
  52. }
  53. Dirty = false;
  54. QueryModified(_address, _size, modifiedAction);
  55. }
  56. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  57. private ulong HandlesToBytes(int handles)
  58. {
  59. return (ulong)handles * _granularity;
  60. }
  61. private void SplitHandle(int handleIndex, int splitIndex)
  62. {
  63. RegionHandle handle = _handles[handleIndex];
  64. ulong address = _address + HandlesToBytes(handleIndex);
  65. ulong size = HandlesToBytes(splitIndex - handleIndex);
  66. // First, the target handle must be removed. Its data can still be used to determine the new handles.
  67. RegionSignal signal = handle.PreAction;
  68. handle.Dispose();
  69. RegionHandle splitLow = _tracking.BeginTracking(address, size);
  70. splitLow.Parent = this;
  71. if (signal != null)
  72. {
  73. splitLow.RegisterAction(signal);
  74. }
  75. _handles[handleIndex] = splitLow;
  76. RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size);
  77. splitHigh.Parent = this;
  78. if (signal != null)
  79. {
  80. splitHigh.RegisterAction(signal);
  81. }
  82. _handles[splitIndex] = splitHigh;
  83. }
  84. private void CreateHandle(int startHandle, int lastHandle)
  85. {
  86. ulong startAddress = _address + HandlesToBytes(startHandle);
  87. // Scan for the first handle before us. If it's overlapping us, it must be split.
  88. for (int i = startHandle - 1; i >= 0; i--)
  89. {
  90. RegionHandle handle = _handles[i];
  91. if (handle != null)
  92. {
  93. if (handle.EndAddress > startAddress)
  94. {
  95. SplitHandle(i, startHandle);
  96. return; // The remainer of this handle should be filled in later on.
  97. }
  98. break;
  99. }
  100. }
  101. // Scan for handles after us. We should create a handle that goes up to this handle's start point, if present.
  102. for (int i = startHandle + 1; i <= lastHandle; i++)
  103. {
  104. RegionHandle handle = _handles[i];
  105. if (handle != null)
  106. {
  107. // Fill up to the found handle.
  108. handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle));
  109. handle.Parent = this;
  110. _handles[startHandle] = handle;
  111. return;
  112. }
  113. }
  114. // Can fill the whole range.
  115. _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle));
  116. _handles[startHandle].Parent = this;
  117. }
  118. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
  119. {
  120. int startHandle = (int)((address - _address) / _granularity);
  121. int lastHandle = (int)((address + (size - 1) - _address) / _granularity);
  122. ulong rgStart = _address + (ulong)startHandle * _granularity;
  123. ulong rgSize = 0;
  124. ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity;
  125. int i = startHandle;
  126. while (i <= lastHandle)
  127. {
  128. RegionHandle handle = _handles[i];
  129. if (handle == null)
  130. {
  131. // Missing handle. A new handle must be created.
  132. CreateHandle(i, lastHandle);
  133. handle = _handles[i];
  134. }
  135. if (handle.EndAddress > endAddress)
  136. {
  137. // End address of handle is beyond the end of the search. Force a split.
  138. SplitHandle(i, lastHandle + 1);
  139. handle = _handles[i];
  140. }
  141. if (handle.Dirty)
  142. {
  143. rgSize += handle.Size;
  144. handle.Reprotect();
  145. }
  146. else
  147. {
  148. // Submit the region scanned so far as dirty
  149. if (rgSize != 0)
  150. {
  151. modifiedAction(rgStart, rgSize);
  152. rgSize = 0;
  153. }
  154. rgStart = handle.EndAddress;
  155. }
  156. i += (int)(handle.Size / _granularity);
  157. }
  158. if (rgSize != 0)
  159. {
  160. modifiedAction(rgStart, rgSize);
  161. }
  162. }
  163. public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
  164. {
  165. int startHandle = (int)((address - _address) / _granularity);
  166. int lastHandle = (int)((address + (size - 1) - _address) / _granularity);
  167. ulong rgStart = _address + (ulong)startHandle * _granularity;
  168. ulong rgSize = 0;
  169. ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity;
  170. int i = startHandle;
  171. while (i <= lastHandle)
  172. {
  173. RegionHandle handle = _handles[i];
  174. if (handle == null)
  175. {
  176. // Missing handle. A new handle must be created.
  177. CreateHandle(i, lastHandle);
  178. handle = _handles[i];
  179. }
  180. if (handle.EndAddress > endAddress)
  181. {
  182. // End address of handle is beyond the end of the search. Force a split.
  183. SplitHandle(i, lastHandle + 1);
  184. handle = _handles[i];
  185. }
  186. if (handle.Dirty && sequenceNumber != handle.SequenceNumber)
  187. {
  188. rgSize += handle.Size;
  189. handle.Reprotect();
  190. }
  191. else
  192. {
  193. // Submit the region scanned so far as dirty
  194. if (rgSize != 0)
  195. {
  196. modifiedAction(rgStart, rgSize);
  197. rgSize = 0;
  198. }
  199. rgStart = handle.EndAddress;
  200. }
  201. handle.SequenceNumber = sequenceNumber;
  202. i += (int)(handle.Size / _granularity);
  203. }
  204. if (rgSize != 0)
  205. {
  206. modifiedAction(rgStart, rgSize);
  207. }
  208. }
  209. public void Dispose()
  210. {
  211. foreach (var handle in _handles)
  212. {
  213. handle?.Dispose();
  214. }
  215. }
  216. }
  217. }