Add support to IVirtualMemoryManager for zero-copy reads (#6251)

* - WritableRegion: enable wrapping IMemoryOwner<byte>
- IVirtualMemoryManager impls of GetWritableRegion() use pooled memory when region is non-contiguous.
- IVirtualMemoryManager: add GetReadOnlySequence() and impls
- ByteMemoryPool: add new method RentCopy()
- ByteMemoryPool: make class static, remove ctor and singleton field from earlier impl

* - BytesReadOnlySequenceSegment: move from Ryujinx.Common.Memory to Ryujinx.Memory
- BytesReadOnlySequenceSegment: add IsContiguousWith() and Replace() methods
- VirtualMemoryManagerBase:
  - remove generic type parameters, instead use ulong for virtual addresses and nuint for host/physical addresses
  - implement IWritableBlock
  - add virtual GetReadOnlySequence() with coalescing of contiguous segments
  - add virtual GetSpan()
  - add virtual GetWritableRegion()
  - add abstract IsMapped()
  - add virtual MapForeign(ulong, nuint, ulong)
  - add virtual Read<T>()
  - add virtual Read(ulong, Span<byte>)
  - add virtual ReadTracked<T>()
  - add virtual SignalMemoryTracking()
  - add virtual Write()
  - add virtual Write<T>()
  - add virtual WriteUntracked()
  - add virtual WriteWithRedundancyCheck()
- VirtualMemoryManagerRefCountedBase: remove generic type parameters
- AddressSpaceManager: remove redundant methods, add required overrides
- HvMemoryManager: remove redundant methods, add required overrides, add overrides for _invalidAccessHandler handling
- MemoryManager: remove redundant methods, add required overrides, add overrides for _invalidAccessHandler handling
- MemoryManagerHostMapped: remove redundant methods, add required overrides, add overrides for _invalidAccessHandler handling
- NativeMemoryManager: add get properties for Pointer and Length
- throughout: removed invalid <inheritdoc/> comments

* make HvMemoryManager class sealed

* remove unused method

* adjust MemoryManagerHostTracked

* let MemoryManagerHostTracked override WriteImpl()
This commit is contained in:
jhorv
2024-04-04 21:23:03 -04:00
committed by GitHub
parent 8e74fa3456
commit 5def0429f8
14 changed files with 635 additions and 819 deletions

View File

@@ -3,6 +3,7 @@ using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.Jit
/// <summary>
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
/// </summary>
public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase<ulong, ulong>, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly bool _unsafeMode;
@@ -96,12 +97,6 @@ namespace Ryujinx.Cpu.Jit
Tracking.Map(va, size);
}
/// <inheritdoc/>
public void MapForeign(ulong va, nuint hostPointer, ulong size)
{
throw new NotSupportedException();
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
{
@@ -138,8 +133,7 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public T Read<T>(ulong va) where T : unmanaged
public override T Read<T>(ulong va)
{
try
{
@@ -158,14 +152,11 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public T ReadTracked<T>(ulong va) where T : unmanaged
public override T ReadTracked<T>(ulong va)
{
try
{
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
return Read<T>(va);
return base.ReadTracked<T>(va);
}
catch (InvalidMemoryRegionException)
{
@@ -178,7 +169,6 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public override void Read(ulong va, Span<byte> data)
{
try
@@ -196,9 +186,7 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public void Write<T>(ulong va, T value) where T : unmanaged
public override void Write<T>(ulong va, T value)
{
try
{
@@ -215,8 +203,7 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public void Write(ulong va, ReadOnlySpan<byte> data)
public override void Write(ulong va, ReadOnlySpan<byte> data)
{
try
{
@@ -233,8 +220,7 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
public override void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
try
{
@@ -251,8 +237,7 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
{
try
{
@@ -279,8 +264,21 @@ namespace Ryujinx.Cpu.Jit
}
}
/// <inheritdoc/>
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
{
if (tracked)
{
SignalMemoryTracking(va, (ulong)size, write: false);
}
else
{
AssertMapped(va, (ulong)size);
}
return new ReadOnlySequence<byte>(_addressSpace.Mirror.GetMemory(va, size));
}
public override ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
{
if (tracked)
{
@@ -294,8 +292,7 @@ namespace Ryujinx.Cpu.Jit
return _addressSpace.Mirror.GetSpan(va, size);
}
/// <inheritdoc/>
public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
{
if (tracked)
{
@@ -309,7 +306,6 @@ namespace Ryujinx.Cpu.Jit
return _addressSpace.Mirror.GetWritableRegion(va, size);
}
/// <inheritdoc/>
public ref T GetRef<T>(ulong va) where T : unmanaged
{
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
@@ -317,9 +313,8 @@ namespace Ryujinx.Cpu.Jit
return ref _addressSpace.Mirror.GetRef<T>(va);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsMapped(ulong va)
public override bool IsMapped(ulong va)
{
return ValidateAddress(va) && _pages.IsMapped(va);
}
@@ -390,11 +385,10 @@ namespace Ryujinx.Cpu.Jit
return _pageTable.Read(va) + (va & PageMask);
}
/// <inheritdoc/>
/// <remarks>
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
/// </remarks>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
AssertValidAddressAndSize(va, size);
@@ -407,23 +401,6 @@ namespace Ryujinx.Cpu.Jit
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
}
/// <summary>
/// Computes the number of pages in a virtual address range.
/// </summary>
/// <param name="va">Virtual address of the range</param>
/// <param name="size">Size of the range</param>
/// <param name="startVa">The virtual address of the beginning of the first page</param>
/// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetPagesCount(ulong va, ulong size, out ulong startVa)
{
// WARNING: Always check if ulong does not overflow during the operations.
startVa = va & ~(ulong)PageMask;
ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
return (int)(vaSpan / PageSize);
}
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
@@ -470,10 +447,16 @@ namespace Ryujinx.Cpu.Jit
_memoryEh.Dispose();
}
protected override Span<byte> GetPhysicalAddressSpan(ulong pa, int size)
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
=> _addressSpace.Mirror.GetMemory(pa, size);
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
=> _addressSpace.Mirror.GetSpan(pa, size);
protected override ulong TranslateVirtualAddressForRead(ulong va)
=> va;
protected override nuint TranslateVirtualAddressChecked(ulong va)
=> (nuint)GetPhysicalAddressChecked(va);
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
=> (nuint)GetPhysicalAddressInternal(va);
}
}