diff options
Diffstat (limited to 'Ryujinx.Memory/MemoryBlock.cs')
-rw-r--r-- | Ryujinx.Memory/MemoryBlock.cs | 128 |
1 files changed, 101 insertions, 27 deletions
diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 3561b81a..82a7d882 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Threading; @@ -11,8 +12,12 @@ namespace Ryujinx.Memory { private readonly bool _usesSharedMemory; private readonly bool _isMirror; + private readonly bool _viewCompatible; + private readonly bool _forceWindows4KBView; private IntPtr _sharedMemory; private IntPtr _pointer; + private ConcurrentDictionary<MemoryBlock, byte> _viewStorages; + private int _viewCount; /// <summary> /// Pointer to the memory block data. @@ -36,12 +41,14 @@ namespace Ryujinx.Memory if (flags.HasFlag(MemoryAllocationFlags.Mirrorable)) { _sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve)); - _pointer = MemoryManagement.MapSharedMemory(_sharedMemory); + _pointer = MemoryManagement.MapSharedMemory(_sharedMemory, size); _usesSharedMemory = true; } else if (flags.HasFlag(MemoryAllocationFlags.Reserve)) { - _pointer = MemoryManagement.Reserve(size); + _viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible); + _forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping); + _pointer = MemoryManagement.Reserve(size, _viewCompatible); } else { @@ -49,6 +56,10 @@ namespace Ryujinx.Memory } Size = size; + + _viewStorages = new ConcurrentDictionary<MemoryBlock, byte>(); + _viewStorages.TryAdd(this, 0); + _viewCount = 1; } /// <summary> @@ -60,7 +71,7 @@ namespace Ryujinx.Memory /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception> private MemoryBlock(ulong size, IntPtr sharedMemory) { - _pointer = MemoryManagement.MapSharedMemory(sharedMemory); + _pointer = MemoryManagement.MapSharedMemory(sharedMemory, size); Size = size; _usesSharedMemory = true; _isMirror = true; @@ -113,32 +124,54 @@ namespace Ryujinx.Memory } /// <summary> - /// Reprotects a region of memory. + /// Maps a view of memory from another memory block. /// </summary> - /// <param name="offset">Starting offset of the range to be reprotected</param> - /// <param name="size">Size of the range to be reprotected</param> - /// <param name="permission">New memory permissions</param> - /// <param name="throwOnFail">True if a failed reprotect should throw</param> + /// <param name="srcBlock">Memory block from where the backing memory will be taken</param> + /// <param name="srcOffset">Offset on <paramref name="srcBlock"/> of the region that should be mapped</param> + /// <param name="dstOffset">Offset to map the view into on this block</param> + /// <param name="size">Size of the range to be mapped</param> + /// <exception cref="NotSupportedException">Throw when the source memory block does not support mirroring</exception> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> - /// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception> - public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true) + public void MapView(MemoryBlock srcBlock, ulong srcOffset, ulong dstOffset, ulong size) { - MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, throwOnFail); + if (srcBlock._sharedMemory == IntPtr.Zero) + { + throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block."); + } + + if (_viewStorages.TryAdd(srcBlock, 0)) + { + srcBlock.IncrementViewCount(); + } + + MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView); } /// <summary> - /// Remaps a region of memory into this memory block. + /// Unmaps a view of memory from another memory block. /// </summary> - /// <param name="offset">Starting offset of the range to be remapped into</param> - /// <param name="sourceAddress">Starting offset of the range to be remapped from</param> - /// <param name="size">Size of the range to be remapped</param> + /// <param name="srcBlock">Memory block from where the backing memory was taken during map</param> + /// <param name="offset">Offset of the view previously mapped with <see cref="MapView"/></param> + /// <param name="size">Size of the range to be unmapped</param> + public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size) + { + MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView); + } + + /// <summary> + /// Reprotects a region of memory. + /// </summary> + /// <param name="offset">Starting offset of the range to be reprotected</param> + /// <param name="size">Size of the range to be reprotected</param> + /// <param name="permission">New memory permissions</param> + /// <param name="throwOnFail">True if a failed reprotect should throw</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> /// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception> - public void Remap(ulong offset, IntPtr sourceAddress, ulong size) + public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true) { - MemoryManagement.Remap(GetPointerInternal(offset, size), sourceAddress, size); + MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, _forceWindows4KBView, throwOnFail); } /// <summary> @@ -274,7 +307,7 @@ namespace Ryujinx.Memory /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public nuint GetPointer(ulong offset, ulong size) => (nuint)(ulong)GetPointerInternal(offset, size); + public IntPtr GetPointer(ulong offset, ulong size) => GetPointerInternal(offset, size); [MethodImpl(MethodImplOptions.AggressiveInlining)] private IntPtr GetPointerInternal(ulong offset, ulong size) @@ -367,22 +400,63 @@ namespace Ryujinx.Memory { if (_usesSharedMemory) { - MemoryManagement.UnmapSharedMemory(ptr); - - if (_sharedMemory != IntPtr.Zero && !_isMirror) - { - MemoryManagement.DestroySharedMemory(_sharedMemory); - _sharedMemory = IntPtr.Zero; - } + MemoryManagement.UnmapSharedMemory(ptr, Size); } else { MemoryManagement.Free(ptr); } + + foreach (MemoryBlock viewStorage in _viewStorages.Keys) + { + viewStorage.DecrementViewCount(); + } + + _viewStorages.Clear(); + } + } + + /// <summary> + /// Increments the number of views that uses this memory block as storage. + /// </summary> + private void IncrementViewCount() + { + Interlocked.Increment(ref _viewCount); + } + + /// <summary> + /// Decrements the number of views that uses this memory block as storage. + /// </summary> + private void DecrementViewCount() + { + if (Interlocked.Decrement(ref _viewCount) == 0 && _sharedMemory != IntPtr.Zero && !_isMirror) + { + MemoryManagement.DestroySharedMemory(_sharedMemory); + _sharedMemory = IntPtr.Zero; } } - private void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock)); - private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); + /// <summary> + /// Checks if the specified memory allocation flags are supported on the current platform. + /// </summary> + /// <param name="flags">Flags to be checked</param> + /// <returns>True if the platform supports all the flags, false otherwise</returns> + public static bool SupportsFlags(MemoryAllocationFlags flags) + { + if (flags.HasFlag(MemoryAllocationFlags.ViewCompatible)) + { + if (OperatingSystem.IsWindows()) + { + return OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134); + } + + return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); + } + + return true; + } + + private static void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock)); + private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } } |