/// <summary> /// Gets the address of an unused (free) region of the specified size. /// </summary> /// <param name="size">Size of the region in bytes</param> /// <param name="freeAddressStartPosition">Position at which memory can be allocated</param> /// <param name="alignment">Required alignment of the region address in bytes</param> /// <param name="start">Start address of the search on the address space</param> /// <returns>GPU virtual address of the allocation, or an all ones mask in case of failure</returns> public ulong GetFreeAddress(ulong size, out ulong freeAddressStartPosition, ulong alignment = 1, ulong start = DefaultStart) { // Note: Address 0 is not considered valid by the driver, // when 0 is returned it's considered a mapping error. lock (_tree) { Logger.Debug?.Print(LogClass.ServiceNv, $"Searching for a free address @ 0x{start:X} of size 0x{size:X}."); ulong address = start; if (alignment == 0) { alignment = 1; } alignment = (alignment + PageMask) & ~PageMask; if (address < AddressSpaceSize) { bool reachedEndOfAddresses = false; ulong targetAddress; if (start == DefaultStart) { Logger.Debug?.Print(LogClass.ServiceNv, $"Target address set to start of the last available range: 0x{_list.Last.Value:X}."); targetAddress = _list.Last.Value; } else { targetAddress = _tree.Floor(address); Logger.Debug?.Print(LogClass.ServiceNv, $"Target address set to floor of 0x{address:X}; resulted in 0x{targetAddress:X}."); if (targetAddress == InvalidAddress) { targetAddress = _tree.Ceiling(address); Logger.Debug?.Print(LogClass.ServiceNv, $"Target address was invalid, set to ceiling of 0x{address:X}; resulted in 0x{targetAddress:X}"); } } while (address < AddressSpaceSize) { if (targetAddress != InvalidAddress) { if (address >= targetAddress) { if (address + size <= _tree.Get(targetAddress)) { Logger.Debug?.Print(LogClass.ServiceNv, $"Found a suitable free address range from 0x{targetAddress:X} to 0x{_tree.Get(targetAddress):X} for 0x{address:X}."); freeAddressStartPosition = targetAddress; return(address); } else { Logger.Debug?.Print(LogClass.ServiceNv, "Address requirements exceeded the available space in the target range."); LinkedListNode <ulong> nextPtr = _dictionary[targetAddress]; if (nextPtr.Next != null) { targetAddress = nextPtr.Next.Value; Logger.Debug?.Print(LogClass.ServiceNv, $"Moved search to successor range starting at 0x{targetAddress:X}."); } else { if (reachedEndOfAddresses) { Logger.Debug?.Print(LogClass.ServiceNv, "Exiting loop, a full pass has already been completed w/ no suitable free address range."); break; } else { reachedEndOfAddresses = true; address = start; targetAddress = _tree.Floor(address); Logger.Debug?.Print(LogClass.ServiceNv, $"Reached the end of the available free ranges, restarting loop @ 0x{targetAddress:X} for 0x{address:X}."); } } } } else { address += PageSize * (targetAddress / PageSize - (address / PageSize)); ulong remainder = address % alignment; if (remainder != 0) { address = (address - remainder) + alignment; } Logger.Debug?.Print(LogClass.ServiceNv, $"Reset and aligned address to {address:X}."); if (address + size > AddressSpaceSize && !reachedEndOfAddresses) { reachedEndOfAddresses = true; address = start; targetAddress = _tree.Floor(address); Logger.Debug?.Print(LogClass.ServiceNv, $"Address requirements exceeded the capacity of available address space, restarting loop @ 0x{targetAddress:X} for 0x{address:X}."); } } } else { break; } } } Logger.Debug?.Print(LogClass.ServiceNv, $"No suitable address range found; returning: 0x{InvalidAddress:X}."); freeAddressStartPosition = InvalidAddress; } return(PteUnmapped); }