Memory Changes part 2 (ryubing/ryujinx!123)

See merge request ryubing/ryujinx!123
This commit is contained in:
LotP
2025-08-25 17:44:15 -05:00
parent d499449f57
commit 50ab108ee1
90 changed files with 2133 additions and 1159 deletions

View File

@@ -10,7 +10,7 @@ namespace Ryujinx.Memory.Range
/// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps.
/// </summary>
/// <typeparam name="T">Type of the range.</typeparam>
public class NonOverlappingRangeList<T> : RangeListBase<T> where T : INonOverlappingRange
public unsafe class NonOverlappingRangeList<T> : RangeListBase<T> where T : class, INonOverlappingRange
{
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
private readonly Dictionary<ulong, RangeItem<T>> _fastQuickAccess = new(AddressEqualityComparer.Comparer);
@@ -196,6 +196,16 @@ namespace Ryujinx.Memory.Range
int startIndex = BinarySearch(startItem.Address);
int endIndex = BinarySearch(endItem.Address);
for (int i = startIndex; i <= endIndex; i++)
{
_quickAccess.Remove(Items[i].Address);
foreach (ulong addr in Items[i].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
}
if (endIndex < Count - 1)
{
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
@@ -213,17 +223,6 @@ namespace Ryujinx.Memory.Range
}
Count -= endIndex - startIndex + 1;
while (startItem != endItem.Next)
{
_quickAccess.Remove(startItem.Address);
foreach (ulong addr in startItem.QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
startItem = startItem.Next;
}
}
/// <summary>
@@ -240,19 +239,22 @@ namespace Ryujinx.Memory.Range
return;
}
RangeItem<T> startItem = Items[startIndex];
int endIndex = startIndex;
while (startItem is not null && startItem.Address < address + size)
while (Items[endIndex] is not null && Items[endIndex].Address < address + size)
{
_quickAccess.Remove(startItem.Address);
foreach (ulong addr in startItem.QuickAccessAddresses)
_quickAccess.Remove(Items[endIndex].Address);
foreach (ulong addr in Items[endIndex].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
_fastQuickAccess.Remove(addr);
}
startItem = startItem.Next;
if (endIndex == Count - 1)
{
break;
}
endIndex++;
}

View File

@@ -6,37 +6,6 @@ using System.Threading;
namespace Ryujinx.Memory.Range
{
public class RangeItem<TValue>(TValue value) where TValue : IRange
{
public RangeItem<TValue> Next;
public RangeItem<TValue> Previous;
public readonly ulong Address = value.Address;
public readonly ulong EndAddress = value.Address + value.Size;
public readonly TValue Value = value;
public readonly List<ulong> QuickAccessAddresses = [];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool OverlapsWith(ulong address, ulong endAddress)
{
return Address < endAddress && address < EndAddress;
}
}
class AddressEqualityComparer : IEqualityComparer<ulong>
{
public bool Equals(ulong u1, ulong u2)
{
return u1 == u2;
}
public int GetHashCode(ulong value) => (int)(value >> 5);
public static readonly AddressEqualityComparer Comparer = new();
}
/// <summary>
/// Result of an Overlaps Finder function. WARNING: if the result is from the optimized
/// Overlaps Finder, the StartIndex will be -1 even when the result isn't empty
@@ -209,43 +178,46 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void RemoveRange(RangeItem<T> startItem, RangeItem<T> endItem)
{
if (endItem.Next is not null)
if (startItem is null)
{
endItem.Next.Previous = startItem.Previous;
return;
}
if (startItem == endItem)
{
Remove(startItem.Value);
return;
}
if (startItem.Previous is not null)
{
startItem.Previous.Next = endItem.Next;
}
int startIndex = BinarySearch(startItem.Address);
int endIndex = BinarySearch(endItem.Address);
RangeItem<T> current = startItem;
while (current != endItem.Next)
for (int i = startIndex; i <= endIndex; i++)
{
foreach (ulong address in current.QuickAccessAddresses)
_quickAccess.Remove(Items[i].Address);
foreach (ulong addr in Items[i].QuickAccessAddresses)
{
_quickAccess.Remove(address);
_quickAccess.Remove(addr);
}
current = current.Next;
}
RangeItem<T>[] array = [];
OverlapResult<T> overlapResult = FindOverlaps(startItem.Address, endItem.EndAddress, ref array);
if (endIndex < Count - 1)
{
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
}
if (startIndex > 0)
{
Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null;
}
if (overlapResult.EndIndex < Count)
if (endIndex < Count - 1)
{
Array.Copy(Items, overlapResult.EndIndex, Items, overlapResult.StartIndex, Count - overlapResult.EndIndex);
Count -= overlapResult.Count;
}
else if (overlapResult.EndIndex == Count)
{
Count = overlapResult.StartIndex;
}
else
{
Debug.Assert(false);
Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1);
}
Count -= endIndex - startIndex + 1;
}
/// <summary>

View File

@@ -4,9 +4,40 @@ using System.Runtime.CompilerServices;
namespace Ryujinx.Memory.Range
{
public abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
public class RangeItem<TValue>(TValue value) where TValue : IRange
{
protected const int BackingInitialSize = 1024;
public RangeItem<TValue> Next;
public RangeItem<TValue> Previous;
public readonly ulong Address = value.Address;
public readonly ulong EndAddress = value.Address + value.Size;
public readonly TValue Value = value;
public readonly List<ulong> QuickAccessAddresses = [];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool OverlapsWith(ulong address, ulong endAddress)
{
return Address < endAddress && address < EndAddress;
}
}
class AddressEqualityComparer : IEqualityComparer<ulong>
{
public bool Equals(ulong u1, ulong u2)
{
return u1 == u2;
}
public int GetHashCode(ulong value) => (int)(value >> 5);
public static readonly AddressEqualityComparer Comparer = new();
}
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
{
private const int BackingInitialSize = 1024;
protected RangeItem<T>[] Items;
protected readonly int BackingGrowthSize;

View File

@@ -36,10 +36,12 @@ namespace Ryujinx.Memory.WindowsShared
class RangeNode<T> : IntrusiveRedBlackTreeNode<RangeNode<T>>, IComparable<RangeNode<T>>, IComparable<ulong>
{
public ulong Start { get; }
public ulong Start { get; private set; }
public ulong End { get; private set; }
public T Value { get; }
public T Value { get; private set; }
public RangeNode() { }
public RangeNode(ulong start, ulong end, T value)
{
Start = start;
@@ -47,6 +49,22 @@ namespace Ryujinx.Memory.WindowsShared
Value = value;
}
public RangeNode<T> Init(ulong start, ulong end, T value)
{
Start = start;
End = end;
Value = value;
Color = true;
Left = null;
Right = null;
Parent = null;
Predecessor = null;
Successor = null;
return this;
}
public void Extend(ulong sizeDelta)
{
End += sizeDelta;

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Collections;
using Ryujinx.Common.Memory.PartialUnmaps;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
@@ -8,6 +9,24 @@ using System.Threading;
namespace Ryujinx.Memory.WindowsShared
{
public class ObjectPool<T>
{
private readonly Stack<T> _objects;
private readonly Func<T> _objectGenerator;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
_objects = new Stack<T>();
}
public T Get() => _objects.Count > 0 ? _objects.Pop() : _objectGenerator();
public void Return(T item) => _objects.Push(item);
public void Clear() => _objects.Clear();
}
/// <summary>
/// Windows memory placeholder manager.
/// </summary>
@@ -18,6 +37,7 @@ namespace Ryujinx.Memory.WindowsShared
private readonly MappingTree<ulong> _mappings;
private readonly MappingTree<MemoryPermission> _protections;
private readonly ObjectPool<RangeNode<MemoryPermission>> _protectionObjectPool;
private readonly nint _partialUnmapStatePtr;
private readonly Thread _partialUnmapTrimThread;
@@ -28,6 +48,8 @@ namespace Ryujinx.Memory.WindowsShared
{
_mappings = new MappingTree<ulong>();
_protections = new MappingTree<MemoryPermission>();
_protectionObjectPool = new ObjectPool<RangeNode<MemoryPermission>>(() => new RangeNode<MemoryPermission>());
_partialUnmapStatePtr = PartialUnmapState.GlobalState;
@@ -70,12 +92,12 @@ namespace Ryujinx.Memory.WindowsShared
{
lock (_mappings)
{
_mappings.Add(new RangeNode<ulong>(address, address + size, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(address, address + size, ulong.MaxValue));
}
lock (_protections)
{
_protections.Add(new RangeNode<MemoryPermission>(address, address + size, MemoryPermission.None));
_protections.Add(_protectionObjectPool.Get().Init(address, address + size, MemoryPermission.None));
}
}
@@ -214,8 +236,8 @@ namespace Ryujinx.Memory.WindowsShared
(nint)size,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
_mappings.Add(new RangeNode<ulong>().Init(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart)));
}
else if (overlapStartsBefore)
{
@@ -226,7 +248,7 @@ namespace Ryujinx.Memory.WindowsShared
(nint)overlappedSize,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(overlapStart, address, overlapValue));
_mappings.Add(new RangeNode<ulong>().Init(overlapStart, address, overlapValue));
}
else if (overlapEndsAfter)
{
@@ -237,10 +259,10 @@ namespace Ryujinx.Memory.WindowsShared
(nint)overlappedSize,
AllocationType.Release | AllocationType.PreservePlaceholder));
_mappings.Add(new RangeNode<ulong>(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
_mappings.Add(new RangeNode<ulong>().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize)));
}
_mappings.Add(new RangeNode<ulong>(address, endAddress, backingOffset));
_mappings.Add(new RangeNode<ulong>().Init(address, endAddress, backingOffset));
}
}
@@ -306,7 +328,7 @@ namespace Ryujinx.Memory.WindowsShared
lock (_mappings)
{
_mappings.Remove(overlap);
_mappings.Add(new RangeNode<ulong>(overlap.Start, overlap.End, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(overlap.Start, overlap.End, ulong.MaxValue));
}
bool overlapStartsBefore = overlap.Start < startAddress;
@@ -433,7 +455,7 @@ namespace Ryujinx.Memory.WindowsShared
unmappedCount++;
}
_mappings.Add(new RangeNode<ulong>(address, endAddress, ulong.MaxValue));
_mappings.Add(new RangeNode<ulong>().Init(address, endAddress, ulong.MaxValue));
}
if (unmappedCount > 1)
@@ -628,14 +650,16 @@ namespace Ryujinx.Memory.WindowsShared
{
if (startAddress > protAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(protAddress, startAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(protAddress, startAddress, protPermission));
}
if (endAddress < protEndAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission));
}
}
_protectionObjectPool.Return(protection);
if (node.End >= endAddress)
{
@@ -643,7 +667,7 @@ namespace Ryujinx.Memory.WindowsShared
}
}
_protections.Add(new RangeNode<MemoryPermission>(startAddress, endAddress, permission));
_protections.Add(_protectionObjectPool.Get().Init(startAddress, endAddress, permission));
}
}
@@ -674,14 +698,16 @@ namespace Ryujinx.Memory.WindowsShared
if (address > protAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(protAddress, address, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(protAddress, address, protPermission));
}
if (endAddress < protEndAddress)
{
_protections.Add(new RangeNode<MemoryPermission>(endAddress, protEndAddress, protPermission));
_protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission));
}
_protectionObjectPool.Return(protection);
if (node.End >= endAddress)
{
break;