aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory/MemoryBlock.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Memory/MemoryBlock.cs')
-rw-r--r--Ryujinx.Memory/MemoryBlock.cs128
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();
}
}