diff --git a/src/Ryujinx.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs
index 6664ed134..08595864a 100644
--- a/src/Ryujinx.Cpu/AddressSpace.cs
+++ b/src/Ryujinx.Cpu/AddressSpace.cs
@@ -5,6 +5,8 @@ namespace Ryujinx.Cpu
{
public class AddressSpace : IDisposable
{
+ private const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
+
private readonly MemoryBlock _backingMemory;
public MemoryBlock Base { get; }
@@ -25,28 +27,42 @@ namespace Ryujinx.Cpu
{
addressSpace = null;
- const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
+ MemoryBlock baseMemory = null;
+ MemoryBlock mirrorMemory = null;
+
+ try
+ {
+ baseMemory = new MemoryBlock(asSize, AsFlags);
+ mirrorMemory = new MemoryBlock(asSize, AsFlags);
+ addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, asSize);
+ }
+ catch (SystemException)
+ {
+ baseMemory?.Dispose();
+ mirrorMemory?.Dispose();
+ }
+
+ return addressSpace != null;
+ }
+
+ public static bool TryCreateWithoutMirror(ulong asSize, out MemoryBlock addressSpace)
+ {
+ addressSpace = null;
ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36);
// Attempt to create the address space with expected size or try to reduce it until it succeed.
- for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize >>= 1)
+ for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize -= 0x100000000UL)
{
- MemoryBlock baseMemory = null;
- MemoryBlock mirrorMemory = null;
-
try
{
- baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
- mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags);
- addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize);
+ MemoryBlock baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
+ addressSpace = baseMemory;
break;
}
catch (SystemException)
{
- baseMemory?.Dispose();
- mirrorMemory?.Dispose();
}
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs
new file mode 100644
index 000000000..62cacd8fb
--- /dev/null
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs
@@ -0,0 +1,330 @@
+using ARMeilleure.Memory;
+using Ryujinx.Memory;
+using Ryujinx.Memory.Range;
+using Ryujinx.Memory.Tracking;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Cpu.Jit
+{
+ ///
+ /// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
+ ///
+ public sealed class MemoryManagerHostNoMirror : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+ {
+ private readonly InvalidAccessHandler _invalidAccessHandler;
+ private readonly bool _unsafeMode;
+
+ private readonly MemoryBlock _addressSpace;
+ private readonly MemoryBlock _backingMemory;
+ private readonly PageTable _pageTable;
+
+ public int AddressSpaceBits { get; }
+ protected override ulong AddressSpaceSize { get; }
+
+ private readonly MemoryEhMeilleure _memoryEh;
+
+ private readonly ManagedPageFlags _pages;
+
+ ///
+ public bool UsesPrivateAllocations => false;
+
+ public IntPtr PageTablePointer => _addressSpace.Pointer;
+
+ public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostMappedUnsafe : MemoryManagerType.HostMapped;
+
+ public MemoryTracking Tracking { get; }
+
+ public event Action UnmapEvent;
+
+ ///
+ /// Creates a new instance of the host mapped memory manager.
+ ///
+ /// Address space instance to use
+ /// True if unmanaged access should not be masked (unsafe), false otherwise.
+ /// Optional function to handle invalid memory accesses
+ public MemoryManagerHostNoMirror(
+ MemoryBlock addressSpace,
+ MemoryBlock backingMemory,
+ bool unsafeMode,
+ InvalidAccessHandler invalidAccessHandler)
+ {
+ _addressSpace = addressSpace;
+ _backingMemory = backingMemory;
+ _pageTable = new PageTable();
+ _invalidAccessHandler = invalidAccessHandler;
+ _unsafeMode = unsafeMode;
+ AddressSpaceSize = addressSpace.Size;
+
+ ulong asSize = PageSize;
+ int asBits = PageBits;
+
+ while (asSize < addressSpace.Size)
+ {
+ asSize <<= 1;
+ asBits++;
+ }
+
+ AddressSpaceBits = asBits;
+
+ _pages = new ManagedPageFlags(asBits);
+
+ Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler);
+ _memoryEh = new MemoryEhMeilleure(addressSpace, null, Tracking);
+ }
+
+ ///
+ /// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
+ ///
+ /// Virtual address of the range
+ /// Size of the range in bytes
+ private void AssertMapped(ulong va, ulong size)
+ {
+ if (!ValidateAddressAndSize(va, size) || !_pages.IsRangeMapped(va, size))
+ {
+ throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
+ }
+ }
+
+ ///
+ public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
+ {
+ AssertValidAddressAndSize(va, size);
+
+ _addressSpace.MapView(_backingMemory, pa, va, size);
+ _pages.AddMapping(va, size);
+ PtMap(va, pa, size);
+
+ Tracking.Map(va, size);
+ }
+
+ private void PtMap(ulong va, ulong pa, ulong size)
+ {
+ while (size != 0)
+ {
+ _pageTable.Map(va, pa);
+
+ va += PageSize;
+ pa += PageSize;
+ size -= PageSize;
+ }
+ }
+
+ ///
+ public void Unmap(ulong va, ulong size)
+ {
+ AssertValidAddressAndSize(va, size);
+
+ UnmapEvent?.Invoke(va, size);
+ Tracking.Unmap(va, size);
+
+ _pages.RemoveMapping(va, size);
+ PtUnmap(va, size);
+ _addressSpace.UnmapView(_backingMemory, va, size);
+ }
+
+ private void PtUnmap(ulong va, ulong size)
+ {
+ while (size != 0)
+ {
+ _pageTable.Unmap(va);
+
+ va += PageSize;
+ size -= PageSize;
+ }
+ }
+
+ ///
+ public void Reprotect(ulong va, ulong size, MemoryPermission permission)
+ {
+ }
+
+ public ref T GetRef(ulong va) where T : unmanaged
+ {
+ if (!IsContiguous(va, Unsafe.SizeOf()))
+ {
+ ThrowMemoryNotContiguous();
+ }
+
+ SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true);
+
+ return ref _backingMemory.GetRef(GetPhysicalAddressChecked(va));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool IsMapped(ulong va)
+ {
+ return ValidateAddress(va) && _pages.IsMapped(va);
+ }
+
+ ///
+ public bool IsRangeMapped(ulong va, ulong size)
+ {
+ AssertValidAddressAndSize(va, size);
+
+ return _pages.IsRangeMapped(va, size);
+ }
+
+ ///
+ public IEnumerable GetHostRegions(ulong va, ulong size)
+ {
+ if (size == 0)
+ {
+ return Enumerable.Empty();
+ }
+
+ var guestRegions = GetPhysicalRegionsImpl(va, size);
+ if (guestRegions == null)
+ {
+ return null;
+ }
+
+ var regions = new HostMemoryRange[guestRegions.Count];
+
+ for (int i = 0; i < regions.Length; i++)
+ {
+ var guestRegion = guestRegions[i];
+ IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
+ regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
+ }
+
+ return regions;
+ }
+
+ ///
+ public IEnumerable GetPhysicalRegions(ulong va, ulong size)
+ {
+ if (size == 0)
+ {
+ return Enumerable.Empty();
+ }
+
+ return GetPhysicalRegionsImpl(va, size);
+ }
+
+ private List GetPhysicalRegionsImpl(ulong va, ulong size)
+ {
+ if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
+ {
+ return null;
+ }
+
+ int pages = GetPagesCount(va, (uint)size, out va);
+
+ var regions = new List();
+
+ ulong regionStart = GetPhysicalAddressInternal(va);
+ ulong regionSize = PageSize;
+
+ for (int page = 0; page < pages - 1; page++)
+ {
+ if (!ValidateAddress(va + PageSize))
+ {
+ return null;
+ }
+
+ ulong newPa = GetPhysicalAddressInternal(va + PageSize);
+
+ if (GetPhysicalAddressInternal(va) + PageSize != newPa)
+ {
+ regions.Add(new MemoryRange(regionStart, regionSize));
+ regionStart = newPa;
+ regionSize = 0;
+ }
+
+ va += PageSize;
+ regionSize += PageSize;
+ }
+
+ regions.Add(new MemoryRange(regionStart, regionSize));
+
+ return regions;
+ }
+
+ private ulong GetPhysicalAddressChecked(ulong va)
+ {
+ if (!IsMapped(va))
+ {
+ ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
+ }
+
+ return GetPhysicalAddressInternal(va);
+ }
+
+ private ulong GetPhysicalAddressInternal(ulong va)
+ {
+ return _pageTable.Read(va) + (va & PageMask);
+ }
+
+ ///
+ ///
+ /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
+ ///
+ public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ {
+ AssertValidAddressAndSize(va, size);
+
+ if (precise)
+ {
+ Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
+ return;
+ }
+
+ _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
+ }
+
+ ///
+ public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
+ {
+ if (guest)
+ {
+ _addressSpace.Reprotect(va, size, protection, false);
+ }
+ else
+ {
+ _pages.TrackingReprotect(va, size, protection);
+ }
+ }
+
+ ///
+ public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags)
+ {
+ return Tracking.BeginTracking(address, size, id, flags);
+ }
+
+ ///
+ public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id, RegionFlags flags)
+ {
+ return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
+ }
+
+ ///
+ public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
+ {
+ return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
+ }
+
+ ///
+ /// Disposes of resources used by the memory manager.
+ ///
+ protected override void Destroy()
+ {
+ _addressSpace.Dispose();
+ _memoryEh.Dispose();
+ }
+
+ protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
+ => _backingMemory.GetMemory(pa, size);
+
+ protected override Span GetPhysicalAddressSpan(nuint pa, int size)
+ => _backingMemory.GetSpan(pa, size);
+
+ protected override nuint TranslateVirtualAddressChecked(ulong va)
+ => (nuint)GetPhysicalAddressChecked(va);
+
+ protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+ => (nuint)GetPhysicalAddressInternal(va);
+ }
+}
diff --git a/src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs b/src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs
index 8ad7db3f1..910880081 100644
--- a/src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs
+++ b/src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs
@@ -3,7 +3,6 @@ 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;
@@ -15,10 +14,7 @@ namespace Ryujinx.Cpu.Nce
///
public sealed class MemoryManagerNative : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
{
- private readonly InvalidAccessHandler _invalidAccessHandler;
-
private readonly MemoryBlock _addressSpace;
- private readonly MemoryBlock _addressSpaceMirror;
private readonly MemoryBlock _backingMemory;
private readonly PageTable _pageTable;
@@ -30,8 +26,6 @@ namespace Ryujinx.Cpu.Nce
///
public bool UsesPrivateAllocations => false;
- public int AddressSpaceBits { get; }
-
public IntPtr PageTablePointer => IntPtr.Zero;
public ulong ReservedSize => (ulong)_addressSpace.Pointer.ToInt64();
@@ -42,24 +36,25 @@ namespace Ryujinx.Cpu.Nce
public event Action UnmapEvent;
+ public int AddressSpaceBits { get; }
protected override ulong AddressSpaceSize { get; }
///
/// Creates a new instance of the host mapped memory manager.
///
- /// Address space instance to use
+ /// Address space memory block
/// Physical backing memory where virtual memory will be mapped to
/// Size of the address space
/// Optional function to handle invalid memory accesses
public MemoryManagerNative(
- AddressSpace addressSpace,
+ MemoryBlock addressSpace,
MemoryBlock backingMemory,
ulong addressSpaceSize,
InvalidAccessHandler invalidAccessHandler = null)
{
_backingMemory = backingMemory;
_pageTable = new PageTable();
- _invalidAccessHandler = invalidAccessHandler;
+ AddressSpaceSize = addressSpaceSize;
ulong asSize = PageSize;
int asBits = PageBits;
@@ -71,43 +66,39 @@ namespace Ryujinx.Cpu.Nce
}
AddressSpaceBits = asBits;
- AddressSpaceSize = addressSpace.AddressSpaceSize;
- _pages = new ManagedPageFlags(AddressSpaceBits);
+ _pages = new ManagedPageFlags(asBits);
- _addressSpace = addressSpace.Base;
- _addressSpaceMirror = addressSpace.Mirror;
+ _addressSpace = addressSpace;
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
_memoryEh = new MemoryEhMeilleure(asSize, Tracking);
}
- ///
- /// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
- ///
- /// Virtual address of the range
- /// Size of the range in bytes
- private void AssertMapped(ulong va, ulong size)
- {
- if (!ValidateAddressAndSize(va, size) || !_pages.IsRangeMapped(va, size))
- {
- throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
- }
- }
-
///
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
{
AssertValidAddressAndSize(va, size);
_addressSpace.MapView(_backingMemory, pa, AddressToOffset(va), size);
- _addressSpaceMirror.MapView(_backingMemory, pa, AddressToOffset(va), size);
_pages.AddMapping(va, size);
PtMap(va, pa, size);
Tracking.Map(va, size);
}
+ private void PtMap(ulong va, ulong pa, ulong size)
+ {
+ while (size != 0)
+ {
+ _pageTable.Map(va, pa);
+
+ va += PageSize;
+ pa += PageSize;
+ size -= PageSize;
+ }
+ }
+
///
public void Unmap(ulong va, ulong size)
{
@@ -119,19 +110,6 @@ namespace Ryujinx.Cpu.Nce
_pages.RemoveMapping(va, size);
PtUnmap(va, size);
_addressSpace.UnmapView(_backingMemory, AddressToOffset(va), size);
- _addressSpaceMirror.UnmapView(_backingMemory, AddressToOffset(va), size);
- }
-
- private void PtMap(ulong va, ulong pa, ulong size)
- {
- while (size != 0)
- {
- _pageTable.Map(va, pa);
-
- va += PageSize;
- pa += PageSize;
- size -= PageSize;
- }
}
private void PtUnmap(ulong va, ulong size)
@@ -151,193 +129,16 @@ namespace Ryujinx.Cpu.Nce
_addressSpace.Reprotect(AddressToOffset(va), size, permission);
}
- public override T Read(ulong va)
- {
- try
- {
- AssertMapped(va, (ulong)Unsafe.SizeOf());
-
- return _addressSpaceMirror.Read(AddressToOffset(va));
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
-
- return default;
- }
- }
-
- public override T ReadTracked(ulong va)
- {
- try
- {
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false);
-
- return Read(va);
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
-
- return default;
- }
- }
-
- public override void Read(ulong va, Span data)
- {
- try
- {
- AssertMapped(va, (ulong)data.Length);
-
- _addressSpaceMirror.Read(AddressToOffset(va), data);
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
-
- public override void Write(ulong va, T value)
- {
- try
- {
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), write: true);
-
- _addressSpaceMirror.Write(AddressToOffset(va), value);
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
- public override void Write(ulong va, ReadOnlySpan data)
- {
- try
- {
- SignalMemoryTracking(va, (ulong)data.Length, write: true);
-
- _addressSpaceMirror.Write(AddressToOffset(va), data);
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
- public override void WriteUntracked(ulong va, ReadOnlySpan data)
- {
- try
- {
- AssertMapped(va, (ulong)data.Length);
-
- _addressSpaceMirror.Write(AddressToOffset(va), data);
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
- public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
- {
- try
- {
- SignalMemoryTracking(va, (ulong)data.Length, false);
-
- Span target = _addressSpaceMirror.GetSpan(AddressToOffset(va), data.Length);
- bool changed = !data.SequenceEqual(target);
-
- if (changed)
- {
- data.CopyTo(target);
- }
-
- return changed;
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
-
- return true;
- }
- }
-
- public override ReadOnlySequence GetReadOnlySequence(ulong va, int size, bool tracked = false)
- {
- if (tracked)
- {
- SignalMemoryTracking(va, (ulong)size, write: false);
- }
- else
- {
- AssertMapped(va, (ulong)size);
- }
-
- return new ReadOnlySequence(_addressSpaceMirror.GetMemory(va, size));
- }
-
- public override ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
- {
- if (tracked)
- {
- SignalMemoryTracking(va, (ulong)size, write: false);
- }
- else
- {
- AssertMapped(va, (ulong)size);
- }
-
- if (size == 0)
- {
- return ReadOnlySpan.Empty;
- }
-
- return _addressSpaceMirror.GetSpan(AddressToOffset(va), size);
- }
-
- public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
- {
- if (tracked)
- {
- SignalMemoryTracking(va, (ulong)size, true);
- }
- else
- {
- AssertMapped(va, (ulong)size);
- }
-
- return _addressSpaceMirror.GetWritableRegion(AddressToOffset(va), size);
- }
-
- ///
public ref T GetRef(ulong va) where T : unmanaged
{
+ if (!IsContiguous(va, Unsafe.SizeOf()))
+ {
+ ThrowMemoryNotContiguous();
+ }
+
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true);
- return ref _addressSpaceMirror.GetRef(AddressToOffset(va));
+ return ref _backingMemory.GetRef(GetPhysicalAddressChecked(va));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -357,19 +158,52 @@ namespace Ryujinx.Cpu.Nce
///
public IEnumerable GetHostRegions(ulong va, ulong size)
{
- AssertValidAddressAndSize(va, size);
+ if (size == 0)
+ {
+ return Enumerable.Empty();
+ }
- return Enumerable.Repeat(new HostMemoryRange((nuint)(ulong)_addressSpaceMirror.GetPointer(AddressToOffset(va), size), size), 1);
+ var guestRegions = GetPhysicalRegionsImpl(va, size);
+ if (guestRegions == null)
+ {
+ return null;
+ }
+
+ var regions = new HostMemoryRange[guestRegions.Count];
+
+ for (int i = 0; i < regions.Length; i++)
+ {
+ var guestRegion = guestRegions[i];
+ IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
+ regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
+ }
+
+ return regions;
}
///
public IEnumerable GetPhysicalRegions(ulong va, ulong size)
{
+ if (size == 0)
+ {
+ return Enumerable.Empty();
+ }
+
+ return GetPhysicalRegionsImpl(va, size);
+ }
+
+ private List GetPhysicalRegionsImpl(ulong va, ulong size)
+ {
+ if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
+ {
+ return null;
+ }
+
int pages = GetPagesCount(va, (uint)size, out va);
var regions = new List();
- ulong regionStart = GetPhysicalAddressChecked(va);
+ ulong regionStart = GetPhysicalAddressInternal(va);
ulong regionSize = PageSize;
for (int page = 0; page < pages - 1; page++)
@@ -379,9 +213,9 @@ namespace Ryujinx.Cpu.Nce
return null;
}
- ulong newPa = GetPhysicalAddressChecked(va + PageSize);
+ ulong newPa = GetPhysicalAddressInternal(va + PageSize);
- if (GetPhysicalAddressChecked(va) + PageSize != newPa)
+ if (GetPhysicalAddressInternal(va) + PageSize != newPa)
{
regions.Add(new MemoryRange(regionStart, regionSize));
regionStart = newPa;
@@ -476,15 +310,14 @@ namespace Ryujinx.Cpu.Nce
protected override void Destroy()
{
_addressSpace.Dispose();
- _addressSpaceMirror.Dispose();
_memoryEh.Dispose();
}
protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
- => _addressSpaceMirror.GetMemory(pa, size);
+ => _backingMemory.GetMemory(pa, size);
protected override Span GetPhysicalAddressSpan(nuint pa, int size)
- => _addressSpaceMirror.GetSpan(pa, size);
+ => _backingMemory.GetSpan(pa, size);
protected override nuint TranslateVirtualAddressChecked(ulong va)
=> (nuint)GetPhysicalAddressChecked(va);
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 0da3d6f22..20c4b2572 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -1603,42 +1603,36 @@ namespace Ryujinx.Graphics.Vulkan
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
- // Setting graphics state with a compute pipeline bound crashes the Adreno driver.
- if (pbp == PipelineBindPoint.Graphics)
+ if (_needsIndexBufferRebind && _indexBufferPattern == null)
{
- DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
+ _indexBuffer.BindIndexBuffer(Gd, Cbs);
+ _needsIndexBufferRebind = false;
+ }
- if (_needsIndexBufferRebind && _indexBufferPattern == null)
+ if (_needsTransformFeedbackBuffersRebind)
+ {
+ PauseTransformFeedbackInternal();
+
+ for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
{
- _indexBuffer.BindIndexBuffer(Gd, Cbs);
- _needsIndexBufferRebind = false;
+ _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i);
}
- if (_needsTransformFeedbackBuffersRebind)
+ _needsTransformFeedbackBuffersRebind = false;
+ }
+
+ if (_vertexBuffersDirty != 0)
+ {
+ while (_vertexBuffersDirty != 0)
{
- PauseTransformFeedbackInternal();
+ int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
- for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
- {
- _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i);
- }
+ _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
- _needsTransformFeedbackBuffersRebind = false;
+ _vertexBuffersDirty &= ~(1UL << i);
}
- if (_vertexBuffersDirty != 0)
- {
- while (_vertexBuffersDirty != 0)
- {
- int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
-
- _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
-
- _vertexBuffersDirty &= ~(1UL << i);
- }
-
- _vertexBufferUpdater.Commit(Cbs);
- }
+ _vertexBufferUpdater.Commit(Cbs);
}
if (_bindingBarriersDirty)
diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
index 7bd963428..dd1b7204f 100644
--- a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
+++ b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
@@ -57,7 +57,6 @@ namespace Ryujinx.HLE.HOS
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
{
IArmProcessContext processContext;
- AddressSpace addressSpace = null;
bool isArm64Host = RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
@@ -71,16 +70,16 @@ namespace Ryujinx.HLE.HOS
}
else
{
- if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
+ if (!AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out var addressSpace))
{
throw new Exception("Address space creation failed");
}
- Logger.Info?.Print(LogClass.Cpu, $"NCE Base AS Address: 0x{addressSpace.Base.Pointer.ToInt64():X} Size: 0x{addressSpace.AddressSpaceSize:X}");
+ Logger.Info?.Print(LogClass.Cpu, $"NCE Base AS Address: 0x{addressSpace.Pointer.ToInt64():X} Size: 0x{addressSpace.Size:X}");
var cpuEngine = new NceEngine(_tickSource);
var memoryManager = new MemoryManagerNative(addressSpace, context.Memory, addressSpaceSize, invalidAccessHandler);
- processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManager, addressSpace.AddressSpaceSize, for64Bit, memoryManager.ReservedSize);
+ processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManager, addressSpace.Size, for64Bit, memoryManager.ReservedSize);
}
}
else
@@ -98,10 +97,14 @@ namespace Ryujinx.HLE.HOS
? new LightningJitEngine(_tickSource)
: new JitEngine(_tickSource);
+ AddressSpace addressSpace = null;
+ MemoryBlock asNoMirror = null;
+
// We want to use host tracked mode if the host page size is > 4KB.
if ((mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe) && MemoryBlock.GetPageSize() <= 0x1000)
{
- if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
+ if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace) &&
+ !AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out asNoMirror))
{
Logger.Warning?.Print(LogClass.Cpu, "Address space creation failed, falling back to software page table");
@@ -112,13 +115,15 @@ namespace Ryujinx.HLE.HOS
switch (mode)
{
case MemoryManagerMode.SoftwarePageTable:
- var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
- processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
+ {
+ var mm = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
+ processContext = new ArmProcessContext(pid, cpuEngine, _gpu, mm, addressSpaceSize, for64Bit);
+ }
break;
case MemoryManagerMode.HostMapped:
case MemoryManagerMode.HostMappedUnsafe:
- if (addressSpace == null)
+ if (addressSpace == null && asNoMirror == null)
{
var memoryManagerHostTracked = new MemoryManagerHostTracked(context.Memory, addressSpaceSize, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManagerHostTracked, addressSpaceSize, for64Bit);
@@ -130,14 +135,29 @@ namespace Ryujinx.HLE.HOS
Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{addressSpace.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
}
- var memoryManagerHostMapped = new MemoryManagerHostMapped(addressSpace, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
- processContext = new ArmProcessContext(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit);
+ bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
+
+ if (addressSpace != null)
+ {
+ var mm = new MemoryManagerHostMapped(addressSpace, unsafeMode, invalidAccessHandler);
+ processContext = new ArmProcessContext(pid, cpuEngine, _gpu, mm, addressSpace.AddressSpaceSize, for64Bit);
+ }
+ else
+ {
+ var mm = new MemoryManagerHostNoMirror(asNoMirror, context.Memory, unsafeMode, invalidAccessHandler);
+ processContext = new ArmProcessContext(pid, cpuEngine, _gpu, mm, asNoMirror.Size, for64Bit);
+ }
}
break;
default:
throw new InvalidOperationException($"{nameof(mode)} contains an invalid value: {mode}");
}
+
+ if (addressSpaceSize != processContext.AddressSpaceSize)
+ {
+ Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{processContext.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
+ }
}
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
index 5f4162448..ed06be78f 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
@@ -221,11 +221,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
break;
case ProcessCreationFlags.AddressSpace64Bit:
+ ulong reservedAddressSpaceSize = _reservedAddressSpaceSize;
if (_reservedAddressSpaceSize < addrSpaceEnd)
{
- int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize);
+ int addressSpaceWidth = (int)ulong.Log2(reservedAddressSpaceSize);
- aliasRegion.Size = 1UL << (addressSpaceWidth - 3);
+ aliasRegion.Size = reservedAddressSpaceSize >= 0x1800000000 ? 0x1000000000 : 1UL << (addressSpaceWidth - 3);
heapRegion.Size = 0x180000000;
stackRegion.Size = 1UL << (addressSpaceWidth - 8);
tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3);
@@ -234,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
AslrRegionStart = Math.Max(reservedSize, 0x8000000);
- addrSpaceEnd = reservedSize + (1UL << addressSpaceWidth);
+ addrSpaceEnd = reservedSize + reservedAddressSpaceSize;
AslrRegionEnd = addrSpaceEnd;
}
else
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
index 5f881a154..38feebcd6 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
@@ -1,4 +1,4 @@
-using LibHac.Account;
+using LibHac.Account;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
@@ -252,7 +252,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ulong argsStart = 0;
uint argsSize = 0;
ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
- uint codeSize = 0;
+ ulong codeSize = 0;
var buildIds = executables.Select(e => (e switch
{