diff options
Diffstat (limited to 'Ryujinx.Memory/WindowsShared/EmulatedSharedMemoryWindows.cs')
-rw-r--r-- | Ryujinx.Memory/WindowsShared/EmulatedSharedMemoryWindows.cs | 703 |
1 files changed, 0 insertions, 703 deletions
diff --git a/Ryujinx.Memory/WindowsShared/EmulatedSharedMemoryWindows.cs b/Ryujinx.Memory/WindowsShared/EmulatedSharedMemoryWindows.cs deleted file mode 100644 index 1417f7d5..00000000 --- a/Ryujinx.Memory/WindowsShared/EmulatedSharedMemoryWindows.cs +++ /dev/null @@ -1,703 +0,0 @@ -using Ryujinx.Memory.Range; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Ryujinx.Memory.WindowsShared -{ - class EmulatedSharedMemoryWindows : IDisposable - { - private static readonly IntPtr InvalidHandleValue = new IntPtr(-1); - private static readonly IntPtr CurrentProcessHandle = new IntPtr(-1); - - public const int MappingBits = 16; // Windows 64kb granularity. - public const ulong MappingGranularity = 1 << MappingBits; - public const ulong MappingMask = MappingGranularity - 1; - - public const ulong BackingSize32GB = 32UL * 1024UL * 1024UL * 1024UL; // Reasonable max size of 32GB. - - private class SharedMemoryMapping : INonOverlappingRange - { - public ulong Address { get; } - - public ulong Size { get; private set; } - - public ulong EndAddress { get; private set; } - - public List<int> Blocks; - - public SharedMemoryMapping(ulong address, ulong size, List<int> blocks = null) - { - Address = address; - Size = size; - EndAddress = address + size; - - Blocks = blocks ?? new List<int>(); - } - - public bool OverlapsWith(ulong address, ulong size) - { - return Address < address + size && address < EndAddress; - } - - public void ExtendTo(ulong endAddress, RangeList<SharedMemoryMapping> list) - { - EndAddress = endAddress; - Size = endAddress - Address; - - list.UpdateEndAddress(this); - } - - public void AddBlocks(IEnumerable<int> blocks) - { - if (Blocks.Count > 0 && blocks.Count() > 0 && Blocks.Last() == blocks.First()) - { - Blocks.AddRange(blocks.Skip(1)); - } - else - { - Blocks.AddRange(blocks); - } - } - - public INonOverlappingRange Split(ulong splitAddress) - { - SharedMemoryMapping newRegion = new SharedMemoryMapping(splitAddress, EndAddress - splitAddress); - - int end = (int)((EndAddress + MappingMask) >> MappingBits); - int start = (int)(Address >> MappingBits); - - Size = splitAddress - Address; - EndAddress = splitAddress; - - int splitEndBlock = (int)((splitAddress + MappingMask) >> MappingBits); - int splitStartBlock = (int)(splitAddress >> MappingBits); - - newRegion.AddBlocks(Blocks.Skip(splitStartBlock - start)); - Blocks.RemoveRange(splitEndBlock - start, end - splitEndBlock); - - return newRegion; - } - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr CreateFileMapping( - IntPtr hFile, - IntPtr lpFileMappingAttributes, - FileMapProtection flProtect, - uint dwMaximumSizeHigh, - uint dwMaximumSizeLow, - [MarshalAs(UnmanagedType.LPWStr)] string lpName); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle(IntPtr hObject); - - [DllImport("KernelBase.dll", SetLastError = true)] - private static extern IntPtr VirtualAlloc2( - IntPtr process, - IntPtr lpAddress, - IntPtr dwSize, - AllocationType flAllocationType, - MemoryProtection flProtect, - IntPtr extendedParameters, - ulong parameterCount); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, AllocationType dwFreeType); - - [DllImport("KernelBase.dll", SetLastError = true)] - private static extern IntPtr MapViewOfFile3( - IntPtr hFileMappingObject, - IntPtr process, - IntPtr baseAddress, - ulong offset, - IntPtr dwNumberOfBytesToMap, - ulong allocationType, - MemoryProtection dwDesiredAccess, - IntPtr extendedParameters, - ulong parameterCount); - - [DllImport("KernelBase.dll", SetLastError = true)] - private static extern bool UnmapViewOfFile2(IntPtr process, IntPtr lpBaseAddress, ulong unmapFlags); - - private ulong _size; - - private object _lock = new object(); - - private ulong _backingSize; - private IntPtr _backingMemHandle; - private int _backingEnd; - private int _backingAllocated; - private Queue<int> _backingFreeList; - - private List<ulong> _mappedBases; - private RangeList<SharedMemoryMapping> _mappings; - private SharedMemoryMapping[] _foundMappings = new SharedMemoryMapping[32]; - private PlaceholderList _placeholders; - - public EmulatedSharedMemoryWindows(ulong size) - { - ulong backingSize = BackingSize32GB; - - _size = size; - _backingSize = backingSize; - - _backingMemHandle = CreateFileMapping( - InvalidHandleValue, - IntPtr.Zero, - FileMapProtection.PageReadWrite | FileMapProtection.SectionReserve, - (uint)(backingSize >> 32), - (uint)backingSize, - null); - - if (_backingMemHandle == IntPtr.Zero) - { - throw new OutOfMemoryException(); - } - - _backingFreeList = new Queue<int>(); - _mappings = new RangeList<SharedMemoryMapping>(); - _mappedBases = new List<ulong>(); - _placeholders = new PlaceholderList(size >> MappingBits); - } - - private (ulong granularStart, ulong granularEnd) GetAlignedRange(ulong address, ulong size) - { - return (address & (~MappingMask), (address + size + MappingMask) & (~MappingMask)); - } - - private void Commit(ulong address, ulong size) - { - (ulong granularStart, ulong granularEnd) = GetAlignedRange(address, size); - - ulong endAddress = address + size; - - lock (_lock) - { - // Search a bit before and after the new mapping. - // When adding our new mapping, we may need to join an existing mapping into our new mapping (or in some cases, to the other side!) - ulong searchStart = granularStart == 0 ? 0 : (granularStart - 1); - int mappingCount = _mappings.FindOverlapsNonOverlapping(searchStart, (granularEnd - searchStart) + 1, ref _foundMappings); - - int first = -1; - int last = -1; - SharedMemoryMapping startOverlap = null; - SharedMemoryMapping endOverlap = null; - - int lastIndex = (int)(address >> MappingBits); - int endIndex = (int)((endAddress + MappingMask) >> MappingBits); - int firstBlock = -1; - int endBlock = -1; - - for (int i = 0; i < mappingCount; i++) - { - SharedMemoryMapping mapping = _foundMappings[i]; - - if (mapping.Address < address) - { - if (mapping.EndAddress >= address) - { - startOverlap = mapping; - } - - if ((int)((mapping.EndAddress - 1) >> MappingBits) == lastIndex) - { - lastIndex = (int)((mapping.EndAddress + MappingMask) >> MappingBits); - firstBlock = mapping.Blocks.Last(); - } - } - - if (mapping.EndAddress > endAddress) - { - if (mapping.Address <= endAddress) - { - endOverlap = mapping; - } - - if ((int)((mapping.Address) >> MappingBits) + 1 == endIndex) - { - endIndex = (int)((mapping.Address) >> MappingBits); - endBlock = mapping.Blocks.First(); - } - } - - if (mapping.OverlapsWith(address, size)) - { - if (first == -1) - { - first = i; - } - - last = i; - } - } - - if (startOverlap == endOverlap && startOverlap != null) - { - // Already fully committed. - return; - } - - var blocks = new List<int>(); - int lastBlock = -1; - - if (firstBlock != -1) - { - blocks.Add(firstBlock); - lastBlock = firstBlock; - } - - bool hasMapped = false; - Action map = () => - { - if (!hasMapped) - { - _placeholders.EnsurePlaceholders(address >> MappingBits, (granularEnd - granularStart) >> MappingBits, SplitPlaceholder); - hasMapped = true; - } - - // There's a gap between this index and the last. Allocate blocks to fill it. - blocks.Add(MapBackingBlock(MappingGranularity * (ulong)lastIndex++)); - }; - - if (first != -1) - { - for (int i = first; i <= last; i++) - { - SharedMemoryMapping mapping = _foundMappings[i]; - int mapIndex = (int)(mapping.Address >> MappingBits); - - while (lastIndex < mapIndex) - { - map(); - } - - if (lastBlock == mapping.Blocks[0]) - { - blocks.AddRange(mapping.Blocks.Skip(1)); - } - else - { - blocks.AddRange(mapping.Blocks); - } - - lastIndex = (int)((mapping.EndAddress - 1) >> MappingBits) + 1; - } - } - - while (lastIndex < endIndex) - { - map(); - } - - if (endBlock != -1 && endBlock != lastBlock) - { - blocks.Add(endBlock); - } - - if (startOverlap != null && endOverlap != null) - { - // Both sides should be coalesced. Extend the start overlap to contain the end overlap, and add together their blocks. - - _mappings.Remove(endOverlap); - - startOverlap.ExtendTo(endOverlap.EndAddress, _mappings); - - startOverlap.AddBlocks(blocks); - startOverlap.AddBlocks(endOverlap.Blocks); - } - else if (startOverlap != null) - { - startOverlap.ExtendTo(endAddress, _mappings); - - startOverlap.AddBlocks(blocks); - } - else - { - var mapping = new SharedMemoryMapping(address, size, blocks); - - if (endOverlap != null) - { - mapping.ExtendTo(endOverlap.EndAddress, _mappings); - - mapping.AddBlocks(endOverlap.Blocks); - - _mappings.Remove(endOverlap); - } - - _mappings.Add(mapping); - } - } - } - - private void Decommit(ulong address, ulong size) - { - (ulong granularStart, ulong granularEnd) = GetAlignedRange(address, size); - ulong endAddress = address + size; - - lock (_lock) - { - int mappingCount = _mappings.FindOverlapsNonOverlapping(granularStart, granularEnd - granularStart, ref _foundMappings); - - int first = -1; - int last = -1; - - for (int i = 0; i < mappingCount; i++) - { - SharedMemoryMapping mapping = _foundMappings[i]; - - if (mapping.OverlapsWith(address, size)) - { - if (first == -1) - { - first = i; - } - - last = i; - } - } - - if (first == -1) - { - return; // Could not find any regions to decommit. - } - - int lastReleasedBlock = -1; - - bool releasedFirst = false; - bool releasedLast = false; - - for (int i = last; i >= first; i--) - { - SharedMemoryMapping mapping = _foundMappings[i]; - bool releaseEnd = true; - bool releaseStart = true; - - if (i == last) - { - // If this is the last region, do not release the block if there is a page ahead of us, or the block continues after us. (it is keeping the block alive) - releaseEnd = last == mappingCount - 1; - - // If the end region starts after the decommit end address, split and readd it after modifying its base address. - if (mapping.EndAddress > endAddress) - { - var newMapping = (SharedMemoryMapping)mapping.Split(endAddress); - _mappings.UpdateEndAddress(mapping); - _mappings.Add(newMapping); - - if ((endAddress & MappingMask) != 0) - { - releaseEnd = false; - } - } - - releasedLast = releaseEnd; - } - - if (i == first) - { - // If this is the first region, do not release the block if there is a region behind us. (it is keeping the block alive) - releaseStart = first == 0; - - // If the first region starts before the decommit address, split it by modifying its end address. - if (mapping.Address < address) - { - var oldMapping = mapping; - mapping = (SharedMemoryMapping)mapping.Split(address); - _mappings.UpdateEndAddress(oldMapping); - - if ((address & MappingMask) != 0) - { - releaseStart = false; - } - } - - releasedFirst = releaseStart; - } - - _mappings.Remove(mapping); - - ulong releasePointer = (mapping.EndAddress + MappingMask) & (~MappingMask); - for (int j = mapping.Blocks.Count - 1; j >= 0; j--) - { - int blockId = mapping.Blocks[j]; - - releasePointer -= MappingGranularity; - - if (lastReleasedBlock == blockId) - { - // When committed regions are fragmented, multiple will have the same block id for their start/end granular block. - // Avoid releasing these blocks twice. - continue; - } - - if ((j != 0 || releaseStart) && (j != mapping.Blocks.Count - 1 || releaseEnd)) - { - ReleaseBackingBlock(releasePointer, blockId); - } - - lastReleasedBlock = blockId; - } - } - - ulong placeholderStart = (granularStart >> MappingBits) + (releasedFirst ? 0UL : 1UL); - ulong placeholderEnd = (granularEnd >> MappingBits) - (releasedLast ? 0UL : 1UL); - - if (placeholderEnd > placeholderStart) - { - _placeholders.RemovePlaceholders(placeholderStart, placeholderEnd - placeholderStart, CoalescePlaceholder); - } - } - } - - public bool CommitMap(IntPtr address, IntPtr size) - { - lock (_lock) - { - foreach (ulong mapping in _mappedBases) - { - ulong offset = (ulong)address - mapping; - - if (offset < _size) - { - Commit(offset, (ulong)size); - return true; - } - } - } - - return false; - } - - public bool DecommitMap(IntPtr address, IntPtr size) - { - lock (_lock) - { - foreach (ulong mapping in _mappedBases) - { - ulong offset = (ulong)address - mapping; - - if (offset < _size) - { - Decommit(offset, (ulong)size); - return true; - } - } - } - - return false; - } - - private int MapBackingBlock(ulong offset) - { - bool allocate = false; - int backing; - - if (_backingFreeList.Count > 0) - { - backing = _backingFreeList.Dequeue(); - } - else - { - if (_backingAllocated == _backingEnd) - { - // Allocate the backing. - _backingAllocated++; - allocate = true; - } - - backing = _backingEnd++; - } - - ulong backingOffset = MappingGranularity * (ulong)backing; - - foreach (ulong baseAddress in _mappedBases) - { - CommitToMap(baseAddress, offset, MappingGranularity, backingOffset, allocate); - allocate = false; - } - - return backing; - } - - private void ReleaseBackingBlock(ulong offset, int id) - { - foreach (ulong baseAddress in _mappedBases) - { - DecommitFromMap(baseAddress, offset); - } - - if (_backingEnd - 1 == id) - { - _backingEnd = id; - } - else - { - _backingFreeList.Enqueue(id); - } - } - - public IntPtr Map() - { - IntPtr newMapping = VirtualAlloc2( - CurrentProcessHandle, - IntPtr.Zero, - (IntPtr)_size, - AllocationType.Reserve | AllocationType.ReservePlaceholder, - MemoryProtection.NoAccess, - IntPtr.Zero, - 0); - - if (newMapping == IntPtr.Zero) - { - throw new OutOfMemoryException(); - } - - // Apply all existing mappings to the new mapping - lock (_lock) - { - int lastBlock = -1; - foreach (SharedMemoryMapping mapping in _mappings) - { - ulong blockAddress = mapping.Address & (~MappingMask); - foreach (int block in mapping.Blocks) - { - if (block != lastBlock) - { - ulong backingOffset = MappingGranularity * (ulong)block; - - CommitToMap((ulong)newMapping, blockAddress, MappingGranularity, backingOffset, false); - - lastBlock = block; - } - - blockAddress += MappingGranularity; - } - } - - _mappedBases.Add((ulong)newMapping); - } - - return newMapping; - } - - private void SplitPlaceholder(ulong address, ulong size) - { - ulong byteAddress = address << MappingBits; - IntPtr byteSize = (IntPtr)(size << MappingBits); - - foreach (ulong mapAddress in _mappedBases) - { - bool result = VirtualFree((IntPtr)(mapAddress + byteAddress), byteSize, AllocationType.PreservePlaceholder | AllocationType.Release); - - if (!result) - { - throw new InvalidOperationException("Placeholder could not be split."); - } - } - } - - private void CoalescePlaceholder(ulong address, ulong size) - { - ulong byteAddress = address << MappingBits; - IntPtr byteSize = (IntPtr)(size << MappingBits); - - foreach (ulong mapAddress in _mappedBases) - { - bool result = VirtualFree((IntPtr)(mapAddress + byteAddress), byteSize, AllocationType.CoalescePlaceholders | AllocationType.Release); - - if (!result) - { - throw new InvalidOperationException("Placeholder could not be coalesced."); - } - } - } - - private void CommitToMap(ulong mapAddress, ulong address, ulong size, ulong backingOffset, bool allocate) - { - IntPtr targetAddress = (IntPtr)(mapAddress + address); - - // Assume the placeholder worked (or already exists) - // Map the backing memory into the mapped location. - - IntPtr mapped = MapViewOfFile3( - _backingMemHandle, - CurrentProcessHandle, - targetAddress, - backingOffset, - (IntPtr)MappingGranularity, - 0x4000, // REPLACE_PLACEHOLDER - MemoryProtection.ReadWrite, - IntPtr.Zero, - 0); - - if (mapped == IntPtr.Zero) - { - throw new InvalidOperationException($"Could not map view of backing memory. (va=0x{address:X16} size=0x{size:X16}, error code {Marshal.GetLastWin32Error()})"); - } - - if (allocate) - { - // Commit this part of the shared memory. - VirtualAlloc2(CurrentProcessHandle, targetAddress, (IntPtr)MappingGranularity, AllocationType.Commit, MemoryProtection.ReadWrite, IntPtr.Zero, 0); - } - } - - private void DecommitFromMap(ulong baseAddress, ulong address) - { - UnmapViewOfFile2(CurrentProcessHandle, (IntPtr)(baseAddress + address), 2); - } - - public bool Unmap(ulong baseAddress) - { - lock (_lock) - { - if (_mappedBases.Remove(baseAddress)) - { - int lastBlock = -1; - - foreach (SharedMemoryMapping mapping in _mappings) - { - ulong blockAddress = mapping.Address & (~MappingMask); - foreach (int block in mapping.Blocks) - { - if (block != lastBlock) - { - DecommitFromMap(baseAddress, blockAddress); - - lastBlock = block; - } - - blockAddress += MappingGranularity; - } - } - - if (!VirtualFree((IntPtr)baseAddress, (IntPtr)0, AllocationType.Release)) - { - throw new InvalidOperationException("Couldn't free mapping placeholder."); - } - - return true; - } - - return false; - } - } - - public void Dispose() - { - // Remove all file mappings - lock (_lock) - { - foreach (ulong baseAddress in _mappedBases.ToArray()) - { - Unmap(baseAddress); - } - } - - // Finally, delete the file mapping. - CloseHandle(_backingMemHandle); - } - } -} |