From 94dc9daa6770b64965438530ecdaebfaeb4104f7 Mon Sep 17 00:00:00 2001 From: DCFApixels <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 18 May 2025 00:49:12 +0800 Subject: [PATCH] update --- .../MetaAttributes/MetaIDAttribute.cs | 31 ++- src/EcsMask.cs | 2 +- src/Internal/Allocators/AllocatorUtility.cs | 17 ++ .../{ => Allocators}/MemoryAllocator.cs | 14 +- .../{ => Allocators}/MemoryAllocator.cs.meta | 0 src/Internal/Allocators/StackAllocator.cs | 249 ++++++++++++++++++ src/Internal/Allocators/TempBuffer.cs | 68 +++++ src/Internal/UnsafeArray.cs | 33 --- src/Utils/DependencyGraph.cs | 5 +- 9 files changed, 361 insertions(+), 58 deletions(-) create mode 100644 src/Internal/Allocators/AllocatorUtility.cs rename src/Internal/{ => Allocators}/MemoryAllocator.cs (96%) rename src/Internal/{ => Allocators}/MemoryAllocator.cs.meta (100%) create mode 100644 src/Internal/Allocators/StackAllocator.cs create mode 100644 src/Internal/Allocators/TempBuffer.cs diff --git a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs index 22ef974..1851e8c 100644 --- a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs +++ b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs @@ -2,6 +2,7 @@ #undef DEBUG #endif using DCFApixels.DragonECS.Core; +using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.Internal; using System; using System.Collections; @@ -113,26 +114,28 @@ namespace DCFApixels.DragonECS } public static string ParseIDFromTypeName(string name) { - char* buffer = TempBuffer.Get(name.Length); - int count = 0; - //skip name[0] char - for (int i = 1, iMax = name.Length; i < iMax; i++) + using (StackAllocator.Alloc(name.Length, out char* buffer)) { - char current = name[i]; - if (current == '_') + int count = 0; + //skip name[0] char + for (int i = 1, iMax = name.Length; i < iMax; i++) { - if (++i >= iMax) { break; } - current = name[i]; - switch (current) + char current = name[i]; + if (current == '_') { - case '1': current = '<'; break; - case '2': current = '>'; break; - case '3': current = ','; break; + if (++i >= iMax) { break; } + current = name[i]; + switch (current) + { + case '1': current = '<'; break; + case '2': current = '>'; break; + case '3': current = ','; break; + } } + buffer[count++] = current; } - buffer[count++] = current; + return new string(buffer, 0, count); } - return new string(buffer, 0, count); } public static string GenerateNewUniqueIDWithAttribute() diff --git a/src/EcsMask.cs b/src/EcsMask.cs index d52e027..6afb948 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -601,7 +601,7 @@ namespace DCFApixels.DragonECS } else { - preSortingBuffer = TempBuffer.Get(maxBufferSize); + preSortingBuffer = TempBuffer.Get(maxBufferSize); } if (_sortIncChunckBuffer.Length > 1) diff --git a/src/Internal/Allocators/AllocatorUtility.cs b/src/Internal/Allocators/AllocatorUtility.cs new file mode 100644 index 0000000..7f6a46a --- /dev/null +++ b/src/Internal/Allocators/AllocatorUtility.cs @@ -0,0 +1,17 @@ +using System; + +namespace DCFApixels.DragonECS.Core.Internal +{ + internal static class AllocatorUtility + { + public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes) + { + ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes); + } + public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes) + { + Span memorySpan = new Span(ptr + startByte, lengthInBytes); + memorySpan.Clear(); + } + } +} diff --git a/src/Internal/MemoryAllocator.cs b/src/Internal/Allocators/MemoryAllocator.cs similarity index 96% rename from src/Internal/MemoryAllocator.cs rename to src/Internal/Allocators/MemoryAllocator.cs index c0a843e..2ff65ff 100644 --- a/src/Internal/MemoryAllocator.cs +++ b/src/Internal/Allocators/MemoryAllocator.cs @@ -38,7 +38,7 @@ namespace DCFApixels.DragonECS.Core.Internal public static Handler AllocAndInit_Internal(int byteLength, Type type) { Handler handler = Alloc_Internal(byteLength, type); - ClearAllocatedMemory(handler.Ptr, 0, byteLength); + AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength); return handler; } #endregion @@ -103,7 +103,7 @@ namespace DCFApixels.DragonECS.Core.Internal private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType) { Handler handler = Realloc_Internal(target, newByteLength, newType); - ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength); + AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength); return handler; } #endregion @@ -174,11 +174,7 @@ namespace DCFApixels.DragonECS.Core.Internal #endif return result; } - private static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes) - { - Span memorySpan = new Span((byte*)ptr + startByte, lengthInBytes); - memorySpan.Clear(); - } + internal struct Meta { #if DEBUG @@ -209,7 +205,7 @@ namespace DCFApixels.DragonECS.Core.Internal [System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")] [System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))] #endif - public readonly struct Handler + public readonly struct Handler : IDisposable { public static readonly Handler Empty = new Handler(); internal readonly Meta* Data; // Data[-1] is meta; @@ -238,6 +234,8 @@ namespace DCFApixels.DragonECS.Core.Internal public IntPtr Ptr { get { return (IntPtr)Data; } } public T* As() where T : unmanaged { return (T*)Ptr; } + void IDisposable.Dispose() { Free((void*)Ptr); } + #region Debugger #if DEBUG #pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. diff --git a/src/Internal/MemoryAllocator.cs.meta b/src/Internal/Allocators/MemoryAllocator.cs.meta similarity index 100% rename from src/Internal/MemoryAllocator.cs.meta rename to src/Internal/Allocators/MemoryAllocator.cs.meta diff --git a/src/Internal/Allocators/StackAllocator.cs b/src/Internal/Allocators/StackAllocator.cs new file mode 100644 index 0000000..8a983ca --- /dev/null +++ b/src/Internal/Allocators/StackAllocator.cs @@ -0,0 +1,249 @@ +#if DISABLE_DEBUG +#undef DEBUG +#endif +using DCFApixels.DragonECS.Internal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS.Core.Internal +{ + internal unsafe class StackAllocator + { + [ThreadStatic] + private static MemoryAllocator.Handler _stackPtrHandler; + [ThreadStatic] + private static int _stackByteLength; + [ThreadStatic] + private static byte* _stackPtr; + [ThreadStatic] + private static int _currentByteLength; + [ThreadStatic] + private static byte* _currentPtr; +#if DEBUG + [ThreadStatic] + private static int _increment; +#endif + [ThreadStatic] + private static HandlerDebugInfo[] _debugInfos; + + #region AllocAndInit + public static Handler AllocAndInit(int count) where T : unmanaged + { + return AllocAndInit_Internal(Marshal.SizeOf() * count, typeof(T)); + } + public static Handler AllocAndInit(int byteLength) + { + return AllocAndInit_Internal(byteLength, null); + } + public static Handler AllocAndInit_Internal(int byteLength, Type type) + { + Handler handler = Alloc_Internal(byteLength, type); + AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength); + return handler; + } + #endregion + + #region Alloc + /* + SAMPLE + using (StackAllocator.HybridAlloc(requiredSize, THRESHOLD, out SomeData* preSortingBuffer)) + { + if (requiredSize < THRESHOLD) + { + SomeData* ptr = stackalloc SomeData[requiredSize]; + preSortingBuffer = ptr; + } + + } + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler HybridAlloc(int count, int threshold, out T* ptr) where T : unmanaged + { + if (count < threshold) + { + ptr = null; + return Handler.FromHandledPtr(_currentPtr); + } + else + { + return Alloc(count, out ptr); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler Alloc(int count, out T* ptr) where T : unmanaged + { + var handler = Alloc_Internal(Marshal.SizeOf() * count, typeof(T)); + ptr = handler.As(); + return handler; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler Alloc(int byteLength, out byte* ptr) + { + var handler = Alloc_Internal(byteLength, null); + ptr = handler.As(); + return handler; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler Alloc_Internal(int byteLength, Type type) + { + byteLength = byteLength == 0 ? 1 : byteLength; + var requiredByteLength = byteLength + sizeof(Meta); +#if DEBUG + int id = _increment++; +#endif + if (_currentByteLength < requiredByteLength) + { + Upsize(requiredByteLength); + } + Meta* newHandledPtr = (Meta*)_currentPtr; + _currentPtr += requiredByteLength; + Handler handler = Handler.FromHandledPtr(newHandledPtr); + +#if DEBUG + newHandledPtr->ID = id; + newHandledPtr->ByteLength = byteLength; + + _debugInfos[id].stackTrace = new System.Diagnostics.StackTrace(); + _debugInfos[id].type = type; +#endif + + return handler; + } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Upsize(int newSize) + { + if (_stackPtrHandler.IsEmpty) + { + _stackByteLength = Math.Max(512, ArrayUtility.NextPow2(newSize)); + _stackPtrHandler = MemoryAllocator.Alloc(_stackByteLength); + _stackPtr = _stackPtrHandler.As(); + _currentPtr = _stackPtr; + _currentByteLength = 0; + } + else + { + var usedLength = _stackByteLength - _currentByteLength; + _stackByteLength = ArrayUtility.NextPow2(newSize + usedLength); + _stackPtrHandler = MemoryAllocator.Realloc(_stackPtrHandler, _stackByteLength); + _stackPtr = _stackPtrHandler.As(); + _currentPtr = _stackPtr + usedLength; + _currentByteLength = _stackByteLength - usedLength; + } + } + #endregion + + #region Free + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(ref Handler target) + { + Free_Internal(target.GetHandledPtr()); + target = default; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(void* dataPtr) + { + Free_Internal(((Meta*)dataPtr) - 1); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Free_Internal(Meta* handledPtr) + { +#if DEBUG + if(handledPtr != _currentPtr) // Если значения одинаковые то это Handler из HybridAlloc, при условии что выделение памяти произошло в основном стеке, а не в StackAllocator + { + _increment--; + if (_increment-- != handledPtr->ID) + { + Throw.UndefinedException(); + } + handledPtr->ID = default; + handledPtr->ByteLength = default; + } +#endif + byte* ptr = (byte*)handledPtr; + long byteDifference = _currentPtr - ptr; + + _currentPtr = ptr; + _currentByteLength += (int)byteDifference; + } + #endregion + + #region Other + internal struct Meta + { +#if DEBUG + public int ID; + public int ByteLength; +#endif + } +#if DEBUG + [System.Diagnostics.DebuggerDisplay("{handler.DebuggerDisplay()}")] +#endif + internal struct HandlerDebugInfo + { +#if DEBUG + public System.Diagnostics.StackTrace stackTrace; + public Type type; +#endif + } + #endregion + + + public readonly struct Handler : IDisposable + { + public static Handler Empty => new Handler(); + internal readonly Meta* Data; // Data[-1] is meta; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Handler(Meta* dataPtr) { Data = dataPtr; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler FromHandledPtr(void* ptr) { return new Handler(((Meta*)ptr) + 1); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Handler FromDataPtr(void* ptr) { return new Handler((Meta*)ptr); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Meta* GetHandledPtr() { return Data - 1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetID_Debug() + { +#if DEBUG + return GetHandledPtr()->ID; +#else + return 0; +#endif + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetByteLength_Debug() + { +#if DEBUG + return GetHandledPtr()->ByteLength; +#else + return 0; +#endif + } + + public bool IsEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Data == null; } + } + public IntPtr Ptr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return (IntPtr)Data; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* As() where T : unmanaged { return (T*)Ptr; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { Free((void*)Ptr); } + } + } + + internal static class StackAllocatorHandlerExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dispose(this ref StackAllocator.Handler self) + { + StackAllocator.Free(ref self); + } + } +} \ No newline at end of file diff --git a/src/Internal/Allocators/TempBuffer.cs b/src/Internal/Allocators/TempBuffer.cs new file mode 100644 index 0000000..f777174 --- /dev/null +++ b/src/Internal/Allocators/TempBuffer.cs @@ -0,0 +1,68 @@ +#if DISABLE_DEBUG +#undef DEBUG +#endif +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if ENABLE_IL2CPP +using Unity.IL2CPP.CompilerServices; +#endif + +namespace DCFApixels.DragonECS.Core.Internal +{ +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + public static unsafe class TempBuffer where T : unmanaged + { + [ThreadStatic] private static T* _ptr; + [ThreadStatic] private static int _size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* Get(int size) + { + if (_size < size) + { + _ptr = TempBufferMemory.Get(size); + _size = size; + } + return _ptr; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear() + { + TempBufferMemory.Clear(); + } + } +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + internal static unsafe class TempBufferMemory + { + [ThreadStatic] private static byte* _ptr; + [ThreadStatic] private static int _byteSize; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* Get(int size) where T : unmanaged + { + int byteSize = size * Marshal.SizeOf(); + if (_byteSize < byteSize) + { + if (_ptr != null) + { + MemoryAllocator.Free(_ptr); + } + _ptr = MemoryAllocator.Alloc(byteSize).As(); + _byteSize = byteSize; + } + return (T*)_ptr; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear() + { + AllocatorUtility.ClearAllocatedMemory(_ptr, 0, _byteSize); + } + } +} diff --git a/src/Internal/UnsafeArray.cs b/src/Internal/UnsafeArray.cs index 9f00cbe..e7ad11a 100644 --- a/src/Internal/UnsafeArray.cs +++ b/src/Internal/UnsafeArray.cs @@ -12,39 +12,6 @@ using Unity.IL2CPP.CompilerServices; namespace DCFApixels.DragonECS.Internal { -#if ENABLE_IL2CPP - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - public static unsafe class TempBuffer where T : unmanaged - { - [ThreadStatic] private static T* _ptr; - [ThreadStatic] private static int _size; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T* Get(int size) - { - if (_size < size) - { - if (_ptr != null) - { - UnmanagedArrayUtility.Free(_ptr); - } - _ptr = UnmanagedArrayUtility.New(size); - _size = size; - } - return _ptr; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear() - { - for (int i = 0; i < _size; i++) - { - _ptr[i] = default; - } - } - } - #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] diff --git a/src/Utils/DependencyGraph.cs b/src/Utils/DependencyGraph.cs index 063a044..ede837e 100644 --- a/src/Utils/DependencyGraph.cs +++ b/src/Utils/DependencyGraph.cs @@ -1,4 +1,5 @@ -using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Core.Internal; +using DCFApixels.DragonECS.Internal; using System; using System.Collections; using System.Collections.Generic; @@ -241,7 +242,7 @@ namespace DCFApixels.DragonECS.Core } else { - var ptr = TempBuffer.Get(_count); + var ptr = TempBuffer.Get(_count); var buffer = UnsafeArray.Manual(ptr, _count); TopoSorting(buffer); ReoderInsertionIndexes(buffer);