| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- using Ryujinx.Common;
- using Ryujinx.Memory;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- namespace Ryujinx.Cpu
- {
- class PrivateMemoryAllocator : PrivateMemoryAllocatorImpl<PrivateMemoryAllocator.Block>
- {
- public const ulong InvalidOffset = ulong.MaxValue;
- public class Block : IComparable<Block>
- {
- public MemoryBlock Memory { get; private set; }
- public ulong Size { get; }
- private struct Range : IComparable<Range>
- {
- public ulong Offset { get; }
- public ulong Size { get; }
- public Range(ulong offset, ulong size)
- {
- Offset = offset;
- Size = size;
- }
- public int CompareTo(Range other)
- {
- return Offset.CompareTo(other.Offset);
- }
- }
- private readonly List<Range> _freeRanges;
- public Block(MemoryBlock memory, ulong size)
- {
- Memory = memory;
- Size = size;
- _freeRanges = new List<Range>
- {
- new Range(0, size)
- };
- }
- public ulong Allocate(ulong size, ulong alignment)
- {
- for (int i = 0; i < _freeRanges.Count; i++)
- {
- var range = _freeRanges[i];
- ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment);
- ulong sizeDelta = alignedOffset - range.Offset;
- ulong usableSize = range.Size - sizeDelta;
- if (sizeDelta < range.Size && usableSize >= size)
- {
- _freeRanges.RemoveAt(i);
- if (sizeDelta != 0)
- {
- InsertFreeRange(range.Offset, sizeDelta);
- }
- ulong endOffset = range.Offset + range.Size;
- ulong remainingSize = endOffset - (alignedOffset + size);
- if (remainingSize != 0)
- {
- InsertFreeRange(endOffset - remainingSize, remainingSize);
- }
- return alignedOffset;
- }
- }
- return InvalidOffset;
- }
- public void Free(ulong offset, ulong size)
- {
- InsertFreeRangeComingled(offset, size);
- }
- private void InsertFreeRange(ulong offset, ulong size)
- {
- var range = new Range(offset, size);
- int index = _freeRanges.BinarySearch(range);
- if (index < 0)
- {
- index = ~index;
- }
- _freeRanges.Insert(index, range);
- }
- private void InsertFreeRangeComingled(ulong offset, ulong size)
- {
- ulong endOffset = offset + size;
- var range = new Range(offset, size);
- int index = _freeRanges.BinarySearch(range);
- if (index < 0)
- {
- index = ~index;
- }
- if (index < _freeRanges.Count && _freeRanges[index].Offset == endOffset)
- {
- endOffset = _freeRanges[index].Offset + _freeRanges[index].Size;
- _freeRanges.RemoveAt(index);
- }
- if (index > 0 && _freeRanges[index - 1].Offset + _freeRanges[index - 1].Size == offset)
- {
- offset = _freeRanges[index - 1].Offset;
- _freeRanges.RemoveAt(--index);
- }
- range = new Range(offset, endOffset - offset);
- _freeRanges.Insert(index, range);
- }
- public bool IsTotallyFree()
- {
- if (_freeRanges.Count == 1 && _freeRanges[0].Size == Size)
- {
- Debug.Assert(_freeRanges[0].Offset == 0);
- return true;
- }
- return false;
- }
- public int CompareTo(Block other)
- {
- return Size.CompareTo(other.Size);
- }
- public virtual void Destroy()
- {
- Memory.Dispose();
- }
- }
- public PrivateMemoryAllocator(int blockAlignment, MemoryAllocationFlags allocationFlags) : base(blockAlignment, allocationFlags)
- {
- }
- public PrivateMemoryAllocation Allocate(ulong size, ulong alignment)
- {
- var allocation = Allocate(size, alignment, CreateBlock);
- return new PrivateMemoryAllocation(this, allocation.Block, allocation.Offset, allocation.Size);
- }
- private Block CreateBlock(MemoryBlock memory, ulong size)
- {
- return new Block(memory, size);
- }
- }
- class PrivateMemoryAllocatorImpl<T> : IDisposable where T : PrivateMemoryAllocator.Block
- {
- private const ulong InvalidOffset = ulong.MaxValue;
- public struct Allocation
- {
- public T Block { get; }
- public ulong Offset { get; }
- public ulong Size { get; }
- public Allocation(T block, ulong offset, ulong size)
- {
- Block = block;
- Offset = offset;
- Size = size;
- }
- }
- private readonly List<T> _blocks;
- private readonly int _blockAlignment;
- private readonly MemoryAllocationFlags _allocationFlags;
- public PrivateMemoryAllocatorImpl(int blockAlignment, MemoryAllocationFlags allocationFlags)
- {
- _blocks = new List<T>();
- _blockAlignment = blockAlignment;
- _allocationFlags = allocationFlags;
- }
- protected Allocation Allocate(ulong size, ulong alignment, Func<MemoryBlock, ulong, T> createBlock)
- {
- // Ensure we have a sane alignment value.
- if ((ulong)(int)alignment != alignment || (int)alignment <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
- }
- for (int i = 0; i < _blocks.Count; i++)
- {
- var block = _blocks[i];
- if (block.Size >= size)
- {
- ulong offset = block.Allocate(size, alignment);
- if (offset != InvalidOffset)
- {
- return new Allocation(block, offset, size);
- }
- }
- }
- ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
- var memory = new MemoryBlock(blockAlignedSize, _allocationFlags);
- var newBlock = createBlock(memory, blockAlignedSize);
- InsertBlock(newBlock);
- ulong newBlockOffset = newBlock.Allocate(size, alignment);
- Debug.Assert(newBlockOffset != InvalidOffset);
- return new Allocation(newBlock, newBlockOffset, size);
- }
- public void Free(PrivateMemoryAllocator.Block block, ulong offset, ulong size)
- {
- block.Free(offset, size);
- if (block.IsTotallyFree())
- {
- for (int i = 0; i < _blocks.Count; i++)
- {
- if (_blocks[i] == block)
- {
- _blocks.RemoveAt(i);
- break;
- }
- }
- block.Destroy();
- }
- }
- private void InsertBlock(T block)
- {
- int index = _blocks.BinarySearch(block);
- if (index < 0)
- {
- index = ~index;
- }
- _blocks.Insert(index, block);
- }
- public void Dispose()
- {
- for (int i = 0; i < _blocks.Count; i++)
- {
- _blocks[i].Destroy();
- }
- _blocks.Clear();
- }
- }
- }
|