From c57f6a7fe3b28969eadc112f09d8f1ffe1d03815 Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Wed, 12 Jul 2023 16:17:58 -0300 Subject: [PATCH] Remove address space mirror and tweak address space layout when host has small adress space --- src/Ryujinx.Cpu/AddressSpace.cs | 36 +- .../Jit/MemoryManagerHostNoMirror.cs | 330 ++++++++++++++++++ src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs | 295 ++++------------ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 46 ++- .../HOS/ArmProcessContextFactory.cs | 40 ++- .../HOS/Kernel/Memory/KPageTableBase.cs | 7 +- .../Loaders/Processes/ProcessLoaderHelper.cs | 4 +- 7 files changed, 476 insertions(+), 282 deletions(-) create mode 100644 src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs 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 {