remove span dummy/update&fix alloc management/add EcsUnsafeSpan & WhereUnsafe

This commit is contained in:
Mikhail 2026-03-16 21:52:45 +08:00
parent 14bf846135
commit c30d4ec5f0
15 changed files with 619 additions and 432 deletions

View File

@ -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;

View File

@ -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
}
}

View File

@ -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();

View File

@ -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)

View File

@ -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();

View File

@ -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
} }

View File

@ -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)]

View File

@ -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()

View File

@ -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;
} }
} }
} }

View File

@ -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;
}
} }
} }

View File

@ -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;

View File

@ -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);
}
}
}
} }

View File

@ -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)]

View File

@ -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

View File

@ -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)
{ {