diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Memory/Tracking | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Memory/Tracking')
| -rw-r--r-- | Ryujinx.Memory/Tracking/AbstractRegion.cs | 73 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/BitMap.cs | 199 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/ConcurrentBitmap.cs | 152 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/IMultiRegionHandle.cs | 55 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/IRegionHandle.cs | 18 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/MemoryTracking.cs | 306 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 415 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/PreciseRegionSignal.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/RegionHandle.cs | 464 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/RegionSignal.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs | 280 | ||||
| -rw-r--r-- | Ryujinx.Memory/Tracking/VirtualRegion.cs | 144 |
12 files changed, 0 insertions, 2114 deletions
diff --git a/Ryujinx.Memory/Tracking/AbstractRegion.cs b/Ryujinx.Memory/Tracking/AbstractRegion.cs deleted file mode 100644 index bd4c8ab5..00000000 --- a/Ryujinx.Memory/Tracking/AbstractRegion.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Ryujinx.Memory.Range; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A region of memory. - /// </summary> - abstract class AbstractRegion : INonOverlappingRange - { - /// <summary> - /// Base address. - /// </summary> - public ulong Address { get; } - - /// <summary> - /// Size of the range in bytes. - /// </summary> - public ulong Size { get; protected set; } - - /// <summary> - /// End address. - /// </summary> - public ulong EndAddress => Address + Size; - - /// <summary> - /// Create a new region. - /// </summary> - /// <param name="address">Base address</param> - /// <param name="size">Size of the range</param> - protected AbstractRegion(ulong address, ulong size) - { - Address = address; - Size = size; - } - - /// <summary> - /// Check if this range overlaps with another. - /// </summary> - /// <param name="address">Base address</param> - /// <param name="size">Size of the range</param> - /// <returns>True if overlapping, false otherwise</returns> - public bool OverlapsWith(ulong address, ulong size) - { - return Address < address + size && address < EndAddress; - } - - /// <summary> - /// Signals to the handles that a memory event has occurred, and unprotects the region. Assumes that the tracking lock has been obtained. - /// </summary> - /// <param name="address">Address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> - public abstract void Signal(ulong address, ulong size, bool write, int? exemptId); - - /// <summary> - /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained. - /// </summary> - /// <param name="address">Address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <param name="exemptId">Optional ID of the handles that should not be signalled</param> - public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId); - - /// <summary> - /// Split this region into two, around the specified address. - /// This region is updated to end at the split address, and a new region is created to represent past that point. - /// </summary> - /// <param name="splitAddress">Address to split the region around</param> - /// <returns>The second part of the split region, with start address at the given split.</returns> - public abstract INonOverlappingRange Split(ulong splitAddress); - } -} diff --git a/Ryujinx.Memory/Tracking/BitMap.cs b/Ryujinx.Memory/Tracking/BitMap.cs deleted file mode 100644 index 173952f3..00000000 --- a/Ryujinx.Memory/Tracking/BitMap.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A bitmap that can check or set large ranges of true/false values at once. - /// </summary> - readonly struct BitMap - { - public const int IntSize = 64; - - private const int IntShift = 6; - private const int IntMask = IntSize - 1; - - /// <summary> - /// Masks representing the bitmap. Least significant bit first, 64-bits per mask. - /// </summary> - public readonly long[] Masks; - - /// <summary> - /// Create a new bitmap. - /// </summary> - /// <param name="count">The number of bits to reserve</param> - public BitMap(int count) - { - Masks = new long[(count + IntMask) / IntSize]; - } - - /// <summary> - /// Check if any bit in the bitmap is set. - /// </summary> - /// <returns>True if any bits are set, false otherwise</returns> - public bool AnySet() - { - for (int i = 0; i < Masks.Length; i++) - { - if (Masks[i] != 0) - { - return true; - } - } - - return false; - } - - /// <summary> - /// Check if a bit in the bitmap is set. - /// </summary> - /// <param name="bit">The bit index to check</param> - /// <returns>True if the bit is set, false otherwise</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSet(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - return (Masks[wordIndex] & wordMask) != 0; - } - - /// <summary> - /// Check if any bit in a range of bits in the bitmap are set. (inclusive) - /// </summary> - /// <param name="start">The first bit index to check</param> - /// <param name="end">The last bit index to check</param> - /// <returns>True if a bit is set, false otherwise</returns> - public bool IsSet(int start, int end) - { - if (start == end) - { - return IsSet(start); - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - return (Masks[startIndex] & startMask & endMask) != 0; - } - - if ((Masks[startIndex] & startMask) != 0) - { - return true; - } - - for (int i = startIndex + 1; i < endIndex; i++) - { - if (Masks[i] != 0) - { - return true; - } - } - - if ((Masks[endIndex] & endMask) != 0) - { - return true; - } - - return false; - } - - /// <summary> - /// Set a bit at a specific index to 1. - /// </summary> - /// <param name="bit">The bit index to set</param> - /// <returns>True if the bit is set, false if it was already set</returns> - public bool Set(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - if ((Masks[wordIndex] & wordMask) != 0) - { - return false; - } - - Masks[wordIndex] |= wordMask; - - return true; - } - - /// <summary> - /// Set a range of bits in the bitmap to 1. - /// </summary> - /// <param name="start">The first bit index to set</param> - /// <param name="end">The last bit index to set</param> - public void SetRange(int start, int end) - { - if (start == end) - { - Set(start); - return; - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - Masks[startIndex] |= startMask & endMask; - } - else - { - Masks[startIndex] |= startMask; - - for (int i = startIndex + 1; i < endIndex; i++) - { - Masks[i] |= -1; - } - - Masks[endIndex] |= endMask; - } - } - - /// <summary> - /// Clear a bit at a specific index to 0. - /// </summary> - /// <param name="bit">The bit index to clear</param> - /// <returns>True if the bit was set, false if it was not</returns> - public bool Clear(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - bool wasSet = (Masks[wordIndex] & wordMask) != 0; - - Masks[wordIndex] &= ~wordMask; - - return wasSet; - } - - /// <summary> - /// Clear the bitmap entirely, setting all bits to 0. - /// </summary> - public void Clear() - { - for (int i = 0; i < Masks.Length; i++) - { - Masks[i] = 0; - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs b/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs deleted file mode 100644 index 994fda92..00000000 --- a/Ryujinx.Memory/Tracking/ConcurrentBitmap.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Threading; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A bitmap that can be safely modified from multiple threads. - /// </summary> - internal class ConcurrentBitmap - { - public const int IntSize = 64; - - public const int IntShift = 6; - public const int IntMask = IntSize - 1; - - /// <summary> - /// Masks representing the bitmap. Least significant bit first, 64-bits per mask. - /// </summary> - public readonly long[] Masks; - - /// <summary> - /// Create a new multithreaded bitmap. - /// </summary> - /// <param name="count">The number of bits to reserve</param> - /// <param name="set">Whether the bits should be initially set or not</param> - public ConcurrentBitmap(int count, bool set) - { - Masks = new long[(count + IntMask) / IntSize]; - - if (set) - { - Array.Fill(Masks, -1L); - } - } - - /// <summary> - /// Check if any bit in the bitmap is set. - /// </summary> - /// <returns>True if any bits are set, false otherwise</returns> - public bool AnySet() - { - for (int i = 0; i < Masks.Length; i++) - { - if (Interlocked.Read(ref Masks[i]) != 0) - { - return true; - } - } - - return false; - } - - /// <summary> - /// Check if a bit in the bitmap is set. - /// </summary> - /// <param name="bit">The bit index to check</param> - /// <returns>True if the bit is set, false otherwise</returns> - public bool IsSet(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - return (Interlocked.Read(ref Masks[wordIndex]) & wordMask) != 0; - } - - /// <summary> - /// Check if any bit in a range of bits in the bitmap are set. (inclusive) - /// </summary> - /// <param name="start">The first bit index to check</param> - /// <param name="end">The last bit index to check</param> - /// <returns>True if a bit is set, false otherwise</returns> - public bool IsSet(int start, int end) - { - if (start == end) - { - return IsSet(start); - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - long startValue = Interlocked.Read(ref Masks[startIndex]); - - if (startIndex == endIndex) - { - return (startValue & startMask & endMask) != 0; - } - - if ((startValue & startMask) != 0) - { - return true; - } - - for (int i = startIndex + 1; i < endIndex; i++) - { - if (Interlocked.Read(ref Masks[i]) != 0) - { - return true; - } - } - - long endValue = Interlocked.Read(ref Masks[endIndex]); - - if ((endValue & endMask) != 0) - { - return true; - } - - return false; - } - - /// <summary> - /// Set a bit at a specific index to either true or false. - /// </summary> - /// <param name="bit">The bit index to set</param> - /// <param name="value">Whether the bit should be set or not</param> - public void Set(int bit, bool value) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - if (value) - { - Interlocked.Or(ref Masks[wordIndex], wordMask); - } - else - { - Interlocked.And(ref Masks[wordIndex], ~wordMask); - } - } - - /// <summary> - /// Clear the bitmap entirely, setting all bits to 0. - /// </summary> - public void Clear() - { - for (int i = 0; i < Masks.Length; i++) - { - Interlocked.Exchange(ref Masks[i], 0); - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs b/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs deleted file mode 100644 index 71bd602f..00000000 --- a/Ryujinx.Memory/Tracking/IMultiRegionHandle.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -namespace Ryujinx.Memory.Tracking -{ - public interface IMultiRegionHandle : IDisposable - { - /// <summary> - /// True if any write has occurred to the whole region since the last use of QueryModified (with no subregion specified). - /// </summary> - bool Dirty { get; } - - /// <summary> - /// Force the range of handles to be dirty, without reprotecting. - /// </summary> - /// <param name="address">Start address of the range</param> - /// <param name="size">Size of the range</param> - public void ForceDirty(ulong address, ulong size); - - /// <summary> - /// Check if any part of the region has been modified, and perform an action for each. - /// Contiguous modified regions are combined. - /// </summary> - /// <param name="modifiedAction">Action to perform for modified regions</param> - void QueryModified(Action<ulong, ulong> modifiedAction); - - - /// <summary> - /// Check if part of the region has been modified within a given range, and perform an action for each. - /// The range is aligned to the level of granularity of the contained handles. - /// Contiguous modified regions are combined. - /// </summary> - /// <param name="address">Start address of the range</param> - /// <param name="size">Size of the range</param> - /// <param name="modifiedAction">Action to perform for modified regions</param> - void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction); - - /// <summary> - /// Check if part of the region has been modified within a given range, and perform an action for each. - /// The sequence number provided is compared with each handle's saved sequence number. - /// If it is equal, then the handle's dirty flag is ignored. Otherwise, the sequence number is saved. - /// The range is aligned to the level of granularity of the contained handles. - /// Contiguous modified regions are combined. - /// </summary> - /// <param name="address">Start address of the range</param> - /// <param name="size">Size of the range</param> - /// <param name="modifiedAction">Action to perform for modified regions</param> - /// <param name="sequenceNumber">Current sequence number</param> - void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber); - - /// <summary> - /// Signal that one of the subregions of this multi-region has been modified. This sets the overall dirty flag. - /// </summary> - void SignalWrite(); - } -} diff --git a/Ryujinx.Memory/Tracking/IRegionHandle.cs b/Ryujinx.Memory/Tracking/IRegionHandle.cs deleted file mode 100644 index 9d99d90e..00000000 --- a/Ryujinx.Memory/Tracking/IRegionHandle.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Ryujinx.Memory.Tracking -{ - public interface IRegionHandle : IDisposable - { - bool Dirty { get; } - - ulong Address { get; } - ulong Size { get; } - ulong EndAddress { get; } - - void ForceDirty(); - void Reprotect(bool asDirty = false); - void RegisterAction(RegionSignal action); - void RegisterPreciseAction(PreciseRegionSignal action); - } -} diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs deleted file mode 100644 index bf1e0ad3..00000000 --- a/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ /dev/null @@ -1,306 +0,0 @@ -using Ryujinx.Common.Pools; -using Ryujinx.Memory.Range; -using System.Collections.Generic; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// Manages memory tracking for a given virutal/physical memory block. - /// </summary> - public class MemoryTracking - { - private readonly IVirtualMemoryManager _memoryManager; - private readonly InvalidAccessHandler _invalidAccessHandler; - - // Only use these from within the lock. - private readonly NonOverlappingRangeList<VirtualRegion> _virtualRegions; - - private readonly int _pageSize; - - /// <summary> - /// This lock must be obtained when traversing or updating the region-handle hierarchy. - /// It is not required when reading dirty flags. - /// </summary> - internal object TrackingLock = new object(); - - /// <summary> - /// Create a new tracking structure for the given "physical" memory block, - /// with a given "virtual" memory manager that will provide mappings and virtual memory protection. - /// </summary> - /// <param name="memoryManager">Virtual memory manager</param> - /// <param name="block">Physical memory block</param> - /// <param name="pageSize">Page size of the virtual memory space</param> - public MemoryTracking(IVirtualMemoryManager memoryManager, int pageSize, InvalidAccessHandler invalidAccessHandler = null) - { - _memoryManager = memoryManager; - _pageSize = pageSize; - _invalidAccessHandler = invalidAccessHandler; - - _virtualRegions = new NonOverlappingRangeList<VirtualRegion>(); - } - - private (ulong address, ulong size) PageAlign(ulong address, ulong size) - { - ulong pageMask = (ulong)_pageSize - 1; - ulong rA = address & ~pageMask; - ulong rS = ((address + size + pageMask) & ~pageMask) - rA; - return (rA, rS); - } - - /// <summary> - /// Indicate that a virtual region has been mapped, and which physical region it has been mapped to. - /// Should be called after the mapping is complete. - /// </summary> - /// <param name="va">Virtual memory address</param> - /// <param name="size">Size to be mapped</param> - public void Map(ulong va, ulong size) - { - // A mapping may mean we need to re-evaluate each VirtualRegion's affected area. - // Find all handles that overlap with the range, we need to recalculate their physical regions - - lock (TrackingLock) - { - ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get(); - - int count = _virtualRegions.FindOverlapsNonOverlapping(va, size, ref overlaps); - - for (int i = 0; i < count; i++) - { - VirtualRegion region = overlaps[i]; - - // If the region has been fully remapped, signal that it has been mapped again. - bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size); - if (remapped) - { - region.SignalMappingChanged(true); - } - - region.UpdateProtection(); - } - } - } - - /// <summary> - /// Indicate that a virtual region has been unmapped. - /// Should be called before the unmapping is complete. - /// </summary> - /// <param name="va">Virtual memory address</param> - /// <param name="size">Size to be unmapped</param> - public void Unmap(ulong va, ulong size) - { - // An unmapping may mean we need to re-evaluate each VirtualRegion's affected area. - // Find all handles that overlap with the range, we need to notify them that the region was unmapped. - - lock (TrackingLock) - { - ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get(); - - int count = _virtualRegions.FindOverlapsNonOverlapping(va, size, ref overlaps); - - for (int i = 0; i < count; i++) - { - VirtualRegion region = overlaps[i]; - - region.SignalMappingChanged(false); - } - } - } - - /// <summary> - /// Get a list of virtual regions that a handle covers. - /// </summary> - /// <param name="va">Starting virtual memory address of the handle</param> - /// <param name="size">Size of the handle's memory region</param> - /// <returns>A list of virtual regions within the given range</returns> - internal List<VirtualRegion> GetVirtualRegionsForHandle(ulong va, ulong size) - { - List<VirtualRegion> result = new List<VirtualRegion>(); - _virtualRegions.GetOrAddRegions(result, va, size, (va, size) => new VirtualRegion(this, va, size)); - - return result; - } - - /// <summary> - /// Remove a virtual region from the range list. This assumes that the lock has been acquired. - /// </summary> - /// <param name="region">Region to remove</param> - internal void RemoveVirtual(VirtualRegion region) - { - _virtualRegions.Remove(region); - } - - /// <summary> - /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. - /// </summary> - /// <param name="address">CPU virtual address of the region</param> - /// <param name="size">Size of the region</param> - /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> - /// <param name="granularity">Desired granularity of write tracking</param> - /// <param name="id">Handle ID</param> - /// <returns>The memory tracking handle</returns> - public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) - { - return new MultiRegionHandle(this, address, size, handles, granularity, id); - } - - /// <summary> - /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. - /// </summary> - /// <param name="address">CPU virtual address of the region</param> - /// <param name="size">Size of the region</param> - /// <param name="granularity">Desired granularity of write tracking</param> - /// <param name="id">Handle ID</param> - /// <returns>The memory tracking handle</returns> - public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) - { - (address, size) = PageAlign(address, size); - - return new SmartMultiRegionHandle(this, address, size, granularity, id); - } - - /// <summary> - /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. - /// </summary> - /// <param name="address">CPU virtual address of the region</param> - /// <param name="size">Size of the region</param> - /// <param name="id">Handle ID</param> - /// <returns>The memory tracking handle</returns> - public RegionHandle BeginTracking(ulong address, ulong size, int id) - { - var (paAddress, paSize) = PageAlign(address, size); - - lock (TrackingLock) - { - bool mapped = _memoryManager.IsRangeMapped(address, size); - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped); - - return handle; - } - } - - /// <summary> - /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. - /// </summary> - /// <param name="address">CPU virtual address of the region</param> - /// <param name="size">Size of the region</param> - /// <param name="bitmap">The bitmap owning the dirty flag for this handle</param> - /// <param name="bit">The bit of this handle within the dirty flag</param> - /// <param name="id">Handle ID</param> - /// <returns>The memory tracking handle</returns> - internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id) - { - var (paAddress, paSize) = PageAlign(address, size); - - lock (TrackingLock) - { - bool mapped = _memoryManager.IsRangeMapped(address, size); - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped); - - return handle; - } - } - - /// <summary> - /// Signal that a virtual memory event happened at the given location. - /// </summary> - /// <param name="address">Virtual address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <returns>True if the event triggered any tracking regions, false otherwise</returns> - public bool VirtualMemoryEvent(ulong address, ulong size, bool write) - { - return VirtualMemoryEvent(address, size, write, precise: false, null); - } - - /// <summary> - /// Signal that a virtual memory event happened at the given location. - /// This can be flagged as a precise event, which will avoid reprotection and call special handlers if possible. - /// A precise event has an exact address and size, rather than triggering on page granularity. - /// </summary> - /// <param name="address">Virtual address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <param name="precise">True if the access is precise, false otherwise</param> - /// <param name="exemptId">Optional ID that of the handles that should not be signalled</param> - /// <returns>True if the event triggered any tracking regions, false otherwise</returns> - public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null) - { - // Look up the virtual region using the region list. - // Signal up the chain to relevant handles. - - bool shouldThrow = false; - - lock (TrackingLock) - { - ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get(); - - int count = _virtualRegions.FindOverlapsNonOverlapping(address, size, ref overlaps); - - if (count == 0 && !precise) - { - if (_memoryManager.IsRangeMapped(address, size)) - { - // TODO: There is currently the possibility that a page can be protected after its virtual region is removed. - // This code handles that case when it happens, but it would be better to find out how this happens. - _memoryManager.TrackingReprotect(address & ~(ulong)(_pageSize - 1), (ulong)_pageSize, MemoryPermission.ReadAndWrite); - return true; // This memory _should_ be mapped, so we need to try again. - } - else - { - shouldThrow = true; - } - } - else - { - for (int i = 0; i < count; i++) - { - VirtualRegion region = overlaps[i]; - - if (precise) - { - region.SignalPrecise(address, size, write, exemptId); - } - else - { - region.Signal(address, size, write, exemptId); - } - } - } - } - - if (shouldThrow) - { - _invalidAccessHandler?.Invoke(address); - - // We can't continue - it's impossible to remove protection from the page. - // Even if the access handler wants us to continue, we wouldn't be able to. - throw new InvalidMemoryRegionException(); - } - - return true; - } - - /// <summary> - /// Reprotect a given virtual region. The virtual memory manager will handle this. - /// </summary> - /// <param name="region">Region to reprotect</param> - /// <param name="permission">Memory permission to protect with</param> - internal void ProtectVirtualRegion(VirtualRegion region, MemoryPermission permission) - { - _memoryManager.TrackingReprotect(region.Address, region.Size, permission); - } - - /// <summary> - /// Returns the number of virtual regions currently being tracked. - /// Useful for tests and metrics. - /// </summary> - /// <returns>The number of virtual regions</returns> - public int GetRegionCount() - { - lock (TrackingLock) - { - return _virtualRegions.Count; - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs deleted file mode 100644 index 68fc5e75..00000000 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ /dev/null @@ -1,415 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A region handle that tracks a large region using many smaller handles, to provide - /// granular tracking that can be used to track partial updates. Backed by a bitmap - /// to improve performance when scanning large regions. - /// </summary> - public class MultiRegionHandle : IMultiRegionHandle - { - /// <summary> - /// A list of region handles for each granularity sized chunk of the whole region. - /// </summary> - private readonly RegionHandle[] _handles; - private readonly ulong Address; - private readonly ulong Granularity; - private readonly ulong Size; - - private ConcurrentBitmap _dirtyBitmap; - - private int _sequenceNumber; - private BitMap _sequenceNumberBitmap; - private BitMap _dirtyCheckedBitmap; - private int _uncheckedHandles; - - public bool Dirty { get; private set; } = true; - - internal MultiRegionHandle( - MemoryTracking tracking, - ulong address, - ulong size, - IEnumerable<IRegionHandle> handles, - ulong granularity, - int id) - { - _handles = new RegionHandle[(size + granularity - 1) / granularity]; - Granularity = granularity; - - _dirtyBitmap = new ConcurrentBitmap(_handles.Length, true); - _sequenceNumberBitmap = new BitMap(_handles.Length); - _dirtyCheckedBitmap = new BitMap(_handles.Length); - - int i = 0; - - if (handles != null) - { - // Inherit from the handles we were given. Any gaps must be filled with new handles, - // and old handles larger than our granularity must copy their state onto new granular handles and dispose. - // It is assumed that the provided handles do not overlap, in order, are on page boundaries, - // and don't extend past the requested range. - - foreach (RegionHandle handle in handles) - { - int startIndex = (int)((handle.RealAddress - address) / granularity); - - // Fill any gap left before this handle. - while (i < startIndex) - { - RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); - fillHandle.Parent = this; - _handles[i++] = fillHandle; - } - - lock (tracking.TrackingLock) - { - if (handle is RegionHandle bitHandle && handle.Size == granularity) - { - handle.Parent = this; - - bitHandle.ReplaceBitmap(_dirtyBitmap, i); - - _handles[i++] = bitHandle; - } - else - { - int endIndex = (int)((handle.RealEndAddress - address) / granularity); - - while (i < endIndex) - { - RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); - splitHandle.Parent = this; - - splitHandle.Reprotect(handle.Dirty); - - RegionSignal signal = handle.PreAction; - if (signal != null) - { - splitHandle.RegisterAction(signal); - } - - _handles[i++] = splitHandle; - } - - handle.Dispose(); - } - } - } - } - - // Fill any remaining space with new handles. - while (i < _handles.Length) - { - RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); - handle.Parent = this; - _handles[i++] = handle; - } - - _uncheckedHandles = _handles.Length; - - Address = address; - Size = size; - } - - public void SignalWrite() - { - Dirty = true; - } - - public IEnumerable<RegionHandle> GetHandles() - { - return _handles; - } - - public void ForceDirty(ulong address, ulong size) - { - Dirty = true; - - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - for (int i = startHandle; i <= lastHandle; i++) - { - if (_sequenceNumberBitmap.Clear(i)) - { - _uncheckedHandles++; - } - - _handles[i].ForceDirty(); - } - } - - public void QueryModified(Action<ulong, ulong> modifiedAction) - { - if (!Dirty) - { - return; - } - - Dirty = false; - - QueryModified(Address, Size, modifiedAction); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ParseDirtyBits(long dirtyBits, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction) - { - while (dirtyBits != 0) - { - int bit = BitOperations.TrailingZeroCount(dirtyBits); - - dirtyBits &= ~(1L << bit); - - int handleIndex = baseBit + bit; - - RegionHandle handle = _handles[handleIndex]; - - if (handleIndex != prevHandle + 1) - { - // Submit handles scanned until the gap as dirty - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - rgSize = 0; - } - - rgStart = handle.RealAddress; - } - - if (handle.Dirty) - { - rgSize += handle.RealSize; - handle.Reprotect(); - } - - prevHandle = handleIndex; - } - - baseBit += ConcurrentBitmap.IntSize; - } - - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) - { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - ulong rgStart = Address + (ulong)startHandle * Granularity; - - if (startHandle == lastHandle) - { - RegionHandle handle = _handles[startHandle]; - - if (handle.Dirty) - { - handle.Reprotect(); - modifiedAction(rgStart, handle.RealSize); - } - - return; - } - - ulong rgSize = 0; - - long[] masks = _dirtyBitmap.Masks; - - int startIndex = startHandle >> ConcurrentBitmap.IntShift; - int startBit = startHandle & ConcurrentBitmap.IntMask; - long startMask = -1L << startBit; - - int endIndex = lastHandle >> ConcurrentBitmap.IntShift; - int endBit = lastHandle & ConcurrentBitmap.IntMask; - long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit)); - - long startValue = Volatile.Read(ref masks[startIndex]); - - int baseBit = startIndex << ConcurrentBitmap.IntShift; - int prevHandle = startHandle - 1; - - if (startIndex == endIndex) - { - ParseDirtyBits(startValue & startMask & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - else - { - ParseDirtyBits(startValue & startMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - - for (int i = startIndex + 1; i < endIndex; i++) - { - ParseDirtyBits(Volatile.Read(ref masks[i]), ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - - long endValue = Volatile.Read(ref masks[endIndex]); - - ParseDirtyBits(endValue & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - 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) - { - long seqMask = mask & ~seqMasks[index]; - long checkMask = (~dirtyBits) & seqMask; - dirtyBits &= seqMask; - - while (dirtyBits != 0) - { - int bit = BitOperations.TrailingZeroCount(dirtyBits); - long bitValue = 1L << bit; - - dirtyBits &= ~bitValue; - - int handleIndex = baseBit + bit; - - RegionHandle handle = _handles[handleIndex]; - - if (handleIndex != prevHandle + 1) - { - // Submit handles scanned until the gap as dirty - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - rgSize = 0; - } - rgStart = handle.RealAddress; - } - - rgSize += handle.RealSize; - handle.Reprotect(false, (checkMasks[index] & bitValue) == 0); - - checkMasks[index] &= ~bitValue; - - prevHandle = handleIndex; - } - - checkMasks[index] |= checkMask; - seqMasks[index] |= mask; - _uncheckedHandles -= BitOperations.PopCount((ulong)seqMask); - - baseBit += ConcurrentBitmap.IntSize; - } - - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) - { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - ulong rgStart = Address + (ulong)startHandle * Granularity; - - if (sequenceNumber != _sequenceNumber) - { - if (_uncheckedHandles != _handles.Length) - { - _sequenceNumberBitmap.Clear(); - _uncheckedHandles = _handles.Length; - } - - _sequenceNumber = sequenceNumber; - } - - if (startHandle == lastHandle) - { - var handle = _handles[startHandle]; - if (_sequenceNumberBitmap.Set(startHandle)) - { - _uncheckedHandles--; - - if (handle.DirtyOrVolatile()) - { - handle.Reprotect(); - - modifiedAction(rgStart, handle.RealSize); - } - } - - return; - } - - if (_uncheckedHandles == 0) - { - return; - } - - ulong rgSize = 0; - - long[] seqMasks = _sequenceNumberBitmap.Masks; - long[] checkedMasks = _dirtyCheckedBitmap.Masks; - long[] masks = _dirtyBitmap.Masks; - - int startIndex = startHandle >> ConcurrentBitmap.IntShift; - int startBit = startHandle & ConcurrentBitmap.IntMask; - long startMask = -1L << startBit; - - int endIndex = lastHandle >> ConcurrentBitmap.IntShift; - int endBit = lastHandle & ConcurrentBitmap.IntMask; - long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit)); - - long startValue = Volatile.Read(ref masks[startIndex]); - - int baseBit = startIndex << ConcurrentBitmap.IntShift; - int prevHandle = startHandle - 1; - - if (startIndex == endIndex) - { - ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - else - { - ParseDirtyBits(startValue, startMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - - for (int i = startIndex + 1; i < endIndex; i++) - { - ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - - long endValue = Volatile.Read(ref masks[endIndex]); - - ParseDirtyBits(endValue, endMask, endIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction); - } - - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - } - } - - public void RegisterAction(ulong address, ulong size, RegionSignal action) - { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - for (int i = startHandle; i <= lastHandle; i++) - { - _handles[i].RegisterAction(action); - } - } - - public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) - { - int startHandle = (int)((address - Address) / Granularity); - int lastHandle = (int)((address + (size - 1) - Address) / Granularity); - - for (int i = startHandle; i <= lastHandle; i++) - { - _handles[i].RegisterPreciseAction(action); - } - } - - public void Dispose() - { - foreach (var handle in _handles) - { - handle.Dispose(); - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs b/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs deleted file mode 100644 index 038f9595..00000000 --- a/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Memory.Tracking -{ - public delegate bool PreciseRegionSignal(ulong address, ulong size, bool write); -} diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs deleted file mode 100644 index 7a59f9f2..00000000 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ /dev/null @@ -1,464 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made, - /// and an action can be performed when the region is read to or written from. - /// </summary> - public class RegionHandle : IRegionHandle - { - /// <summary> - /// If more than this number of checks have been performed on a dirty flag since its last reprotect, - /// then it is dirtied infrequently. - /// </summary> - private const int CheckCountForInfrequent = 3; - - /// <summary> - /// Number of frequent dirty/consume in a row to make this handle volatile. - /// </summary> - private const int VolatileThreshold = 5; - - public bool Dirty - { - get - { - return Bitmap.IsSet(DirtyBit); - } - protected set - { - Bitmap.Set(DirtyBit, value); - } - } - - internal int SequenceNumber { get; set; } - internal int Id { get; } - - public bool Unmapped { get; private set; } - - public ulong Address { get; } - public ulong Size { get; } - public ulong EndAddress { get; } - - public ulong RealAddress { get; } - public ulong RealSize { get; } - public ulong RealEndAddress { get; } - - internal IMultiRegionHandle Parent { get; set; } - - private event Action _onDirty; - - private object _preActionLock = new object(); - private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. - private PreciseRegionSignal _preciseAction; // Action to perform on a precise read or write. - private readonly List<VirtualRegion> _regions; - private readonly MemoryTracking _tracking; - private bool _disposed; - - private int _checkCount = 0; - private int _volatileCount = 0; - private bool _volatile; - - internal MemoryPermission RequiredPermission - { - get - { - // If this is unmapped, allow reprotecting as RW as it can't be dirtied. - // This is required for the partial unmap cases where part of the data are still being accessed. - if (Unmapped) - { - return MemoryPermission.ReadAndWrite; - } - - if (_preAction != null) - { - return MemoryPermission.None; - } - - return Dirty ? MemoryPermission.ReadAndWrite : MemoryPermission.Read; - } - } - - internal RegionSignal PreAction => _preAction; - - internal ConcurrentBitmap Bitmap; - internal int DirtyBit; - - /// <summary> - /// Create a new bitmap backed region handle. The handle is registered with the given tracking object, - /// and will be notified of any changes to the specified region. - /// </summary> - /// <param name="tracking">Tracking object for the target memory block</param> - /// <param name="address">Virtual address of the region to track</param> - /// <param name="size">Size of the region to track</param> - /// <param name="realAddress">The real, unaligned address of the handle</param> - /// <param name="realSize">The real, unaligned size of the handle</param> - /// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param> - /// <param name="bit">The bit index representing the dirty flag for this handle</param> - /// <param name="id">Handle ID</param> - /// <param name="mapped">True if the region handle starts mapped</param> - internal RegionHandle( - MemoryTracking tracking, - ulong address, - ulong size, - ulong realAddress, - ulong realSize, - ConcurrentBitmap bitmap, - int bit, - int id, - bool mapped = true) - { - Bitmap = bitmap; - DirtyBit = bit; - - Dirty = mapped; - - Id = id; - - Unmapped = !mapped; - Address = address; - Size = size; - EndAddress = address + size; - - RealAddress = realAddress; - RealSize = realSize; - RealEndAddress = realAddress + realSize; - - _tracking = tracking; - _regions = tracking.GetVirtualRegionsForHandle(address, size); - foreach (var region in _regions) - { - region.Handles.Add(this); - } - } - - /// <summary> - /// Create a new region handle. The handle is registered with the given tracking object, - /// and will be notified of any changes to the specified region. - /// </summary> - /// <param name="tracking">Tracking object for the target memory block</param> - /// <param name="address">Virtual address of the region to track</param> - /// <param name="size">Size of the region to track</param> - /// <param name="realAddress">The real, unaligned address of the handle</param> - /// <param name="realSize">The real, unaligned size of the handle</param> - /// <param name="id">Handle ID</param> - /// <param name="mapped">True if the region handle starts mapped</param> - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true) - { - Bitmap = new ConcurrentBitmap(1, mapped); - - Id = id; - - Unmapped = !mapped; - - Address = address; - Size = size; - EndAddress = address + size; - - RealAddress = realAddress; - RealSize = realSize; - RealEndAddress = realAddress + realSize; - - _tracking = tracking; - _regions = tracking.GetVirtualRegionsForHandle(address, size); - foreach (var region in _regions) - { - region.Handles.Add(this); - } - } - - /// <summary> - /// Replace the bitmap and bit index used to track dirty state. - /// </summary> - /// <remarks> - /// The tracking lock should be held when this is called, to ensure neither bitmap is modified. - /// </remarks> - /// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param> - /// <param name="bit">The bit index representing the dirty flag for this handle</param> - internal void ReplaceBitmap(ConcurrentBitmap bitmap, int bit) - { - // Assumes the tracking lock is held, so nothing else can signal right now. - - var oldBitmap = Bitmap; - var oldBit = DirtyBit; - - bitmap.Set(bit, Dirty); - - Bitmap = bitmap; - DirtyBit = bit; - - Dirty |= oldBitmap.IsSet(oldBit); - } - - /// <summary> - /// Clear the volatile state of this handle. - /// </summary> - private void ClearVolatile() - { - _volatileCount = 0; - _volatile = false; - } - - /// <summary> - /// Check if this handle is dirty, or if it is volatile. (changes very often) - /// </summary> - /// <returns>True if the handle is dirty or volatile, false otherwise</returns> - public bool DirtyOrVolatile() - { - _checkCount++; - return _volatile || Dirty; - } - - /// <summary> - /// Signal that a memory action occurred within this handle's virtual regions. - /// </summary> - /// <param name="address">Address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <param name="handleIterable">Reference to the handles being iterated, in case the list needs to be copied</param> - internal void Signal(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable) - { - // If this handle was already unmapped (even if just partially), - // then we have nothing to do until it is mapped again. - // The pre-action should be still consumed to avoid flushing on remap. - if (Unmapped) - { - Interlocked.Exchange(ref _preAction, null); - return; - } - - if (_preAction != null) - { - // Limit the range to within this handle. - ulong maxAddress = Math.Max(address, RealAddress); - ulong minEndAddress = Math.Min(address + size, RealAddress + RealSize); - - // Copy the handles list in case it changes when we're out of the lock. - if (handleIterable is List<RegionHandle>) - { - handleIterable = handleIterable.ToArray(); - } - - // Temporarily release the tracking lock while we're running the action. - Monitor.Exit(_tracking.TrackingLock); - - try - { - lock (_preActionLock) - { - _preAction?.Invoke(maxAddress, minEndAddress - maxAddress); - - // The action is removed after it returns, to ensure that the null check above succeeds when - // it's still in progress rather than continuing and possibly missing a required data flush. - Interlocked.Exchange(ref _preAction, null); - } - } - finally - { - Monitor.Enter(_tracking.TrackingLock); - } - } - - if (write) - { - bool oldDirty = Dirty; - Dirty = true; - if (!oldDirty) - { - _onDirty?.Invoke(); - } - Parent?.SignalWrite(); - } - } - - /// <summary> - /// Signal that a precise memory action occurred within this handle's virtual regions. - /// If there is no precise action, or the action returns false, the normal signal handler will be called. - /// </summary> - /// <param name="address">Address accessed</param> - /// <param name="size">Size of the region affected in bytes</param> - /// <param name="write">Whether the region was written to or read</param> - /// <param name="handleIterable">Reference to the handles being iterated, in case the list needs to be copied</param> - /// <returns>True if a precise action was performed and returned true, false otherwise</returns> - internal bool SignalPrecise(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable) - { - if (!Unmapped && _preciseAction != null && _preciseAction(address, size, write)) - { - return true; - } - - Signal(address, size, write, ref handleIterable); - - return false; - } - - /// <summary> - /// Force this handle to be dirty, without reprotecting. - /// </summary> - public void ForceDirty() - { - Dirty = true; - } - - /// <summary> - /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write. - /// </summary> - /// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param> - /// <param name="consecutiveCheck">True if this reprotect is the result of consecutive dirty checks</param> - public void Reprotect(bool asDirty, bool consecutiveCheck = false) - { - if (_volatile) return; - - Dirty = asDirty; - - bool protectionChanged = false; - - lock (_tracking.TrackingLock) - { - foreach (VirtualRegion region in _regions) - { - protectionChanged |= region.UpdateProtection(); - } - } - - if (!protectionChanged) - { - // Counteract the check count being incremented when this handle was forced dirty. - // It doesn't count for protected write tracking. - - _checkCount--; - } - else if (!asDirty) - { - if (consecutiveCheck || (_checkCount > 0 && _checkCount < CheckCountForInfrequent)) - { - if (++_volatileCount >= VolatileThreshold && _preAction == null) - { - _volatile = true; - return; - } - } - else - { - _volatileCount = 0; - } - - _checkCount = 0; - } - } - - /// <summary> - /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write. - /// </summary> - /// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param> - public void Reprotect(bool asDirty = false) - { - Reprotect(asDirty, false); - } - - /// <summary> - /// Register an action to perform when the tracked region is read or written. - /// The action is automatically removed after it runs. - /// </summary> - /// <param name="action">Action to call on read or write</param> - public void RegisterAction(RegionSignal action) - { - ClearVolatile(); - - lock (_preActionLock) - { - RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action); - - if (lastAction == null && action != lastAction) - { - lock (_tracking.TrackingLock) - { - foreach (VirtualRegion region in _regions) - { - region.UpdateProtection(); - } - } - } - } - } - - /// <summary> - /// Register an action to perform when a precise access occurs (one with exact address and size). - /// If the action returns true, read/write tracking are skipped. - /// </summary> - /// <param name="action">Action to call on read or write</param> - public void RegisterPreciseAction(PreciseRegionSignal action) - { - _preciseAction = action; - } - - /// <summary> - /// Register an action to perform when the region is written to. - /// This action will not be removed when it is called - it is called each time the dirty flag is set. - /// </summary> - /// <param name="action">Action to call on dirty</param> - public void RegisterDirtyEvent(Action action) - { - _onDirty += action; - } - - /// <summary> - /// Add a child virtual region to this handle. - /// </summary> - /// <param name="region">Virtual region to add as a child</param> - internal void AddChild(VirtualRegion region) - { - _regions.Add(region); - } - - /// <summary> - /// Signal that this handle has been mapped or unmapped. - /// </summary> - /// <param name="mapped">True if the handle has been mapped, false if unmapped</param> - internal void SignalMappingChanged(bool mapped) - { - if (Unmapped == mapped) - { - Unmapped = !mapped; - - if (Unmapped) - { - ClearVolatile(); - Dirty = false; - } - } - } - - /// <summary> - /// Check if this region overlaps with another. - /// </summary> - /// <param name="address">Base address</param> - /// <param name="size">Size of the region</param> - /// <returns>True if overlapping, false otherwise</returns> - public bool OverlapsWith(ulong address, ulong size) - { - return Address < address + size && address < EndAddress; - } - - /// <summary> - /// Dispose the handle. Within the tracking lock, this removes references from virtual regions. - /// </summary> - public void Dispose() - { - ObjectDisposedException.ThrowIf(_disposed, this); - - _disposed = true; - - lock (_tracking.TrackingLock) - { - foreach (VirtualRegion region in _regions) - { - region.RemoveHandle(this); - } - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/RegionSignal.cs b/Ryujinx.Memory/Tracking/RegionSignal.cs deleted file mode 100644 index c8a28d7d..00000000 --- a/Ryujinx.Memory/Tracking/RegionSignal.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Memory.Tracking -{ - public delegate void RegionSignal(ulong address, ulong size); -} diff --git a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs deleted file mode 100644 index 4acddefa..00000000 --- a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A MultiRegionHandle that attempts to segment a region's handles into the regions requested - /// to avoid iterating over granular chunks for canonically large regions. - /// If minimum granularity is to be expected, use MultiRegionHandle. - /// </summary> - public class SmartMultiRegionHandle : IMultiRegionHandle - { - /// <summary> - /// A list of region handles starting at each granularity size increment. - /// </summary> - private readonly RegionHandle[] _handles; - private readonly ulong _address; - private readonly ulong _granularity; - private readonly ulong _size; - private MemoryTracking _tracking; - private readonly int _id; - - public bool Dirty { get; private set; } = true; - - internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id) - { - // For this multi-region handle, the handle list starts empty. - // As regions are queried, they are added to the _handles array at their start index. - // When a region being added overlaps another, the existing region is split. - // A query can therefore scan multiple regions, though with no overlaps they can cover a large area. - - _tracking = tracking; - _handles = new RegionHandle[size / granularity]; - _granularity = granularity; - - _address = address; - _size = size; - _id = id; - } - - public void SignalWrite() - { - Dirty = true; - } - - public void ForceDirty(ulong address, ulong size) - { - foreach (var handle in _handles) - { - if (handle != null && handle.OverlapsWith(address, size)) - { - handle.ForceDirty(); - } - } - } - - public void RegisterAction(RegionSignal action) - { - foreach (var handle in _handles) - { - if (handle != null) - { - handle?.RegisterAction((address, size) => action(handle.Address, handle.Size)); - } - } - } - - public void RegisterPreciseAction(PreciseRegionSignal action) - { - foreach (var handle in _handles) - { - if (handle != null) - { - handle?.RegisterPreciseAction((address, size, write) => action(handle.Address, handle.Size, write)); - } - } - } - - public void QueryModified(Action<ulong, ulong> modifiedAction) - { - if (!Dirty) - { - return; - } - - Dirty = false; - - QueryModified(_address, _size, modifiedAction); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ulong HandlesToBytes(int handles) - { - return (ulong)handles * _granularity; - } - - private void SplitHandle(int handleIndex, int splitIndex) - { - RegionHandle handle = _handles[handleIndex]; - ulong address = _address + HandlesToBytes(handleIndex); - ulong size = HandlesToBytes(splitIndex - handleIndex); - - // First, the target handle must be removed. Its data can still be used to determine the new handles. - RegionSignal signal = handle.PreAction; - handle.Dispose(); - - RegionHandle splitLow = _tracking.BeginTracking(address, size, _id); - splitLow.Parent = this; - if (signal != null) - { - splitLow.RegisterAction(signal); - } - _handles[handleIndex] = splitLow; - - RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id); - splitHigh.Parent = this; - if (signal != null) - { - splitHigh.RegisterAction(signal); - } - _handles[splitIndex] = splitHigh; - } - - private void CreateHandle(int startHandle, int lastHandle) - { - ulong startAddress = _address + HandlesToBytes(startHandle); - - // Scan for the first handle before us. If it's overlapping us, it must be split. - for (int i = startHandle - 1; i >= 0; i--) - { - RegionHandle handle = _handles[i]; - if (handle != null) - { - if (handle.EndAddress > startAddress) - { - SplitHandle(i, startHandle); - return; // The remainer of this handle should be filled in later on. - } - break; - } - } - - // Scan for handles after us. We should create a handle that goes up to this handle's start point, if present. - for (int i = startHandle + 1; i <= lastHandle; i++) - { - RegionHandle handle = _handles[i]; - if (handle != null) - { - // Fill up to the found handle. - handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id); - handle.Parent = this; - _handles[startHandle] = handle; - return; - } - } - - // Can fill the whole range. - _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id); - _handles[startHandle].Parent = this; - } - - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) - { - int startHandle = (int)((address - _address) / _granularity); - int lastHandle = (int)((address + (size - 1) - _address) / _granularity); - - ulong rgStart = _address + (ulong)startHandle * _granularity; - ulong rgSize = 0; - - ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity; - - int i = startHandle; - - while (i <= lastHandle) - { - RegionHandle handle = _handles[i]; - if (handle == null) - { - // Missing handle. A new handle must be created. - CreateHandle(i, lastHandle); - handle = _handles[i]; - } - - if (handle.EndAddress > endAddress) - { - // End address of handle is beyond the end of the search. Force a split. - SplitHandle(i, lastHandle + 1); - handle = _handles[i]; - } - - if (handle.Dirty) - { - rgSize += handle.Size; - handle.Reprotect(); - } - else - { - // Submit the region scanned so far as dirty - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - rgSize = 0; - } - rgStart = handle.EndAddress; - } - - i += (int)(handle.Size / _granularity); - } - - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - } - } - - public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) - { - int startHandle = (int)((address - _address) / _granularity); - int lastHandle = (int)((address + (size - 1) - _address) / _granularity); - - ulong rgStart = _address + (ulong)startHandle * _granularity; - ulong rgSize = 0; - - ulong endAddress = _address + ((ulong)lastHandle + 1) * _granularity; - - int i = startHandle; - - while (i <= lastHandle) - { - RegionHandle handle = _handles[i]; - if (handle == null) - { - // Missing handle. A new handle must be created. - CreateHandle(i, lastHandle); - handle = _handles[i]; - } - - if (handle.EndAddress > endAddress) - { - // End address of handle is beyond the end of the search. Force a split. - SplitHandle(i, lastHandle + 1); - handle = _handles[i]; - } - - if (handle.Dirty && sequenceNumber != handle.SequenceNumber) - { - rgSize += handle.Size; - handle.Reprotect(); - } - else - { - // Submit the region scanned so far as dirty - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - rgSize = 0; - } - rgStart = handle.EndAddress; - } - - handle.SequenceNumber = sequenceNumber; - - i += (int)(handle.Size / _granularity); - } - - if (rgSize != 0) - { - modifiedAction(rgStart, rgSize); - } - } - - public void Dispose() - { - foreach (var handle in _handles) - { - handle?.Dispose(); - } - } - } -} diff --git a/Ryujinx.Memory/Tracking/VirtualRegion.cs b/Ryujinx.Memory/Tracking/VirtualRegion.cs deleted file mode 100644 index 9651426b..00000000 --- a/Ryujinx.Memory/Tracking/VirtualRegion.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Ryujinx.Memory.Range; -using System.Collections.Generic; - -namespace Ryujinx.Memory.Tracking -{ - /// <summary> - /// A region of virtual memory. - /// </summary> - class VirtualRegion : AbstractRegion - { - public List<RegionHandle> Handles = new List<RegionHandle>(); - - private readonly MemoryTracking _tracking; - private MemoryPermission _lastPermission; - - public VirtualRegion(MemoryTracking tracking, ulong address, ulong size, MemoryPermission lastPermission = MemoryPermission.Invalid) : base(address, size) - { - _lastPermission = lastPermission; - _tracking = tracking; - } - - /// <inheritdoc/> - public override void Signal(ulong address, ulong size, bool write, int? exemptId) - { - IList<RegionHandle> handles = Handles; - - for (int i = 0; i < handles.Count; i++) - { - if (exemptId == null || handles[i].Id != exemptId.Value) - { - handles[i].Signal(address, size, write, ref handles); - } - } - - UpdateProtection(); - } - - /// <inheritdoc/> - public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId) - { - IList<RegionHandle> handles = Handles; - - bool allPrecise = true; - - for (int i = 0; i < handles.Count; i++) - { - if (exemptId == null || handles[i].Id != exemptId.Value) - { - allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); - } - } - - // Only update protection if a regular signal handler was called. - // This allows precise actions to skip reprotection costs if they want (they can still do it manually). - if (!allPrecise) - { - UpdateProtection(); - } - } - - /// <summary> - /// Signal that this region has been mapped or unmapped. - /// </summary> - /// <param name="mapped">True if the region has been mapped, false if unmapped</param> - public void SignalMappingChanged(bool mapped) - { - _lastPermission = MemoryPermission.Invalid; - - foreach (RegionHandle handle in Handles) - { - handle.SignalMappingChanged(mapped); - } - } - - /// <summary> - /// Gets the strictest permission that the child handles demand. Assumes that the tracking lock has been obtained. - /// </summary> - /// <returns>Protection level that this region demands</returns> - public MemoryPermission GetRequiredPermission() - { - // Start with Read/Write, each handle can strip off permissions as necessary. - // Assumes the tracking lock has already been obtained. - - MemoryPermission result = MemoryPermission.ReadAndWrite; - - foreach (var handle in Handles) - { - result &= handle.RequiredPermission; - if (result == 0) return result; - } - return result; - } - - /// <summary> - /// Updates the protection for this virtual region. - /// </summary> - public bool UpdateProtection() - { - MemoryPermission permission = GetRequiredPermission(); - - if (_lastPermission != permission) - { - _tracking.ProtectVirtualRegion(this, permission); - _lastPermission = permission; - - return true; - } - - return false; - } - - /// <summary> - /// Removes a handle from this virtual region. If there are no handles left, this virtual region is removed. - /// </summary> - /// <param name="handle">Handle to remove</param> - public void RemoveHandle(RegionHandle handle) - { - lock (_tracking.TrackingLock) - { - Handles.Remove(handle); - UpdateProtection(); - if (Handles.Count == 0) - { - _tracking.RemoveVirtual(this); - } - } - } - - public override INonOverlappingRange Split(ulong splitAddress) - { - VirtualRegion newRegion = new VirtualRegion(_tracking, splitAddress, EndAddress - splitAddress, _lastPermission); - Size = splitAddress - Address; - - // The new region inherits all of our parents. - newRegion.Handles = new List<RegionHandle>(Handles); - foreach (var parent in Handles) - { - parent.AddChild(newRegion); - } - - return newRegion; - } - } -} |
