| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- 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)
- {
- _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);
- 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);
- 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);
- 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();
- }
- }
- }
- }
|