mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2026-04-22 01:45:55 +08:00
remove span dummy/update&fix alloc management/add EcsUnsafeSpan & WhereUnsafe
This commit is contained in:
parent
14bf846135
commit
c30d4ec5f0
@ -196,7 +196,7 @@ namespace DCFApixels.DragonECS
|
|||||||
{
|
{
|
||||||
if (_groupSparsePagePoolCount <= 0)
|
if (_groupSparsePagePoolCount <= 0)
|
||||||
{
|
{
|
||||||
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).As<int>();
|
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).Ptr;
|
||||||
}
|
}
|
||||||
var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount];
|
var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount];
|
||||||
_groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty;
|
_groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty;
|
||||||
@ -228,7 +228,7 @@ namespace DCFApixels.DragonECS
|
|||||||
for (int i = 0; i < _groupSparsePagePoolCount; i++)
|
for (int i = 0; i < _groupSparsePagePoolCount; i++)
|
||||||
{
|
{
|
||||||
ref var page = ref _groupSparsePagePool[i];
|
ref var page = ref _groupSparsePagePool[i];
|
||||||
if (page.IsEmpty == false)
|
if (page.IsCreated)
|
||||||
{
|
{
|
||||||
MemoryAllocator.FreeAndClear(ref page);
|
MemoryAllocator.FreeAndClear(ref page);
|
||||||
}
|
}
|
||||||
@ -291,7 +291,7 @@ namespace DCFApixels.DragonECS
|
|||||||
private int _count = 0;
|
private int _count = 0;
|
||||||
internal bool _isReleased = true;
|
internal bool _isReleased = true;
|
||||||
|
|
||||||
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).As<int>();
|
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).Ptr;
|
||||||
internal static readonly long _nullPagePtrFake = (long)_nullPage;
|
internal static readonly long _nullPagePtrFake = (long)_nullPage;
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@ -344,17 +344,6 @@ namespace DCFApixels.DragonECS
|
|||||||
#endif
|
#endif
|
||||||
return _dense[++index];
|
return _dense[++index];
|
||||||
}
|
}
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// // TODO добавить лок енумератора на изменение
|
|
||||||
//#if DEBUG || DRAGONECS_STABILITY_MODE
|
|
||||||
// if (index < 0 || index >= Count) { Throw.ArgumentOutOfRange(); }
|
|
||||||
//#endif
|
|
||||||
// var oldValue = _dense[index];
|
|
||||||
// _dense[index] = value;
|
|
||||||
// _sparse[oldValue] = 0;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -394,7 +383,7 @@ namespace DCFApixels.DragonECS
|
|||||||
page.IndexesXOR = 0;
|
page.IndexesXOR = 0;
|
||||||
page.Count = 0;
|
page.Count = 0;
|
||||||
}
|
}
|
||||||
_sparsePagesHandler.Dispose();
|
_sparsePagesHandler.DisposeAndReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -555,7 +544,6 @@ namespace DCFApixels.DragonECS
|
|||||||
ref PageSlot page = ref _sparsePages[i];
|
ref PageSlot page = ref _sparsePages[i];
|
||||||
if (page.Indexes != _nullPage)
|
if (page.Indexes != _nullPage)
|
||||||
{
|
{
|
||||||
//TODO тут надо оптимизировать отчисткой не всего а по dense списку
|
|
||||||
for (int j = 0; j < PageSlot.SIZE; j++)
|
for (int j = 0; j < PageSlot.SIZE; j++)
|
||||||
{
|
{
|
||||||
page.Indexes[j] = 0;
|
page.Indexes[j] = 0;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#if DISABLE_DEBUG
|
#if DISABLE_DEBUG
|
||||||
#undef DEBUG
|
#undef DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
using DCFApixels.DragonECS.Core;
|
||||||
using DCFApixels.DragonECS.Core.Internal;
|
using DCFApixels.DragonECS.Core.Internal;
|
||||||
using DCFApixels.DragonECS.Core.Unchecked;
|
using DCFApixels.DragonECS.Core.Unchecked;
|
||||||
using System;
|
using System;
|
||||||
@ -127,6 +128,7 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Other
|
#region Other
|
||||||
|
public ReadOnlySpan<int> AsSystemSpan() { return _values; }
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int First() { return _values[0]; }
|
public int First() { return _values[0]; }
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -170,6 +172,7 @@ namespace DCFApixels.DragonECS
|
|||||||
_worldID = span._worldID;
|
_worldID = span._worldID;
|
||||||
}
|
}
|
||||||
public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { }
|
public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { }
|
||||||
|
public DebuggerProxy(EcsUnsafeSpan span) : this(span.ToSpan()) { }
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -317,3 +320,164 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace DCFApixels.DragonECS.Core
|
||||||
|
{
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
using Unity.IL2CPP.CompilerServices;
|
||||||
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
|
#endif
|
||||||
|
[DebuggerTypeProxy(typeof(EcsSpan.DebuggerProxy))]
|
||||||
|
public unsafe readonly struct EcsUnsafeSpan
|
||||||
|
{
|
||||||
|
private readonly int* _values;
|
||||||
|
private readonly int _length;
|
||||||
|
private readonly short _worldID;
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
public bool IsNull
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return _worldID == 0; }
|
||||||
|
}
|
||||||
|
public short WorldID
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return _worldID; }
|
||||||
|
}
|
||||||
|
public EcsWorld World
|
||||||
|
{
|
||||||
|
get { return EcsWorld.GetWorld(_worldID); }
|
||||||
|
}
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return _length; }
|
||||||
|
}
|
||||||
|
public EcsLongsSpan Longs
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return ToSpan().Longs; }
|
||||||
|
}
|
||||||
|
public bool IsSourceEntities
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return ToSpan() == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int this[int index]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if ((uint)index >= (uint)_length || (uint)index < 0)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowIndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
#elif DRAGONECS_STABILITY_MODE
|
||||||
|
return EcsConsts.NULL_ENTITY_ID;
|
||||||
|
#endif
|
||||||
|
return _values[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
internal EcsUnsafeSpan(short worldID, int* array, int length)
|
||||||
|
{
|
||||||
|
_worldID = worldID;
|
||||||
|
_values = array;
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal EcsUnsafeSpan(short worldID, int* array, int start, int length)
|
||||||
|
{
|
||||||
|
_worldID = worldID;
|
||||||
|
_values = array + start;
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Slice/ToArray
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public EcsUnsafeSpan Slice(int start)
|
||||||
|
{
|
||||||
|
if ((uint)start > (uint)_length)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
return new EcsUnsafeSpan(_worldID, _values, start, _length - start);
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public EcsUnsafeSpan Slice(int start, int length)
|
||||||
|
{
|
||||||
|
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
return new EcsUnsafeSpan(_worldID, _values, start, length);
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public EcsSpan ToSpan() { return new EcsSpan(_worldID, new ReadOnlySpan<int>(_values, _length)); }
|
||||||
|
public int[] ToArray() { return new ReadOnlySpan<int>(_values, _length).ToArray(); }
|
||||||
|
public int ToArray(ref int[] dynamicBuffer)
|
||||||
|
{
|
||||||
|
if (dynamicBuffer.Length < _length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref dynamicBuffer, ArrayUtility.CeilPow2(_length));
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
foreach (var e in this)
|
||||||
|
{
|
||||||
|
dynamicBuffer[i++] = e;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
public void ToCollection(ICollection<int> collection)
|
||||||
|
{
|
||||||
|
foreach (var e in this)
|
||||||
|
{
|
||||||
|
collection.Add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region operators
|
||||||
|
public static bool operator ==(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values == right._values; }
|
||||||
|
public static bool operator !=(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values != right._values; }
|
||||||
|
public static implicit operator EcsSpan(EcsUnsafeSpan a) { return a.ToSpan(); }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Enumerator
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ReadOnlySpan<int>.Enumerator GetEnumerator() { return new ReadOnlySpan<int>(_values, _length).GetEnumerator(); }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Other
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int First() { return _values[0]; }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int Last() { return _values[_length - 1]; }
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return CollectionUtility.EntitiesToString(ToArray(), "span");
|
||||||
|
}
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is EcsUnsafeSpan other && other == this;
|
||||||
|
}
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return *_values ^ _length ^ (_worldID << 16);
|
||||||
|
}
|
||||||
|
private static class ThrowHelper
|
||||||
|
{
|
||||||
|
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
|
||||||
|
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
|
||||||
|
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -615,8 +615,8 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
private void Cleanup(bool disposing)
|
private void Cleanup(bool disposing)
|
||||||
{
|
{
|
||||||
_bufferHandler.Dispose();
|
_bufferHandler.DisposeAndReset();
|
||||||
_chunckBufferHandler.Dispose();
|
_chunckBufferHandler.DisposeAndReset();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -644,8 +644,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (_sortIncChunckBuffer.Length > 1)
|
if (_sortIncChunckBuffer.Length > 1)
|
||||||
{
|
{
|
||||||
var comparer = new IncCountComparer(counts);
|
sortIncBuffer.AsSpan().Sort(new IncCountComparer(counts));
|
||||||
UnsafeArraySortHalperX<int>.InsertionSort(sortIncBuffer.ptr, sortIncBuffer.Length, ref comparer);
|
|
||||||
ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer);
|
ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer);
|
||||||
}
|
}
|
||||||
if (_sortIncChunckBuffer.Length > 0)
|
if (_sortIncChunckBuffer.Length > 0)
|
||||||
@ -659,8 +658,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (_sortExcChunckBuffer.Length > 1)
|
if (_sortExcChunckBuffer.Length > 1)
|
||||||
{
|
{
|
||||||
ExcCountComparer comparer = new ExcCountComparer(counts);
|
sortExcBuffer.AsSpan().Sort(new ExcCountComparer(counts));
|
||||||
UnsafeArraySortHalperX<int>.InsertionSort(sortExcBuffer.ptr, sortExcBuffer.Length, ref comparer);
|
|
||||||
ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer);
|
ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer);
|
||||||
}
|
}
|
||||||
// Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным.
|
// Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным.
|
||||||
@ -670,8 +668,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (_sortAnyChunckBuffer.Length > 1)
|
if (_sortAnyChunckBuffer.Length > 1)
|
||||||
{
|
{
|
||||||
ExcCountComparer comparer = new ExcCountComparer(counts);
|
sortAnyBuffer.AsSpan().Sort(new ExcCountComparer(counts));
|
||||||
UnsafeArraySortHalperX<int>.InsertionSort(sortAnyBuffer.ptr, sortAnyBuffer.Length, ref comparer);
|
|
||||||
ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer);
|
ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer);
|
||||||
}
|
}
|
||||||
// Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc
|
// Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc
|
||||||
@ -699,9 +696,13 @@ namespace DCFApixels.DragonECS
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IterateTo
|
#region IterateTo
|
||||||
//TODO Перемеиноваться в CacheTo
|
public EcsMaskFlags MaskFlags
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return _maskFlags; }
|
||||||
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void IterateTo(EcsSpan source, EcsGroup group)
|
public void CacheTo(EcsSpan source, EcsGroup group)
|
||||||
{
|
{
|
||||||
switch (_maskFlags)
|
switch (_maskFlags)
|
||||||
{
|
{
|
||||||
@ -709,7 +710,7 @@ namespace DCFApixels.DragonECS
|
|||||||
group.CopyFrom(source);
|
group.CopyFrom(source);
|
||||||
break;
|
break;
|
||||||
case EcsMaskFlags.Inc:
|
case EcsMaskFlags.Inc:
|
||||||
IterateOnlyInc(source).CopyTo(group);
|
IterateOnlyInc(source).CacheTo(group);
|
||||||
break;
|
break;
|
||||||
case EcsMaskFlags.Exc:
|
case EcsMaskFlags.Exc:
|
||||||
case EcsMaskFlags.Any:
|
case EcsMaskFlags.Any:
|
||||||
@ -717,7 +718,7 @@ namespace DCFApixels.DragonECS
|
|||||||
case EcsMaskFlags.IncAny:
|
case EcsMaskFlags.IncAny:
|
||||||
case EcsMaskFlags.ExcAny:
|
case EcsMaskFlags.ExcAny:
|
||||||
case EcsMaskFlags.IncExcAny:
|
case EcsMaskFlags.IncExcAny:
|
||||||
Iterate(source).CopyTo(group);
|
Iterate(source).CacheTo(group);
|
||||||
break;
|
break;
|
||||||
case EcsMaskFlags.Broken:
|
case EcsMaskFlags.Broken:
|
||||||
group.Clear();
|
group.Clear();
|
||||||
@ -728,21 +729,21 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IterateTo(EcsSpan source, ref int[] array)
|
public int CacheTo(EcsSpan source, ref int[] array)
|
||||||
{
|
{
|
||||||
switch (_maskFlags)
|
switch (_maskFlags)
|
||||||
{
|
{
|
||||||
case EcsMaskFlags.Empty:
|
case EcsMaskFlags.Empty:
|
||||||
return source.ToArray(ref array);
|
return source.ToArray(ref array);
|
||||||
case EcsMaskFlags.Inc:
|
case EcsMaskFlags.Inc:
|
||||||
return IterateOnlyInc(source).CopyTo(ref array);
|
return IterateOnlyInc(source).CacheTo(ref array);
|
||||||
case EcsMaskFlags.Exc:
|
case EcsMaskFlags.Exc:
|
||||||
case EcsMaskFlags.Any:
|
case EcsMaskFlags.Any:
|
||||||
case EcsMaskFlags.IncExc:
|
case EcsMaskFlags.IncExc:
|
||||||
case EcsMaskFlags.IncAny:
|
case EcsMaskFlags.IncAny:
|
||||||
case EcsMaskFlags.ExcAny:
|
case EcsMaskFlags.ExcAny:
|
||||||
case EcsMaskFlags.IncExcAny:
|
case EcsMaskFlags.IncExcAny:
|
||||||
return Iterate(source).CopyTo(ref array);
|
return Iterate(source).CacheTo(ref array);
|
||||||
case EcsMaskFlags.Broken:
|
case EcsMaskFlags.Broken:
|
||||||
return new EcsSpan(World.ID, Array.Empty<int>()).ToArray(ref array);
|
return new EcsSpan(World.ID, Array.Empty<int>()).ToArray(ref array);
|
||||||
default:
|
default:
|
||||||
@ -772,7 +773,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
#region CopyTo
|
#region CopyTo
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void CopyTo(EcsGroup group)
|
public void CacheTo(EcsGroup group)
|
||||||
{
|
{
|
||||||
group.Clear();
|
group.Clear();
|
||||||
var enumerator = GetEnumerator();
|
var enumerator = GetEnumerator();
|
||||||
@ -782,7 +783,7 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int CopyTo(ref int[] array)
|
public int CacheTo(ref int[] array)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
var enumerator = GetEnumerator();
|
var enumerator = GetEnumerator();
|
||||||
@ -928,7 +929,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
#region CopyTo
|
#region CopyTo
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void CopyTo(EcsGroup group)
|
public void CacheTo(EcsGroup group)
|
||||||
{
|
{
|
||||||
group.Clear();
|
group.Clear();
|
||||||
var enumerator = GetEnumerator();
|
var enumerator = GetEnumerator();
|
||||||
@ -938,7 +939,7 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int CopyTo(ref int[] array)
|
public int CacheTo(ref int[] array)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
var enumerator = GetEnumerator();
|
var enumerator = GetEnumerator();
|
||||||
|
|||||||
@ -698,7 +698,7 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
UnsafeArray<int> ua = UnsafeArray<int>.Manual(poolIdsPtr, count);
|
UnsafeArray<int> ua = UnsafeArray<int>.Manual(poolIdsPtr, count);
|
||||||
@ -711,7 +711,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (count >= BUFFER_THRESHOLD)
|
if (count >= BUFFER_THRESHOLD)
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
MemoryAllocator.Free(poolIdsPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -748,7 +748,7 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count);
|
GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count);
|
||||||
@ -759,7 +759,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (count >= BUFFER_THRESHOLD)
|
if (count >= BUFFER_THRESHOLD)
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
MemoryAllocator.Free(poolIdsPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//foreach (var pool in _pools)
|
//foreach (var pool in _pools)
|
||||||
@ -1082,7 +1082,7 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count);
|
GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count);
|
||||||
@ -1105,7 +1105,7 @@ namespace DCFApixels.DragonECS
|
|||||||
|
|
||||||
if (count >= BUFFER_THRESHOLD)
|
if (count >= BUFFER_THRESHOLD)
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
MemoryAllocator.Free(poolIdsPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public ReadOnlySpan<object> GetComponentsFor(int entityID)
|
public ReadOnlySpan<object> GetComponentsFor(int entityID)
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using DCFApixels.DragonECS.Core;
|
|||||||
using DCFApixels.DragonECS.Core.Internal;
|
using DCFApixels.DragonECS.Core.Internal;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@ -31,6 +30,10 @@ namespace DCFApixels.DragonECS
|
|||||||
private StructList<WorldComponentPoolAbstract> _worldComponentPools;
|
private StructList<WorldComponentPoolAbstract> _worldComponentPools;
|
||||||
private int _builtinWorldComponentsCount = 0;
|
private int _builtinWorldComponentsCount = 0;
|
||||||
|
|
||||||
|
public static int AllWorldsCount
|
||||||
|
{
|
||||||
|
get { return _worldIdDispenser.Count; }
|
||||||
|
}
|
||||||
static EcsWorld()
|
static EcsWorld()
|
||||||
{
|
{
|
||||||
_worlds[NULL_WORLD_ID] = new NullWorld();
|
_worlds[NULL_WORLD_ID] = new NullWorld();
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#endif
|
#endif
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
|
||||||
#if ENABLE_IL2CPP
|
#if ENABLE_IL2CPP
|
||||||
using Unity.IL2CPP.CompilerServices;
|
using Unity.IL2CPP.CompilerServices;
|
||||||
#endif
|
#endif
|
||||||
@ -13,13 +14,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
[Il2CppSetOption(Option.NullChecks, false)]
|
[Il2CppSetOption(Option.NullChecks, false)]
|
||||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||||
#endif
|
#endif
|
||||||
internal sealed class EcsWhereExecutor : MaskQueryExecutor
|
internal sealed unsafe class EcsWhereExecutor : MaskQueryExecutor
|
||||||
{
|
{
|
||||||
private EcsMaskIterator _iterator;
|
private EcsMaskIterator _iterator;
|
||||||
|
|
||||||
private int[] _filteredAllEntities = new int[32];
|
private HMem<int> _filteredAllEntities = Alloc<int>(32);
|
||||||
private int _filteredAllEntitiesCount = 0;
|
private int _filteredAllEntitiesCount = 0;
|
||||||
private int[] _filteredEntities = null;
|
private HMem<int> _filteredEntities = default;
|
||||||
private int _filteredEntitiesCount = 0;
|
private int _filteredEntitiesCount = 0;
|
||||||
|
|
||||||
private long _version;
|
private long _version;
|
||||||
@ -54,6 +55,14 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
protected sealed override void OnDestroy()
|
protected sealed override void OnDestroy()
|
||||||
{
|
{
|
||||||
if (_isDestroyed) { return; }
|
if (_isDestroyed) { return; }
|
||||||
|
if (_filteredAllEntities.IsCreated)
|
||||||
|
{
|
||||||
|
_filteredAllEntities.DisposeAndReset();
|
||||||
|
}
|
||||||
|
if (_filteredEntities.IsCreated)
|
||||||
|
{
|
||||||
|
_filteredEntities.DisposeAndReset();
|
||||||
|
}
|
||||||
_isDestroyed = true;
|
_isDestroyed = true;
|
||||||
_versionsChecker.Dispose();
|
_versionsChecker.Dispose();
|
||||||
}
|
}
|
||||||
@ -67,7 +76,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
if (_versionsChecker.CheckAndNext() == false)
|
if (_versionsChecker.CheckAndNext() == false)
|
||||||
{
|
{
|
||||||
_version++;
|
_version++;
|
||||||
_filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities);
|
_filteredAllEntitiesCount = _iterator.CacheTo(World.Entities, ref _filteredAllEntities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -77,19 +86,19 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
if (span.IsNull) { Throw.ArgumentNull(nameof(span)); }
|
if (span.IsNull) { Throw.ArgumentNull(nameof(span)); }
|
||||||
if (span.WorldID != World.ID) { Throw.Quiery_ArgumentDifferentWorldsException(); }
|
if (span.WorldID != World.ID) { Throw.Quiery_ArgumentDifferentWorldsException(); }
|
||||||
#endif
|
#endif
|
||||||
if (_filteredEntities == null)
|
if (_filteredEntities.IsCreated == false)
|
||||||
{
|
{
|
||||||
_filteredEntities = new int[32];
|
_filteredEntities = Alloc<int>(32);
|
||||||
}
|
}
|
||||||
_filteredEntitiesCount = _iterator.IterateTo(span, ref _filteredEntities);
|
_filteredEntitiesCount = _iterator.CacheTo(span, ref _filteredEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EcsSpan Execute()
|
public EcsUnsafeSpan Execute()
|
||||||
{
|
{
|
||||||
Execute_Iternal();
|
Execute_Iternal();
|
||||||
|
var result = new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
|
||||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||||
var newSpan = new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
|
||||||
using (EcsGroup group = EcsGroup.New(World))
|
using (EcsGroup group = EcsGroup.New(World))
|
||||||
{
|
{
|
||||||
foreach (var e in World.Entities)
|
foreach (var e in World.Entities)
|
||||||
@ -100,29 +109,29 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.SetEquals(newSpan) == false)
|
if (group.SetEquals(result.ToSpan()) == false)
|
||||||
{
|
{
|
||||||
int[] array = new int[_filteredAllEntities.Length];
|
int[] array = new int[_filteredAllEntities.Length];
|
||||||
var count = _iterator.IterateTo(World.Entities, ref array);
|
var count = _iterator.CacheTo(World.Entities, ref array);
|
||||||
|
|
||||||
EcsDebug.PrintError(newSpan.ToString() + "\r\n" + group.ToSpan().ToString());
|
EcsDebug.PrintError(result.ToString() + "\r\n" + group.ToSpan().ToString());
|
||||||
Throw.DeepDebugException();
|
Throw.DeepDebugException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
return result;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EcsSpan ExecuteFor(EcsSpan span)
|
public EcsUnsafeSpan ExecuteFor(EcsSpan span)
|
||||||
{
|
{
|
||||||
if (span.IsSourceEntities)
|
if (span.IsSourceEntities)
|
||||||
{
|
{
|
||||||
return Execute();
|
return Execute();
|
||||||
}
|
}
|
||||||
ExecuteFor_Iternal(span);
|
ExecuteFor_Iternal(span);
|
||||||
|
var result = new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
|
||||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||||
var newSpan = new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
foreach (var e in result)
|
||||||
foreach (var e in newSpan)
|
|
||||||
{
|
{
|
||||||
if (World.IsMatchesMask(Mask, e) == false)
|
if (World.IsMatchesMask(Mask, e) == false)
|
||||||
{
|
{
|
||||||
@ -130,26 +139,28 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EcsSpan Execute(Comparison<int> comparison)
|
public EcsUnsafeSpan Execute(Comparison<int> comparison)
|
||||||
{
|
{
|
||||||
Execute_Iternal();
|
Execute_Iternal();
|
||||||
ArraySortHalperX<int>.Sort(_filteredAllEntities, comparison, _filteredAllEntitiesCount);
|
Span<int> result = _filteredAllEntities.AsSpan(_filteredAllEntitiesCount);
|
||||||
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
result.Sort(comparison);
|
||||||
|
return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EcsSpan ExecuteFor(EcsSpan span, Comparison<int> comparison)
|
public EcsUnsafeSpan ExecuteFor(EcsSpan source, Comparison<int> comparison)
|
||||||
{
|
{
|
||||||
if (span.IsSourceEntities)
|
if (source.IsSourceEntities)
|
||||||
{
|
{
|
||||||
return Execute(comparison);
|
return Execute(comparison);
|
||||||
}
|
}
|
||||||
ExecuteFor_Iternal(span);
|
ExecuteFor_Iternal(source);
|
||||||
ArraySortHalperX<int>.Sort(_filteredEntities, comparison, _filteredEntitiesCount);
|
Span<int> result = _filteredEntities.AsSpan(_filteredEntitiesCount);
|
||||||
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
result.Sort(comparison);
|
||||||
|
return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,7 +66,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
if (_versionsChecker.CheckAndNext() == false)
|
if (_versionsChecker.CheckAndNext() == false)
|
||||||
{
|
{
|
||||||
_version++;
|
_version++;
|
||||||
_iterator.IterateTo(World.Entities, _filteredAllGroup);
|
_iterator.CacheTo(World.Entities, _filteredAllGroup);
|
||||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||||
if (_filteredGroup == null)
|
if (_filteredGroup == null)
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
{
|
{
|
||||||
_filteredGroup = EcsGroup.New(World);
|
_filteredGroup = EcsGroup.New(World);
|
||||||
}
|
}
|
||||||
_iterator.IterateTo(span, _filteredGroup);
|
_iterator.CacheTo(span, _filteredGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@ -84,6 +84,76 @@ namespace DCFApixels.DragonECS
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region WhereUnsafe
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
|
||||||
|
where TAspect : new()
|
||||||
|
where TCollection : IEntityStorage
|
||||||
|
{
|
||||||
|
return entities.ToSpan().WhereUnsafe(out aspect);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect)
|
||||||
|
where TAspect : new()
|
||||||
|
{
|
||||||
|
return group.ToSpan().WhereUnsafe(out aspect);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect)
|
||||||
|
where TAspect : new()
|
||||||
|
{
|
||||||
|
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
|
||||||
|
return executor.ExecuteFor(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask)
|
||||||
|
where TCollection : IEntityStorage
|
||||||
|
{
|
||||||
|
return entities.ToSpan().WhereUnsafe(mask);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask)
|
||||||
|
{
|
||||||
|
return group.ToSpan().WhereUnsafe(mask);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask)
|
||||||
|
{
|
||||||
|
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
|
||||||
|
return executor.ExecuteFor(span);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region WhereUnsafe with sort
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect, Comparison<int> comparison)
|
||||||
|
where TAspect : new()
|
||||||
|
where TCollection : IEntityStorage
|
||||||
|
{
|
||||||
|
return entities.ToSpan().WhereUnsafe(out aspect, comparison);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect, Comparison<int> comparison)
|
||||||
|
where TAspect : new()
|
||||||
|
{
|
||||||
|
return group.ToSpan().WhereUnsafe(out aspect, comparison);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect, Comparison<int> comparison)
|
||||||
|
where TAspect : new()
|
||||||
|
{
|
||||||
|
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
|
||||||
|
return executor.ExecuteFor(span, comparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask, Comparison<int> comparison)
|
||||||
|
where TCollection : IEntityStorage
|
||||||
|
{
|
||||||
|
return entities.ToSpan().WhereUnsafe(mask, comparison);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask, Comparison<int> comparison)
|
||||||
|
{
|
||||||
|
return group.ToSpan().WhereUnsafe(mask, comparison);
|
||||||
|
}
|
||||||
|
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask, Comparison<int> comparison)
|
||||||
|
{
|
||||||
|
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
|
||||||
|
return executor.ExecuteFor(span);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region WhereToGroup
|
#region WhereToGroup
|
||||||
public static EcsReadonlyGroup WhereToGroup<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
|
public static EcsReadonlyGroup WhereToGroup<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
|
||||||
where TAspect : new()
|
where TAspect : new()
|
||||||
|
|||||||
@ -1,25 +1,89 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
|
||||||
namespace DCFApixels.DragonECS.Core.Internal
|
namespace DCFApixels.DragonECS.Core.Internal
|
||||||
{
|
{
|
||||||
internal static class AllocatorUtility
|
internal static unsafe class AllocatorUtility
|
||||||
{
|
{
|
||||||
public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
|
public static void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
|
||||||
{
|
{
|
||||||
ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes);
|
ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes);
|
||||||
}
|
}
|
||||||
public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
|
public static void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
|
||||||
{
|
{
|
||||||
#if ENABLE_DUMMY_SPAN
|
|
||||||
lengthInBytes += startByte;
|
|
||||||
for (int i = startByte; i < lengthInBytes; i++)
|
|
||||||
{
|
|
||||||
ptr[i] = 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Span<byte> memorySpan = new Span<byte>(ptr + startByte, lengthInBytes);
|
Span<byte> memorySpan = new Span<byte>(ptr + startByte, lengthInBytes);
|
||||||
memorySpan.Clear();
|
memorySpan.Clear();
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int CacheTo(this EcsMaskIterator it, EcsSpan source, ref HMem<int> array)
|
||||||
|
{
|
||||||
|
switch (it.MaskFlags)
|
||||||
|
{
|
||||||
|
case EcsMaskFlags.Empty:
|
||||||
|
{
|
||||||
|
if(array.Length < source.Count)
|
||||||
|
{
|
||||||
|
array = Realloc<int>(array, source.Count);
|
||||||
|
}
|
||||||
|
source.AsSystemSpan().CopyTo(array.AsSpan());
|
||||||
|
return source.Count;
|
||||||
|
}
|
||||||
|
case EcsMaskFlags.Inc:
|
||||||
|
{
|
||||||
|
return it.IterateOnlyInc(source).CacheTo(ref array);
|
||||||
|
}
|
||||||
|
case EcsMaskFlags.Exc:
|
||||||
|
case EcsMaskFlags.Any:
|
||||||
|
case EcsMaskFlags.IncExc:
|
||||||
|
case EcsMaskFlags.IncAny:
|
||||||
|
case EcsMaskFlags.ExcAny:
|
||||||
|
case EcsMaskFlags.IncExcAny:
|
||||||
|
{
|
||||||
|
return it.Iterate(source).CacheTo(ref array);
|
||||||
|
}
|
||||||
|
case EcsMaskFlags.Broken:
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Throw.UndefinedException();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int CacheTo(this EcsMaskIterator.OnlyIncEnumerable e, ref HMem<int> array)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
var enumerator = e.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (array.Length <= count)
|
||||||
|
{
|
||||||
|
array = Realloc<int>(array, array.Length << 1);
|
||||||
|
}
|
||||||
|
array.Ptr[count++] = enumerator.Current;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int CacheTo(this EcsMaskIterator.Enumerable e, ref HMem<int> array)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
var enumerator = e.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (array.Length <= count)
|
||||||
|
{
|
||||||
|
array = Realloc<int>(array, array.Length << 1);
|
||||||
|
}
|
||||||
|
array.Ptr[count++] = enumerator.Current;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#undef DEBUG
|
#undef DEBUG
|
||||||
#endif
|
#endif
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS.Core.Internal
|
namespace DCFApixels.DragonECS.Core.Internal
|
||||||
@ -26,30 +27,30 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region AllocAndInit
|
#region AllocAndInit
|
||||||
public static Handler AllocAndInit<T>(int count) where T : unmanaged
|
public static HMem<T> AllocAndInit<T>(int count) where T : unmanaged
|
||||||
{
|
{
|
||||||
return AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T));
|
return new HMem<T>(AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
|
||||||
}
|
}
|
||||||
public static Handler AllocAndInit(int byteLength)
|
public static HMem<byte> AllocAndInit(int byteLength)
|
||||||
{
|
{
|
||||||
return AllocAndInit_Internal(byteLength, null);
|
return new HMem<byte>(AllocAndInit_Internal(byteLength, null), byteLength);
|
||||||
}
|
}
|
||||||
public static Handler AllocAndInit_Internal(int byteLength, Type type)
|
public static Handler AllocAndInit_Internal(int byteLength, Type type)
|
||||||
{
|
{
|
||||||
Handler handler = Alloc_Internal(byteLength, type);
|
Handler handler = Alloc_Internal(byteLength, type);
|
||||||
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength);
|
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, 0, byteLength);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Alloc
|
#region Alloc
|
||||||
public static Handler Alloc<T>(int count) where T : unmanaged
|
public static HMem<T> Alloc<T>(int count) where T : unmanaged
|
||||||
{
|
{
|
||||||
return Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T));
|
return new HMem<T>(Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
|
||||||
}
|
}
|
||||||
public static Handler Alloc(int byteLength)
|
public static HMem<byte> Alloc(int byteLength)
|
||||||
{
|
{
|
||||||
return Alloc_Internal(byteLength, null);
|
return new HMem<byte>(Alloc_Internal(byteLength, null), byteLength); ;
|
||||||
}
|
}
|
||||||
public static Handler Alloc_Internal(int byteLength, Type type)
|
public static Handler Alloc_Internal(int byteLength, Type type)
|
||||||
{
|
{
|
||||||
@ -84,54 +85,78 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ReallocAndInit
|
#region ReallocAndInit
|
||||||
public static Handler ReallocAndInit<T>(void* target, int oldCount, int newCount) where T : unmanaged
|
public static HMem<T> ReallocAndInit<T>(void* target, int oldCount, int newCount) where T : unmanaged
|
||||||
{
|
{
|
||||||
return ReallocAndInit<T>(Handler.FromDataPtr(target), oldCount, newCount);
|
return ReallocAndInit<T>(Handler.FromDataPtr(target), oldCount, newCount);
|
||||||
}
|
}
|
||||||
public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength)
|
public static HMem<byte> ReallocAndInit(void* target, int oldByteLength, int newByteLength)
|
||||||
{
|
{
|
||||||
return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength);
|
return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength);
|
||||||
}
|
}
|
||||||
public static Handler ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
|
public static HMem<T> ReallocAndInit<T>(HMem<T> target, int newCount) where T : unmanaged
|
||||||
{
|
{
|
||||||
var size = Marshal.SizeOf<T>();
|
var size = Marshal.SizeOf<T>();
|
||||||
return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T));
|
return new HMem<T>(ReallocAndInit_Internal(target, size * target.Length, size * newCount, typeof(T)), newCount);
|
||||||
}
|
}
|
||||||
public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
|
public static HMem<T> ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
|
||||||
{
|
{
|
||||||
return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null);
|
var size = Marshal.SizeOf<T>();
|
||||||
|
return new HMem<T>(ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)), newCount);
|
||||||
|
}
|
||||||
|
public static HMem<byte> ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
|
||||||
|
{
|
||||||
|
return new HMem<byte>(ReallocAndInit_Internal(target, oldByteLength, newByteLength, null), newByteLength);
|
||||||
}
|
}
|
||||||
private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType)
|
private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType)
|
||||||
{
|
{
|
||||||
Handler handler = Realloc_Internal(target, newByteLength, newType);
|
Handler handler = Realloc_Internal(target, newByteLength, newType);
|
||||||
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength);
|
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, oldByteLength, newByteLength - oldByteLength);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Realloc
|
#region Realloc
|
||||||
public static Handler Realloc<T>(void* target, int newCount) where T : unmanaged
|
public static HMem<T> Realloc<T>(void* target, int newCount) where T : unmanaged
|
||||||
{
|
{
|
||||||
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
|
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
|
||||||
}
|
}
|
||||||
public static Handler Realloc(void* target, int newByteLength)
|
public static HMem<byte> Realloc(void* target, int newByteLength)
|
||||||
{
|
{
|
||||||
return Realloc(Handler.FromDataPtr(target), newByteLength);
|
return new HMem<byte>(Realloc(Handler.FromDataPtr(target), newByteLength), newByteLength);
|
||||||
}
|
}
|
||||||
public static Handler Realloc<T>(Handler target, int newCount) where T : unmanaged
|
public static HMem<T> Realloc<T>(Handler target, int newCount) where T : unmanaged
|
||||||
{
|
{
|
||||||
return Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T));
|
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
|
||||||
}
|
}
|
||||||
public static Handler Realloc(Handler target, int newByteLength)
|
public static HMem<byte> Realloc(Handler target, int newByteLength)
|
||||||
{
|
{
|
||||||
return Realloc_Internal(target, newByteLength, null);
|
return new HMem<byte>(Realloc_Internal(target, newByteLength, null), newByteLength);
|
||||||
}
|
}
|
||||||
private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType)
|
private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType)
|
||||||
{
|
{
|
||||||
newByteLength = newByteLength == 0 ? 1 : newByteLength;
|
newByteLength = newByteLength == 0 ? 1 : newByteLength;
|
||||||
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta));
|
if (target.IsCreated == false)
|
||||||
|
{
|
||||||
|
return Alloc_Internal(newByteLength, newType);
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
int id = 0;
|
||||||
|
lock (_idDispenser)
|
||||||
|
{
|
||||||
|
if (_debugInfos.Length <= _idDispenser.Count)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _debugInfos, ArrayUtility.NextPow2(_idDispenser.Count));
|
||||||
|
}
|
||||||
|
id = _idDispenser.UseFree();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal(
|
||||||
|
(IntPtr)target.GetHandledPtr(),
|
||||||
|
(IntPtr)newByteLength + sizeof(Meta));
|
||||||
Handler handler = Handler.FromHandledPtr(newHandledPtr);
|
Handler handler = Handler.FromHandledPtr(newHandledPtr);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
newHandledPtr->ID = id;
|
||||||
|
newHandledPtr->ByteLength = newByteLength;
|
||||||
#if DRAGONECS_DEEP_DEBUG
|
#if DRAGONECS_DEEP_DEBUG
|
||||||
_debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace();
|
_debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace();
|
||||||
#endif
|
#endif
|
||||||
@ -142,11 +167,39 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Clone
|
||||||
|
public static HMem<T> From<T>(HMem<T> source)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var result = Alloc<T>(source.Length);
|
||||||
|
source.AsSpan().CopyTo(result.AsSpan());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public static HMem<T> From<T>(T* ptr, int length)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return From<T>(new ReadOnlySpan<T>(ptr, length));
|
||||||
|
}
|
||||||
|
public static HMem<T> From<T>(ReadOnlySpan<T> source)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var result = Alloc<T>(source.Length);
|
||||||
|
source.CopyTo(result.AsSpan());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Free
|
#region Free
|
||||||
public static void Free(Handler target)
|
public static void Free(Handler target)
|
||||||
{
|
{
|
||||||
Free_Internal(target.GetHandledPtr());
|
Free_Internal(target.GetHandledPtr());
|
||||||
}
|
}
|
||||||
|
public static void FreeAndClear<T>(ref HMem<T> target)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
Free_Internal(target.Handler.GetHandledPtr());
|
||||||
|
target = default;
|
||||||
|
}
|
||||||
public static void FreeAndClear(ref Handler target)
|
public static void FreeAndClear(ref Handler target)
|
||||||
{
|
{
|
||||||
Free_Internal(target.GetHandledPtr());
|
Free_Internal(target.GetHandledPtr());
|
||||||
@ -159,6 +212,10 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
private static void Free_Internal(Meta* handledPtr)
|
private static void Free_Internal(Meta* handledPtr)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
if (handledPtr == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
lock (_idDispenser)
|
lock (_idDispenser)
|
||||||
{
|
{
|
||||||
_idDispenser.Release(handledPtr->ID);
|
_idDispenser.Release(handledPtr->ID);
|
||||||
@ -210,11 +267,90 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public readonly struct HMem<T> : IDisposable, IEquatable<HMem<T>>
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
public readonly T* Ptr;
|
||||||
|
public readonly int Length;
|
||||||
|
|
||||||
|
internal HMem(Handler handler, int length)
|
||||||
|
{
|
||||||
|
Ptr = handler.As<T>();
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCreated
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return Ptr != null; }
|
||||||
|
}
|
||||||
|
public IntPtr RawPtr
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return new IntPtr(Ptr); }
|
||||||
|
}
|
||||||
|
public Handler Handler
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return Handler.FromDataPtr(Ptr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public HMem<U> As<U>()
|
||||||
|
where U : unmanaged
|
||||||
|
{
|
||||||
|
if (IsCreated)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalBytes = (long)Length * sizeof(T);
|
||||||
|
long newLengthLong = totalBytes / sizeof(U);
|
||||||
|
#if DEBUG
|
||||||
|
if (totalBytes % sizeof(U) != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Cannot cast Memory<{typeof(T).Name}> to Memory<{typeof(U).Name}> because the size of the underlying memory ({totalBytes} bytes) is not a multiple of the size of {typeof(U).Name} ({sizeof(U)} bytes).");
|
||||||
|
}
|
||||||
|
if (newLengthLong > int.MaxValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Resulting length ({newLengthLong}) exceeds int.MaxValue.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return new HMem<U>(Handler, (int)newLengthLong);
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Handler.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() { return Handler.DebuggerDisplay(); }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override int GetHashCode() { return RawPtr.GetHashCode(); }
|
||||||
|
public override bool Equals(object obj) { return obj is Handler h && h == this; }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool Equals(HMem<T> other) { return other.Ptr == Ptr; }
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator ==(HMem<T> a, HMem<T> b) { return a.Ptr == b.Ptr; }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator !=(HMem<T> a, HMem<T> b) { return a.Ptr != b.Ptr; }
|
||||||
|
|
||||||
|
public Span<T> AsSpan() { return new Span<T>(Ptr, Length); }
|
||||||
|
public Span<T> AsSpan(int length)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (length > Length) { Throw.UndefinedException(); }
|
||||||
|
#endif
|
||||||
|
return new Span<T>(Ptr, length);
|
||||||
|
}
|
||||||
|
public static implicit operator Handler(HMem<T> memory) { return memory.Handler; }
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")]
|
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")]
|
||||||
[System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))]
|
[System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||||
#endif
|
#endif
|
||||||
public readonly struct Handler : IDisposable
|
public readonly struct Handler : IDisposable, IEquatable<Handler>
|
||||||
{
|
{
|
||||||
public static readonly Handler Empty = new Handler();
|
public static readonly Handler Empty = new Handler();
|
||||||
internal readonly Meta* Data; // Data[-1] is meta;
|
internal readonly Meta* Data; // Data[-1] is meta;
|
||||||
@ -239,16 +375,37 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEmpty { get { return Data == null; } }
|
public bool IsCreated
|
||||||
public IntPtr Ptr { get { return (IntPtr)Data; } }
|
{
|
||||||
public T* As<T>() where T : unmanaged { return (T*)Ptr; }
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return Data != null; }
|
||||||
|
}
|
||||||
|
public IntPtr RawPtr
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get { return (IntPtr)Data; }
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public T* As<T>() where T : unmanaged { return (T*)RawPtr; }
|
||||||
|
|
||||||
void IDisposable.Dispose() { Free((void*)Ptr); }
|
public void Dispose() { Free((void*)RawPtr); }
|
||||||
|
|
||||||
|
public override string ToString() { return DebuggerDisplay(); }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override int GetHashCode() { return RawPtr.GetHashCode(); }
|
||||||
|
public override bool Equals(object obj) { return obj is Handler h && h == this; }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool Equals(Handler other) { return other.Data == Data; }
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator ==(Handler a, Handler b) { return a.Data == b.Data; }
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool operator !=(Handler a, Handler b) { return a.Data != b.Data; }
|
||||||
|
|
||||||
#region Debugger
|
#region Debugger
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
internal unsafe string DebuggerDisplay()
|
internal string DebuggerDisplay()
|
||||||
{
|
{
|
||||||
if (Data == null)
|
if (Data == null)
|
||||||
{
|
{
|
||||||
@ -282,6 +439,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
private unsafe struct Union
|
private unsafe struct Union
|
||||||
{
|
{
|
||||||
@ -303,13 +461,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
|
|
||||||
public HandlerDebugInfo[] OtherHandlersInfo;
|
public HandlerDebugInfo[] OtherHandlersInfo;
|
||||||
|
|
||||||
public unsafe DebuggerProxy(Handler handler)
|
public DebuggerProxy(Handler handler)
|
||||||
{
|
{
|
||||||
IsAlive = handler.Ptr.ToPointer() != null;
|
IsAlive = handler.RawPtr.ToPointer() != null;
|
||||||
if (IsAlive == false) { return; }
|
if (IsAlive == false) { return; }
|
||||||
|
|
||||||
Meta = handler.GetHandledPtr()[0];
|
Meta = handler.GetHandledPtr()[0];
|
||||||
_data = (byte*)handler.Ptr;
|
_data = (byte*)handler.RawPtr;
|
||||||
DebugInfo = _debugInfos[Meta.ID];
|
DebugInfo = _debugInfos[Meta.ID];
|
||||||
|
|
||||||
if (DebugInfo.type == null)
|
if (DebugInfo.type == null)
|
||||||
@ -337,9 +495,15 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
|
|
||||||
internal static class MemoryAllocatorHandlerExtensions
|
internal static class MemoryAllocatorHandlerExtensions
|
||||||
{
|
{
|
||||||
public static void Dispose(this ref MemoryAllocator.Handler self)
|
public static void DisposeAndReset(this ref MemoryAllocator.Handler self)
|
||||||
{
|
{
|
||||||
MemoryAllocator.FreeAndClear(ref self);
|
MemoryAllocator.FreeAndClear(ref self);
|
||||||
}
|
}
|
||||||
|
public static void DisposeAndReset<T>(this ref MemoryAllocator.HMem<T> self)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
self.Dispose();
|
||||||
|
self = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
{
|
{
|
||||||
MemoryAllocator.Free(_ptr);
|
MemoryAllocator.Free(_ptr);
|
||||||
}
|
}
|
||||||
_ptr = MemoryAllocator.Alloc<byte>(byteSize).As<byte>();
|
_ptr = MemoryAllocator.Alloc<byte>(byteSize).Ptr;
|
||||||
_byteSize = byteSize;
|
_byteSize = byteSize;
|
||||||
}
|
}
|
||||||
return (T*)_ptr;
|
return (T*)_ptr;
|
||||||
|
|||||||
@ -6,7 +6,6 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS.Core.Internal
|
namespace DCFApixels.DragonECS.Core.Internal
|
||||||
{
|
{
|
||||||
@ -270,114 +269,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal static unsafe class UnmanagedArrayUtility
|
|
||||||
{
|
|
||||||
private static class MetaCache<T>
|
|
||||||
{
|
|
||||||
public readonly static int Size;
|
|
||||||
static MetaCache()
|
|
||||||
{
|
|
||||||
T def = default;
|
|
||||||
Size = Marshal.SizeOf(def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T* New<T>(int capacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
//Console.WriteLine($"{typeof(T).Name} - {Marshal.SizeOf<T>()} - {capacity} - {Marshal.SizeOf<T>() * capacity}");
|
|
||||||
//return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
|
||||||
return MemoryAllocator.Alloc<T>(capacity).As<T>();
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void New<T>(out T* ptr, int capacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
//ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
|
||||||
ptr = MemoryAllocator.Alloc<T>(capacity).As<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T* NewAndInit<T>(int capacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
//int newSize = MetaCache<T>.Size * capacity;
|
|
||||||
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
|
||||||
//
|
|
||||||
//for (int i = 0; i < newSize; i++)
|
|
||||||
//{
|
|
||||||
// *(newPointer + i) = 0;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//return (T*)newPointer;
|
|
||||||
return MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void NewAndInit<T>(out T* ptr, int capacity) where T : unmanaged
|
|
||||||
{
|
|
||||||
//int newSize = MetaCache<T>.Size * capacity;
|
|
||||||
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
|
||||||
//
|
|
||||||
//for (int i = 0; i < newSize; i++)
|
|
||||||
//{
|
|
||||||
// *(newPointer + i) = 0;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//ptr = (T*)newPointer;
|
|
||||||
ptr = MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void Free(void* pointer)
|
|
||||||
{
|
|
||||||
//Marshal.FreeHGlobal(new IntPtr(pointer));
|
|
||||||
MemoryAllocator.Free(dataPtr: pointer);
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void Free<T>(ref T* pointer, ref int length) where T : unmanaged
|
|
||||||
{
|
|
||||||
//Marshal.FreeHGlobal(new IntPtr(pointer));
|
|
||||||
MemoryAllocator.Free(dataPtr: pointer);
|
|
||||||
pointer = null;
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T* Clone<T>(T* sourcePtr, int length) where T : unmanaged
|
|
||||||
{
|
|
||||||
T* clone = New<T>(length);
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
clone[i] = sourcePtr[i];
|
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T* Resize<T>(void* oldPointer, int newCount) where T : unmanaged
|
|
||||||
{
|
|
||||||
//return (T*)(Marshal.ReAllocHGlobal(
|
|
||||||
// new IntPtr(oldPointer),
|
|
||||||
// new IntPtr(MetaCache<T>.Size * newCount))).ToPointer();
|
|
||||||
return MemoryAllocator.Realloc<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), newCount).As<T>();
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T* ResizeAndInit<T>(void* oldPointer, int oldSize, int newSize) where T : unmanaged
|
|
||||||
{
|
|
||||||
//int sizeT = MetaCache<T>.Size;
|
|
||||||
//T* result = (T*)Marshal.ReAllocHGlobal(
|
|
||||||
// new IntPtr(oldPointer),
|
|
||||||
// new IntPtr(sizeT * newSize)).ToPointer();
|
|
||||||
//Init((byte*)result, sizeT * oldSize, sizeT * newSize);
|
|
||||||
//return result;
|
|
||||||
return MemoryAllocator.ReallocAndInit<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), oldSize, newSize).As<T>();
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void Init(byte* pointer, int startByteIndex, int endByteIndex)
|
|
||||||
{
|
|
||||||
for (int i = startByteIndex; i < endByteIndex; i++)
|
|
||||||
{
|
|
||||||
*(pointer + i) = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CollectionUtility
|
public static class CollectionUtility
|
||||||
{
|
{
|
||||||
@ -390,4 +281,24 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})";
|
return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class SpanUtility
|
||||||
|
{
|
||||||
|
public static void Sort<T>(Span<T> span, Comparison<T> comparison)
|
||||||
|
{
|
||||||
|
span.Sort(new StructComparison<T>(comparison));
|
||||||
|
}
|
||||||
|
private readonly struct StructComparison<T> : IComparer<T>
|
||||||
|
{
|
||||||
|
public readonly Comparison<T> Comparison;
|
||||||
|
public StructComparison(Comparison<T> comparison)
|
||||||
|
{
|
||||||
|
Comparison = comparison;
|
||||||
|
}
|
||||||
|
public int Compare(T x, T y)
|
||||||
|
{
|
||||||
|
return Comparison(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public UnsafeArray(int length)
|
public UnsafeArray(int length)
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.New(out ptr, length);
|
ptr = MemoryAllocator.Alloc<T>(length).Ptr;
|
||||||
Length = length;
|
Length = length;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public UnsafeArray(int length, bool isInit)
|
public UnsafeArray(int length, bool isInit)
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.NewAndInit(out ptr, length);
|
ptr = MemoryAllocator.AllocAndInit<T>(length).Ptr;
|
||||||
Length = length;
|
Length = length;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -90,18 +90,20 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public UnsafeArray<T> Clone()
|
public UnsafeArray<T> Clone()
|
||||||
{
|
{
|
||||||
return new UnsafeArray<T>(UnmanagedArrayUtility.Clone(ptr, Length), Length);
|
return new UnsafeArray<T>(MemoryAllocator.From(ptr, Length).Ptr, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.Free(ref ptr, ref Length);
|
MemoryAllocator.Free(ptr);
|
||||||
|
ptr = default;
|
||||||
|
Length = default;
|
||||||
}
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ReadonlyDispose()
|
public void ReadonlyDispose()
|
||||||
{
|
{
|
||||||
UnmanagedArrayUtility.Free(ptr);
|
MemoryAllocator.Free(ptr);
|
||||||
}
|
}
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@ -114,6 +116,9 @@ namespace DCFApixels.DragonECS.Core.Internal
|
|||||||
return CollectionUtility.AutoToString(elements, "ua");
|
return CollectionUtility.AutoToString(elements, "ua");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<T> AsSpan() { return new Span<T>(ptr, Length); }
|
||||||
|
public T[] ToArray() { return AsSpan().ToArray(); }
|
||||||
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
|
||||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@ -1,202 +0,0 @@
|
|||||||
#if ENABLE_DUMMY_SPAN
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
|
|
||||||
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
|
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS
|
|
||||||
{
|
|
||||||
internal static class ThrowHelper
|
|
||||||
{
|
|
||||||
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
|
|
||||||
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
|
|
||||||
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
[DebuggerDisplay("{ToString(),raw}")]
|
|
||||||
public readonly ref struct ReadOnlySpan<T>
|
|
||||||
{
|
|
||||||
public static ReadOnlySpan<T> Empty => new ReadOnlySpan<T>(null);
|
|
||||||
|
|
||||||
internal readonly T[] _array;
|
|
||||||
private readonly int _start;
|
|
||||||
private readonly int _length;
|
|
||||||
|
|
||||||
#region Properties
|
|
||||||
public int Length => _length;
|
|
||||||
public bool IsEmpty => _length == 0;
|
|
||||||
public ref readonly T this[int index]
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if ((uint)index >= (uint)_length || (uint)index < 0)
|
|
||||||
ThrowHelper.ThrowIndexOutOfRangeException();
|
|
||||||
return ref _array[index + _start];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ReadOnlySpan(T[] array)
|
|
||||||
{
|
|
||||||
_array = array ?? Array.Empty<T>();
|
|
||||||
_start = 0;
|
|
||||||
_length = array.Length;
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ReadOnlySpan(T[] array, int start, int length)
|
|
||||||
{
|
|
||||||
if (array == null)
|
|
||||||
{
|
|
||||||
if (start != 0 || length != 0)
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
_array = Array.Empty<T>();
|
|
||||||
_start = 0;
|
|
||||||
_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
_array = array;
|
|
||||||
_start = start;
|
|
||||||
_length = length;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Object
|
|
||||||
#pragma warning disable CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
|
|
||||||
[Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use the equality operator instead.")]
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public override bool Equals(object obj) => throw new NotSupportedException();
|
|
||||||
[Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")]
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public override int GetHashCode() => throw new NotSupportedException();
|
|
||||||
#pragma warning restore CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
//if (typeof(T) == typeof(char))
|
|
||||||
// return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
|
|
||||||
return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]";
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region operators
|
|
||||||
public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<T>(ArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array, segment.Offset, segment.Count);
|
|
||||||
public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => left._length == right._length && left._array == right._array;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Enumerator
|
|
||||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
|
||||||
public ref struct Enumerator
|
|
||||||
{
|
|
||||||
private readonly ReadOnlySpan<T> _span;
|
|
||||||
private int _index;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal Enumerator(ReadOnlySpan<T> span)
|
|
||||||
{
|
|
||||||
_span = span;
|
|
||||||
_index = span._start - 1;
|
|
||||||
}
|
|
||||||
public ref readonly T Current
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => ref _span[_index];
|
|
||||||
}
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
int index = _index + 1;
|
|
||||||
if (index < _span.Length)
|
|
||||||
{
|
|
||||||
_index = index;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Other
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public ref readonly T GetPinnableReference()
|
|
||||||
{
|
|
||||||
if (_length != 0) ThrowHelper.ThrowInvalidOperationException();
|
|
||||||
return ref _array[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
//public void CopyTo(Span<T> destination)
|
|
||||||
//{
|
|
||||||
// if ((uint)_length <= (uint)destination.Length)
|
|
||||||
// {
|
|
||||||
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// ThrowHelper.ThrowArgumentException_DestinationTooShort();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public bool TryCopyTo(Span<T> destination)
|
|
||||||
//{
|
|
||||||
// bool retVal = false;
|
|
||||||
// if ((uint)_length <= (uint)destination.Length)
|
|
||||||
// {
|
|
||||||
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
|
|
||||||
// retVal = true;
|
|
||||||
// }
|
|
||||||
// return retVal;
|
|
||||||
//}
|
|
||||||
public void CopyTo(T[] array)
|
|
||||||
{
|
|
||||||
if (_length > array.Length)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _length; i++)
|
|
||||||
{
|
|
||||||
array[i] = _array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ReadOnlySpan<T> Slice(int start)
|
|
||||||
{
|
|
||||||
if ((uint)start > (uint)_length)
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
return new ReadOnlySpan<T>(_array, _start + start, _length - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ReadOnlySpan<T> Slice(int start, int length)
|
|
||||||
{
|
|
||||||
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
return new ReadOnlySpan<T>(_array, _start + start, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T[] ToArray()
|
|
||||||
{
|
|
||||||
if (_length == 0)
|
|
||||||
return Array.Empty<T>();
|
|
||||||
var result = new T[_length];
|
|
||||||
Array.Copy(_array, _start, result, 0, _length);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -62,6 +62,14 @@ namespace DCFApixels.DragonECS.Core.Unchecked
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Unsafe Span
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe EcsUnsafeSpan CreateUnsafeSpan(short worldID, int* ptr, int length)
|
||||||
|
{
|
||||||
|
return new EcsUnsafeSpan(worldID, ptr, length);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region EcsGroup
|
#region EcsGroup
|
||||||
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
|
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user