12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421 |
- #if ENABLE_UNITY_COLLECTIONS_CHECKS
- #define ENABLE_UNITY_ALLOCATION_CHECKS
- #endif
- #pragma warning disable 0649
-
- using System;
- using System.Diagnostics;
- using System.Runtime.InteropServices;
- using AOT;
- using Unity.Burst;
- using Unity.Collections.LowLevel.Unsafe;
- using Unity.Mathematics;
- using UnityEngine.Assertions;
- using Unity.Jobs.LowLevel.Unsafe;
-
- namespace Unity.Collections
- {
- /// <summary>
- /// Manages custom memory allocators.
- /// </summary>
- public static class AllocatorManager
- {
- internal static Block AllocateBlock<T>(ref this T t, int sizeOf, int alignOf, int items) where T : unmanaged, IAllocator
- {
- CheckValid(t.Handle);
- Block block = default;
- block.Range.Pointer = IntPtr.Zero;
- block.Range.Items = items;
- block.Range.Allocator = t.Handle;
- block.BytesPerItem = sizeOf;
- // Make the alignment multiple of cacheline size
- block.Alignment = math.max(JobsUtility.CacheLineSize, alignOf);
-
- var error = t.Try(ref block);
- CheckFailedToAllocate(error);
- return block;
- }
-
- internal static Block AllocateBlock<T,U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : unmanaged
- {
- return AllocateBlock(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
- }
-
- internal static unsafe void* Allocate<T>(ref this T t, int sizeOf, int alignOf, int items) where T : unmanaged, IAllocator
- {
- return (void*)AllocateBlock(ref t, sizeOf, alignOf, items).Range.Pointer;
- }
-
- internal static unsafe U* Allocate<T,U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : unmanaged
- {
- return (U*)Allocate(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
- }
-
- internal static unsafe void* AllocateStruct<T, U>(ref this T t, U u, int items) where T : unmanaged, IAllocator where U : struct
- {
- return (void*)Allocate(ref t, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
- }
-
- internal static unsafe void FreeBlock<T>(ref this T t, ref Block block) where T : unmanaged, IAllocator
- {
- CheckValid(t.Handle);
- block.Range.Items = 0;
- var error = t.Try(ref block);
- CheckFailedToFree(error);
- }
-
- internal static unsafe void Free<T>(ref this T t, void* pointer, int sizeOf, int alignOf, int items) where T : unmanaged, IAllocator
- {
- if (pointer == null)
- return;
- Block block = default;
- block.AllocatedItems = items;
- block.Range.Pointer = (IntPtr)pointer;
- block.BytesPerItem = sizeOf;
- block.Alignment = alignOf;
- t.FreeBlock(ref block);
- }
-
- internal static unsafe void Free<T,U>(ref this T t, U* pointer, int items) where T : unmanaged, IAllocator where U : unmanaged
- {
- Free(ref t, pointer, UnsafeUtility.SizeOf<U>(), UnsafeUtility.AlignOf<U>(), items);
- }
-
- /// <summary>
- /// Allocates memory from an allocator.
- /// </summary>
- /// <param name="handle">A handle to the allocator.</param>
- /// <param name="itemSizeInBytes">The number of bytes to allocate.</param>
- /// <param name="alignmentInBytes">The alignment in bytes (must be a power of two).</param>
- /// <param name="items">The number of values to allocate space for. Defaults to 1.</param>
- /// <returns>A pointer to the allocated memory.</returns>
- public unsafe static void* Allocate(AllocatorHandle handle, int itemSizeInBytes, int alignmentInBytes, int items = 1)
- {
- return handle.Allocate(itemSizeInBytes, alignmentInBytes, items);
- }
-
- /// <summary>
- /// Allocates enough memory for an unmanaged value of a given type.
- /// </summary>
- /// <typeparam name="T">The type of value to allocate for.</typeparam>
- /// <param name="handle">A handle to the allocator.</param>
- /// <param name="items">The number of values to allocate for space for. Defaults to 1.</param>
- /// <returns>A pointer to the allocated memory.</returns>
- public unsafe static T* Allocate<T>(AllocatorHandle handle, int items = 1) where T : unmanaged
- {
- return handle.Allocate(default(T), items);
- }
-
- /// <summary>
- /// Frees an allocation.
- /// </summary>
- /// <remarks>For some allocators, the size of the allocation must be known to properly deallocate.
- /// Other allocators only need the pointer when deallocating and so will ignore `itemSizeInBytes`, `alignmentInBytes` and `items`.</remarks>
- /// <param name="handle">A handle to the allocator.</param>
- /// <param name="pointer">A pointer to the allocated memory.</param>
- /// <param name="itemSizeInBytes">The size in bytes of the allocation.</param>
- /// <param name="alignmentInBytes">The alignment in bytes (must be a power of two).</param>
- /// <param name="items">The number of values that the memory was allocated for.</param>
- public unsafe static void Free(AllocatorHandle handle, void* pointer, int itemSizeInBytes, int alignmentInBytes, int items = 1)
- {
- handle.Free(pointer, itemSizeInBytes, alignmentInBytes, items);
- }
-
- /// <summary>
- /// Frees an allocation.
- /// </summary>
- /// <param name="handle">A handle to the allocator.</param>
- /// <param name="pointer">A pointer to the allocated memory.</param>
- public unsafe static void Free(AllocatorHandle handle, void* pointer)
- {
- handle.Free((byte*)pointer, 1);
- }
-
- /// <summary>
- /// Frees an allocation.
- /// </summary>
- /// <remarks>For some allocators, the size of the allocation must be known to properly deallocate.
- /// Other allocators only need the pointer when deallocating and so will ignore `T` and `items`.</remarks>
- /// <typeparam name="T">The type of value that the memory was allocated for.</typeparam>
- /// <param name="handle">A handle to the allocator.</param>
- /// <param name="pointer">A pointer to the allocated memory.</param>
- /// <param name="items">The number of values that the memory was allocated for.</param>
- public unsafe static void Free<T>(AllocatorHandle handle, T* pointer, int items = 1) where T : unmanaged
- {
- handle.Free(pointer, items);
- }
-
- /// <summary>
- /// Corresponds to Allocator.Invalid.
- /// </summary>
- /// <value>Corresponds to Allocator.Invalid.</value>
- public static readonly AllocatorHandle Invalid = new AllocatorHandle { Index = 0 };
-
- /// <summary>
- /// Corresponds to Allocator.None.
- /// </summary>
- /// <value>Corresponds to Allocator.None.</value>
- public static readonly AllocatorHandle None = new AllocatorHandle { Index = 1 };
-
- /// <summary>
- /// Corresponds to Allocator.Temp.
- /// </summary>
- /// <value>Corresponds to Allocator.Temp.</value>
- public static readonly AllocatorHandle Temp = new AllocatorHandle { Index = 2 };
-
- /// <summary>
- /// Corresponds to Allocator.TempJob.
- /// </summary>
- /// <value>Corresponds to Allocator.TempJob.</value>
- public static readonly AllocatorHandle TempJob = new AllocatorHandle { Index = 3 };
-
- /// <summary>
- /// Corresponds to Allocator.Persistent.
- /// </summary>
- /// <value>Corresponds to Allocator.Persistent.</value>
- public static readonly AllocatorHandle Persistent = new AllocatorHandle { Index = 4 };
-
- /// <summary>
- /// Corresponds to Allocator.AudioKernel.
- /// </summary>
- /// <value>Corresponds to Allocator.AudioKernel.</value>
- public static readonly AllocatorHandle AudioKernel = new AllocatorHandle { Index = 5 };
-
- /// <summary>
- /// Used for calling an allocator function.
- /// </summary>
- public delegate int TryFunction(IntPtr allocatorState, ref Block block);
-
- /// <summary>
- /// Represents the allocator function used within an allocator.
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct AllocatorHandle : IAllocator
- {
- internal ref TableEntry TableEntry => ref SharedStatics.TableEntry.Ref.Data.ElementAt(Index);
- internal bool IsInstalled => ((SharedStatics.IsInstalled.Ref.Data.ElementAt(Index>>6) >> (Index&63)) & 1) != 0;
-
- internal void IncrementVersion()
- {
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- if (IsInstalled && IsCurrent)
- {
- // When allocator version is larger than 0x7FFF, allocator.ToAllocator
- // returns a negative value which causes problem when comparing to Allocator.None.
- // So only lower 15 bits of version is valid.
- Version = OfficialVersion = (ushort)(++OfficialVersion & 0x7FFF);
- }
- #endif
- }
-
- internal void Rewind()
- {
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- InvalidateDependents();
- IncrementVersion();
- #endif
- }
-
- internal void Install(TableEntry tableEntry)
- {
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- // if this allocator has never been visited before, then the unsafelists for its child allocators
- // and child safety handles are uninitialized, which means their allocator is Allocator.Invalid.
- // rectify that here.
- if(ChildSafetyHandles.Allocator.Value != (int)Allocator.Persistent)
- {
- ChildSafetyHandles = new UnsafeList<AtomicSafetyHandle>(0, Allocator.Persistent);
- ChildAllocators = new UnsafeList<AllocatorHandle>(0, Allocator.Persistent);
- }
- #endif
- Rewind();
- TableEntry = tableEntry;
- }
-
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- internal ref ushort OfficialVersion => ref SharedStatics.Version.Ref.Data.ElementAt(Index);
- internal ref UnsafeList<AtomicSafetyHandle> ChildSafetyHandles => ref SharedStatics.ChildSafetyHandles.Ref.Data.ElementAt(Index);
- internal ref UnsafeList<AllocatorHandle> ChildAllocators => ref SharedStatics.ChildAllocators.Ref.Data.ElementAt(Index);
- internal ref AllocatorHandle Parent => ref SharedStatics.Parent.Ref.Data.ElementAt(Index);
- internal ref int IndexInParent => ref SharedStatics.IndexInParent.Ref.Data.ElementAt(Index);
-
- internal bool IsCurrent => (Version == 0) || (Version == OfficialVersion);
- internal bool IsValid => (Index < FirstUserIndex) || (IsInstalled && IsCurrent);
-
- /// <summary>
- /// <para>Determines if the handle is still valid, because we intend to release it if it is.</para>
- /// </summary>
- /// <param name="handle">Safety handle.</param>
- internal static unsafe bool CheckExists(AtomicSafetyHandle handle)
- {
- bool res = false;
- #if UNITY_DOTSRUNTIME
- // In DOTS Runtime, AtomicSaftyHandle version is at 8 bytes offset of nodePtr
- int* versionNode = (int*)((byte *)handle.nodePtr + sizeof(void *));
- res = (handle.version == (*versionNode & AtomicSafetyNodeVersionMask.ReadWriteDisposeUnprotect));
- #else
- int* versionNode = (int*) (void*) handle.versionNode;
- res = (handle.version == (*versionNode & AtomicSafetyHandle.ReadWriteDisposeCheck));
- #endif
- return res;
- }
-
- internal static unsafe bool AreTheSame(AtomicSafetyHandle a, AtomicSafetyHandle b)
- {
- if(a.version != b.version)
- return false;
- #if UNITY_DOTSRUNTIME
- if(a.nodePtr != b.nodePtr)
- #else
- if(a.versionNode != b.versionNode)
- #endif
- return false;
- return true;
- }
-
- internal static bool AreTheSame(AllocatorHandle a, AllocatorHandle b)
- {
- if(a.Index != b.Index)
- return false;
- if(a.Version != b.Version)
- return false;
- return true;
- }
-
- internal bool NeedsUseAfterFreeTracking()
- {
- if(IsValid == false)
- return false;
- if(ChildSafetyHandles.Allocator.Value != (int)Allocator.Persistent)
- return false;
- return true;
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- /// <value>For internal use only.</value>
- public const int InvalidChildSafetyHandleIndex = -1;
-
- internal int AddSafetyHandle(AtomicSafetyHandle handle)
- {
- if(!NeedsUseAfterFreeTracking())
- return InvalidChildSafetyHandleIndex;
- var result = ChildSafetyHandles.Length;
- ChildSafetyHandles.Add(handle);
- return result;
- }
-
- internal bool TryRemoveSafetyHandle(AtomicSafetyHandle handle, int safetyHandleIndex)
- {
- if(!NeedsUseAfterFreeTracking())
- return false;
- if(safetyHandleIndex == InvalidChildSafetyHandleIndex)
- return false;
- safetyHandleIndex = math.min(safetyHandleIndex, ChildSafetyHandles.Length - 1);
- while(safetyHandleIndex >= 0)
- {
- unsafe
- {
- var safetyHandle = ChildSafetyHandles.Ptr + safetyHandleIndex;
- if(AreTheSame(*safetyHandle, handle))
- {
- ChildSafetyHandles.RemoveAtSwapBack(safetyHandleIndex);
- return true;
- }
- }
- --safetyHandleIndex;
- }
- return false;
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- /// <value>For internal use only.</value>
- public const int InvalidChildAllocatorIndex = -1;
-
- internal int AddChildAllocator(AllocatorHandle handle)
- {
- if(!NeedsUseAfterFreeTracking())
- return InvalidChildAllocatorIndex;
- var result = ChildAllocators.Length;
- ChildAllocators.Add(handle);
- handle.Parent = this;
- handle.IndexInParent = result;
- return result;
- }
-
- internal bool TryRemoveChildAllocator(AllocatorHandle handle, int childAllocatorIndex)
- {
- if(!NeedsUseAfterFreeTracking())
- return false;
- if(childAllocatorIndex == InvalidChildAllocatorIndex)
- return false;
- childAllocatorIndex = math.min(childAllocatorIndex, ChildAllocators.Length - 1);
- while(childAllocatorIndex >= 0)
- {
- unsafe
- {
- var allocatorHandle = ChildAllocators.Ptr + childAllocatorIndex;
- if(AreTheSame(*allocatorHandle, handle))
- {
- ChildAllocators.RemoveAtSwapBack(childAllocatorIndex);
- return true;
- }
- }
- --childAllocatorIndex;
- }
- return false;
- }
-
- // when you rewind an allocator, it invalidates and unregisters all of its child allocators - allocators that use as
- // backing memory, memory that was allocated from this (parent) allocator. the rewind operation was itself unmanaged,
- // until we added a managed global table of delegates alongside the unmanaged global table of function pointers. once
- // this table was added, the "unregister" extension function became managed, because it manipulates a managed array of
- // delegates.
-
- // a workaround (UnmanagedUnregister) was found that makes it possible for rewind to become unmanaged again: only in
- // the case that we rewind an allocator and invalidate all of its child allocators, we then unregister the child
- // allocators without touching the managed array of delegates as well.
-
- // this can "leak" delegates - it's possible for this to cause us to hold onto a GC reference to a delegate until
- // the end of the program, long after the delegate is no longer needed. but, there are only 65,536 such slots to
- // burn, and delegates are small data structures, and the leak ends when a delegate slot is reused, and most importantly,
- // when we've rewound an allocator while child allocators remain registered, we are likely before long to encounter
- // a use-before-free crash or a safety handle violation, both of which are likely to terminate the session before
- // anything can leak.
-
- [NotBurstCompatible]
- internal void InvalidateDependents()
- {
- if(!NeedsUseAfterFreeTracking())
- return;
- for(var i = 0; i < ChildSafetyHandles.Length; ++i)
- {
- unsafe
- {
- AtomicSafetyHandle* handle = ChildSafetyHandles.Ptr + i;
- if(CheckExists(*handle))
- AtomicSafetyHandle.Release(*handle);
- }
- }
- ChildSafetyHandles.Clear();
- if(Parent.IsValid)
- Parent.TryRemoveChildAllocator(this, IndexInParent);
- Parent = default;
- IndexInParent = InvalidChildAllocatorIndex;
- for(var i = 0; i < ChildAllocators.Length; ++i)
- {
- unsafe
- {
- AllocatorHandle* handle = (AllocatorHandle*)ChildAllocators.Ptr + i;
- if(handle->IsValid)
- handle->UnmanagedUnregister(); // see above comment
- }
- }
- ChildAllocators.Clear();
- }
-
- #endif
-
- /// <summary>
- /// Returns the AllocatorHandle of an allocator.
- /// </summary>
- /// <param name="a">The Allocator to copy.</param>
- /// <returns>The AllocatorHandle of an allocator.</returns>
- public static implicit operator AllocatorHandle(Allocator a) => new AllocatorHandle
- {
- Index = (ushort)((uint)a & 0xFFFF),
- Version = (ushort)((uint)a >> 16)
- };
-
- /// <summary>
- /// This allocator's index into the global table of allocator functions.
- /// </summary>
- /// <value>This allocator's index into the global table of allocator functions.</value>
- public ushort Index;
-
- /// <summary>
- /// This allocator's version number.
- /// </summary>
- /// <remarks>An allocator function is uniquely identified by its *combination* of <see cref="Index"/> and <see cref="Version"/> together: each
- /// index has a version number that starts at 0; the version number is incremented each time the allocator is invalidated. Only the
- /// lower 15 bits of Version is in use because when allocator version is larger than 0x7FFF, allocator.ToAllocator returns a negative value
- /// which causes problem when comparing to Allocator.None.
- /// </remarks>
- /// <value>This allocator's version number.</value>
- public ushort Version;
-
- /// <summary>
- /// The <see cref="Index"/> cast to int.
- /// </summary>
- /// <value>The <see cref="Index"/> cast to int.</value>
- public int Value => Index;
-
- /// <summary>
- /// Allocates a block from this allocator.
- /// </summary>
- /// <typeparam name="T">The type of value to allocate for.</typeparam>
- /// <param name="block">Outputs the allocated block.</param>
- /// <param name="items">The number of values to allocate for.</param>
- /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
- public int TryAllocateBlock<T>(out Block block, int items) where T : struct
- {
- block = new Block
- {
- Range = new Range { Items = items, Allocator = this },
- BytesPerItem = UnsafeUtility.SizeOf<T>(),
- Alignment = 1 << math.min(3, math.tzcnt(UnsafeUtility.SizeOf<T>()))
- };
- var returnCode = Try(ref block);
- return returnCode;
- }
-
- /// <summary>
- /// Allocates a block with this allocator function.
- /// </summary>
- /// <typeparam name="T">The type of value to allocate for.</typeparam>
- /// <param name="items">The number of values to allocate for.</param>
- /// <returns>The allocated block.</returns>
- /// <exception cref="ArgumentException">Thrown if the allocator is not valid or if the allocation failed.</exception>
- public Block AllocateBlock<T>(int items) where T : struct
- {
- CheckValid(this);
- var error = TryAllocateBlock<T>(out Block block, items);
- CheckAllocatedSuccessfully(error);
- return block;
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- static void CheckAllocatedSuccessfully(int error)
- {
- if (error != 0)
- throw new ArgumentException($"Error {error}: Failed to Allocate");
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- /// <value>For internal use only.</value>
- public TryFunction Function => default;
-
- /// <summary>
- /// Tries to allocate the block with this allocator.
- /// </summary>
- /// <param name="block">The block to allocate.</param>
- /// <returns>0 if successful. Otherwise, returns an error code.</returns>
- public int Try(ref Block block)
- {
- block.Range.Allocator = this;
- var error = AllocatorManager.Try(ref block);
- return error;
- }
-
- /// <summary>
- /// This handle.
- /// </summary>
- /// <value>This handle.</value>
- public AllocatorHandle Handle { get { return this; } set { this = value; } }
-
- /// <summary>
- /// Retrieve the Allocator associated with this allocator handle.
- /// </summary>
- /// <value>The Allocator retrieved.</value>
- public Allocator ToAllocator
- {
- get
- {
- uint lo = Index;
- uint hi = Version;
- uint value = (hi << 16) | lo;
- return (Allocator)value;
- }
- }
-
- /// <summary>
- /// Check whether this allocator is a custom allocator.
- /// </summary>
- /// <remarks>The AllocatorHandle is a custom allocator if its Index is larger or equal to `FirstUserIndex`.</remarks>
- /// <value>True if this AllocatorHandle is a custom allocator.</value>
- public bool IsCustomAllocator { get { return this.Index >= FirstUserIndex; } }
-
- /// <summary>
- /// Dispose the allocator.
- /// </summary>
- public void Dispose()
- {
- Rewind();
- }
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct BlockHandle
- {
- /// <summary>
- /// Represents the handle.
- /// </summary>
- /// <value>Represents the handle.</value>
- public ushort Value;
- }
-
- /// <summary>
- /// A range of allocated memory.
- /// </summary>
- /// <remarks>The name is perhaps misleading: only in combination with a <see cref="Block"/> does
- /// a `Range` have sufficient information to represent the number of bytes in an allocation. The reason `Range` is its own type that's separate from `Block`
- /// stems from some efficiency concerns in the implementation details. In most cases, a `Range` is only used in conjunction with an associated `Block`.
- /// </remarks>
- [StructLayout(LayoutKind.Sequential)]
- public struct Range : IDisposable
- {
- /// <summary>
- /// Pointer to the start of this range.
- /// </summary>
- /// <value>Pointer to the start of this range.</value>
- public IntPtr Pointer; // 0
-
- /// <summary>
- /// Number of items allocated in this range.
- /// </summary>
- /// <remarks>The actual allocation may be larger. See <see cref="Block.AllocatedItems"/>.</remarks>
- /// <value>Number of items allocated in this range. </value>
- public int Items; // 8
-
- /// <summary>
- /// The allocator function used for this range.
- /// </summary>
- /// <value>The allocator function used for this range.</value>
- public AllocatorHandle Allocator; // 12
-
- /// <summary>
- /// Deallocates the memory represented by this range.
- /// </summary>
- /// <remarks>
- /// Same as disposing the <see cref="Block"/> which contains this range.
- ///
- /// Cannot be used with allocators which need the allocation size to deallocate.
- /// </remarks>
- public void Dispose()
- {
- Block block = new Block { Range = this };
- block.Dispose();
- this = block.Range;
- }
- }
-
- /// <summary>
- /// Represents an individual allocation within an allocator.
- /// </summary>
- /// <remarks>A block consists of a <see cref="Range"/> plus metadata about the type of elements for which the block was allocated.</remarks>
- [StructLayout(LayoutKind.Sequential)]
- public struct Block : IDisposable
- {
- /// <summary>
- /// The range of memory encompassed by this block.
- /// </summary>
- /// <value>The range of memory encompassed by this block.</value>
- public Range Range;
-
- /// <summary>
- /// Number of bytes per item.
- /// </summary>
- /// <value>Number of bytes per item.</value>
- public int BytesPerItem;
-
- /// <summary>
- /// Number of items allocated for.
- /// </summary>
- /// <value>Number of items allocated for.</value>
- public int AllocatedItems;
-
- /// <summary>
- /// Log2 of the byte alignment.
- /// </summary>
- /// <remarks>The alignment must always be power of 2. Storing the alignment as its log2 helps enforces this.</remarks>
- /// <value>Log2 of the byte alignment.</value>
- public byte Log2Alignment;
-
- /// <summary>
- /// This field only exists to pad the `Block` struct. Ignore it.
- /// </summary>
- /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
- public byte Padding0;
-
- /// <summary>
- /// This field only exists to pad the `Block` struct. Ignore it.
- /// </summary>
- /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
- public ushort Padding1;
-
- /// <summary>
- /// This field only exists to pad the `Block` struct. Ignore it.
- /// </summary>
- /// <value>This field only exists to pad the `Block` struct. Ignore it.</value>
- public uint Padding2;
-
- /// <summary>
- /// Number of bytes requested for this block.
- /// </summary>
- /// <remarks>The actual allocation size may be larger due to alignment.</remarks>
- /// <value>Number of bytes requested for this block.</value>
- public long Bytes => BytesPerItem * Range.Items;
-
- /// <summary>
- /// Number of bytes allocated for this block.
- /// </summary>
- /// <remarks>The requested allocation size may be smaller. Any excess is due to alignment</remarks>
- /// <value>Number of bytes allocated for this block.</value>
- public long AllocatedBytes => BytesPerItem * AllocatedItems;
-
- /// <summary>
- /// The alignment.
- /// </summary>
- /// <remarks>Must be power of 2 that's greater than or equal to 0.
- ///
- /// Set alignment *before* the allocation is made. Setting it after has no effect on the allocation.</remarks>
- /// <param name="value">A new alignment. If not a power of 2, it will be rounded up to the next largest power of 2.</param>
- /// <value>The alignment.</value>
- public int Alignment
- {
- get => 1 << Log2Alignment;
- set => Log2Alignment = (byte)(32 - math.lzcnt(math.max(1, value) - 1));
- }
-
- /// <summary>
- /// Deallocates this block.
- /// </summary>
- /// <remarks>Same as <see cref="TryAllocate"/>.</remarks>
- public void Dispose()
- {
- TryFree();
- }
-
- /// <summary>
- /// Attempts to allocate this block.
- /// </summary>
- /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
- public int TryAllocate()
- {
- Range.Pointer = IntPtr.Zero;
- return Try(ref this);
- }
-
- /// <summary>
- /// Attempts to free this block.
- /// </summary>
- /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
- public int TryFree()
- {
- Range.Items = 0;
- return Try(ref this);
- }
-
- /// <summary>
- /// Allocates this block.
- /// </summary>
- /// <exception cref="ArgumentException">Thrown if safety checks are enabled and the allocation fails.</exception>
- public void Allocate()
- {
- var error = TryAllocate();
- CheckFailedToAllocate(error);
- }
-
- /// <summary>
- /// Frees the block.
- /// </summary>
- /// <exception cref="ArgumentException">Thrown if safety checks are enabled and the deallocation fails.</exception>
- public void Free()
- {
- var error = TryFree();
- CheckFailedToFree(error);
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- void CheckFailedToAllocate(int error)
- {
- if (error != 0)
- throw new ArgumentException($"Error {error}: Failed to Allocate {this}");
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- void CheckFailedToFree(int error)
- {
- if (error != 0)
- throw new ArgumentException($"Error {error}: Failed to Free {this}");
- }
- }
-
- /// <summary>
- /// An allocator function pointer.
- /// </summary>
- public interface IAllocator : IDisposable
- {
- /// <summary>
- /// The allocator function. It can allocate, deallocate, or reallocate.
- /// </summary>
- TryFunction Function { get; }
-
- /// <summary>
- /// Invoke the allocator function.
- /// </summary>
- /// <param name="block">The block to allocate, deallocate, or reallocate. See <see cref="AllocatorManager.Try"/></param>
- /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
- int Try(ref Block block);
-
- /// <summary>
- /// This allocator.
- /// </summary>
- /// <value>This allocator.</value>
- AllocatorHandle Handle { get; set; }
-
- /// <summary>
- /// Cast the Allocator index into Allocator
- /// </summary>
- Allocator ToAllocator { get; }
-
- /// <summary>
- /// Check whether an allocator is a custom allocator
- /// </summary>
- bool IsCustomAllocator { get; }
- }
-
-
- /// <summary>
- /// Memory allocation Success status
- /// </summary>
- public const int kErrorNone = 0;
-
- /// <summary>
- /// Memory allocation Buffer Overflow status
- /// </summary>
- public const int kErrorBufferOverflow = -1;
-
- #if !UNITY_IOS
-
- [BurstDiscard]
- private static void CheckDelegate(ref bool useDelegate)
- {
- //@TODO: This should use BurstCompiler.IsEnabled once that is available as an efficient API.
- useDelegate = true;
- }
-
- private static bool UseDelegate()
- {
- bool result = false;
- CheckDelegate(ref result);
- return result;
- }
- #endif
-
- private static int allocate_block(ref Block block)
- {
- TableEntry tableEntry = default;
- tableEntry = block.Range.Allocator.TableEntry;
- var function = new FunctionPointer<TryFunction>(tableEntry.function);
- // this is a path for bursted caller, for non-Burst C#, it generates garbage each time we call Invoke
- return function.Invoke(tableEntry.state, ref block);
- }
-
- #if !UNITY_IOS
- [BurstDiscard]
- private static void forward_mono_allocate_block(ref Block block, ref int error)
- {
- TableEntry tableEntry = default;
- tableEntry = block.Range.Allocator.TableEntry;
-
- var index = block.Range.Allocator.Handle.Index;
- if (index >= Managed.kMaxNumCustomAllocator)
- {
- throw new ArgumentException("Allocator index into TryFunction delegate table exceeds maximum.");
- }
- ref TryFunction function = ref Managed.TryFunctionDelegates[block.Range.Allocator.Handle.Index];
- error = function(tableEntry.state, ref block);
- }
- #endif
-
- internal static Allocator LegacyOf(AllocatorHandle handle)
- {
- if (handle.Value >= FirstUserIndex)
- return Allocator.Persistent;
- return (Allocator) handle.Value;
- }
-
- static unsafe int TryLegacy(ref Block block)
- {
- if (block.Range.Pointer == IntPtr.Zero) // Allocate
- {
- block.Range.Pointer = (IntPtr)Memory.Unmanaged.Allocate(block.Bytes, block.Alignment, LegacyOf(block.Range.Allocator));
- block.AllocatedItems = block.Range.Items;
- return (block.Range.Pointer == IntPtr.Zero) ? -1 : 0;
- }
- if (block.Bytes == 0) // Free
- {
- if (LegacyOf(block.Range.Allocator) != Allocator.None)
- {
- Memory.Unmanaged.Free((void*)block.Range.Pointer, LegacyOf(block.Range.Allocator));
- }
- block.Range.Pointer = IntPtr.Zero;
- block.AllocatedItems = 0;
- return 0;
- }
- // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
- return -1;
- }
-
- /// <summary>
- /// Invokes the allocator function of a block.
- /// </summary>
- /// <remarks>The allocator function is looked up from a global table.
- ///
- /// - If the block range's Pointer is null, it will allocate.
- /// - If the block range's Pointer is not null, it will reallocate.
- /// - If the block range's Items is 0, it will deallocate.
- /// </remarks>
- /// <param name="block">The block to allocate, deallocate, or reallocate.</param>
- /// <returns>0 if successful. Otherwise, returns the error code from the block's allocator function.</returns>
- public static unsafe int Try(ref Block block)
- {
- if (block.Range.Allocator.Value < FirstUserIndex)
- return TryLegacy(ref block);
- TableEntry tableEntry = default;
- tableEntry = block.Range.Allocator.TableEntry;
- var function = new FunctionPointer<TryFunction>(tableEntry.function);
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- // if the allocator being passed in has a version of 0, that means "whatever the current version is."
- // so we patch it here, with whatever the current version is...
- if(block.Range.Allocator.Version == 0)
- block.Range.Allocator.Version = block.Range.Allocator.OfficialVersion;
- #endif
-
- #if !UNITY_IOS
- if (UseDelegate())
- {
- int error = kErrorNone;
- forward_mono_allocate_block(ref block, ref error);
- return error;
- }
- #endif
- return allocate_block(ref block);
- }
-
- /// <summary>
- /// A stack allocator with no storage of its own. Uses the storage of its parent.
- /// </summary>
- [BurstCompile(CompileSynchronously = true)]
- internal struct StackAllocator : IAllocator, IDisposable
- {
- public AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
- public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
- public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
-
- internal AllocatorHandle m_handle;
-
- internal Block m_storage;
- internal long m_top;
-
- public void Initialize(Block storage)
- {
- m_storage = storage;
- m_top = 0;
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- m_storage.Range.Allocator.AddChildAllocator(Handle);
- #endif
- }
-
- public unsafe int Try(ref Block block)
- {
- if (block.Range.Pointer == IntPtr.Zero) // Allocate
- {
- if (m_top + block.Bytes > m_storage.Bytes)
- {
- return -1;
- }
-
- block.Range.Pointer = (IntPtr)((byte*)m_storage.Range.Pointer + m_top);
- block.AllocatedItems = block.Range.Items;
- m_top += block.Bytes;
- return 0;
- }
-
- if (block.Bytes == 0) // Free
- {
- if ((byte*)block.Range.Pointer - (byte*)m_storage.Range.Pointer == (long)(m_top - block.AllocatedBytes))
- {
- m_top -= block.AllocatedBytes;
- var blockSizeInBytes = block.AllocatedItems * block.BytesPerItem;
- block.Range.Pointer = IntPtr.Zero;
- block.AllocatedItems = 0;
- return 0;
- }
-
- return -1;
- }
-
- // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
- return -1;
- }
-
- [BurstCompile(CompileSynchronously = true)]
- [MonoPInvokeCallback(typeof(TryFunction))]
- public static unsafe int Try(IntPtr allocatorState, ref Block block)
- {
- return ((StackAllocator*)allocatorState)->Try(ref block);
- }
-
- public TryFunction Function => Try;
-
- public void Dispose()
- {
- m_handle.Rewind();
- }
- }
-
- /// <summary>
- /// Slab allocator with no backing storage.
- /// </summary>
- [BurstCompile(CompileSynchronously = true)]
- internal struct SlabAllocator : IAllocator, IDisposable
- {
- public AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
-
- public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
-
- public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
-
- internal AllocatorHandle m_handle;
-
- internal Block Storage;
- internal int Log2SlabSizeInBytes;
- internal FixedList4096Bytes<int> Occupied;
- internal long budgetInBytes;
- internal long allocatedBytes;
-
- public long BudgetInBytes => budgetInBytes;
-
- public long AllocatedBytes => allocatedBytes;
-
- internal int SlabSizeInBytes
- {
- get => 1 << Log2SlabSizeInBytes;
- set => Log2SlabSizeInBytes = (byte)(32 - math.lzcnt(math.max(1, value) - 1));
- }
-
- internal int Slabs => (int)(Storage.Bytes >> Log2SlabSizeInBytes);
-
- internal void Initialize(Block storage, int slabSizeInBytes, long budget)
- {
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- storage.Range.Allocator.AddChildAllocator(Handle);
- #endif
- Assert.IsTrue((slabSizeInBytes & (slabSizeInBytes - 1)) == 0);
- Storage = storage;
- Log2SlabSizeInBytes = 0;
- Occupied = default;
- budgetInBytes = budget;
- allocatedBytes = 0;
- SlabSizeInBytes = slabSizeInBytes;
- Occupied.Length = (Slabs + 31) / 32;
- }
-
- public int Try(ref Block block)
- {
- if (block.Range.Pointer == IntPtr.Zero) // Allocate
- {
- if (block.Bytes + allocatedBytes > budgetInBytes)
- return -2; //over allocator budget
- if (block.Bytes > SlabSizeInBytes)
- return -1;
- for (var wordIndex = 0; wordIndex < Occupied.Length; ++wordIndex)
- {
- var word = Occupied[wordIndex];
- if (word == -1)
- continue;
- for (var bitIndex = 0; bitIndex < 32; ++bitIndex)
- if ((word & (1 << bitIndex)) == 0)
- {
- Occupied[wordIndex] |= 1 << bitIndex;
- block.Range.Pointer = Storage.Range.Pointer +
- (int)(SlabSizeInBytes * (wordIndex * 32U + bitIndex));
- block.AllocatedItems = SlabSizeInBytes / block.BytesPerItem;
- allocatedBytes += block.Bytes;
- return 0;
- }
- }
-
- return -1;
- }
-
- if (block.Bytes == 0) // Free
- {
- var slabIndex = ((ulong)block.Range.Pointer - (ulong)Storage.Range.Pointer) >>
- Log2SlabSizeInBytes;
- int wordIndex = (int)(slabIndex >> 5);
- int bitIndex = (int)(slabIndex & 31);
- Occupied[wordIndex] &= ~(1 << bitIndex);
- block.Range.Pointer = IntPtr.Zero;
- var blockSizeInBytes = block.AllocatedItems * block.BytesPerItem;
- allocatedBytes -= blockSizeInBytes;
- block.AllocatedItems = 0;
- return 0;
- }
-
- // Reallocate (keep existing pointer and change size if possible. otherwise, allocate new thing and copy)
- return -1;
- }
-
- [BurstCompile(CompileSynchronously = true)]
- [MonoPInvokeCallback(typeof(TryFunction))]
- public static unsafe int Try(IntPtr allocatorState, ref Block block)
- {
- return ((SlabAllocator*)allocatorState)->Try(ref block);
- }
-
- public TryFunction Function => Try;
-
- public void Dispose()
- {
- m_handle.Rewind();
- }
- }
-
- internal struct TableEntry
- {
- internal IntPtr function;
- internal IntPtr state;
- }
-
- internal struct Array16<T> where T : unmanaged
- {
- internal T f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
- }
- internal struct Array256<T> where T : unmanaged
- {
- internal Array16<T> f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
- }
- internal struct Array4096<T> where T : unmanaged
- {
- internal Array256<T> f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15;
- }
- internal struct Array32768<T> : IIndexable<T> where T : unmanaged
- {
- internal Array4096<T> f0, f1, f2, f3, f4, f5, f6, f7;
- public int Length { get { return 32768; } set {} }
- public ref T ElementAt(int index)
- {
- unsafe { fixed(Array4096<T>* p = &f0) { return ref UnsafeUtility.AsRef<T>((T*)p + index); } }
- }
- }
-
- /// <summary>
- /// Contains arrays of the allocator function pointers.
- /// </summary>
- internal sealed class SharedStatics
- {
- internal sealed class IsInstalled { internal static readonly SharedStatic<Long1024> Ref = SharedStatic<Long1024>.GetOrCreate<IsInstalled>(); }
- internal sealed class TableEntry { internal static readonly SharedStatic<Array32768<AllocatorManager.TableEntry>> Ref = SharedStatic<Array32768<AllocatorManager.TableEntry>>.GetOrCreate<TableEntry>(); }
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- internal sealed class Version { internal static readonly SharedStatic<Array32768<ushort>> Ref = SharedStatic<Array32768<ushort>>.GetOrCreate<Version>(); }
- internal sealed class ChildSafetyHandles { internal static readonly SharedStatic<Array32768<UnsafeList<AtomicSafetyHandle>>> Ref = SharedStatic<Array32768<UnsafeList<AtomicSafetyHandle>>>.GetOrCreate<ChildSafetyHandles>(); }
- internal sealed class ChildAllocators { internal static readonly SharedStatic<Array32768<UnsafeList<AllocatorHandle>>> Ref = SharedStatic<Array32768<UnsafeList<AllocatorHandle>>>.GetOrCreate<ChildAllocators>(); }
- internal sealed class Parent { internal static readonly SharedStatic<Array32768<AllocatorHandle>> Ref = SharedStatic<Array32768<AllocatorHandle>>.GetOrCreate<Parent>(); }
- internal sealed class IndexInParent { internal static readonly SharedStatic<Array32768<int>> Ref = SharedStatic<Array32768<int>>.GetOrCreate<IndexInParent>(); }
- #endif
- }
-
- internal static class Managed
- {
- #if !UNITY_IOS
- /// <summary>
- /// Memory allocation status
- /// </summary>
- internal const int kMaxNumCustomAllocator = 32768;
- internal static TryFunction[] TryFunctionDelegates = new TryFunction[kMaxNumCustomAllocator];
- #endif
-
- /// <summary>
- /// Register TryFunction delegates for managed caller to avoid garbage collections
- /// </summary>
- /// <param name="index">Index into the TryFunction delegates table.</param>
- /// <param name="function">TryFunction delegate to be registered.</param>
- [NotBurstCompatible]
- public static void RegisterDelegate(int index, TryFunction function)
- {
- #if !UNITY_IOS
- if(index >= kMaxNumCustomAllocator)
- {
- throw new ArgumentException("index to be registered in TryFunction delegate table exceeds maximum.");
- }
- // Register TryFunction delegates for managed caller to avoid garbage collections
- Managed.TryFunctionDelegates[index] = function;
- #endif
- }
-
- /// <summary>
- /// Unregister TryFunction delegate
- /// </summary>
- /// <param name="int">Index into the TryFunction delegates table.</param>
- [NotBurstCompatible]
- public static void UnregisterDelegate(int index)
- {
- #if !UNITY_IOS
- if (index >= kMaxNumCustomAllocator)
- {
- throw new ArgumentException("index to be unregistered in TryFunction delegate table exceeds maximum.");
- }
- Managed.TryFunctionDelegates[index] = default;
- #endif
- }
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- public static void Initialize()
- {
- }
-
- /// <summary>
- /// Saves an allocator's function pointers at a particular index in the global function table.
- /// </summary>
- /// <param name="handle">The global function table index at which to install the allocator function.</param>
- /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
- /// <param name="functionPointer">The allocator function to install in the global function table.</param>
- /// <param name="function">The allocator function to install in the global function table.</param>
- internal static void Install(AllocatorHandle handle,
- IntPtr allocatorState,
- FunctionPointer<TryFunction> functionPointer,
- TryFunction function)
- {
- if(functionPointer.Value == IntPtr.Zero)
- handle.Unregister();
- else
- {
- int error = ConcurrentMask.TryAllocate(ref SharedStatics.IsInstalled.Ref.Data, handle.Value, 1);
- if (ConcurrentMask.Succeeded(error))
- {
- handle.Install(new TableEntry { state = allocatorState, function = functionPointer.Value });
- Managed.RegisterDelegate(handle.Index, function);
- }
- }
- }
-
- /// <summary>
- /// Saves an allocator's function pointers at a particular index in the global function table.
- /// </summary>
- /// <param name="handle">The global function table index at which to install the allocator function.</param>
- /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
- /// <param name="function">The allocator function to install in the global function table.</param>
- internal static void Install(AllocatorHandle handle, IntPtr allocatorState, TryFunction function)
- {
- var functionPointer = (function == null)
- ? new FunctionPointer<TryFunction>(IntPtr.Zero)
- : BurstCompiler.CompileFunctionPointer(function);
- Install(handle, allocatorState, functionPointer, function);
- }
-
- /// <summary>
- /// Saves an allocator's function pointers in a free slot of the global function table. Thread safe.
- /// </summary>
- /// <param name="allocatorState">IntPtr to allocator's custom state.</param>
- /// <param name="functionPointer">Function pointer to create or save in the function table.</param>
- /// <returns>Returns a handle to the newly registered allocator function.</returns>
- internal static AllocatorHandle Register(IntPtr allocatorState, FunctionPointer<TryFunction> functionPointer)
- {
- var tableEntry = new TableEntry { state = allocatorState, function = functionPointer.Value };
- var error = ConcurrentMask.TryAllocate(ref SharedStatics.IsInstalled.Ref.Data, out int offset, (FirstUserIndex+63)>>6, SharedStatics.IsInstalled.Ref.Data.Length, 1);
- AllocatorHandle handle = default;
- if(ConcurrentMask.Succeeded(error))
- {
- handle.Index = (ushort)offset;
- handle.Install(tableEntry);
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- handle.Version = handle.OfficialVersion;
- #endif
- }
- return handle;
- }
-
- /// <summary>
- /// Saves an allocator's function pointers in a free slot of the global function table. Thread safe.
- /// </summary>
- /// <typeparam name="T">The type of allocator to register.</typeparam>
- /// <param name="t">Reference to the allocator.</param>
- [NotBurstCompatible]
- public static unsafe void Register<T>(ref this T t) where T : unmanaged, IAllocator
- {
- var functionPointer = (t.Function == null)
- ? new FunctionPointer<TryFunction>(IntPtr.Zero)
- : BurstCompiler.CompileFunctionPointer(t.Function);
- t.Handle = Register((IntPtr)UnsafeUtility.AddressOf(ref t), functionPointer);
-
- Managed.RegisterDelegate(t.Handle.Index, t.Function);
-
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- if (!t.Handle.IsValid)
- throw new InvalidOperationException("Allocator registration succeeded, but failed to produce valid handle.");
- #endif
- }
-
- /// <summary>
- /// Removes an allocator's function pointers from the global function table, without managed code
- /// </summary>
- /// <typeparam name="T">The type of allocator to unregister.</typeparam>
- /// <param name="t">Reference to the allocator.</param>
- public static void UnmanagedUnregister<T>(ref this T t) where T : unmanaged, IAllocator
- {
- if(t.Handle.IsInstalled)
- {
- t.Handle.Install(default);
- ConcurrentMask.TryFree(ref SharedStatics.IsInstalled.Ref.Data, t.Handle.Value, 1);
- }
- }
-
- /// <summary>
- /// Removes an allocator's function pointers from the global function table.
- /// </summary>
- /// <typeparam name="T">The type of allocator to unregister.</typeparam>
- /// <param name="t">Reference to the allocator.</param>
- [NotBurstCompatible]
- public static void Unregister<T>(ref this T t) where T : unmanaged, IAllocator
- {
- if(t.Handle.IsInstalled)
- {
- t.Handle.Install(default);
- ConcurrentMask.TryFree(ref SharedStatics.IsInstalled.Ref.Data, t.Handle.Value, 1);
- Managed.UnregisterDelegate(t.Handle.Index);
- }
- }
-
- /// <summary>
- /// Create a custom allocator by allocating a backing storage to store the allocator and then register it
- /// </summary>
- /// <typeparam name="T">The type of allocator to create.</typeparam>
- /// <param name="backingAllocator">Allocator used to allocate backing storage.</param>
- /// <returns>Returns reference to the newly created allocator.</returns>
- [NotBurstCompatible]
- internal static ref T CreateAllocator<T>(AllocatorHandle backingAllocator)
- where T : unmanaged, IAllocator
- {
- unsafe
- {
- var allocatorPtr = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>(), 16, backingAllocator);
- *allocatorPtr = default;
- ref T allocator = ref UnsafeUtility.AsRef<T>(allocatorPtr);
- Register(ref allocator);
- return ref allocator;
- }
- }
-
- /// <summary>
- /// Destroy a custom allocator by unregistering the allocator and freeing its backing storage
- /// </summary>
- /// <typeparam name="T">The type of allocator to destroy.</typeparam>
- /// <param name="t">Reference to the allocator.</param>
- /// <param name="backingAllocator">Allocator used in allocating the backing storage.</param>
- [NotBurstCompatible]
- internal static void DestroyAllocator<T>(ref this T t, AllocatorHandle backingAllocator)
- where T : unmanaged, IAllocator
- {
- Unregister(ref t);
-
- unsafe
- {
- var allocatorPtr = UnsafeUtility.AddressOf<T>(ref t);
- Memory.Unmanaged.Free(allocatorPtr, backingAllocator);
- }
- }
-
- /// <summary>
- /// For internal use only.
- /// </summary>
- public static void Shutdown()
- {
- }
-
- /// <summary>
- /// Index in the global function table of the first user-defined allocator.
- /// </summary>
- /// <remarks>The indexes from 0 up to `FirstUserIndex` are reserved and so should not be used for your own allocators.</remarks>
- /// <value>Index in the global function table of the first user-defined allocator.</value>
- public const ushort FirstUserIndex = 64;
-
- internal static bool IsCustomAllocator(AllocatorHandle allocator)
- {
- return allocator.Index >= FirstUserIndex;
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- internal static void CheckFailedToAllocate(int error)
- {
- if (error != 0)
- throw new ArgumentException("failed to allocate");
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- internal static void CheckFailedToFree(int error)
- {
- if (error != 0)
- throw new ArgumentException("failed to free");
- }
-
- [Conditional("ENABLE_UNITY_ALLOCATION_CHECKS")]
- internal static void CheckValid(AllocatorHandle handle)
- {
- #if ENABLE_UNITY_ALLOCATION_CHECKS
- if(handle.IsValid == false)
- throw new ArgumentException("allocator handle is not valid.");
- #endif
- }
- }
-
- /// <summary>
- /// Provides a wrapper for custom allocator.
- /// </summary>
- [BurstCompatible(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
- public unsafe struct AllocatorHelper<T> : IDisposable
- where T : unmanaged, AllocatorManager.IAllocator
- {
- /// <summary>
- /// Pointer to a custom allocator.
- /// </summary>
- readonly T* m_allocator;
-
- /// <summary>
- /// Allocator used to allocate backing storage of T.
- /// </summary>
- AllocatorManager.AllocatorHandle m_backingAllocator;
-
- /// <summary>
- /// Get the custom allocator.
- /// </summary>
- public ref T Allocator => ref UnsafeUtility.AsRef<T>(m_allocator);
-
- /// <summary>
- /// Allocate the custom allocator from backingAllocator and register it.
- /// </summary>
- /// <param name="backingAllocator">Allocator used to allocate backing storage.</param>
- [NotBurstCompatible]
- public AllocatorHelper(AllocatorManager.AllocatorHandle backingAllocator)
- {
- ref var allocator = ref AllocatorManager.CreateAllocator<T>(backingAllocator);
- m_allocator = (T*)UnsafeUtility.AddressOf<T>(ref allocator);
- m_backingAllocator = backingAllocator;
- }
-
- /// <summary>
- /// Dispose the custom allocator backing memory and unregister it.
- /// </summary>
- [NotBurstCompatible]
- public void Dispose()
- {
- ref var allocator = ref UnsafeUtility.AsRef<T>(m_allocator);
- AllocatorManager.DestroyAllocator(ref allocator, m_backingAllocator);
- }
- }
- }
-
- #pragma warning restore 0649
|